Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,164 @@
# tasks — react-ui-registry-deployment
## 0. Pre-flight (non-negotiable)
- [ ] 0.1 Confirm clean working tree (`git status` reports nothing) and no in-flight worktrees with uncommitted work (`git worktree list` then `git status` in each)
- [ ] 0.2 If any worktrees exist under `.claude/worktrees/`, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (`git worktree remove`) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
- [ ] 0.3 Confirm the package set in `MANIFEST.md` matches `find packages -maxdepth 3 -name package.json` count. Drift here means a prior change archived without regenerating the manifest.
## 1. Registry build script (in `@suaveplan/react-ui`)
- [ ] 1.1 Create `packages/react/react-ui/scripts/build-registry.ts`.
Walks the package's component source tree and emits one
`registry-item` JSON per component plus a top-level
`registry/r/index.json` listing every entry.
- [ ] 1.2 Match the shadcn `registry-item` v1 schema exactly:
`$schema`, `name`, `type`, `dependencies`, `registryDependencies`,
`files[]`, `tailwind`, `cssVars`. Validate the emitted JSON
against a Zod schema before writing — the build fails on shape
drift.
- [ ] 1.3 Emit deterministic output: stable key order, LF
line endings, trailing newline. The CI snapshot diff in the
package's existing test suite will catch accidental drift.
- [ ] 1.4 Co-locate `build-registry.md` per the genesis docs rule
(≥200 words, Purpose / Features / Basic Example / Advanced
Example / API Reference / Implementation Notes).
- [ ] 1.5 Add a smoke test
`packages/react/react-ui/scripts/build-registry.test.ts` that
invokes the script in a temp dir, parses every emitted JSON, and
asserts schema conformance.
## 2. Caddy + Docker scaffolding
- [ ] 2.1 Write `ops/registry/Dockerfile` — `FROM caddy:2-alpine`,
`COPY Caddyfile /etc/caddy/Caddyfile`, healthcheck on
`/health`.
- [ ] 2.2 Write `ops/registry/Caddyfile` — `ui.suaveplan.com` site
block with:
- `root * /srv/registry`
- `handle_path /r/*` → `file_server` with
`Access-Control-Allow-Origin: *`
- `handle_path /storybook/*` → `file_server` with restrictive
CSP
- `handle /health` → `respond 200 "ok"`
- `tls` block pinning `protocols tls1.2 tls1.3`
- `header` block with HSTS, `X-Content-Type-Options`,
`Referrer-Policy`, `Permissions-Policy`
- `encode gzip zstd`
- explicit deny on `/.well-known/*` outside ACME
- [ ] 2.3 Write `ops/registry/docker-compose.yml` — one service
`registry`, port `80:80` and `443:443`, named volumes
`caddy_data` and `caddy_config`, bind-mount
`/srv/registry:/srv/registry:ro`, `restart: unless-stopped`.
- [ ] 2.4 Write `ops/registry/.env.example` — placeholders
`OVH_SSH_HOST=__OVH_HOST__`, `OVH_SSH_USER=deploy-registry`,
`REGISTRY_DEPLOY_PATH=/srv/registry/r/`,
`STORYBOOK_DEPLOY_PATH=/srv/registry/storybook/`. No real
values.
## 3. Deployment runbook
- [ ] 3.1 Write `ops/registry/README.md` covering:
- Architecture overview (one Caddy container, two paths)
- First-time setup (clone, env, `docker compose up -d`, cert
issuance check)
- Creating the `deploy-registry` user with scoped sudoers rule
- Installing the CI public key on OVH
- The on-deploy flow (path-filtered GitHub Action, rsync,
no-reload-needed-for-static)
- Manual ad-hoc admin via `ssh root@ovh` (out-of-band; CI
intentionally does not use root)
- Rollback via `bash ops/registry/rollback.sh` (one-shot atomic
swap from `/srv/registry/.previous/<timestamp>/`)
- Cert renewal (Caddy handles automatically; how to verify and
how to force-renew if needed)
- GitHub Actions secret rotation steps
- [ ] 3.2 Write `ops/registry/rollback.sh` referenced from the
runbook. Idempotent, exits non-zero with a clear message if no
snapshot exists.
## 4. CI workflow
- [ ] 4.1 Write `.github/workflows/registry-deploy.yml` — triggers
on push to `genesis` with path filter
`packages/react/react-ui/**` plus `ops/registry/**`. Job steps:
- `actions/checkout@v4`
- Setup Bun via `oven-sh/setup-bun@v2`
- `bun install --frozen-lockfile`
- `bunx turbo build --filter=@suaveplan/react-ui`
- `bun packages/react/react-ui/scripts/build-registry.ts`
- `cd packages/react/react-ui && bun run build-storybook`
- `webfactory/ssh-agent@v0.9.0` with the `OVH_SSH_PRIVATE_KEY`
secret
- `rsync -azh --delete --backup
--backup-dir=/srv/registry/.previous/$(date -u
+%Y%m%dT%H%M%SZ)/r/ packages/react/react-ui/registry/r/
"$OVH_SSH_USER@$OVH_SSH_HOST:/srv/registry/r/"`
- same for storybook with its own `--backup-dir`
- post-deploy `curl -fsSL
https://ui.suaveplan.com/health` smoke test
- [ ] 4.2 Add a `concurrency:` block keyed on the workflow + ref
so back-to-back pushes serialise, never race.
- [ ] 4.3 Document the three required GitHub Actions secrets in
the runbook (`OVH_SSH_PRIVATE_KEY`, `OVH_SSH_HOST`,
`OVH_SSH_USER`). NEVER commit values.
## 5. Manual user steps (call out clearly)
The user MUST perform these one-time manual steps; they cannot be
scripted in this change because they involve secrets and OVH
host-side configuration.
- [ ] 5.1 **USER:** Generate a deploy SSH keypair locally
(`ssh-keygen -t ed25519 -C "registry-deploy" -f
~/.ssh/registry_deploy`).
- [ ] 5.2 **USER:** Copy the public key to OVH under the
`deploy-registry` user's `~/.ssh/authorized_keys` (after creating
the user per the runbook).
- [ ] 5.3 **USER:** Add the private key as the
`OVH_SSH_PRIVATE_KEY` secret in the GitHub repo settings.
- [ ] 5.4 **USER:** Add `OVH_SSH_HOST` and `OVH_SSH_USER` as
GitHub Actions secrets (host = the OVH hostname, user =
`deploy-registry`).
- [ ] 5.5 **USER:** Confirm `ui.suaveplan.com` A record points
to the OVH IP (already done per change context, but re-verify
with `dig +short ui.suaveplan.com` before first deploy).
## 6. Verification (Web App & Page Verification gate)
Per CLAUDE.md "Web App & Page Verification" — required even though
this is mostly static content.
- [ ] 6.1 Deploy from a feature branch first (NOT `genesis`)
against a staging path or a temporary subdomain to validate the
pipeline before pointing it at production.
- [ ] 6.2 Run a Playwright sweep: navigate to `/`, `/r/index.json`,
`/storybook/`, plus a sampled subset of per-component manifests.
Assert HTTPS, valid cert, expected DOM content, zero
`console.error`, zero failed subresource requests.
- [ ] 6.3 End-to-end shadcn-add test: spin up a clean Vite + React
project in a temp dir, point its `components.json` at the
public registry, run `npx shadcn@latest add @suaveplan/button`,
assert `src/components/ui/button.tsx` lands with the expected
exports. This is the contract test.
- [ ] 6.4 Capture browser console + network during the Playwright
sweep; any unexpected warning or 4xx/5xx fails the gate.
- [ ] 6.5 Run the rollback script once on the OVH host and verify
the previous registry contents serve. Roll forward again before
closing the task.
- [ ] 6.6 24 h after first production deploy, query the Caddy
access log and confirm 5xx count is zero.
## 7. Documentation + change archive
- [ ] 7.1 Refresh root docs per AGENTS §13b: `MANIFEST.md`,
`MISSING.yml`, `STATUS.md`, `SUMMARY.md`. The `STATUS.md` row
for ops infrastructure should reflect that the registry deploy
exists.
- [ ] 7.2 Run `bunx markdownlint-cli2 --fix` on every touched
Markdown file; resolve any unfixable violations by hand.
- [ ] 7.3 Fold `specs/registry-deployment/spec.md` into the
canonical `openspec/specs/registry-deployment/spec.md` (a new
capability spec — first ops-tier capability on genesis).
- [ ] 7.4 Move `openspec/changes/react-ui-registry-deployment/`
to `openspec/archive/<yyyy-mm-dd>-react-ui-registry-deployment/`.