Skip to content

Commit dbc0474

Browse files
fix(docker): ship each API's nested node_modules so ajv-formats resolves (#44)
The runner stage copied only the root node_modules, but a prod dependency installs NESTED under the app when the root store slot is held by a different major. Dev tooling pins ajv-formats@2 at the hoisted root, so the apps' prod ajv-formats@3 lands in apps/<app>/node_modules and never made it into the runtime image — platform-api and citizen-portal-api booted then crashed with "Cannot find module 'ajv-formats'". Copy apps/<app>/node_modules into each runner. ajv stays hoisted at root; the nested ajv-formats resolves it up the tree. Verified against the exact turbo-prune + npm install + npm prune --omit=dev sequence. The clean package-lock regen in #43 changed the hoist layout and exposed this latent gap.
1 parent ab21eda commit dbc0474

2 files changed

Lines changed: 16 additions & 0 deletions

File tree

apps/citizen-portal-api/Dockerfile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ COPY --from=builder --chown=node:0 /app/packages/database/dist ./packages/databa
8383
COPY --from=builder --chown=node:0 /app/apps/citizen-portal-api/package.json ./apps/citizen-portal-api/package.json
8484
COPY --from=builder --chown=node:0 /app/apps/citizen-portal-api/dist ./apps/citizen-portal-api/dist
8585

86+
# The app's own NESTED production deps. A prod dependency installs nested under the app (instead of
87+
# the hoisted root store) when the root slot is held by a different major — here dev tooling pins
88+
# ajv-formats@2 at root, so the app's prod ajv-formats@3 lands in apps/citizen-portal-api/node_modules.
89+
# The runner ships only the root node_modules, so without this the app boots and dies with
90+
# "Cannot find module 'ajv-formats'". (ajv itself stays hoisted at root; the nested ajv-formats
91+
# resolves it up the tree.) See .mdd/docs/73-citizen-portal-api-dockerfile.md.
92+
COPY --from=builder --chown=node:0 /app/apps/citizen-portal-api/node_modules ./apps/citizen-portal-api/node_modules
93+
8694
# OpenShift/K8s hardening: the `restricted-v2` SCC runs the container as an arbitrary
8795
# assigned UID with GID 0. The COPYs above group-own everything by root (`--chown=…:0`)
8896
# and npm's default perms already grant the group read+traverse, so any assigned UID can

apps/platform-api/Dockerfile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@ COPY --from=builder --chown=node:0 /app/packages/database/dist ./packages/databa
8585
COPY --from=builder --chown=node:0 /app/apps/platform-api/package.json ./apps/platform-api/package.json
8686
COPY --from=builder --chown=node:0 /app/apps/platform-api/dist ./apps/platform-api/dist
8787

88+
# The app's own NESTED production deps. A prod dependency installs nested under the app (instead of
89+
# the hoisted root store) when the root slot is held by a different major — here dev tooling pins
90+
# ajv-formats@2 at root, so the app's prod ajv-formats@3 lands in apps/platform-api/node_modules.
91+
# The runner ships only the root node_modules, so without this the app boots and dies with
92+
# "Cannot find module 'ajv-formats'". (ajv itself stays hoisted at root; the nested ajv-formats
93+
# resolves it up the tree.) See .mdd/docs/72-platform-api-dockerfile.md.
94+
COPY --from=builder --chown=node:0 /app/apps/platform-api/node_modules ./apps/platform-api/node_modules
95+
8896
# OpenShift/K8s hardening: the `restricted-v2` SCC runs the container as an arbitrary
8997
# assigned UID with GID 0. The COPYs above group-own everything by root (`--chown=…:0`)
9098
# and npm's default perms already grant the group read+traverse, so any assigned UID can

0 commit comments

Comments
 (0)