diff --git a/.github/workflows/publish-immutable-actions.yml b/.github/workflows/publish-immutable-actions.yml new file mode 100644 index 0000000..87c0207 --- /dev/null +++ b/.github/workflows/publish-immutable-actions.yml @@ -0,0 +1,20 @@ +name: 'Publish Immutable Action Version' + +on: + release: + types: [published] + +jobs: + publish: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + packages: write + + steps: + - name: Checking out + uses: actions/checkout@v4 + - name: Publish + id: publish + uses: actions/publish-immutable-action@0.0.3 diff --git a/README.md b/README.md index 9664651..eb4713d 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,6 @@ See ["Caching dependencies to speed up workflows"](https://docs.github.com/en/ac ### v4 * Updated to node 20 -* Added a `save-always` flag to save the cache even if a prior step fails ### v3 @@ -65,9 +64,9 @@ If you are using a `self-hosted` Windows runner, `GNU tar` and `zstd` are requir ### Outputs -* `cache-hit` - A boolean value to indicate an exact match was found for the key. - - > **Note** `cache-hit` will only be set to `true` when a cache hit occurs for the exact `key` match. For a partial key match via `restore-keys` or a cache miss, it will be set to `false`. +* `cache-hit` - A string value to indicate an exact match was found for the key. + * If there's a cache hit, this will be 'true' or 'false' to indicate if there's an exact match for `key`. + * If there's a cache miss, this will be an empty string. See [Skipping steps based on cache-hit](#skipping-steps-based-on-cache-hit) for info on using this output diff --git a/RELEASES.md b/RELEASES.md index e70823e..b490d02 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,5 +1,12 @@ # Releases +### 4.1.1 +- Restore original behavior of `cache-hit` output - [#1467](https://github.com/actions/cache/pull/1467) + +### 4.1.0 +- Ensure `cache-hit` output is set when a cache is missed - [#1404](https://github.com/actions/cache/pull/1404) +- Deprecate `save-always` input - [#1452](https://github.com/actions/cache/pull/1452) + ### 4.0.2 - Fixed restore `fail-on-cache-miss` not working. diff --git a/__tests__/restore.test.ts b/__tests__/restore.test.ts index 9b066eb..250f7ef 100644 --- a/__tests__/restore.test.ts +++ b/__tests__/restore.test.ts @@ -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(setCacheHitOutputMock).toHaveBeenCalledTimes(1); + expect(setCacheHitOutputMock).toHaveBeenCalledTimes(0); expect(failedMock).toHaveBeenCalledWith( `Failed to restore cache entry. Exiting as fail-on-cache-miss is set. Input key: ${key}` diff --git a/__tests__/restoreOnly.test.ts b/__tests__/restoreOnly.test.ts index 840307d..81e5bca 100644 --- a/__tests__/restoreOnly.test.ts +++ b/__tests__/restoreOnly.test.ts @@ -86,8 +86,7 @@ test("restore with no cache found", async () => { ); expect(outputMock).toHaveBeenCalledWith("cache-primary-key", key); - expect(outputMock).toHaveBeenCalledWith("cache-hit", "false"); - expect(outputMock).toHaveBeenCalledTimes(2); + expect(outputMock).toHaveBeenCalledTimes(1); expect(failedMock).toHaveBeenCalledTimes(0); expect(infoMock).toHaveBeenCalledWith( diff --git a/action.yml b/action.yml index 14f247f..7af7458 100644 --- a/action.yml +++ b/action.yml @@ -29,7 +29,11 @@ inputs: save-always: description: 'Run the post step to save the cache even if another step before fails' default: 'false' - required: false + required: false + deprecationMessage: | + save-always does not work as intended and will be removed in a future release. + A separate `actions/cache/restore` step should be used instead. + See https://github.com/actions/cache/tree/main/save#always-save-cache for more details. outputs: cache-hit: description: 'A boolean value to indicate an exact match was found for the primary key' @@ -37,7 +41,7 @@ runs: using: 'node20' main: 'dist/restore/index.js' post: 'dist/save/index.js' - post-if: "success() || github.event.inputs.save-always" + post-if: "success()" branding: icon: 'archive' color: 'gray-dark' diff --git a/caching-strategies.md b/caching-strategies.md index 48cb676..86ae37c 100644 --- a/caching-strategies.md +++ b/caching-strategies.md @@ -81,7 +81,7 @@ On similar lines, commit sha can be used to create a very specialized and short key: cache-${{ github.sha }} ``` -### Using multiple factors while forming a key depening on the need +### Using multiple factors while forming a key depending on the need Cache key can be formed by combination of more than one metadata, evaluated info. @@ -243,24 +243,7 @@ with: ### Saving cache even if the build fails -There can be cases where a cache should be saved even if the build job fails. For example, a job can fail due to flaky tests but the caches can still be re-used. You can use `actions/cache/save` action to save the cache by using `if: always()` condition. - -Similarly, `actions/cache/save` action can be conditionally used based on the output of the previous steps. This way you get more control on when to save the cache. - -```yaml -steps: - - uses: actions/checkout@v4 - . - . // restore if need be - . - - name: Build - run: /build.sh - - uses: actions/cache/save@v4 - if: always() // or any other condition to invoke the save action - with: - path: path/to/dependencies - key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} -``` +See [Always save cache](./save/README.md#always-save-cache). ### Saving cache once and reusing in multiple workflows diff --git a/dist/restore-only/index.js b/dist/restore-only/index.js index d2f3631..14e97b1 100644 --- a/dist/restore-only/index.js +++ b/dist/restore-only/index.js @@ -59415,7 +59415,8 @@ function restoreImpl(stateProvider, earlyExit) { const lookupOnly = utils.getInputAsBool(constants_1.Inputs.LookupOnly); const cacheKey = yield cache.restoreCache(cachePaths, primaryKey, restoreKeys, { lookupOnly: lookupOnly }, enableCrossOsArchive); if (!cacheKey) { - core.setOutput(constants_1.Outputs.CacheHit, false.toString()); + // `cache-hit` is intentionally not set to `false` here to preserve existing behavior + // See https://github.com/actions/cache/issues/1466 if (failOnCacheMiss) { throw new Error(`Failed to restore cache entry. Exiting as fail-on-cache-miss is set. Input key: ${primaryKey}`); } diff --git a/dist/restore/index.js b/dist/restore/index.js index 842eaa6..29b70b2 100644 --- a/dist/restore/index.js +++ b/dist/restore/index.js @@ -59415,7 +59415,8 @@ function restoreImpl(stateProvider, earlyExit) { const lookupOnly = utils.getInputAsBool(constants_1.Inputs.LookupOnly); const cacheKey = yield cache.restoreCache(cachePaths, primaryKey, restoreKeys, { lookupOnly: lookupOnly }, enableCrossOsArchive); if (!cacheKey) { - core.setOutput(constants_1.Outputs.CacheHit, false.toString()); + // `cache-hit` is intentionally not set to `false` here to preserve existing behavior + // See https://github.com/actions/cache/issues/1466 if (failOnCacheMiss) { throw new Error(`Failed to restore cache entry. Exiting as fail-on-cache-miss is set. Input key: ${primaryKey}`); } diff --git a/package-lock.json b/package-lock.json index a232507..fb36255 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cache", - "version": "4.0.2", + "version": "4.1.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "cache", - "version": "4.0.2", + "version": "4.1.1", "license": "MIT", "dependencies": { "@actions/cache": "^3.2.3", diff --git a/package.json b/package.json index 7468af5..ce050e5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cache", - "version": "4.0.2", + "version": "4.1.1", "private": true, "description": "Cache dependencies and build outputs", "main": "dist/restore/index.js", diff --git a/save/README.md b/save/README.md index ea64d71..e986626 100644 --- a/save/README.md +++ b/save/README.md @@ -70,19 +70,45 @@ with: ### Always save cache -There are instances where some flaky test cases would fail the entire workflow and users would get frustrated because the builds would run for hours and the cache couldn't be saved as the workflow failed in between. For such use-cases, users now have the ability to use the `actions/cache/save` action to save the cache by using an `if: always()` condition. This way the cache will always be saved if generated, or a warning will be generated that nothing is found on the cache path. Users can also use the `if` condition to only execute the `actions/cache/save` action depending on the output of previous steps. This way they get more control of when to save the cache. +There are instances where some flaky test cases would fail the entire workflow and users would get frustrated because the builds would run for hours and the cache couldn't be saved as the workflow failed in between. +For such use-cases, users now have the ability to use the `actions/cache/save` action to save the cache by using an [`always()`](https://docs.github.com/actions/writing-workflows/choosing-what-your-workflow-does/expressions#always) condition. +This way the cache will always be saved if generated, or a warning will be generated that nothing is found on the cache path. Users can also use the `if` condition to only execute the `actions/cache/save` action depending on the output of previous steps. This way they get more control of when to save the cache. + +To avoid saving a cache that already exists, the `cache-hit` output from a restore step should be checked. + +The `cache-primary-key` output from the restore step should also be used to ensure +the cache key does not change during the build if it's calculated based on file contents. ```yaml -steps: - - uses: actions/checkout@v4 - . - . // restore if need be - . - - name: Build - run: /build.sh - - uses: actions/cache/save@v4 - if: always() // or any other condition to invoke the save action - with: - path: path/to/dependencies - key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} +name: Always Caching Primes + +on: push + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Restore cached Primes + id: cache-primes-restore + uses: actions/cache/restore@v4 + with: + key: ${{ runner.os }}-primes + path: | + path/to/dependencies + some/other/dependencies + + # Intermediate workflow steps + + - name: Always Save Primes + id: cache-primes-save + if: always() && steps.cache-primes-restore.outputs.cache-hit != 'true' + uses: actions/cache/save@v4 + with: + key: ${{ steps.cache-primes-restore.outputs.cache-primary-key }} + path: | + path/to/dependencies + some/other/dependencies ``` diff --git a/src/restoreImpl.ts b/src/restoreImpl.ts index bb302ac..54bef49 100644 --- a/src/restoreImpl.ts +++ b/src/restoreImpl.ts @@ -51,7 +51,9 @@ export async function restoreImpl( ); if (!cacheKey) { - core.setOutput(Outputs.CacheHit, false.toString()); + // `cache-hit` is intentionally not set to `false` here to preserve existing behavior + // See https://github.com/actions/cache/issues/1466 + if (failOnCacheMiss) { throw new Error( `Failed to restore cache entry. Exiting as fail-on-cache-miss is set. Input key: ${primaryKey}`