@@ -5,10 +5,13 @@ description: |
55 This composite action handles three scenarios that arise when running GitHub Actions
66 inside Docker containers:
77
8- 1. **Node.js glibc patching** — GitHub Actions ships its own Node.js 20 binary at
9- `/__e/node20/bin/node`, but older containers (e.g., Ubuntu 18.04) have a glibc
10- too old for that binary. This action detects the failure and replaces the binary with
11- an unofficial glibc-217-compatible build.
8+ 1. **Node.js glibc patching** — GitHub Actions ships its own Node.js binary (Node 20 at
9+ `/__e/node20/bin/node`, Node 24 at `/__e/node24/bin/node`), but older containers
10+ (e.g., Ubuntu 18.04) have a glibc too old for those binaries. This action detects the
11+ failure and replaces the Node 20 binary with the official unofficial-builds
12+ glibc-217 build. Node 24 patching is opt-in (see `node24-url`) and disabled by
13+ default, because no working glibc-217 Node 24 build exists yet — so containers that
14+ require Node 24 and a glibc older than 2.28 (GCC <= 6 / Clang <= 5) are unsupported.
1215 2. **nektos/act support** — When running locally with https://github.com/nektos/act[nektos/act],
1316 the `/__e/` mechanism does not exist. This action installs system Node.js as a fallback.
1417 3. **git safe.directory** — Containers run as root while the workspace may be owned by a
@@ -17,11 +20,14 @@ description: |
1720 This action must be the **first step** in a job, before `actions/checkout`. It requires
1821 no Node.js runtime because it is a composite action that uses only `bash` steps.
1922
20- NOTE: Node 24 unofficial glibc-217 builds do not currently exist. When GitHub Actions
21- migrates to Node 24, this action will need an updated build or a different strategy.
23+ NOTE: No working glibc-217 Node 24 build exists yet (nodejs/unofficial-builds#190 has not
24+ shipped one, and the Boost mirror build crashes on heavy child_process operations on old
25+ kernels), so Node 24 patching is opt-in and off by default. Containers needing Node 24 on
26+ a pre-2.28 glibc (GCC <= 6 / Clang <= 5) are unsupported until a known-good build can be
27+ passed via `node24-url`.
2228
2329 The xref:actions/cpp-matrix.adoc[] action automatically configures the volume mounts
24- (`/node20217`) that this action writes to for old containers.
30+ (`/node20217` and `/node24` ) that this action writes to for old containers.
2531
2632 == Usage
2733
@@ -73,6 +79,18 @@ inputs:
7379 Example: `https://archives.boost.io/misc/node/node-v20.9.0-linux-x64-glibc-217.tar.gz`
7480 required : false
7581 default : ' '
82+ node24-url :
83+ description : |
84+ URL of a glibc-217-compatible Node.js 24 tarball to patch into `/node24` for old
85+ containers. Node 24 patching is OPT-IN and disabled by default, because no working
86+ glibc-217 Node 24 build exists yet: nodejs/unofficial-builds#190 has not shipped one,
87+ and the Boost archive mirror build crashes silently on heavy `child_process`
88+ operations (e.g. @actions/cache) on kernels older than 5.3. As a result, containers
89+ that require Node 24 and a glibc older than 2.28 (GCC <= 6 / Clang <= 5) are not
90+ supported. Set this to a known-good build to re-enable Node 24 patching once one
91+ exists. Example: `https://example.com/node-v24.x.y-linux-x64-glibc-217.tar.xz`
92+ required : false
93+ default : ' '
7694 setup-safe-directory :
7795 description : |
7896 Whether to configure `git config --global --add safe.directory '*'`.
@@ -88,8 +106,11 @@ inputs:
88106
89107outputs :
90108 node-patched :
91- description : " 'true' if a glibc-217-compatible Node.js was downloaded and installed, 'false' otherwise."
109+ description : " 'true' if a glibc-217-compatible Node.js (20 and/or 24) was downloaded and installed, 'false' otherwise."
92110 value : ${{ steps.node-patch.outputs.node-patched }}
111+ node24-patched :
112+ description : " 'true' if a glibc-217-compatible Node.js 24 was downloaded and installed into /node24, 'false' otherwise."
113+ value : ${{ steps.node-patch.outputs.node24-patched }}
93114 node-installed :
94115 description : " 'true' if system Node.js was installed via apt-get (act workaround), 'false' otherwise."
95116 value : ${{ steps.act-node.outputs.node-installed }}
@@ -110,54 +131,73 @@ runs:
110131 run : |
111132 if [ '${{ inputs.trace-commands }}' = 'true' ]; then set -x; fi
112133
113- # Default output
134+ # Default outputs
114135 echo 'node-patched=false' >> "$GITHUB_OUTPUT"
136+ echo 'node24-patched=false' >> "$GITHUB_OUTPUT"
137+
138+ # patch_node LABEL MOUNT URL
139+ #
140+ # Replaces the runner-provided Node.js binary at /__e/node<LABEL> (which old
141+ # containers cannot run because of a too-new glibc) with a glibc-217 build
142+ # extracted into MOUNT — the writable directory cpp-matrix bind-mounts over
143+ # /__e/node<LABEL> for old containers. A writable MOUNT is the signal that we
144+ # are in such a container; without it there is nothing to patch.
145+ # Prints nothing to GITHUB_OUTPUT; returns 0 only when a patch was applied.
146+ patch_node() {
147+ local LABEL="$1" MOUNT="$2" URL="$3"
148+
149+ if [ ! -d "$MOUNT" ] || [ ! -w "$MOUNT" ]; then
150+ return 1
151+ fi
115152
116- # If the runner's Node.js path doesn't exist, we're not in a container — nothing to do
117- if [ ! -e /__e/node20/bin/node ]; then
118- exit 0
119- fi
120-
121- # Check if the runner's Node.js binary works
122- if /__e/node20/bin/node --version 2>/dev/null; then
123- echo "Node.js is functional: $(/__e/node20/bin/node --version)"
124- exit 0
125- fi
153+ # If the runner's binary already works, the container's glibc is new enough.
154+ if "/__e/node${LABEL}/bin/node" --version >/dev/null 2>&1; then
155+ echo "Node.js ${LABEL} is functional: $("/__e/node${LABEL}/bin/node" --version)"
156+ return 1
157+ fi
126158
127- echo "Node.js binary at /__e/node20 /bin/node is not functional (likely glibc incompatibility) "
159+ echo "Node.js ${LABEL} at /__e/node${LABEL} /bin/node is missing or not functional; patching from $URL "
128160
129- # Check if /node20217 volume mount exists (set up by cpp-matrix)
130- if [ ! -d /node20217 ] || [ ! -w /node20217 ]; then
131- echo "::warning::/node20217 is not available or not writable. Ensure cpp-matrix volume mounts are configured for old containers."
132- exit 0
133- fi
161+ # curl is required; xz-utils is required for .tar.xz (Node 24) tarballs.
162+ if ! command -v curl >/dev/null 2>&1 || ! command -v xz >/dev/null 2>&1; then
163+ if command -v apt-get >/dev/null 2>&1; then
164+ apt-get -o Acquire::Retries=3 update && apt-get -o Acquire::Retries=3 install -y curl xz-utils
165+ else
166+ echo "::warning::curl/xz are unavailable and apt-get cannot install them. Cannot patch Node.js ${LABEL}."
167+ return 1
168+ fi
169+ fi
134170
135- # Determine download URL
136- VERSION='${{ inputs.node-version }}'
137- URL='${{ inputs.node-url }}'
138- if [ -z "$URL" ]; then
139- URL="https://unofficial-builds.nodejs.org/download/release/v${VERSION}/node-v${VERSION}-linux-x64-glibc-217.tar.gz"
140- fi
171+ local TMP="/tmp/node${LABEL}-glibc217.tar"
172+ curl -fsSL --retry 3 --retry-delay 5 "$URL" -o "$TMP"
173+ # GNU tar autodetects gzip/xz compression on extract.
174+ tar -xf "$TMP" --strip-components 1 -C "$MOUNT"
175+ rm -f "$TMP"
141176
142- # Install curl if not available
143- if ! command -v curl >/dev/null 2>&1; then
144- if command -v apt-get >/dev/null 2>&1; then
145- apt-get -o Acquire::Retries=3 update && apt-get -o Acquire::Retries=3 install -y curl
146- else
147- echo "::warning::curl is not available and apt-get is not available to install it. Cannot download Node.js."
148- exit 0
177+ if "/__e/node${LABEL}/bin/node" --version >/dev/null 2>&1; then
178+ echo "Patched Node.js ${LABEL}: $("/__e/node${LABEL}/bin/node" --version)"
179+ return 0
149180 fi
181+ echo "::warning::Patched /__e/node${LABEL}/bin/node but it still does not run."
182+ return 1
183+ }
184+
185+ # Node 20 — official unofficial-builds glibc-217 tarball.
186+ NODE20_URL='${{ inputs.node-url }}'
187+ if [ -z "$NODE20_URL" ]; then
188+ NODE20_URL="https://unofficial-builds.nodejs.org/download/release/v${{ inputs.node-version }}/node-v${{ inputs.node-version }}-linux-x64-glibc-217.tar.gz"
189+ fi
190+ if patch_node 20 /node20217 "$NODE20_URL"; then
191+ echo 'node-patched=true' >> "$GITHUB_OUTPUT"
150192 fi
151193
152- # Download and extract
153- echo "Downloading Node.js from: $URL"
154- curl -fsSL "$URL" -o /tmp/node-glibc217.tar.gz
155- tar -xzf /tmp/node-glibc217.tar.gz --strip-components 1 -C /node20217
156- rm -f /tmp/node-glibc217.tar.gz
157-
158- # Verify
159- echo "Patched Node.js version: $(/__e/node20/bin/node --version)"
160- echo 'node-patched=true' >> "$GITHUB_OUTPUT"
194+ # Node 24 — opt-in only. No working glibc-217 Node 24 build exists yet, so this
195+ # is skipped unless the caller supplies a known-good `node24-url`.
196+ NODE24_URL='${{ inputs.node24-url }}'
197+ if [ -n "$NODE24_URL" ] && patch_node 24 /node24 "$NODE24_URL"; then
198+ echo 'node-patched=true' >> "$GITHUB_OUTPUT"
199+ echo 'node24-patched=true' >> "$GITHUB_OUTPUT"
200+ fi
161201
162202 - name : Install Node.js for act
163203 id : act-node
0 commit comments