Compare commits

..

No commits in common. "main" and "v3.3.0" have entirely different histories.
main ... v3.3.0

89 changed files with 121159 additions and 138395 deletions

View File

@ -1,22 +0,0 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
groups:
minor-actions-dependencies:
update-types: [minor, patch]
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
allow:
- dependency-type: direct
- dependency-type: production

View File

@ -15,5 +15,3 @@ jobs:
call-check-dist: call-check-dist:
name: Check dist/ name: Check dist/
uses: actions/reusable-workflows/.github/workflows/check-dist.yml@main uses: actions/reusable-workflows/.github/workflows/check-dist.yml@main
with:
node-version: "20.x"

View File

@ -10,7 +10,7 @@ jobs:
issues: write issues: write
pull-requests: write pull-requests: write
steps: steps:
- uses: actions/stale@v9 - uses: actions/stale@v3
with: with:
days-before-issue-stale: 200 days-before-issue-stale: 200
days-before-issue-close: 5 days-before-issue-close: 5

View File

@ -17,11 +17,11 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v3 uses: github/codeql-action/init@v2
# Override language selection by uncommenting this and choosing your languages # Override language selection by uncommenting this and choosing your languages
# with: # with:
# languages: go, javascript, csharp, python, cpp, java, ruby # languages: go, javascript, csharp, python, cpp, java, ruby
@ -29,7 +29,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
# If this step fails, then you should remove it and run the build manually (see below). # If this step fails, then you should remove it and run the build manually (see below).
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@v3 uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell. # Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@ -43,4 +43,4 @@ jobs:
# make release # make release
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3 uses: github/codeql-action/analyze@v2

View File

@ -14,3 +14,9 @@ jobs:
- name: add_assignees - name: add_assignees
run: | run: |
curl -X POST -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN}}" https://api.github.com/repos/${{github.repository}}/issues/${{ github.event.issue.number}}/assignees -d '{"assignees":["${{steps.oncall.outputs.CURRENT}}"]}' curl -X POST -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN}}" https://api.github.com/repos/${{github.repository}}/issues/${{ github.event.issue.number}}/assignees -d '{"assignees":["${{steps.oncall.outputs.CURRENT}}"]}'
- uses: actions/add-to-project@v0.4.0
name: Add to Project Board
with:
project-url: https://github.com/orgs/actions/projects/12
github-token: ${{ secrets.CACHE_BOARD_TOKEN }}

View File

@ -18,3 +18,9 @@ jobs:
- name: Add Assignee - name: Add Assignee
run: | run: |
curl -X POST -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN}}" https://api.github.com/repos/${{github.repository}}/issues/${{ github.event.pull_request.number}}/assignees -d '{"assignees":["${{steps.oncall.outputs.CURRENT}}"]}' curl -X POST -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN}}" https://api.github.com/repos/${{github.repository}}/issues/${{ github.event.pull_request.number}}/assignees -d '{"assignees":["${{steps.oncall.outputs.CURRENT}}"]}'
- uses: actions/add-to-project@v0.4.0
name: Add to Project Board
with:
project-url: https://github.com/orgs/actions/projects/12
github-token: ${{ secrets.CACHE_BOARD_TOKEN }}

View File

@ -1,20 +0,0 @@
name: 'Publish Immutable Action Version'
on:
release:
types: [released]
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

View File

@ -1,28 +0,0 @@
name: Release new action version
on:
release:
types: [released]
workflow_dispatch:
inputs:
TAG_NAME:
description: 'Tag name that the major tag will point to'
required: true
env:
TAG_NAME: ${{ github.event.inputs.TAG_NAME || github.event.release.tag_name }}
permissions:
contents: write
jobs:
update_tag:
name: Update the major tag to include the ${{ github.event.inputs.TAG_NAME || github.event.release.tag_name }} changes
environment:
name: releaseNewActionVersion
runs-on: ubuntu-latest
steps:
- name: Update the ${{ env.TAG_NAME }} tag
id: update-major-tag
uses: actions/publish-action@v0.3.0
with:
source-tag: ${{ env.TAG_NAME }}
slack-webhook: ${{ secrets.SLACK_WEBHOOK }}

View File

@ -20,11 +20,11 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Setup Node.js 20.x - name: Setup Node.js 16.x
uses: actions/setup-node@v4 uses: actions/setup-node@v3
with: with:
node-version: 20.x node-version: 16.x
cache: npm cache: npm
- run: npm ci - run: npm ci
- name: Prettier Format Check - name: Prettier Format Check
@ -43,7 +43,7 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Generate files in working directory - name: Generate files in working directory
shell: bash shell: bash
run: __tests__/create-cache-files.sh ${{ runner.os }} test-cache run: __tests__/create-cache-files.sh ${{ runner.os }} test-cache
@ -66,7 +66,7 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Restore cache - name: Restore cache
uses: ./ uses: ./
with: with:
@ -96,7 +96,7 @@ jobs:
https_proxy: http://squid-proxy:3128 https_proxy: http://squid-proxy:3128
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Generate files - name: Generate files
run: __tests__/create-cache-files.sh proxy test-cache run: __tests__/create-cache-files.sh proxy test-cache
- name: Save cache - name: Save cache
@ -119,7 +119,7 @@ jobs:
https_proxy: http://squid-proxy:3128 https_proxy: http://squid-proxy:3128
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Restore cache - name: Restore cache
uses: ./ uses: ./
with: with:

View File

@ -13,10 +13,4 @@ allowed:
reviewed: reviewed:
npm: npm:
- sax - sax
- "@protobuf-ts/plugin-framework" # Apache-2.0
- "@protobuf-ts/runtime" # Apache-2.0
- fs.realpath # ISC
- glob # ISC
- prettier # MIT
- lodash # MIT

BIN
.licenses/NOTICE generated

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
.licenses/npm/psl.dep.yml generated Normal file

Binary file not shown.

Binary file not shown.

BIN
.licenses/npm/tough-cookie-3.0.1.dep.yml generated Normal file

Binary file not shown.

BIN
.licenses/npm/tough-cookie-4.0.0.dep.yml generated Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
.licenses/npm/universalify.dep.yml generated Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -2,10 +2,11 @@
This action allows caching dependencies and build outputs to improve workflow execution time. This action allows caching dependencies and build outputs to improve workflow execution time.
>Two other actions are available in addition to the primary `cache` action: Two other actions are available in addition to the primary `cache` action:
>
>* [Restore action](./restore/README.md) * [Restore action](./restore/README.md)
>* [Save action](./save/README.md)
* [Save action](./save/README.md)
[![Tests](https://github.com/actions/cache/actions/workflows/workflow.yml/badge.svg)](https://github.com/actions/cache/actions/workflows/workflow.yml) [![Tests](https://github.com/actions/cache/actions/workflows/workflow.yml/badge.svg)](https://github.com/actions/cache/actions/workflows/workflow.yml)
@ -15,30 +16,8 @@ See ["Caching dependencies to speed up workflows"](https://docs.github.com/en/ac
## What's New ## What's New
### ⚠️ Important changes
The cache backend service has been rewritten from the ground up for improved performance and reliability. [actions/cache](https://github.com/actions/cache) now integrates with the new cache service (v2) APIs.
The new service will gradually roll out as of **February 1st, 2025**. The legacy service will also be sunset on the same date. Changes in these release are **fully backward compatible**.
**We are deprecating some versions of this action**. We recommend upgrading to version `v4` or `v3` as soon as possible before **February 1st, 2025.** (Upgrade instructions below).
If you are using pinned SHAs, please use the SHAs of versions `v4.2.0` or `v3.4.0`
If you do not upgrade, all workflow runs using any of the deprecated [actions/cache](https://github.com/actions/cache) will fail.
Upgrading to the recommended versions will not break your workflows.
Read more about the change & access the migration guide: [reference to the announcement](https://github.com/actions/cache/discussions/1510).
### v4
* Integrated with the new cache service (v2) APIs.
* Updated to node 20
### v3 ### v3
* Integrated with the new cache service (v2) APIs.
* Added support for caching in GHES 3.5+. * Added support for caching in GHES 3.5+.
* Fixed download issue for files > 2GB during restore. * Fixed download issue for files > 2GB during restore.
* Updated the minimum runner version support from node 12 -> node 16. * Updated the minimum runner version support from node 12 -> node 16.
@ -48,13 +27,12 @@ Read more about the change & access the migration guide: [reference to the annou
* Fixed cache not working with github workspace directory or current directory. * Fixed cache not working with github workspace directory or current directory.
* Fixed the download stuck problem by introducing a timeout of 1 hour for cache downloads. * Fixed the download stuck problem by introducing a timeout of 1 hour for cache downloads.
* Fix zstd not working for windows on gnu tar in issues. * Fix zstd not working for windows on gnu tar in issues.
* Allowing users to provide a custom timeout as input for aborting download of a cache segment using an environment variable `SEGMENT_DOWNLOAD_TIMEOUT_MINS`. Default is 10 minutes. * Allowing users to provide a custom timeout as input for aborting download of a cache segment using an environment variable `SEGMENT_DOWNLOAD_TIMEOUT_MINS`. Default is 60 minutes.
* New actions are available for granular control over caches - [restore](restore/action.yml) and [save](save/action.yml). * New actions are available for granular control over caches - [restore](restore/action.yml) and [save](save/action.yml).
* Support cross-os caching as an opt-in feature. See [Cross OS caching](./tips-and-workarounds.md#cross-os-cache) for more info. * Support cross-os caching as an opt-in feature. See [Cross OS caching](./tips-and-workarounds.md#cross-os-cache) for more info.
* Added option to fail job on cache miss. See [Exit workflow on cache miss](./restore/README.md#exit-workflow-on-cache-miss) for more info. * Added option to fail job on cache miss. See [Exit workflow on cache miss](./restore/README.md#exit-workflow-on-cache-miss) for more info.
* Fix zstd not being used after zstd version upgrade to 1.5.4 on hosted runners * Fix zstd not being used after zstd version upgrade to 1.5.4 on hosted runners
* Added option to lookup cache without downloading it. * Added option to lookup cache without downloading it.
* Reduced segment size to 128MB and segment timeout to 10 minutes to fail fast in case the cache download is stuck.
See the [v2 README.md](https://github.com/actions/cache/blob/v2/README.md) for older updates. See the [v2 README.md](https://github.com/actions/cache/blob/v2/README.md) for older updates.
@ -72,20 +50,20 @@ If you are using a `self-hosted` Windows runner, `GNU tar` and `zstd` are requir
* `key` - An explicit key for a cache entry. See [creating a cache key](#creating-a-cache-key). * `key` - An explicit key for a cache entry. See [creating a cache key](#creating-a-cache-key).
* `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. * `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.
* `restore-keys` - An ordered multiline string listing the prefix-matched keys, that are used for restoring stale cache if no cache hit occurred for key. * `restore-keys` - An ordered list of prefix-matched keys to use for restoring stale cache if no cache hit occurred for key.
* `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` * `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` * `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` * `lookup-only` - Skip downloading cache. Only check if cache entry exists. Default: `false`
#### Environment Variables #### Environment Variables
* `SEGMENT_DOWNLOAD_TIMEOUT_MINS` - Segment download timeout (in minutes, default `10`) to abort download of the segment if not completed in the defined number of minutes. [Read more](https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout) * `SEGMENT_DOWNLOAD_TIMEOUT_MINS` - Segment download timeout (in minutes, default `60`) to abort download of the segment if not completed in the defined number of minutes. [Read more](https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout)
### Outputs ### Outputs
* `cache-hit` - A string value to indicate an exact match was found for the key. * `cache-hit` - A boolean 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. > **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`.
See [Skipping steps based on cache-hit](#skipping-steps-based-on-cache-hit) for info on using this output See [Skipping steps based on cache-hit](#skipping-steps-based-on-cache-hit) for info on using this output
@ -109,11 +87,11 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- name: Cache Primes - name: Cache Primes
id: cache-primes id: cache-primes
uses: actions/cache@v4 uses: actions/cache@v3
with: with:
path: prime-numbers path: prime-numbers
key: ${{ runner.os }}-primes key: ${{ runner.os }}-primes
@ -140,11 +118,11 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- name: Restore cached Primes - name: Restore cached Primes
id: cache-primes-restore id: cache-primes-restore
uses: actions/cache/restore@v4 uses: actions/cache/restore@v3
with: with:
path: | path: |
path/to/dependencies path/to/dependencies
@ -155,7 +133,7 @@ jobs:
. .
- name: Save Primes - name: Save Primes
id: cache-primes-save id: cache-primes-save
uses: actions/cache/save@v4 uses: actions/cache/save@v3
with: with:
path: | path: |
path/to/dependencies path/to/dependencies
@ -176,7 +154,6 @@ Every programming language and framework has its own way of caching.
See [Examples](examples.md) for a list of `actions/cache` implementations for use with: See [Examples](examples.md) for a list of `actions/cache` implementations for use with:
* [Bun](./examples.md#bun)
* [C# - NuGet](./examples.md#c---nuget) * [C# - NuGet](./examples.md#c---nuget)
* [Clojure - Lein Deps](./examples.md#clojure---lein-deps) * [Clojure - Lein Deps](./examples.md#clojure---lein-deps)
* [D - DUB](./examples.md#d---dub) * [D - DUB](./examples.md#d---dub)
@ -210,7 +187,7 @@ A cache key can include any of the contexts, functions, literals, and operators
For example, using the [`hashFiles`](https://docs.github.com/en/actions/learn-github-actions/expressions#hashfiles) function allows you to create a new cache when dependencies change. For example, using the [`hashFiles`](https://docs.github.com/en/actions/learn-github-actions/expressions#hashfiles) function allows you to create a new cache when dependencies change.
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: | path: |
path/to/dependencies path/to/dependencies
@ -228,7 +205,7 @@ Additionally, you can use arbitrary command output in a cache key, such as a dat
echo "date=$(/bin/date -u "+%Y%m%d")" >> $GITHUB_OUTPUT echo "date=$(/bin/date -u "+%Y%m%d")" >> $GITHUB_OUTPUT
shell: bash shell: bash
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: path/to/dependencies path: path/to/dependencies
key: ${{ runner.os }}-${{ steps.get-date.outputs.date }}-${{ hashFiles('**/lockfiles') }} key: ${{ runner.os }}-${{ steps.get-date.outputs.date }}-${{ hashFiles('**/lockfiles') }}
@ -248,9 +225,9 @@ Example:
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- uses: actions/cache@v4 - uses: actions/cache@v3
id: cache id: cache
with: with:
path: path/to/dependencies path: path/to/dependencies
@ -278,11 +255,11 @@ jobs:
build-linux: build-linux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- name: Cache Primes - name: Cache Primes
id: cache-primes id: cache-primes
uses: actions/cache@v4 uses: actions/cache@v3
with: with:
path: prime-numbers path: prime-numbers
key: primes key: primes
@ -293,7 +270,7 @@ jobs:
- name: Cache Numbers - name: Cache Numbers
id: cache-numbers id: cache-numbers
uses: actions/cache@v4 uses: actions/cache@v3
with: with:
path: numbers path: numbers
key: primes key: primes
@ -305,11 +282,11 @@ jobs:
build-windows: build-windows:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- name: Cache Primes - name: Cache Primes
id: cache-primes id: cache-primes
uses: actions/cache@v4 uses: actions/cache@v3
with: with:
path: prime-numbers path: prime-numbers
key: primes key: primes

View File

@ -1,174 +1,81 @@
# Releases # Releases
### 4.2.3 ### 3.0.0
- Updated minimum runner version support from node 12 -> node 16
- Bump `@actions/cache` to v4.0.3 (obfuscates SAS token in debug logs for cache entries) ### 3.0.1
- Added support for caching from GHES 3.5.
- Fixed download issue for files > 2GB during restore.
### 4.2.2 ### 3.0.2
- Added support for dynamic cache size cap on GHES.
- Bump `@actions/cache` to v4.0.2 ### 3.0.3
- Fixed avoiding empty cache save when no files are available for caching. ([issue](https://github.com/actions/cache/issues/624))
### 4.2.1 ### 3.0.4
- Fixed tar creation error while trying to create tar with path as `~/` home folder on `ubuntu-latest`. ([issue](https://github.com/actions/cache/issues/689))
- Bump `@actions/cache` to v4.0.1 ### 3.0.5
- Removed error handling by consuming actions/cache 3.0 toolkit, Now cache server error handling will be done by toolkit. ([PR](https://github.com/actions/cache/pull/834))
### 4.2.0 ### 3.0.6
- Fixed [#809](https://github.com/actions/cache/issues/809) - zstd -d: no such file or directory error
- Fixed [#833](https://github.com/actions/cache/issues/833) - cache doesn't work with github workspace directory
TLDR; The cache backend service has been rewritten from the ground up for improved performance and reliability. [actions/cache](https://github.com/actions/cache) now integrates with the new cache service (v2) APIs. ### 3.0.7
- Fixed [#810](https://github.com/actions/cache/issues/810) - download stuck issue. A new timeout is introduced in the download process to abort the download if it gets stuck and doesn't finish within an hour.
The new service will gradually roll out as of **February 1st, 2025**. The legacy service will also be sunset on the same date. Changes in these release are **fully backward compatible**. ### 3.0.8
- Fix zstd not working for windows on gnu tar in issues [#888](https://github.com/actions/cache/issues/888) and [#891](https://github.com/actions/cache/issues/891).
- Allowing users to provide a custom timeout as input for aborting download of a cache segment using an environment variable `SEGMENT_DOWNLOAD_TIMEOUT_MINS`. Default is 60 minutes.
**We are deprecating some versions of this action**. We recommend upgrading to version `v4` or `v3` as soon as possible before **February 1st, 2025.** (Upgrade instructions below). ### 3.0.9
- Enhanced the warning message for cache unavailablity in case of GHES.
If you are using pinned SHAs, please use the SHAs of versions `v4.2.0` or `v3.4.0` ### 3.0.10
- Fix a bug with sorting inputs.
- Update definition for restore-keys in README.md
If you do not upgrade, all workflow runs using any of the deprecated [actions/cache](https://github.com/actions/cache) will fail. ### 3.0.11
- Update toolkit version to 3.0.5 to include `@actions/core@^1.10.0`
- Update `@actions/cache` to use updated `saveState` and `setOutput` functions from `@actions/core@^1.10.0`
Upgrading to the recommended versions will not break your workflows. ### 3.1.0-beta.1
- Update `@actions/cache` on windows to use gnu tar and zstd by default and fallback to bsdtar and zstd if gnu tar is not available. ([issue](https://github.com/actions/cache/issues/984))
### 4.1.2 ### 3.1.0-beta.2
- Added support for fallback to gzip to restore old caches on windows.
- Add GitHub Enterprise Cloud instances hostname filters to inform API endpoint choices - [#1474](https://github.com/actions/cache/pull/1474) ### 3.1.0-beta.3
- Security fix: Bump braces from 3.0.2 to 3.0.3 - [#1475](https://github.com/actions/cache/pull/1475) - Bug fixes for bsdtar fallback if gnutar not available and gzip fallback if cache saved using old cache action on windows.
### 4.1.1 ### 3.2.0-beta.1
- Added two new actions - [restore](restore/action.yml) and [save](save/action.yml) for granular control on cache.
- Restore original behavior of `cache-hit` output - [#1467](https://github.com/actions/cache/pull/1467) ### 3.2.0
- Released the two new actions - [restore](restore/action.yml) and [save](save/action.yml) for granular control on cache
### 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.
### 4.0.1
- Updated `isGhes` check
### 4.0.0
- Updated minimum runner version support from node 12 -> node 20
### 3.4.0
- Integrated with the new cache service (v2) APIs
### 3.3.3
- Updates @actions/cache to v3.2.3 to fix accidental mutated path arguments to `getCacheVersion` [actions/toolkit#1378](https://github.com/actions/toolkit/pull/1378)
- Additional audit fixes of npm package(s)
### 3.3.2
- Fixes bug with Azure SDK causing blob downloads to get stuck.
### 3.3.1
- Reduced segment size to 128MB and segment timeout to 10 minutes to fail fast in case the cache download is stuck.
### 3.3.0
- Added option to lookup cache without downloading it.
### 3.2.6
- Fix zstd not being used after zstd version upgrade to 1.5.4 on hosted runners.
### 3.2.5
- Added fix to prevent from setting MYSYS environment variable globally.
### 3.2.4
- Added option to fail job on cache miss.
### 3.2.3
- Support cross os caching on Windows as an opt-in feature.
- Fix issue with symlink restoration on Windows for cross-os caches.
### 3.2.2
- Reverted the changes made in 3.2.1 to use gnu tar and zstd by default on windows.
### 3.2.1 ### 3.2.1
- Update `@actions/cache` on windows to use gnu tar and zstd by default and fallback to bsdtar and zstd if gnu tar is not available. ([issue](https://github.com/actions/cache/issues/984)) - Update `@actions/cache` on windows to use gnu tar and zstd by default and fallback to bsdtar and zstd if gnu tar is not available. ([issue](https://github.com/actions/cache/issues/984))
- Added support for fallback to gzip to restore old caches on windows. - Added support for fallback to gzip to restore old caches on windows.
- Added logs for cache version in case of a cache miss. - Added logs for cache version in case of a cache miss.
### 3.2.0 ### 3.2.2
- Reverted the changes made in 3.2.1 to use gnu tar and zstd by default on windows.
- Released the two new actions - [restore](restore/action.yml) and [save](save/action.yml) for granular control on cache ### 3.2.3
- Support cross os caching on Windows as an opt-in feature.
- Fix issue with symlink restoration on Windows for cross-os caches.
### 3.2.0-beta.1 ### 3.2.4
- Added option to fail job on cache miss.
- Added two new actions - [restore](restore/action.yml) and [save](save/action.yml) for granular control on cache. ### 3.2.5
- Added fix to prevent from setting MYSYS environment variable globally.
### 3.1.0-beta.3 ### 3.2.6
- Fix zstd not being used after zstd version upgrade to 1.5.4 on hosted runners.
- Bug fixes for bsdtar fallback if gnutar not available and gzip fallback if cache saved using old cache action on windows. ### 3.3.0
- Added option to lookup cache without downloading it.
### 3.1.0-beta.2
- Added support for fallback to gzip to restore old caches on windows.
### 3.1.0-beta.1
- Update `@actions/cache` on windows to use gnu tar and zstd by default and fallback to bsdtar and zstd if gnu tar is not available. ([issue](https://github.com/actions/cache/issues/984))
### 3.0.11
- Update toolkit version to 3.0.5 to include `@actions/core@^1.10.0`
- Update `@actions/cache` to use updated `saveState` and `setOutput` functions from `@actions/core@^1.10.0`
### 3.0.10
- Fix a bug with sorting inputs.
- Update definition for restore-keys in README.md
### 3.0.9
- Enhanced the warning message for cache unavailablity in case of GHES.
### 3.0.8
- Fix zstd not working for windows on gnu tar in issues [#888](https://github.com/actions/cache/issues/888) and [#891](https://github.com/actions/cache/issues/891).
- Allowing users to provide a custom timeout as input for aborting download of a cache segment using an environment variable `SEGMENT_DOWNLOAD_TIMEOUT_MINS`. Default is 60 minutes.
### 3.0.7
- Fixed [#810](https://github.com/actions/cache/issues/810) - download stuck issue. A new timeout is introduced in the download process to abort the download if it gets stuck and doesn't finish within an hour.
### 3.0.6
- Fixed [#809](https://github.com/actions/cache/issues/809) - zstd -d: no such file or directory error
- Fixed [#833](https://github.com/actions/cache/issues/833) - cache doesn't work with github workspace directory
### 3.0.5
- Removed error handling by consuming actions/cache 3.0 toolkit, Now cache server error handling will be done by toolkit. ([PR](https://github.com/actions/cache/pull/834))
### 3.0.4
- Fixed tar creation error while trying to create tar with path as `~/` home folder on `ubuntu-latest`. ([issue](https://github.com/actions/cache/issues/689))
### 3.0.3
- Fixed avoiding empty cache save when no files are available for caching. ([issue](https://github.com/actions/cache/issues/624))
### 3.0.2
- Added support for dynamic cache size cap on GHES.
### 3.0.1
- Added support for caching from GHES 3.5.
- Fixed download issue for files > 2GB during restore.
### 3.0.0
- Updated minimum runner version support from node 12 -> node 16

View File

@ -8,26 +8,17 @@ import * as testUtils from "../src/utils/testUtils";
jest.mock("@actions/core"); jest.mock("@actions/core");
jest.mock("@actions/cache"); jest.mock("@actions/cache");
let pristineEnv: NodeJS.ProcessEnv;
beforeAll(() => { beforeAll(() => {
pristineEnv = process.env;
jest.spyOn(core, "getInput").mockImplementation((name, options) => { jest.spyOn(core, "getInput").mockImplementation((name, options) => {
return jest.requireActual("@actions/core").getInput(name, options); return jest.requireActual("@actions/core").getInput(name, options);
}); });
}); });
beforeEach(() => { afterEach(() => {
jest.resetModules();
process.env = pristineEnv;
delete process.env[Events.Key]; delete process.env[Events.Key];
delete process.env[RefKey]; delete process.env[RefKey];
}); });
afterAll(() => {
process.env = pristineEnv;
});
test("isGhes returns true if server url is not github.com", () => { test("isGhes returns true if server url is not github.com", () => {
try { try {
process.env["GITHUB_SERVER_URL"] = "http://example.com"; process.env["GITHUB_SERVER_URL"] = "http://example.com";
@ -240,28 +231,3 @@ test("isCacheFeatureAvailable for ac disabled on dotcom", () => {
delete process.env["GITHUB_SERVER_URL"]; delete process.env["GITHUB_SERVER_URL"];
} }
}); });
test("isGhes returns false when the GITHUB_SERVER_URL environment variable is not defined", async () => {
delete process.env["GITHUB_SERVER_URL"];
expect(actionUtils.isGhes()).toBeFalsy();
});
test("isGhes returns false when the GITHUB_SERVER_URL environment variable is set to github.com", async () => {
process.env["GITHUB_SERVER_URL"] = "https://github.com";
expect(actionUtils.isGhes()).toBeFalsy();
});
test("isGhes returns false when the GITHUB_SERVER_URL environment variable is set to a GitHub Enterprise Cloud-style URL", async () => {
process.env["GITHUB_SERVER_URL"] = "https://contoso.ghe.com";
expect(actionUtils.isGhes()).toBeFalsy();
});
test("isGhes returns false when the GITHUB_SERVER_URL environment variable has a .localhost suffix", async () => {
process.env["GITHUB_SERVER_URL"] = "https://mock-github.localhost";
expect(actionUtils.isGhes()).toBeFalsy();
});
test("isGhes returns true when the GITHUB_SERVER_URL environment variable is set to some other URL", async () => {
process.env["GITHUB_SERVER_URL"] = "https://src.onpremise.fabrikam.com";
expect(actionUtils.isGhes()).toBeTruthy();
});

View File

@ -2,7 +2,7 @@ import * as cache from "@actions/cache";
import * as core from "@actions/core"; import * as core from "@actions/core";
import { Events, RefKey } from "../src/constants"; import { Events, RefKey } from "../src/constants";
import { restoreRun } from "../src/restoreImpl"; import run from "../src/restore";
import * as actionUtils from "../src/utils/actionUtils"; import * as actionUtils from "../src/utils/actionUtils";
import * as testUtils from "../src/utils/testUtils"; import * as testUtils from "../src/utils/testUtils";
@ -71,7 +71,7 @@ test("restore with no cache found", async () => {
return Promise.resolve(undefined); return Promise.resolve(undefined);
}); });
await restoreRun(); await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith( expect(restoreCacheMock).toHaveBeenCalledWith(
@ -114,7 +114,7 @@ test("restore with restore keys and no cache found", async () => {
return Promise.resolve(undefined); return Promise.resolve(undefined);
}); });
await restoreRun(); await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith( expect(restoreCacheMock).toHaveBeenCalledWith(
@ -156,7 +156,7 @@ test("restore with cache found for key", async () => {
return Promise.resolve(key); return Promise.resolve(key);
}); });
await restoreRun(); await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith( expect(restoreCacheMock).toHaveBeenCalledWith(
@ -201,7 +201,7 @@ test("restore with cache found for restore key", async () => {
return Promise.resolve(restoreKey); return Promise.resolve(restoreKey);
}); });
await restoreRun(); await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith( expect(restoreCacheMock).toHaveBeenCalledWith(
@ -246,7 +246,7 @@ test("Fail restore when fail on cache miss is enabled and primary + restore keys
return Promise.resolve(undefined); return Promise.resolve(undefined);
}); });
await restoreRun(); await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith( expect(restoreCacheMock).toHaveBeenCalledWith(
@ -289,7 +289,7 @@ test("restore when fail on cache miss is enabled and primary key doesn't match r
return Promise.resolve(restoreKey); return Promise.resolve(restoreKey);
}); });
await restoreRun(); await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith( expect(restoreCacheMock).toHaveBeenCalledWith(
@ -335,7 +335,7 @@ test("restore with fail on cache miss disabled and no cache found", async () =>
return Promise.resolve(undefined); return Promise.resolve(undefined);
}); });
await restoreRun(); await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith( expect(restoreCacheMock).toHaveBeenCalledWith(

View File

@ -2,7 +2,7 @@ import * as cache from "@actions/cache";
import * as core from "@actions/core"; import * as core from "@actions/core";
import { Events, Inputs, RefKey } from "../src/constants"; import { Events, Inputs, RefKey } from "../src/constants";
import { restoreImpl } from "../src/restoreImpl"; import run from "../src/restoreImpl";
import { StateProvider } from "../src/stateProvider"; import { StateProvider } from "../src/stateProvider";
import * as actionUtils from "../src/utils/actionUtils"; import * as actionUtils from "../src/utils/actionUtils";
import * as testUtils from "../src/utils/testUtils"; import * as testUtils from "../src/utils/testUtils";
@ -60,7 +60,7 @@ test("restore with invalid event outputs warning", async () => {
const invalidEvent = "commit_comment"; const invalidEvent = "commit_comment";
process.env[Events.Key] = invalidEvent; process.env[Events.Key] = invalidEvent;
delete process.env[RefKey]; delete process.env[RefKey];
await restoreImpl(new StateProvider()); await run(new StateProvider());
expect(logWarningMock).toHaveBeenCalledWith( expect(logWarningMock).toHaveBeenCalledWith(
`Event Validation Error: The event type ${invalidEvent} is not supported because it's not tied to a branch or tag ref.` `Event Validation Error: The event type ${invalidEvent} is not supported because it's not tied to a branch or tag ref.`
); );
@ -76,7 +76,7 @@ test("restore without AC available should no-op", async () => {
const restoreCacheMock = jest.spyOn(cache, "restoreCache"); const restoreCacheMock = jest.spyOn(cache, "restoreCache");
const setCacheHitOutputMock = jest.spyOn(core, "setOutput"); const setCacheHitOutputMock = jest.spyOn(core, "setOutput");
await restoreImpl(new StateProvider()); await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(0); expect(restoreCacheMock).toHaveBeenCalledTimes(0);
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1); expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
@ -92,7 +92,7 @@ test("restore on GHES without AC available should no-op", async () => {
const restoreCacheMock = jest.spyOn(cache, "restoreCache"); const restoreCacheMock = jest.spyOn(cache, "restoreCache");
const setCacheHitOutputMock = jest.spyOn(core, "setOutput"); const setCacheHitOutputMock = jest.spyOn(core, "setOutput");
await restoreImpl(new StateProvider()); await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(0); expect(restoreCacheMock).toHaveBeenCalledTimes(0);
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1); expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
@ -119,7 +119,7 @@ test("restore on GHES with AC available ", async () => {
return Promise.resolve(key); return Promise.resolve(key);
}); });
await restoreImpl(new StateProvider()); await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith( expect(restoreCacheMock).toHaveBeenCalledWith(
@ -143,7 +143,7 @@ test("restore on GHES with AC available ", async () => {
test("restore with no path should fail", async () => { test("restore with no path should fail", async () => {
const failedMock = jest.spyOn(core, "setFailed"); const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache"); const restoreCacheMock = jest.spyOn(cache, "restoreCache");
await restoreImpl(new StateProvider()); await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(0); expect(restoreCacheMock).toHaveBeenCalledTimes(0);
// this input isn't necessary for restore b/c tarball contains entries relative to workspace // this input isn't necessary for restore b/c tarball contains entries relative to workspace
expect(failedMock).not.toHaveBeenCalledWith( expect(failedMock).not.toHaveBeenCalledWith(
@ -155,7 +155,7 @@ test("restore with no key", async () => {
testUtils.setInput(Inputs.Path, "node_modules"); testUtils.setInput(Inputs.Path, "node_modules");
const failedMock = jest.spyOn(core, "setFailed"); const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache"); const restoreCacheMock = jest.spyOn(cache, "restoreCache");
await restoreImpl(new StateProvider()); await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(0); expect(restoreCacheMock).toHaveBeenCalledTimes(0);
expect(failedMock).toHaveBeenCalledWith( expect(failedMock).toHaveBeenCalledWith(
"Input required and not supplied: key" "Input required and not supplied: key"
@ -174,7 +174,7 @@ test("restore with too many keys should fail", async () => {
}); });
const failedMock = jest.spyOn(core, "setFailed"); const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache"); const restoreCacheMock = jest.spyOn(cache, "restoreCache");
await restoreImpl(new StateProvider()); await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith( expect(restoreCacheMock).toHaveBeenCalledWith(
[path], [path],
@ -200,7 +200,7 @@ test("restore with large key should fail", async () => {
}); });
const failedMock = jest.spyOn(core, "setFailed"); const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache"); const restoreCacheMock = jest.spyOn(cache, "restoreCache");
await restoreImpl(new StateProvider()); await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith( expect(restoreCacheMock).toHaveBeenCalledWith(
[path], [path],
@ -226,7 +226,7 @@ test("restore with invalid key should fail", async () => {
}); });
const failedMock = jest.spyOn(core, "setFailed"); const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache"); const restoreCacheMock = jest.spyOn(cache, "restoreCache");
await restoreImpl(new StateProvider()); await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith( expect(restoreCacheMock).toHaveBeenCalledWith(
[path], [path],
@ -260,7 +260,7 @@ test("restore with no cache found", async () => {
return Promise.resolve(undefined); return Promise.resolve(undefined);
}); });
await restoreImpl(new StateProvider()); await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith( expect(restoreCacheMock).toHaveBeenCalledWith(
@ -301,7 +301,7 @@ test("restore with restore keys and no cache found", async () => {
return Promise.resolve(undefined); return Promise.resolve(undefined);
}); });
await restoreImpl(new StateProvider()); await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith( expect(restoreCacheMock).toHaveBeenCalledWith(
@ -341,7 +341,7 @@ test("restore with cache found for key", async () => {
return Promise.resolve(key); return Promise.resolve(key);
}); });
await restoreImpl(new StateProvider()); await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith( expect(restoreCacheMock).toHaveBeenCalledWith(
@ -383,7 +383,7 @@ test("restore with cache found for restore key", async () => {
return Promise.resolve(restoreKey); return Promise.resolve(restoreKey);
}); });
await restoreImpl(new StateProvider()); await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith( expect(restoreCacheMock).toHaveBeenCalledWith(
@ -424,7 +424,7 @@ test("restore with lookup-only set", async () => {
return Promise.resolve(key); return Promise.resolve(key);
}); });
await restoreImpl(new StateProvider()); await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith( expect(restoreCacheMock).toHaveBeenCalledWith(
@ -449,19 +449,3 @@ 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

@ -2,7 +2,7 @@ import * as cache from "@actions/cache";
import * as core from "@actions/core"; import * as core from "@actions/core";
import { Events, RefKey } from "../src/constants"; import { Events, RefKey } from "../src/constants";
import { restoreOnlyRun } from "../src/restoreImpl"; import run from "../src/restoreOnly";
import * as actionUtils from "../src/utils/actionUtils"; import * as actionUtils from "../src/utils/actionUtils";
import * as testUtils from "../src/utils/testUtils"; import * as testUtils from "../src/utils/testUtils";
@ -72,7 +72,7 @@ test("restore with no cache found", async () => {
return Promise.resolve(undefined); return Promise.resolve(undefined);
}); });
await restoreOnlyRun(); await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith( expect(restoreCacheMock).toHaveBeenCalledWith(
@ -114,7 +114,7 @@ test("restore with restore keys and no cache found", async () => {
return Promise.resolve(undefined); return Promise.resolve(undefined);
}); });
await restoreOnlyRun(); await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith( expect(restoreCacheMock).toHaveBeenCalledWith(
@ -153,7 +153,7 @@ test("restore with cache found for key", async () => {
return Promise.resolve(key); return Promise.resolve(key);
}); });
await restoreOnlyRun(); await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith( expect(restoreCacheMock).toHaveBeenCalledWith(
@ -196,7 +196,7 @@ test("restore with cache found for restore key", async () => {
return Promise.resolve(restoreKey); return Promise.resolve(restoreKey);
}); });
await restoreOnlyRun(); await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1); expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith( expect(restoreCacheMock).toHaveBeenCalledWith(

View File

@ -2,7 +2,7 @@ import * as cache from "@actions/cache";
import * as core from "@actions/core"; import * as core from "@actions/core";
import { Events, Inputs, RefKey } from "../src/constants"; import { Events, Inputs, RefKey } from "../src/constants";
import { saveRun } from "../src/saveImpl"; import run from "../src/save";
import * as actionUtils from "../src/utils/actionUtils"; import * as actionUtils from "../src/utils/actionUtils";
import * as testUtils from "../src/utils/testUtils"; import * as testUtils from "../src/utils/testUtils";
@ -100,7 +100,7 @@ test("save with valid inputs uploads a cache", async () => {
return Promise.resolve(cacheId); return Promise.resolve(cacheId);
}); });
await saveRun(); await run();
expect(saveCacheMock).toHaveBeenCalledTimes(1); expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith( expect(saveCacheMock).toHaveBeenCalledWith(

View File

@ -2,7 +2,7 @@ import * as cache from "@actions/cache";
import * as core from "@actions/core"; import * as core from "@actions/core";
import { Events, Inputs, RefKey } from "../src/constants"; import { Events, Inputs, RefKey } from "../src/constants";
import { saveImpl } from "../src/saveImpl"; import run from "../src/saveImpl";
import { StateProvider } from "../src/stateProvider"; import { StateProvider } from "../src/stateProvider";
import * as actionUtils from "../src/utils/actionUtils"; import * as actionUtils from "../src/utils/actionUtils";
import * as testUtils from "../src/utils/testUtils"; import * as testUtils from "../src/utils/testUtils";
@ -77,7 +77,7 @@ test("save with invalid event outputs warning", async () => {
const invalidEvent = "commit_comment"; const invalidEvent = "commit_comment";
process.env[Events.Key] = invalidEvent; process.env[Events.Key] = invalidEvent;
delete process.env[RefKey]; delete process.env[RefKey];
await saveImpl(new StateProvider()); await run(new StateProvider());
expect(logWarningMock).toHaveBeenCalledWith( expect(logWarningMock).toHaveBeenCalledWith(
`Event Validation Error: The event type ${invalidEvent} is not supported because it's not tied to a branch or tag ref.` `Event Validation Error: The event type ${invalidEvent} is not supported because it's not tied to a branch or tag ref.`
); );
@ -100,7 +100,7 @@ test("save with no primary key in state outputs warning", async () => {
}); });
const saveCacheMock = jest.spyOn(cache, "saveCache"); const saveCacheMock = jest.spyOn(cache, "saveCache");
await saveImpl(new StateProvider()); await run(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(0); expect(saveCacheMock).toHaveBeenCalledTimes(0);
expect(logWarningMock).toHaveBeenCalledWith(`Key is not specified.`); expect(logWarningMock).toHaveBeenCalledWith(`Key is not specified.`);
@ -115,7 +115,7 @@ test("save without AC available should no-op", async () => {
const saveCacheMock = jest.spyOn(cache, "saveCache"); const saveCacheMock = jest.spyOn(cache, "saveCache");
await saveImpl(new StateProvider()); await run(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(0); expect(saveCacheMock).toHaveBeenCalledTimes(0);
}); });
@ -128,7 +128,7 @@ test("save on ghes without AC available should no-op", async () => {
const saveCacheMock = jest.spyOn(cache, "saveCache"); const saveCacheMock = jest.spyOn(cache, "saveCache");
await saveImpl(new StateProvider()); await run(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(0); expect(saveCacheMock).toHaveBeenCalledTimes(0);
}); });
@ -161,7 +161,7 @@ test("save on GHES with AC available", async () => {
return Promise.resolve(cacheId); return Promise.resolve(cacheId);
}); });
await saveImpl(new StateProvider()); await run(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(1); expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith( expect(saveCacheMock).toHaveBeenCalledWith(
@ -194,7 +194,7 @@ test("save with exact match returns early", async () => {
}); });
const saveCacheMock = jest.spyOn(cache, "saveCache"); const saveCacheMock = jest.spyOn(cache, "saveCache");
await saveImpl(new StateProvider()); await run(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(0); expect(saveCacheMock).toHaveBeenCalledTimes(0);
expect(infoMock).toHaveBeenCalledWith( expect(infoMock).toHaveBeenCalledWith(
@ -221,7 +221,7 @@ test("save with missing input outputs warning", async () => {
}); });
const saveCacheMock = jest.spyOn(cache, "saveCache"); const saveCacheMock = jest.spyOn(cache, "saveCache");
await saveImpl(new StateProvider()); await run(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(0); expect(saveCacheMock).toHaveBeenCalledTimes(0);
expect(logWarningMock).toHaveBeenCalledWith( expect(logWarningMock).toHaveBeenCalledWith(
@ -259,7 +259,7 @@ test("save with large cache outputs warning", async () => {
); );
}); });
await saveImpl(new StateProvider()); await run(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(1); expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith( expect(saveCacheMock).toHaveBeenCalledWith(
@ -306,7 +306,7 @@ test("save with reserve cache failure outputs warning", async () => {
throw error; throw error;
}); });
await saveImpl(new StateProvider()); await run(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(1); expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith( expect(saveCacheMock).toHaveBeenCalledWith(
@ -349,7 +349,7 @@ test("save with server error outputs warning", async () => {
throw new Error("HTTP Error Occurred"); throw new Error("HTTP Error Occurred");
}); });
await saveImpl(new StateProvider()); await run(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(1); expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith( expect(saveCacheMock).toHaveBeenCalledWith(
@ -392,7 +392,7 @@ test("save with valid inputs uploads a cache", async () => {
return Promise.resolve(cacheId); return Promise.resolve(cacheId);
}); });
await saveImpl(new StateProvider()); await run(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(1); expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith( expect(saveCacheMock).toHaveBeenCalledWith(

View File

@ -2,7 +2,7 @@ import * as cache from "@actions/cache";
import * as core from "@actions/core"; import * as core from "@actions/core";
import { Events, Inputs, RefKey } from "../src/constants"; import { Events, Inputs, RefKey } from "../src/constants";
import { saveOnlyRun } from "../src/saveImpl"; import run from "../src/saveOnly";
import * as actionUtils from "../src/utils/actionUtils"; import * as actionUtils from "../src/utils/actionUtils";
import * as testUtils from "../src/utils/testUtils"; import * as testUtils from "../src/utils/testUtils";
@ -90,7 +90,7 @@ test("save with valid inputs uploads a cache", async () => {
return Promise.resolve(cacheId); return Promise.resolve(cacheId);
}); });
await saveOnlyRun(); await run();
expect(saveCacheMock).toHaveBeenCalledTimes(1); expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith( expect(saveCacheMock).toHaveBeenCalledWith(
@ -122,7 +122,7 @@ test("save failing logs the warning message", async () => {
return Promise.resolve(cacheId); return Promise.resolve(cacheId);
}); });
await saveOnlyRun(); await run();
expect(saveCacheMock).toHaveBeenCalledTimes(1); expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith( expect(saveCacheMock).toHaveBeenCalledWith(

View File

@ -9,7 +9,7 @@ inputs:
description: 'An explicit key for restoring and saving the cache' description: 'An explicit key for restoring and saving the cache'
required: true required: true
restore-keys: restore-keys:
description: 'An ordered multiline string listing the prefix-matched keys, that are used for restoring stale cache if no cache hit occurred for key. Note `cache-hit` returns false in this case.' 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 required: false
upload-chunk-size: upload-chunk-size:
description: 'The chunk size used to split up large files during upload, in bytes' description: 'The chunk size used to split up large files during upload, in bytes'
@ -26,22 +26,14 @@ inputs:
description: 'Check if a cache entry exists for the given input(s) (key, restore-keys) without downloading the cache' description: 'Check if a cache entry exists for the given input(s) (key, restore-keys) without downloading the cache'
default: 'false' default: 'false'
required: false required: false
save-always:
description: 'Run the post step to save the cache even if another step before fails'
default: '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: 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'
runs: runs:
using: 'node20' using: 'node16'
main: 'dist/restore/index.js' main: 'dist/restore/index.js'
post: 'dist/save/index.js' post: 'dist/save/index.js'
post-if: "success()" post-if: success()
branding: branding:
icon: 'archive' icon: 'archive'
color: 'gray-dark' color: 'gray-dark'

View File

@ -12,7 +12,7 @@ This document lists some of the strategies (and example workflows if possible) w
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
key: ${{ some-metadata }}-cache key: ${{ some-metadata }}-cache
``` ```
@ -24,7 +24,7 @@ In your workflows, you can use different strategies to name your key depending o
One of the most common use case is to use hash for lockfile as key. This way, same cache will be restored for a lockfile until there's a change in dependencies listed in lockfile. One of the most common use case is to use hash for lockfile as key. This way, same cache will be restored for a lockfile until there's a change in dependencies listed in lockfile.
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: | path: |
path/to/dependencies path/to/dependencies
@ -37,7 +37,7 @@ One of the most common use case is to use hash for lockfile as key. This way, sa
If cache is not found matching the primary key, restore keys can be used to download the closest matching cache that was recently created. This ensures that the build/install step will need to additionally fetch just a handful of newer dependencies, and hence saving build time. If cache is not found matching the primary key, restore keys can be used to download the closest matching cache that was recently created. This ensures that the build/install step will need to additionally fetch just a handful of newer dependencies, and hence saving build time.
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: | path: |
path/to/dependencies path/to/dependencies
@ -54,7 +54,7 @@ The restore keys can be provided as a complete name, or a prefix, read more [her
In case of workflows with matrix running for multiple Operating Systems, the caches can be stored separately for each of them. This can be used in combination with hashfiles in case multiple caches are being generated per OS. In case of workflows with matrix running for multiple Operating Systems, the caches can be stored separately for each of them. This can be used in combination with hashfiles in case multiple caches are being generated per OS.
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: | path: |
path/to/dependencies path/to/dependencies
@ -73,7 +73,7 @@ Caches scoped to the particular workflow run id or run attempt can be stored and
On similar lines, commit sha can be used to create a very specialized and short lived cache. On similar lines, commit sha can be used to create a very specialized and short lived cache.
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: | path: |
path/to/dependencies path/to/dependencies
@ -81,12 +81,12 @@ On similar lines, commit sha can be used to create a very specialized and short
key: cache-${{ github.sha }} key: cache-${{ github.sha }}
``` ```
### Using multiple factors while forming a key depending on the need ### Using multiple factors while forming a key depening on the need
Cache key can be formed by combination of more than one metadata, evaluated info. Cache key can be formed by combination of more than one metadata, evaluated info.
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: | path: |
path/to/dependencies path/to/dependencies
@ -102,7 +102,7 @@ The [GitHub Context](https://docs.github.com/en/actions/learn-github-actions/con
While setting paths for caching dependencies it is important to give correct path depending on the hosted runner you are using or whether the action is running in a container job. Assigning different `path` for save and restore will result in cache miss. While setting paths for caching dependencies it is important to give correct path depending on the hosted runner you are using or whether the action is running in a container job. Assigning different `path` for save and restore will result in cache miss.
Below are GitHub hosted runner specific paths one should take care of when writing a workflow which saves/restores caches across OS. Below are GiHub hosted runner specific paths one should take care of when writing a workflow which saves/restores caches across OS.
#### Ubuntu Paths #### Ubuntu Paths
@ -146,9 +146,9 @@ In case you are using a centralized job to create and save your cache that can b
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- uses: actions/cache/restore@v4 - uses: actions/cache/restore@v3
id: cache id: cache
with: with:
path: path/to/dependencies path: path/to/dependencies
@ -171,9 +171,9 @@ You can use the output of this action to exit the workflow on cache miss. This w
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- uses: actions/cache/restore@v4 - uses: actions/cache/restore@v3
id: cache id: cache
with: with:
path: path/to/dependencies path: path/to/dependencies
@ -194,7 +194,7 @@ steps:
If you want to avoid re-computing the cache key again in `save` action, the outputs from `restore` action can be used as input to the `save` action. If you want to avoid re-computing the cache key again in `save` action, the outputs from `restore` action can be used as input to the `save` action.
```yaml ```yaml
- uses: actions/cache/restore@v4 - uses: actions/cache/restore@v3
id: restore-cache id: restore-cache
with: with:
path: | path: |
@ -204,7 +204,7 @@ If you want to avoid re-computing the cache key again in `save` action, the outp
. .
. .
. .
- uses: actions/cache/save@v4 - uses: actions/cache/save@v3
with: with:
path: | path: |
path/to/dependencies path/to/dependencies
@ -219,7 +219,7 @@ On the other hand, the key can also be explicitly re-computed while executing th
Let's say we have a restore step that computes key at runtime Let's say we have a restore step that computes key at runtime
```yaml ```yaml
uses: actions/cache/restore@v4 uses: actions/cache/restore@v3
id: restore-cache id: restore-cache
with: with:
key: cache-${{ hashFiles('**/lockfiles') }} key: cache-${{ hashFiles('**/lockfiles') }}
@ -228,7 +228,7 @@ with:
Case 1: Where an user would want to reuse the key as it is Case 1: Where an user would want to reuse the key as it is
```yaml ```yaml
uses: actions/cache/save@v4 uses: actions/cache/save@v3
with: with:
key: ${{ steps.restore-cache.outputs.cache-primary-key }} key: ${{ steps.restore-cache.outputs.cache-primary-key }}
``` ```
@ -236,14 +236,31 @@ with:
Case 2: Where the user would want to re-evaluate the key Case 2: Where the user would want to re-evaluate the key
```yaml ```yaml
uses: actions/cache/save@v4 uses: actions/cache/save@v3
with: with:
key: npm-cache-${{hashfiles(package-lock.json)}} key: npm-cache-${{hashfiles(package-lock.json)}}
``` ```
### Saving cache even if the build fails ### Saving cache even if the build fails
See [Always save cache](./save/README.md#always-save-cache). 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@v3
.
. // restore if need be
.
- name: Build
run: /build.sh
- uses: actions/cache/save@v3
if: always() // or any other condition to invoke the save action
with:
path: path/to/dependencies
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
```
### Saving cache once and reusing in multiple workflows ### Saving cache once and reusing in multiple workflows
@ -253,12 +270,12 @@ In case of multi-module projects, where the built artifact of one project needs
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- name: Build - name: Build
run: ./build-parent-module.sh run: ./build-parent-module.sh
- uses: actions/cache/save@v4 - uses: actions/cache/save@v3
id: cache id: cache
with: with:
path: path/to/dependencies path: path/to/dependencies
@ -269,9 +286,9 @@ steps:
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- uses: actions/cache/restore@v4 - uses: actions/cache/restore@v3
id: cache id: cache
with: with:
path: path/to/dependencies path: path/to/dependencies

File diff suppressed because one or more lines are too long

64191
dist/restore/index.js vendored

File diff suppressed because one or more lines are too long

64201
dist/save-only/index.js vendored

File diff suppressed because one or more lines are too long

64208
dist/save/index.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,5 @@
# Examples # Examples
- [Bun](#bun)
- [C# - NuGet](#c---nuget) - [C# - NuGet](#c---nuget)
- [Clojure - Lein Deps](#clojure---lein-deps) - [Clojure - Lein Deps](#clojure---lein-deps)
- [D - DUB](#d---dub) - [D - DUB](#d---dub)
@ -40,34 +39,13 @@
- [Swift, Objective-C - CocoaPods](#swift-objective-c---cocoapods) - [Swift, Objective-C - CocoaPods](#swift-objective-c---cocoapods)
- [Swift - Swift Package Manager](#swift---swift-package-manager) - [Swift - Swift Package Manager](#swift---swift-package-manager)
- [Swift - Mint](#swift---mint) - [Swift - Mint](#swift---mint)
- [* - Bazel](#---bazel)
## Bun
```yaml
- uses: actions/cache@v4
with:
path: |
~/.bun/install/cache
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }}
```
### Windows
```yaml
- uses: actions/cache@v4
with:
path: |
~\.bun
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }}
```
## C# - NuGet ## C# - NuGet
Using [NuGet lock files](https://docs.microsoft.com/nuget/consume-packages/package-references-in-project-files#locking-dependencies): Using [NuGet lock files](https://docs.microsoft.com/nuget/consume-packages/package-references-in-project-files#locking-dependencies):
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: ~/.nuget/packages path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
@ -76,10 +54,10 @@ Using [NuGet lock files](https://docs.microsoft.com/nuget/consume-packages/packa
``` ```
Depending on the environment, huge packages might be pre-installed in the global cache folder. Depending on the environment, huge packages might be pre-installed in the global cache folder.
With `actions/cache@v4` you can now exclude unwanted packages with [exclude pattern](https://github.com/actions/toolkit/tree/main/packages/glob#exclude-patterns) With `actions/cache@v3` you can now exclude unwanted packages with [exclude pattern](https://github.com/actions/toolkit/tree/main/packages/glob#exclude-patterns)
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: | path: |
~/.nuget/packages ~/.nuget/packages
@ -96,7 +74,7 @@ Or you could move the cache folder like below.
env: env:
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
steps: steps:
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: ${{ github.workspace }}/.nuget/packages path: ${{ github.workspace }}/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
@ -108,7 +86,7 @@ steps:
```yaml ```yaml
- name: Cache lein project dependencies - name: Cache lein project dependencies
uses: actions/cache@v4 uses: actions/cache@v3
with: with:
path: ~/.m2/repository path: ~/.m2/repository
key: ${{ runner.os }}-clojure-${{ hashFiles('**/project.clj') }} key: ${{ runner.os }}-clojure-${{ hashFiles('**/project.clj') }}
@ -122,7 +100,7 @@ steps:
### POSIX ### POSIX
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: ~/.dub path: ~/.dub
key: ${{ runner.os }}-dub-${{ hashFiles('**/dub.selections.json') }} key: ${{ runner.os }}-dub-${{ hashFiles('**/dub.selections.json') }}
@ -133,7 +111,7 @@ steps:
### Windows ### Windows
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: ~\AppData\Local\dub path: ~\AppData\Local\dub
key: ${{ runner.os }}-dub-${{ hashFiles('**/dub.selections.json') }} key: ${{ runner.os }}-dub-${{ hashFiles('**/dub.selections.json') }}
@ -146,7 +124,7 @@ steps:
### Linux ### Linux
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: | path: |
~/.deno ~/.deno
@ -157,7 +135,7 @@ steps:
### macOS ### macOS
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: | path: |
~/.deno ~/.deno
@ -168,7 +146,7 @@ steps:
### Windows ### Windows
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: | path: |
~\.deno ~\.deno
@ -179,7 +157,7 @@ steps:
## Elixir - Mix ## Elixir - Mix
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: | path: |
deps deps
@ -206,7 +184,7 @@ steps:
### Linux ### Linux
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: | path: |
~/.cache/go-build ~/.cache/go-build
@ -219,7 +197,7 @@ steps:
### macOS ### macOS
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: | path: |
~/Library/Caches/go-build ~/Library/Caches/go-build
@ -232,7 +210,7 @@ steps:
### Windows ### Windows
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: | path: |
~\AppData\Local\go-build ~\AppData\Local\go-build
@ -248,7 +226,7 @@ We cache the elements of the Cabal store separately, as the entirety of `~/.caba
```yaml ```yaml
- name: Cache ~/.cabal/packages, ~/.cabal/store and dist-newstyle - name: Cache ~/.cabal/packages, ~/.cabal/store and dist-newstyle
uses: actions/cache@v4 uses: actions/cache@v3
with: with:
path: | path: |
~/.cabal/packages ~/.cabal/packages
@ -263,14 +241,14 @@ We cache the elements of the Cabal store separately, as the entirety of `~/.caba
### Linux or macOS ### Linux or macOS
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
name: Cache ~/.stack name: Cache ~/.stack
with: with:
path: ~/.stack path: ~/.stack
key: ${{ runner.os }}-stack-global-${{ hashFiles('stack.yaml') }}-${{ hashFiles('package.yaml') }} key: ${{ runner.os }}-stack-global-${{ hashFiles('stack.yaml') }}-${{ hashFiles('package.yaml') }}
restore-keys: | restore-keys: |
${{ runner.os }}-stack-global- ${{ runner.os }}-stack-global-
- uses: actions/cache@v4 - uses: actions/cache@v3
name: Cache .stack-work name: Cache .stack-work
with: with:
path: .stack-work path: .stack-work
@ -282,7 +260,7 @@ We cache the elements of the Cabal store separately, as the entirety of `~/.caba
### Windows ### Windows
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
name: Cache %APPDATA%\stack %LOCALAPPDATA%\Programs\stack name: Cache %APPDATA%\stack %LOCALAPPDATA%\Programs\stack
with: with:
path: | path: |
@ -291,7 +269,7 @@ We cache the elements of the Cabal store separately, as the entirety of `~/.caba
key: ${{ runner.os }}-stack-global-${{ hashFiles('stack.yaml') }}-${{ hashFiles('package.yaml') }} key: ${{ runner.os }}-stack-global-${{ hashFiles('stack.yaml') }}-${{ hashFiles('package.yaml') }}
restore-keys: | restore-keys: |
${{ runner.os }}-stack-global- ${{ runner.os }}-stack-global-
- uses: actions/cache@v4 - uses: actions/cache@v3
name: Cache .stack-work name: Cache .stack-work
with: with:
path: .stack-work path: .stack-work
@ -305,7 +283,7 @@ We cache the elements of the Cabal store separately, as the entirety of `~/.caba
> **Note** Ensure no Gradle daemons are running anymore when your workflow completes. Creating the cache package might fail due to locks being held by Gradle. Refer to the [Gradle Daemon documentation](https://docs.gradle.org/current/userguide/gradle_daemon.html) on how to disable or stop the Gradle Daemons. > **Note** Ensure no Gradle daemons are running anymore when your workflow completes. Creating the cache package might fail due to locks being held by Gradle. Refer to the [Gradle Daemon documentation](https://docs.gradle.org/current/userguide/gradle_daemon.html) on how to disable or stop the Gradle Daemons.
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: | path: |
~/.gradle/caches ~/.gradle/caches
@ -319,7 +297,7 @@ We cache the elements of the Cabal store separately, as the entirety of `~/.caba
```yaml ```yaml
- name: Cache local Maven repository - name: Cache local Maven repository
uses: actions/cache@v4 uses: actions/cache@v3
with: with:
path: ~/.m2/repository path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
@ -355,7 +333,7 @@ After [deprecation](https://github.blog/changelog/2022-10-11-github-actions-depr
`Get npm cache directory` step can then be used with `actions/cache` as shown below `Get npm cache directory` step can then be used with `actions/cache` as shown below
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
id: npm-cache # use this to check for `cache-hit` ==> if: steps.npm-cache.outputs.cache-hit != 'true' id: npm-cache # use this to check for `cache-hit` ==> if: steps.npm-cache.outputs.cache-hit != 'true'
with: with:
path: ${{ steps.npm-cache-dir.outputs.dir }} path: ${{ steps.npm-cache-dir.outputs.dir }}
@ -368,7 +346,7 @@ After [deprecation](https://github.blog/changelog/2022-10-11-github-actions-depr
```yaml ```yaml
- name: restore lerna - name: restore lerna
uses: actions/cache@v4 uses: actions/cache@v3
with: with:
path: '**/node_modules' path: '**/node_modules'
key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
@ -382,7 +360,7 @@ The yarn cache directory will depend on your operating system and version of `ya
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
- uses: actions/cache@v4 - uses: actions/cache@v3
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@ -400,7 +378,7 @@ The yarn 2 cache directory will depend on your config. See https://yarnpkg.com/c
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT
- uses: actions/cache@v4 - uses: actions/cache@v3
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with: with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
@ -415,7 +393,7 @@ Esy allows you to export built dependencies and import pre-built dependencies.
```yaml ```yaml
- name: Restore Cache - name: Restore Cache
id: restore-cache id: restore-cache
uses: actions/cache@v4 uses: actions/cache@v3
with: with:
path: _export path: _export
key: ${{ runner.os }}-esy-${{ hashFiles('esy.lock/index.json') }} key: ${{ runner.os }}-esy-${{ hashFiles('esy.lock/index.json') }}
@ -444,7 +422,7 @@ Esy allows you to export built dependencies and import pre-built dependencies.
id: composer-cache id: composer-cache
run: | run: |
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: ${{ steps.composer-cache.outputs.dir }} path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
@ -465,7 +443,7 @@ Locations:
### Simple example ### Simple example
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: ~/.cache/pip path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
@ -478,7 +456,7 @@ Replace `~/.cache/pip` with the correct `path` if not using Ubuntu.
### Multiple OS's in a workflow ### Multiple OS's in a workflow
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
if: startsWith(runner.os, 'Linux') if: startsWith(runner.os, 'Linux')
with: with:
path: ~/.cache/pip path: ~/.cache/pip
@ -486,7 +464,7 @@ Replace `~/.cache/pip` with the correct `path` if not using Ubuntu.
restore-keys: | restore-keys: |
${{ runner.os }}-pip- ${{ runner.os }}-pip-
- uses: actions/cache@v4 - uses: actions/cache@v3
if: startsWith(runner.os, 'macOS') if: startsWith(runner.os, 'macOS')
with: with:
path: ~/Library/Caches/pip path: ~/Library/Caches/pip
@ -494,7 +472,7 @@ Replace `~/.cache/pip` with the correct `path` if not using Ubuntu.
restore-keys: | restore-keys: |
${{ runner.os }}-pip- ${{ runner.os }}-pip-
- uses: actions/cache@v4 - uses: actions/cache@v3
if: startsWith(runner.os, 'Windows') if: startsWith(runner.os, 'Windows')
with: with:
path: ~\AppData\Local\pip\Cache path: ~\AppData\Local\pip\Cache
@ -520,7 +498,7 @@ jobs:
- os: windows-latest - os: windows-latest
path: ~\AppData\Local\pip\Cache path: ~\AppData\Local\pip\Cache
steps: steps:
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: ${{ matrix.path }} path: ${{ matrix.path }}
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
@ -534,12 +512,11 @@ 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
- name: pip cache - name: pip cache
uses: actions/cache@v4 uses: actions/cache@v3
with: with:
path: ${{ steps.pip-cache.outputs.dir }} path: ${{ steps.pip-cache.outputs.dir }}
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
@ -557,7 +534,7 @@ jobs:
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: ~/.local/share/virtualenvs path: ~/.local/share/virtualenvs
key: ${{ runner.os }}-python-${{ steps.setup-python.outputs.python-version }}-pipenv-${{ hashFiles('Pipfile.lock') }} key: ${{ runner.os }}-python-${{ steps.setup-python.outputs.python-version }}-pipenv-${{ hashFiles('Pipfile.lock') }}
@ -584,7 +561,7 @@ For renv, the cache directory will vary by OS. The `RENV_PATHS_ROOT` environment
cat("##[set-output name=r-version;]", R.Version()$version.string, sep = "") cat("##[set-output name=r-version;]", R.Version()$version.string, sep = "")
shell: Rscript {0} shell: Rscript {0}
- name: Restore Renv package cache - name: Restore Renv package cache
uses: actions/cache@v4 uses: actions/cache@v3
with: with:
path: ${{ env.RENV_PATHS_ROOT }} path: ${{ env.RENV_PATHS_ROOT }}
key: ${{ steps.get-version.outputs.os-version }}-${{ steps.get-version.outputs.r-version }}-${{ inputs.cache-version }}-${{ hashFiles('renv.lock') }} key: ${{ steps.get-version.outputs.os-version }}-${{ steps.get-version.outputs.r-version }}-${{ inputs.cache-version }}-${{ hashFiles('renv.lock') }}
@ -610,7 +587,7 @@ whenever possible:
## Rust - Cargo ## Rust - Cargo
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: | path: |
~/.cargo/bin/ ~/.cargo/bin/
@ -625,7 +602,7 @@ whenever possible:
```yaml ```yaml
- name: Cache SBT - name: Cache SBT
uses: actions/cache@v4 uses: actions/cache@v3
with: with:
path: | path: |
~/.ivy2/cache ~/.ivy2/cache
@ -636,7 +613,7 @@ whenever possible:
## Swift, Objective-C - Carthage ## Swift, Objective-C - Carthage
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: Carthage path: Carthage
key: ${{ runner.os }}-carthage-${{ hashFiles('**/Cartfile.resolved') }} key: ${{ runner.os }}-carthage-${{ hashFiles('**/Cartfile.resolved') }}
@ -647,7 +624,7 @@ whenever possible:
## Swift, Objective-C - CocoaPods ## Swift, Objective-C - CocoaPods
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: Pods path: Pods
key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }} key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }}
@ -658,7 +635,7 @@ whenever possible:
## Swift - Swift Package Manager ## Swift - Swift Package Manager
```yaml ```yaml
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: .build path: .build
key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }}
@ -673,42 +650,10 @@ env:
MINT_PATH: .mint/lib MINT_PATH: .mint/lib
MINT_LINK_PATH: .mint/bin MINT_LINK_PATH: .mint/bin
steps: steps:
- uses: actions/cache@v4 - uses: actions/cache@v3
with: with:
path: .mint path: .mint
key: ${{ runner.os }}-mint-${{ hashFiles('**/Mintfile') }} key: ${{ runner.os }}-mint-${{ hashFiles('**/Mintfile') }}
restore-keys: | restore-keys: |
${{ runner.os }}-mint- ${{ runner.os }}-mint-
``` ```
## * - Bazel
[`bazelisk`](https://github.com/bazelbuild/bazelisk) does not have be to separately downloaded and installed because it's already included in GitHub's `ubuntu-latest` and `macos-latest` base images.
### Linux
```yaml
- name: Cache Bazel
uses: actions/cache@v4
with:
path: |
~/.cache/bazel
key: ${{ runner.os }}-bazel-${{ hashFiles('.bazelversion', '.bazelrc', 'WORKSPACE', 'WORKSPACE.bazel', 'MODULE.bazel') }}
restore-keys: |
${{ runner.os }}-bazel-
- run: bazelisk test //...
```
### macOS
```yaml
- name: Cache Bazel
uses: actions/cache@v4
with:
path: |
/private/var/tmp/_bazel_runner/
key: ${{ runner.os }}-bazel-${{ hashFiles('.bazelversion', '.bazelrc', 'WORKSPACE', 'WORKSPACE.bazel', 'MODULE.bazel') }}
restore-keys: |
${{ runner.os }}-bazel-
- run: bazelisk test //...
```

1733
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "cache", "name": "cache",
"version": "4.2.3", "version": "3.3.0",
"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",
@ -23,10 +23,10 @@
"author": "GitHub", "author": "GitHub",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/cache": "^4.0.3", "@actions/cache": "^3.2.0",
"@actions/core": "^1.11.1", "@actions/core": "^1.10.0",
"@actions/exec": "^1.1.1", "@actions/exec": "^1.1.1",
"@actions/io": "^1.1.3" "@actions/io": "^1.1.2"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^27.5.2", "@types/jest": "^27.5.2",
@ -34,7 +34,7 @@
"@types/node": "^16.18.3", "@types/node": "^16.18.3",
"@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0", "@typescript-eslint/parser": "^5.45.0",
"@vercel/ncc": "^0.38.3", "@zeit/ncc": "^0.20.5",
"eslint": "^8.28.0", "eslint": "^8.28.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.26.0", "eslint-plugin-import": "^2.26.0",

View File

@ -10,7 +10,7 @@ The restore action restores a cache. It works similarly to the `cache` action ex
* `path` - A list of files, directories, and wildcard patterns to restore. See [`@actions/glob`](https://github.com/actions/toolkit/tree/main/packages/glob) for supported patterns. * `path` - A list of files, directories, and wildcard patterns to restore. See [`@actions/glob`](https://github.com/actions/toolkit/tree/main/packages/glob) for supported patterns.
* `restore-keys` - An ordered list of prefix-matched keys to use for restoring stale cache if no cache hit occurred for key. * `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 cache entry is not found. 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. Default: `false` * `lookup-only` - Check if a cache entry exists for the given input(s) (key, restore-keys) without downloading the cache. Default: `false`
### Outputs ### Outputs
@ -23,7 +23,7 @@ The restore action restores a cache. It works similarly to the `cache` action ex
### Environment Variables ### Environment Variables
* `SEGMENT_DOWNLOAD_TIMEOUT_MINS` - Segment download timeout (in minutes, default `10`) to abort download of the segment if not completed in the defined number of minutes. [Read more](https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout) * `SEGMENT_DOWNLOAD_TIMEOUT_MINS` - Segment download timeout (in minutes, default `60`) to abort download of the segment if not completed in the defined number of minutes. [Read more](https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout)
## Use cases ## Use cases
@ -35,9 +35,9 @@ If you are using separate jobs to create and save your cache(s) to be reused by
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- uses: actions/cache/restore@v4 - uses: actions/cache/restore@v3
id: cache id: cache
with: with:
path: path/to/dependencies path: path/to/dependencies
@ -64,12 +64,12 @@ In case of multi-module projects, where the built artifact of one project needs
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- name: Build - name: Build
run: /build-parent-module.sh run: /build-parent-module.sh
- uses: actions/cache/save@v4 - uses: actions/cache/save@v3
id: cache id: cache
with: with:
path: path/to/dependencies path: path/to/dependencies
@ -80,9 +80,9 @@ steps:
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- uses: actions/cache/restore@v4 - uses: actions/cache/restore@v3
id: cache id: cache
with: with:
path: path/to/dependencies path: path/to/dependencies
@ -107,9 +107,9 @@ To fail if there is no cache hit for the primary key, leave `restore-keys` empty
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- uses: actions/cache/restore@v4 - uses: actions/cache/restore@v3
id: cache id: cache
with: with:
path: path/to/dependencies path: path/to/dependencies

View File

@ -9,7 +9,7 @@ inputs:
description: 'An explicit key for restoring the cache' description: 'An explicit key for restoring the cache'
required: true required: true
restore-keys: restore-keys:
description: 'An ordered multiline string listing the prefix-matched keys, that are used for restoring stale cache if no cache hit occurred for key. Note `cache-hit` returns false in this case.' 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 required: false
enableCrossOsArchive: enableCrossOsArchive:
description: 'An optional boolean when enabled, allows windows runners to restore caches that were saved on other platforms' description: 'An optional boolean when enabled, allows windows runners to restore caches that were saved on other platforms'
@ -31,7 +31,7 @@ outputs:
cache-matched-key: cache-matched-key:
description: 'Key of the cache that was restored, it could either be the primary key on cache-hit or a partial/complete match of one of the restore keys' description: 'Key of the cache that was restored, it could either be the primary key on cache-hit or a partial/complete match of one of the restore keys'
runs: runs:
using: 'node20' using: 'node16'
main: '../dist/restore-only/index.js' main: '../dist/restore-only/index.js'
branding: branding:
icon: 'archive' icon: 'archive'

View File

@ -23,7 +23,7 @@ If you are using separate jobs for generating common artifacts and sharing them
```yaml ```yaml
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- name: Install Dependencies - name: Install Dependencies
run: /install.sh run: /install.sh
@ -31,7 +31,7 @@ steps:
- name: Build artifacts - name: Build artifacts
run: /build.sh run: /build.sh
- uses: actions/cache/save@v4 - uses: actions/cache/save@v3
id: cache id: cache
with: with:
path: path/to/dependencies path: path/to/dependencies
@ -47,7 +47,7 @@ Let's say we have a restore step that computes a key at runtime.
#### Restore a cache #### Restore a cache
```yaml ```yaml
uses: actions/cache/restore@v4 uses: actions/cache/restore@v3
id: restore-cache id: restore-cache
with: with:
key: cache-${{ hashFiles('**/lockfiles') }} key: cache-${{ hashFiles('**/lockfiles') }}
@ -55,7 +55,7 @@ with:
#### Case 1 - Where a user would want to reuse the key as it is #### Case 1 - Where a user would want to reuse the key as it is
```yaml ```yaml
uses: actions/cache/save@v4 uses: actions/cache/save@v3
with: with:
key: ${{ steps.restore-cache.outputs.cache-primary-key }} key: ${{ steps.restore-cache.outputs.cache-primary-key }}
``` ```
@ -63,54 +63,26 @@ with:
#### Case 2 - Where the user would want to re-evaluate the key #### Case 2 - Where the user would want to re-evaluate the key
```yaml ```yaml
uses: actions/cache/save@v4 uses: actions/cache/save@v3
with: with:
key: npm-cache-${{hashfiles(package-lock.json)}} key: npm-cache-${{hashfiles(package-lock.json)}}
``` ```
### Always save cache ### 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. 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.
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.
Here's an example where we imagine we're calculating a lot of prime numbers and want to cache them:
```yaml ```yaml
name: Always Caching Prime Numbers steps:
- uses: actions/checkout@v3
on: push .
. // restore if need be
jobs: .
build: - name: Build
runs-on: ubuntu-latest run: /build.sh
- uses: actions/cache/save@v3
steps: if: always() // or any other condition to invoke the save action
- uses: actions/checkout@v4 with:
path: path/to/dependencies
- name: Restore cached Prime Numbers key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
id: cache-prime-numbers-restore
uses: actions/cache/restore@v4
with:
key: ${{ runner.os }}-prime-numbers
path: |
path/to/dependencies
some/other/dependencies
# Intermediate workflow steps
- name: Always Save Prime Numbers
id: cache-prime-numbers-save
if: always() && steps.cache-prime-numbers-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
key: ${{ steps.cache-prime-numbers-restore.outputs.cache-primary-key }}
path: |
path/to/dependencies
some/other/dependencies
``` ```

View File

@ -16,7 +16,7 @@ inputs:
default: 'false' default: 'false'
required: false required: false
runs: runs:
using: 'node20' using: 'node16'
main: '../dist/save-only/index.js' main: '../dist/save-only/index.js'
branding: branding:
icon: 'archive' icon: 'archive'

View File

@ -1,3 +1,10 @@
import { restoreRun } from "./restoreImpl"; import restoreImpl from "./restoreImpl";
import { StateProvider } from "./stateProvider";
restoreRun(true); async function run(): Promise<void> {
await restoreImpl(new StateProvider());
}
run();
export default run;

View File

@ -2,16 +2,11 @@ import * as cache from "@actions/cache";
import * as core from "@actions/core"; import * as core from "@actions/core";
import { Events, Inputs, Outputs, State } from "./constants"; import { Events, Inputs, Outputs, State } from "./constants";
import { import { IStateProvider } from "./stateProvider";
IStateProvider,
NullStateProvider,
StateProvider
} from "./stateProvider";
import * as utils from "./utils/actionUtils"; import * as utils from "./utils/actionUtils";
export async function restoreImpl( async function restoreImpl(
stateProvider: IStateProvider, stateProvider: IStateProvider
earlyExit?: boolean | undefined
): Promise<string | undefined> { ): Promise<string | undefined> {
try { try {
if (!utils.isCacheFeatureAvailable()) { if (!utils.isCacheFeatureAvailable()) {
@ -51,9 +46,6 @@ export async function restoreImpl(
); );
if (!cacheKey) { if (!cacheKey) {
// `cache-hit` is intentionally not set to `false` here to preserve existing behavior
// See https://github.com/actions/cache/issues/1466
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}`
@ -65,6 +57,7 @@ export async function restoreImpl(
...restoreKeys ...restoreKeys
].join(", ")}` ].join(", ")}`
); );
return; return;
} }
@ -86,36 +79,7 @@ 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);
}
} }
} }
async function run( export default restoreImpl;
stateProvider: IStateProvider,
earlyExit: boolean | undefined
): Promise<void> {
await 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
// that all promises that we care about have successfully
// resolved, so simply exit with success.
if (earlyExit) {
process.exit(0);
}
}
export async function restoreOnlyRun(
earlyExit?: boolean | undefined
): Promise<void> {
await run(new NullStateProvider(), earlyExit);
}
export async function restoreRun(
earlyExit?: boolean | undefined
): Promise<void> {
await run(new StateProvider(), earlyExit);
}

View File

@ -1,3 +1,10 @@
import { restoreOnlyRun } from "./restoreImpl"; import restoreImpl from "./restoreImpl";
import { NullStateProvider } from "./stateProvider";
restoreOnlyRun(true); async function run(): Promise<void> {
await restoreImpl(new NullStateProvider());
}
run();
export default run;

View File

@ -1,3 +1,10 @@
import { saveRun } from "./saveImpl"; import saveImpl from "./saveImpl";
import { StateProvider } from "./stateProvider";
saveRun(true); async function run(): Promise<void> {
await saveImpl(new StateProvider());
}
run();
export default run;

View File

@ -2,11 +2,7 @@ import * as cache from "@actions/cache";
import * as core from "@actions/core"; import * as core from "@actions/core";
import { Events, Inputs, State } from "./constants"; import { Events, Inputs, State } from "./constants";
import { import { IStateProvider } from "./stateProvider";
IStateProvider,
NullStateProvider,
StateProvider
} from "./stateProvider";
import * as utils from "./utils/actionUtils"; import * as utils from "./utils/actionUtils";
// Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in // Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in
@ -14,9 +10,7 @@ import * as utils from "./utils/actionUtils";
// throw an uncaught exception. Instead of failing this action, just warn. // throw an uncaught exception. Instead of failing this action, just warn.
process.on("uncaughtException", e => utils.logWarning(e.message)); process.on("uncaughtException", e => utils.logWarning(e.message));
export async function saveImpl( async function saveImpl(stateProvider: IStateProvider): Promise<number | void> {
stateProvider: IStateProvider
): Promise<number | void> {
let cacheId = -1; let cacheId = -1;
try { try {
if (!utils.isCacheFeatureAvailable()) { if (!utils.isCacheFeatureAvailable()) {
@ -78,47 +72,4 @@ export async function saveImpl(
return cacheId; return cacheId;
} }
export async function saveOnlyRun( export default saveImpl;
earlyExit?: boolean | undefined
): Promise<void> {
try {
const cacheId = await saveImpl(new NullStateProvider());
if (cacheId === -1) {
core.warning(`Cache save failed.`);
}
} catch (err) {
console.error(err);
if (earlyExit) {
process.exit(1);
}
}
// 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
// that all promises that we care about have successfully
// resolved, so simply exit with success.
if (earlyExit) {
process.exit(0);
}
}
export async function saveRun(earlyExit?: boolean | undefined): Promise<void> {
try {
await saveImpl(new StateProvider());
} catch (err) {
console.error(err);
if (earlyExit) {
process.exit(1);
}
}
// 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
// that all promises that we care about have successfully
// resolved, so simply exit with success.
if (earlyExit) {
process.exit(0);
}
}

View File

@ -1,3 +1,15 @@
import { saveOnlyRun } from "./saveImpl"; import * as core from "@actions/core";
saveOnlyRun(true); import saveImpl from "./saveImpl";
import { NullStateProvider } from "./stateProvider";
async function run(): Promise<void> {
const cacheId = await saveImpl(new NullStateProvider());
if (cacheId === -1) {
core.warning(`Cache save failed.`);
}
}
run();
export default run;

View File

@ -7,13 +7,7 @@ export function isGhes(): boolean {
const ghUrl = new URL( const ghUrl = new URL(
process.env["GITHUB_SERVER_URL"] || "https://github.com" process.env["GITHUB_SERVER_URL"] || "https://github.com"
); );
return ghUrl.hostname.toUpperCase() !== "GITHUB.COM";
const hostname = ghUrl.hostname.trimEnd().toUpperCase();
const isGitHubHost = hostname === "GITHUB.COM";
const isGitHubEnterpriseCloudHost = hostname.endsWith(".GHE.COM");
const isLocalHost = hostname.endsWith(".LOCALHOST");
return !isGitHubHost && !isGitHubEnterpriseCloudHost && !isLocalHost;
} }
export function isExactKeyMatch(key: string, cacheKey?: string): boolean { export function isExactKeyMatch(key: string, cacheKey?: string): boolean {

View File

@ -1,44 +1,35 @@
# Tips and workarounds
## Cache segment restore timeout ## Cache segment restore timeout
A cache gets downloaded in multiple segments of fixed sizes (`1GB` for a `32-bit` runner and `2GB` for a `64-bit` runner). Sometimes, a segment download gets stuck which causes the workflow job to be stuck forever and fail. Version `v3.0.8` of `actions/cache` introduces a segment download timeout. The segment download timeout will allow the segment download to get aborted and hence allow the job to proceed with a cache miss. A cache gets downloaded in multiple segments of fixed sizes (`1GB` for a `32-bit` runner and `2GB` for a `64-bit` runner). Sometimes, a segment download gets stuck which causes the workflow job to be stuck forever and fail. Version `v3.0.8` of `actions/cache` introduces a segment download timeout. The segment download timeout will allow the segment download to get aborted and hence allow the job to proceed with a cache miss.
Default value of this timeout is 10 minutes and can be customized by specifying an [environment variable](https://docs.github.com/en/actions/learn-github-actions/environment-variables) named `SEGMENT_DOWNLOAD_TIMEOUT_MINS` with timeout value in minutes. Default value of this timeout is 60 minutes and can be customized by specifying an [environment variable](https://docs.github.com/en/actions/learn-github-actions/environment-variables) named `SEGMENT_DOWNLOAD_TIMEOUT_MINS` with timeout value in minutes.
## Update a cache ## Update a cache
A cache today is immutable and cannot be updated. But some use cases require the cache to be saved even though there was a "hit" during restore. To do so, use a `key` which is unique for every run and use `restore-keys` to restore the nearest cache. For example: A cache today is immutable and cannot be updated. But some use cases require the cache to be saved even though there was a "hit" during restore. To do so, use a `key` which is unique for every run and use `restore-keys` to restore the nearest cache. For example:
```yaml ```yaml
- name: update cache on every commit - name: update cache on every commit
uses: actions/cache@v4 uses: actions/cache@v3
with: with:
path: prime-numbers path: prime-numbers
key: primes-${{ runner.os }}-${{ github.run_id }} # Can use time based key as well key: primes-${{ runner.os }}-${{ github.run_id }} # Can use time based key as well
restore-keys: | restore-keys: |
primes-${{ runner.os }} primes-${{ runner.os }}
``` ```
Please note that this will create a new cache on every run and hence will consume the cache [quota](./README.md#cache-limits). Please note that this will create a new cache on every run and hence will consume the cache [quota](./README.md#cache-limits).
## Use cache across feature branches ## Use cache across feature branches
Reusing cache across feature branches is not allowed today to provide cache [isolation](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#restrictions-for-accessing-a-cache). However if both feature branches are from the default branch, a good way to achieve this is to ensure that the default branch has a cache. This cache will then be consumable by both feature branches. Reusing cache across feature branches is not allowed today to provide cache [isolation](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#restrictions-for-accessing-a-cache). However if both feature branches are from the default branch, a good way to achieve this is to ensure that the default branch has a cache. This cache will then be consumable by both feature branches.
## Cross OS cache ## Cross OS cache
From `v3.2.3` cache is cross-os compatible when `enableCrossOsArchive` input is passed as true. This means that a cache created on `ubuntu-latest` or `mac-latest` can be used by `windows-latest` and vice versa, provided the workflow which runs on `windows-latest` have input `enableCrossOsArchive` as true. This is useful to cache dependencies which are independent of the runner platform. This will help reduce the consumption of the cache quota and help build for multiple platforms from the same cache. Things to keep in mind while using this feature: From `v3.2.3` cache is cross-os compatible when `enableCrossOsArchive` input is passed as true. This means that a cache created on `ubuntu-latest` or `mac-latest` can be used by `windows-latest` and vice versa, provided the workflow which runs on `windows-latest` have input `enableCrossOsArchive` as true. This is useful to cache dependencies which are independent of the runner platform. This will help reduce the consumption of the cache quota and help build for multiple platforms from the same cache. Things to keep in mind while using this feature:
- Only cache those files which are compatible across OSs.
- Only cache files that are compatible across OSs. - Caching symlinks might cause issues while restoration as they work differently on different OSs.
- Caching symlinks might cause issues while restoring them as they behave differently on different OSs. - Only cache files from within your github workspace directory.
- Be mindful when caching files from outside your github workspace directory as the directory is located at different places across OS. - Avoid using directory pointers such as `${{ github.workspace }}` or `~` (home) which eventually evaluate to an absolute path and will not match across OSs.
- Avoid using directory pointers such as `${{ github.workspace }}` or `~` (home) which eventually evaluate to an absolute path that does not match across OSs.
## Force deletion of caches overriding default cache eviction policy ## Force deletion of caches overriding default cache eviction policy
Caches have [branch scope restriction](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#restrictions-for-accessing-a-cache) in place. This means that if caches for a specific branch are using a lot of storage quota, it may result into more frequently used caches from `default` branch getting thrashed. For example, if there are many pull requests happening on a repo and are creating caches, these cannot be used in default branch scope but will still occupy a lot of space till they get cleaned up by [eviction policy](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy). But sometime we want to clean them up on a faster cadence so as to ensure default branch is not thrashing. In order to achieve this, [gh-actions-cache cli](https://github.com/actions/gh-actions-cache/) can be used to delete caches for specific branches.
Caches have [branch scope restriction](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#restrictions-for-accessing-a-cache) in place. This means that if caches for a specific branch are using a lot of storage quota, it may result into more frequently used caches from `default` branch getting thrashed. For example, if there are many pull requests happening on a repo and are creating caches, these cannot be used in default branch scope but will still occupy a lot of space till they get cleaned up by [eviction policy](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy). But sometime we want to clean them up on a faster cadence so as to ensure default branch is not thrashing. This workflow uses `gh-actions-cache` to delete all the caches created by a branch.
<details> <details>
<summary>Example</summary> <summary>Example</summary>
@ -59,23 +50,28 @@ jobs:
actions: write actions: write
contents: read contents: read
steps: steps:
- name: Check out code
uses: actions/checkout@v3
- name: Cleanup - name: Cleanup
run: | run: |
echo "Fetching list of cache key" gh extension install actions/gh-actions-cache
cacheKeysForPR=$(gh cache list --ref $BRANCH --limit 100 --json id --jq '.[].id')
REPO=${{ github.repository }}
BRANCH=refs/pull/${{ github.event.pull_request.number }}/merge
## Setting this to not fail the workflow while deleting cache keys. echo "Fetching list of cache key"
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 )
## Setting this to not fail the workflow while deleting cache keys.
set +e set +e
echo "Deleting caches..." echo "Deleting caches..."
for cacheKey in $cacheKeysForPR for cacheKey in $cacheKeysForPR
do do
gh cache delete $cacheKey gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
done done
echo "Done" echo "Done"
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge
``` ```
</details> </details>