Compare commits

...

10 Commits

Author SHA1 Message Date
PrinsFrank
0d93be3424 Also add "force-overwrite" option to save-only action 2024-08-02 21:12:44 +02:00
PrinsFrank
b70ee8ec79 Merge branch 'main' into add-force-overwrite-for-cache 2024-08-02 18:43:15 +02:00
r4mimu
40c3b67b29 Fix cache-hit output when cache missed (#1404)
* fix: cache-hit output

* fix: Output chache hit timing

* fix: Output chache hit timing

---------

Co-authored-by: Josh Gross <joshmgross@github.com>
2024-07-31 16:56:48 +00:00
Oleg A.
e47d9f9ec8 Explicit use bash for Windows (#1377)
Co-authored-by: Josh Gross <joshmgross@github.com>
2024-07-31 16:52:21 +00:00
P. Ottlinger
4a28cbc054 Update README.md and use v4 of checkout action (#1437)
Update examples to use latest available checkout action v4.
2024-07-29 16:07:30 -04:00
Bethany
0c45773b62 Merge pull request #1327 from cdce8p/fix-fail-on-cache-miss
Fix `fail-on-cache-miss` not working
2024-03-19 09:31:49 -04:00
Marc Mueller
8a55f839aa Add test case for process exit
Co-authored-by: Bethany <bethanyj28@users.noreply.github.com>
2024-03-19 09:28:12 +01:00
Marc Mueller
3884cace14 Bump version 2024-03-01 07:28:18 +01:00
Marc Mueller
e29dad3e36 Fix fail-on-cache-miss not working 2024-03-01 07:28:18 +01:00
Frank Prins
7dd9af18b0 Add force-overwrite for cache 2024-01-22 18:54:37 +01:00
15 changed files with 76 additions and 44 deletions

View File

@@ -14,6 +14,10 @@ See ["Caching dependencies to speed up workflows"](https://docs.github.com/en/ac
## What's New ## What's New
### Unreleased
* Added the `force-overwrite` flag to force overwrite any existing cache for incremental caching
### v4 ### v4
* Updated to node 20 * Updated to node 20
@@ -91,7 +95,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Cache Primes - name: Cache Primes
id: cache-primes id: cache-primes
@@ -122,7 +126,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Restore cached Primes - name: Restore cached Primes
id: cache-primes-restore id: cache-primes-restore
@@ -229,7 +233,7 @@ Example:
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: actions/cache@v4 - uses: actions/cache@v4
id: cache id: cache
@@ -259,7 +263,7 @@ jobs:
build-linux: build-linux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Cache Primes - name: Cache Primes
id: cache-primes id: cache-primes
@@ -286,7 +290,7 @@ jobs:
build-windows: build-windows:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Cache Primes - name: Cache Primes
id: cache-primes id: cache-primes

View File

@@ -1,5 +1,9 @@
# Releases # Releases
### 4.0.2
- Fixed restore `fail-on-cache-miss` not working.
### 4.0.1 ### 4.0.1
- Updated `isGhes` check - Updated `isGhes` check

View File

@@ -260,7 +260,7 @@ test("Fail restore when fail on cache miss is enabled and primary + restore keys
); );
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key); expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(0); expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
expect(failedMock).toHaveBeenCalledWith( expect(failedMock).toHaveBeenCalledWith(
`Failed to restore cache entry. Exiting as fail-on-cache-miss is set. Input key: ${key}` `Failed to restore cache entry. Exiting as fail-on-cache-miss is set. Input key: ${key}`

View File

@@ -449,3 +449,19 @@ test("restore with lookup-only set", async () => {
); );
expect(failedMock).toHaveBeenCalledTimes(0); expect(failedMock).toHaveBeenCalledTimes(0);
}); });
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);
});

View File

@@ -86,7 +86,8 @@ test("restore with no cache found", async () => {
); );
expect(outputMock).toHaveBeenCalledWith("cache-primary-key", key); expect(outputMock).toHaveBeenCalledWith("cache-primary-key", key);
expect(outputMock).toHaveBeenCalledTimes(1); expect(outputMock).toHaveBeenCalledWith("cache-hit", "false");
expect(outputMock).toHaveBeenCalledTimes(2);
expect(failedMock).toHaveBeenCalledTimes(0); expect(failedMock).toHaveBeenCalledTimes(0);
expect(infoMock).toHaveBeenCalledWith( expect(infoMock).toHaveBeenCalledWith(

View File

@@ -29,7 +29,11 @@ inputs:
save-always: save-always:
description: 'Run the post step to save the cache even if another step before fails' description: 'Run the post step to save the cache even if another step before fails'
default: 'false' default: 'false'
required: false required: false
force-overwrite:
description: 'Delete any previous (incremental) cache before pushing a new cache even if a cache for the primary cache key exists'
default: 'false'
required: false
outputs: outputs:
cache-hit: cache-hit:
description: 'A boolean value to indicate an exact match was found for the primary key' description: 'A boolean value to indicate an exact match was found for the primary key'

View File

@@ -59392,7 +59392,7 @@ const core = __importStar(__nccwpck_require__(2186));
const constants_1 = __nccwpck_require__(9042); const constants_1 = __nccwpck_require__(9042);
const stateProvider_1 = __nccwpck_require__(1527); const stateProvider_1 = __nccwpck_require__(1527);
const utils = __importStar(__nccwpck_require__(6850)); const utils = __importStar(__nccwpck_require__(6850));
function restoreImpl(stateProvider) { function restoreImpl(stateProvider, earlyExit) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
try { try {
if (!utils.isCacheFeatureAvailable()) { if (!utils.isCacheFeatureAvailable()) {
@@ -59415,6 +59415,7 @@ function restoreImpl(stateProvider) {
const lookupOnly = utils.getInputAsBool(constants_1.Inputs.LookupOnly); const lookupOnly = utils.getInputAsBool(constants_1.Inputs.LookupOnly);
const cacheKey = yield cache.restoreCache(cachePaths, primaryKey, restoreKeys, { lookupOnly: lookupOnly }, enableCrossOsArchive); const cacheKey = yield cache.restoreCache(cachePaths, primaryKey, restoreKeys, { lookupOnly: lookupOnly }, enableCrossOsArchive);
if (!cacheKey) { if (!cacheKey) {
core.setOutput(constants_1.Outputs.CacheHit, false.toString());
if (failOnCacheMiss) { if (failOnCacheMiss) {
throw new Error(`Failed to restore cache entry. Exiting as fail-on-cache-miss is set. Input key: ${primaryKey}`); throw new Error(`Failed to restore cache entry. Exiting as fail-on-cache-miss is set. Input key: ${primaryKey}`);
} }
@@ -59438,21 +59439,16 @@ function restoreImpl(stateProvider) {
} }
catch (error) { catch (error) {
core.setFailed(error.message); core.setFailed(error.message);
if (earlyExit) {
process.exit(1);
}
} }
}); });
} }
exports.restoreImpl = restoreImpl; exports.restoreImpl = restoreImpl;
function run(stateProvider, earlyExit) { function run(stateProvider, earlyExit) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
try { yield restoreImpl(stateProvider, earlyExit);
yield restoreImpl(stateProvider);
}
catch (err) {
console.error(err);
if (earlyExit) {
process.exit(1);
}
}
// node will stay alive if any promises are not resolved, // node will stay alive if any promises are not resolved,
// which is a possibility if HTTP requests are dangling // which is a possibility if HTTP requests are dangling
// due to retries or timeouts. We know that if we got here // due to retries or timeouts. We know that if we got here

16
dist/restore/index.js vendored
View File

@@ -59392,7 +59392,7 @@ const core = __importStar(__nccwpck_require__(2186));
const constants_1 = __nccwpck_require__(9042); const constants_1 = __nccwpck_require__(9042);
const stateProvider_1 = __nccwpck_require__(1527); const stateProvider_1 = __nccwpck_require__(1527);
const utils = __importStar(__nccwpck_require__(6850)); const utils = __importStar(__nccwpck_require__(6850));
function restoreImpl(stateProvider) { function restoreImpl(stateProvider, earlyExit) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
try { try {
if (!utils.isCacheFeatureAvailable()) { if (!utils.isCacheFeatureAvailable()) {
@@ -59415,6 +59415,7 @@ function restoreImpl(stateProvider) {
const lookupOnly = utils.getInputAsBool(constants_1.Inputs.LookupOnly); const lookupOnly = utils.getInputAsBool(constants_1.Inputs.LookupOnly);
const cacheKey = yield cache.restoreCache(cachePaths, primaryKey, restoreKeys, { lookupOnly: lookupOnly }, enableCrossOsArchive); const cacheKey = yield cache.restoreCache(cachePaths, primaryKey, restoreKeys, { lookupOnly: lookupOnly }, enableCrossOsArchive);
if (!cacheKey) { if (!cacheKey) {
core.setOutput(constants_1.Outputs.CacheHit, false.toString());
if (failOnCacheMiss) { if (failOnCacheMiss) {
throw new Error(`Failed to restore cache entry. Exiting as fail-on-cache-miss is set. Input key: ${primaryKey}`); throw new Error(`Failed to restore cache entry. Exiting as fail-on-cache-miss is set. Input key: ${primaryKey}`);
} }
@@ -59438,21 +59439,16 @@ function restoreImpl(stateProvider) {
} }
catch (error) { catch (error) {
core.setFailed(error.message); core.setFailed(error.message);
if (earlyExit) {
process.exit(1);
}
} }
}); });
} }
exports.restoreImpl = restoreImpl; exports.restoreImpl = restoreImpl;
function run(stateProvider, earlyExit) { function run(stateProvider, earlyExit) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
try { yield restoreImpl(stateProvider, earlyExit);
yield restoreImpl(stateProvider);
}
catch (err) {
console.error(err);
if (earlyExit) {
process.exit(1);
}
}
// node will stay alive if any promises are not resolved, // node will stay alive if any promises are not resolved,
// which is a possibility if HTTP requests are dangling // which is a possibility if HTTP requests are dangling
// due to retries or timeouts. We know that if we got here // due to retries or timeouts. We know that if we got here

View File

@@ -513,6 +513,7 @@ jobs:
```yaml ```yaml
- name: Get pip cache dir - name: Get pip cache dir
id: pip-cache id: pip-cache
shell: bash
run: | run: |
echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "cache", "name": "cache",
"version": "4.0.1", "version": "4.0.2",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "cache", "name": "cache",
"version": "4.0.1", "version": "4.0.2",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/cache": "^3.2.3", "@actions/cache": "^3.2.3",

View File

@@ -1,6 +1,6 @@
{ {
"name": "cache", "name": "cache",
"version": "4.0.1", "version": "4.0.2",
"private": true, "private": true,
"description": "Cache dependencies and build outputs", "description": "Cache dependencies and build outputs",
"main": "dist/restore/index.js", "main": "dist/restore/index.js",

View File

@@ -15,6 +15,10 @@ inputs:
description: 'An optional boolean when enabled, allows windows runners to save caches that can be restored on other platforms' description: 'An optional boolean when enabled, allows windows runners to save caches that can be restored on other platforms'
default: 'false' default: 'false'
required: false required: false
force-overwrite:
description: 'Delete any previous (incremental) cache before pushing a new cache even if a cache for the primary cache key exists'
default: 'false'
required: false
runs: runs:
using: 'node20' using: 'node20'
main: '../dist/save-only/index.js' main: '../dist/save-only/index.js'

View File

@@ -5,7 +5,8 @@ export enum Inputs {
UploadChunkSize = "upload-chunk-size", // Input for cache, save 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 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
ForceOverwrite = "force-overwrite" // Input for cache action
} }
export enum Outputs { export enum Outputs {

View File

@@ -10,7 +10,8 @@ import {
import * as utils from "./utils/actionUtils"; import * as utils from "./utils/actionUtils";
export async function restoreImpl( export async function restoreImpl(
stateProvider: IStateProvider stateProvider: IStateProvider,
earlyExit?: boolean | undefined
): Promise<string | undefined> { ): Promise<string | undefined> {
try { try {
if (!utils.isCacheFeatureAvailable()) { if (!utils.isCacheFeatureAvailable()) {
@@ -50,6 +51,7 @@ export async function restoreImpl(
); );
if (!cacheKey) { if (!cacheKey) {
core.setOutput(Outputs.CacheHit, false.toString());
if (failOnCacheMiss) { if (failOnCacheMiss) {
throw new Error( throw new Error(
`Failed to restore cache entry. Exiting as fail-on-cache-miss is set. Input key: ${primaryKey}` `Failed to restore cache entry. Exiting as fail-on-cache-miss is set. Input key: ${primaryKey}`
@@ -61,7 +63,6 @@ export async function restoreImpl(
...restoreKeys ...restoreKeys
].join(", ")}` ].join(", ")}`
); );
return; return;
} }
@@ -83,6 +84,9 @@ export async function restoreImpl(
return cacheKey; return cacheKey;
} catch (error: unknown) { } catch (error: unknown) {
core.setFailed((error as Error).message); core.setFailed((error as Error).message);
if (earlyExit) {
process.exit(1);
}
} }
} }
@@ -90,14 +94,7 @@ async function run(
stateProvider: IStateProvider, stateProvider: IStateProvider,
earlyExit: boolean | undefined earlyExit: boolean | undefined
): Promise<void> { ): Promise<void> {
try { await restoreImpl(stateProvider, earlyExit);
await restoreImpl(stateProvider);
} catch (err) {
console.error(err);
if (earlyExit) {
process.exit(1);
}
}
// node will stay alive if any promises are not resolved, // node will stay alive if any promises are not resolved,
// which is a possibility if HTTP requests are dangling // which is a possibility if HTTP requests are dangling

View File

@@ -47,13 +47,21 @@ export async function saveImpl(
// NO-OP in case of SaveOnly action // NO-OP in case of SaveOnly action
const restoredKey = stateProvider.getCacheState(); const restoredKey = stateProvider.getCacheState();
if (utils.isExactKeyMatch(primaryKey, restoredKey)) { const forceOverwrite = utils.getInputAsBool(Inputs.ForceOverwrite);
if (utils.isExactKeyMatch(primaryKey, restoredKey) && !forceOverwrite) {
core.info( core.info(
`Cache hit occurred on the primary key ${primaryKey}, not saving cache.` `Cache hit occurred on the primary key "${primaryKey}" and force-overwrite is disabled, not saving cache.`
); );
return; return;
} }
if ((restoredKey == undefined || utils.isExactKeyMatch(primaryKey, restoredKey)) && forceOverwrite) {
core.info(
`Cache hit occurred on the primary key "${primaryKey}" or running as save-only and force-overwrite is enabled, deleting cache.`
);
await cache.deleteCache(primaryKey)
}
const cachePaths = utils.getInputAsArray(Inputs.Path, { const cachePaths = utils.getInputAsArray(Inputs.Path, {
required: true required: true
}); });