Skip to content

Gate dependency resolution to versions at least 7 days old#2576

Open
TalZaccai wants to merge 5 commits into
mainfrom
talzacc/pnpm-minimum-release-age
Open

Gate dependency resolution to versions at least 7 days old#2576
TalZaccai wants to merge 5 commits into
mainfrom
talzacc/pnpm-minimum-release-age

Conversation

@TalZaccai

@TalZaccai TalZaccai commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Why

Dependency resolution can pick a version that's too fresh for the environment
that installs it, causing red CI:

  • Feed lag: the ADO smoke pipeline's internal feed trails public npm by
    hours, so a just-bumped version fails with ERR_PNPM_FETCH_404 (qs@6.15.3 in
    fix: remediate Dependabot security alerts (2026-06-25) #2556) while every public-npm GitHub lane is green.
  • Release-age policy: a new policy blocks installing any version published < 7
    days ago, so lockfiles generated on runners can fail everywhere else.

This fixes both at resolution time.

Changes

  • ts/pnpm-workspace.yaml — set minimumReleaseAge: 10080 (7 days) so pnpm
    resolves the newest satisfying version that's also ≥ 7 days old. Frozen
    installs skip the check; non-frozen paths (lockfile generation and the shell's
    pnpm deploy) enforce it.
  • js-yaml^4.2.0 (config, shell, tools package.json +
    regenerated pnpm-lock.yaml) — ^4.3.0 matched only the < 7-day 4.3.0, so
    pnpm deploy failed with ERR_PNPM_NO_MATURE_MATCHING_VERSION; ^4.2.0
    resolves the mature 4.2.0 and still adopts 4.3.0 once it ages in.
  • ts/tools/scripts/fix-dependabot-alerts.mjsdefer a security fix
    when its only satisfying version is < 7 days old (non-fatal, auto-retried once
    it matures); threshold from --min-release-age-days /
    DEPENDABOT_MIN_RELEASE_AGE_DAYS / minimumReleaseAge, 0 disables. Also made
    import-safe for unit tests.
  • .github/workflows/fix-dependabot-alerts.yml — export
    DEPENDABOT_MIN_RELEASE_AGE_DAYS=7 so the gate applies in every workspace
    (incl. those without a pnpm-workspace.yaml), and report deferred fixes in the
    PR body, commit message, and job summary.

Not changed

The ADO pipelines and their WIF / Azure auth are untouched, and existing lockfile
entries aren't rewritten.

TalZaccai and others added 3 commits June 30, 2026 19:24
Add `minimumReleaseAge: 10080` (7 days) to ts/pnpm-workspace.yaml so pnpm
will not resolve dependency versions that were published less than a week
ago. This keeps freshly published transitive versions out of the lockfile
until they have propagated to downstream mirrors/feeds and satisfied the
organization's minimum package-age window, and it reduces exposure to
compromised just-published releases. The constraint applies at resolution
time to all direct and transitive dependencies; installs from an existing
frozen lockfile are not re-resolved and are unaffected.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The pnpm-workspace minimumReleaseAge gate makes pnpm refuse to resolve any
dependency version published less than 7 days ago (and a device/registry
release-age policy enforces the same at install time). When the only version
that satisfies a security advisory is still too new, writing an override for it
produces a lockfile that fails to resolve.

Teach fix-dependabot-alerts.mjs to detect this case and defer the fix instead
of applying it: assessFixMaturity checks npm publish times for versions
satisfying the >=patched range, and a fix is only applied when a satisfying
version is already old enough. Deferred fixes are reported (console + JSON) and
re-attempted automatically on a later run, and are kept out of the resolved set
so the workflow neither applies them nor fails on them. The age floor is read
from pnpm-workspace.yaml (with flag/env overrides); 0 disables the feature.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The fix-dependabot-alerts script now reports fixes it deferred because no
satisfying version is old enough yet. Parse the new .summary.deferred count,
.deferred[] entries (with per-package eligibility dates), and .minReleaseAgeDays
from the discovery run, and surface them in the PR body, the commit message, and
the job summary so a waiting security fix is visible rather than silent. All new
jq reads tolerate older JSON that lacks these fields.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a “minimum package release age” gate to prevent pnpm from resolving (and lockfiles from adopting) dependency versions that are too newly published for downstream feeds/policies, and updates the automated Dependabot remediation flow to defer fixes that would require too-new versions.

Changes:

  • Add minimumReleaseAge: 10080 (7 days) to the ts/ pnpm workspace to gate resolution to sufficiently-aged versions.
  • Extend fix-dependabot-alerts.mjs to detect when no eligible (old-enough) patched version exists and mark the fix as deferred (reported but not applied), including JSON output support for CI.
  • Enhance the fix-dependabot-alerts workflow to surface deferred fixes in the PR body, commit message, and job summary.
Show a summary per file
File Description
ts/tools/scripts/fix-dependabot-alerts.mjs Adds release-age deferral logic (via npm publish times) and makes the script importable for unit testing by gating main() execution.
ts/pnpm-workspace.yaml Enables pnpm’s minimumReleaseAge to keep too-fresh versions out of the ts/ lockfile.
.github/workflows/fix-dependabot-alerts.yml Reports deferred fixes in CI outputs (PR body/commit message/summary).

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 3/3 changed files
  • Comments generated: 2
  • Review effort level: Low

Comment thread .github/workflows/fix-dependabot-alerts.yml
Comment thread ts/tools/scripts/fix-dependabot-alerts.mjs Outdated
TalZaccai and others added 2 commits July 1, 2026 00:13
The ^4.3.0 pin is satisfied only by js-yaml 4.3.0, which was published
less than 7 days ago. With minimumReleaseAge enabled, pnpm deploy's
non-frozen re-resolution had no mature match and failed with
ERR_PNPM_NO_MATURE_MATCHING_VERSION. Loosening to ^4.2.0 lets the gate
resolve the mature 4.2.0 while still allowing 4.3.0 once it ages in.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two fixes for the Copilot reviewer comments on the min-release-age work:

- Export DEPENDABOT_MIN_RELEASE_AGE_DAYS=7 in the fix-dependabot-alerts
  workflow so the deferral gate applies in every workspace, including ones
  without a pnpm-workspace.yaml (e.g. docs/) that previously fell back to 0
  (disabled) and could still pin versions under 7 days old.

- Make fix-dependabot-alerts.mjs safe to import for unit tests: hoist the
  direct-invocation check to IS_MAIN and gate all import-time argv
  validation and process.exit() calls (unknown flags, --help, incompatible
  flag combos) behind it, so a test runner injecting --* argv can't kill the
  process on import. readMinReleaseAgeDays now accepts explicit
  argv/env/root for testing while defaulting to the previous behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@TalZaccai TalZaccai marked this pull request as ready for review July 1, 2026 19:08
@TalZaccai TalZaccai requested a review from robgruen July 1, 2026 19:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants