Skip to content

Commit 6f01d09

Browse files
authored
fix(claude-code): create /etc/claude-code as root so managed-settings is readable (#101)
BuildKit applies COPY --chmod to any parent directories it auto-creates, so "COPY --chmod=0644 managed-settings.json /etc/claude-code/managed-settings.json" landed the file inside a drw-r--r-- directory — no execute bit, not traversable by anyone in the container. The node user could not read the file even though the file mode itself was correct, and jq in the CI verify step failed with "Could not open file ... Permission denied". Pre-create the directory explicitly as root before the COPY, and copy the file with --chown=root:root so it matches normal /etc/ ownership conventions. Also add a test -r precondition to the verify-command so future breakage of this kind surfaces with a clear "file unreadable" failure before jq sees it. Verified locally: built --target base from the modified Dockerfile, confirmed /etc/claude-code is now drwxr-xr-x and managed-settings.json is -rw-r--r-- readable as the node user, and the full verify-command exits 0 under the exact bash -c wrapping the CI matrix uses.
1 parent dcd8d19 commit 6f01d09

2 files changed

Lines changed: 14 additions & 6 deletions

File tree

‎.github/workflows/build-claude-code.yml‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,19 +77,19 @@ jobs:
7777
matrix:
7878
include:
7979
- image-suffix: claude-code
80-
verify-command: "bun --version || true && claude --version && mise --version && fish --version && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && printenv AGENT_BROWSER_EXECUTABLE_PATH | grep -qx /usr/bin/chromium"
80+
verify-command: "bun --version || true && claude --version && mise --version && fish --version && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && printenv AGENT_BROWSER_EXECUTABLE_PATH | grep -qx /usr/bin/chromium"
8181
runner: ubuntu-24.04
8282
arch: amd64
8383
- image-suffix: claude-code
84-
verify-command: "bun --version || true && claude --version && mise --version && fish --version && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && printenv AGENT_BROWSER_EXECUTABLE_PATH | grep -qx /usr/bin/chromium"
84+
verify-command: "bun --version || true && claude --version && mise --version && fish --version && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp && printenv AGENT_BROWSER_EXECUTABLE_PATH | grep -qx /usr/bin/chromium"
8585
runner: ubuntu-24.04-arm
8686
arch: arm64
8787
- image-suffix: claude-code-sandbox
88-
verify-command: "claude --version && mise --version && fish --version && which iptables && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp"
88+
verify-command: "claude --version && mise --version && fish --version && which iptables && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp"
8989
runner: ubuntu-24.04
9090
arch: amd64
9191
- image-suffix: claude-code-sandbox
92-
verify-command: "claude --version && mise --version && fish --version && which iptables && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp"
92+
verify-command: "claude --version && mise --version && fish --version && which iptables && rtk --version && ralphex --version && test -x /usr/local/bin/patch-playwright-mcp && test -r /etc/claude-code/managed-settings.json && jq -r '.hooks.SessionStart[0].hooks[0].command' /etc/claude-code/managed-settings.json | grep -qx /usr/local/bin/patch-playwright-mcp"
9393
runner: ubuntu-24.04-arm
9494
arch: arm64
9595
runs-on: ${{ matrix.runner }}

‎claude-code/.devcontainer/Dockerfile‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,16 @@ COPY --chmod=0755 patch-playwright-mcp.sh /usr/local/bin/patch-playwright-mcp
149149
# Image-policy settings at the Linux managed location (highest precedence,
150150
# outside any volume mount). Wires patch-playwright-mcp as a SessionStart hook
151151
# so cache dirs created by mid-session plugin auto-updates get patched before
152-
# the next session reads them. See issue #98.
153-
COPY --chmod=0644 managed-settings.json /etc/claude-code/managed-settings.json
152+
# the next session reads them. See issues #98, #101.
153+
#
154+
# Pre-create /etc/claude-code as root: BuildKit applies COPY --chmod to any
155+
# parent directories it auto-creates, which would leave the dir at mode 0644
156+
# (no execute bit, not traversable). Creating it explicitly avoids that and
157+
# makes the file readable for the node user at runtime.
158+
USER root
159+
RUN mkdir -p /etc/claude-code
160+
COPY --chown=root:root --chmod=0644 managed-settings.json /etc/claude-code/managed-settings.json
161+
USER node
154162

155163
# ── Claude Code CLI ───────────────────────────────────────────────────────────
156164
# Using npm instead of the native installer (curl claude.ai/install.sh | bash).

0 commit comments

Comments
 (0)