diff --git a/config/golden-image-container-integration.yaml b/config/golden-image-container-integration.yaml index 15b42deb..653ad3fd 100644 --- a/config/golden-image-container-integration.yaml +++ b/config/golden-image-container-integration.yaml @@ -83,11 +83,6 @@ toolchains: archive: pnpm-11.22.0.tgz download_url: https://registry.npmjs.org/pnpm/-/pnpm-11.22.0.tgz archive_sha256: 57a97e6f23a3faffc03153a4ef8c770a0552612b8640aebe39bfdd5754d0ebdc - - name: rust - version: 1.97.1 - archive: rust-1.97.1-x86_64-unknown-linux-gnu.tar.xz - download_url: https://static.rust-lang.org/dist/rust-1.97.1-x86_64-unknown-linux-gnu.tar.xz - archive_sha256: 88f28fa9af20594179f85d6df67078dfd6fa93e2f6da5e1e9b0ac4997988ca4f - name: rustup version: 1.29.0 archive: rustup-init diff --git a/config/golden-image-container.yaml b/config/golden-image-container.yaml index 8730eb70..f3757efb 100644 --- a/config/golden-image-container.yaml +++ b/config/golden-image-container.yaml @@ -88,11 +88,6 @@ toolchains: archive: pnpm-11.22.0.tgz download_url: https://registry.npmjs.org/pnpm/-/pnpm-11.22.0.tgz archive_sha256: 57a97e6f23a3faffc03153a4ef8c770a0552612b8640aebe39bfdd5754d0ebdc - - name: rust - version: 1.97.1 - archive: rust-1.97.1-x86_64-unknown-linux-gnu.tar.xz - download_url: https://static.rust-lang.org/dist/rust-1.97.1-x86_64-unknown-linux-gnu.tar.xz - archive_sha256: 88f28fa9af20594179f85d6df67078dfd6fa93e2f6da5e1e9b0ac4997988ca4f - name: rustup version: 1.29.0 archive: rustup-init diff --git a/config/golden-image-integration.yaml b/config/golden-image-integration.yaml index b4f603bf..bbd78eba 100644 --- a/config/golden-image-integration.yaml +++ b/config/golden-image-integration.yaml @@ -82,11 +82,6 @@ toolchains: archive: pnpm-11.22.0.tgz download_url: https://registry.npmjs.org/pnpm/-/pnpm-11.22.0.tgz archive_sha256: 57a97e6f23a3faffc03153a4ef8c770a0552612b8640aebe39bfdd5754d0ebdc - - name: rust - version: 1.97.1 - archive: rust-1.97.1-x86_64-unknown-linux-gnu.tar.xz - download_url: https://static.rust-lang.org/dist/rust-1.97.1-x86_64-unknown-linux-gnu.tar.xz - archive_sha256: 88f28fa9af20594179f85d6df67078dfd6fa93e2f6da5e1e9b0ac4997988ca4f - name: rustup version: 1.29.0 archive: rustup-init diff --git a/config/golden-image.yaml b/config/golden-image.yaml index fe989fe9..7c7a7e78 100644 --- a/config/golden-image.yaml +++ b/config/golden-image.yaml @@ -82,11 +82,6 @@ toolchains: archive: pnpm-11.22.0.tgz download_url: https://registry.npmjs.org/pnpm/-/pnpm-11.22.0.tgz archive_sha256: 57a97e6f23a3faffc03153a4ef8c770a0552612b8640aebe39bfdd5754d0ebdc - - name: rust - version: 1.97.1 - archive: rust-1.97.1-x86_64-unknown-linux-gnu.tar.xz - download_url: https://static.rust-lang.org/dist/rust-1.97.1-x86_64-unknown-linux-gnu.tar.xz - archive_sha256: 88f28fa9af20594179f85d6df67078dfd6fa93e2f6da5e1e9b0ac4997988ca4f - name: rustup version: 1.29.0 archive: rustup-init diff --git a/internal/imagebuild/assets/provision.sh b/internal/imagebuild/assets/provision.sh index f2b1e8ee..b87fc3d1 100644 --- a/internal/imagebuild/assets/provision.sh +++ b/internal/imagebuild/assets/provision.sh @@ -219,9 +219,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,rust,rustup,uv \ - && "${toolchain_set}" != bun,gh,go,node22,node24,node25,pnpm,rust,rustup,uv,yarn \ - && "${toolchain_set}" != bun,flutter,gh,go,node22,node24,node25,pnpm,rust,rustup,uv,yarn ]]; then +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 echo "toolchain manifest does not pin the exact baked set" >&2 exit 1 fi @@ -332,28 +332,6 @@ for toolchain_name in "${toolchain_names[@]}"; do ln -sfn "${package_root}/bin/pnpx.cjs" /usr/local/bin/pnpx [[ "$(pnpm --version)" == "${toolchain_version}" ]] ;; - rust) - # The standalone archive alone is not what the estate asks for. Every - # setup-system pins rust-toolchain.toml to a channel with rustfmt and - # clippy, and actions-rust-lang/setup-rust-toolchain resolves that through - # rustup -- so a system-wide rustc with no rustup, no clippy and no - # rustfmt is invisible to it and every job downloads a toolchain instead. - # Measured across the seven public anchors: 28 workflow requests for - # 1.98.0 and 7 for the 1.89 MSRV, against a baked 1.97.1. - # - # rustup owns the layout, so the archive installs into it rather than into - # /usr/local, and the extra channels are installed beside it. RUSTUP_HOME - # and CARGO_HOME are system paths so the toolchains are shared rather than - # re-fetched per job, and the shims land on PATH for jobs that never call - # the action at all. - tar --extract --xz --file "${toolchain_archive}" \ - --directory "${toolchain_scratch}" --no-same-owner --no-same-permissions - "${toolchain_scratch}/rust-${toolchain_version}-x86_64-unknown-linux-gnu/install.sh" \ - --prefix=/usr/local \ - --components=rustc,cargo,rust-std-x86_64-unknown-linux-gnu >/dev/null - [[ "$(rustc --version)" == "rustc ${toolchain_version} "* ]] - [[ "$(cargo --version)" == "cargo ${toolchain_version} "* ]] - ;; rustup) install -o root -g root -m 0755 "${toolchain_archive}" "${toolchain_scratch}/rustup-init" export RUSTUP_HOME=/usr/local/rustup CARGO_HOME=/usr/local/cargo @@ -365,7 +343,10 @@ for toolchain_name in "${toolchain_names[@]}"; do # rust-toolchain.toml files name, so the action finds them already present. mapfile -t rust_channels < <(jq -r '.channels[]?' <<<"${entry}") rust_default="$(jq -r '.default_channel // ""' <<<"${entry}")" - [[ ${#rust_channels[@]} -gt 0 && -n "${rust_default}" ]] + if [[ ${#rust_channels[@]} -eq 0 || -z "${rust_default}" ]]; then + echo "rustup entry carries no channels or no default channel: the manifest names them, so the provisioning contract dropped them" >&2 + exit 1 + fi for channel in "${rust_channels[@]}"; do "${CARGO_HOME}/bin/rustup" toolchain install "${channel}" \ --profile minimal --component clippy --component rustfmt >/dev/null diff --git a/internal/imagebuild/assets/smoke-integration.sh b/internal/imagebuild/assets/smoke-integration.sh index 5612ee59..0b58b5d0 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,rust,uv,yarn ]] +[[ "$(printf '%s\n' "${smoke_toolchain_names[@]}" | LC_ALL=C sort | paste -sd, -)" == bun,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}")" @@ -128,9 +128,26 @@ for smoke_toolchain in "${smoke_toolchain_names[@]}"; do fi ;; pnpm) [[ "$(pnpm --version)" == "${expected_version}" ]] ;; - rust) - [[ "$(rustc --version)" == "rustc ${expected_version} "* ]] - [[ "$(cargo --version)" == "cargo ${expected_version} "* ]] + rustup) + # rustup owns the whole Rust surface: the estate pins channels in + # rust-toolchain.toml, actions-rust-lang/setup-rust-toolchain resolves + # them through rustup, and the shims on PATH serve jobs that never call + # the action. Every pinned channel must already hold clippy and rustfmt, + # or the per-job download this image exists to remove comes back. + [[ "$(rustup --version 2>/dev/null)" == "rustup ${expected_version} "* ]] + [[ "$(stat --format='%U' -- /usr/local/rustup)" == root ]] + [[ "$(stat --format='%U' -- /usr/local/cargo)" == root ]] + grep -q '^RUSTUP_HOME=/usr/local/rustup$' /etc/environment + mapfile -t smoke_rust_channels < <(jq -r '.channels[]?' <<<"${entry}") + smoke_rust_default="$(jq -er '.default_channel' <<<"${entry}")" + [[ ${#smoke_rust_channels[@]} -gt 0 ]] + for smoke_rust_channel in "${smoke_rust_channels[@]}"; do + [[ "$(rustup run "${smoke_rust_channel}" rustc --version)" == "rustc ${smoke_rust_channel}"* ]] + [[ "$(rustup run "${smoke_rust_channel}" cargo clippy --version)" == clippy* ]] + [[ "$(rustup run "${smoke_rust_channel}" rustfmt --version)" == rustfmt* ]] + done + [[ "$(rustc --version)" == "rustc ${smoke_rust_default}"* ]] + [[ "$(cargo --version)" == "cargo "* ]] ;; uv) [[ "$(uv --version)" == "uv ${expected_version}"* ]] diff --git a/internal/imagebuild/assets/smoke.sh b/internal/imagebuild/assets/smoke.sh index 166c4bb7..dc76ab17 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,rust,uv \ - || "${smoke_toolchain_set}" == bun,gh,go,node22,node24,node25,pnpm,rust,uv,yarn \ - || "${smoke_toolchain_set}" == bun,flutter,gh,go,node22,node24,node25,pnpm,rust,uv,yarn ]] +[[ "${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 ]] 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}")" @@ -115,9 +115,26 @@ for smoke_toolchain in "${smoke_toolchain_names[@]}"; do fi ;; pnpm) [[ "$(pnpm --version)" == "${expected_version}" ]] ;; - rust) - [[ "$(rustc --version)" == "rustc ${expected_version} "* ]] - [[ "$(cargo --version)" == "cargo ${expected_version} "* ]] + rustup) + # rustup owns the whole Rust surface: the estate pins channels in + # rust-toolchain.toml, actions-rust-lang/setup-rust-toolchain resolves + # them through rustup, and the shims on PATH serve jobs that never call + # the action. Every pinned channel must already hold clippy and rustfmt, + # or the per-job download this image exists to remove comes back. + [[ "$(rustup --version 2>/dev/null)" == "rustup ${expected_version} "* ]] + [[ "$(stat --format='%U' -- /usr/local/rustup)" == root ]] + [[ "$(stat --format='%U' -- /usr/local/cargo)" == root ]] + grep -q '^RUSTUP_HOME=/usr/local/rustup$' /etc/environment + mapfile -t smoke_rust_channels < <(jq -r '.channels[]?' <<<"${entry}") + smoke_rust_default="$(jq -er '.default_channel' <<<"${entry}")" + [[ ${#smoke_rust_channels[@]} -gt 0 ]] + for smoke_rust_channel in "${smoke_rust_channels[@]}"; do + [[ "$(rustup run "${smoke_rust_channel}" rustc --version)" == "rustc ${smoke_rust_channel}"* ]] + [[ "$(rustup run "${smoke_rust_channel}" cargo clippy --version)" == clippy* ]] + [[ "$(rustup run "${smoke_rust_channel}" rustfmt --version)" == rustfmt* ]] + done + [[ "$(rustc --version)" == "rustc ${smoke_rust_default}"* ]] + [[ "$(cargo --version)" == "cargo "* ]] ;; uv) [[ "$(uv --version)" == "uv ${expected_version}"* ]] diff --git a/internal/imagebuild/orchestrator.go b/internal/imagebuild/orchestrator.go index 4c63cb53..bd49ab71 100644 --- a/internal/imagebuild/orchestrator.go +++ b/internal/imagebuild/orchestrator.go @@ -15,6 +15,7 @@ import ( "strings" "time" + "github.com/NDDev-OpenNetwork/github-actions/internal/imagemanifest" "github.com/NDDev-OpenNetwork/github-actions/internal/imageplan" ) @@ -296,6 +297,26 @@ type guestToolchain struct { Version string `json:"version"` Archive string `json:"archive"` ArchiveSHA256 string `json:"archive_sha256"` + // Channels and DefaultChannel ride along for version managers. Dropping + // them here once produced a rustup with no toolchains: the manifest named + // the channels, every manifest gate passed, and the builder still died + // because this projection was the one place they were not carried. + Channels []string `json:"channels,omitempty"` + DefaultChannel string `json:"default_channel,omitempty"` +} + +// guestToolchainFromPlan is the single projection from a manifest toolchain to +// the guest contract. Provision receives the pushed archive path; smoke +// receives none, because it verifies installed state rather than archives. +func guestToolchainFromPlan(toolchain imagemanifest.Toolchain, archivePath string) guestToolchain { + return guestToolchain{ + Name: toolchain.Name, + Version: toolchain.Version, + Archive: archivePath, + ArchiveSHA256: toolchain.ArchiveSHA256, + Channels: toolchain.Channels, + DefaultChannel: toolchain.DefaultChannel, + } } func encodeToolchains(toolchains []guestToolchain) (string, error) { @@ -573,12 +594,8 @@ exit 1`); err != nil { if _, err = o.Runner.Run(ctx, o.incus(plan.Project, "file", "push", artifacts.Toolchains[toolchain.Name], destination, "--mode", "0600")...); err != nil { return "", "", fmt.Errorf("push verified %s toolchain archive: %w", toolchain.Name, err) } - toolchainRequests = append(toolchainRequests, guestToolchain{ - Name: toolchain.Name, - Version: toolchain.Version, - Archive: "/var/tmp/" + toolchain.Archive, - ArchiveSHA256: toolchain.ArchiveSHA256, - }) + toolchainRequests = append(toolchainRequests, + guestToolchainFromPlan(toolchain, "/var/tmp/"+toolchain.Archive)) } encodedToolchains, err := encodeToolchains(toolchainRequests) if err != nil { @@ -800,11 +817,7 @@ func (o Orchestrator) smoke(ctx context.Context, plan imageplan.Plan, fingerprin smoke, _ := scripts.ReadFile(smokeName) smokeToolchains := make([]guestToolchain, 0, len(plan.Toolchains)) for _, toolchain := range plan.Toolchains { - smokeToolchains = append(smokeToolchains, guestToolchain{ - Name: toolchain.Name, - Version: toolchain.Version, - ArchiveSHA256: toolchain.ArchiveSHA256, - }) + smokeToolchains = append(smokeToolchains, guestToolchainFromPlan(toolchain, "")) } encodedToolchains, err := encodeToolchains(smokeToolchains) if err != nil { diff --git a/internal/imagebuild/orchestrator_test.go b/internal/imagebuild/orchestrator_test.go index 397aebed..70ae123c 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:3d89d31c7c5a423238a6888b2b4c4d894f22fcb91e0d6ea76fd689d0c5a09b9b" { + if first != "sha256:6b92afb396a9d31a6772e489343006f2ce05c93a59a641701e2bf49ff8fe23fc" { t.Fatalf("deployed standard recipe fingerprint drifted: %q", first) } smoke, err := SmokeFingerprint(plan) @@ -689,3 +689,42 @@ func TestFailedBuildPreservesTheBuilderOnlyWhenAsked(t *testing.T) { } } } + +// TestGuestToolchainContractCarriesChannels guards the projection between the +// manifest and the guest scripts. The channels were once present in the +// manifest, validated by imagemanifest tests, and still absent inside the +// builder, because this projection dropped them; provision then failed with no +// toolchain installed. Both the provisioning and the smoke contract must carry +// them. +func TestGuestToolchainContractCarriesChannels(t *testing.T) { + t.Parallel() + + pinned := imagemanifest.Toolchain{ + Name: "rustup", + Version: "1.29.0", + Archive: "rustup-init", + ArchiveSHA256: strings.Repeat("a", 64), + Channels: []string{"1.98.0", "1.89"}, + DefaultChannel: "1.98.0", + } + + for name, projected := range map[string]guestToolchain{ + "provision": guestToolchainFromPlan(pinned, "/var/tmp/"+pinned.Archive), + "smoke": guestToolchainFromPlan(pinned, ""), + } { + encoded, err := encodeToolchains([]guestToolchain{projected}) + if err != nil { + t.Fatalf("%s: encode: %v", name, err) + } + decoded, err := base64.StdEncoding.DecodeString(encoded) + if err != nil { + t.Fatalf("%s: decode: %v", name, err) + } + contract := string(decoded) + for _, needle := range []string{`"channels":["1.98.0","1.89"]`, `"default_channel":"1.98.0"`} { + if !strings.Contains(contract, needle) { + t.Fatalf("%s contract %s lost %s", name, contract, needle) + } + } + } +} diff --git a/internal/imagemanifest/manifest_test.go b/internal/imagemanifest/manifest_test.go index 52bea667..c05f1c03 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:02591ff082938633b0435312ef80f7a397021f80fe155fa62ece7cc6026e8e70" { + if fingerprint != "sha256:d7fdc01a61f2afc9991ef658511ea3cf73f73c251d0043c04c392f06b6397d1a" { t.Fatalf("standard manifest fingerprint drifted: %q", fingerprint) } } @@ -77,7 +77,6 @@ func assertBakedToolchains(t *testing.T, manifest Manifest) { "node24": {"24.19.0", "14b342e71204f811bde6153be8e04b62aef63c236fef92b55f9c83154b409647"}, "node25": {"25.9.0", "1d8db7d6e291d167e8c467ae4094be175e1a0b3969c7ae1f8955b9f7824f7b2e"}, "pnpm": {"11.22.0", "57a97e6f23a3faffc03153a4ef8c770a0552612b8640aebe39bfdd5754d0ebdc"}, - "rust": {"1.97.1", "88f28fa9af20594179f85d6df67078dfd6fa93e2f6da5e1e9b0ac4997988ca4f"}, "rustup": {"1.29.0", "4acc9acc76d5079515b46346a485974457b5a79893cfb01112423c89aeb5aa10"}, "uv": {"0.11.30", "04bc7d180d6138bf6dc08387acf507a823f397a98fea55da36b0ccc7fbce3b68"}, "yarn": {"4.18.0", "606e7e2dfc8bcc24e1b3a70a1043288a271ad2cc71cf42248fadc25f5938a497"}, diff --git a/internal/imagemanifest/types.go b/internal/imagemanifest/types.go index 45dea67c..c1ae04af 100644 --- a/internal/imagemanifest/types.go +++ b/internal/imagemanifest/types.go @@ -116,12 +116,13 @@ type BrowserSmoke struct { // already on PATH, and actions/setup-go resolves a pre-seeded runner tool cache, // so a complete set turns per-job toolchain installation into a no-op. func BakedToolchains() []string { - // rustup is baked beside rust, not instead of it. A system-wide rustc - // satisfies a job that calls cargo directly; it is invisible to - // actions-rust-lang/setup-rust-toolchain, which resolves the channel in - // rust-toolchain.toml through rustup. Both are needed because the estate - // does both. - return []string{"bun", "gh", "go", "node22", "node24", "node25", "pnpm", "rust", "rustup", "uv", "yarn"} + // rustup owns the whole Rust surface. A standalone rust tarball was baked + // beside it once, and the rustup shims overwrote its /usr/local/bin + // binaries during provisioning, so the "system" rust the recipe promised + // 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"} } // OptionalToolchains may be pinned by an image that needs them and omitted by diff --git a/internal/imageplan/plan_test.go b/internal/imageplan/plan_test.go index 0976bb57..7bfa0007 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", "rust", "rustup", "uv", "yarn"}) { + if !slices.Equal(names, []string{"bun", "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 da72aee2..852e3b46 100644 --- a/internal/repositorycontract/external_downloads_test.go +++ b/internal/repositorycontract/external_downloads_test.go @@ -68,7 +68,7 @@ func TestExternalDownloadFallbackMarkersRemainBounded(t *testing.T) { "--retry 2", "verify_candidate", "nddev_tool_cache_event=", }, "scripts/install-benchmark-toolchain.sh": { - "--retry 2", "toolchain SHA-256 mismatch", "rustc 1.97.1", + "--retry 2", "toolchain SHA-256 mismatch", "rustc 1.98.0", }, "internal/garmproviderincus/provider/incus.go": { "--retry 2", "validateRunnerTool", diff --git a/scripts/install-benchmark-toolchain.sh b/scripts/install-benchmark-toolchain.sh index 43e15359..b3e6aac5 100755 --- a/scripts/install-benchmark-toolchain.sh +++ b/scripts/install-benchmark-toolchain.sh @@ -68,8 +68,8 @@ install -d -m 0700 "${install_root}" "${binary_root}" case "${toolchain}" in rust) - if [[ "$(rustc --version 2>/dev/null || true)" == "rustc 1.97.1 "* && - "$(cargo --version 2>/dev/null || true)" == "cargo 1.97.1 "* ]]; then + if [[ "$(rustc --version 2>/dev/null || true)" == "rustc 1.98.0 "* && + "$(cargo --version 2>/dev/null || true)" == "cargo 1.98.0 "* ]]; then exit 0 fi : "${CARGO_HOME:?CARGO_HOME is required for Rust installation}" @@ -85,12 +85,12 @@ case "${toolchain}" in "${rustup_init}" chmod 0700 "${rustup_init}" export RUSTUP_HOME="${rustup_home}" - "${rustup_init}" --default-toolchain 1.97.1 --profile minimal --no-modify-path -y + "${rustup_init}" --default-toolchain 1.98.0 --profile minimal --no-modify-path -y printf 'RUSTUP_HOME=%s\n' "${rustup_home}" >>"${GITHUB_ENV}" printf '%s/bin\n' "${CARGO_HOME}" >>"${GITHUB_PATH}" export PATH="${CARGO_HOME}/bin:${PATH}" - [[ "$(rustc --version)" == "rustc 1.97.1 "* ]] - [[ "$(cargo --version)" == "cargo 1.97.1 "* ]] + [[ "$(rustc --version)" == "rustc 1.98.0 "* ]] + [[ "$(cargo --version)" == "cargo 1.98.0 "* ]] ;; uv) if [[ "$(uv --version 2>/dev/null || true)" == "uv 0.11.30"* ]]; then