Add fail-on-cache-miss option

pull/1036/head
Marc Mueller 3 years ago
parent 6fd2d4538c
commit a5631aba37

@ -205,3 +205,72 @@ test("restore with cache found for restore key", async () => {
);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("Fail restore when fail on cache miss is enabled and primary key not found", async () => {
const path = "node_modules";
const key = "node-test";
const restoreKey = "node-";
testUtils.setInputs({
path: path,
key,
restoreKeys: [restoreKey],
failOnCacheMiss: true
});
const failedMock = jest.spyOn(core, "setFailed");
const stateMock = jest.spyOn(core, "saveState");
const setCacheHitOutputMock = jest.spyOn(core, "setOutput");
const restoreCacheMock = jest
.spyOn(cache, "restoreCache")
.mockImplementationOnce(() => {
return Promise.resolve(undefined);
});
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, [restoreKey]);
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(0);
expect(failedMock).toHaveBeenCalledWith(
`Failed to restore cache entry. Exiting as fail-on-cache-miss is set. Input key: ${key}`
);
expect(failedMock).toHaveBeenCalledTimes(1);
});
test("Fail restore when fail on cache miss is enabled and primary key doesn't match restored key", async () => {
const path = "node_modules";
const key = "node-test";
const restoreKey = "node-";
testUtils.setInputs({
path: path,
key,
restoreKeys: [restoreKey],
failOnCacheMiss: true
});
const failedMock = jest.spyOn(core, "setFailed");
const stateMock = jest.spyOn(core, "saveState");
const setCacheHitOutputMock = jest.spyOn(core, "setOutput");
const restoreCacheMock = jest
.spyOn(cache, "restoreCache")
.mockImplementationOnce(() => {
return Promise.resolve(restoreKey);
});
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, [restoreKey]);
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "false");
expect(failedMock).toHaveBeenCalledWith(
`Restored cache key doesn't match the given input key. Exiting as fail-on-cache-miss is set. Input key: ${key}`
);
expect(failedMock).toHaveBeenCalledTimes(1);
});

@ -11,6 +11,10 @@ inputs:
restore-keys:
description: 'An ordered list of keys to use for restoring stale cache if no cache hit occurred for key. Note `cache-hit` returns false in this case.'
required: false
fail-on-cache-miss:
description: 'Fail the workflow if the cache is not found for the primary key'
required: false
default: "false"
upload-chunk-size:
description: 'The chunk size used to split up large files during upload, in bytes'
required: false

@ -4978,7 +4978,8 @@ var Inputs;
Inputs["Path"] = "path";
Inputs["RestoreKeys"] = "restore-keys";
Inputs["UploadChunkSize"] = "upload-chunk-size";
Inputs["EnableCrossOsArchive"] = "enableCrossOsArchive"; // Input for cache, restore, save action
Inputs["EnableCrossOsArchive"] = "enableCrossOsArchive";
Inputs["FailOnCacheMiss"] = "fail-on-cache-miss"; // Input for cache, restore action
})(Inputs = exports.Inputs || (exports.Inputs = {}));
var Outputs;
(function (Outputs) {
@ -50497,6 +50498,9 @@ function restoreImpl(stateProvider) {
const enableCrossOsArchive = utils.getInputAsBool(constants_1.Inputs.EnableCrossOsArchive);
const cacheKey = yield cache.restoreCache(cachePaths, primaryKey, restoreKeys, {}, enableCrossOsArchive);
if (!cacheKey) {
if (core.getBooleanInput(constants_1.Inputs.FailOnCacheMiss) == true) {
throw new Error(`Failed to restore cache entry. Exiting as fail-on-cache-miss is set. Input key: ${primaryKey}`);
}
core.info(`Cache not found for input keys: ${[
primaryKey,
...restoreKeys
@ -50507,6 +50511,10 @@ function restoreImpl(stateProvider) {
stateProvider.setState(constants_1.State.CacheMatchedKey, cacheKey);
const isExactKeyMatch = utils.isExactKeyMatch(core.getInput(constants_1.Inputs.Key, { required: true }), cacheKey);
core.setOutput(constants_1.Outputs.CacheHit, isExactKeyMatch.toString());
if (!isExactKeyMatch &&
core.getBooleanInput(constants_1.Inputs.FailOnCacheMiss) == true) {
throw new Error(`Restored cache key doesn't match the given input key. Exiting as fail-on-cache-miss is set. Input key: ${primaryKey}`);
}
core.info(`Cache restored from key: ${cacheKey}`);
return cacheKey;
}

@ -4978,7 +4978,8 @@ var Inputs;
Inputs["Path"] = "path";
Inputs["RestoreKeys"] = "restore-keys";
Inputs["UploadChunkSize"] = "upload-chunk-size";
Inputs["EnableCrossOsArchive"] = "enableCrossOsArchive"; // Input for cache, restore, save action
Inputs["EnableCrossOsArchive"] = "enableCrossOsArchive";
Inputs["FailOnCacheMiss"] = "fail-on-cache-miss"; // Input for cache, restore action
})(Inputs = exports.Inputs || (exports.Inputs = {}));
var Outputs;
(function (Outputs) {
@ -50497,6 +50498,9 @@ function restoreImpl(stateProvider) {
const enableCrossOsArchive = utils.getInputAsBool(constants_1.Inputs.EnableCrossOsArchive);
const cacheKey = yield cache.restoreCache(cachePaths, primaryKey, restoreKeys, {}, enableCrossOsArchive);
if (!cacheKey) {
if (core.getBooleanInput(constants_1.Inputs.FailOnCacheMiss) == true) {
throw new Error(`Failed to restore cache entry. Exiting as fail-on-cache-miss is set. Input key: ${primaryKey}`);
}
core.info(`Cache not found for input keys: ${[
primaryKey,
...restoreKeys
@ -50507,6 +50511,10 @@ function restoreImpl(stateProvider) {
stateProvider.setState(constants_1.State.CacheMatchedKey, cacheKey);
const isExactKeyMatch = utils.isExactKeyMatch(core.getInput(constants_1.Inputs.Key, { required: true }), cacheKey);
core.setOutput(constants_1.Outputs.CacheHit, isExactKeyMatch.toString());
if (!isExactKeyMatch &&
core.getBooleanInput(constants_1.Inputs.FailOnCacheMiss) == true) {
throw new Error(`Restored cache key doesn't match the given input key. Exiting as fail-on-cache-miss is set. Input key: ${primaryKey}`);
}
core.info(`Cache restored from key: ${cacheKey}`);
return cacheKey;
}

@ -5034,7 +5034,8 @@ var Inputs;
Inputs["Path"] = "path";
Inputs["RestoreKeys"] = "restore-keys";
Inputs["UploadChunkSize"] = "upload-chunk-size";
Inputs["EnableCrossOsArchive"] = "enableCrossOsArchive"; // Input for cache, restore, save action
Inputs["EnableCrossOsArchive"] = "enableCrossOsArchive";
Inputs["FailOnCacheMiss"] = "fail-on-cache-miss"; // Input for cache, restore action
})(Inputs = exports.Inputs || (exports.Inputs = {}));
var Outputs;
(function (Outputs) {

@ -4978,7 +4978,8 @@ var Inputs;
Inputs["Path"] = "path";
Inputs["RestoreKeys"] = "restore-keys";
Inputs["UploadChunkSize"] = "upload-chunk-size";
Inputs["EnableCrossOsArchive"] = "enableCrossOsArchive"; // Input for cache, restore, save action
Inputs["EnableCrossOsArchive"] = "enableCrossOsArchive";
Inputs["FailOnCacheMiss"] = "fail-on-cache-miss"; // Input for cache, restore action
})(Inputs = exports.Inputs || (exports.Inputs = {}));
var Outputs;
(function (Outputs) {

@ -7,6 +7,7 @@ The restore action, as the name suggest, restores a cache. It acts similar to th
* `path` - A list of files, directories, and wildcard patterns to cache and restore. See [`@actions/glob`](https://github.com/actions/toolkit/tree/main/packages/glob) for supported patterns.
* `key` - String used while saving cache for restoring the cache
* `restore-keys` - An ordered list of prefix-matched keys to use for restoring stale cache if no cache hit occurred for key.
* `fail-on-cache-miss` - Fail the workflow if the cache is not found for the primary key
## Outputs
@ -95,7 +96,7 @@ steps:
### Exit workflow on cache miss
You can use the output of this action to exit the workflow on cache miss. This way you can restrict your workflow to only initiate the build when `cache-hit` occurs, in other words, cache with exact key is found.
You can use `fail-on-cache-miss: true` to exit the workflow on a cache miss. This way you can restrict your workflow to only initiate the build when a cache with the exact key is found.
```yaml
steps:
@ -106,10 +107,7 @@ steps:
with:
path: path/to/dependencies
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
- name: Check cache hit
if: steps.cache.outputs.cache-hit != 'true'
run: exit 1
fail-on-cache-miss: true
- name: Build
run: /build.sh

@ -15,6 +15,10 @@ inputs:
description: 'An optional boolean when enabled, allows windows runners to restore caches that were saved on other platforms'
default: 'false'
required: false
fail-on-cache-miss:
description: 'Fail the workflow if the cache is not found for the primary key'
required: false
default: "false"
outputs:
cache-hit:
description: 'A boolean value to indicate an exact match was found for the primary key'

@ -3,7 +3,8 @@ export enum Inputs {
Path = "path", // Input for cache, restore, save action
RestoreKeys = "restore-keys", // Input for cache, restore action
UploadChunkSize = "upload-chunk-size", // Input for cache, save action
EnableCrossOsArchive = "enableCrossOsArchive" // Input for cache, restore, save action
EnableCrossOsArchive = "enableCrossOsArchive", // Input for cache, restore, save action
FailOnCacheMiss = "fail-on-cache-miss" // Input for cache, restore action
}
export enum Outputs {

@ -44,6 +44,11 @@ async function restoreImpl(
);
if (!cacheKey) {
if (core.getBooleanInput(Inputs.FailOnCacheMiss) == true) {
throw new Error(
`Failed to restore cache entry. Exiting as fail-on-cache-miss is set. Input key: ${primaryKey}`
);
}
core.info(
`Cache not found for input keys: ${[
primaryKey,
@ -63,6 +68,15 @@ async function restoreImpl(
);
core.setOutput(Outputs.CacheHit, isExactKeyMatch.toString());
if (
!isExactKeyMatch &&
core.getBooleanInput(Inputs.FailOnCacheMiss) == true
) {
throw new Error(
`Restored cache key doesn't match the given input key. Exiting as fail-on-cache-miss is set. Input key: ${primaryKey}`
);
}
core.info(`Cache restored from key: ${cacheKey}`);
return cacheKey;

@ -14,11 +14,13 @@ interface CacheInput {
key: string;
restoreKeys?: string[];
enableCrossOsArchive?: boolean;
failOnCacheMiss?: boolean;
}
export function setInputs(input: CacheInput): void {
setInput(Inputs.Path, input.path);
setInput(Inputs.Key, input.key);
setInput(Inputs.FailOnCacheMiss, "false");
input.restoreKeys &&
setInput(Inputs.RestoreKeys, input.restoreKeys.join("\n"));
input.enableCrossOsArchive !== undefined &&
@ -26,12 +28,15 @@ export function setInputs(input: CacheInput): void {
Inputs.EnableCrossOsArchive,
input.enableCrossOsArchive.toString()
);
input.failOnCacheMiss &&
setInput(Inputs.FailOnCacheMiss, String(input.failOnCacheMiss));
}
export function clearInputs(): void {
delete process.env[getInputName(Inputs.Path)];
delete process.env[getInputName(Inputs.Key)];
delete process.env[getInputName(Inputs.RestoreKeys)];
delete process.env[getInputName(Inputs.FailOnCacheMiss)];
delete process.env[getInputName(Inputs.UploadChunkSize)];
delete process.env[getInputName(Inputs.EnableCrossOsArchive)];
}

Loading…
Cancel
Save