Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/jq/matrix.jq
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Build-matrix building blocks shared by build.yml (PR) and deploy.yml:
# jq -L .github/jq 'include "matrix"; ...' matrix.json
# The image variants (Dockerfile target), the platforms and the layer cache
# scope live here only, so the two workflows cannot drift apart.

def targets: {std: "runner", ndk: "runner-ndk", slim: "runner-slim"};

def legs: {
amd64: {platform: "linux/amd64", arch: "amd64", runner: "ubuntu-latest"},
arm64: {platform: "linux/arm64", arch: "arm64", runner: "ubuntu-24.04-arm"}
};

# One build leg. The standard image keeps the plain per-arch cache scope, the
# other variants their own (they also read the standard one).
def leg($java; $node; $variant; $arch):
{java_version: $java, node_version: $node, variant: $variant, target: targets[$variant]}
+ legs[$arch]
| . + {cache_scope: (if $variant == "std" then $arch else "\($variant)-\($arch)" end)};
35 changes: 35 additions & 0 deletions .github/jq/tags.jq
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Image tags of deploy.yml, one list per matrix entry:
# jq -L .github/jq 'include "matrix"; include "tags"; ...' matrix.json
# $v holds the Dockerfile versions: runner_version, compile_sdk, build_tools,
# ndk_version. $dj / $dn are the default JDK / Node major from matrix.json.

# The fully pinned tag, baked into the image as RUNNER_IMAGE_TAG; it never
# changes meaning.
def image_tag($v):
if .variant == "slim" then "\($v.runner_version)-jdk\(.java_version)-node\(.node_version)-slim"
else "\($v.runner_version)-jdk\(.java_version)-sdk\($v.compile_sdk)-node\(.node_version)\(if .variant == "ndk" then "-ndk" else "" end)"
end;

# Every tag the merged manifest list is published under.
def tags($v; $dj; $dn):
. as $e
| "\($v.runner_version)-jdk\($e.java_version)-sdk\($v.compile_sdk)" as $sdk
| if $e.variant == "std" then
[ (if $e.node_version == $dn then "jdk\($e.java_version)" else empty end),
(if $e.java_version == $dj then "node\($e.node_version)" else empty end),
"jdk\($e.java_version)-node\($e.node_version)",
"\($sdk)-node\($e.node_version)",
# Every Gradle-pinnable SDK component in the tag (compileSdk, build-tools), so
# consumers can let Renovate derive them from the image pin.
"\($sdk)-bt\($v.build_tools)-node\($e.node_version)",
(if $e.java_version == $dj and $e.node_version == $dn then "latest" else empty end) ]
elif $e.variant == "ndk" then
# Only the ndk variant ships an NDK, so only it spells the NDK version out
# (upstream's -sdk-ndk- and -sdk-bt-ndk- tags; Gradle ndkVersion via Renovate).
[ "ndk",
"\($sdk)-node\($e.node_version)-ndk",
"\($sdk)-ndk\($v.ndk_version)-node\($e.node_version)",
"\($sdk)-bt\($v.build_tools)-ndk\($v.ndk_version)-node\($e.node_version)" ]
else
["slim", ($e | image_tag($v))]
end;
57 changes: 29 additions & 28 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
# nullglob: a pattern that matches nothing (e.g. hosts/*.sh) must
# drop out instead of being passed to shellcheck as a literal.
shopt -s nullglob
files=(*.sh hosts/*.sh tests/*.sh)
files=(*.sh hosts/*.sh tests/*.sh tests/bench/*.sh)
if [ "${#files[@]}" -eq 0 ]; then
echo "::error::No shell scripts found to check"
exit 1
Expand All @@ -41,7 +41,8 @@ jobs:
set -euo pipefail
for t in tests/*.sh; do
# image-smoke.sh needs a built image; it runs in the build job.
[ "$(basename "$t")" = image-smoke.sh ] && continue
# harness.sh and lib.sh are sourced by the tests, not tests.
case "$(basename "$t")" in image-smoke.sh | harness.sh | lib.sh) continue ;; esac
bash "$t"
done

Expand All @@ -51,9 +52,26 @@ jobs:
timeout-minutes: 5
outputs:
matrix: ${{ steps.generate.outputs.matrix }}
runner_tag: ${{ steps.versions.outputs.runner_tag }}
steps:
- uses: actions/checkout@v7

# Only VERSION is needed here: JDK and Node come from the matrix, and the
# SDK args are consumed straight from the Dockerfile defaults since
# nothing in a PR build needs them for tagging.
- name: Extract versions from Dockerfile
id: versions
run: |
set -euo pipefail
RUNNER_TAG=$(grep -m1 '^ARG VERSION=' Dockerfile | cut -d= -f2)

if [ -z "$RUNNER_TAG" ]; then
echo "::error::Failed to extract versions from Dockerfile"
exit 1
fi

echo "runner_tag=${RUNNER_TAG}" >> "$GITHUB_OUTPUT"

- name: Generate matrix from matrix.json
id: generate
run: |
Expand All @@ -67,20 +85,19 @@ jobs:
# also gets the image smoke test (smoke: true), and the opt-in
# runner-ndk and runner-slim targets get one amd64 build + smoke test
# each, since deploy.yml publishes them.
MATRIX=$(jq -c '
# The variants/legs table is shared with deploy.yml (.github/jq/matrix.jq).
MATRIX=$(jq -c -L .github/jq '
include "matrix";
.default_jdk as $dj | .default_node as $dn
| ( [ .java_versions[] | {java_version: ., node_version: $dn} ]
+ [ .node_versions[] | {java_version: $dj, node_version: .} ]
| unique ) as $combos
| def leg($arch; $runner): {platform: "linux/\($arch)", arch: $arch, runner: $runner};
def std: {variant: "std", target: "runner"};
{ include:
( [ $combos[] | . + std + leg("amd64"; "ubuntu-latest")
| { include:
( [ $combos[] | leg(.java_version; .node_version; "std"; "amd64")
+ {smoke: (.java_version == $dj and .node_version == $dn)} ]
+ [ {java_version: $dj, node_version: $dn} + std + leg("arm64"; "ubuntu-24.04-arm") + {smoke: false} ]
+ [ {java_version: $dj, node_version: $dn} + leg("amd64"; "ubuntu-latest") + {smoke: true, variant: "ndk", target: "runner-ndk"} ]
+ [ {java_version: $dj, node_version: $dn} + leg("amd64"; "ubuntu-latest") + {smoke: true, variant: "slim", target: "runner-slim"} ] )
| map(. + {cache_scope: (if .variant == "std" then .arch else "\(.variant)-\(.arch)" end)}) }' matrix.json)
+ [ leg($dj; $dn; "std"; "arm64") + {smoke: false} ]
+ [ leg($dj; $dn; "ndk"; "amd64") + {smoke: true} ]
+ [ leg($dj; $dn; "slim"; "amd64") + {smoke: true} ] ) }' matrix.json)
echo "matrix=${MATRIX}" >> "$GITHUB_OUTPUT"

build:
Expand All @@ -94,22 +111,6 @@ jobs:
steps:
- uses: actions/checkout@v7

- name: Extract versions from Dockerfile
id: versions
run: |
set -euo pipefail
# Only VERSION is extracted here: JDK and Node come from the matrix,
# and the SDK args are consumed straight from the Dockerfile defaults
# since nothing in a PR build needs them for tagging.
RUNNER_TAG=$(grep -m1 '^ARG VERSION=' Dockerfile | cut -d= -f2)

if [ -z "$RUNNER_TAG" ]; then
echo "::error::Failed to extract versions from Dockerfile"
exit 1
fi

echo "runner_tag=${RUNNER_TAG}" >> "$GITHUB_OUTPUT"

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4

Expand All @@ -132,7 +133,7 @@ jobs:
${{ matrix.cache_scope != matrix.arch && format('type=gha,scope={0}', matrix.cache_scope) || '' }}
cache-to: type=gha,mode=max,scope=${{ matrix.cache_scope }}
build-args: |
VERSION=${{ steps.versions.outputs.runner_tag }}
VERSION=${{ needs.matrix.outputs.runner_tag }}
JAVA_VERSION=${{ matrix.java_version }}
NODE_VERSION=${{ matrix.node_version }}
RUNNER_IMAGE_TAG=pr-${{ github.event.pull_request.number }}
Expand Down
153 changes: 60 additions & 93 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,73 @@ jobs:
outputs:
build_matrix: ${{ steps.generate.outputs.build_matrix }}
merge_matrix: ${{ steps.generate.outputs.merge_matrix }}
runner_tag: ${{ steps.versions.outputs.runner_tag }}
steps:
- uses: actions/checkout@v7

# The only place the Dockerfile is read: the build jobs take the VERSION
# build-arg from this job's outputs, and every image tag is computed here,
# per matrix entry.
- name: Extract versions from Dockerfile
id: versions
run: |
set -euo pipefail
RUNNER_TAG=$(grep -m1 '^ARG VERSION=' Dockerfile | cut -d= -f2)
RUNNER_VERSION=$(echo "$RUNNER_TAG" | cut -d- -f1)
COMPILE_SDK=$(grep -m1 '^ARG COMPILE_SDK=' Dockerfile | cut -d= -f2)
BUILD_TOOLS=$(grep -m1 '^ARG BUILD_TOOLS=' Dockerfile | cut -d= -f2)
NDK_VERSION=$(grep -m1 '^ARG NDK_VERSION=' Dockerfile | cut -d= -f2)

if [ -z "$RUNNER_TAG" ] || [ -z "$RUNNER_VERSION" ] || [ -z "$COMPILE_SDK" ] || [ -z "$BUILD_TOOLS" ] || [ -z "$NDK_VERSION" ]; then
echo "::error::Failed to extract versions from Dockerfile"
exit 1
fi

{
echo "runner_tag=${RUNNER_TAG}"
echo "runner_version=${RUNNER_VERSION}"
echo "compile_sdk=${COMPILE_SDK}"
echo "build_tools=${BUILD_TOOLS}"
echo "ndk_version=${NDK_VERSION}"
} >> "$GITHUB_OUTPUT"

- name: Generate matrices from matrix.json
id: generate
env:
RUNNER_VERSION: ${{ steps.versions.outputs.runner_version }}
COMPILE_SDK: ${{ steps.versions.outputs.compile_sdk }}
BUILD_TOOLS: ${{ steps.versions.outputs.build_tools }}
NDK_VERSION: ${{ steps.versions.outputs.ndk_version }}
run: |
set -euo pipefail
# Every JDK x Node pair is published as the `runner` target (variant
# "std"). The default pair is additionally published as `runner-ndk`
# (the image plus the NDK) and `runner-slim` (no Android SDK). Each
# variant has its own digests, cache scope and tags so they never mix
# with the standard image.
ALL=$(jq -c '
.default_jdk as $dj | .default_node as $dn
| [ .java_versions[] as $j | .node_versions[] as $n |
{ java_version: $j, node_version: $n,
is_default_jdk: ($j == $dj),
is_default_node: ($n == $dn),
variant: "std", target: "runner" } ] as $std
| ( [ $std[] | select(.is_default_jdk and .is_default_node) ] ) as $default_pair
| ( [ $default_pair[] | . + {variant: "ndk", target: "runner-ndk"} ]
+ [ $default_pair[] | . + {variant: "slim", target: "runner-slim"} ] ) as $extra
| ( $std + $extra ) as $combos
# with the standard image. The variant/platform/cache-scope table is
# shared with build.yml (.github/jq/matrix.jq); the tag rules are in
# .github/jq/tags.jq. A build entry carries the tag baked into the
# image, a merge entry the metadata-action `type=raw` lines of the
# manifest list.
ALL=$(jq -c -L .github/jq \
--arg runner_version "$RUNNER_VERSION" --arg compile_sdk "$COMPILE_SDK" \
--arg build_tools "$BUILD_TOOLS" --arg ndk_version "$NDK_VERSION" '
include "matrix"; include "tags";
{runner_version: $runner_version, compile_sdk: $compile_sdk,
build_tools: $build_tools, ndk_version: $ndk_version} as $v
| .default_jdk as $dj | .default_node as $dn
| [ .java_versions[] as $j | .node_versions[] as $n
| {java_version: $j, node_version: $n, variant: "std"} ] as $std
| ( $std
+ [ $std[] | select(.java_version == $dj and .node_version == $dn)
| (. + {variant: "ndk"}), (. + {variant: "slim"}) ] ) as $images
| { build: { include: [
$combos[] as $c |
($c + {platform: "linux/amd64", arch: "amd64", runner: "ubuntu-latest",
cache_scope: (if $c.variant == "std" then "amd64" else "\($c.variant)-amd64" end)}),
($c + {platform: "linux/arm64", arch: "arm64", runner: "ubuntu-24.04-arm",
cache_scope: (if $c.variant == "std" then "arm64" else "\($c.variant)-arm64" end)}) ] },
merge: { include: $combos } }' matrix.json)
$images[] as $i | ("amd64", "arm64") as $arch
| leg($i.java_version; $i.node_version; $i.variant; $arch)
+ {image_tag: ($i | image_tag($v))} ] },
merge: { include: [
$images[]
| . + {tags: (tags($v; $dj; $dn) | map("type=raw,value=" + .) | join("\n"))} ] } }' matrix.json)
echo "build_matrix=$(jq -c .build <<< "$ALL")" >> "$GITHUB_OUTPUT"
echo "merge_matrix=$(jq -c .merge <<< "$ALL")" >> "$GITHUB_OUTPUT"

Expand All @@ -60,26 +97,6 @@ jobs:
steps:
- uses: actions/checkout@v7

- name: Extract versions from Dockerfile
id: versions
run: |
set -euo pipefail
RUNNER_TAG=$(grep -m1 '^ARG VERSION=' Dockerfile | cut -d= -f2)
RUNNER_VERSION=$(echo "$RUNNER_TAG" | cut -d- -f1)
COMPILE_SDK=$(grep -m1 '^ARG COMPILE_SDK=' Dockerfile | cut -d= -f2)

if [ -z "$RUNNER_TAG" ] || [ -z "$RUNNER_VERSION" ] || [ -z "$COMPILE_SDK" ]; then
echo "::error::Failed to extract versions from Dockerfile"
exit 1
fi

# build_tools / ndk_version are only needed for the merge job's tags.
{
echo "runner_tag=${RUNNER_TAG}"
echo "runner_version=${RUNNER_VERSION}"
echo "compile_sdk=${COMPILE_SDK}"
} >> "$GITHUB_OUTPUT"

- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
Expand All @@ -90,18 +107,6 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4

# Image identity baked into the image as RUNNER_IMAGE_TAG /
# RUNNER_IMAGE_REVISION (Dockerfile ARG -> ENV). The tag is the fully
# pinned one, which never changes meaning; the revision is the commit.
- name: Docker metadata (image identity)
id: meta
uses: docker/metadata-action@v6
with:
images: ghcr.io/${{ github.repository_owner }}/github-runner
tags: |
type=raw,value=${{ steps.versions.outputs.runner_version }}-jdk${{ matrix.java_version }}-sdk${{ steps.versions.outputs.compile_sdk }}-node${{ matrix.node_version }}${{ matrix.variant == 'ndk' && '-ndk' || '' }},enable=${{ matrix.variant != 'slim' }}
type=raw,value=${{ steps.versions.outputs.runner_version }}-jdk${{ matrix.java_version }}-node${{ matrix.node_version }}-slim,enable=${{ matrix.variant == 'slim' }}

- name: Build and push by digest
id: build
uses: docker/build-push-action@v7
Expand All @@ -121,11 +126,13 @@ jobs:
# pipeline consumes the provenance, and turning it off keeps the
# registry view to just amd64 + arm64.
provenance: false
# RUNNER_IMAGE_TAG is the fully pinned tag, which never changes meaning;
# the revision is the commit.
build-args: |
VERSION=${{ steps.versions.outputs.runner_tag }}
VERSION=${{ needs.matrix.outputs.runner_tag }}
JAVA_VERSION=${{ matrix.java_version }}
NODE_VERSION=${{ matrix.node_version }}
RUNNER_IMAGE_TAG=${{ steps.meta.outputs.version }}
RUNNER_IMAGE_TAG=${{ matrix.image_tag }}
RUNNER_IMAGE_REVISION=${{ github.sha }}
# zstd layers pull and decompress faster on small hosts (Raspberry Pi) than gzip.
# force-compression recompresses cached/base layers too; oci-mediatypes is
Expand Down Expand Up @@ -156,30 +163,6 @@ jobs:
fail-fast: false
matrix: ${{ fromJson(needs.matrix.outputs.merge_matrix) }}
steps:
- uses: actions/checkout@v7

- name: Extract versions from Dockerfile
id: versions
run: |
set -euo pipefail
RUNNER_TAG=$(grep -m1 '^ARG VERSION=' Dockerfile | cut -d= -f2)
RUNNER_VERSION=$(echo "$RUNNER_TAG" | cut -d- -f1)
COMPILE_SDK=$(grep -m1 '^ARG COMPILE_SDK=' Dockerfile | cut -d= -f2)
BUILD_TOOLS=$(grep -m1 '^ARG BUILD_TOOLS=' Dockerfile | cut -d= -f2)
NDK_VERSION=$(grep -m1 '^ARG NDK_VERSION=' Dockerfile | cut -d= -f2)

if [ -z "$RUNNER_TAG" ] || [ -z "$RUNNER_VERSION" ] || [ -z "$COMPILE_SDK" ] || [ -z "$BUILD_TOOLS" ] || [ -z "$NDK_VERSION" ]; then
echo "::error::Failed to extract versions from Dockerfile"
exit 1
fi

{
echo "runner_version=${RUNNER_VERSION}"
echo "compile_sdk=${COMPILE_SDK}"
echo "build_tools=${BUILD_TOOLS}"
echo "ndk_version=${NDK_VERSION}"
} >> "$GITHUB_OUTPUT"

- name: Download digests
uses: actions/download-artifact@v8
with:
Expand All @@ -202,23 +185,7 @@ jobs:
uses: docker/metadata-action@v6
with:
images: ghcr.io/${{ github.repository_owner }}/github-runner
tags: |
type=raw,value=jdk${{ matrix.java_version }},enable=${{ matrix.variant == 'std' && matrix.is_default_node }}
type=raw,value=node${{ matrix.node_version }},enable=${{ matrix.variant == 'std' && matrix.is_default_jdk }}
type=raw,value=jdk${{ matrix.java_version }}-node${{ matrix.node_version }},enable=${{ matrix.variant == 'std' }}
type=raw,value=${{ steps.versions.outputs.runner_version }}-jdk${{ matrix.java_version }}-sdk${{ steps.versions.outputs.compile_sdk }}-node${{ matrix.node_version }},enable=${{ matrix.variant == 'std' }}
# Every Gradle-pinnable SDK component in the tag (compileSdk, build-tools), so
# consumers can let Renovate derive them from the image pin.
type=raw,value=${{ steps.versions.outputs.runner_version }}-jdk${{ matrix.java_version }}-sdk${{ steps.versions.outputs.compile_sdk }}-bt${{ steps.versions.outputs.build_tools }}-node${{ matrix.node_version }},enable=${{ matrix.variant == 'std' }}
type=raw,value=latest,enable=${{ matrix.variant == 'std' && matrix.is_default_jdk && matrix.is_default_node }}
type=raw,value=ndk,enable=${{ matrix.variant == 'ndk' }}
type=raw,value=${{ steps.versions.outputs.runner_version }}-jdk${{ matrix.java_version }}-sdk${{ steps.versions.outputs.compile_sdk }}-node${{ matrix.node_version }}-ndk,enable=${{ matrix.variant == 'ndk' }}
# Only the ndk variant ships an NDK, so only it spells the NDK version out
# (upstream's -sdk-ndk- and -sdk-bt-ndk- tags; Gradle ndkVersion via Renovate).
type=raw,value=${{ steps.versions.outputs.runner_version }}-jdk${{ matrix.java_version }}-sdk${{ steps.versions.outputs.compile_sdk }}-ndk${{ steps.versions.outputs.ndk_version }}-node${{ matrix.node_version }},enable=${{ matrix.variant == 'ndk' }}
type=raw,value=${{ steps.versions.outputs.runner_version }}-jdk${{ matrix.java_version }}-sdk${{ steps.versions.outputs.compile_sdk }}-bt${{ steps.versions.outputs.build_tools }}-ndk${{ steps.versions.outputs.ndk_version }}-node${{ matrix.node_version }},enable=${{ matrix.variant == 'ndk' }}
type=raw,value=slim,enable=${{ matrix.variant == 'slim' }}
type=raw,value=${{ steps.versions.outputs.runner_version }}-jdk${{ matrix.java_version }}-node${{ matrix.node_version }}-slim,enable=${{ matrix.variant == 'slim' }}
tags: ${{ matrix.tags }}

- name: Create manifest list and push
working-directory: ${{ runner.temp }}/digests
Expand Down
Loading
Loading