From 6244e5b14d49e953cea0842f76fb768bb9eb24bc Mon Sep 17 00:00:00 2001 From: rldyourmnd Date: Tue, 1 Sep 2026 04:00:28 +0500 Subject: [PATCH] feat(image): bake the CodeQL bundle and point dockerd at the member mirror Two measured per-job taxes leave the images: - Every analyze job on an ephemeral runner downloaded the 800 MB CodeQL bundle. The action resolves the runner tool cache first -- CodeQL/0.0.0-codeql-bundle-v/ with the x64.complete marker -- so the bundle is now a pinned toolchain like go or uv, verified by upstream checksum at build and by `codeql version` as the runner in both smokes. - Every docker pull went straight to Docker Hub: one shared egress address against a 100-pulls-per-6h anonymous limit, full layers on every disposable worker. dockerd on the docker family now names the member zot (https://192.0.2.1:5001) as its registry mirror. dockerd falls back to the upstream on mirror miss or refusal, so the line is safe before the zot sync surface is deployed and instant once it is. The network-surface inventory classifies docker-provision.sh now that it carries a URL. Aliases move to b21/b14 (VM manifests follow to b18/b17). Claude-Session: https://claude.ai/code/session_01LsGid6U5RrQdFvJmvYdGCF --- .../golden-image-container-integration.yaml | 7 ++++++- config/golden-image-container.yaml | 7 ++++++- config/golden-image-integration.yaml | 7 ++++++- config/golden-image.yaml | 7 ++++++- .../imagebuild/assets/docker-provision.sh | 1 + internal/imagebuild/assets/provision.sh | 20 ++++++++++++++++--- .../imagebuild/assets/smoke-integration.sh | 8 +++++++- internal/imagebuild/assets/smoke.sh | 12 ++++++++--- internal/imagebuild/orchestrator_test.go | 2 +- internal/imagemanifest/manifest_test.go | 3 ++- internal/imagemanifest/types.go | 2 +- internal/imagemanifest/validate.go | 10 ++++++++++ internal/imageplan/plan_test.go | 2 +- .../external_downloads_test.go | 1 + 14 files changed, 74 insertions(+), 15 deletions(-) diff --git a/config/golden-image-container-integration.yaml b/config/golden-image-container-integration.yaml index bc03c9cd..ed3f7d40 100644 --- a/config/golden-image-container-integration.yaml +++ b/config/golden-image-container-integration.yaml @@ -2,7 +2,7 @@ schema_version: 1 image: type: container - alias: nddev-u24-amd64-ctr-docker-runner-2.336.0-r20260801-b13 + alias: nddev-u24-amd64-ctr-docker-runner-2.336.0-r20260801-b14 current_alias: nddev-ubuntu-24.04-amd64-container-docker-current previous_alias: nddev-ubuntu-24.04-amd64-container-docker-previous source_alias: nddev-source-ubuntu-24.04-amd64-container-r20260801 @@ -53,6 +53,11 @@ toolchains: archive: bun-linux-x64.zip download_url: https://github.com/oven-sh/bun/releases/download/bun-v1.3.14/bun-linux-x64.zip archive_sha256: 951ee2aee855f08595aeec6225226a298d3fea83a3dcd6465c09cbccdf7e848f + - name: codeql + version: 2.26.4 + archive: codeql-bundle-linux64.tar.gz + download_url: https://github.com/github/codeql-action/releases/download/codeql-bundle-v2.26.4/codeql-bundle-linux64.tar.gz + archive_sha256: 48e1ab8b874d57bd6fd7c90fefee75addc5a45e9bd063982df9beb45a62dd5d3 - name: go version: 1.26.6 archive: go1.26.6.linux-amd64.tar.gz diff --git a/config/golden-image-container.yaml b/config/golden-image-container.yaml index 7018e098..d565f2f6 100644 --- a/config/golden-image-container.yaml +++ b/config/golden-image-container.yaml @@ -2,7 +2,7 @@ schema_version: 1 image: type: container - alias: nddev-ubuntu-24.04-amd64-container-runner-2.336.0-r20260801-b20 + alias: nddev-ubuntu-24.04-amd64-container-runner-2.336.0-r20260801-b21 current_alias: nddev-ubuntu-24.04-amd64-container-current previous_alias: nddev-ubuntu-24.04-amd64-container-previous source_alias: nddev-source-ubuntu-24.04-amd64-container-r20260801 @@ -53,6 +53,11 @@ toolchains: archive: bun-linux-x64.zip download_url: https://github.com/oven-sh/bun/releases/download/bun-v1.3.14/bun-linux-x64.zip archive_sha256: 951ee2aee855f08595aeec6225226a298d3fea83a3dcd6465c09cbccdf7e848f + - name: codeql + version: 2.26.4 + archive: codeql-bundle-linux64.tar.gz + download_url: https://github.com/github/codeql-action/releases/download/codeql-bundle-v2.26.4/codeql-bundle-linux64.tar.gz + archive_sha256: 48e1ab8b874d57bd6fd7c90fefee75addc5a45e9bd063982df9beb45a62dd5d3 - name: go version: 1.26.6 archive: go1.26.6.linux-amd64.tar.gz diff --git a/config/golden-image-integration.yaml b/config/golden-image-integration.yaml index 0c12a31d..40b7ae08 100644 --- a/config/golden-image-integration.yaml +++ b/config/golden-image-integration.yaml @@ -1,7 +1,7 @@ schema_version: 1 image: - alias: nddev-ubuntu-24.04-amd64-docker-runner-2.336.0-r20260801-b16 + alias: nddev-ubuntu-24.04-amd64-docker-runner-2.336.0-r20260801-b17 current_alias: nddev-ubuntu-24.04-amd64-docker-current previous_alias: nddev-ubuntu-24.04-amd64-docker-previous source_alias: nddev-source-ubuntu-24.04-amd64-r20260801 @@ -52,6 +52,11 @@ toolchains: archive: bun-linux-x64.zip download_url: https://github.com/oven-sh/bun/releases/download/bun-v1.3.14/bun-linux-x64.zip archive_sha256: 951ee2aee855f08595aeec6225226a298d3fea83a3dcd6465c09cbccdf7e848f + - name: codeql + version: 2.26.4 + archive: codeql-bundle-linux64.tar.gz + download_url: https://github.com/github/codeql-action/releases/download/codeql-bundle-v2.26.4/codeql-bundle-linux64.tar.gz + archive_sha256: 48e1ab8b874d57bd6fd7c90fefee75addc5a45e9bd063982df9beb45a62dd5d3 - name: go version: 1.26.6 archive: go1.26.6.linux-amd64.tar.gz diff --git a/config/golden-image.yaml b/config/golden-image.yaml index 160e6c0a..4f53a3a2 100644 --- a/config/golden-image.yaml +++ b/config/golden-image.yaml @@ -1,7 +1,7 @@ schema_version: 1 image: - alias: nddev-ubuntu-24.04-amd64-runner-2.336.0-r20260801-b17 + alias: nddev-ubuntu-24.04-amd64-runner-2.336.0-r20260801-b18 current_alias: nddev-ubuntu-24.04-amd64-current previous_alias: nddev-ubuntu-24.04-amd64-previous source_alias: nddev-source-ubuntu-24.04-amd64-r20260801 @@ -52,6 +52,11 @@ toolchains: archive: bun-linux-x64.zip download_url: https://github.com/oven-sh/bun/releases/download/bun-v1.3.14/bun-linux-x64.zip archive_sha256: 951ee2aee855f08595aeec6225226a298d3fea83a3dcd6465c09cbccdf7e848f + - name: codeql + version: 2.26.4 + archive: codeql-bundle-linux64.tar.gz + download_url: https://github.com/github/codeql-action/releases/download/codeql-bundle-v2.26.4/codeql-bundle-linux64.tar.gz + archive_sha256: 48e1ab8b874d57bd6fd7c90fefee75addc5a45e9bd063982df9beb45a62dd5d3 - name: go version: 1.26.6 archive: go1.26.6.linux-amd64.tar.gz diff --git a/internal/imagebuild/assets/docker-provision.sh b/internal/imagebuild/assets/docker-provision.sh index 40ef9124..32dbc289 100644 --- a/internal/imagebuild/assets/docker-provision.sh +++ b/internal/imagebuild/assets/docker-provision.sh @@ -40,6 +40,7 @@ jq -n --arg storage_driver "${GHA_DOCKER_STORAGE_DRIVER}" '({ "default-address-pools":[{"base":"172.30.0.0/16","size":24}], "exec-opts":["native.cgroupdriver=systemd"], "features":{"buildkit":true}, + "registry-mirrors":["https://192.0.2.1:5001"], "live-restore":false, "log-driver":"local", "log-opts":{"max-file":"3","max-size":"10m"}, diff --git a/internal/imagebuild/assets/provision.sh b/internal/imagebuild/assets/provision.sh index c5e50786..e63ff8b4 100644 --- a/internal/imagebuild/assets/provision.sh +++ b/internal/imagebuild/assets/provision.sh @@ -237,9 +237,9 @@ toolchain_manifest="$(printf '%s' "${GHA_TOOLCHAINS_B64}" | base64 --decode)" jq -e 'type == "array"' <<<"${toolchain_manifest}" >/dev/null mapfile -t toolchain_names < <(jq -r '.[].name' <<<"${toolchain_manifest}") toolchain_set="$(printf '%s\n' "${toolchain_names[@]}" | LC_ALL=C sort | paste -sd, -)" -if [[ "${toolchain_set}" != bun,gh,go,rustup,uv \ - && "${toolchain_set}" != bun,gh,go,node22,node24,node25,pnpm,rustup,uv,yarn \ - && "${toolchain_set}" != bun,flutter,gh,go,node22,node24,node25,pnpm,rustup,uv,yarn ]]; then +if [[ "${toolchain_set}" != bun,codeql,gh,go,rustup,uv \ + && "${toolchain_set}" != bun,codeql,gh,go,node22,node24,node25,pnpm,rustup,uv,yarn \ + && "${toolchain_set}" != bun,codeql,flutter,gh,go,node22,node24,node25,pnpm,rustup,uv,yarn ]]; then echo "toolchain manifest does not pin the exact baked set" >&2 exit 1 fi @@ -254,6 +254,20 @@ for toolchain_name in "${toolchain_names[@]}"; do echo "${toolchain_sha256} ${toolchain_archive}" | sha256sum --check --strict toolchain_scratch="$(mktemp -d /var/tmp/gha-toolchain.XXXXXXXXXX)" case "${toolchain_name}" in + codeql) + # The action resolves ${runner_tool_cache}/CodeQL/0.0.0-codeql-bundle-v + # with an x64.complete marker beside x64/, and prefers it over the 800 MB + # network download an ephemeral runner would otherwise repeat every + # analyze job. + codeql_root="${runner_tool_cache}/CodeQL/0.0.0-codeql-bundle-v${toolchain_version}" + install -d -o runner -g runner -m 0755 "${runner_tool_cache}/CodeQL" \ + "${codeql_root}" "${codeql_root}/x64" + tar --extract --gzip --file "${toolchain_archive}" \ + --directory "${codeql_root}/x64" --no-same-owner --no-same-permissions + chown -R runner:runner "${codeql_root}" + install -o runner -g runner -m 0644 /dev/null "${codeql_root}/x64.complete" + [[ "$(runuser -u runner -- "${codeql_root}/x64/codeql/codeql" version --format=terse)" == "${toolchain_version}" ]] + ;; bun) unzip -q "${toolchain_archive}" -d "${toolchain_scratch}" install -o root -g root -m 0755 \ diff --git a/internal/imagebuild/assets/smoke-integration.sh b/internal/imagebuild/assets/smoke-integration.sh index d139f643..b8f4e260 100644 --- a/internal/imagebuild/assets/smoke-integration.sh +++ b/internal/imagebuild/assets/smoke-integration.sh @@ -88,7 +88,7 @@ runner_tool_cache="$(jq -er .runner_tool_cache /etc/nddev/image-build.json)" [[ "${runner_tool_cache}" == /home/runner/actions-runner/_work/_tool ]] smoke_toolchains="$(printf '%s' "${GHA_TOOLCHAINS_B64}" | base64 --decode)" mapfile -t smoke_toolchain_names < <(jq -r '.[].name' <<<"${smoke_toolchains}") -[[ "$(printf '%s\n' "${smoke_toolchain_names[@]}" | LC_ALL=C sort | paste -sd, -)" == bun,gh,go,node22,node24,node25,pnpm,rustup,uv,yarn ]] +[[ "$(printf '%s\n' "${smoke_toolchain_names[@]}" | LC_ALL=C sort | paste -sd, -)" == bun,codeql,gh,go,node22,node24,node25,pnpm,rustup,uv,yarn ]] for smoke_toolchain in "${smoke_toolchain_names[@]}"; do entry="$(jq -ce --arg name "${smoke_toolchain}" '.[] | select(.name == $name)' <<<"${smoke_toolchains}")" expected_version="$(jq -er .version <<<"${entry}")" @@ -96,6 +96,12 @@ for smoke_toolchain in "${smoke_toolchain_names[@]}"; do [[ "$(jq -er --arg name "${smoke_toolchain}" '.toolchains[$name].version' /etc/nddev/image-build.json)" == "${expected_version}" ]] [[ "$(jq -er --arg name "${smoke_toolchain}" '.toolchains[$name].archive_sha256' /etc/nddev/image-build.json)" == "${expected_sha256}" ]] case "${smoke_toolchain}" in + codeql) + codeql_root="${runner_tool_cache}/CodeQL/0.0.0-codeql-bundle-v${expected_version}" + test -f "${codeql_root}/x64.complete" + [[ "$(stat --format='%U' -- "${codeql_root}/x64/codeql/codeql")" == runner ]] + [[ "$(runuser -u runner -- "${codeql_root}/x64/codeql/codeql" version --format=terse)" == "${expected_version}" ]] + ;; bun) [[ "$(bun --version)" == "${expected_version}" ]] # The path oven-sh/setup-bun probes before deciding to download. diff --git a/internal/imagebuild/assets/smoke.sh b/internal/imagebuild/assets/smoke.sh index 26a573b6..6b61ab5e 100644 --- a/internal/imagebuild/assets/smoke.sh +++ b/internal/imagebuild/assets/smoke.sh @@ -64,9 +64,9 @@ runner_tool_cache="$(jq -er .runner_tool_cache /etc/nddev/image-build.json)" smoke_toolchains="$(printf '%s' "${GHA_TOOLCHAINS_B64}" | base64 --decode)" mapfile -t smoke_toolchain_names < <(jq -r '.[].name' <<<"${smoke_toolchains}") smoke_toolchain_set="$(printf '%s\n' "${smoke_toolchain_names[@]}" | LC_ALL=C sort | paste -sd, -)" -[[ "${smoke_toolchain_set}" == bun,gh,go,rustup,uv \ - || "${smoke_toolchain_set}" == bun,gh,go,node22,node24,node25,pnpm,rustup,uv,yarn \ - || "${smoke_toolchain_set}" == bun,flutter,gh,go,node22,node24,node25,pnpm,rustup,uv,yarn ]] +[[ "${smoke_toolchain_set}" == bun,codeql,gh,go,rustup,uv \ + || "${smoke_toolchain_set}" == bun,codeql,gh,go,node22,node24,node25,pnpm,rustup,uv,yarn \ + || "${smoke_toolchain_set}" == bun,codeql,flutter,gh,go,node22,node24,node25,pnpm,rustup,uv,yarn ]] for smoke_toolchain in "${smoke_toolchain_names[@]}"; do entry="$(jq -ce --arg name "${smoke_toolchain}" '.[] | select(.name == $name)' <<<"${smoke_toolchains}")" expected_version="$(jq -er .version <<<"${entry}")" @@ -74,6 +74,12 @@ for smoke_toolchain in "${smoke_toolchain_names[@]}"; do [[ "$(jq -er --arg name "${smoke_toolchain}" '.toolchains[$name].version' /etc/nddev/image-build.json)" == "${expected_version}" ]] [[ "$(jq -er --arg name "${smoke_toolchain}" '.toolchains[$name].archive_sha256' /etc/nddev/image-build.json)" == "${expected_sha256}" ]] case "${smoke_toolchain}" in + codeql) + codeql_root="${runner_tool_cache}/CodeQL/0.0.0-codeql-bundle-v${expected_version}" + test -f "${codeql_root}/x64.complete" + [[ "$(stat --format='%U' -- "${codeql_root}/x64/codeql/codeql")" == runner ]] + [[ "$(runuser -u runner -- "${codeql_root}/x64/codeql/codeql" version --format=terse)" == "${expected_version}" ]] + ;; bun) [[ "$(bun --version)" == "${expected_version}" ]] # The path oven-sh/setup-bun probes before deciding to download. diff --git a/internal/imagebuild/orchestrator_test.go b/internal/imagebuild/orchestrator_test.go index 37b6d23b..b121f608 100644 --- a/internal/imagebuild/orchestrator_test.go +++ b/internal/imagebuild/orchestrator_test.go @@ -164,7 +164,7 @@ func TestRecipeFingerprintIsDeterministic(t *testing.T) { // alias is part of the recipe, so a manifest whose contents changed under an // unchanged alias would otherwise ask the builder to produce different bytes // for a name that is already promoted. - if first != "sha256:4cc00e2ee72d7ed4646f84229f3b6a2d5c6d4aeeb05aae29f15f4e1bd381eeb1" { + if first != "sha256:ba41e9cfcebc856bfa3ee0b75f12d4e3f7a9b85598308491ea70f72b2fe2baff" { t.Fatalf("deployed standard recipe fingerprint drifted: %q", first) } smoke, err := SmokeFingerprint(plan) diff --git a/internal/imagemanifest/manifest_test.go b/internal/imagemanifest/manifest_test.go index fbfefeef..1e6e273b 100644 --- a/internal/imagemanifest/manifest_test.go +++ b/internal/imagemanifest/manifest_test.go @@ -42,7 +42,7 @@ func TestRepositoryManifestIsValidAndPinned(t *testing.T) { if !strings.HasPrefix(fingerprint, "sha256:") || len(fingerprint) != len("sha256:")+64 { t.Fatalf("unexpected fingerprint %q", fingerprint) } - if fingerprint != "sha256:4c76a6c764a529923aa4dcd0327863aa43a4700b3a5e5d99aa30426a7db96217" { + if fingerprint != "sha256:47a1569f94fa824b69de51d5ee917a5e8b72cc4a4c9b727c214019f891615a56" { t.Fatalf("standard manifest fingerprint drifted: %q", fingerprint) } } @@ -71,6 +71,7 @@ func assertBakedToolchains(t *testing.T, manifest Manifest) { t.Helper() wanted := map[string]struct{ version, archiveSHA256 string }{ "bun": {"1.3.14", "951ee2aee855f08595aeec6225226a298d3fea83a3dcd6465c09cbccdf7e848f"}, + "codeql": {"2.26.4", "48e1ab8b874d57bd6fd7c90fefee75addc5a45e9bd063982df9beb45a62dd5d3"}, "gh": {"2.97.0", "a2c9b8497e1f85b1ad0dfcb78b5a622e098801b8e461e459e88e1ee12f018112"}, "go": {"1.26.6", "708effb774be8237570d0add163225abbdfaf4fca28b2611df167beba4feef89"}, "node22": {"22.23.2", "d60acfe00a2932254bb0ad20e01b0d74397a0875595de719654b214f4b03f307"}, diff --git a/internal/imagemanifest/types.go b/internal/imagemanifest/types.go index c1ae04af..4b800ae0 100644 --- a/internal/imagemanifest/types.go +++ b/internal/imagemanifest/types.go @@ -122,7 +122,7 @@ func BakedToolchains() []string { // was unreachable in the shipped image. The default channel serves jobs // that call cargo directly, and actions-rust-lang/setup-rust-toolchain // resolves rust-toolchain.toml channels through rustup either way. - return []string{"bun", "gh", "go", "node22", "node24", "node25", "pnpm", "rustup", "uv", "yarn"} + return []string{"bun", "codeql", "gh", "go", "node22", "node24", "node25", "pnpm", "rustup", "uv", "yarn"} } // OptionalToolchains may be pinned by an image that needs them and omitted by diff --git a/internal/imagemanifest/validate.go b/internal/imagemanifest/validate.go index 6887dbd0..5e273512 100644 --- a/internal/imagemanifest/validate.go +++ b/internal/imagemanifest/validate.go @@ -112,6 +112,16 @@ var toolchainAssets = map[string]struct { return "/rustup/archive/" + version + "/x86_64-unknown-linux-gnu/rustup-init" }, }, + // The CodeQL bundle the action would otherwise download on every analyze + // job of an ephemeral runner: 800 MB per run, resolved from the runner + // tool cache when present. + "codeql": { + Host: "github.com", + Archive: func(string) string { return "codeql-bundle-linux64.tar.gz" }, + Path: func(version string) string { + return "/github/codeql-action/releases/download/codeql-bundle-v" + version + "/codeql-bundle-linux64.tar.gz" + }, + }, "uv": { Host: "github.com", Archive: func(string) string { return "uv-x86_64-unknown-linux-gnu.tar.gz" }, diff --git a/internal/imageplan/plan_test.go b/internal/imageplan/plan_test.go index 7bfa0007..fa75f8f8 100644 --- a/internal/imageplan/plan_test.go +++ b/internal/imageplan/plan_test.go @@ -82,7 +82,7 @@ func TestBuildProducesDeterministicBoundedPlan(t *testing.T) { } // The baked set plus the optional Flutter SDK this manifest pins, still // sorted: the plan's ordering is what makes the recipe fingerprint stable. - if !slices.Equal(names, []string{"bun", "flutter", "gh", "go", "node22", "node24", "node25", "pnpm", "rustup", "uv", "yarn"}) { + if !slices.Equal(names, []string{"bun", "codeql", "flutter", "gh", "go", "node22", "node24", "node25", "pnpm", "rustup", "uv", "yarn"}) { t.Fatalf("plan toolchains are not the canonical sorted set: %v", names) } if first.CompilerCache.Name != "sccache" || first.CompilerCache.Version != "v0.17.0" || diff --git a/internal/repositorycontract/external_downloads_test.go b/internal/repositorycontract/external_downloads_test.go index 852e3b46..3c81ada9 100644 --- a/internal/repositorycontract/external_downloads_test.go +++ b/internal/repositorycontract/external_downloads_test.go @@ -20,6 +20,7 @@ func TestEveryNetworkBootstrapSurfaceIsClassified(t *testing.T) { "internal/garmproviderincus/provider/incus.go", "internal/garmproviderincus/provider/specs.go", "internal/imagebuild/assets/container-provision.sh", + "internal/imagebuild/assets/docker-provision.sh", "internal/imagebuild/assets/provision.sh", "internal/imagebuild/assets/smoke-integration.sh", "internal/imagebuild/assets/smoke.sh",