Add CI pipeline, NuGet lock files and Dependabot (R-04) - #14
Merged
Conversation
Every gate this repo has was enforced by nothing. ADR-010 made backend warnings build failures and ADR-012 made the frontend type-check and lint, both explicitly so this item would have something to call. Adds .github/workflows/ci.yml running two parallel jobs on every PR to main: backend restores in locked mode, builds, tests and checks for vulnerable packages; frontend runs npm ci, typecheck, lint, build and a blocking npm audit. Adds .github/dependabot.yml for npm, nuget and github-actions, and pins Node in .nvmrc plus engines. NuGet now restores from six committed packages.lock.json files, so the transitive closure is reproducible and an unexpected change fails with NU1004 instead of resolving silently. CI builds Debug, not Release, and this is load-bearing. ADR-005 makes the IntegrationTest environment throw in RELEASE builds, so a Release build fails all 14 integration tests by design: 84 passing becomes 70. A comment in the workflow records this so it is not "fixed" back. The vulnerable-package step parses output rather than trusting the exit code, because dotnet list package --vulnerable exits 0 even when it finds something. A step that cannot fail is the exact failure this item exists to remove. Every gate verified by negative test: NU1004 on a lock mismatch, NU1903 on a vulnerable package, CS0219 on an unused local, a non-zero exit on an inverted assertion, TS2322 on a bad annotation, no-console on a console.log. Closes INFRA-01, INFRA-06, BUILD-07 and R-04, completing Phase 1. Adds INFRA-07: the checks run but are not yet required to merge, which is a branch-protection setting rather than anything a PR can contain. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ticks the one acceptance criterion that could not be verified locally: both jobs run on a PR. Run 31275373275 on PR #14, 84/84 on Linux. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The CI pipeline.
.github/workflows/ci.ymlruns the full pre-merge checklist on every PR tomain, plus NuGet lock files, Dependabot for three ecosystems, and a pinned Node version.Closes
R-04,INFRA-01,INFRA-06,BUILD-07— Phase 1 of the roadmap is now complete.Why
Spec: docs/specs/002-ci-pipeline.md · ADR-013.
ADR-010 made backend warnings build failures. ADR-012 made the frontend type-check and lint. Both were built explicitly so this item would have something to call — and until now nothing called either.
BUILD-03is the standing proof that this matters:npm run lintcould not start for eleven months and nobody noticed.Layers touched
Docs / build configuration / CI. No file under
Domain/,Application/,Infrastructure/,Api/, orrecipe-manager-frontend/src/is modified.Migration
None.
Contract impact
None.
What lands
ubuntu-latest. Backend: locked restore → build → test → vulnerable-package check. Frontend:npm ci→ typecheck → lint → build →npm audit --audit-level=highRestorePackagesWithLockFileinDirectory.Build.props; six committedpackages.lock.json;--locked-modein CInpm,nuget,github-actions, grouped, weekly.nvmrc(24) +engines(>=20), workflow readsnode-version-fileThree findings that changed the implementation
1. CI must build Debug, not Release — and this is load-bearing.
Choosing
--configuration Releasewas reflex. ADR-005 makesProgram.Mainthrow"IntegrationTest environment is not allowed in RELEASE builds"— a guard added in 2025 so that setting that environment on a real deployment cannot start the app with no database.WebApplicationFactoryuses exactly that environment name, so a Release build fails all 14 integration tests by design. Measured: 84 passing → 70. The workflow carries a comment so nobody "improves" it back. Nothing is lost:TreatWarningsAsErrorsis unconditional (ADR-010), so the warning gate is identical in Debug.A security guard written about deployment turned out to dictate a CI flag three roadmap items later, with nothing linking the two.
2.
dotnet list package --vulnerableexits 0 even when it finds vulnerabilities.Verified locally. Written the obvious way, that step would have been a green tick that could never go red — the exact
BUILD-03failure mode, inside the code meant to prevent it. The step parses the output instead, and the comment says why so it does not get "simplified" later.3. Actions are pinned by commit SHA, not tag.
A tag is mutable and can be repointed at new code — the mechanism behind the 2025
tj-actions/changed-filescompromise. SHAs were resolved from the GitHub API, not written from memory. Thegithub-actionsDependabot ecosystem is what keeps them from rotting; SHA-pinning without it is a decision you regret in six months.Verification
Every command below was run locally, and the numbers are observed rather than inherited.
Negative tests — each gate observed rejecting
Per the 2026-08-04 decisions-log entry: a gate never seen rejecting anything is indistinguishable from no gate.
FluentResults3.16→3.15NU1004, restore refusesNewtonsoft.Json@12.0.1NU1903at restore (ADR-010 fires first)Recipe.csCS0219as errorEntityTestsassertiondotnet testexits 1const x: number = "..."TS2322, build exits 1 before Viteconsole.logHomePage.tsxno-consoleerrorEvery change was reverted;
git statusconfirms no test artifact survived.Not verified locally, by nature: that both jobs actually start on a PR. That is what this PR's own checks demonstrate — please confirm they appear and go green before merging.
Architecture impact
ADR-013 appended to docs/architecture.md. An ADR is warranted not because CI is structural in the layering sense, but because
RestorePackagesWithLockFileinDirectory.Build.propschanges restore behaviour for every project — the same blast radius as ADR-010 and ADR-011.No new dependency in either ecosystem. Dependency direction untouched.
Security review
npm audit --audit-level=highfails the frontend job, andNU1903fails restore. Dependabot raises PRs weekly.permissions: contents: readat the top level; no secrets are referenced, so a fork PR gains nothing by running the workflow, and nothing here can read the database password.pull_request, deliberately notpull_request_target— the latter runs a writable token in the base repo's context against untrusted PR code, which is the standard way CI becomes an exfiltration path.SEC-01andSEC-02remain the two Critical deploy-gate items. Every endpoint is still anonymous and world-writable; CI does not alter that.Known issues / roadmap
Docs updated:
README.md,CLAUDE.md,known-issues.md,roadmap.md,tech-stack.md,conventions.md,architecture.md(ADR-013),decisions-log.md,workflows/release-workflow.md, and three agent files.Action required from you — this is not fully delivered without it
Enable branch protection on
mainrequiring theBackendandFrontendchecks. The workflow makes the checks exist; only a repository setting makes them required, and no PR can contain that. Until then a red run can be merged past. Tracked asINFRA-07.While there: confirm Dependabot pull requests are enabled —
dependabot.ymlconfigures them, but the repository toggle governs.Follow-ups
R-06Testcontainers, now unblocked.R-07frontend tests; the workflow gains anpm teststep when atestscript exists.TEST-05/BUILD-06— agree a coverage number, fix the coverage script, then add a coverage step.INFRA-02/INFRA-03— versioning and rollback. CI publishes nothing.🤖 Generated with Claude Code