From d9cdff81549db3d336f29631ca3f71eaf9fd36c8 Mon Sep 17 00:00:00 2001 From: Timothy Date: Sat, 27 Jun 2026 01:29:12 +0200 Subject: [PATCH] docs: record Central Package Management + scheduled vuln scan (#14) [skip ci] - ci-cd.md: new "Dependency management" section (CPM, NuGet audit, scheduled dependency-scan.yml); Dockerfile notes now list Directory.Packages.props and why it's required before restore. - CLAUDE.md: dependency convention (edit central Directory.Packages.props, never re-add Version=); correct the test framework note (NUnit, not xUnit). Co-Authored-By: Claude Opus 4.8 --- CLAUDE.md | 3 ++- docs/ci-cd.md | 43 +++++++++++++++++++++++++++++++++---------- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 863ee7f7c..c51837810 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -56,7 +56,8 @@ docker build -f docker/Dockerfile -t ersatztv:dev . - Follow existing MediatR CQRS pattern for new features - Domain logic in `ErsatzTV.Core`, infrastructure in `ErsatzTV.Infrastructure` - Keep Blazor pages thin — delegate to MediatR handlers -- Test with xUnit (existing test projects) +- Test with **NUnit** + Shouldly + NSubstitute (the existing `*.Tests` projects); xUnit is **not** used here +- **Dependencies use Central Package Management**: versions live in the repo-root `Directory.Packages.props`; csproj reference packages by name only. Add/upgrade by editing the central `` — never put `Version=` back on a `` (trips `NU1008`). See `docs/ci-cd.md` → Dependency management. - **Versioning**: release tags are `vYY..` (year · sequential release-within-year · patch) — inherited from upstream, **not** year.month. `v26.3.1` = our infra rebuild of upstream 26.3.0 (no app changes); `v26.4.0` is reserved for the first release with app changes. Never `[skip ci]` a commit you'll tag (it suppresses the release build). Full policy: `docs/ci-cd.md` → Versioning & releases. - Backlog tracked via [Gitea Issues](http://192.168.1.95:3000/timothy/ersatztv/issues) diff --git a/docs/ci-cd.md b/docs/ci-cd.md index 01d362ee4..ac3798df5 100644 --- a/docs/ci-cd.md +++ b/docs/ci-cd.md @@ -73,19 +73,42 @@ the image build. - Base image: **`192.168.1.95:3000/timothy/ersatztv-ffmpeg:7.1.1`** (our Gitea fork of the archived `ghcr.io/ersatztv/ersatztv-ffmpeg`). FFmpeg 8 upgrade is backlogged: base image → ersatztv-ffmpeg#4, app-side compat → ersatztv#9. -- Copies `Directory.Build.props`, `Directory.Build.targets`, `global.json`, - `.editorconfig` before `dotnet restore` so the image build uses the same MSBuild - config, SDK pin, and analyzer severities as local/CI builds (it previously copied - only `*.sln`). +- Copies `Directory.Build.props`, `Directory.Build.targets`, `Directory.Packages.props`, + `global.json`, `.editorconfig` before `dotnet restore` so the image build uses the same + MSBuild config, central package versions, SDK pin, and analyzer severities as local/CI + builds (it previously copied only `*.sln`). `Directory.Packages.props` is **required** + here: under Central Package Management the csproj carry no inline versions, so the + image's restore fails (`NU1015`) without the central manifest. - amd64-only (jazz is x86_64). No arm32/arm64, no DMG/exe artifacts, no GHCR/DockerHub. -## NuGet audit +## Dependency management (Central Package Management + scans) -.NET 10 runs NuGet audit on restore. Several projects set `TreatWarningsAsErrors=true`, -so vulnerable transitive packages failed the build. `Directory.Build.props` demotes -low/moderate/high advisories (NU1901-1903) to warnings and promotes NU1904 (critical) -to an error in **every** project via `WarningsAsErrors`. Underlying vulnerable deps are -tracked in ersatztv#8. +**Central Package Management (CPM)** — package versions live in a single repo-root +`Directory.Packages.props` (`ManagePackageVersionsCentrally=true`); the per-project +csproj reference packages by name only (no `Version=`). One source of truth, atomic +one-line bumps, and cross-project version drift is structurally impossible. To add or +change a dependency, edit the `` entry centrally — never put a `Version=` +back on a `` (that trips `NU1008`). The Docker build must copy this file +before restore (see Dockerfile notes). The `.mcp/` vendored tool (gitignored, not in the +solution) keeps inline versions via a **local-only** `.mcp/Directory.Packages.props` +opt-out (`ManagePackageVersionsCentrally=false`). (ersatztv#14) + +**NuGet audit** — .NET 10 runs NuGet audit on restore. Several projects set +`TreatWarningsAsErrors=true`, so vulnerable transitive packages failed the build. +`Directory.Build.props` demotes low/moderate/high advisories (NU1901-1903) to warnings +and promotes NU1904 (critical) to an error in **every** project via `WarningsAsErrors`. +Underlying vulnerable deps are tracked in ersatztv#8. + +**Scheduled vulnerability scan** — `.gitea/workflows/dependency-scan.yml` runs weekly +(cron `0 6 * * 1`) + on `workflow_dispatch`: `dotnet list package --vulnerable +--include-transitive` over the **full** solution (incl. Scanner, which the image build +strips). `dotnet list` exits 0 even with findings, so the step (`bash -euo pipefail`) +greps for the "has the following vulnerable packages" marker and fails the run if present. +Detection only — it surfaces advisories on a schedule, a Gitea-native stand-in for +Dependabot; it does **not** open update PRs (that's Renovate — server-management#484). +Gitea registers `schedule` triggers only from the default branch, so the cron starts +after merge to `main`; use `workflow_dispatch` to run on demand. **Expected to be RED +until ersatztv#8 clears the current advisories.** (ersatztv#14) ## Registry