fix(publish-template-image): chmod a+rX + drop :ro so agent can read /configs

Hot-fix for #2275 Phase 2 — the boot smoke step in v1@3c8f8fe failed
on every template publish with `PermissionError: [Errno 13] Permission
denied: '/configs/config.yaml'` because `mktemp -d` creates the dir
with mode 700 and `chmod -R go+r` adds 'r' to files but doesn't add
'x' to directories. Inside the image the entrypoint drops priv to
uid 1000 (agent), which then cannot traverse /configs to even reach
config.yaml — main.py exits before any executor code runs.

Two changes:
1. `chmod -R a+rX` (capital X) adds 'x' to directories AND already-
   executable files, so the temp dir becomes traversable for agent
   while config.yaml stays a regular world-readable file.
2. Drop `:ro` on the mount so the entrypoint's `chown -R agent
   /configs` succeeds. The container is ephemeral; modifications to
   the host mktemp dir don't matter and the dir gets nuked right
   after the smoke run.

Reproduced + diagnosed against claude-code publish run 25202651546
which failed within a few seconds on Path('/configs/config.yaml').exists()
in molecule_runtime/config.py:298.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hongming Wang 2026-04-30 21:49:26 -07:00
parent 3c8f8fe48b
commit a5212a349b

View File

@ -287,11 +287,22 @@ jobs:
# Mount the repo's own config.yaml at /configs so the runtime
# can reach create_executor() — that's where the lazy imports
# we want to test actually live. World-readable so the
# entrypoint's drop-priv to uid 1000 can read it.
# we want to test actually live. The image's entrypoint drops
# priv from root to agent (uid 1000) before exec'ing
# molecule-runtime, so /configs needs to be readable AND
# traversable from uid 1000.
#
# Use `a+rX` (capital X — only adds x where it's already
# executable, i.e. directories): mktemp -d creates the dir
# with mode 700, so a bare `go+r` would leave the dir
# un-traversable for agent and config.py would
# PermissionError on `Path('/configs/config.yaml').exists()`.
# Mount RW (not :ro) so the entrypoint's `chown -R agent
# /configs` succeeds — its silent chown failure on a :ro
# mount was the original symptom.
SMOKE_CONFIG_DIR=$(mktemp -d)
cp config.yaml "${SMOKE_CONFIG_DIR}/"
chmod -R go+r "${SMOKE_CONFIG_DIR}"
chmod -R a+rX "${SMOKE_CONFIG_DIR}"
# Stub credentials — adapters validate shape at create_executor
# time but the smoke times out before any real call goes out.
@ -299,7 +310,7 @@ jobs:
# specific key sees a non-empty value.
set +e
timeout 60 docker run --rm \
-v "${SMOKE_CONFIG_DIR}:/configs:ro" \
-v "${SMOKE_CONFIG_DIR}:/configs" \
-e WORKSPACE_ID=fake-smoke \
-e MOLECULE_SMOKE_MODE=1 \
-e MOLECULE_SMOKE_TIMEOUT_SECS=10 \