From 048a8870e65bbcfc995a68d8b2289f64441459ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Mon, 14 Sep 2026 15:59:23 +0000 Subject: [PATCH] :bug: Write the tool-cache completion marker into the image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit setup-node still downloaded although the image carried a perfect /opt/hostedtoolcache/node/26.8.2/x64/ tree. actions/tool-cache accepts a cached tool only when the sibling marker /.complete exists (tc.find() tests it explicitly); a bare directory is ignored, so the probe fell through to the download. The marker is what tc.cacheDir() writes after installing a tool, so the baked entry must create it too. Job-container diagnostics also confirmed the path was never in question: RUNNER_TOOL_CACHE=/opt/hostedtoolcache, no mount over it, node -v from the baked path prints v26.8.2. CONTRIBUTING records both invariants — the marker, and the fact that a Dockerfile change keeps the same tag, which forcePull=false can hide. --- .gitea/workflows/ci.yml | 2 ++ CONTRIBUTING.md | 5 +++++ docker/Dockerfile | 7 +++++++ 3 files changed, 14 insertions(+) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 2e35e8f..0e15d4a 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -75,6 +75,8 @@ jobs: grep -E 'hostedtoolcache|workspace|overlay' /proc/mounts || true ls -la /opt/hostedtoolcache || true ls -la /opt/hostedtoolcache/node || true + ls -la "/opt/hostedtoolcache/node/$(tr -d '[:space:]' < .node-version)/" || true + test -f "/opt/hostedtoolcache/node/$(tr -d '[:space:]' < .node-version)/x64.complete" && echo MARKER-PRESENT || echo MARKER-MISSING ls -la "/opt/hostedtoolcache/node/$(tr -d '[:space:]' < .node-version)/x64/bin" || true "/opt/hostedtoolcache/node/$(tr -d '[:space:]' < .node-version)/x64/bin/node" -v || true env | grep -iE 'RUNNER|TOOL|CACHE' || true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ee23cfd..786164a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -102,6 +102,11 @@ Bumping Node is one coordinated change, committed as a unit: Skipping step 2 fails CI at image pull; skipping step 3 silently reverts to the per-job download. +Two invariants the image must satisfy for the probe to hit, both easy to break: + +- **The `x64.complete` marker.** `actions/tool-cache` accepts a cached tool only when `/.complete` exists next to the directory (`tc.find()` checks it); a plausible-looking `node//x64/` alone is ignored and the download happens anyway. See the comment in [docker/Dockerfile](./docker/Dockerfile). +- **Tag freshness.** The tag encodes only the Node version, so a Dockerfile change (like the marker above) produces _new content under an unchanged tag_. `act_runner` skips the pull when a tag of that name already exists locally (`forcePull=false` in the job log), so the runner must either force-pull (`force_pull` under `container:` in its `config.yaml`, if the installed version has it) or have the tag removed on the runner host (`docker rmi gitea.e1nsnull.de/tmu/act-ci:`) after any image change. Symptom of getting this wrong: CI keeps running the previous image while the registry shows the new digest. + ## Publishing workflow Publishing is CI-only by policy. Local `npm publish` is not supported. The maintainer triggers releases from `main`: diff --git a/docker/Dockerfile b/docker/Dockerfile index babcb5a..8cb0475 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -39,6 +39,13 @@ ARG NODE_VERSION=26.8.2 # node image: bin/ + lib/ under /usr/local → tool cache: bin/ + lib/ under /x64. COPY --from=nodebase /usr/local /opt/hostedtoolcache/node/${NODE_VERSION}/x64 +# actions/tool-cache only accepts a cached tool when the sibling marker file +# "/.complete" exists — tc.find() checks it and falls back to +# downloading otherwise, however complete the directory is. The marker is what +# tc.cacheDir() writes after *it* installs a tool, so a pre-baked entry has to +# reproduce it explicitly. +RUN touch "/opt/hostedtoolcache/node/${NODE_VERSION}/x64.complete" + # Fail the build (not CI) if the overlay or the version arg were wrong. # Shell form on purpose: exec form (`RUN [...]`) does not expand ARG values. RUN "/opt/hostedtoolcache/node/${NODE_VERSION}/x64/bin/node" --version