Files
pad/cmd/depages
xarmian a55d15dbea feat(ci): an advisory dependency-age check (cmd/depages) (TASK-1391) (#2014)
* feat(ci): an advisory dependency-age check (cmd/depages) (TASK-1391)

Companion to Dependabot's cooldown (TASK-1390), which holds a release for 7
days before PROPOSING it but cannot see a version added by hand. cmd/depages
dates every Go module (go.sum), npm package (web/package-lock.json, registry
packages) and action pin (`uses: owner/repo@<sha>`) a change adds, from the Go
module proxy, the npm packument and the GitHub commits API, and fails when
one is younger than --days and not allowed in .github/dep-age-allow.txt
(which requires a reason per line). --base <ref> checks only what is new
since the ref; a dependency that cannot be dated is a warning, not a failure.

CI runs it on pull requests as "Dependency age (advisory)", against the
merge commit's first parent. NOT a required check (lead ruling): red means
wait or allow with a reason.

Rewritten rather than promoted: the /tmp scanner the task names is gone. Its
edge cases are kept (pseudo-versions included, scoped npm names, sub-actions
sharing a repo SHA, the full packument for per-version times). A real run
against main as of 2026-09-01 dated 185 dependencies and flagged one:
golang.org/x/net v0.60.0, #1922's security bump, the case the allowlist is for.

* fix(ci): depages covers aliases, composite actions and lockfile versions; the token stays on GitHub (TASK-1391)

Codex round 1:
- An npm alias is checked as the package it installs (the entry's name).
- A lockfile older than v2 is refused instead of read as empty.
- Every YAML file under .github is scanned, so a local composite action's
  uses: pins count too. docker:// images are out of scope and documented.
- GH_TOKEN is sent only to api.github.com, whatever DEPAGES_GITHUB_API says.
- The base-diff test now covers a go.sum module, a bumped npm package and a
  composite action's new pin in one change.

* fix(ci): depages scans action.yml anywhere in the repo; its README tables the coverage (TASK-1391)

Codex round 2: a local composite action can live outside .github. Every
action.yml / action.yaml in the repository is scanned too (node_modules and
.git skipped). Not changed, with the reasons in the README's coverage table:
docker:// images (no SHA, no publish time) and dating a pin by its commit
(the threat is fresh code, and an old commit is old code).

* feat(ci): depages prints the allowlist line to paste for each young version (TASK-1391)

Lead, on the first real run flagging #1922's x/net security bump: the job's
output now says the check is advisory and prints, per flagged version, the
exact .github/dep-age-allow.txt line with a <reason> placeholder.

* test(models): classify cmd/depages' getJSON in the raw-decode guard (TASK-1391)

CI: TestReservedFieldDecodeSitesAreClassified found cmd/depages/main.go's
getJSON decoding into an interface. It decodes registry and GitHub API answers
into their own structs, never a collection definition; allow-listed with that
reason, and the receipt count goes 26 -> 27.
2026-10-10 04:48:59 -04:00
..

depages

Flags dependencies younger than a threshold (TASK-1391). It is the companion to Dependabot's cooldown (TASK-1390, .github/dependabot.yml), which holds a release for 7 days (14 for a major) before proposing it but cannot see a version added by hand.

go run ./cmd/depages --days 7                 # every dependency in the tree
go run ./cmd/depages --days 7 --base origin/main   # only what is new since origin/main

It checks Go modules (go.sum), npm packages (web/package-lock.json) and GitHub Actions pins (uses: owner/repo@<sha> in .github/workflows), dating each from the Go module proxy, the npm registry packument and the GitHub commits API (set GH_TOKEN to avoid the unauthenticated rate limit).

What it covers

Source Covered Dated by
Go modules (go.sum, content lines; pseudo-versions included) yes the module proxy's .info time
npm packages from the registry (web/package-lock.json, lockfile v2/v3; an alias as the package it installs) yes the packument's per-version time
npm git or tarball dependencies no they have no registry publish time
A v1 lockfile refused, exit 2
Action pins owner/repo[/path]@<sha> in any YAML under .github, and in any action.yml / action.yaml in the repo yes the commit's committer date
docker:// action images no not pinned by a commit SHA; no publish time here

An action is dated by its COMMIT, deliberately: the threat is freshly published code, and a pin to an old commit is old code whatever tag points at it.

Exit status: 0 when nothing is too young (a dependency that cannot be dated is printed as a warning, not a failure); 1 when one is younger than --days and not allowed; 2 on a usage or local error.

CI runs it on every pull request as Dependency age (advisory), against the base branch (--base HEAD^1 on the merge commit). It is not a required check: a red result is a prompt, not a block.

Allowing a young version

A red run prints the exact line for each flagged version. Add it to .github/dep-age-allow.txt, putting the reason in place of <...>:

<go|npm|action> <name>@<version>  <reason>

for example npm @tiptap/core@3.31.5 coordinated Tiptap bump (security fix). The reason is required, so every exception says why. Remove the line once the version is older than the threshold.