U5 — the gate everything else has to pass
continuous_integration.yaml: six blocking checks, then a separate publish per environment so a Vite build never gets tagged for the wrong one, then a call into last commit's deploy workflow. Along the way: the lint list had drifted (two problems not in the requirement, one already fixed), and the Umami-origin gate needed a variable pair of its own since the backend's side of that comparison lives on the host, not in CI. Pinned the two vulnerable packages while at it.
This commit is contained in:
+101
@@ -0,0 +1,101 @@
|
||||
# Code Generation Summary — U5 CI Workflow & Quality Gates
|
||||
|
||||
**Date**: 2026-07-28
|
||||
|
||||
## Files Created
|
||||
|
||||
- `.gitea/workflows/continuous_integration.yaml` — 12-job CI workflow (C-12)
|
||||
|
||||
## Files Modified (FR-21 — lint fixes)
|
||||
|
||||
- `frontend/src/components/cms/AddCmsInstanceDialog.tsx` — close-triggered reset moved from a
|
||||
`useEffect` into a `handleOpenChange` wrapper (event response, not a render synchronization)
|
||||
- `frontend/src/components/users/InviteUserDialog.tsx` — same pattern, plus reordered `useForm()`
|
||||
before its use
|
||||
- `frontend/src/pages/SettingsPage.tsx` — replaced the prop-sync effect with the "adjust state
|
||||
during render" pattern (comparing fetched `availability` against a tracked previous value)
|
||||
- `frontend/src/components/cms/SetStatusDialog.tsx` — `watch('status')` replaced with
|
||||
`useWatch({ control, name: 'status' })`; the instance-driven reset effect (which the React
|
||||
Compiler plugin could not previously analyze past the `watch()` incompatibility) split into a
|
||||
render-time sync (on `instance` change) and a `handleOpenChange` wrapper (on close)
|
||||
- `frontend/src/components/ui/badge.tsx` — added an inline `eslint-disable-next-line
|
||||
react-refresh/only-export-components`, matching the existing precedent in `button.tsx` rather than
|
||||
introducing a new file-splitting convention
|
||||
|
||||
## Files Modified (FR-22 — package pins, closing OPEN-03)
|
||||
|
||||
- `src/SlpModularCms.Core/SlpModularCms.Core.csproj` — added `Microsoft.OpenApi` `2.11.0` and
|
||||
`System.Security.Cryptography.Xml` `10.0.10`
|
||||
- `src/SlpModularCms.Modules.Master.Tests/SlpModularCms.Modules.Master.Tests.csproj` and
|
||||
`src/SlpModularCms.Modules.Availability.Tests/SlpModularCms.Modules.Availability.Tests.csproj` —
|
||||
added `System.Security.Cryptography.Xml` `10.0.10` (both projects carry their own direct
|
||||
`Microsoft.AspNetCore.DataProtection` reference for testing, an independent path to the vulnerable
|
||||
transitive version that Core's pin alone does not reach)
|
||||
|
||||
## What the CI Workflow Implements
|
||||
|
||||
Twelve jobs: `config` (env→output passthrough), the six blocking gates (`backend-build`,
|
||||
`backend-test`, `vulnerability-scan`, `frontend-build`, `frontend-test`, `frontend-lint`),
|
||||
`publish-test`/`publish-production` (environment-specific `dotnet publish`, each running the
|
||||
REF-U5-01 Umami-origin drift check first), and `deploy-test`/`deploy-production` (call
|
||||
`deploy-scp.yaml` from U6 with `secrets: inherit`).
|
||||
|
||||
Triggers: `pull_request` (validation only — the six gates, no publish/deploy jobs run since none of
|
||||
them are reachable without `push`-to-`master` or `workflow_dispatch`), `push` to `master`
|
||||
(validation + automatic test deploy), `workflow_dispatch` with `deploy_production` (default `false`).
|
||||
|
||||
## New Decisions Raised During This Unit
|
||||
|
||||
**REF-U5-01** — the design record said the REF-U3-01 gate compares "both values" without saying
|
||||
how the CI job would read the backend side. It can't: per D-16, the live
|
||||
`SecurityHeaders:AllowedScriptOrigins` is a **host** environment variable, invisible to the Gitea
|
||||
runner, exactly the class of problem REF-U3-01 already exists to solve for the frontend's Umami ID.
|
||||
Resolved by adding `SECURITY_ALLOWED_SCRIPT_ORIGINS_TEST` / `_PRODUCTION` Gitea variables that must
|
||||
be **manually kept in sync** with the corresponding host env var — carried to Operations host-setup
|
||||
documentation as an explicit two-places-must-match note, not silently assumed to always agree.
|
||||
|
||||
**Artifact model clarified**: unlike the reference project (where the frontend `dist/` folder *is*
|
||||
the deployable artifact), this repository's admin SPA is embedded into the .NET publish output by
|
||||
the existing `BuildAndCopyAdminFrontend` MSBuild target. The CI workflow's `frontend-build` gate
|
||||
therefore validates the frontend independently (PR-time signal only); the actual per-environment
|
||||
deployable artifacts are produced by `publish-test` / `publish-production`, which set the
|
||||
environment's `VITE_*` variables on the `dotnet publish` process so the embedded frontend build
|
||||
picks them up exactly as it would during a local `dotnet publish`.
|
||||
|
||||
## Gitea Variables and Secrets Introduced by This Unit
|
||||
|
||||
Beyond what `infrastructure-design.md` § 6 already defined for U6:
|
||||
|
||||
| Variable | Scope | Purpose |
|
||||
|---|---|---|
|
||||
| `VITE_SENTRY_DSN` | shared | Frontend Sentry DSN, same value both environments (D-19 — one Sentry project) |
|
||||
| `VITE_UMAMI_SCRIPT_URL` | shared | Umami script host, same value both environments (D-24 — one Umami instance) |
|
||||
| `VITE_UMAMI_WEBSITE_ID_TEST` / `_PRODUCTION` | per-environment | Umami website entry ID |
|
||||
| `SECURITY_ALLOWED_SCRIPT_ORIGINS_TEST` / `_PRODUCTION` | per-environment | REF-U5-01 gate input — must mirror the host's `SecurityHeaders__AllowedScriptOrigins__0` |
|
||||
|
||||
## Verification (Step 13.5 — this unit's own check)
|
||||
|
||||
- **Lint (FR-21)**: `pnpm run lint` — 0 problems (was 6; 2 more found by drift, all fixed)
|
||||
- **Vulnerability scan (FR-22)**: `dotnet list SlpModularCms.sln package --vulnerable
|
||||
--include-transitive` — clean across all 10 projects (was 2 packages, multiple advisories each)
|
||||
- **Backend build**: `dotnet build SlpModularCms.sln -c Release` — 0 errors
|
||||
- **Backend tests**: 372 passed, 0 failed (196 Core, 82 Availability, 57 Master, 37 Identity) —
|
||||
unchanged from the Round 2 baseline, confirming the package pins introduced no regressions
|
||||
- **Frontend type-check**: `npx tsc -b` — clean
|
||||
- **Frontend tests**: 237 passed, 0 failed — unchanged from the Round 2 baseline, confirming the
|
||||
dialog refactors preserved behaviour
|
||||
- **Frontend build**: `pnpm run build` — succeeds
|
||||
- **CI workflow YAML**: parsed successfully — 12 jobs, `deploy-production` confirmed unreachable
|
||||
without `workflow_dispatch` + the flag
|
||||
|
||||
No C# test project applies to `.gitea/workflows/` itself (pipeline configuration, same exception as
|
||||
U6 and the Slave API). Real execution — an actual Gitea instance running the workflow against the
|
||||
Pi — is out of scope until Operations, matching U6's boundary.
|
||||
|
||||
## Deferred to Operations (not built here, by design)
|
||||
|
||||
- Actual values for all Gitea variables and secrets listed above and in
|
||||
`infrastructure-design.md` § 6
|
||||
- Keeping `SECURITY_ALLOWED_SCRIPT_ORIGINS_*` in sync with the host's real CSP configuration
|
||||
(REF-U5-01)
|
||||
- Real pipeline run against the actual Gitea instance and self-hosted runner
|
||||
Reference in New Issue
Block a user