7.7 KiB
7.7 KiB
tasks — react-ui-registry-deployment
0. Pre-flight (non-negotiable)
- 0.1 Confirm clean working tree (
git statusreports nothing) and no in-flight worktrees with uncommitted work (git worktree listthengit statusin 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.mdmatchesfind packages -maxdepth 3 -name package.jsoncount. 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 oneregistry-itemJSON per component plus a top-levelregistry/r/index.jsonlisting every entry. - 1.2 Match the shadcn
registry-itemv1 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.mdper 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.tsthat 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.comsite block with:root * /srv/registryhandle_path /r/*→file_serverwithAccess-Control-Allow-Origin: *handle_path /storybook/*→file_serverwith restrictive CSPhandle /health→respond 200 "ok"tlsblock pinningprotocols tls1.2 tls1.3headerblock with HSTS,X-Content-Type-Options,Referrer-Policy,Permissions-Policyencode gzip zstd- explicit deny on
/.well-known/*outside ACME
- 2.3 Write
ops/registry/docker-compose.yml— one serviceregistry, port80:80and443:443, named volumescaddy_dataandcaddy_config, bind-mount/srv/registry:/srv/registry:ro,restart: unless-stopped. - 2.4 Write
ops/registry/.env.example— placeholdersOVH_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.mdcovering:- Architecture overview (one Caddy container, two paths)
- First-time setup (clone, env,
docker compose up -d, cert issuance check) - Creating the
deploy-registryuser 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.shreferenced 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 togenesiswith path filterpackages/react/react-ui/**plusops/registry/**. Job steps:actions/checkout@v4- Setup Bun via
oven-sh/setup-bun@v2 bun install --frozen-lockfilebunx turbo build --filter=@suaveplan/react-uibun packages/react/react-ui/scripts/build-registry.tscd packages/react/react-ui && bun run build-storybookwebfactory/ssh-agent@v0.9.0with theOVH_SSH_PRIVATE_KEYsecretrsync -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/healthsmoke 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-registryuser's~/.ssh/authorized_keys(after creating the user per the runbook). - 5.3 USER: Add the private key as the
OVH_SSH_PRIVATE_KEYsecret in the GitHub repo settings. - 5.4 USER: Add
OVH_SSH_HOSTandOVH_SSH_USERas GitHub Actions secrets (host = the OVH hostname, user =deploy-registry). - 5.5 USER: Confirm
ui.suaveplan.comA record points to the OVH IP (already done per change context, but re-verify withdig +short ui.suaveplan.combefore 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, zeroconsole.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.jsonat the public registry, runnpx shadcn@latest add @suaveplan/button, assertsrc/components/ui/button.tsxlands 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. TheSTATUS.mdrow for ops infrastructure should reflect that the registry deploy exists. - 7.2 Run
bunx markdownlint-cli2 --fixon every touched Markdown file; resolve any unfixable violations by hand. - 7.3 Fold
specs/registry-deployment/spec.mdinto the canonicalopenspec/specs/registry-deployment/spec.md(a new capability spec — first ops-tier capability on genesis). - 7.4 Move
openspec/changes/react-ui-registry-deployment/toopenspec/archive/<yyyy-mm-dd>-react-ui-registry-deployment/.