From a5212a349b923e6d248e6132fdcf61f0b2a4698c Mon Sep 17 00:00:00 2001 From: Hongming Wang Date: Thu, 30 Apr 2026 21:49:26 -0700 Subject: [PATCH] fix(publish-template-image): chmod a+rX + drop :ro so agent can read /configs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .github/workflows/publish-template-image.yml | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish-template-image.yml b/.github/workflows/publish-template-image.yml index ecf0336..0a6061f 100644 --- a/.github/workflows/publish-template-image.yml +++ b/.github/workflows/publish-template-image.yml @@ -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 \