Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion agents/hermes/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ RUN set -eu; \
exit 1; \
fi; \
fi; \
rm -rf /root/.npm /root/.cache/electron /root/.cache/node-gyp; \
rm -rf /root/.npm /root/.cache/electron /root/.cache/node-gyp /root/.cache/uv; \
test -d "$hermes_web_dist"

ENV HERMES_WEB_DIST="/opt/hermes/hermes_cli/web_dist"
Expand Down Expand Up @@ -1191,6 +1191,7 @@ RUN check_metadata() { \
&& check_absent /root/.npm \
&& check_absent /root/.cache/electron \
&& check_absent /root/.cache/node-gyp \
&& check_absent /root/.cache/uv \
&& check_absent /sandbox/.cache \
&& check_metadata /scripts/patch-bundled-npm-brace-expansion.mts 'root:root 444' \
&& check_metadata /scripts/patch-bundled-npm-tar.mts 'root:root 444' \
Expand Down
7 changes: 5 additions & 2 deletions agents/hermes/Dockerfile.base
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,8 @@ RUN set -eu; \
# differential. Keep this hash-verified downstream override at the first stable
# release that fixes those issues plus GHSA-v9pg-7xvm-68hf. Re-review the
# version and both hashes on every Hermes version bump.
# uv creates a phony .git cache marker even with --no-cache. Remove the cache
# after the final uv command because the root cache is not used at runtime.
# hadolint ignore=DL3059
RUN printf '%s\n' \
"python-multipart==0.0.32 \\" \
Expand All @@ -533,7 +535,8 @@ RUN printf '%s\n' \
--no-deps --no-cache --require-hashes -r /tmp/multipart-req.txt \
&& rm -f /tmp/multipart-req.txt \
&& /opt/hermes/.venv/bin/python -c \
"import multipart; assert multipart.__version__ == '0.0.32', multipart.__version__"
"import multipart; assert multipart.__version__ == '0.0.32', multipart.__version__" \
&& rm -rf /root/.cache/uv

ENV PATH="/usr/local/bin:/opt/hermes/.venv/bin:${PATH}" \
HERMES_TUI_DIR="/opt/hermes/ui-tui" \
Expand Down Expand Up @@ -561,7 +564,7 @@ RUN chmod -R a+rX /opt/hermes/.venv \
# Gate the exact completed base filesystem before it can be published.
COPY scripts/checks/node-tar-image-scan.mts /scripts/checks/node-tar-image-scan.mts
RUN set -eu; \
for build_only_path in /opt/hermes/tests /root/.npm /root/.cache/electron /root/.cache/node-gyp; do \
for build_only_path in /opt/hermes/tests /root/.npm /root/.cache/electron /root/.cache/node-gyp /root/.cache/uv; do \
if [ -e "$build_only_path" ] || [ -L "$build_only_path" ]; then \
echo "ERROR: build-only Hermes path leaked into the base image: $build_only_path" >&2; \
exit 1; \
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/live/hermes-root-entrypoint-smoke.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ async function assertBuildOnlyPathsAbsent(probe: DockerProbe, container: string)
probe,
container,
"build-only Hermes paths are present in the runtime image",
'for path in /opt/hermes/tests /root/.npm /root/.cache/electron /root/.cache/node-gyp; do test ! -e "$path" && test ! -L "$path"; done',
'for path in /opt/hermes/tests /root/.npm /root/.cache/electron /root/.cache/node-gyp /root/.cache/uv; do test ! -e "$path" && test ! -L "$path"; done',
);
}

Expand Down
7 changes: 4 additions & 3 deletions test/sandbox-provisioning.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1325,7 +1325,7 @@ describe("Hermes sandbox provisioning", () => {
fs.writeFileSync(path.join(hermesWebDir, "package.json"), "{}\n");
fs.writeFileSync(path.join(hermesWebDir, "package-lock.json"), "{}\n");
fs.mkdirSync(path.join(hermesWebDir, "node_modules"), { recursive: true });
for (const cache of ["npm", "electron", "node-gyp"]) {
for (const cache of ["npm", "electron", "node-gyp", "uv"]) {
const cachePath = path.join(rootCache, cache);
fs.mkdirSync(cachePath, { recursive: true });
fs.writeFileSync(path.join(cachePath, "build-only-cache"), "unused after image assembly\n");
Expand All @@ -1338,7 +1338,8 @@ describe("Hermes sandbox provisioning", () => {
.replaceAll("/opt/hermes", hermesRoot)
.replaceAll("/root/.npm", path.join(rootCache, "npm"))
.replaceAll("/root/.cache/electron", path.join(rootCache, "electron"))
.replaceAll("/root/.cache/node-gyp", path.join(rootCache, "node-gyp"));
.replaceAll("/root/.cache/node-gyp", path.join(rootCache, "node-gyp"))
.replaceAll("/root/.cache/uv", path.join(rootCache, "uv"));
try {
const { result, calls } = runLoggedDockerShell(command, tmp, [
'npm() { printf "npm %s\\n" "$*" >> "$call_log"; if [ -n "${hermes_web_dist:-}" ] && [ "${1:-}" = "run" ] && [ "${2:-}" = "build" ]; then mkdir -p "$hermes_web_dist"; fi; }',
Expand All @@ -1349,7 +1350,7 @@ describe("Hermes sandbox provisioning", () => {
expect(calls).toContain(`npm run build --prefix ${hermesWebDir}`);
expect(fs.existsSync(hermesWebDist)).toBe(true);
expect(fs.existsSync(path.join(hermesWebDir, "node_modules"))).toBe(false);
for (const cache of ["npm", "electron", "node-gyp"]) {
for (const cache of ["npm", "electron", "node-gyp", "uv"]) {
expect(() => fs.lstatSync(path.join(rootCache, cache))).toThrow();
}
} finally {
Expand Down
Loading