From 529beb4c7e62f5ec9fd825e0b6cd7b5e01e69ae7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 27 Nov 2025 05:28:15 +0000 Subject: [PATCH 1/8] feat: Implement ability to disable adding job ID + rust environment hashes to cache names (#279) --- README.md | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d4bb74b..ce90719 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,16 @@ sensible defaults. # default: empty key: "" + # If the automatic `job`-based cache key should include the job id. + # default: "true" + add-job-id-key: "" + + # Weather the a hash of the rust environment should be included in the cache key. + # This includes a hash of all Cargo.toml/Cargo.lock files, rust-toolchain files, + # and .cargo/config.toml files (if present), as well as the specified 'env-vars'. + # default: "true" + add-rust-environment-hash-key: "" + # A whitespace separated list of env-var *prefixes* who's value contributes # to the environment cache key. # The env-vars are matched by *prefix*, so the default `RUST` var will @@ -121,12 +131,14 @@ This action currently caches the following files/directories: 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), +- 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 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). -- a hash of all `rust-toolchain` / `rust-toolchain.toml` files in the root of the repository (if present). -- a hash of all `.cargo/config.toml` files in the root of the repository (if present). +- 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). + - a hash of all `rust-toolchain` / `rust-toolchain.toml` files in the root of the repository (if present). + - a hash of all `.cargo/config.toml` files in the root of the repository (if present). An additional input `key` can be provided if the builtin keys are not sufficient. From 84bb9f8a73ff8b7e7eaf5e7a39198e0f6d45c9a6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 27 Nov 2025 05:28:16 +0000 Subject: [PATCH 2/8] feat: Implement ability to disable adding job ID + rust environment hashes to cache names (#279) --- action.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/action.yml b/action.yml index e3b2784..1ae1a2c 100644 --- a/action.yml +++ b/action.yml @@ -11,6 +11,14 @@ inputs: key: description: "An additional cache key that is added alongside the automatic `job`-based cache key and can be used to further differentiate jobs." required: false + add-job-id-key: + description: "If the automatic `job`-based cache key should include the job id. Defaults to true." + required: false + default: "true" + add-rust-environment-hash-key: + description: "Weather the a hash of the rust environment should be included in the cache key. This includes a hash of all Cargo.toml/Cargo.lock files, rust-toolchain files, and .cargo/config.toml files (if present), as well as the specified 'env-vars'. Defaults to true." + required: false + default: "true" env-vars: description: "Additional environment variables to include in the cache key, separated by spaces." required: false From de4326e969ec225f6932f154d3dfdf8719c3945f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 27 Nov 2025 05:28:17 +0000 Subject: [PATCH 3/8] feat: Implement ability to disable adding job ID + rust environment hashes to cache names (#279) --- src/config.ts | 171 ++++++++++++++++++++++++++------------------------ 1 file changed, 89 insertions(+), 82 deletions(-) diff --git a/src/config.ts b/src/config.ts index e592916..1a79706 100644 --- a/src/config.ts +++ b/src/config.ts @@ -69,7 +69,7 @@ export class CacheConfig { } const job = process.env.GITHUB_JOB; - if (job) { + if ((job) && core.getInput("add-job-id-key").toLowerCase() == "true") { key += `-${job}`; } } @@ -116,7 +116,10 @@ export class CacheConfig { self.keyEnvs = keyEnvs; - key += `-${digest(hasher)}`; + // Add job hash suffix if 'add-rust-environment-hash-key' is true + if (core.getInput("add-rust-environment-hash-key").toLowerCase() == "true") { + key += `-${digest(hasher)}`; + } self.restoreKey = key; @@ -139,111 +142,115 @@ export class CacheConfig { } self.workspaces = workspaces; - let keyFiles = await globFiles(".cargo/config.toml\nrust-toolchain\nrust-toolchain.toml"); - const parsedKeyFiles = []; // keyFiles that are parsed, pre-processed and hashed - - hasher = crypto.createHash("sha1"); + // Add hash suffix of all rust environment lockfiles + manifests if + // 'add-rust-environment-hash-key' is true + if (core.getInput("add-rust-environment-hash-key").toLowerCase() == "true") { + let keyFiles = await globFiles(".cargo/config.toml\nrust-toolchain\nrust-toolchain.toml"); + const parsedKeyFiles = []; // keyFiles that are parsed, pre-processed and hashed - for (const workspace of workspaces) { - const root = workspace.root; - keyFiles.push( - ...(await globFiles( - `${root}/**/.cargo/config.toml\n${root}/**/rust-toolchain\n${root}/**/rust-toolchain.toml`, - )), - ); + hasher = crypto.createHash("sha1"); - const workspaceMembers = await workspace.getWorkspaceMembers(); + for (const workspace of workspaces) { + const root = workspace.root; + keyFiles.push( + ...(await globFiles( + `${root}/**/.cargo/config.toml\n${root}/**/rust-toolchain\n${root}/**/rust-toolchain.toml`, + )), + ); - const cargo_manifests = sort_and_uniq(workspaceMembers.map((member) => path.join(member.path, "Cargo.toml"))); + const workspaceMembers = await workspace.getWorkspaceMembers(); - for (const cargo_manifest of cargo_manifests) { - try { - const content = await fs_promises.readFile(cargo_manifest, { encoding: "utf8" }); - // Use any since TomlPrimitive is not exposed - const parsed = toml.parse(content) as { [key: string]: any }; + const cargo_manifests = sort_and_uniq(workspaceMembers.map((member) => path.join(member.path, "Cargo.toml"))); - if ("package" in parsed) { - const pack = parsed.package; - if ("version" in pack) { - pack["version"] = "0.0.0"; - } - } + for (const cargo_manifest of cargo_manifests) { + try { + const content = await fs_promises.readFile(cargo_manifest, { encoding: "utf8" }); + // Use any since TomlPrimitive is not exposed + const parsed = toml.parse(content) as { [key: string]: any }; - for (const prefix of ["", "build-", "dev-"]) { - const section_name = `${prefix}dependencies`; - if (!(section_name in parsed)) { - continue; + if ("package" in parsed) { + const pack = parsed.package; + if ("version" in pack) { + pack["version"] = "0.0.0"; + } } - const deps = parsed[section_name]; - for (const key of Object.keys(deps)) { - const dep = deps[key]; - - try { - if ("path" in dep) { - dep.version = "0.0.0"; - dep.path = ""; - } - } catch (_e) { - // Not an object, probably a string (version), - // continue. + for (const prefix of ["", "build-", "dev-"]) { + const section_name = `${prefix}dependencies`; + if (!(section_name in parsed)) { continue; } + const deps = parsed[section_name]; + + for (const key of Object.keys(deps)) { + const dep = deps[key]; + + try { + if ("path" in dep) { + dep.version = "0.0.0"; + dep.path = ""; + } + } catch (_e) { + // Not an object, probably a string (version), + // continue. + continue; + } + } } - } - hasher.update(JSON.stringify(parsed)); + hasher.update(JSON.stringify(parsed)); - parsedKeyFiles.push(cargo_manifest); - } catch (e) { - // Fallback to caching them as regular file - core.warning(`Error parsing Cargo.toml manifest, fallback to caching entire file: ${e}`); - keyFiles.push(cargo_manifest); + parsedKeyFiles.push(cargo_manifest); + } catch (e) { + // Fallback to caching them as regular file + core.warning(`Error parsing Cargo.toml manifest, fallback to caching entire file: ${e}`); + keyFiles.push(cargo_manifest); + } } - } - - const cargo_lock = path.join(workspace.root, "Cargo.lock"); - if (await exists(cargo_lock)) { - try { - const content = await fs_promises.readFile(cargo_lock, { encoding: "utf8" }); - const parsed = toml.parse(content); - if ((parsed.version !== 3 && parsed.version !== 4) || !("package" in parsed)) { - // Fallback to caching them as regular file since this action - // can only handle Cargo.lock format version 3 - core.warning("Unsupported Cargo.lock format, fallback to caching entire file"); - keyFiles.push(cargo_lock); - continue; - } + const cargo_lock = path.join(workspace.root, "Cargo.lock"); + if (await exists(cargo_lock)) { + try { + const content = await fs_promises.readFile(cargo_lock, { encoding: "utf8" }); + const parsed = toml.parse(content); + + if ((parsed.version !== 3 && parsed.version !== 4) || !("package" in parsed)) { + // Fallback to caching them as regular file since this action + // can only handle Cargo.lock format version 3 + core.warning("Unsupported Cargo.lock format, fallback to caching entire file"); + keyFiles.push(cargo_lock); + continue; + } - // Package without `[[package]].source` and `[[package]].checksum` - // are the one with `path = "..."` to crates within the workspace. - const packages = (parsed.package as any[]).filter((p: any) => "source" in p || "checksum" in p); + // Package without `[[package]].source` and `[[package]].checksum` + // are the one with `path = "..."` to crates within the workspace. + const packages = (parsed.package as any[]).filter((p: any) => "source" in p || "checksum" in p); - hasher.update(JSON.stringify(packages)); + hasher.update(JSON.stringify(packages)); - parsedKeyFiles.push(cargo_lock); - } catch (e) { - // Fallback to caching them as regular file - core.warning(`Error parsing Cargo.lock manifest, fallback to caching entire file: ${e}`); - keyFiles.push(cargo_lock); + parsedKeyFiles.push(cargo_lock); + } catch (e) { + // Fallback to caching them as regular file + core.warning(`Error parsing Cargo.lock manifest, fallback to caching entire file: ${e}`); + keyFiles.push(cargo_lock); + } } } - } - keyFiles = sort_and_uniq(keyFiles); + keyFiles = sort_and_uniq(keyFiles); - for (const file of keyFiles) { - for await (const chunk of fs.createReadStream(file)) { - hasher.update(chunk); + for (const file of keyFiles) { + for await (const chunk of fs.createReadStream(file)) { + hasher.update(chunk); + } } - } - let lockHash = digest(hasher); + keyFiles.push(...parsedKeyFiles); + self.keyFiles = sort_and_uniq(keyFiles); - keyFiles.push(...parsedKeyFiles); - self.keyFiles = sort_and_uniq(keyFiles); + let lockHash = digest(hasher); + key += `-${lockHash}`; + } - key += `-${lockHash}`; self.cacheKey = key; self.cachePaths = [path.join(CARGO_HOME, "registry"), path.join(CARGO_HOME, "git")]; From be880ac2823fe58c4b140693fea0559f8b22d54d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 27 Nov 2025 05:28:21 +0000 Subject: [PATCH 4/8] Don't overwrite env for cargo-metadata call (#285) In some cases(eg. if RUST_TOOLCHAIN is set and cargo is managed by rustup) cargo metadata will behave incorrectly if some environment variables are removed --- src/workspace.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/workspace.ts b/src/workspace.ts index a7d4b30..48326e4 100644 --- a/src/workspace.ts +++ b/src/workspace.ts @@ -15,7 +15,7 @@ export class Workspace { const meta: Meta = JSON.parse( await getCmdOutput("cargo", ["metadata", "--all-features", "--format-version", "1", ...extraArgs], { cwd: this.root, - env: { "CARGO_ENCODED_RUSTFLAGS": "" }, + env: { ...process.env, "CARGO_ENCODED_RUSTFLAGS": "" }, }), ); core.debug(`workspace "${this.root}" has ${meta.packages.length} packages`); From a97c6619c01ce81f370475a1d0af028dce1e0849 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 27 Nov 2025 05:28:44 +0000 Subject: [PATCH 5/8] fix: apply code build script --- dist/restore/index.js | 157 ++++++++++++++++++++++-------------------- 1 file changed, 82 insertions(+), 75 deletions(-) diff --git a/dist/restore/index.js b/dist/restore/index.js index dec193a..1454580 100644 --- a/dist/restore/index.js +++ b/dist/restore/index.js @@ -144188,7 +144188,7 @@ class Workspace { lib_core.debug(`collecting metadata for "${this.root}"`); const meta = JSON.parse(await getCmdOutput("cargo", ["metadata", "--all-features", "--format-version", "1", ...extraArgs], { cwd: this.root, - env: { "CARGO_ENCODED_RUSTFLAGS": "" }, + env: { ...process.env, "CARGO_ENCODED_RUSTFLAGS": "" }, })); lib_core.debug(`workspace "${this.root}" has ${meta.packages.length} packages`); for (const pkg of meta.packages.filter(filter)) { @@ -144269,7 +144269,7 @@ class CacheConfig { key += `-${inputKey}`; } const job = process.env.GITHUB_JOB; - if (job) { + if ((job) && lib_core.getInput("add-job-id-key").toLowerCase() == "true") { key += `-${job}`; } } @@ -144306,7 +144306,10 @@ class CacheConfig { } } self.keyEnvs = keyEnvs; - key += `-${digest(hasher)}`; + // Add job hash suffix if 'add-rust-environment-hash-key' is true + if (lib_core.getInput("add-rust-environment-hash-key").toLowerCase() == "true") { + key += `-${digest(hasher)}`; + } self.restoreKey = key; // Construct the lockfiles portion of the key: // This considers all the files found via globbing for various manifests @@ -144323,90 +144326,94 @@ class CacheConfig { workspaces.push(new Workspace(root, target)); } self.workspaces = workspaces; - let keyFiles = await globFiles(".cargo/config.toml\nrust-toolchain\nrust-toolchain.toml"); - const parsedKeyFiles = []; // keyFiles that are parsed, pre-processed and hashed - hasher = external_crypto_default().createHash("sha1"); - for (const workspace of workspaces) { - const root = workspace.root; - keyFiles.push(...(await globFiles(`${root}/**/.cargo/config.toml\n${root}/**/rust-toolchain\n${root}/**/rust-toolchain.toml`))); - const workspaceMembers = await workspace.getWorkspaceMembers(); - const cargo_manifests = sort_and_uniq(workspaceMembers.map((member) => external_path_default().join(member.path, "Cargo.toml"))); - for (const cargo_manifest of cargo_manifests) { - try { - const content = await promises_default().readFile(cargo_manifest, { encoding: "utf8" }); - // Use any since TomlPrimitive is not exposed - const parsed = parse(content); - if ("package" in parsed) { - const pack = parsed.package; - if ("version" in pack) { - pack["version"] = "0.0.0"; - } - } - for (const prefix of ["", "build-", "dev-"]) { - const section_name = `${prefix}dependencies`; - if (!(section_name in parsed)) { - continue; - } - const deps = parsed[section_name]; - for (const key of Object.keys(deps)) { - const dep = deps[key]; - try { - if ("path" in dep) { - dep.version = "0.0.0"; - dep.path = ""; - } + // Add hash suffix of all rust environment lockfiles + manifests if + // 'add-rust-environment-hash-key' is true + if (lib_core.getInput("add-rust-environment-hash-key").toLowerCase() == "true") { + let keyFiles = await globFiles(".cargo/config.toml\nrust-toolchain\nrust-toolchain.toml"); + const parsedKeyFiles = []; // keyFiles that are parsed, pre-processed and hashed + hasher = external_crypto_default().createHash("sha1"); + for (const workspace of workspaces) { + const root = workspace.root; + keyFiles.push(...(await globFiles(`${root}/**/.cargo/config.toml\n${root}/**/rust-toolchain\n${root}/**/rust-toolchain.toml`))); + const workspaceMembers = await workspace.getWorkspaceMembers(); + const cargo_manifests = sort_and_uniq(workspaceMembers.map((member) => external_path_default().join(member.path, "Cargo.toml"))); + for (const cargo_manifest of cargo_manifests) { + try { + const content = await promises_default().readFile(cargo_manifest, { encoding: "utf8" }); + // Use any since TomlPrimitive is not exposed + const parsed = parse(content); + if ("package" in parsed) { + const pack = parsed.package; + if ("version" in pack) { + pack["version"] = "0.0.0"; } - catch (_e) { - // Not an object, probably a string (version), - // continue. + } + for (const prefix of ["", "build-", "dev-"]) { + const section_name = `${prefix}dependencies`; + if (!(section_name in parsed)) { continue; } + const deps = parsed[section_name]; + for (const key of Object.keys(deps)) { + const dep = deps[key]; + try { + if ("path" in dep) { + dep.version = "0.0.0"; + dep.path = ""; + } + } + catch (_e) { + // Not an object, probably a string (version), + // continue. + continue; + } + } } + hasher.update(JSON.stringify(parsed)); + parsedKeyFiles.push(cargo_manifest); + } + catch (e) { + // Fallback to caching them as regular file + lib_core.warning(`Error parsing Cargo.toml manifest, fallback to caching entire file: ${e}`); + keyFiles.push(cargo_manifest); } - hasher.update(JSON.stringify(parsed)); - parsedKeyFiles.push(cargo_manifest); - } - catch (e) { - // Fallback to caching them as regular file - lib_core.warning(`Error parsing Cargo.toml manifest, fallback to caching entire file: ${e}`); - keyFiles.push(cargo_manifest); } - } - const cargo_lock = external_path_default().join(workspace.root, "Cargo.lock"); - if (await utils_exists(cargo_lock)) { - try { - const content = await promises_default().readFile(cargo_lock, { encoding: "utf8" }); - const parsed = parse(content); - if ((parsed.version !== 3 && parsed.version !== 4) || !("package" in parsed)) { - // Fallback to caching them as regular file since this action - // can only handle Cargo.lock format version 3 - lib_core.warning("Unsupported Cargo.lock format, fallback to caching entire file"); + const cargo_lock = external_path_default().join(workspace.root, "Cargo.lock"); + if (await utils_exists(cargo_lock)) { + try { + const content = await promises_default().readFile(cargo_lock, { encoding: "utf8" }); + const parsed = parse(content); + if ((parsed.version !== 3 && parsed.version !== 4) || !("package" in parsed)) { + // Fallback to caching them as regular file since this action + // can only handle Cargo.lock format version 3 + lib_core.warning("Unsupported Cargo.lock format, fallback to caching entire file"); + keyFiles.push(cargo_lock); + continue; + } + // Package without `[[package]].source` and `[[package]].checksum` + // are the one with `path = "..."` to crates within the workspace. + const packages = parsed.package.filter((p) => "source" in p || "checksum" in p); + hasher.update(JSON.stringify(packages)); + parsedKeyFiles.push(cargo_lock); + } + catch (e) { + // Fallback to caching them as regular file + lib_core.warning(`Error parsing Cargo.lock manifest, fallback to caching entire file: ${e}`); keyFiles.push(cargo_lock); - continue; } - // Package without `[[package]].source` and `[[package]].checksum` - // are the one with `path = "..."` to crates within the workspace. - const packages = parsed.package.filter((p) => "source" in p || "checksum" in p); - hasher.update(JSON.stringify(packages)); - parsedKeyFiles.push(cargo_lock); - } - catch (e) { - // Fallback to caching them as regular file - lib_core.warning(`Error parsing Cargo.lock manifest, fallback to caching entire file: ${e}`); - keyFiles.push(cargo_lock); } } - } - keyFiles = sort_and_uniq(keyFiles); - for (const file of keyFiles) { - for await (const chunk of external_fs_default().createReadStream(file)) { - hasher.update(chunk); + keyFiles = sort_and_uniq(keyFiles); + for (const file of keyFiles) { + for await (const chunk of external_fs_default().createReadStream(file)) { + hasher.update(chunk); + } } + keyFiles.push(...parsedKeyFiles); + self.keyFiles = sort_and_uniq(keyFiles); + let lockHash = digest(hasher); + key += `-${lockHash}`; } - let lockHash = digest(hasher); - keyFiles.push(...parsedKeyFiles); - self.keyFiles = sort_and_uniq(keyFiles); - key += `-${lockHash}`; self.cacheKey = key; self.cachePaths = [external_path_default().join(config_CARGO_HOME, "registry"), external_path_default().join(config_CARGO_HOME, "git")]; if (self.cacheBin) { From ef784a091c721eb81afe78f1a6a7c1f9a5dfbfde Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 27 Nov 2025 05:28:46 +0000 Subject: [PATCH 6/8] fix: apply code build script --- dist/save/index.js | 157 +++++++++++++++++++++++---------------------- 1 file changed, 82 insertions(+), 75 deletions(-) diff --git a/dist/save/index.js b/dist/save/index.js index 6b089b8..be2aedd 100644 --- a/dist/save/index.js +++ b/dist/save/index.js @@ -144177,7 +144177,7 @@ class Workspace { core.debug(`collecting metadata for "${this.root}"`); const meta = JSON.parse(await getCmdOutput("cargo", ["metadata", "--all-features", "--format-version", "1", ...extraArgs], { cwd: this.root, - env: { "CARGO_ENCODED_RUSTFLAGS": "" }, + env: { ...process.env, "CARGO_ENCODED_RUSTFLAGS": "" }, })); core.debug(`workspace "${this.root}" has ${meta.packages.length} packages`); for (const pkg of meta.packages.filter(filter)) { @@ -144258,7 +144258,7 @@ class CacheConfig { key += `-${inputKey}`; } const job = process.env.GITHUB_JOB; - if (job) { + if ((job) && core.getInput("add-job-id-key").toLowerCase() == "true") { key += `-${job}`; } } @@ -144295,7 +144295,10 @@ class CacheConfig { } } self.keyEnvs = keyEnvs; - key += `-${digest(hasher)}`; + // Add job hash suffix if 'add-rust-environment-hash-key' is true + if (core.getInput("add-rust-environment-hash-key").toLowerCase() == "true") { + key += `-${digest(hasher)}`; + } self.restoreKey = key; // Construct the lockfiles portion of the key: // This considers all the files found via globbing for various manifests @@ -144312,90 +144315,94 @@ class CacheConfig { workspaces.push(new Workspace(root, target)); } self.workspaces = workspaces; - let keyFiles = await globFiles(".cargo/config.toml\nrust-toolchain\nrust-toolchain.toml"); - const parsedKeyFiles = []; // keyFiles that are parsed, pre-processed and hashed - hasher = external_crypto_default().createHash("sha1"); - for (const workspace of workspaces) { - const root = workspace.root; - keyFiles.push(...(await globFiles(`${root}/**/.cargo/config.toml\n${root}/**/rust-toolchain\n${root}/**/rust-toolchain.toml`))); - const workspaceMembers = await workspace.getWorkspaceMembers(); - const cargo_manifests = sort_and_uniq(workspaceMembers.map((member) => external_path_default().join(member.path, "Cargo.toml"))); - for (const cargo_manifest of cargo_manifests) { - try { - const content = await promises_default().readFile(cargo_manifest, { encoding: "utf8" }); - // Use any since TomlPrimitive is not exposed - const parsed = parse(content); - if ("package" in parsed) { - const pack = parsed.package; - if ("version" in pack) { - pack["version"] = "0.0.0"; - } - } - for (const prefix of ["", "build-", "dev-"]) { - const section_name = `${prefix}dependencies`; - if (!(section_name in parsed)) { - continue; - } - const deps = parsed[section_name]; - for (const key of Object.keys(deps)) { - const dep = deps[key]; - try { - if ("path" in dep) { - dep.version = "0.0.0"; - dep.path = ""; - } + // Add hash suffix of all rust environment lockfiles + manifests if + // 'add-rust-environment-hash-key' is true + if (core.getInput("add-rust-environment-hash-key").toLowerCase() == "true") { + let keyFiles = await globFiles(".cargo/config.toml\nrust-toolchain\nrust-toolchain.toml"); + const parsedKeyFiles = []; // keyFiles that are parsed, pre-processed and hashed + hasher = external_crypto_default().createHash("sha1"); + for (const workspace of workspaces) { + const root = workspace.root; + keyFiles.push(...(await globFiles(`${root}/**/.cargo/config.toml\n${root}/**/rust-toolchain\n${root}/**/rust-toolchain.toml`))); + const workspaceMembers = await workspace.getWorkspaceMembers(); + const cargo_manifests = sort_and_uniq(workspaceMembers.map((member) => external_path_default().join(member.path, "Cargo.toml"))); + for (const cargo_manifest of cargo_manifests) { + try { + const content = await promises_default().readFile(cargo_manifest, { encoding: "utf8" }); + // Use any since TomlPrimitive is not exposed + const parsed = parse(content); + if ("package" in parsed) { + const pack = parsed.package; + if ("version" in pack) { + pack["version"] = "0.0.0"; } - catch (_e) { - // Not an object, probably a string (version), - // continue. + } + for (const prefix of ["", "build-", "dev-"]) { + const section_name = `${prefix}dependencies`; + if (!(section_name in parsed)) { continue; } + const deps = parsed[section_name]; + for (const key of Object.keys(deps)) { + const dep = deps[key]; + try { + if ("path" in dep) { + dep.version = "0.0.0"; + dep.path = ""; + } + } + catch (_e) { + // Not an object, probably a string (version), + // continue. + continue; + } + } } + hasher.update(JSON.stringify(parsed)); + parsedKeyFiles.push(cargo_manifest); + } + catch (e) { + // Fallback to caching them as regular file + core.warning(`Error parsing Cargo.toml manifest, fallback to caching entire file: ${e}`); + keyFiles.push(cargo_manifest); } - hasher.update(JSON.stringify(parsed)); - parsedKeyFiles.push(cargo_manifest); - } - catch (e) { - // Fallback to caching them as regular file - core.warning(`Error parsing Cargo.toml manifest, fallback to caching entire file: ${e}`); - keyFiles.push(cargo_manifest); } - } - const cargo_lock = external_path_default().join(workspace.root, "Cargo.lock"); - if (await exists(cargo_lock)) { - try { - const content = await promises_default().readFile(cargo_lock, { encoding: "utf8" }); - const parsed = parse(content); - if ((parsed.version !== 3 && parsed.version !== 4) || !("package" in parsed)) { - // Fallback to caching them as regular file since this action - // can only handle Cargo.lock format version 3 - core.warning("Unsupported Cargo.lock format, fallback to caching entire file"); + const cargo_lock = external_path_default().join(workspace.root, "Cargo.lock"); + if (await exists(cargo_lock)) { + try { + const content = await promises_default().readFile(cargo_lock, { encoding: "utf8" }); + const parsed = parse(content); + if ((parsed.version !== 3 && parsed.version !== 4) || !("package" in parsed)) { + // Fallback to caching them as regular file since this action + // can only handle Cargo.lock format version 3 + core.warning("Unsupported Cargo.lock format, fallback to caching entire file"); + keyFiles.push(cargo_lock); + continue; + } + // Package without `[[package]].source` and `[[package]].checksum` + // are the one with `path = "..."` to crates within the workspace. + const packages = parsed.package.filter((p) => "source" in p || "checksum" in p); + hasher.update(JSON.stringify(packages)); + parsedKeyFiles.push(cargo_lock); + } + catch (e) { + // Fallback to caching them as regular file + core.warning(`Error parsing Cargo.lock manifest, fallback to caching entire file: ${e}`); keyFiles.push(cargo_lock); - continue; } - // Package without `[[package]].source` and `[[package]].checksum` - // are the one with `path = "..."` to crates within the workspace. - const packages = parsed.package.filter((p) => "source" in p || "checksum" in p); - hasher.update(JSON.stringify(packages)); - parsedKeyFiles.push(cargo_lock); - } - catch (e) { - // Fallback to caching them as regular file - core.warning(`Error parsing Cargo.lock manifest, fallback to caching entire file: ${e}`); - keyFiles.push(cargo_lock); } } - } - keyFiles = sort_and_uniq(keyFiles); - for (const file of keyFiles) { - for await (const chunk of external_fs_default().createReadStream(file)) { - hasher.update(chunk); + keyFiles = sort_and_uniq(keyFiles); + for (const file of keyFiles) { + for await (const chunk of external_fs_default().createReadStream(file)) { + hasher.update(chunk); + } } + keyFiles.push(...parsedKeyFiles); + self.keyFiles = sort_and_uniq(keyFiles); + let lockHash = digest(hasher); + key += `-${lockHash}`; } - let lockHash = digest(hasher); - keyFiles.push(...parsedKeyFiles); - self.keyFiles = sort_and_uniq(keyFiles); - key += `-${lockHash}`; self.cacheKey = key; self.cachePaths = [external_path_default().join(CARGO_HOME, "registry"), external_path_default().join(CARGO_HOME, "git")]; if (self.cacheBin) { From c86728c5dce1789054c07fe7d1454a951a57877f Mon Sep 17 00:00:00 2001 From: Raj-StepSecurity Date: Tue, 2 Dec 2025 10:32:26 +0530 Subject: [PATCH 7/8] workflow files cherry-picked --- .github/workflows/buildjet.yml | 6 ++- .github/workflows/check-dist.yml | 4 +- .github/workflows/coverage.yml | 10 +++- .github/workflows/git-registry.yml | 6 ++- .github/workflows/install.yml | 6 ++- .github/workflows/multi-job-cache.yml | 75 +++++++++++++++++++++++++++ .github/workflows/simple.yml | 6 ++- .github/workflows/target-dir.yml | 6 ++- .github/workflows/workspaces.yml | 6 ++- 9 files changed, 116 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/multi-job-cache.yml diff --git a/.github/workflows/buildjet.yml b/.github/workflows/buildjet.yml index 64ebef2..d155eaf 100644 --- a/.github/workflows/buildjet.yml +++ b/.github/workflows/buildjet.yml @@ -6,6 +6,8 @@ on: pull_request: branches: ["main"] +permissions: {} + jobs: buildjet: strategy: @@ -25,7 +27,9 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@v6 + with: + persist-credentials: false - run: rustup toolchain install stable --profile minimal --no-self-update diff --git a/.github/workflows/check-dist.yml b/.github/workflows/check-dist.yml index bd6704f..7529fbc 100644 --- a/.github/workflows/check-dist.yml +++ b/.github/workflows/check-dist.yml @@ -23,7 +23,9 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@v6 + with: + persist-credentials: false - name: Setup Node.js 20.x uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 43e0129..f66a178 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -6,6 +6,8 @@ on: pull_request: branches: ["main"] +permissions: {} + jobs: coverage: strategy: @@ -25,11 +27,15 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@v6 + with: + persist-credentials: false - run: rustup toolchain install stable --profile minimal --component llvm-tools-preview --no-self-update - - uses: taiki-e/install-action@ef59e9fa397c19f99f2dda1d9857c0c704c41966 # cargo-llvm-cov + - uses: taiki-e/install-action@v2 + with: + tool: cargo-llvm-cov - uses: ./ with: diff --git a/.github/workflows/git-registry.yml b/.github/workflows/git-registry.yml index 6d556f2..6361308 100644 --- a/.github/workflows/git-registry.yml +++ b/.github/workflows/git-registry.yml @@ -6,6 +6,8 @@ on: pull_request: branches: ["main"] +permissions: {} + jobs: git-registry: strategy: @@ -26,7 +28,9 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@v6 + with: + persist-credentials: false - run: rustup toolchain install stable --profile minimal --no-self-update diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml index 4b34b2f..682cf66 100644 --- a/.github/workflows/install.yml +++ b/.github/workflows/install.yml @@ -6,6 +6,8 @@ on: pull_request: branches: ["main"] +permissions: {} + jobs: install: strategy: @@ -25,7 +27,9 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@v6 + with: + persist-credentials: false - run: rustup toolchain install stable --profile minimal --no-self-update diff --git a/.github/workflows/multi-job-cache.yml b/.github/workflows/multi-job-cache.yml new file mode 100644 index 0000000..3482544 --- /dev/null +++ b/.github/workflows/multi-job-cache.yml @@ -0,0 +1,75 @@ +name: multi-job-cache + +on: [push, pull_request] + +permissions: {} + +jobs: + multi-job-cache-1: + if: github.repository == 'step-security/rust-cache' + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + + name: Test multi-job cache (1) on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + + env: + CARGO_TERM_COLOR: always + + steps: + - name: checkout + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: install rust toolchain + run: rustup toolchain install stable --profile minimal --target wasm32-unknown-unknown --no-self-update + + - name: cache + uses: ./ + with: + workspaces: | + tests + add-job-id-key: "false" + add-rust-environment-hash-key: "false" + + - name: cargo check (tests) + working-directory: tests + run: cargo check + + multi-job-cache-2: + if: github.repository == 'step-security/rust-cache' + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + + name: Test multi-job cache (2) on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + + env: + CARGO_TERM_COLOR: always + + steps: + - name: checkout + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: install rust toolchain + run: rustup toolchain install stable --profile minimal --target wasm32-unknown-unknown --no-self-update + + - name: cache + uses: ./ + with: + workspaces: | + tests/wasm-workspace + add-job-id-key: "false" + add-rust-environment-hash-key: "false" + + - name: cargo check (tests/wasm-workspace) + working-directory: tests/wasm-workspace + run: cargo check + diff --git a/.github/workflows/simple.yml b/.github/workflows/simple.yml index c1baf5a..205585a 100644 --- a/.github/workflows/simple.yml +++ b/.github/workflows/simple.yml @@ -6,6 +6,8 @@ on: pull_request: branches: ["main"] +permissions: {} + jobs: simple: strategy: @@ -25,7 +27,9 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@v6 + with: + persist-credentials: false - run: rustup toolchain install stable --profile minimal --no-self-update diff --git a/.github/workflows/target-dir.yml b/.github/workflows/target-dir.yml index d2131db..cf2ef86 100644 --- a/.github/workflows/target-dir.yml +++ b/.github/workflows/target-dir.yml @@ -6,6 +6,8 @@ on: pull_request: branches: ["main"] +permissions: {} + jobs: target-dir: strategy: @@ -25,7 +27,9 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@v6 + with: + persist-credentials: false - run: rustup toolchain install stable --profile minimal --no-self-update diff --git a/.github/workflows/workspaces.yml b/.github/workflows/workspaces.yml index d47f0dd..9c62006 100644 --- a/.github/workflows/workspaces.yml +++ b/.github/workflows/workspaces.yml @@ -6,6 +6,8 @@ on: pull_request: branches: ["main"] +permissions: {} + jobs: workspaces: strategy: @@ -25,7 +27,9 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@v6 + with: + persist-credentials: false - run: rustup toolchain install stable --profile minimal --target wasm32-unknown-unknown --no-self-update From f0004dc7d3b36afa584b76ad5651d82ef460f3fe Mon Sep 17 00:00:00 2001 From: Raj-StepSecurity Date: Thu, 4 Dec 2025 10:12:07 +0530 Subject: [PATCH 8/8] comments addressed --- .github/workflows/multi-job-cache.yml | 2 +- package-lock.json | 5 +++-- package.json | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/multi-job-cache.yml b/.github/workflows/multi-job-cache.yml index 3482544..1ef7422 100644 --- a/.github/workflows/multi-job-cache.yml +++ b/.github/workflows/multi-job-cache.yml @@ -10,7 +10,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + os: ${{ github.actor == 'dependabot[bot]' && fromJSON('["ubuntu-latest"]') || fromJSON('["ubuntu-latest", "macos-latest", "windows-latest"]') }} name: Test multi-job cache (1) on ${{ matrix.os }} runs-on: ${{ matrix.os }} diff --git a/package-lock.json b/package-lock.json index 44d8be8..99e59d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rust-cache", - "version": "2.8.1", + "version": "2.8.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rust-cache", - "version": "2.8.1", + "version": "2.8.2", "license": "LGPL-3.0", "dependencies": { "@actions/buildjet-cache": "npm:github-actions.cache-buildjet@0.2.0", @@ -567,6 +567,7 @@ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.1.tgz", "integrity": "sha512-dKYCMuPO1bmrpuogcjQ8z7ICCH3FP6WmxpwC03yjzGfZhj9fTJg6+bS1+UAplekbN2C+M61UNllGOOoAfGCrdQ==", "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.1.0", diff --git a/package.json b/package.json index 5552a7c..919f616 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "rust-cache", - "version": "2.8.1", + "version": "2.8.2", "description": "A GitHub Action that implements smart caching for rust/cargo projects with sensible defaults.", "keywords": [ "actions",