3
0
Fork 0
mirror of https://github.com/Swatinem/rust-cache synced 2026-06-24 09:10:31 +00:00
This commit is contained in:
Gary Sassano 2026-06-16 22:22:30 +08:00 committed by GitHub
commit 7e9cf3c268
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 420 additions and 141 deletions

29
.github/workflows/target-key.yml vendored Normal file
View file

@ -0,0 +1,29 @@
name: target-key
on: [push, pull_request]
permissions: {}
jobs:
target-key:
if: github.repository == 'Swatinem/rust-cache'
runs-on: ubuntu-latest
env:
CARGO_TERM_COLOR: always
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- run: rustup toolchain install stable --profile minimal --no-self-update
- uses: ./
with:
workspaces: tests
cache-workspace-crates: "true"
target-key: ${{ hashFiles('tests/**/*.rs', 'tests/Cargo.toml', 'tests/Cargo.lock') }}
- run: cargo build --release
working-directory: tests

View file

@ -1,5 +1,9 @@
# Changelog
## Unreleased
- Add `target-key` for opt-in source-keyed workspace target caching.
## 2.9.1
- Fix regression in hash calculation

View file

@ -76,6 +76,14 @@ sensible defaults.
# default: "false"
cache-workspace-crates: ""
# An additional key for source-keyed target caching.
# When set together with `cache-targets` and `cache-workspace-crates`,
# workspace target directories are cached separately from CARGO_HOME using
# this key. This allows rebuilt workspace artifacts to be saved under a new
# source key while preserving the normal dependency cache behavior.
# default: empty
target-key: ""
# Determines whether the cache should be saved.
# If `false`, the cache is only restored.
# Useful for jobs where the matrix is additive e.g. additional Cargo features,
@ -173,6 +181,14 @@ to recreate it from the compressed crate archives in `~/.cargo/registry/cache`.
The action will try to restore from a previous `Cargo.lock` version as well, so
lockfile updates should only re-build changed dependencies.
When `target-key` is set together with `cache-targets` and
`cache-workspace-crates`, workspace target directories are restored after the
normal CARGO_HOME cache and saved as a separate source-keyed cache. This is useful
for workflows that deliberately cache workspace crates and can provide a stable
source fingerprint, for example `${{ hashFiles('src/**', 'Cargo.toml') }}`.
The target cache still restores from older target caches via restore prefixes,
but exact source matches can become Cargo no-ops across repeated runs.
The action invokes `cargo metadata` to determine the current set of dependencies.
Additionally, the action automatically works around

View file

@ -44,6 +44,9 @@ inputs:
description: "Similar to cache-all-crates. If `true` the workspace crates will be cached."
required: false
default: "false"
target-key:
description: "An additional key for source-keyed target caching. When set together with cache-targets and cache-workspace-crates, workspace target directories are cached separately using this key."
required: false
save-if:
description: "Determiners whether the cache should be saved. If `false`, the cache is only restored."
required: false

View file

@ -1,4 +1,4 @@
import { f as debug, m as getDefaultExportFromCjs, n as mkdirP, l as exec, w as which, o as warning, i as info, H as HttpCodes, p as HttpClientError, q as HttpClient, t as isDebug, u as setSecret, B as BearerCredentialHandler, e as error } from './cleanup-ChNUL7jL.js';
import { f as debug, m as getDefaultExportFromCjs, n as mkdirP, l as exec, w as which, o as warning, i as info, H as HttpCodes, p as HttpClientError, q as HttpClient, t as isDebug, u as setSecret, B as BearerCredentialHandler, e as error } from './cleanup-ctNqmXyy.js';
import * as path from 'path';
import * as fs from 'fs';
import { writeFileSync, existsSync } from 'fs';

View file

@ -1,4 +1,4 @@
import { v as commonjsGlobal, x as requireTunnel, m as getDefaultExportFromCjs, y as getAugmentedNamespace } from './cleanup-ChNUL7jL.js';
import { v as commonjsGlobal, x as requireTunnel, m as getDefaultExportFromCjs, y as getAugmentedNamespace } from './cleanup-ctNqmXyy.js';
import os__default from 'os';
import crypto__default from 'crypto';
import fs__default from 'fs';

View file

@ -34120,10 +34120,10 @@ async function getCacheProvider() {
let cache;
switch (cacheProvider) {
case "github":
cache = await import('./cache-Cb-Up9r2.js');
cache = await import('./cache-BSoAyDaq.js');
break;
case "warpbuild":
cache = await import('./cache-1jS6aShy.js').then(function (n) { return n.c; });
cache = await import('./cache-D5WyUDMY.js').then(function (n) { return n.c; });
break;
default:
throw new Error(`The \`cache-provider\` \`${cacheProvider}\` is not valid.`);
@ -34192,6 +34192,18 @@ class CacheConfig {
cacheKey = "";
/** The secondary (restore) key that only contains the prefix and environment */
restoreKey = "";
/** Whether the primary cache needs saving in the post action */
cacheNeedsSave = true;
/** Workspace target paths cached separately when `target-key` is used */
targetCachePaths = [];
/** The source-keyed workspace target cache key */
targetCacheKey = "";
/** The source-keyed workspace target restore keys */
targetRestoreKeys = [];
/** Whether workspace targets are cached separately from CARGO_HOME */
targetCacheEnabled = false;
/** Whether the workspace target cache needs saving in the post action */
targetCacheNeedsSave = false;
/** Whether to cache CARGO_HOME/.bin */
cacheBin = true;
/** The workspace configurations */
@ -34387,22 +34399,41 @@ class CacheConfig {
let lockHash = digest(hasher);
key += `-${lockHash}`;
}
self.cacheKey = key;
self.cachePaths = [path__default.join(CARGO_HOME, "registry"), path__default.join(CARGO_HOME, "git")];
const baseCacheKey = key;
const baseRestoreKey = self.restoreKey;
const cargoCachePaths = [path__default.join(CARGO_HOME, "registry"), path__default.join(CARGO_HOME, "git")];
if (self.cacheBin) {
self.cachePaths = [
path__default.join(CARGO_HOME, "bin"),
path__default.join(CARGO_HOME, ".crates.toml"),
path__default.join(CARGO_HOME, ".crates2.json"),
...self.cachePaths,
];
cargoCachePaths.unshift(path__default.join(CARGO_HOME, "bin"), path__default.join(CARGO_HOME, ".crates.toml"), path__default.join(CARGO_HOME, ".crates2.json"));
}
const cacheTargets = getInput("cache-targets").toLowerCase() || "true";
if (cacheTargets === "true") {
self.cachePaths.push(...workspaces.map((ws) => ws.target));
const targetCachePaths = cacheTargets === "true" ? workspaces.map((ws) => ws.target) : [];
const cacheDirectories = getInput("cache-directories").trim().split(/\s+/).filter(Boolean);
const targetKey = getInput("target-key");
const workspaceCrates = getInput("cache-workspace-crates").toLowerCase() || "false";
if (targetKey && cacheTargets !== "true") {
warning("`target-key` is ignored because `cache-targets` is not `true`.");
}
const cacheDirectories = getInput("cache-directories");
for (const dir of cacheDirectories.trim().split(/\s+/).filter(Boolean)) {
if (targetKey && workspaceCrates !== "true") {
warning("`target-key` is ignored because `cache-workspace-crates` is not `true`.");
}
self.targetCacheEnabled = Boolean(targetKey) && cacheTargets === "true" && workspaceCrates === "true";
if (self.targetCacheEnabled) {
self.cacheKey = baseCacheKey;
self.cachePaths = [...cargoCachePaths, ...cacheDirectories];
const targetKeyPrefix = `${baseRestoreKey}-target`;
const targetKeyEnvironment = baseCacheKey.slice(baseRestoreKey.length);
self.targetCachePaths = targetCachePaths;
self.targetCacheKey = `${targetKeyPrefix}${targetKeyEnvironment}-${targetKey}`;
self.targetRestoreKeys = uniqInOrder([`${targetKeyPrefix}${targetKeyEnvironment}-`, `${targetKeyPrefix}-`]);
}
else {
self.cacheKey = baseCacheKey;
self.cachePaths = [...cargoCachePaths];
}
if (!self.targetCacheEnabled && cacheTargets === "true") {
self.cachePaths.push(...targetCachePaths);
}
for (const dir of self.targetCacheEnabled ? [] : cacheDirectories) {
self.cachePaths.push(dir);
}
const bins = await getCargoBins();
@ -34438,14 +34469,26 @@ class CacheConfig {
for (const workspace of this.workspaces) {
info(` ${workspace.root}`);
}
info(`Cache Paths:`);
info(`${this.targetCacheEnabled ? "Cargo Cache" : "Cache"} Paths:`);
for (const path of this.cachePaths) {
info(` ${path}`);
}
info(`Restore Key:`);
info(`${this.targetCacheEnabled ? "Cargo Restore" : "Restore"} Key:`);
info(` ${this.restoreKey}`);
info(`Cache Key:`);
info(`${this.targetCacheEnabled ? "Cargo Cache" : "Cache"} Key:`);
info(` ${this.cacheKey}`);
if (this.targetCacheEnabled) {
info(`Target Cache Paths:`);
for (const path of this.targetCachePaths) {
info(` ${path}`);
}
info(`Target Restore Keys:`);
for (const key of this.targetRestoreKeys) {
info(` ${key}`);
}
info(`Target Cache Key:`);
info(` ${this.targetCacheKey}`);
}
info(`.. Prefix:`);
info(` - ${this.keyPrefix}`);
info(`.. Environment considered:`);
@ -34563,6 +34606,9 @@ function sort_and_uniq(a) {
return accumulator;
}, []);
}
function uniqInOrder(a) {
return a.filter((value, index) => a.indexOf(value) === index);
}
async function cleanTargetDir(targetDir, packages, checkTimestamp = false) {
debug(`cleaning target directory "${targetDir}"`);

77
dist/restore.js vendored
View file

@ -1,4 +1,4 @@
import { e as error, g as getCacheProvider, a as getInput, b as exportVariable, C as CacheConfig, i as info, c as cleanTargetDir, r as reportError, s as setOutput } from './cleanup-ChNUL7jL.js';
import { e as error, g as getCacheProvider, a as getInput, b as exportVariable, C as CacheConfig, i as info, r as reportError, s as setOutput, c as cleanTargetDir } from './cleanup-ctNqmXyy.js';
import 'os';
import 'crypto';
import 'fs';
@ -47,44 +47,43 @@ async function run() {
return;
}
try {
var cacheOnFailure = getInput("cache-on-failure").toLowerCase();
let cacheOnFailure = getInput("cache-on-failure").toLowerCase();
if (cacheOnFailure !== "true") {
cacheOnFailure = "false";
}
var lookupOnly = getInput("lookup-only").toLowerCase() === "true";
const lookupOnly = getInput("lookup-only").toLowerCase() === "true";
exportVariable("CACHE_ON_FAILURE", cacheOnFailure);
exportVariable("CARGO_INCREMENTAL", 0);
const config = await CacheConfig.new();
config.printInfo(cacheProvider);
info("");
info(`... ${lookupOnly ? "Checking" : "Restoring"} cache ...`);
const key = config.cacheKey;
// Pass a copy of cachePaths to avoid mutating the original array as reported by:
// https://github.com/actions/toolkit/pull/1378
// TODO: remove this once the underlying bug is fixed.
const restoreKey = await cacheProvider.cache.restoreCache(config.cachePaths.slice(), key, [config.restoreKey], {
lookupOnly,
});
if (restoreKey) {
const match = restoreKey.localeCompare(key, undefined, {
sensitivity: "accent",
}) === 0;
info(`${lookupOnly ? "Found" : "Restored from"} cache key "${restoreKey}" full match: ${match}.`);
if (!match) {
// pre-clean the target directory on cache mismatch
for (const workspace of config.workspaces) {
try {
await cleanTargetDir(workspace.target, [], true);
}
catch { }
}
// We restored the cache but it is not a full match.
const cacheResult = await restoreCache(cacheProvider, config.cachePaths, config.cacheKey, [config.restoreKey], lookupOnly);
config.cacheNeedsSave = !cacheResult.match;
if (config.targetCacheEnabled) {
if (cacheResult.found && !cacheResult.match) {
// pre-clean the target directory on cargo cache mismatch before restoring target cache
await cleanTargets(config);
}
const targetResult = await restoreCache(cacheProvider, config.targetCachePaths, config.targetCacheKey, config.targetRestoreKeys, lookupOnly, "target");
config.targetCacheNeedsSave = !targetResult.match;
if (targetResult.found && !targetResult.match) {
// pre-clean the target directory on target cache mismatch
await cleanTargets(config);
}
if (!cacheResult.match || !targetResult.match) {
config.saveState();
}
setCacheHitOutput(match);
setCacheHitOutput(cacheResult.match && targetResult.match);
}
else if (cacheResult.match) {
setCacheHitOutput(true);
}
else {
info("No cache found.");
if (cacheResult.found) {
// pre-clean the target directory on cache mismatch
await cleanTargets(config);
}
config.saveState();
setCacheHitOutput(false);
}
@ -98,4 +97,30 @@ async function run() {
function setCacheHitOutput(cacheHit) {
setOutput("cache-hit", cacheHit.toString());
}
async function restoreCache(cacheProvider, paths, key, restoreKeys, lookupOnly, name = "") {
const label = name ? `${name} cache` : "cache";
// Pass a copy of cachePaths to avoid mutating the original array as reported by:
// https://github.com/actions/toolkit/pull/1378
// TODO: remove this once the underlying bug is fixed.
const restoreKey = await cacheProvider.cache.restoreCache(paths.slice(), key, restoreKeys, {
lookupOnly,
});
if (!restoreKey) {
info(`No ${label} found.`);
return { found: false, match: false };
}
const match = restoreKey.localeCompare(key, undefined, {
sensitivity: "accent",
}) === 0;
info(`${lookupOnly ? "Found" : "Restored from"} ${label} key "${restoreKey}" full match: ${match}.`);
return { found: true, match };
}
async function cleanTargets(config) {
for (const workspace of config.workspaces) {
try {
await cleanTargetDir(workspace.target, [], true);
}
catch { }
}
}
run();

83
dist/save.js vendored
View file

@ -1,4 +1,4 @@
import { e as error, g as getCacheProvider, a as getInput, d as isCacheUpToDate, i as info, C as CacheConfig, c as cleanTargetDir, f as debug, h as cleanRegistry, j as cleanBin, k as cleanGit, r as reportError, l as exec } from './cleanup-ChNUL7jL.js';
import { e as error, g as getCacheProvider, a as getInput, d as isCacheUpToDate, i as info, C as CacheConfig, c as cleanTargetDir, f as debug, h as cleanRegistry, j as cleanBin, k as cleanGit, r as reportError, l as exec } from './cleanup-ctNqmXyy.js';
import 'os';
import 'crypto';
import 'fs';
@ -58,6 +58,8 @@ async function run() {
if (process.env["RUNNER_OS"] == "macOS") {
await macOsWorkaround();
}
const cleanCargo = !config.targetCacheEnabled || config.cacheNeedsSave;
const cleanTargets = !config.targetCacheEnabled || config.targetCacheNeedsSave;
const workspaceCrates = getInput("cache-workspace-crates").toLowerCase() || "false";
const allPackages = [];
for (const workspace of config.workspaces) {
@ -67,43 +69,62 @@ async function run() {
packages.push(...wsMembers);
}
allPackages.push(...packages);
if (cleanTargets) {
try {
info(`... Cleaning ${workspace.target} ...`);
await cleanTargetDir(workspace.target, packages);
}
catch (e) {
debug(`${e.stack}`);
}
}
}
if (cleanCargo) {
try {
info(`... Cleaning ${workspace.target} ...`);
await cleanTargetDir(workspace.target, packages);
const crates = getInput("cache-all-crates").toLowerCase() || "false";
info(`... Cleaning cargo registry (cache-all-crates: ${crates}) ...`);
await cleanRegistry(allPackages, crates !== "true");
}
catch (e) {
debug(`${e.stack}`);
}
if (config.cacheBin) {
try {
info(`... Cleaning cargo/bin ...`);
await cleanBin(config.cargoBins);
}
catch (e) {
debug(`${e.stack}`);
}
}
try {
info(`... Cleaning cargo git cache ...`);
await cleanGit(allPackages);
}
catch (e) {
debug(`${e.stack}`);
}
}
try {
const crates = getInput("cache-all-crates").toLowerCase() || "false";
info(`... Cleaning cargo registry (cache-all-crates: ${crates}) ...`);
await cleanRegistry(allPackages, crates !== "true");
}
catch (e) {
debug(`${e.stack}`);
}
if (config.cacheBin) {
try {
info(`... Cleaning cargo/bin ...`);
await cleanBin(config.cargoBins);
if (config.targetCacheEnabled) {
if (config.cacheNeedsSave) {
info(`... Saving cargo cache ...`);
await saveCache(cacheProvider, config.cachePaths, config.cacheKey);
}
catch (e) {
debug(`${e.stack}`);
else {
info(`Cargo cache up-to-date.`);
}
if (config.targetCacheNeedsSave) {
info(`... Saving target cache ...`);
await saveCache(cacheProvider, config.targetCachePaths, config.targetCacheKey);
}
else {
info(`Target cache up-to-date.`);
}
}
try {
info(`... Cleaning cargo git cache ...`);
await cleanGit(allPackages);
else {
info(`... Saving cache ...`);
await saveCache(cacheProvider, config.cachePaths, config.cacheKey);
}
catch (e) {
debug(`${e.stack}`);
}
info(`... Saving cache ...`);
// Pass a copy of cachePaths to avoid mutating the original array as reported by:
// https://github.com/actions/toolkit/pull/1378
// TODO: remove this once the underlying bug is fixed.
await cacheProvider.cache.saveCache(config.cachePaths.slice(), config.cacheKey);
}
catch (e) {
reportError(e);
@ -111,6 +132,12 @@ async function run() {
process.exit();
}
run();
async function saveCache(cacheProvider, paths, key) {
// Pass a copy of cachePaths to avoid mutating the original array as reported by:
// https://github.com/actions/toolkit/pull/1378
// TODO: remove this once the underlying bug is fixed.
await cacheProvider.cache.saveCache(paths.slice(), key);
}
async function macOsWorkaround() {
try {
// Workaround for https://github.com/actions/cache/issues/403

View file

@ -27,6 +27,20 @@ export class CacheConfig {
/** The secondary (restore) key that only contains the prefix and environment */
public restoreKey = "";
/** Whether the primary cache needs saving in the post action */
public cacheNeedsSave = true;
/** Workspace target paths cached separately when `target-key` is used */
public targetCachePaths: Array<string> = [];
/** The source-keyed workspace target cache key */
public targetCacheKey = "";
/** The source-keyed workspace target restore keys */
public targetRestoreKeys: Array<string> = [];
/** Whether workspace targets are cached separately from CARGO_HOME */
public targetCacheEnabled = false;
/** Whether the workspace target cache needs saving in the post action */
public targetCacheNeedsSave = false;
/** Whether to cache CARGO_HOME/.bin */
public cacheBin: boolean = true;
@ -266,24 +280,50 @@ export class CacheConfig {
key += `-${lockHash}`;
}
self.cacheKey = key;
const baseCacheKey = key;
const baseRestoreKey = self.restoreKey;
self.cachePaths = [path.join(CARGO_HOME, "registry"), path.join(CARGO_HOME, "git")];
const cargoCachePaths = [path.join(CARGO_HOME, "registry"), path.join(CARGO_HOME, "git")];
if (self.cacheBin) {
self.cachePaths = [
cargoCachePaths.unshift(
path.join(CARGO_HOME, "bin"),
path.join(CARGO_HOME, ".crates.toml"),
path.join(CARGO_HOME, ".crates2.json"),
...self.cachePaths,
];
);
}
const cacheTargets = core.getInput("cache-targets").toLowerCase() || "true";
if (cacheTargets === "true") {
self.cachePaths.push(...workspaces.map((ws) => ws.target));
const targetCachePaths = cacheTargets === "true" ? workspaces.map((ws) => ws.target) : [];
const cacheDirectories = core.getInput("cache-directories").trim().split(/\s+/).filter(Boolean);
const targetKey = core.getInput("target-key");
const workspaceCrates = core.getInput("cache-workspace-crates").toLowerCase() || "false";
if (targetKey && cacheTargets !== "true") {
core.warning("`target-key` is ignored because `cache-targets` is not `true`.");
}
if (targetKey && workspaceCrates !== "true") {
core.warning("`target-key` is ignored because `cache-workspace-crates` is not `true`.");
}
const cacheDirectories = core.getInput("cache-directories");
for (const dir of cacheDirectories.trim().split(/\s+/).filter(Boolean)) {
self.targetCacheEnabled = Boolean(targetKey) && cacheTargets === "true" && workspaceCrates === "true";
if (self.targetCacheEnabled) {
self.cacheKey = baseCacheKey;
self.cachePaths = [...cargoCachePaths, ...cacheDirectories];
const targetKeyPrefix = `${baseRestoreKey}-target`;
const targetKeyEnvironment = baseCacheKey.slice(baseRestoreKey.length);
self.targetCachePaths = targetCachePaths;
self.targetCacheKey = `${targetKeyPrefix}${targetKeyEnvironment}-${targetKey}`;
self.targetRestoreKeys = uniqInOrder([`${targetKeyPrefix}${targetKeyEnvironment}-`, `${targetKeyPrefix}-`]);
} else {
self.cacheKey = baseCacheKey;
self.cachePaths = [...cargoCachePaths];
}
if (!self.targetCacheEnabled && cacheTargets === "true") {
self.cachePaths.push(...targetCachePaths);
}
for (const dir of self.targetCacheEnabled ? [] : cacheDirectories) {
self.cachePaths.push(dir);
}
@ -325,14 +365,26 @@ export class CacheConfig {
for (const workspace of this.workspaces) {
core.info(` ${workspace.root}`);
}
core.info(`Cache Paths:`);
core.info(`${this.targetCacheEnabled ? "Cargo Cache" : "Cache"} Paths:`);
for (const path of this.cachePaths) {
core.info(` ${path}`);
}
core.info(`Restore Key:`);
core.info(`${this.targetCacheEnabled ? "Cargo Restore" : "Restore"} Key:`);
core.info(` ${this.restoreKey}`);
core.info(`Cache Key:`);
core.info(`${this.targetCacheEnabled ? "Cargo Cache" : "Cache"} Key:`);
core.info(` ${this.cacheKey}`);
if (this.targetCacheEnabled) {
core.info(`Target Cache Paths:`);
for (const path of this.targetCachePaths) {
core.info(` ${path}`);
}
core.info(`Target Restore Keys:`);
for (const key of this.targetRestoreKeys) {
core.info(` ${key}`);
}
core.info(`Target Cache Key:`);
core.info(` ${this.targetCacheKey}`);
}
core.info(`.. Prefix:`);
core.info(` - ${this.keyPrefix}`);
core.info(`.. Environment considered:`);
@ -465,3 +517,7 @@ function sort_and_uniq(a: string[]) {
return accumulator;
}, []);
}
function uniqInOrder(a: string[]) {
return a.filter((value, index) => a.indexOf(value) === index);
}

View file

@ -2,7 +2,7 @@ import * as core from "@actions/core";
import { cleanTargetDir } from "./cleanup";
import { CacheConfig } from "./config";
import { getCacheProvider, reportError } from "./utils";
import { CacheProvider, getCacheProvider, reportError } from "./utils";
process.on("uncaughtException", (e) => {
core.error(e.message);
@ -20,11 +20,11 @@ async function run() {
}
try {
var cacheOnFailure = core.getInput("cache-on-failure").toLowerCase();
let cacheOnFailure = core.getInput("cache-on-failure").toLowerCase();
if (cacheOnFailure !== "true") {
cacheOnFailure = "false";
}
var lookupOnly = core.getInput("lookup-only").toLowerCase() === "true";
const lookupOnly = core.getInput("lookup-only").toLowerCase() === "true";
core.exportVariable("CACHE_ON_FAILURE", cacheOnFailure);
core.exportVariable("CARGO_INCREMENTAL", 0);
@ -34,36 +34,42 @@ async function run() {
core.info("");
core.info(`... ${lookupOnly ? "Checking" : "Restoring"} cache ...`);
const key = config.cacheKey;
// Pass a copy of cachePaths to avoid mutating the original array as reported by:
// https://github.com/actions/toolkit/pull/1378
// TODO: remove this once the underlying bug is fixed.
const restoreKey = await cacheProvider.cache.restoreCache(config.cachePaths.slice(), key, [config.restoreKey], {
lookupOnly,
});
if (restoreKey) {
const match =
restoreKey.localeCompare(key, undefined, {
sensitivity: "accent",
}) === 0;
core.info(`${lookupOnly ? "Found" : "Restored from"} cache key "${restoreKey}" full match: ${match}.`);
if (!match) {
// pre-clean the target directory on cache mismatch
for (const workspace of config.workspaces) {
try {
await cleanTargetDir(workspace.target, [], true);
} catch {}
}
const cacheResult = await restoreCache(cacheProvider, config.cachePaths, config.cacheKey, [config.restoreKey], lookupOnly);
config.cacheNeedsSave = !cacheResult.match;
if (config.targetCacheEnabled) {
if (cacheResult.found && !cacheResult.match) {
// pre-clean the target directory on cargo cache mismatch before restoring target cache
await cleanTargets(config);
}
// We restored the cache but it is not a full match.
const targetResult = await restoreCache(
cacheProvider,
config.targetCachePaths,
config.targetCacheKey,
config.targetRestoreKeys,
lookupOnly,
"target",
);
config.targetCacheNeedsSave = !targetResult.match;
if (targetResult.found && !targetResult.match) {
// pre-clean the target directory on target cache mismatch
await cleanTargets(config);
}
if (!cacheResult.match || !targetResult.match) {
config.saveState();
}
setCacheHitOutput(match);
setCacheHitOutput(cacheResult.match && targetResult.match);
} else if (cacheResult.match) {
setCacheHitOutput(true);
} else {
core.info("No cache found.");
config.saveState();
if (cacheResult.found) {
// pre-clean the target directory on cache mismatch
await cleanTargets(config);
}
config.saveState();
setCacheHitOutput(false);
}
} catch (e) {
@ -78,4 +84,45 @@ function setCacheHitOutput(cacheHit: boolean): void {
core.setOutput("cache-hit", cacheHit.toString());
}
async function restoreCache(
cacheProvider: CacheProvider,
paths: string[],
key: string,
restoreKeys: string[],
lookupOnly: boolean,
name = "",
): Promise<RestoreResult> {
const label = name ? `${name} cache` : "cache";
// Pass a copy of cachePaths to avoid mutating the original array as reported by:
// https://github.com/actions/toolkit/pull/1378
// TODO: remove this once the underlying bug is fixed.
const restoreKey = await cacheProvider.cache.restoreCache(paths.slice(), key, restoreKeys, {
lookupOnly,
});
if (!restoreKey) {
core.info(`No ${label} found.`);
return { found: false, match: false };
}
const match =
restoreKey.localeCompare(key, undefined, {
sensitivity: "accent",
}) === 0;
core.info(`${lookupOnly ? "Found" : "Restored from"} ${label} key "${restoreKey}" full match: ${match}.`);
return { found: true, match };
}
interface RestoreResult {
found: boolean;
match: boolean;
}
async function cleanTargets(config: CacheConfig) {
for (const workspace of config.workspaces) {
try {
await cleanTargetDir(workspace.target, [], true);
} catch {}
}
}
run();

View file

@ -3,7 +3,7 @@ import * as exec from "@actions/exec";
import { cleanBin, cleanGit, cleanRegistry, cleanTargetDir } from "./cleanup";
import { CacheConfig, isCacheUpToDate } from "./config";
import { getCacheProvider, reportError } from "./utils";
import { CacheProvider, getCacheProvider, reportError } from "./utils";
process.on("uncaughtException", (e) => {
core.error(e.message);
@ -36,6 +36,8 @@ async function run() {
await macOsWorkaround();
}
const cleanCargo = !config.targetCacheEnabled || config.cacheNeedsSave;
const cleanTargets = !config.targetCacheEnabled || config.targetCacheNeedsSave;
const workspaceCrates = core.getInput("cache-workspace-crates").toLowerCase() || "false";
const allPackages = [];
for (const workspace of config.workspaces) {
@ -45,43 +47,60 @@ async function run() {
packages.push(...wsMembers);
}
allPackages.push(...packages);
if (cleanTargets) {
try {
core.info(`... Cleaning ${workspace.target} ...`);
await cleanTargetDir(workspace.target, packages);
} catch (e) {
core.debug(`${(e as any).stack}`);
}
}
}
if (cleanCargo) {
try {
core.info(`... Cleaning ${workspace.target} ...`);
await cleanTargetDir(workspace.target, packages);
const crates = core.getInput("cache-all-crates").toLowerCase() || "false";
core.info(`... Cleaning cargo registry (cache-all-crates: ${crates}) ...`);
await cleanRegistry(allPackages, crates !== "true");
} catch (e) {
core.debug(`${(e as any).stack}`);
}
if (config.cacheBin) {
try {
core.info(`... Cleaning cargo/bin ...`);
await cleanBin(config.cargoBins);
} catch (e) {
core.debug(`${(e as any).stack}`);
}
}
try {
core.info(`... Cleaning cargo git cache ...`);
await cleanGit(allPackages);
} catch (e) {
core.debug(`${(e as any).stack}`);
}
}
try {
const crates = core.getInput("cache-all-crates").toLowerCase() || "false";
core.info(`... Cleaning cargo registry (cache-all-crates: ${crates}) ...`);
await cleanRegistry(allPackages, crates !== "true");
} catch (e) {
core.debug(`${(e as any).stack}`);
}
if (config.cacheBin) {
try {
core.info(`... Cleaning cargo/bin ...`);
await cleanBin(config.cargoBins);
} catch (e) {
core.debug(`${(e as any).stack}`);
if (config.targetCacheEnabled) {
if (config.cacheNeedsSave) {
core.info(`... Saving cargo cache ...`);
await saveCache(cacheProvider, config.cachePaths, config.cacheKey);
} else {
core.info(`Cargo cache up-to-date.`);
}
}
try {
core.info(`... Cleaning cargo git cache ...`);
await cleanGit(allPackages);
} catch (e) {
core.debug(`${(e as any).stack}`);
if (config.targetCacheNeedsSave) {
core.info(`... Saving target cache ...`);
await saveCache(cacheProvider, config.targetCachePaths, config.targetCacheKey);
} else {
core.info(`Target cache up-to-date.`);
}
} else {
core.info(`... Saving cache ...`);
await saveCache(cacheProvider, config.cachePaths, config.cacheKey);
}
core.info(`... Saving cache ...`);
// Pass a copy of cachePaths to avoid mutating the original array as reported by:
// https://github.com/actions/toolkit/pull/1378
// TODO: remove this once the underlying bug is fixed.
await cacheProvider.cache.saveCache(config.cachePaths.slice(), config.cacheKey);
} catch (e) {
reportError(e);
}
@ -90,6 +109,13 @@ async function run() {
run();
async function saveCache(cacheProvider: CacheProvider, paths: string[], key: string) {
// Pass a copy of cachePaths to avoid mutating the original array as reported by:
// https://github.com/actions/toolkit/pull/1378
// TODO: remove this once the underlying bug is fixed.
await cacheProvider.cache.saveCache(paths.slice(), key);
}
async function macOsWorkaround() {
try {
// Workaround for https://github.com/actions/cache/issues/403