Problem
A non-frozen pantry install writes the services it infers from the project's .env (#36, env_deps.zig) into pantry.lock as if the workspace declared them. So the committed lockfile depends on whichever database the developer who last ran pantry install has in their local .env.
The Stacks release refreshes pantry.lock with pantry install --ignore-scripts --quiet on the machine cutting the release. Cut from a checkout whose .env says DB_CONNECTION=postgres, v0.75.335's lock gained:
"postgresql.org": "latest"in the root workspace's dependenciespostgresql.org@18.4,gnome.org/libxslt@1.1.45andunicode.org@73.2.0in packages
CI has no such .env, so its install removed them again, and the lockfile check failed with "Installing changed pantry.lock in a way no platform difference explains (removed: gnome.org/libxslt@1.1.45, postgresql.org@18.4, unicode.org@73.2.0)". The release never published (stacksjs/stacks Releaser run 40665558792, CI run 40665557398).
Reproduction (pantry 0.11.144)
In a Stacks checkout whose config/deps.ts declares sqlite and whose committed pantry.lock has no postgres:
- Set
DB_CONNECTION=postgresin.envand runpantry install --ignore-scripts --quiet.pantry.lockgains the four entries above. - Set
DB_CONNECTION=sqliteand run it again. They're removed (19 lines). - With
DB_CONNECTION=postgresstill in.env,PANTRY_ENV_DEPS=0 pantry install --ignore-scripts --quietleaves them out, matching CI.
A process-level DB_CONNECTION doesn't change the result; the file is read directly.
Expected
Inferring services from .env is right for installing what a checkout needs to run. Recording them in the lockfile's declared dependencies is not: the lock should describe the workspace's declared dependencies, the same on every machine. Options:
- install inferred services without writing them into
pantry.lock; or - record them apart from the workspace's declared dependencies, so a lock check can tell local inference from declarations; or
- skip
.envinference when the purpose of the run is maintaining the lockfile.
The --frozen-lockfile rule in env_deps.zig already treats the lockfile as the contract. This is the same principle applied to the run that writes it.
What Stacks does on its side
stacksjs/stacks passes PANTRY_ENV_DEPS=0 when its release refreshes pantry.lock, so a release cut on a Postgres machine stops producing a lock CI rejects. Other projects that commit pantry.lock and choose their database in .env have the same exposure without it.