# Toolchain runtimes: one recipe, two build targets. `maven` adds the JDK and Maven, `node` adds Node and npm. # Both inherit the published control image (minimal Ubuntu, Python, PyYAML, Git) and copy only the tool # distributions out of the official source images. No OS package, library or file tree of a source image is # inherited, and nothing is installed or downloaded when a pipeline runs. One image is built per catalog entry. # The JavaScript-action runtime (a single `node` binary) is provided once, in the shared base stage, and is # last on PATH so the toolchain's own `node` wins for builds. Build context is the repository root. # # CONTROL_IMAGE required, the published control image pinned as @sha256: # MAVEN_IMAGE source of the JDK and Maven (target maven); override per catalog entry # NODE_IMAGE source of Node and npm (target node); override per catalog entry # ACTIONS_NODE_IMAGE source of the JavaScript-action runtime, with ACTIONS_NODE_VERSION as its expected version # Source-image defaults are the digests in base-image-pins.json. Every source image must be pinned by digest. ARG CONTROL_IMAGE ARG MAVEN_IMAGE=maven:3.9.11-eclipse-temurin-21@sha256:6fdc855a6ed81d288ca7ca37ac6ff5e9308b612485c0801d70b25a858c83d237 ARG NODE_IMAGE=node:22.20.0-bookworm@sha256:915acd9e9b885ead0c620e27e37c81b74c226e0e1c8177f37a60217b6eabb0d7 ARG ACTIONS_NODE_IMAGE=node:22.20.0-bookworm@sha256:915acd9e9b885ead0c620e27e37c81b74c226e0e1c8177f37a60217b6eabb0d7 ARG ACTIONS_NODE_VERSION=22.20.0 FROM ${MAVEN_IMAGE} AS maven-source FROM ${NODE_IMAGE} AS node-source FROM ${ACTIONS_NODE_IMAGE} AS actions-node-source # Patched tool distributions (see runtime/TOOL-SECURITY.md). This stage only builds /out; it is never shipped. # Downloads use the control image's Python standard library and are verified against upstream checksums. FROM ${CONTROL_IMAGE} AS patched-tools COPY --from=maven-source /usr/share/maven/ /out/maven/ RUN set -eu; \ mkdir /download; \ python_fetch() { python3 -I -c 'import base64,hashlib,sys,urllib.request as u; url,algo,want,out=sys.argv[1:]; data=u.urlopen(url,timeout=60).read(); digest=hashlib.new(algo,data).digest(); got=digest.hex() if algo=="sha256" else base64.b64encode(digest).decode(); got==want or sys.exit(f"{url}: checksum mismatch, got {got}"); open(out,"wb").write(data)' "$@"; }; \ central=https://repo.maven.apache.org/maven2; registry=https://registry.npmjs.org; \ python_fetch "${central}/com/google/guava/guava/33.7.2-jre/guava-33.7.2-jre.jar" sha256 b530942257fb935f8b2cfaa5f8eb5bd59c555fd8e8d01b8ce98912e077ea606c /download/guava-33.7.2-jre.jar; \ python_fetch "${central}/org/codehaus/plexus/plexus-utils/3.6.1/plexus-utils-3.6.1.jar" sha256 05a63effd67e2d6b9d610cc82e2bd7473289d34802e57a529b28110f28af5679 /download/plexus-utils-3.6.1.jar; \ python_fetch "${registry}/npm/-/npm-11.21.1.tgz" sha512 YTzL6QBEJXyHcTu0JiUc7RKMp+mzlJtl7rGLmERaXUQeFHIMc9atdr6G9M9C8vDWbv40epoTQAzNL2/PtZVDhQ== /download/npm-11.21.1.tgz; \ python_fetch "${registry}/undici/-/undici-6.29.1.tgz" sha512 EvRlUt/QqAEvXdQeRMYUfwOQsfzpDztykrG2owGEE/rBOBoYv9uF39ZzsNDh4lTFHhTr2XOZjKkYgueD12ZmGA== /download/undici-6.29.1.tgz; \ lib=/out/maven/lib; \ test -f "${lib}/guava-33.4.8-jre.jar"; \ test -f "${lib}/plexus-utils-3.6.0.jar"; \ rm "${lib}/guava-33.4.8-jre.jar" "${lib}/plexus-utils-3.6.0.jar"; \ cp /download/guava-33.7.2-jre.jar /download/plexus-utils-3.6.1.jar "${lib}/"; \ test "$(find /out/maven -name 'guava-*.jar' | wc -l)" = 1; \ test "$(find /out/maven -name 'plexus-utils-*.jar' | wc -l)" = 1; \ mkdir /out/npm; \ tar -xzf /download/npm-11.21.1.tgz -C /out/npm --strip-components=1; \ grep -q '"version": "11.21.1"' /out/npm/package.json; \ grep -q '"version": "6.29.0"' /out/npm/node_modules/undici/package.json; \ rm -r /out/npm/node_modules/undici; \ mkdir /out/npm/node_modules/undici; \ tar -xzf /download/undici-6.29.1.tgz -C /out/npm/node_modules/undici --strip-components=1; \ grep -q '"version": "6.29.1"' /out/npm/node_modules/undici/package.json; \ test "$(find /out/npm -type d -name undici -path '*node_modules/undici' | wc -l)" = 1 FROM ${CONTROL_IMAGE} AS base ARG CONTROL_IMAGE ARG ACTIONS_NODE_IMAGE ARG ACTIONS_NODE_VERSION COPY --from=actions-node-source /usr/local/bin/node /opt/actions-node/bin/node ENV PATH="${PATH}:/opt/actions-node/bin" RUN set -eu; \ printf '%s' "${CONTROL_IMAGE}" | grep -Eq '@sha256:[0-9a-f]{64}$' || { echo 'CONTROL_IMAGE must be pinned by @sha256 digest' >&2; exit 1; }; \ printf '%s' "${ACTIONS_NODE_IMAGE}" | grep -Eq '@sha256:[0-9a-f]{64}$' || { echo 'ACTIONS_NODE_IMAGE must be pinned by @sha256 digest' >&2; exit 1; }; \ test "$(/opt/actions-node/bin/node --version)" = "v${ACTIONS_NODE_VERSION}" LABEL io.platform-ci.control-image="${CONTROL_IMAGE}" FROM base AS maven ARG MAVEN_IMAGE COPY --from=maven-source /opt/java/openjdk/ /opt/java/openjdk/ COPY --from=patched-tools /out/maven/ /usr/share/maven/ ENV JAVA_HOME=/opt/java/openjdk ENV MAVEN_HOME=/usr/share/maven ENV PATH="${JAVA_HOME}/bin:${MAVEN_HOME}/bin:${PATH}" # Temurin's Ubuntu images point the JDK trust store at a file made by an OS Java package. That package is not # installed here, so when the link has no target the store is built from the system CA certificates instead. RUN set -eu; \ printf '%s' "${MAVEN_IMAGE}" | grep -Eq '@sha256:[0-9a-f]{64}$' || { echo 'MAVEN_IMAGE must be pinned by @sha256 digest' >&2; exit 1; }; \ store="${JAVA_HOME}/lib/security/cacerts"; \ if [ ! -e "${store}" ]; then \ rm -f "${store}"; \ mkdir /tmp/ca-split; \ csplit -s -z -f /tmp/ca-split/cert- /etc/ssl/certs/ca-certificates.crt '/-----BEGIN CERTIFICATE-----/' '{*}'; \ for certificate in /tmp/ca-split/cert-*; do \ keytool -importcert -noprompt -trustcacerts -storetype PKCS12 -keystore "${store}" -storepass changeit -alias "$(basename "${certificate}")" -file "${certificate}" >/dev/null; \ done; \ rm -rf /tmp/ca-split; \ fi; \ keytool -list -cacerts -storepass changeit | grep -q trustedCertEntry; \ java -version; \ mvn --version; \ mvn --version | grep -q 'Apache Maven 3.9.11' FROM base AS node ARG NODE_IMAGE COPY --from=node-source /usr/local/bin/node /usr/local/bin/node COPY --from=patched-tools /out/npm/ /usr/local/lib/node_modules/npm/ RUN set -eu; \ printf '%s' "${NODE_IMAGE}" | grep -Eq '@sha256:[0-9a-f]{64}$' || { echo 'NODE_IMAGE must be pinned by @sha256 digest' >&2; exit 1; }; \ ln -s ../lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm; \ ln -s ../lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx; \ test "$(npm --version)" = "11.21.1"; \ node --version; \ npm --version