Release and ci
Skill andymai/gridfinity-layout-tool/.claude/skills/release-and-ci
PR-to-production pipeline — failed CI checks (Build, Unit Tests Complete, Quality, PWA Smoke Test, Conventional Commit), bundle-size (size-limit) failures, PR title rules, release-please version bumps, stuck "chore(main): release" PRs, Vercel deploy/skip logic, production rollback, "Production smoke failed" issues, editing .github/workflows/*.From its SKILL.md
npx -y skills add andymai/gridfinity-layout-tool --skill release-and-ciAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
SKILL.md
13.4 KB, as published. Nobody here has run it
Release and CI
When to use
- A PR check is red and you need to diagnose or reproduce it locally.
- Shipping a change: PR title, merge, release, deploy questions.
- Releases stopped flowing, a version bump is wrong, or production needs a rollback.
- Adding or editing anything under
.github/workflows/or the Vercel config.
Mental model
- Everything squash-merges to
main. The PR title (validated by.github/workflows/pr-title.ymlas a conventional commit, lowercase subject) becomes the main commit, the CHANGELOG line, and the version-bump signal. fix/perf → patch, feat → minor,!/breaking footer → major. Types refactor/chore/test/docs/style are hidden from the CHANGELOG and bump nothing (release-please-config.json). - On a schedule (06:00/13:00/23:00 UTC, chosen as activity troughs, or manual
workflow_dispatch),.github/workflows/release.ymlruns release-please, which maintains achore(main): release gridfinity-layout-tool x.y.zPR and auto-approves + auto-merges it (GitHub App token, 6 merge retries). No human touches releases.CHANGELOG.md,.release-please-manifest.json, and theversioninpackage.jsonare machine-owned — never hand-edit; the manifest and package.json version must move in lockstep. - Vercel deploys main only on release merges or a
[deploy]commit (scripts/vercel-ignore.sh) — merges accumulate and ship with the next scheduled release, so production deploys ≤3x/day. That cadence is for CHANGELOG and What's New coherence, not user protection: a deploy never interrupts a live tab (skipWaitingandclientsClaimboth false,usePWAUpdatepolls hourly behind a smoke gate), and Vercel Skew Protection is enabled, which is a dashboard setting no repo grep will surface. Slot placement is insensitive, landing within 2% of the best available triple in either DST offset, so it needs no DST revisit and is not worth re-litigating. To ship sooner: trigger the Release workflow manually, or put[deploy]in the squash commit message (that is also the route for avercel.json-only change, which bumps no release). - Two smoke layers, one spec (
e2e/smoke/pwa-smoke.spec.tsviaplaywright.smoke.config.ts):smoke-preview.ymlpolls the GitHub deployments API for the Vercel preview URL on PRs;smoke-postpromote.ymlsmokeshttps://gridfinitylayouttool.comafter each production deploy and on failure runsvercel rollbackand files apriority: criticalissue. - CI is stricter on main than on PRs: PRs run 6 sharded test jobs (3
generators, 3core); main runs the full suite with coverage thresholds plus the fail-closed OSV scan. A PR can merge green and still break main. - Every CI job carries
if: !startsWith(github.head_ref, 'release-please--'). Any new required workflow MUST copy this guard, or the release PR waits forever on a check that never runs and all releases silently stall. Branch protection requires the aggregateUnit Tests Complete(test-gatejob inci.yml), not shard names — change shard counts freely, never rename that job.
The workflow files themselves (ci.yml, release.yml, smoke-*.yml, vite.config.ts) carry incident-history comments — read them before editing; they explain traps this file only indexes.
Recipe: diagnose a failed PR check
gh pr checks <pr-number>— identify the failing context.- Conventional Commit → the PR title is wrong (bad type or uppercase subject). Edit the title on GitHub; it re-validates on edit, no push needed. Allowed types are listed in
pr-title.yml. - Quality → the red step is only an aggregator; every real check runs with
continue-on-error. Rungh run view <run-id> --log-failedand find the earlier step with the failure annotation. A "skipped" check counts as failure and meanspnpm installdied (usually lockfile drift under--frozen-lockfile). For what each check enforces and its legitimate unblock, see the quality-gates skill. - Unit Tests (shard) / Unit Tests Complete → reproduce locally:
pnpm run build:en-locale pnpm vitest run --project=generators # generators shard pnpm vitest run --project=unit --project=dom # core shardbuild:en-localeis mandatory first —en.jsonis generated and gitignored. CI runs vitest with-u, so snapshot mismatches can NEVER fail CI; rule them out. See the testing skill for the project layout. - Build at "Check bundle size" → bundle-budget recipe below.
- PWA Smoke Test timeout ("Timed out waiting for Vercel preview") → check the Vercel dashboard for a failed preview build; if the PR is infra-only and should skip, confirm its paths are absent from
scripts/vercel-ignore.sh. For real test failures, download the trace artifact, or run locally:PLAYWRIGHT_TEST_BASE_URL=<preview-url> pnpm exec playwright test --config=playwright.smoke.config.ts
Recipe: fix a bundle-size failure
- Reproduce:
pnpm run build && pnpm run size:check. - Budgets live in the
size-limitarray inpackage.json: Main bundle 190 kB gzip, Eager initial JS 260 kB (main + react-vendor + state + core-foundation), Total JS 2000 kB. - Open
bundle-analysis.html(repo root, emitted by the build) to see what grew. - Preferred fix: keep the weight out of eager chunks — dynamic-import it, or check the
advancedChunksrules invite.config.ts(~lines 246–290) still route it to a lazy chunk. Raising a limit inpackage.jsonis a last resort and needs justification in the PR. - If you rename/add a chunk in
vite.config.ts, update the matchingsize-limitpath glob inpackage.jsonin the same PR — the globs match chunk file names and drift silently otherwise.
Recipe: ship end-to-end
- Branch off main (protected; PRs only). Pre-commit (
.husky/pre-commit) does NOT run tests — runpnpm run test:runandpnpm run qualityyourself before pushing. - Title the PR as a conventional commit, lowercase subject, scope = the touched slice (recent scopes: baseplate, bin-designer, generation, scan, design-system, deps, ci, api, shared). This exact string ships in the CHANGELOG.
- Wait for checks (smoke-preview adds ~5 min for the Vercel preview), squash-merge.
- If SPA paths changed, production deploys immediately; post-promote smoke guards it. release.yml then updates/merges the release PR unattended. Users see the new version (
version.json/__APP_VERSION__, emitted byscripts/vite-plugin-version.ts) after the release-merge deploy. - Verify production:
curl -s https://gridfinitylayouttool.com/version.json—gitShashould match the deployed commit (the file is no-store byvercel.jsondesign).
Recipe: unstick a stalled release
Symptom: a chore(main): release gridfinity-layout-tool x.y.z PR sits open; no versions ship.
gh pr list --search "chore(main): release"to find it; check the latest release.yml run:gh run list --workflow=release.yml, thengh run view <id> --log-failed.- Two usual causes: expired/rotated GitHub App credentials (APP_ID / APP_PRIVATE_KEY secrets — the 6-attempt merge loop errors out), or a newly added required check missing the
release-please--skip guard so it never runs on the release branch. - Unblock now by merging the release PR manually (squash); fix the root cause after.
Recipe: roll back production
- If post-promote smoke failed, rollback already ran — look for a "Production smoke failed: <sha>" issue and verify the alias in the Vercel dashboard first.
- Manual: Vercel dashboard → Deployments → previous good deploy → Instant Rollback, or
npx vercel rollback --token $VERCEL_TOKEN --scope $VERCEL_ORG_ID --yes(mirrorssmoke-postpromote.yml). - Rollback only moves the alias. Users' installed service workers keep the bad precache until a NEW deploy's SW passes the client-side smoke gate (
src/shared/pwa/smokeGate.ts). Always fix forward with a new merge to main. - Verify with
curl -s https://gridfinitylayouttool.com/version.json.
PWA/deploy invariants (do not "simplify" these)
| Setting | Where | Why it must stay |
|---|---|---|
registerType: 'prompt', skipWaiting: false, clientsClaim: false | vite.config.ts VitePWA | smokeGate.ts activates the waiting SW (SKIP_WAITING), then smokes the new bundle in a hidden iframe served by the now-active SW; on failure the caller unregisters + purges the precache. autoUpdate would skip the gate and ship broken bundles straight into users' service workers |
globIgnores: wwwMigration-*, smokeBoot-*, version.json | vite.config.ts workbox | Precaching one-shot chunks caused SW install failures on hash mismatch — the old SW stays in control and no fix can ever reach users |
/sw.js must-revalidate, /version.json no-store | vercel.json headers | The smoke gate fetches version.json to detect fresh deploys; caching it strands users on stale versions |
No extraHTTPHeaders bypass token | playwright.smoke.config.ts | Playwright sends extra headers to every origin (fonts.gstatic.com etc.), breaking CORS preflights; the spec sets a same-origin cookie via query string instead |
No actions/checkout in pr-title.yml / labeler.yml | those workflows | They use pull_request_target (secrets in base-branch context); checking out PR code there is a documented pwn-request vector |
Traps
| Symptom | Cause | Fix |
|---|---|---|
| Main CI red right after a green PR merged | Coverage thresholds and fail-closed OSV run only on main | Coverage step → add tests; OSV → bump/override the vulnerable dep. Not snapshots (CI passes -u) |
| Deploy didn't happen after merging | scripts/vercel-ignore.sh builds main only on release merges or [deploy] commits | Wait for the next scheduled release, run the Release workflow manually, or use [deploy] |
pnpm install can't resolve a version visible on npm | minimumReleaseAge: 10080 in pnpm-workspace.yaml (7-day supply-chain cooldown) | Wait, pin the previous version, or add to minimumReleaseAgeExclude with justification per SECURITY.md |
| CHANGELOG entry / version bump missing for a merged PR | PR title used a hidden type (chore/refactor/etc.) | Titles are baked in at squash; land a follow-up correctly-typed commit if a bump is needed |
| Green "PWA Smoke Test" but no preview was actually tested | Vercel legitimately ignored the build (docs/CI-only PR) or Dependabot PR (no bypass secret) | Expected; the job passes with a warning by design |
| Unminified production stack traces in PostHog | Maps upload from the Vercel production build (scripts/vercel-build.sh), never from CI | Check the deploy log; needs POSTHOG_CLI_API_KEY / POSTHOG_CLI_PROJECT_ID in Vercel |
Fresh clone: bare pnpm vitest fails with lazy-load errors | en.json is generated and gitignored | pnpm run build:en-locale first |
For share/collab runtime env vars on Vercel, see the share-api-collab skill. For pre-commit gate internals and unblocks, see quality-gates.