Skip to content

deps: update pnpm to 10.16.0 for minimumReleaseAge enforcement - #280

Merged
taoeffect merged 11 commits into
masterfrom
fix-pnpm-min-release-age
Jul 28, 2026
Merged

deps: update pnpm to 10.16.0 for minimumReleaseAge enforcement#280
taoeffect merged 11 commits into
masterfrom
fix-pnpm-min-release-age

Conversation

@pedrogaudencio

@pedrogaudencio pedrogaudencio commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator
  • update pnpm@10 to pnpm@10.16.0 in Dockerfiles
  • bump documented pnpm requirement in AGENTS.md to 10.16.0+ (minimum for minimumReleaseAge)

Fixes #176

AI Disclosure

Co-authored with: Fable 5

* update pnpm@10 to pnpm@10.34.3 in Dockerfiles to match packageManager
* bump documented pnpm requirement in AGENTS.md to 10.16.0+ (minimum for minimumReleaseAge)

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

Updates the Docker build environment and contributor documentation to align with the repository’s pinned pnpm version and to ensure pnpm’s dependency age-gating (minimumReleaseAge) can be enforced as intended (Fixes #176).

Changes:

  • Pin pnpm to 10.34.3 in Docker build images (matching package.json#packageManager).
  • Update AGENTS.md to document a pnpm minimum version that supports minimumReleaseAge.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
Dockerfile.rootless Pins globally installed pnpm to 10.34.3 during image build.
Dockerfile Pins globally installed pnpm to 10.34.3 during image build.
docker/forkana/Dockerfile Pins globally installed pnpm to 10.34.3 during image build.
AGENTS.md Updates documented pnpm prerequisite to reflect minimum version needed for dependency age gating.

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

Comment thread AGENTS.md
@pedrogaudencio

pedrogaudencio commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator Author

/review


AI review started.

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown

Advanced AI Review

  • Type: Agentic (crush)
  • Model: glm-5.2
Click to expand review

I'll investigate the context around this PR to provide a thorough review.Let me verify the pnpm configuration for the minimum release age feature and check the packageManager alignment.The minimumReleaseAge: 20160 enforcement is in pnpm-workspace.yaml. Now let me verify the factual claim about which pnpm version introduced minimumReleaseAge, and check for DRY issues across the Dockerfiles.Let me view the full Dockerfiles and check the packageManager field to assess DRY and corepack options.Let me check CI workflows and the Makefile for any pnpm version references that might also need syncing.I've gathered enough context. Here's my review.

Context verified

  • The actual minimumReleaseAge enforcement (minimumReleaseAge: 20160) lives in pnpm-workspace.yaml and is already in place — this PR is purely the version pin that makes the build images capable of honoring it. The factual claim in AGENTS.md is correct: minimumReleaseAge was introduced in pnpm 10.16.0.
  • package.json:3 declares "packageManager": "pnpm@10.34.3" and package.json:6 "pnpm": ">= 10.16.0", so the Dockerfile pin (10.34.3) and AGENTS.md minimum (10.16.0+) are both consistent. The two-version layered approach (min-supported vs. exact-reproducible) is intentional and correct.

1. 🟡 DRY violation — pnpm version now hardcoded in 3 Dockerfiles, diverging from CI's single-source pattern

  • Addressed
  • Dismissed

Pinning to an exact version is good for reproducibility, but it creates a new sync hazard: the literal 10.34.3 must now be kept manually in sync across four locations —

Dockerfile:22                      && npm install -g pnpm@10.34.3 \
Dockerfile.rootless:22             && npm install -g pnpm@10.34.3 \
docker/forkana/Dockerfile:21       && npm install -g pnpm@10.34.3 \
package.json:3                     "packageManager": "pnpm@10.34.3",

If someone bumps packageManager (e.g. via corepack use pnpm@x) and forgets the three Dockerfiles, the images silently build with a stale pnpm — exactly the kind of drift minimumReleaseAge pinning is meant to prevent.

Notably, CI already avoids this duplication: every workflow uses pnpm/action-setup@v4 with no version: field, so it resolves the version from package.json's packageManager automatically:

.github/workflows/pull-tests.yml:27        uses: pnpm/action-setup@v4
.github/workflows/pull-compliance.yml:38   - uses: pnpm/action-setup@v4

The Dockerfiles should follow the same single-source-of-truth approach via corepack, which ships with Node and reads packageManager on first invocation (the repo — including package.json — is COPYed at Dockerfile:26, before make build runs pnpm at Dockerfile:30). For each of the three files, replace:

RUN apk --no-cache add \
    build-base \
    git \
    nodejs \
    npm \
    && npm install -g pnpm@10.34.3 \
    && rm -rf /var/cache/apk/*

with:

RUN apk --no-cache add \
    build-base \
    git \
    nodejs \
    npm \
    && corepack enable \
    && rm -rf /var/cache/apk/*

This eliminates all three hardcoded versions; the exact pnpm release is then governed solely by package.json:3. (The npm package can stay — it's needed as a bootstrapper for corepack/fallback — and corepack will fetch the resolved pnpm binary on first use, which is fine since the build already requires network.)

If, for some reason, corepack can't be used in these alpine images, a weaker fallback is to at least add a comment next to each pin such as # keep in sync with package.json packageManager — but corepack is the real fix.


No 🔴 (bugs/security) issues found; the version numbers are internally consistent and the spelling/grammar in the changed strings is correct.


Review generated using glm-5.2 via Z.AI. Comment /review to re-run.

* remove duplicated hardcoded pnpm version from 3 Dockerfiles
* keep package.json as the single pnpm version source
* preserve npm in Alpine build deps for Corepack bootstrap
Copilot AI review requested due to automatic review settings July 24, 2026 19:02

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

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings July 24, 2026 20:40

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

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@pedrogaudencio

Copy link
Copy Markdown
Collaborator Author

@taoeffect approved! ✅

Confirmed that the pnpm version is set to 10.34.3 and that the age gate in pnpm-workspace.yaml contains exactly minimumReleaseAge: 20160 (14 days × 24 h × 60 min); same age gate for uv in pyproject.toml, contains exclude-newer = "14 days" under [tool.uv].

@pedrogaudencio
pedrogaudencio requested a review from taoeffect July 25, 2026 12:41
@taoeffect

Copy link
Copy Markdown
Member

@pedrogaudencio This PR doesn't seem to address my comment in any way as it doesn't touch .npmrc and there was no explanation given as to why.

@pedrogaudencio

Copy link
Copy Markdown
Collaborator Author

@pedrogaudencio This PR doesn't seem to address my comment in any way as it doesn't touch .npmrc and there was no explanation given as to why.

@taoeffect you're right, I missed addressing this but I skipped adding the directive in .npmrc intentionally: using pnpm 10.34.3 version is enough to set it in pnpm-workspace.yaml. Here are my Fable's findings):

  1. Is the workspace-level setting sufficient?

Yes. For the pnpm version this project uses, pnpm-workspace.yaml is the canonical and sufficient location:

• The project enforces pnpm via package.json: "packageManager": "pnpm@10.34.3" and "engines": { "pnpm": ">= 10.16.0" } — 10.16 is exactly the version where minimumReleaseAge was introduced as a pnpm-workspace.yaml setting.
• Per the pnpm docs, minimumReleaseAge in pnpm-workspace.yaml applies to all dependencies, including transitive ones, for every install in the workspace. There is no separate "global enforcement" step that requires .npmrc.
• It is also forward-compatible: pnpm ≥ 11 ignores `.npmrc` for these keys entirely and only reads them from pnpm-workspace.yaml (per-project) or the global config.yaml. Duplicating into .npmrc would create a dead/drifting copy on future upgrades.
  1. Is adding min-release-age=20160 to .npmrc necessary for SECURITY.md alignment?

No — and it would actually be incorrect:

• SECURITY.md specifies the policy as: "Node.js/frontend dependencies use pnpm `minimumReleaseAge: 20160` in `pnpm-workspace.yaml`." The current configuration matches the written policy verbatim; adding .npmrc entries goes beyond what the policy defines.
• Key/unit mismatch: min-release-age is an npm (≥ 11.10) setting whose value is in days. min-release-age=20160 would mean a ~55-year cooldown to npm, and pnpm would not read that key at all (pnpm's ini-style equivalent in 10.16–10.x is minimum-release-age, in minutes).
• npm never installs project dependencies here: all installs go through pnpm install (Makefile targets), and npm appears only to bootstrap pnpm itself (npm install -g pnpm@10.34.3 in the Dockerfile) and to install an unrelated CLI in one workflow. So an npm-side age gate would protect nothing in practice.
  1. Misconfiguration or standard behavior?

Standard behavior. The existing .npmrc contains only general, npm-compatible flags (audit=false, save-exact=true, etc.), while pnpm-specific supply-chain settings live in pnpm-workspace.yaml — this is the layout pnpm itself recommends since 10.16 and requires from v11 onward. The absence of an age-gating key in
.npmrc is expected, not a gap.

@taoeffect

taoeffect commented Jul 26, 2026

Copy link
Copy Markdown
Member

I'm not satisfied with Fable's response. If we have an .npmrc file it should contain the right settings. And otherwise why do we have it at all? It's just there adding confusion and should be deleted. What is its purpose? If it has one, add the delay to it too and also a comment saying that the file is mostly ignored (or only used for XYZ settings) and reference the actual file pnpm uses. If it has none, delete it.

If you decide to delete it just make sure that the pnpm file has all the stuff from npmrc like save-exact.

@pedrogaudencio

Copy link
Copy Markdown
Collaborator Author

• npm never installs project dependencies here: all installs go through pnpm install (Makefile targets), and npm appears only to bootstrap pnpm itself (npm install -g pnpm@10.34.3 in the Dockerfile) and to install an unrelated CLI in one workflow. So an npm-side age gate would protect nothing in practice.

.npmrc is there to configure pnpm's behaviour during its installation. Should I clarify this in a comment inside .npmrc itself or SECURITY.md/README.md Prerequisites section are more suitable?

@taoeffect

Copy link
Copy Markdown
Member

I'd do it inside of npmrc, and please add the min age 14 days to it as well

* added header comment clarifying that pnpm manages project dependencies.
* added minimum-release-age=20160 to enforce 14-day delay for new releases
Copilot AI review requested due to automatic review settings July 26, 2026 18:00

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

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Comment thread .npmrc Outdated
taoeffect

This comment was marked as outdated.

@taoeffect taoeffect left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Almost! Review ready:

Comment thread docker/forkana/Dockerfile Outdated
Comment thread .npmrc Outdated
@taoeffect

taoeffect commented Jul 26, 2026

Copy link
Copy Markdown
Member

/review (just curious to see if GLM-5.2 catches the .npmrc bug)


AI review started.

@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown

Advanced AI Review

  • Type: Agentic (crush)
  • Model: glm-5.2
Click to expand review

I'll investigate the codebase to verify the changes are consistent and correct.The .npmrc changes are already committed (c1cab3a955). Let me verify the critical question: does pnpm actually honor the minimum-release-age setting?Let me verify the critical question — whether pnpm actually honors the minimum-release-age setting, since the entire security policy hinges on this.pnpm does support minimumReleaseAge (added v10.16.0), so pinning to 10.34.3 and the AGENTS.md bump are correct. Now let me verify the .npmrc comment's accuracy and how pnpm is actually invoked.Let me verify consistency across the repo and confirm pnpm reads the setting from .npmrc.Critical finding — there's a pnpm-workspace.yaml that already defines this. Let me inspect it and SECURITY.md.This is a key discovery: minimumReleaseAge already lives authoritatively in pnpm-workspace.yaml (documented in SECURITY.md). Let me confirm pnpm's precedence between the two config files.I now have everything needed. Here's my review.


Review: deps: update pnpm to 10.34.3 for minimumReleaseAge enforcement

The Dockerfile pinning (pnpm@10pnpm@10.34.3) and the AGENTS.md version bump are correct and consistent with the existing packageManager: pnpm@10.34.3 in package.json:3. However, the .npmrc change reintroduces a duplicate, dead security setting that contradicts the project's own deliberate design.

Issue 1 — 🔴 .npmrc duplicates minimumReleaseAge that already lives (and wins precedence) in pnpm-workspace.yaml

  • Addressed
  • Dismissed

.npmrc:15 adds:

minimum-release-age=20160

But the canonical setting is already defined in pnpm-workspace.yaml:2:

minimumReleaseAge: 20160

…and SECURITY.md:11 documents pnpm-workspace.yaml as the authoritative location:

- Node.js/frontend dependencies use pnpm `minimumReleaseAge: 20160` in `pnpm-workspace.yaml`.

This is a real problem, not just style, for three reasons:

  1. It's silently ignored by pnpm. In pnpm v10 the documented precedence is pnpm-workspace.yaml > .npmrc (see pnpm PR #10073, which added the code comment // Priority: pnpm-workspace.yaml > .npmrc > default to pnpm-workspace.yaml). Because minimumReleaseAge exists in pnpm-workspace.yaml, the .npmrc value is dead weight for pnpm. In pnpm v11, non-auth .npmrc settings are dropped entirely. So this line gives a false impression that editing .npmrc tunes pnpm's enforcement — it does not. Someone could "loosen" or "tighten" .npmrc and see zero effect, a classic false-sense-of-security footgun.

  2. It reverts an intentional design decision. Commit a952072df1 ("Improve pnpm security (Improve pnpm security #224)") deliberately moved this setting out of .npmrc into pnpm-workspace.yaml:

    * move pnpm minimumReleaseAge into pnpm-workspace.yaml
    

    This PR re-adds exactly what Improve pnpm security #224 removed.

  3. DRY violation on a security-critical value. 20160 is now declared in two files. The day someone tunes the window in one file and not the other (e.g. to 7 days = 10080), the configs will silently disagree, and only the pnpm-workspace.yaml value will actually take effect.

Note also that the cited rationale in the .npmrc header doesn't rescue it: the comment (:4) references npm install -g pnpm@10.34.3 in the Dockerfile, but in all three Dockerfiles that command runs before the project (and thus .npmrc) is COPYed into the image, so .npmrc does not gate that bootstrap anyway:

&& npm install -g pnpm@10.34.3 \        # runs here…
&& rm -rf /var/cache/apk/*
COPY . ${GOPATH}/src/code.gitea.io/gitea  # …before .npmrc exists

Suggested fix — remove the line and keep pnpm-workspace.yaml as the single source of truth (consistent with #224 and SECURITY.md); the clarifying header comment is useful and can stay:

 auto-install-peers=true
 dedupe-peer-dependents=false
 enable-pre-post-scripts=true
-minimum-release-age=20160

If the team does want to age-gate the standalone npm global installs too (e.g. @charcharm/crush in .github/workflows/pull-review-crush.yml:424), that should be an explicit, documented decision in SECURITY.md rather than an undocumented duplicate that reads like a pnpm setting.


Issue 2 — 🟡 README.md prerequisite still lists pnpm with no version, while AGENTS.md now requires 10.16.0+

  • Addressed
  • Dismissed

This PR bumps AGENTS.md:13 to:

- **pnpm**: 10.16.0+ (required for `minimumReleaseAge` dependency age gating)

but README.md:11 was left as:

- pnpm

That version requirement is security-critical, not cosmetic: minimumReleaseAge was only added in pnpm v10.16.0. On an older pnpm, pnpm-workspace.yaml's minimumReleaseAge key is an unknown setting that pnpm warns about and silently does not enforce — so a contributor following the README could install e.g. pnpm 10.5, pass the documented prerequisites, and get no supply-chain age gating at all.

Suggested fix — make README.md match AGENTS.md:

 ### Prerequisites

 - Go 1.25. See https://go.dev/doc/manage-install
 - node
-- pnpm
+- pnpm 10.16.0+ (required for `minimumReleaseAge` dependency age gating)

Issue 3 — ⚪ .npmrc header comment hardcodes pnpm@10.34.3, which will drift on the next bump

  • Addressed
  • Dismissed

.npmrc:4 says:

#   - Bootstrapping pnpm itself (e.g. `npm install -g pnpm@10.34.3` in the Dockerfile)

The exact version now lives in four places (package.json:3, Dockerfile:22, Dockerfile.rootless:22, docker/forkana/Dockerfile:21) plus this comment. On the next pnpm bump this example will silently go stale and mislead readers about what the Dockerfiles actually install.

Suggested fix — reference the version generically so the comment doesn't need maintenance:

-#   - Bootstrapping pnpm itself (e.g. `npm install -g pnpm@10.34.3` in the Dockerfile)
+#   - Bootstrapping pnpm itself (e.g. `npm install -g pnpm` in the Dockerfile;
+#     exact version pinned in the Dockerfiles and package.json `packageManager`)

(If Issue 1 is resolved by keeping the comment, this keeps it accurate; if you prefer, drop the version token entirely: `npm install -g pnpm`.)


Review generated using glm-5.2 via Z.AI. Comment /review to re-run.

@taoeffect

taoeffect commented Jul 26, 2026

Copy link
Copy Markdown
Member

(just curious to see if GLM-5.2 catches the .npmrc bug)

Looks like it didn't.

* install pnpm@^10.34.3 with --min-release-age=14
* lower pnpm bootstrap version from ^10.34.3 to ^10.16.0 in all three Dockerfiles, keeping the 14-day --min-release-age gate
* remove the packageManager pin (pnpm@10.34.3) from package.json, relying on the existing engines constraint (>= 10.16.0)
* add version "^10.16.0" to all 9 pnpm/action-setup@v4 steps
* required after packageManager removal from package.json
* keeps CI aligned with Dockerfile bootstrap and engines.pnpm constraint
Copilot AI review requested due to automatic review settings July 27, 2026 18:21

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

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

package.json:6

  • The PR title/description indicate pnpm should be pinned (e.g. pnpm@10.34.3) via the packageManager field, but this change removes it entirely. That makes the pnpm version less reproducible and no longer aligns with the stated intent to “match packageManager”.
{
  "type": "module",
  "engines": {
    "node": ">= 22.6.0",
    "pnpm": ">= 10.16.0"
  },

Comment thread docker/forkana/Dockerfile
Comment thread Dockerfile
Comment thread Dockerfile.rootless
@pedrogaudencio pedrogaudencio changed the title deps: update pnpm to 10.34.3 for minimumReleaseAge enforcement deps: update pnpm to 10.16.0 for minimumReleaseAge enforcement Jul 27, 2026
Copilot AI review requested due to automatic review settings July 27, 2026 18:51

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

Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.

Comment thread package.json
Comment thread Dockerfile
Comment thread Dockerfile.rootless
Comment thread docker/forkana/Dockerfile
@pedrogaudencio

pedrogaudencio commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

/review


AI review started.

@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown

Advanced AI Review

  • Type: Agentic (crush)
  • Model: glm-5.2
Click to expand review

I'll investigate the codebase to verify the claims in the changes and find issues.Let me research the critical technical claims about npm's min-release-age semantics and version requirements.This is a critical finding. npm's min-release-age=14 means 14 days (bare number = days), but it requires npm 11.10.0+. Node 24.10.0 ships npm 11.6.1 which is too old. Let me verify the Alpine 3.23 npm version claim and the CI node versions.Let me verify the critical Alpine 3.23 npm version claim more carefully, since the entire security gate depends on it.Alpine 3.23 ships npm 11.11.0 ✓. Now let me verify the .npmrc comment claims about where npm is actually used, and check the crush workflow.Let me check all workflow files for pnpm setup consistency and examine the actions job condition.Now I have all the context I need. Let me verify one final detail about how pnpm handles the .npmrc settings.I now have a thorough understanding of all the changes and their technical context. Here is my review.


Review: deps: update pnpm to 10.16.0 for minimumReleaseAge enforcement

I verified the core technical claims: Alpine 3.23 ships npm 11.11.0 (✓ supports min-release-age), npm interprets a bare number as days while pnpm's minimumReleaseAge uses minutes, and pnpm silently ignores the npm-specific min-release-age key in .npmrc. No hard runtime bugs were found, but several issues warrant attention.


1. 🟡 .npmrc comment mischaracterizes actively-used pnpm settings as "legacy"

  • Addressed
  • Dismissed

The header comment at .npmrc:1-7 states:

# npm is not used to install project dependencies; project dependencies are
# installed with pnpm. This file contains npm settings used for standalone npm
# installs and legacy pnpm-compatible settings.

This is misleading. On pnpm 10.x (which this project pins via ^10.16.0), pnpm still reads non-auth settings from .npmrc. The settings auto-install-peers=true (.npmrc:13), dedupe-peer-dependents=false (.npmrc:14), enable-pre-post-scripts=true (.npmrc:15), and save-exact=true (.npmrc:12) are all actively consumed by pnpm during pnpm install (the command make deps-frontend runs). Calling them "legacy pnpm-compatible settings" risks a future maintainer removing them under the false assumption they're unused, which would silently change pnpm's installation behavior.

Additionally, the comment doesn't note that min-release-age requires npm ≥ 11.10.0 — older npm versions silently ignore it, giving a false sense of security.

Suggested rewrite:

# Project dependencies are installed with pnpm, not npm. However, pnpm 10.x
# still reads several settings from this file (auto-install-peers,
# dedupe-peer-dependents, enable-pre-post-scripts, save-exact), so they are
# not merely legacy. The npm-specific keys below (audit, fund,
# update-notifier, min-release-age) apply to standalone npm invocations:
#   - Bootstrapping pnpm itself (see Dockerfile, which passes
#     --min-release-age explicitly because this file is not yet copied)
#   - Installing CLI tools in CI (e.g. @charmland/crush)
# Note: min-release-age requires npm >= 11.10.0; older npm silently ignores it.

2. 🟡 DRY: the 14-day policy value is duplicated across 5 config locations with mixed units

  • Addressed
  • Dismissed

The cooldown window (14 days) appears in five separate locations, expressed in two different units:

Location Key Value Unit
.npmrc:16 min-release-age 14 days
pnpm-workspace.yaml:2 minimumReleaseAge 20160 minutes
Dockerfile:26 --min-release-age 14 days
Dockerfile.rootless:26 --min-release-age 14 days
docker/forkana/Dockerfile:25 --min-release-age 14 days

The npm/pnpm unit difference (days vs. minutes) is a well-known footgun. If the policy changes to, say, 21 days, all five locations must be updated, and pnpm-workspace.yaml requires a non-trivial conversion (21 * 24 * 60 = 30240). The Dockerfile comments already cross-reference pnpm-workspace.yaml, but .npmrc and pnpm-workspace.yaml themselves do not cross-reference each other.

Suggestion: add a cross-reference comment in .npmrc and ensure the pnpm-workspace.yaml comment (which already shows the math) is the canonical source:

# .npmrc — keep in sync with pnpm-workspace.yaml:minimumReleaseAge (which uses
# minutes; npm uses days). The canonical policy value is 14 days.
min-release-age=14

3. 🟡 Removing packageManager from package.json reduces build reproducibility

  • Addressed
  • Dismissed

The diff removes:

"packageManager": "pnpm@10.34.3",

This was the single source of truth for the exact pnpm version. Its removal means:

  • Corepack users lose automatic provisioning — corepack enable no longer knows which pnpm to install.
  • CI reproducibility degrades: version: "^10.16.0" in the workflows (e.g., .github/workflows/pull-compliance.yml:40) is a floating range that resolves to whatever the latest 10.x is on the day CI runs. A lockfile generated with pnpm 10.34.3 today could be installed with pnpm 10.40.0 next month.
  • engines.pnpm: ">= 10.16.0" is a minimum constraint, not a pin — it allows pnpm 11.x, yet the CI range ^10.16.0 caps at < 11.0.0, creating a silent mismatch.

The packageManager field and engines.pnpm serve complementary purposes (exact pin vs. compatibility range) and are not mutually exclusive. Consider restoring it:

"packageManager": "pnpm@10.34.3",
"engines": {
    "node": ">= 22.6.0",
    "pnpm": ">= 10.16.0"
},

If the intent is to avoid pinning a version newer than the 14-day age gate allows, pin a version that is already > 14 days old (10.34.3 qualifies).


4. ⚪️ The crush workflow's npm install -g @charmland/crush may silently bypass min-release-age

  • Addressed
  • Dismissed

The .npmrc:6-7 comment cites @charmland/crush as a beneficiary of this file's npm settings. However, .github/workflows/pull-review-crush.yml does not use actions/setup-node — it relies on whatever Node.js/npm ubuntu-latest ships on PATH (.github/workflows/pull-review-crush.yml:424):

npm install -g @charmland/crush

If the runner image's default npm is < 11.10.0 (e.g., Node 22.x bundles npm 10.x), min-release-age=14 is silently ignored, and the crush install receives no age-gating — contradicting the security intent. The current ubuntu-latest likely ships Node 24.x with npm ≥ 11.10.0, but this is implicit and can change without notice.

Suggestion: add an explicit setup-node step to guarantee npm ≥ 11.10.0:

      - uses: actions/setup-node@v5
        with:
          node-version: 24
      - run: npm install -g @charmland/crush

5. ⚪️ .npmrc comment lists Dockerfile pnpm bootstrap as a consumer of this file, but it isn't

  • Addressed
  • Dismissed

.npmrc:5 states:

#   - Bootstrapping pnpm itself (e.g. `npm install -g --min-release-age=14 "pnpm@^10.16.0"` in the Dockerfile)

But the Dockerfile comment itself (Dockerfile:18-20) explains that the repo .npmrc is not yet copied at that point, which is exactly why the --min-release-age=14 flag is passed explicitly. Listing the Dockerfile bootstrap under "this file contains npm settings used for..." is misleading — that particular npm invocation does not read this .npmrc. Consider moving the Dockerfile example to a separate note clarifying it uses the CLI flag instead of this file.


Review generated using glm-5.2 via Z.AI. Comment /review to re-run.

@pedrogaudencio

pedrogaudencio commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

Advanced AI Review

  • Type: Agentic (crush)
  • Model: Fable 5
Click to expand review

1. 🟡 npm install -g @charmland/crush is not age-gated — project .npmrc is ignored in global mode

  • Addressed
  • Dismissed

The new .npmrc comment block (.npmrc:1-7) states that the file's npm settings cover:

- Installing specific standalone CLI tools in CI workflows
(e.g. @charmland/crush in .github/workflows/pull-review-crush.yml)

However, npm explicitly does not read the per-project .npmrc in global mode. From the official npm docs (npmrc, "Per-project config file"):

Additionally, this file is not read in global mode, such as when running npm install -g.

So the crush install at .github/workflows/pull-review-crush.yml:424:

npm install -g @charmland/crush

receives no min-release-age gating at all, even though this workflow is the most security-sensitive npm consumer in the repo (it feeds an LLM agent that runs against PR content). The Dockerfiles get this right by passing the flag explicitly; the workflow should do the same:

npm install -g --min-release-age=14 @charmland/crush

Note: --min-release-age requires npm >= 11.10.0. The job runs on ubuntu-latest with the runner's preinstalled Node/npm (no setup-node step in this job), so verify the runner image ships npm >= 11.10.0 — otherwise add a version check or a setup-node step, since older npm silently ignores the .npmrc key and (for the CLI flag) merely warns Unknown cli config without failing.

Either way, the .npmrc comment should be corrected so it no longer claims coverage of -g installs it cannot provide.

2. 🟡 CI pnpm bootstrap via pnpm/action-setup floats to brand-new releases, bypassing the 14-day policy

  • Addressed
  • Dismissed

The commit series age-gates the pnpm bootstrap in all three Dockerfiles (npm install -g --min-release-age=14 "pnpm@^10.16.0"), but the equivalent bootstrap in CI does not get the same treatment. All nine workflow call sites now use:

- uses: pnpm/action-setup@v4
  with:
    version: "^10.16.0"

pnpm/action-setup resolves the newest version matching the range at run time and has no minimum-age option, so CI will install a new pnpm release the moment it is published — exactly the window the 14-day minimumReleaseAge policy is meant to avoid, applied to the package manager itself (which runs with full lifecycle-script privileges in CI, including release-signing jobs in release-nightly.yml, release-tag-rc.yml, and release-tag-version.yml).

If the intent (per commit 5c39999024 deps: age-gate pnpm bootstrap with min-release-age) is to age-gate the pnpm bootstrap everywhere, consider either:

  • pinning an exact, known-good version in the workflows (version: "10.34.3"), updated deliberately, or
  • replacing pnpm/action-setup with an explicit npm install -g --min-release-age=14 "pnpm@^10.16.0" step (matching the Dockerfiles), which also removes the duplication of the range across seven workflow files.

If floating-with-range in CI is an accepted trade-off, it deserves a comment in at least one workflow explaining why CI differs from the Docker builds.

3. ⚪️ .npmrc comment has a dangling bullet list

  • Addressed
  • Dismissed

At .npmrc:1-7, the bullet list has no lead-in sentence — the bullets describing what npm is used for hang off a sentence about pnpm:

# All project dependencies must be installed via pnpm (e.g. `make node_modules`).
#   - Bootstrapping pnpm itself (e.g. `npm install -g --min-release-age=14 "pnpm@^10.16.0"` in the Dockerfile)
#   - Installing specific standalone CLI tools in CI workflows

It reads as if a line such as "npm is only used for:" was dropped during editing. Suggested wording (also incorporating the fix from issue 1):

# Project dependencies are installed with pnpm (e.g. `make node_modules`), not
# npm. This file configures the standalone npm invocations that remain:
#   - bootstrapping pnpm itself (Dockerfiles pass --min-release-age explicitly,
#     since project .npmrc files are not read by `npm install -g`)
#   - one-off `npm install` runs inside the repo

Copilot AI review requested due to automatic review settings July 28, 2026 09:06

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@taoeffect taoeffect left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM!

@taoeffect
taoeffect merged commit ddce04b into master Jul 28, 2026
32 checks passed
@taoeffect
taoeffect deleted the fix-pnpm-min-release-age branch July 28, 2026 14:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve security of pnpm

3 participants