Skip to content

Docker builds of generated projects fail pnpm's minimumReleaseAge policy when any locked dependency is <24h old #122

Description

@PAMulligan

Description

docker build of a freshly generated project can fail at RUN pnpm install --frozen-lockfile with a supply-chain policy violation. The generated Dockerfile (templates/node-server/Dockerfile, also used by --railway and --fly; same line in templates/docker/Dockerfile) activates pnpm@latest via corepack, and current pnpm enforces a default minimumReleaseAge of 24 hours — so the in-image install rejects any lockfile entry published less than a day before the image build:

#11 [build 5/9] RUN pnpm install --frozen-lockfile
#11 1.955 ? Verifying lockfile against supply-chain policies (275 entries)...
#11 7.682 ✗ Lockfile failed supply-chain policy check (275 entries in 5.7s)
#11 7.741 [ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION] 1 lockfile entries failed verification:
#11 7.741   eslint@10.5.0 was published at 2026-06-12T17:54:40.577Z, within the minimumReleaseAge cutoff (2026-06-12T00:56:01.617Z)
#11 ERROR: process "/bin/sh -c pnpm install --frozen-lockfile" did not complete successfully: exit code: 1

Hit for real while building examples/blog-api-node (#121): the project was generated and installed with local pnpm 10.15.1 (which has no such default), the lockfile was committed, and the in-image pnpm@latest refused that same lockfile the same day.

Why

setup-project.sh resolves dependencies with whatever pnpm the developer has; the Dockerfile installs with whatever pnpm is latest at build time. Those two disagree on supply-chain policy, so docker compose up --build on a fresh project fails for up to 24 hours after any dependency in the tree publishes a release — and the window re-opens with every release. Affects the node, railway, and fly targets alike.

Why CI doesn't catch it

The generated-projects CI job generates and type-checks projects but never runs docker build, and the committed examples aren't image-built in CI either. The failure only appears on user machines (or example e2e runs) at unlucky times.

Acceptance Criteria

  • setup-project.sh writes a packageManager field (the pnpm version that resolved the lockfile, e.g. via pnpm --version) into the generated package.json, so corepack uses the same pnpm locally and in-image — mirrors the fix shipped in examples/blog-api-node (feat: add blog API Node.js/Docker example #121)
  • Decide the template Dockerfile stance: keep corepack prepare pnpm@latest --activate as a fallback (corepack still honors packageManager when present) or prepare a pinned version; document the minimumReleaseAge interaction in a Dockerfile comment
  • templates/docker/Dockerfile kept in sync with templates/node-server/Dockerfile
  • Consider a CI smoke job that docker builds one generated project so this class of breakage fails in CI instead of on user machines
  • CI passes

Resources

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

Relationships

None yet

Development

No branches or pull requests

Issue actions