From cdd1fe03bdecef6274dcdd3eb0aae6f63eb7b6d3 Mon Sep 17 00:00:00 2001 From: brettheap Date: Sat, 25 Jul 2026 06:49:14 -0400 Subject: [PATCH 1/4] fix: give Corepack a user-writable cache --- devBenches/devcontainer.test/README.md | 4 ++-- devBenches/devcontainer.test/test.sh | 3 +++ user-layer/Dockerfile | 11 ++++++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/devBenches/devcontainer.test/README.md b/devBenches/devcontainer.test/README.md index 9765d55..e7ed39e 100644 --- a/devBenches/devcontainer.test/README.md +++ b/devBenches/devcontainer.test/README.md @@ -6,7 +6,7 @@ Test harness for `dev-bench-base:$USER` - the Layer 3 user image built on top of Layer 1a adds developer tools on top of Layer 0: - Python 3.x with pip and development tools (black, flake8, isort, pylint, pytest, ipython) -- Node.js LTS with npm and yarn +- Node.js LTS with npm, yarn, and a user-writable Corepack cache for pnpm - Python package managers (uv) - Spec-driven tools (`specify`, `openspec`) - Speckit worktree bootstrap (`speckit-worktree-enable`) @@ -42,7 +42,7 @@ docker compose down The `test.sh` script validates: - ✅ Python development tools -- ✅ Node.js development tools +- ✅ Node.js development tools and unprivileged pnpm/Corepack operation - ✅ Python package managers (uv) - ✅ Spec-driven tools (`specify`, `openspec`) - ✅ Worktree-mode Speckit bootstrap installation diff --git a/devBenches/devcontainer.test/test.sh b/devBenches/devcontainer.test/test.sh index 233077c..42f96e4 100755 --- a/devBenches/devcontainer.test/test.sh +++ b/devBenches/devcontainer.test/test.sh @@ -68,6 +68,9 @@ echo "=== Node.js Development ===" test_tool_output "Node.js" "node --version" test_tool_output "npm" "npm --version" test_tool_output "yarn" "yarn --version" +test_tool_output "pnpm as unprivileged user" "pnpm --version" +test_tool "Corepack cache is user-owned and writable" \ + "test \"\$COREPACK_HOME\" = \"\$HOME/.cache/corepack\" && test -d \"\$COREPACK_HOME\" && test -w \"\$COREPACK_HOME\"" test_tool "/usr/local/bin in PATH" "echo \$PATH | grep -q '/usr/local/bin'" echo "" diff --git a/user-layer/Dockerfile b/user-layer/Dockerfile index 53917ac..aaf0dfd 100644 --- a/user-layer/Dockerfile +++ b/user-layer/Dockerfile @@ -18,7 +18,7 @@ FROM ${BASE_IMAGE} # Container version labels LABEL layer="3" LABEL layer.name="user-layer" -LABEL layer.version="1.2.0" +LABEL layer.version="1.2.1" LABEL layer.description="User personalization layer" # Build arguments @@ -28,6 +28,11 @@ ARG USER_GID=1000 ARG DOCKER_SOCKET_GID="" ARG EXTRA_CHOWN_DIRS="" +# Corepack downloads package-manager shims and versions on demand. Keep its +# mutable cache in the Layer 3 user's home rather than the root-owned cache +# inherited from a shared base image. +ENV COREPACK_HOME=/home/${USERNAME}/.cache/corepack + USER root # ======================================== @@ -100,6 +105,10 @@ RUN if [ -n "$EXTRA_CHOWN_DIRS" ]; then \ USER $USERNAME +# Pre-create the user-owned Corepack cache so pnpm/yarn work immediately in +# interactive and non-interactive bench sessions. +RUN mkdir -p "$COREPACK_HOME" + # Pre-create .vscode-server so volume mounts inherit correct ownership RUN mkdir -p /home/$USERNAME/.vscode-server/extensions From 820dbcc5e42ff54bb6cfc1b41334089c1bd72bcc Mon Sep 17 00:00:00 2001 From: brettheap Date: Sat, 25 Jul 2026 06:49:14 -0400 Subject: [PATCH 2/4] fix: give Corepack a user-writable cache --- devBenches/devcontainer.test/README.md | 4 ++-- devBenches/devcontainer.test/test.sh | 3 +++ user-layer/Dockerfile | 11 +++++++++- user-layer/corepack-user-cache | 28 ++++++++++++++++++++++++++ 4 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 user-layer/corepack-user-cache diff --git a/devBenches/devcontainer.test/README.md b/devBenches/devcontainer.test/README.md index 9765d55..e7ed39e 100644 --- a/devBenches/devcontainer.test/README.md +++ b/devBenches/devcontainer.test/README.md @@ -6,7 +6,7 @@ Test harness for `dev-bench-base:$USER` - the Layer 3 user image built on top of Layer 1a adds developer tools on top of Layer 0: - Python 3.x with pip and development tools (black, flake8, isort, pylint, pytest, ipython) -- Node.js LTS with npm and yarn +- Node.js LTS with npm, yarn, and a user-writable Corepack cache for pnpm - Python package managers (uv) - Spec-driven tools (`specify`, `openspec`) - Speckit worktree bootstrap (`speckit-worktree-enable`) @@ -42,7 +42,7 @@ docker compose down The `test.sh` script validates: - ✅ Python development tools -- ✅ Node.js development tools +- ✅ Node.js development tools and unprivileged pnpm/Corepack operation - ✅ Python package managers (uv) - ✅ Spec-driven tools (`specify`, `openspec`) - ✅ Worktree-mode Speckit bootstrap installation diff --git a/devBenches/devcontainer.test/test.sh b/devBenches/devcontainer.test/test.sh index 233077c..f04950c 100755 --- a/devBenches/devcontainer.test/test.sh +++ b/devBenches/devcontainer.test/test.sh @@ -68,6 +68,9 @@ echo "=== Node.js Development ===" test_tool_output "Node.js" "node --version" test_tool_output "npm" "npm --version" test_tool_output "yarn" "yarn --version" +test_tool_output "pnpm as unprivileged user" "pnpm --version" +test_tool "Corepack cache is user-owned and writable" \ + "runtime_home=\$(getent passwd \"\$(id -u)\" | cut -d: -f6); cache=\"\$runtime_home/.cache/corepack\"; test -d \"\$cache\" && test -w \"\$cache\" && test \"\$(stat -c '%u' \"\$cache\")\" = \"\$(id -u)\"" test_tool "/usr/local/bin in PATH" "echo \$PATH | grep -q '/usr/local/bin'" echo "" diff --git a/user-layer/Dockerfile b/user-layer/Dockerfile index 53917ac..a76e858 100644 --- a/user-layer/Dockerfile +++ b/user-layer/Dockerfile @@ -18,7 +18,7 @@ FROM ${BASE_IMAGE} # Container version labels LABEL layer="3" LABEL layer.name="user-layer" -LABEL layer.version="1.2.0" +LABEL layer.version="1.2.1" LABEL layer.description="User personalization layer" # Build arguments @@ -30,6 +30,15 @@ ARG EXTRA_CHOWN_DIRS="" USER root +# Corepack's shared base-layer cache is root-owned. Route Corepack and its +# package-manager shims through a runtime wrapper that selects a writable cache +# from the effective user's passwd home, including for non-interactive calls. +COPY corepack-user-cache /usr/local/libexec/workbenches-corepack-user-cache +RUN chmod 0755 /usr/local/libexec/workbenches-corepack-user-cache && \ + for command in corepack pnpm pnpx yarn yarnpkg; do \ + ln -sfn /usr/local/libexec/workbenches-corepack-user-cache "/usr/local/bin/$command"; \ + done + # ======================================== # USER CREATION # ======================================== diff --git a/user-layer/corepack-user-cache b/user-layer/corepack-user-cache new file mode 100644 index 0000000..90b90e4 --- /dev/null +++ b/user-layer/corepack-user-cache @@ -0,0 +1,28 @@ +#!/bin/sh +set -eu + +command_name=${0##*/} +case "$command_name" in + corepack|pnpm|pnpx|yarn|yarnpkg) ;; + *) + echo "Unsupported Corepack command: $command_name" >&2 + exit 64 + ;; +esac + +# Respect an explicit caller override. Replace only the shared image default, +# which is root-owned and therefore unusable by Layer 3 runtime users. +case "${COREPACK_HOME:-}" in + ""|/opt/corepack) + runtime_home=$(getent passwd "$(id -u)" | cut -d: -f6) + if [ -z "$runtime_home" ]; then + echo "Could not determine the effective user's home directory" >&2 + exit 1 + fi + COREPACK_HOME="$runtime_home/.cache/corepack" + export COREPACK_HOME + ;; +esac + +mkdir -p "$COREPACK_HOME" +exec "/usr/bin/$command_name" "$@" From b837741feff011332e944c0138d3f5131ffac67c Mon Sep 17 00:00:00 2001 From: brettheap Date: Sat, 25 Jul 2026 06:55:18 -0400 Subject: [PATCH 3/4] fix: scope Corepack cache to effective user --- user-layer/Dockerfile | 9 --------- 1 file changed, 9 deletions(-) diff --git a/user-layer/Dockerfile b/user-layer/Dockerfile index 97bea2a..a76e858 100644 --- a/user-layer/Dockerfile +++ b/user-layer/Dockerfile @@ -28,11 +28,6 @@ ARG USER_GID=1000 ARG DOCKER_SOCKET_GID="" ARG EXTRA_CHOWN_DIRS="" -# Corepack downloads package-manager shims and versions on demand. Keep its -# mutable cache in the Layer 3 user's home rather than the root-owned cache -# inherited from a shared base image. -ENV COREPACK_HOME=/home/${USERNAME}/.cache/corepack - USER root # Corepack's shared base-layer cache is root-owned. Route Corepack and its @@ -114,10 +109,6 @@ RUN if [ -n "$EXTRA_CHOWN_DIRS" ]; then \ USER $USERNAME -# Pre-create the user-owned Corepack cache so pnpm/yarn work immediately in -# interactive and non-interactive bench sessions. -RUN mkdir -p "$COREPACK_HOME" - # Pre-create .vscode-server so volume mounts inherit correct ownership RUN mkdir -p /home/$USERNAME/.vscode-server/extensions From 1b9311ceaa2c189b997220afa515d4d9edd42477 Mon Sep 17 00:00:00 2001 From: brettheap Date: Sat, 25 Jul 2026 06:57:42 -0400 Subject: [PATCH 4/4] docs: attribute Corepack cache to user layer --- devBenches/devcontainer.test/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/devBenches/devcontainer.test/README.md b/devBenches/devcontainer.test/README.md index e7ed39e..e9cc383 100644 --- a/devBenches/devcontainer.test/README.md +++ b/devBenches/devcontainer.test/README.md @@ -6,7 +6,7 @@ Test harness for `dev-bench-base:$USER` - the Layer 3 user image built on top of Layer 1a adds developer tools on top of Layer 0: - Python 3.x with pip and development tools (black, flake8, isort, pylint, pytest, ipython) -- Node.js LTS with npm, yarn, and a user-writable Corepack cache for pnpm +- Node.js LTS with npm and yarn - Python package managers (uv) - Spec-driven tools (`specify`, `openspec`) - Speckit worktree bootstrap (`speckit-worktree-enable`) @@ -17,6 +17,9 @@ Layer 1a adds developer tools on top of Layer 0: - Zsh and oh-my-zsh with plugins - PATH configuration for all dev tools +The Layer 3 user image adds the effective-user Corepack cache exercised by the +unprivileged pnpm checks in this harness. + ## Quick Start ```bash