Workflow: .github/workflows/ci.yml (single workflow, runs on push to main and on every PR targeting main).
Jobs
| Job | What it runs |
|---|---|
lint |
pnpm lint — ESLint 10 flat config plus the TSDoc public-export gate — and pnpm --filter blog-admin lint, which runs the integration fixture’s AdonisJS ESLint config |
typecheck |
pnpm -r typecheck checks package and docs source, then pnpm typecheck:tests runs tsc --noEmit with every package’s tsconfig.test.json. Existing package-level test debt is explicit in scripts/test-typecheck-baseline.json; every listed package is still checked, new failing packages fail CI, and a newly passing package fails until its stale baseline entry is removed |
unit |
Package Japa suites run under c8 and the UI Vitest suite runs with V8 coverage; LCOV and JSON summaries are retained in the package-coverage-* artifact for 30 days. Coverage is informational until the baseline supports defensible thresholds |
security |
Required §19 gate — packages/core/tests/security/*.spec.ts (route sweep, mass-assignment probes, allowlist fuzzing, upload spoofing, the XSS corpus over the persist path, the authorization cross-check, serialization leakage) plus examples/blog-admin/tests/functional/adonia_security.spec.ts on sqlite (guest sweep over the committed router, Shield XSRF, compiled SQL). mapping.spec.ts fails the job when docs/security-conformance.md and the suite drift apart in either direction |
build |
pnpm build on the supported Node 24 baseline |
docs |
pnpm docs:api:diff then pnpm docs:build — G2-1/G2-2’s gate. docs:api:diff compares the generated public API inventory with the PR base and requires a new major Changeset for removals. docs:build verifies generated references, compiles checked fenced samples, enforces the no-check budget, and fails on dead relative links. Neither command needs pnpm build |
test-blog-admin |
examples/blog-admin suite matrixed over DB_CONNECTION=pg|mysql|sqlite; the pg axis starts postgres:17, the mysql axis starts mysql:8.4 (health-checked), the sqlite axis starts no real services (in-process) |
install-from-scratch |
Phase-1 exit gate (never leaves CI): scripts/test-fresh-install.sh — packs @adonia/* to tarballs, scaffolds a FRESH AdonisJS v7 inertia-react starter, installs the tarballs, runs node ace add @adonia/core (configure hook, zero manual edits), adds one posts resource, and curl-asserts the CRUD flow (XSRF cookie-jar flow, mass-assignment guard, validation failures) over HTTP; matrixed over DB_CONNECTION=pg|mysql|sqlite with the same service pattern as test-blog-admin |
playwright |
caches Chromium and Firefox, maps lvh.me test domains, runs both desktop projects, and uploads retained traces and failure screenshots from test-results/ when the suite fails |
bundle-size |
pnpm build then pnpm size — bundles the scaffolded panel entry with React/Inertia external, gates the base chunks at TECH_SPEC §18’s 225 kB gzip, asserts the rich-text/code/image editors stay behind import(), and posts a per-chunk report to the job summary and a sticky PR comment. See bundle budget |
performance |
Required §18 gate — pnpm perf benchmarks validated production descriptor/index/export/render/controller paths, asserts fixed N+1 and widget request counts, enforces cold descriptor p95 plus bundle/lazy-editor budgets, publishes the report to the job summary, and uploads raw JSON/chunks for 90 days. The same raw JSON is committed durably at docs/public/performance-report.json and linked from the generated report |
Notes on design decisions:
- GHA does not support conditional services. The
test-blog-adminandinstall-from-scratchmatrices give a non-matching service a no-opalpineimage with no health check, so GHA never waits on it; only the service matchingmatrix.dbis real and health-gated. install-from-scratchruns the exactverdaccio@6.9.0release on127.0.0.1:54573becausenode ace add @adonia/coreinstalls from a registry before running the configure hook; the starter kit andcreate-adonisjsare pinned too./etc/hostshas no wildcard support, so the Playwright job enumerates the subdomains the domain-mounted/tenant panel tests will use instead of*.lvh.me.examples/blog-admin, the fresh-install script, andtest:e2eare required inputs. CI invokes them directly, so deleting or renaming any of them fails instead of turning a required job into a green no-op.bundle-sizemeasures a bundle, not a file:packages/uibuilds withtsc -b, so gatingdist/index.jswould gate a barrel of re-export statements.scripts/panel_bundle.jsbundles the scaffolded panel entry with esbuild instead, and.size-limit.jsonpoints at the resulting.size-limit-build/base/*.jschunks.docsis separate fromlint: root ESLint ignores extracted documentation samples, whiledocs:buildchecks them against the real API. A red docs job therefore reports documentation drift directly instead of hiding it under lint.- The PR-comment step is skipped for fork PRs (their token is read-only) and is
continue-on-error, so the required check never goes red over a comment permission. The job summary carries the same report either way.
Required status checks (branch protection)
Branch protection cannot be configured from this repo — it must be set once in the GitHub UI:
Settings → Branches → Add branch ruleset for main, enable “Require status checks to pass before merging” and mark all of the following as required:
Lint (ESLint flat config)TypecheckUnit testsSecurity conformance (§19)Build (Node 24)Docs (samples + site build)blog-admin tests (pg)blog-admin tests (mysql)blog-admin tests (sqlite)install-from-scratch (pg)install-from-scratch (mysql)install-from-scratch (sqlite)Playwright e2ePerformance (§18)Bundle size (size-limit)
Also enable “Require branches to be up to date before merging” so the matrix cannot go stale against main.