Repository navigation
Docker image dependency tree is resolved fresh at build time, not from the published shrinkwrap #1960
Description
Activity
- added a commit that references this issue
on Jul 28, 2026 - added a commit that references this issue
on Jul 28, 2026 Checked whether this has actually bitten a published image, since #2043 raised the possibility that the unpinned resolve picked up
@harperfast/rocksdb-js2.6.x. Short version: the mechanism is confirmed live on the shipped 5.2.0 artifact, but no published image was broken by it.The mechanism reproduces on the released tarball. Installing
harper@5.2.0the way the Dockerfile does — from a local.tgz, so npm never sees a packument and never learns the bundled shrinkwrap exists:bundled npm-shrinkwrap.json pins: @harperfast/rocksdb-js 2.5.0 fresh resolve from the tarball: @harperfast/rocksdb-js 2.7.0Both v5.2.0 Dockerfiles still use that path (
npm install --ignore-scripts --global harper-*.tgzin harper,npm install --global harperfast-harper-pro-*.tgzin harper-pro), so every image build resolves^2.5.0to whatever is latest at build time. The relevant publish dates: 2.6.0 and 2.6.1 on 2026-07-31, 2.7.0 on 2026-08-04 — all inside the^2.5.0range, all after the shrinkwrap was written.But the 5.2.0 image is fine. The
Release Harper Pro to Environmentsrun of 2026-08-01T02:22 (immediately after the 5.2.0 tag) is green end to end, including the stages that would have caught a broken table open:- Build Docker image (v5.2.0) — success
- Deploy to stage hosts — success
- Health check stage — success
- Smoke test — create/verify cluster (v5.2.0) — success
A cluster was created and verified against that image, so whatever rocksdb-js it resolved at build time opens tables correctly. Nothing needs rebuilding, and the fleet isn't exposed today.
What that leaves. The exposure isn't a broken image, it's that the image's dependency tree is decided by publish timing rather than by anything we tested. Two consequences worth separating:
- Every rebuild of the same tag can produce a different tree.
harper:5.2.0built today resolves 2.7.0; built on 2026-08-01 it resolved something else. That's a reproducibility hole in a released artifact, independent of whether any particular resolve is broken. - The next incompatible publish inside a caret range lands in the image with no Harper change to review. This time the stage smoke test would have caught a total failure — it wouldn't necessarily catch a subtler incompatibility, and it runs after the image is already built and tagged.
Keeping this at P1 on that basis rather than escalating: real and structural, but not currently breaking anything. #2042 remains the fix.
— KrAIs (Claude Opus 5)
- added a commit that references this issue
on Aug 6, 2026
SummaryThe Dockerfile installs harper from a locally built tarball:```
npm install --ignore-scripts --global harper-.tgz
&& cd && npm install --omit=dev --ignore-scriptsnpm decides whether to honour a package's bundled `npm-shrinkwrap.json` from the `_hasShrinkwrap` flag in the **registry packument** — metadata the registry sets at publish time. A local tarball has no packument, so npm never learns the shrinkwrap exists: it opens the tarball, reads `package.json`, and resolves the whole tree fresh.## EvidenceThe *same* published `harper@5.1.23` artifact, installed two ways:| | shrinkwrap pin | via registry | via local tarball | latest on npm | |---|---|---|---|---| | fastify | 5.8.5 | 5.8.5 | **5.10.0** | 5.10.0 | | chokidar | 4.0.3 | 4.0.3 | 4.0.3 | 5.0.0 | | graphql | 16.14.2 | 16.14.2 | 16.14.2 | 17.0.2 |fastify is the discriminator — the tarball install resolved to latest rather than the pin. (chokidar and graphql match only coincidentally: harper's `^4.0.3` / `^16.10.0` ranges cap below those new majors.) The registry install produced exactly 794 packages, matching the shrinkwrap's 794 entries, in an isolated nested tree.## Why it matters1. **The image is not reproducible.** Two builds of the same harper commit, weeks apart, can ship different dependency trees — whatever is newest on npm within our semver ranges at build time. 2. **The image does not match what npm consumers get.** Registry consumers install the pinned tree; the image installs something else. 3. It is why the image still carries the react-native tree after #1937's shrinkwrap prune (see #1959) — that fix reaches registry installs but not this one.## DirectionMake the image install honour the shrinkwrap. Extracting the tarball and installing in place works — the shrinkwrap is then present as the lockfile:tar -xzf harper-.tgz --strip-components=1 -C
```Measured locally (macOS, like-for-like): 444M -> 268M with #1959's prune in place, and the tree becomes pinned.
npm ciwould be exact rather than merely shrinkwrap-guided, but currently trips the sync check because the published `package.json` retains `devDependencies` while the shipped shrinkwrap has them pruned.Open questions for whoever picks this up:npm install --globaland accepting an unpinned image.npm civiable (stripdevDependenciesfrom the packedpackage.json), which would give an exactly-pinned image.which harper— the entrypoint depends on it, including theHARPER_RUNTIME=bunpath.Depends on fix(build): strip the react-native tree from the published shrinkwrap #1959: honouring an unpruned shrinkwrap would pin the react-native tree in, so this should land after it.🤖 Generated with Claude Code