mirror of
https://code.forgejo.org/actions/cache.git
synced 2025-04-02 04:57:46 +02:00
Add fail-on-cache-miss option
This commit is contained in:
parent
6fd2d4538c
commit
a5631aba37
11 changed files with 124 additions and 11 deletions
|
@ -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
|
||||
|
|
10
dist/restore-only/index.js
vendored
10
dist/restore-only/index.js
vendored
|
@ -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;
|
||||
}
|
||||
|
|
10
dist/restore/index.js
vendored
10
dist/restore/index.js
vendored
|
@ -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;
|
||||
}
|
||||
|
|
3
dist/save-only/index.js
vendored
3
dist/save-only/index.js
vendored
|
@ -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) {
|
||||
|
|
3
dist/save/index.js
vendored
3
dist/save/index.js
vendored
|
@ -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'
|
||||
|
@ -27,4 +31,4 @@ runs:
|
|||
main: '../dist/restore-only/index.js'
|
||||
branding:
|
||||
icon: 'archive'
|
||||
color: 'gray-dark'
|
||||
color: 'gray-dark'
|
||||
|
|
|
@ -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…
Add table
Reference in a new issue