setup-node never consults `node` on PATH; its only fast path is a probe of /opt/hostedtoolcache, which the ephemeral act_runner job containers always miss, so every job paid a ~50 MB Node download. docker/Dockerfile extends the runner's default catthehacker/act image with the Node distribution overlaid at the exact tool-cache layout, so setup-node finds 26.8.2 and skips the fetch while node-version-file, cache: npm and registry-url keep working unchanged. Image layers dedupe against the base the host already pulled and prune via normal docker hygiene — the cleanup story a host bind of /opt/hostedtoolcache lacks. To make the bake deterministic, .node-version is pinned to the exact 26.8.2 the image carries; scripts/runner-image.sh guards that coupling and builds/pushes the tag the three node jobs now reference via container.image. Ops follow-up (outside the repo): build once with `npm run build:runner-image -- --push` on a machine with registry creds. If the package is private, the runner needs container registry credentials in its config.
31 lines
1.6 KiB
Docker
31 lines
1.6 KiB
Docker
# CI job image for the Gitea act_runner: the runner's default job image with
|
|
# Node pre-planted where actions/setup-node looks first.
|
|
#
|
|
# Why this layout: setup-node ignores `node` on PATH; its only fast path is a
|
|
# probe of /opt/hostedtoolcache/node/<version>/<arch>. Without an entry there
|
|
# it downloads the ~50 MB distribution on EVERY job (the runner's job
|
|
# containers are ephemeral, so its tool cache never survives a job). The
|
|
# official node images keep exactly the layout setup-node expects under
|
|
# /usr/local, so this layer is a pure file overlay — no scripts, no env.
|
|
#
|
|
# Why not a host bind of /opt/hostedtoolcache: binds never self-prune. Docker
|
|
# images are content-addressed: the base layers dedupe against the act image
|
|
# the host already has, and `docker image prune` / re-pulls are the cleanup
|
|
# story.
|
|
#
|
|
# NODE_VERSION must match `.node-version` exactly. setup-node resolves a float
|
|
# like `26` to the latest known patch at runtime, so a bump silently busts the
|
|
# baked entry; `.node-version` is pinned to x.y.z and scripts/runner-image.sh
|
|
# guards the coupling. Rebuild + repoint `container.image` in
|
|
# .gitea/workflows/ci.yml on every bump.
|
|
FROM catthehacker/ubuntu:act-latest
|
|
|
|
ARG NODE_VERSION=26.8.2
|
|
|
|
# node image: bin/ + lib/ under /usr/local → tool cache: bin/ + lib/ under <ver>/x64.
|
|
COPY --from=node:${NODE_VERSION} /usr/local /opt/hostedtoolcache/node/${NODE_VERSION}/x64
|
|
|
|
# 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
|