Problem
derivedDockerfile() / SANDBOX_ENTRY_PATH (src/docker-image.js) hardcode the assumption that npm's global install prefix is /usr/local:
const SANDBOX_ENTRY_PATH =
"/usr/local/lib/node_modules/@tbrandenburg/node-red-cli/bin/node-red-cli-sandbox-entry.js";
Any community/base image that overrides npm's global prefix via NPM_CONFIG_PREFIX (or an .npmrc prefix setting) — increasingly common for rootless dev-container images that want npm install -g to work without root — breaks this silently: npm install -g @tbrandenburg/node-red-cli@<version> succeeds (exit 0, no error in the build log) but installs to the actual configured prefix, not /usr/local. The subsequent ENTRYPOINT ["node", "<SANDBOX_ENTRY_PATH>"] then fails at container-run time with MODULE_NOT_FOUND for a path that was never populated.
This is unrelated to and independent of #15 (which was about the build failing outright with EACCES when the default prefix is non-writable for a non-root user). Here the build succeeds cleanly; only the runtime docker run fails, and only for images that redirect the prefix rather than just changing the default user.
Reproduction
$ docker run --rm --entrypoint sh <some-image-with-custom-npm-prefix> -c 'npm config get prefix'
/home/node/.npm-global # (example — anything other than /usr/local)
Building a derived image from such a base and running it:
$ node-red-cli flows.json calculate --docker <that-image> --set x=4 --set y=5
node:internal/modules/cjs/loader:1568
throw err;
Error: Cannot find module '/usr/local/lib/node_modules/@tbrandenburg/node-red-cli/bin/node-red-cli-sandbox-entry.js'
...
code: 'MODULE_NOT_FOUND'
Confirmed directly by inspecting the derived image's filesystem:
$ docker run --rm --entrypoint sh <derived-image-tag> -c \
'test -f /usr/local/lib/node_modules/@tbrandenburg/node-red-cli/bin/node-red-cli-sandbox-entry.js && echo HAS || echo MISSING'
MISSING
while the base image itself reports:
$ docker run --rm --entrypoint sh <base-image> -c 'npm config get prefix; env | grep -i npm'
/home/node/.npm-global
NPM_CONFIG_PREFIX=/home/node/.npm-global
Root cause
buildRunArgs's counterpart on the build side, derivedDockerfile(), does:
FROM <image>
USER root
RUN npm install -g @tbrandenburg/node-red-cli@<version>
ENTRYPOINT ["node", "/usr/local/lib/node_modules/@tbrandenburg/node-red-cli/bin/node-red-cli-sandbox-entry.js"]
USER root (added for #15/#16) makes the install writable, but does not change where npm installs to — that's controlled entirely by NPM_CONFIG_PREFIX/.npmrc, which persists across USER switches within the same build (env vars and .npmrc files aren't user-scoped). If the base image sets a non-default prefix, the package lands there instead of /usr/local, and the hardcoded SANDBOX_ENTRY_PATH constant used for both the test -f existence check (hasSandboxEntry()) and the final ENTRYPOINT no longer matches reality.
Expected behavior
The derived-image build and entrypoint should work correctly regardless of the base image's configured npm global prefix — generic compatibility with any community/base image was the explicit design goal of the --docker <image> derived-build path (see #15/#16).
Proposed fix
In derivedDockerfile(), before RUN npm install -g ..., explicitly reset the install location to a known value so SANDBOX_ENTRY_PATH is guaranteed correct regardless of the base image's own npm configuration, e.g.:
FROM <image>
USER root
ENV NPM_CONFIG_PREFIX=/usr/local
RUN npm install -g @tbrandenburg/node-red-cli@<version>
ENTRYPOINT ["node", "/usr/local/lib/node_modules/@tbrandenburg/node-red-cli/bin/node-red-cli-sandbox-entry.js"]
(Exact mechanism open to the implementer — an explicit ENV NPM_CONFIG_PREFIX=/usr/local override is the most direct approach; alternatively, resolve the actual post-install path via npm root -g in a build step and pass it through to ENTRYPOINT, though that requires shell-form entrypoint or a wrapper script instead of the current exec-form array.)
Scope
Affects --docker <image[:tag]> (derived-image mode) for any base image that overrides npm's default global prefix — a real and growing pattern among rootless/dev-container-style community images, not specific to any one image.
Problem
derivedDockerfile()/SANDBOX_ENTRY_PATH(src/docker-image.js) hardcode the assumption that npm's global install prefix is/usr/local:Any community/base image that overrides npm's global prefix via
NPM_CONFIG_PREFIX(or an.npmrcprefixsetting) — increasingly common for rootless dev-container images that wantnpm install -gto work without root — breaks this silently:npm install -g @tbrandenburg/node-red-cli@<version>succeeds (exit 0, no error in the build log) but installs to the actual configured prefix, not/usr/local. The subsequentENTRYPOINT ["node", "<SANDBOX_ENTRY_PATH>"]then fails at container-run time withMODULE_NOT_FOUNDfor a path that was never populated.This is unrelated to and independent of #15 (which was about the build failing outright with
EACCESwhen the default prefix is non-writable for a non-root user). Here the build succeeds cleanly; only the runtimedocker runfails, and only for images that redirect the prefix rather than just changing the default user.Reproduction
Building a derived image from such a base and running it:
Confirmed directly by inspecting the derived image's filesystem:
while the base image itself reports:
Root cause
buildRunArgs's counterpart on the build side,derivedDockerfile(), does:USER root(added for #15/#16) makes the install writable, but does not change where npm installs to — that's controlled entirely byNPM_CONFIG_PREFIX/.npmrc, which persists acrossUSERswitches within the same build (env vars and.npmrcfiles aren't user-scoped). If the base image sets a non-default prefix, the package lands there instead of/usr/local, and the hardcodedSANDBOX_ENTRY_PATHconstant used for both thetest -fexistence check (hasSandboxEntry()) and the finalENTRYPOINTno longer matches reality.Expected behavior
The derived-image build and entrypoint should work correctly regardless of the base image's configured npm global prefix — generic compatibility with any community/base image was the explicit design goal of the
--docker <image>derived-build path (see #15/#16).Proposed fix
In
derivedDockerfile(), beforeRUN npm install -g ..., explicitly reset the install location to a known value soSANDBOX_ENTRY_PATHis guaranteed correct regardless of the base image's own npm configuration, e.g.:(Exact mechanism open to the implementer — an explicit
ENV NPM_CONFIG_PREFIX=/usr/localoverride is the most direct approach; alternatively, resolve the actual post-install path vianpm root -gin a build step and pass it through toENTRYPOINT, though that requires shell-form entrypoint or a wrapper script instead of the current exec-form array.)Scope
Affects
--docker <image[:tag]>(derived-image mode) for any base image that overrides npm's default global prefix — a real and growing pattern among rootless/dev-container-style community images, not specific to any one image.