From b13636f54b8948290ab7f7ee1860c2d83e0cedf1 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Tue, 9 Dec 2025 11:01:11 -0500 Subject: [PATCH] Consider all installed toolchains in cache key --- README.md | 3 ++- dist/restore/index.js | 50 +++++++++++++++++++++++++++++++--------- dist/save/index.js | 50 +++++++++++++++++++++++++++++++--------- src/config.ts | 53 +++++++++++++++++++++++++++++++++---------- 4 files changed, 121 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 280226b..96f486a 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,8 @@ This cache is automatically keyed by: - the github [`job_id`](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_id) (if `add-job-id-key` is `"true"`), -- the rustc release / host / hash, +- the rustc release / host / hash (for all installed toolchains when + available), - the following values, if `add-rust-environment-hash-key` is `"true"`: - the value of some compiler-specific environment variables (eg. RUSTFLAGS, etc), and - a hash of all `Cargo.lock` / `Cargo.toml` files found anywhere in the repository (if present). diff --git a/dist/restore/index.js b/dist/restore/index.js index 8dc39e9..7ee1ddf 100644 --- a/dist/restore/index.js +++ b/dist/restore/index.js @@ -150809,7 +150809,7 @@ class CacheConfig { /** The prefix portion of the cache key */ this.keyPrefix = ""; /** The rust version considered for the cache key */ - this.keyRust = ""; + this.keyRust = []; /** The environment variables considered for the cache key */ this.keyEnvs = []; /** The files considered for the cache key */ @@ -150863,12 +150863,15 @@ class CacheConfig { // The env vars are sorted, matched by prefix and hashed into the // resulting environment hash. let hasher = external_crypto_default().createHash("sha1"); - const rustVersion = await getRustVersion(cmdFormat); - let keyRust = `${rustVersion.release} ${rustVersion.host}`; - hasher.update(keyRust); - hasher.update(rustVersion["commit-hash"]); - keyRust += ` (${rustVersion["commit-hash"]})`; - self.keyRust = keyRust; + const rustVersions = Array.from(await getRustVersions(cmdFormat)); + // Doesn't matter how they're sorted, just as long as it's deterministic. + rustVersions.sort(); + for (const rustVersion of rustVersions) { + const { release, host, "commit-hash": commitHash } = rustVersion; + const keyRust = `${release} ${host} ${commitHash}`; + hasher.update(keyRust); + self.keyRust.push(keyRust); + } // these prefixes should cover most of the compiler / rust / cargo keys const envPrefixes = ["CARGO", "CC", "CFLAGS", "CXX", "CMAKE", "RUST"]; envPrefixes.push(...lib_core.getInput("env-vars").split(/\s+/).filter(Boolean)); @@ -151052,7 +151055,10 @@ class CacheConfig { lib_core.info(`.. Prefix:`); lib_core.info(` - ${this.keyPrefix}`); lib_core.info(`.. Environment considered:`); - lib_core.info(` - Rust Version: ${this.keyRust}`); + lib_core.info(` - Rust Versions:`); + for (const rust of this.keyRust) { + lib_core.info(` - ${rust}`); + } for (const env of this.keyEnvs) { lib_core.info(` - ${env}`); } @@ -151087,9 +151093,31 @@ function isCacheUpToDate() { function digest(hasher) { return hasher.digest("hex").substring(0, HASH_LENGTH); } -async function getRustVersion(cmdFormat) { - const stdout = await getCmdOutput(cmdFormat, "rustc -vV"); - let splits = stdout +async function getRustVersions(cmdFormat) { + const versions = new Set(); + versions.add(parseRustVersion(await getCmdOutput(cmdFormat, "rustc -vV"))); + const stdout = await (async () => { + try { + return await getCmdOutput(cmdFormat, "rustup toolchain list --quiet"); + } + catch (e) { + lib_core.warning(`Error running rustup toolchain list, falling back to default toolchain only: ${e}`); + return undefined; + } + })(); + if (stdout !== undefined) { + for (const toolchain of stdout.split(/[\n\r]+/)) { + const trimmed = toolchain.trim(); + if (!trimmed) { + continue; + } + versions.add(parseRustVersion(await getCmdOutput(cmdFormat, `rustup run ${toolchain} rustc -vV`))); + } + } + return versions; +} +function parseRustVersion(stdout) { + const splits = stdout .split(/[\n\r]+/) .filter(Boolean) .map((s) => s.split(":").map((s) => s.trim())) diff --git a/dist/save/index.js b/dist/save/index.js index 3c9703c..bb929e7 100644 --- a/dist/save/index.js +++ b/dist/save/index.js @@ -150809,7 +150809,7 @@ class CacheConfig { /** The prefix portion of the cache key */ this.keyPrefix = ""; /** The rust version considered for the cache key */ - this.keyRust = ""; + this.keyRust = []; /** The environment variables considered for the cache key */ this.keyEnvs = []; /** The files considered for the cache key */ @@ -150863,12 +150863,15 @@ class CacheConfig { // The env vars are sorted, matched by prefix and hashed into the // resulting environment hash. let hasher = external_crypto_default().createHash("sha1"); - const rustVersion = await getRustVersion(cmdFormat); - let keyRust = `${rustVersion.release} ${rustVersion.host}`; - hasher.update(keyRust); - hasher.update(rustVersion["commit-hash"]); - keyRust += ` (${rustVersion["commit-hash"]})`; - self.keyRust = keyRust; + const rustVersions = Array.from(await getRustVersions(cmdFormat)); + // Doesn't matter how they're sorted, just as long as it's deterministic. + rustVersions.sort(); + for (const rustVersion of rustVersions) { + const { release, host, "commit-hash": commitHash } = rustVersion; + const keyRust = `${release} ${host} ${commitHash}`; + hasher.update(keyRust); + self.keyRust.push(keyRust); + } // these prefixes should cover most of the compiler / rust / cargo keys const envPrefixes = ["CARGO", "CC", "CFLAGS", "CXX", "CMAKE", "RUST"]; envPrefixes.push(...core.getInput("env-vars").split(/\s+/).filter(Boolean)); @@ -151052,7 +151055,10 @@ class CacheConfig { core.info(`.. Prefix:`); core.info(` - ${this.keyPrefix}`); core.info(`.. Environment considered:`); - core.info(` - Rust Version: ${this.keyRust}`); + core.info(` - Rust Versions:`); + for (const rust of this.keyRust) { + core.info(` - ${rust}`); + } for (const env of this.keyEnvs) { core.info(` - ${env}`); } @@ -151087,9 +151093,31 @@ function isCacheUpToDate() { function digest(hasher) { return hasher.digest("hex").substring(0, HASH_LENGTH); } -async function getRustVersion(cmdFormat) { - const stdout = await getCmdOutput(cmdFormat, "rustc -vV"); - let splits = stdout +async function getRustVersions(cmdFormat) { + const versions = new Set(); + versions.add(parseRustVersion(await getCmdOutput(cmdFormat, "rustc -vV"))); + const stdout = await (async () => { + try { + return await getCmdOutput(cmdFormat, "rustup toolchain list --quiet"); + } + catch (e) { + core.warning(`Error running rustup toolchain list, falling back to default toolchain only: ${e}`); + return undefined; + } + })(); + if (stdout !== undefined) { + for (const toolchain of stdout.split(/[\n\r]+/)) { + const trimmed = toolchain.trim(); + if (!trimmed) { + continue; + } + versions.add(parseRustVersion(await getCmdOutput(cmdFormat, `rustup run ${toolchain} rustc -vV`))); + } + } + return versions; +} +function parseRustVersion(stdout) { + const splits = stdout .split(/[\n\r]+/) .filter(Boolean) .map((s) => s.split(":").map((s) => s.trim())) diff --git a/src/config.ts b/src/config.ts index 40c89a1..d562033 100644 --- a/src/config.ts +++ b/src/config.ts @@ -41,7 +41,7 @@ export class CacheConfig { /** The prefix portion of the cache key */ private keyPrefix = ""; /** The rust version considered for the cache key */ - private keyRust = ""; + private keyRust: Array = []; /** The environment variables considered for the cache key */ private keyEnvs: Array = []; /** The files considered for the cache key */ @@ -104,14 +104,16 @@ export class CacheConfig { // resulting environment hash. let hasher = crypto.createHash("sha1"); - const rustVersion = await getRustVersion(cmdFormat); + const rustVersions = Array.from(await getRustVersions(cmdFormat)); + // Doesn't matter how they're sorted, just as long as it's deterministic. + rustVersions.sort(); - let keyRust = `${rustVersion.release} ${rustVersion.host}`; - hasher.update(keyRust); - hasher.update(rustVersion["commit-hash"]); - - keyRust += ` (${rustVersion["commit-hash"]})`; - self.keyRust = keyRust; + for (const rustVersion of rustVersions) { + const { release, host, "commit-hash": commitHash } = rustVersion; + const keyRust = `${release} ${host} ${commitHash}`; + hasher.update(keyRust); + self.keyRust.push(keyRust); + } // these prefixes should cover most of the compiler / rust / cargo keys const envPrefixes = ["CARGO", "CC", "CFLAGS", "CXX", "CMAKE", "RUST"]; @@ -334,7 +336,10 @@ export class CacheConfig { core.info(`.. Prefix:`); core.info(` - ${this.keyPrefix}`); core.info(`.. Environment considered:`); - core.info(` - Rust Version: ${this.keyRust}`); + core.info(` - Rust Versions:`); + for (const rust of this.keyRust) { + core.info(` - ${rust}`); + } for (const env of this.keyEnvs) { core.info(` - ${env}`); } @@ -379,9 +384,33 @@ interface RustVersion { "commit-hash": string; } -async function getRustVersion(cmdFormat: string): Promise { - const stdout = await getCmdOutput(cmdFormat, "rustc -vV"); - let splits = stdout +async function getRustVersions(cmdFormat: string): Promise> { + const versions = new Set(); + + versions.add(parseRustVersion(await getCmdOutput(cmdFormat, "rustc -vV"))); + + const stdout = await (async () => { + try { + return await getCmdOutput(cmdFormat, "rustup toolchain list --quiet"); + } catch (e) { + core.warning(`Error running rustup toolchain list, falling back to default toolchain only: ${e}`); + return undefined; + } + })(); + if (stdout !== undefined) { + for (const toolchain of stdout.split(/[\n\r]+/)) { + const trimmed = toolchain.trim(); + if (!trimmed) { + continue; + } + versions.add(parseRustVersion(await getCmdOutput(cmdFormat, `rustup run ${toolchain} rustc -vV`))); + } + } + return versions; +} + +function parseRustVersion(stdout: string): RustVersion { + const splits = stdout .split(/[\n\r]+/) .filter(Boolean) .map((s) => s.split(":").map((s) => s.trim()))