mirror of
https://code.forgejo.org/actions/cache.git
synced 2024-05-20 16:21:24 +02:00
Compare commits
11 commits
cbe6d9c269
...
5fe1153101
Author | SHA1 | Date | |
---|---|---|---|
5fe1153101 | |||
34664da2c2 | |||
0c45773b62 | |||
8a55f839aa | |||
3884cace14 | |||
e29dad3e36 | |||
3e72a6ebe1 | |||
3e29e63694 | |||
2d00cd51fd | |||
727a4d2137 | |||
f908277314 |
|
@ -58,6 +58,7 @@ If you are using a `self-hosted` Windows runner, `GNU tar` and `zstd` are requir
|
|||
* `enableCrossOsArchive` - An optional boolean when enabled, allows Windows runners to save or restore caches that can be restored or saved respectively on other platforms. Default: `false`
|
||||
* `fail-on-cache-miss` - Fail the workflow if cache entry is not found. Default: `false`
|
||||
* `lookup-only` - If true, only checks if cache entry exists and skips download. Does not change save cache behavior. Default: `false`
|
||||
* `save-always` - If true, always saves the cache, even if the job fails. Requires a [step `id`](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsid). Default: `false`
|
||||
|
||||
#### Environment Variables
|
||||
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
# Releases
|
||||
|
||||
### 4.0.2
|
||||
|
||||
- Fixed restore `fail-on-cache-miss` not working.
|
||||
|
||||
### 4.0.1
|
||||
|
||||
- Updated `isGhes` check
|
||||
|
|
|
@ -173,8 +173,12 @@ test("restore with cache found for key", async () => {
|
|||
expect(stateMock).toHaveBeenCalledWith("CACHE_RESULT", key);
|
||||
expect(stateMock).toHaveBeenCalledTimes(2);
|
||||
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(2);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "true");
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith(
|
||||
"save-always-d18d746b9",
|
||||
"false"
|
||||
);
|
||||
|
||||
expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
|
@ -218,8 +222,12 @@ test("restore with cache found for restore key", async () => {
|
|||
expect(stateMock).toHaveBeenCalledWith("CACHE_RESULT", restoreKey);
|
||||
expect(stateMock).toHaveBeenCalledTimes(2);
|
||||
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(2);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "false");
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith(
|
||||
"save-always-d18d746b9",
|
||||
"false"
|
||||
);
|
||||
expect(infoMock).toHaveBeenCalledWith(
|
||||
`Cache restored from key: ${restoreKey}`
|
||||
);
|
||||
|
@ -260,7 +268,11 @@ test("Fail restore when fail on cache miss is enabled and primary + restore keys
|
|||
);
|
||||
|
||||
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(0);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith(
|
||||
"save-always-d18d746b9",
|
||||
"false"
|
||||
);
|
||||
|
||||
expect(failedMock).toHaveBeenCalledWith(
|
||||
`Failed to restore cache entry. Exiting as fail-on-cache-miss is set. Input key: ${key}`
|
||||
|
@ -306,8 +318,12 @@ test("restore when fail on cache miss is enabled and primary key doesn't match r
|
|||
expect(stateMock).toHaveBeenCalledWith("CACHE_RESULT", restoreKey);
|
||||
expect(stateMock).toHaveBeenCalledTimes(2);
|
||||
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(2);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "false");
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith(
|
||||
"save-always-d18d746b9",
|
||||
"false"
|
||||
);
|
||||
|
||||
expect(infoMock).toHaveBeenCalledWith(
|
||||
`Cache restored from key: ${restoreKey}`
|
||||
|
|
|
@ -79,8 +79,12 @@ test("restore without AC available should no-op", async () => {
|
|||
await restoreImpl(new StateProvider());
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(0);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(2);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "false");
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith(
|
||||
"save-always-d18d746b9",
|
||||
"false"
|
||||
);
|
||||
});
|
||||
|
||||
test("restore on GHES without AC available should no-op", async () => {
|
||||
|
@ -95,8 +99,12 @@ test("restore on GHES without AC available should no-op", async () => {
|
|||
await restoreImpl(new StateProvider());
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(0);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(2);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "false");
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith(
|
||||
"save-always-d18d746b9",
|
||||
"false"
|
||||
);
|
||||
});
|
||||
|
||||
test("restore on GHES with AC available ", async () => {
|
||||
|
@ -133,8 +141,12 @@ test("restore on GHES with AC available ", async () => {
|
|||
);
|
||||
|
||||
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(2);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "true");
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith(
|
||||
"save-always-d18d746b9",
|
||||
"false"
|
||||
);
|
||||
|
||||
expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
|
@ -355,8 +367,12 @@ test("restore with cache found for key", async () => {
|
|||
);
|
||||
|
||||
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(2);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "true");
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith(
|
||||
"save-always-d18d746b9",
|
||||
"false"
|
||||
);
|
||||
|
||||
expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
|
@ -397,8 +413,12 @@ test("restore with cache found for restore key", async () => {
|
|||
);
|
||||
|
||||
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(2);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "false");
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith(
|
||||
"save-always-d18d746b9",
|
||||
"false"
|
||||
);
|
||||
expect(infoMock).toHaveBeenCalledWith(
|
||||
`Cache restored from key: ${restoreKey}`
|
||||
);
|
||||
|
@ -441,11 +461,68 @@ test("restore with lookup-only set", async () => {
|
|||
expect(stateMock).toHaveBeenCalledWith("CACHE_RESULT", key);
|
||||
expect(stateMock).toHaveBeenCalledTimes(2);
|
||||
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(2);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "true");
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith(
|
||||
"save-always-d18d746b9",
|
||||
"false"
|
||||
);
|
||||
|
||||
expect(infoMock).toHaveBeenCalledWith(
|
||||
`Cache found and can be restored from key: ${key}`
|
||||
);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test("restore with save-always set", async () => {
|
||||
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => true);
|
||||
const path = "node_modules";
|
||||
const key = "node-test";
|
||||
testUtils.setInputs({
|
||||
path: path,
|
||||
key,
|
||||
saveAlways: true
|
||||
});
|
||||
|
||||
const setSaveAlwaysOutputMock = jest.spyOn(core, "setOutput");
|
||||
const restoreCacheMock = jest
|
||||
.spyOn(cache, "restoreCache")
|
||||
.mockImplementationOnce(() => {
|
||||
return Promise.resolve(undefined);
|
||||
});
|
||||
|
||||
await restoreImpl(new StateProvider());
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(restoreCacheMock).toHaveBeenCalledWith(
|
||||
[path],
|
||||
key,
|
||||
[],
|
||||
{
|
||||
lookupOnly: false
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
expect(setSaveAlwaysOutputMock).toHaveBeenCalledTimes(1);
|
||||
expect(setSaveAlwaysOutputMock).toHaveBeenCalledWith(
|
||||
"save-always-d18d746b9",
|
||||
"true"
|
||||
);
|
||||
});
|
||||
|
||||
test("restore failure with earlyExit should call process exit", async () => {
|
||||
testUtils.setInput(Inputs.Path, "node_modules");
|
||||
const failedMock = jest.spyOn(core, "setFailed");
|
||||
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
|
||||
const processExitMock = jest.spyOn(process, "exit").mockImplementation();
|
||||
|
||||
// call restoreImpl with `earlyExit` set to true
|
||||
await restoreImpl(new StateProvider(), true);
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(0);
|
||||
expect(failedMock).toHaveBeenCalledWith(
|
||||
"Input required and not supplied: key"
|
||||
);
|
||||
expect(processExitMock).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
|
|
@ -86,7 +86,8 @@ test("restore with no cache found", async () => {
|
|||
);
|
||||
|
||||
expect(outputMock).toHaveBeenCalledWith("cache-primary-key", key);
|
||||
expect(outputMock).toHaveBeenCalledTimes(1);
|
||||
expect(outputMock).toHaveBeenCalledWith("save-always-d18d746b9", "false");
|
||||
expect(outputMock).toHaveBeenCalledTimes(2);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
|
||||
expect(infoMock).toHaveBeenCalledWith(
|
||||
|
@ -169,8 +170,9 @@ test("restore with cache found for key", async () => {
|
|||
expect(outputMock).toHaveBeenCalledWith("cache-primary-key", key);
|
||||
expect(outputMock).toHaveBeenCalledWith("cache-hit", "true");
|
||||
expect(outputMock).toHaveBeenCalledWith("cache-matched-key", key);
|
||||
expect(outputMock).toHaveBeenCalledWith("save-always-d18d746b9", "false");
|
||||
|
||||
expect(outputMock).toHaveBeenCalledTimes(3);
|
||||
expect(outputMock).toHaveBeenCalledTimes(4);
|
||||
|
||||
expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
|
@ -212,8 +214,9 @@ test("restore with cache found for restore key", async () => {
|
|||
expect(outputMock).toHaveBeenCalledWith("cache-primary-key", key);
|
||||
expect(outputMock).toHaveBeenCalledWith("cache-hit", "false");
|
||||
expect(outputMock).toHaveBeenCalledWith("cache-matched-key", restoreKey);
|
||||
expect(outputMock).toHaveBeenCalledWith("save-always-d18d746b9", "false");
|
||||
|
||||
expect(outputMock).toHaveBeenCalledTimes(3);
|
||||
expect(outputMock).toHaveBeenCalledTimes(4);
|
||||
|
||||
expect(infoMock).toHaveBeenCalledWith(
|
||||
`Cache restored from key: ${restoreKey}`
|
||||
|
|
|
@ -33,11 +33,13 @@ inputs:
|
|||
outputs:
|
||||
cache-hit:
|
||||
description: 'A boolean value to indicate an exact match was found for the primary key'
|
||||
save-always-d18d746b9:
|
||||
description: "Run the post step to save the cache even if another step before fails"
|
||||
runs:
|
||||
using: 'node20'
|
||||
main: 'dist/restore/index.js'
|
||||
post: 'dist/save/index.js'
|
||||
post-if: "success() || github.event.inputs.save-always"
|
||||
post-if: "success() || (contains(steps.*.outputs.save-always-d18d746b9, 'true') && !contains(steps.*.outputs.save-always-d18d746b9, 'false'))"
|
||||
branding:
|
||||
icon: 'archive'
|
||||
color: 'gray-dark'
|
||||
|
|
15
dist/restore-only/index.js
vendored
15
dist/restore-only/index.js
vendored
|
@ -59392,7 +59392,7 @@ const core = __importStar(__nccwpck_require__(2186));
|
|||
const constants_1 = __nccwpck_require__(9042);
|
||||
const stateProvider_1 = __nccwpck_require__(1527);
|
||||
const utils = __importStar(__nccwpck_require__(6850));
|
||||
function restoreImpl(stateProvider) {
|
||||
function restoreImpl(stateProvider, earlyExit) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
try {
|
||||
if (!utils.isCacheFeatureAvailable()) {
|
||||
|
@ -59438,21 +59438,16 @@ function restoreImpl(stateProvider) {
|
|||
}
|
||||
catch (error) {
|
||||
core.setFailed(error.message);
|
||||
if (earlyExit) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.restoreImpl = restoreImpl;
|
||||
function run(stateProvider, earlyExit) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
try {
|
||||
yield restoreImpl(stateProvider);
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
if (earlyExit) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
yield restoreImpl(stateProvider, earlyExit);
|
||||
// node will stay alive if any promises are not resolved,
|
||||
// which is a possibility if HTTP requests are dangling
|
||||
// due to retries or timeouts. We know that if we got here
|
||||
|
|
15
dist/restore/index.js
vendored
15
dist/restore/index.js
vendored
|
@ -59392,7 +59392,7 @@ const core = __importStar(__nccwpck_require__(2186));
|
|||
const constants_1 = __nccwpck_require__(9042);
|
||||
const stateProvider_1 = __nccwpck_require__(1527);
|
||||
const utils = __importStar(__nccwpck_require__(6850));
|
||||
function restoreImpl(stateProvider) {
|
||||
function restoreImpl(stateProvider, earlyExit) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
try {
|
||||
if (!utils.isCacheFeatureAvailable()) {
|
||||
|
@ -59438,21 +59438,16 @@ function restoreImpl(stateProvider) {
|
|||
}
|
||||
catch (error) {
|
||||
core.setFailed(error.message);
|
||||
if (earlyExit) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.restoreImpl = restoreImpl;
|
||||
function run(stateProvider, earlyExit) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
try {
|
||||
yield restoreImpl(stateProvider);
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
if (earlyExit) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
yield restoreImpl(stateProvider, earlyExit);
|
||||
// node will stay alive if any promises are not resolved,
|
||||
// which is a possibility if HTTP requests are dangling
|
||||
// due to retries or timeouts. We know that if we got here
|
||||
|
|
4
package-lock.json
generated
4
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "cache",
|
||||
"version": "4.0.1",
|
||||
"version": "4.0.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "cache",
|
||||
"version": "4.0.1",
|
||||
"version": "4.0.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/cache": "^3.2.3",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "cache",
|
||||
"version": "4.0.1",
|
||||
"version": "4.0.2",
|
||||
"private": true,
|
||||
"description": "Cache dependencies and build outputs",
|
||||
"main": "dist/restore/index.js",
|
||||
|
|
|
@ -5,13 +5,15 @@ export enum Inputs {
|
|||
UploadChunkSize = "upload-chunk-size", // Input for cache, save action
|
||||
EnableCrossOsArchive = "enableCrossOsArchive", // Input for cache, restore, save action
|
||||
FailOnCacheMiss = "fail-on-cache-miss", // Input for cache, restore action
|
||||
LookupOnly = "lookup-only" // Input for cache, restore action
|
||||
LookupOnly = "lookup-only", // Input for cache, restore action
|
||||
SaveAlways = "save-always" // Input for cache action
|
||||
}
|
||||
|
||||
export enum Outputs {
|
||||
CacheHit = "cache-hit", // Output from cache, restore action
|
||||
CachePrimaryKey = "cache-primary-key", // Output from restore action
|
||||
CacheMatchedKey = "cache-matched-key" // Output from restore action
|
||||
CacheMatchedKey = "cache-matched-key", // Output from restore action
|
||||
SaveAlways = "save-always-d18d746b9" // Output from cache action, with unique suffix for detection in post-if
|
||||
}
|
||||
|
||||
export enum State {
|
||||
|
|
|
@ -10,8 +10,14 @@ import {
|
|||
import * as utils from "./utils/actionUtils";
|
||||
|
||||
export async function restoreImpl(
|
||||
stateProvider: IStateProvider
|
||||
stateProvider: IStateProvider,
|
||||
earlyExit?: boolean | undefined
|
||||
): Promise<string | undefined> {
|
||||
core.setOutput(
|
||||
Outputs.SaveAlways,
|
||||
core.getInput(Inputs.SaveAlways) || "false"
|
||||
);
|
||||
|
||||
try {
|
||||
if (!utils.isCacheFeatureAvailable()) {
|
||||
core.setOutput(Outputs.CacheHit, "false");
|
||||
|
@ -83,6 +89,9 @@ export async function restoreImpl(
|
|||
return cacheKey;
|
||||
} catch (error: unknown) {
|
||||
core.setFailed((error as Error).message);
|
||||
if (earlyExit) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,14 +99,7 @@ async function run(
|
|||
stateProvider: IStateProvider,
|
||||
earlyExit: boolean | undefined
|
||||
): Promise<void> {
|
||||
try {
|
||||
await restoreImpl(stateProvider);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
if (earlyExit) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
await restoreImpl(stateProvider, earlyExit);
|
||||
|
||||
// node will stay alive if any promises are not resolved,
|
||||
// which is a possibility if HTTP requests are dangling
|
||||
|
|
|
@ -16,6 +16,7 @@ interface CacheInput {
|
|||
enableCrossOsArchive?: boolean;
|
||||
failOnCacheMiss?: boolean;
|
||||
lookupOnly?: boolean;
|
||||
saveAlways?: boolean;
|
||||
}
|
||||
|
||||
export function setInputs(input: CacheInput): void {
|
||||
|
@ -32,6 +33,8 @@ export function setInputs(input: CacheInput): void {
|
|||
setInput(Inputs.FailOnCacheMiss, input.failOnCacheMiss.toString());
|
||||
input.lookupOnly !== undefined &&
|
||||
setInput(Inputs.LookupOnly, input.lookupOnly.toString());
|
||||
input.saveAlways !== undefined &&
|
||||
setInput(Inputs.SaveAlways, input.saveAlways.toString());
|
||||
}
|
||||
|
||||
export function clearInputs(): void {
|
||||
|
@ -42,4 +45,5 @@ export function clearInputs(): void {
|
|||
delete process.env[getInputName(Inputs.EnableCrossOsArchive)];
|
||||
delete process.env[getInputName(Inputs.FailOnCacheMiss)];
|
||||
delete process.env[getInputName(Inputs.LookupOnly)];
|
||||
delete process.env[getInputName(Inputs.SaveAlways)];
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue