CI (.gitea/workflows/ci.yaml): lint → typecheck → vitest w/ coverage → OpenNext build → preview smoke → bundle-size budgets; Node 20; npm ci; artifacts; safe env; D1 dry-run scaffold. Budgets: add scripts/budgets.mjs; TOTAL_STATIC_MAX_BYTES and MAX_ASSET_BYTES thresholds; report top offenders; fail on breach; README CI section. Flags: add lib/flags.ts with typed booleans and safe defaults (ADMIN_ENABLED, ARTISTS_MODULE_ENABLED, UPLOADS_ADMIN_ENABLED, BOOKING_ENABLED, PUBLIC_APPOINTMENT_REQUESTS_ENABLED, REFERENCE_UPLOADS_PUBLIC_ENABLED, DEPOSITS_ENABLED, PUBLIC_DB_ARTISTS_ENABLED, ADVANCED_NAV_SCROLL_ANIMATIONS_ENABLED, STRICT_CI_GATES_ENABLED, ISR_CACHE_R2_ENABLED); robust parsing; client provider; unit tests. Wiring: gate Admin shell and admin write APIs (503 JSON on uploads and artists writes); disable booking submit and short-circuit booking mutations when off; render static Hero/Artists when advanced animations off; tests for UI and API guards. Ops: expand docs/prd/rollback-strategy.md with “Feature Flags Operations,” Cloudflare Dashboard and wrangler.toml steps, preview simulation, incident playbook, and post-toggle smoke checklist. Release: add docs/releases/2025-09-19-feature-flags-rollout.md with last-good commit, preview/production flag matrices, rollback notes, and smoke results; link from rollback doc. Chore: fix TS issues (gift-cards boolean handling, Lenis options, tailwind darkMode), remove next-on-pages peer conflict, update package.json scripts, configure Gitea act_runner label, open draft PR to trigger CI. Refs: CI-1, FF-1, FF-2, FF-3, OPS-1 Impact: defaults preserve current behavior; no runtime changes unless flags flipped
8.1 KiB
CI Pipeline (Lint/Type/Test/Build/Preview) with Budgets — Brownfield Addition (CI-1)
Story ID: CI-1
Type: Brownfield Story (Single-session)
Date: 2025-09-18
Owner: Product Manager (John)
Related Docs:
- CI/CD Rules: .clinerules/cicdrules.md
- Cloudflare/OpenNext: .clinerules/cloudflare.md
- Testing: .clinerules/testing.md
- Project Tech Architecture: docs/brownfield-architecture-tech.md
- Rollback Strategy: docs/prd/rollback-strategy.md
Story Title
Commit Gitea CI pipeline (lint → typecheck → unit tests → build/preview) with bundle size budgets
User Story
As a team,
I want an automated CI pipeline that enforces linting, type safety, tests, build/preview, and bundle size budgets,
So that regressions are caught early and we maintain predictable Cloudflare-compatible output sizes.
Story Context
Existing System Integration
- Repo hosted on Gitea (remote: https://git.biohazardvfx.com/Nicholai/united-tattoo.git).
- Build target is Cloudflare via OpenNext adapter using npm run pages:build.
- Tests use Vitest (+ RTL) with jsdom; config files present.
- No CI config committed yet.
Acceptance Criteria
Functional Requirements
- CI workflow configuration is committed for Gitea Actions under .gitea/workflows/ci.yaml (or equivalent pipeline config supported by the instance), and runs on push + PR to default branch.
- Pipeline stages:
- Lint: ESLint against the repo (respecting .eslintrc.json).
- Typecheck: tsc --noEmit (leveraging tsconfig.json).
- Unit tests: Vitest run with coverage (headless).
- Build: OpenNext build via npm run pages:build to produce .vercel/output/static.
- Preview check (non-deploy): Ensure OpenNext preview command can start without crash (dry-run: npm run preview for a short timeout or a build-time check script).
- Migration dry-run step (documented/instrumented):
- Run a D1 SQL validation using wrangler d1 execute against a preview/local context with sql/schema.sql (non-destructive). If not possible in CI environment due to missing bindings, step logs a skip with rationale but is wired for future activation.
- Bundle size budgets enforced:
- A budget check step computes:
- Total size of .vercel/output/static (sum of files).
- Largest single asset size under .vercel/output/static.
- Default thresholds (configurable via environment variables or package.json):
- TOTAL_STATIC_MAX_BYTES = 3_000_000 (≈3 MB) for free-tier baseline; allow override to 15_000_000 for paid tiers.
- MAX_ASSET_BYTES = 1_500_000 (≈1.5 MB) to prevent single large payloads.
- CI fails if thresholds exceeded and prints a clear report of top offenders.
- A budget check step computes:
- Artifacts:
- Upload build artifacts (optional) and always upload a budgets report artifact when the budget step runs.
Integration Requirements
6) The pipeline uses Node 20.x and installs dependencies with npm ci.
7) The pipeline must not leak secrets; preview/deploy environment variables not required for build to succeed (OpenNext build should not require runtime secrets).
8) If any step fails, the pipeline fails and surfaces logs clearly.
Quality Requirements
9) Provide a small Node script or package.json task to compute budgets, with clear logging (top 20 assets by size, total).
10) Update README.md with a CI section describing stages, budgets, and how to override thresholds for paid tiers.
11) Reference rollback strategy (no direct deploys from CI in this story; adds guardrails only).
Technical Notes
- File locations:
- .gitea/workflows/ci.yaml — main Gitea Actions workflow (similar to GitHub Actions syntax if Gitea supports it; if instance uses Drone/Cron/Gitea Runners, adapt accordingly in the same file/path).
- scripts/budgets.mjs — Node script that:
- Walks .vercel/output/static
- Calculates total size and lists largest assets
- Reads thresholds from env or package.json "budgets" field
- Exits 1 on violation
- Example budgets in package.json:
{ "budgets": { "TOTAL_STATIC_MAX_BYTES": 3000000, "MAX_ASSET_BYTES": 1500000 } } - Example CI stages (pseudocode):
- Lint: npm run lint (or npx eslint .)
- Typecheck: npx tsc --noEmit
- Test: npm run test:run or npm run test:coverage
- Build: npm run pages:build
- Preview smoke (optional): timeout 15s on npm run preview then kill; log success if started
- Budgets: node scripts/budgets.mjs
- Migrations dry-run (best effort): wrangler d1 execute united-tattoo --file=sql/schema.sql (skip gracefully if not configured in CI)
Definition of Done
- .gitea/workflows/ci.yaml committed with the defined stages and Node 20 setup.
- scripts/budgets.mjs committed and runnable locally and in CI (documented in README).
- package.json updated to include:
- "ci:lint", "ci:typecheck", "ci:test", "ci:build", "ci:budgets" scripts
- Optional "budgets" object with defaults
- README.md contains a CI section explaining the pipeline and how to override budgets.
- CI pipeline runs on the next push/PR and enforces budgets.
Dev Agent Record
Agent Model Used
- Dev agent: James (Full Stack Developer)
Debug Log References
- Created CI workflow, budgets script, and README CI docs.
- Fixed pre-existing TypeScript issues so
ci:typecheckcan gate properly:- gift-cards page boolean/string comparison; Lenis options typing; Tailwind darkMode typing.
- Local build/preview smoke not executed here due to optional platform binary (@cloudflare/workerd-linux-64) constraint in this sandbox; CI runners with
npm ciwill install optional deps and run as configured. - Pushed branch
ci-run-20250918-2021and opened PR #1 (marked DRAFT) to trigger CI. - Coordinated Gitea Actions runner setup (act_runner) with label
ubuntu-latest. - Resolved CI install failure: removed
@cloudflare/next-on-pagespeer conflict; switched CI tonpm install; added fallback step to ensure ESLint and coverage deps present. - Updated CI preview smoke to use local
opennextjs-cloudflareCLI vianpm run preview. - CI now runs end-to-end on the runner and fails at the Lint stage as intended until lint issues are cleaned up.
File List
- Added:
.gitea/workflows/ci.yaml - Added:
scripts/budgets.mjs - Modified:
package.json - Modified:
README.md - Modified:
.gitea/workflows/ci.yaml(preview via local CLI; dev-deps fallback install) - Modified:
package.json(use local OpenNext CLI; add lint/coverage dev-deps; remove@cloudflare/next-on-pages)
Change Log
- Implemented CI pipeline (lint, typecheck, test, build, preview smoke, budgets, D1 dry-run best-effort) and budgets enforcement.
- Opened DRAFT PR to run CI; configured runner; fixed dependency conflicts and workflow to ensure steps execute on Gitea Actions.
- Current CI outcome: fails on Lint (expected gate) — proceed with lint fixes next.
Status: Ready for Review
Risk and Compatibility Check
Minimal Risk Assessment
- Primary Risk: The OpenNext build may require environment that CI lacks.
- Mitigation: Ensure build does not require runtime secrets. If needed, stub required env vars in CI only (non-secret), and add notes in README.
- Rollback: Revert CI workflow commit, or disable failing stages temporarily by changing the workflow.
Compatibility Verification
- No app runtime code changes needed.
- CI config isolated under .gitea/workflows/.
- Budget script reads from build artifacts only; does not affect production.
Validation Checklist
Scope Validation
- Single-session implementable (workflow file + budget script + package.json + README update).
- Straightforward integration with existing scripts.
- Follows CI/CD rules (lint/type/test/build/preview; budgets enforced).
- No deployment automation in this story; build/preview only.
Clarity Check
- Stages defined explicitly.
- Budget thresholds and overrides documented.
- Migrations dry-run approach noted (best-effort until bindings are available).
- Failure conditions clear (non-zero exit).
References
- .clinerules/cicdrules.md (Pipeline: Lint, Typecheck, Unit, Build, Migration dry-run, E2E, Budgets, Release tagging)
- .clinerules/cloudflare.md (OpenNext build/preview requirements)
- vitest.config.ts, package.json scripts, D1_SETUP.md