Build ErsatzTV Image / CI image pin matches docker/ci (pull_request) Successful in 6s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 5s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 6s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 16s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 14s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m6s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 14m42s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Functional E2E (curl contracts) (pull_request) Successful in 13m43s
Review fixes from the cold adversarial pass on PR #411. Verdict was MERGEABLE, but one
HIGH is a genuine defect worth the round-trip.
[decisions-edit] rationale: the 12 "modified" lines in docs/decisions.md are lines THIS PR
added in its own previous commit (e33205e8), not a settled entry on main. The hook diffs
against HEAD; net against origin/main this file is still a pure insertion (65 added, 0
deleted -- verified, and CI's decisions-guard diffs against the PR base). It is also the
sanctioned reason regardless: the entry as first written contained a factual error (see
HIGH below).
HIGH -- `--memory=2g` alone silently grants 2g of SWAP as well. Docker defaults an unset
--memory-swap to 2x --memory. Verified live on bumblebee: `--memory=2g` alone gives
memory.max=2147483648 AND memory.swap.max=2147483648; adding `--memory-swap=2g` gives
memory.swap.max=0. Capping RAM while permitting swap is close to the worst outcome on the
one host whose swap thrash is the entire reason for the cap -- and a swapping mysqld
mid-DDL is exactly the pathology behind the known `Command Timeout expired` migrations
flake, so the naive cap could have made that flake worse rather than better. The comment
promising "if this ever OOM-kills the service" described a failure mode that would not
have happened first: it would have swapped, silently. Now --memory-swap=2g (swap off);
prefer a loud OOM over silent degradation.
The same 2x applies to the runners' 10g job slots (10 GiB RAM + 10 GiB swap each), which
is a plausible direct mechanism for the incident's 21 GiB swapped. That is #604's
boundary -- reporting it there.
MEDIUM -- `if: always()` does not make a step advisory. It controls whether the step RUNS,
not whether its failure fails the job, and defaults.run.shell: bash means -e is on, so a
failed cat/redirect in an informational step could redden a green test job. Use
continue-on-error: true, matching the neighbouring Coverage summary step.
MEDIUM -- the peak step must run LAST. memory.peak read at step N reports the peak only up
to N, so sitting before Coverage summary excluded reportgenerator's memory -- under-
reporting the very number #604 sizes caps on. Moved after it.
LOW -- dependency-scan.yml gets the env too; workflow env does not cross workflow files,
and that one runs unattended on a cron against the prod media host.
NIT -- Dockerfile ENV moved below the openapi-generator wget: an ENV invalidates every
layer under it, and that ~30MB download is deliberately ordered early to stay cached
(#190). Nothing between it and restore compiles, so this placement is free.
NIT -- docs no longer imply this shrinks `format`. dotnet format loads Roslyn in-process
via MSBuildWorkspace and never spawns csc, so its 3.95 GiB is untouched; sizing the small
lane on the assumption it shrank would be a trap.
Also record honestly that 543 MiB is init+idle rather than the 787-migration replay (so 2g
is a measured floor + headroom, not a measured ceiling -- the migrations job going green
is what validates it) and that --cpus=2 is judgement with no measurement behind it. The
entry criticises dressing estimates as measurements; it should not do that itself.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
70 lines
3.0 KiB
YAML
70 lines
3.0 KiB
YAML
name: Dependency vulnerability scan
|
|
|
|
# Scheduled NuGet advisory scan — a Gitea-native stand-in for Dependabot (ersatztv#14).
|
|
# Surfaces vulnerable direct/transitive packages on a schedule instead of only when a
|
|
# `dotnet restore` happens to break. This is DETECTION ONLY; automated update PRs are
|
|
# tracked separately (self-hosted Renovate — server-management#484).
|
|
#
|
|
# Scans the FULL solution (including the Scanner project, which the image build strips)
|
|
# so coverage isn't narrower than the code we ship.
|
|
#
|
|
# NOTE: Gitea runs `schedule` triggers only from the default branch (main); the workflow
|
|
# must be merged to main before the cron registers. Use `workflow_dispatch` to run on demand.
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
schedule:
|
|
# Mondays 06:00 UTC
|
|
- cron: '0 6 * * 1'
|
|
|
|
# Independent of the build pipeline's concurrency group; a stale scan can be cancelled.
|
|
concurrency:
|
|
group: ersatztv-depscan
|
|
cancel-in-progress: true
|
|
|
|
# No persistent MSBuild/Roslyn servers (ersatztv#406). Workflow `env:` does not cross workflow
|
|
# files, so docker-build.yml's copy of these does not apply here and this has to be repeated.
|
|
# Smaller stakes than the build pipeline — `dotnet restore` + `dotnet list` are MSBuild-driven and
|
|
# never invoke csc, so this is lingering worker nodes (hundreds of MiB), not a 7.8 GB VBCSCompiler.
|
|
# Worth setting anyway: this runs unattended on a Monday 06:00 cron against the same host that runs
|
|
# prod media, and node reuse keeps workers alive ~15 min after the job.
|
|
env:
|
|
UseSharedCompilation: "false"
|
|
DOTNET_CLI_USE_MSBUILD_SERVER: "0"
|
|
MSBUILDDISABLENODEREUSE: "1"
|
|
|
|
jobs:
|
|
scan:
|
|
name: NuGet vulnerable packages
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup .NET
|
|
uses: actions/setup-dotnet@v4
|
|
with:
|
|
dotnet-version: '10.0.x'
|
|
|
|
- name: Restore
|
|
run: dotnet restore ErsatzTV.sln
|
|
|
|
- name: Scan for vulnerable packages (direct + transitive)
|
|
# bash + `set -euo pipefail` so a failing `dotnet list` (e.g. the audit source
|
|
# is unreachable while restore served from cache) fails the job instead of
|
|
# falling through to a false "no vulnerable packages" green.
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
echo "Running: dotnet list package --vulnerable --include-transitive"
|
|
dotnet list ErsatzTV.sln package --vulnerable --include-transitive 2>&1 | tee depscan.txt
|
|
# `dotnet list package --vulnerable` exits 0 even when advisories exist, so detect
|
|
# findings by the report marker and fail the run if any are present. Expect this to
|
|
# be RED until ersatztv#8 clears the current NCalcSync / SQLitePCLRaw advisories;
|
|
# after that, a red run means a NEW advisory has appeared.
|
|
if grep -q "has the following vulnerable packages" depscan.txt; then
|
|
echo "::error::Vulnerable NuGet packages detected — see report above (tracked: ersatztv#8)."
|
|
exit 1
|
|
fi
|
|
echo "No vulnerable packages found."
|