Skip to content

fix(dev): keep container dependencies in sync with package-lock - #140

Merged
Bccorb merged 1 commit into
mainfrom
fix/dev-container-stale-dependencies
Aug 1, 2026
Merged

fix(dev): keep container dependencies in sync with package-lock#140
Bccorb merged 1 commit into
mainfrom
fix/dev-container-stale-dependencies

Conversation

@Bccorb

@Bccorb Bccorb commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Problem

docker-compose.dev.yml masked node_modules with an anonymous volume (- /app/node_modules).
That volume persists across docker compose up --build, so the container kept running whatever
was installed the first time it booted, however far the host's package.json had moved on.

It surfaced on 2026-07-31 as a boot crash:

The requested module '@seamless-auth/types' does not provide an export named 'PublicSystemConfigResponseSchema'

The host had @seamless-auth/types 0.6.0, which exports it. The container had 0.4.0, which does
not. The error reads like a bug in @seamless-auth/types rather than a stale install, so it costs
real debugging time, and it only cleared with --renew-anon-volumes.

Change

  • syncDevDeps.sh hashes package-lock.json, compares it to a stamp stored inside the volume, and
    runs npm ci only when the two have diverged.
  • Dockerfile.dev runs the same script instead of a bare npm ci, so the stamp ships in the image
    and a fresh volume does not reinstall what the image already has.
  • The volume is now named (node-modules-dev) rather than anonymous, so it can be listed and
    dropped by name.
  • CONTRIBUTING.md documents that the container owns its own node_modules, that it re-syncs on
    boot, and how to discard the volume.

node_modules stays out of the bind mount on purpose. The host tree is built for the host's
platform, so sharing it breaks platform-specific binaries (esbuild under tsx, sqlite3).

Verification

Run against an isolated stack (-p sa-dev-verify, ports 5399/55432), then torn down with its
volumes.

  1. First boot on a fresh volume: Dependencies match package-lock.json, skipping install. Healthy,
    types 0.6.0, no redundant install.
  2. Pinned the lockfile to types 0.5.0 and restarted: reinstalled, and the volume then held 0.5.0.
    The container tracks the lockfile rather than the image.
  3. Restored 0.6.0 and ran a plain docker compose up -d against the now stale volume, reproducing
    the original failure: it reinstalled (added 845 packages in 5s) and came up healthy on 0.6.0.
    No --renew-anon-volumes needed.
  4. Restart with an unchanged lockfile: install skipped, so boots stay fast.

Checks: format:check clean, lint clean, typecheck clean, test:run 91 files, 963 passed,
1 skipped.

No changeset: this is contributor tooling and does not change the published package's behavior.

Note for existing dev stacks

An existing seamless-auth-dev stack still holds the old anonymous volume. After
docker compose -f docker-compose.dev.yml down and up -d --build, it switches to the named
volume, and the orphaned anonymous volume can be cleared with docker volume prune.

The dev stack masked node_modules with an anonymous volume, which persists across
`docker compose up --build`. The container kept running whatever was installed the
first time it booted, so a dependency added or bumped on the host never reached it.
That surfaced as a boot crash naming a missing export from @seamless-auth/types,
which reads like a bug in that package rather than a stale install, and it only
cleared with `--renew-anon-volumes`.

syncDevDeps.sh now compares a hash of package-lock.json against a stamp inside the
volume and reinstalls when they differ. The image build writes the stamp too, so a
fresh volume does not reinstall what the image already has, and an unchanged
lockfile skips the install entirely.

The volume is now named rather than anonymous, so it can be inspected and dropped
by name. node_modules stays out of the bind mount because the host tree is built
for the host's platform, not the container's.
@Bccorb
Bccorb merged commit bab83c6 into main Aug 1, 2026
2 checks passed
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.

1 participant