Compare commits

..

22 Commits

Author SHA1 Message Date
4a39428162 Merge branch 'main' into phantsure/symlink 2023-01-05 16:45:22 +05:30
a88f332eb2 Address review comments 2023-01-05 11:03:51 +00:00
94407e72c8 Address review comments 2023-01-05 10:56:17 +00:00
48a27058cf Fix tests 2023-01-05 10:35:38 +00:00
2637f06e5f Address review comments and update tests 2023-01-05 09:29:13 +00:00
1f414295fe Fixed broken link (#1057) 2023-01-05 12:14:32 +05:30
af9067e3c7 Fix tests 2023-01-04 08:10:32 +00:00
5e66b6cac9 Add support to opt-in enable cross-os caching on windows 2023-01-04 07:54:25 +00:00
365406cb70 Merge pull request #1051 from uhooi/feature/add_mint_example
Add Mint example
2022-12-30 11:00:22 +05:30
d6217569d5 Update Mint example 2022-12-29 22:28:56 +09:00
84e54000da Merge remote-tracking branch 'origin/main' into feature/add_mint_example 2022-12-29 22:27:37 +09:00
4723a57e26 Revert compression changes related to windows but keep version logging (#1049)
* Revert compression changes related to windows
 due to symlink issues

* Added tips and workarounds for cross os
2022-12-27 16:38:40 +05:30
d1507cccba Merge pull request #1042 from me-and/correct-readme-re-windows
README.md: remove outdated Windows cache tip link
2022-12-26 15:19:43 +05:30
3337563725 Merge branch 'main' into correct-readme-re-windows 2022-12-26 11:44:54 +05:30
60c7666709 save/README.md: Fix typo in example (#1040)
Co-authored-by: Sankalp Kotewar <98868223+kotewar@users.noreply.github.com>
2022-12-26 10:19:10 +05:30
b053f2b699 Fix formatting error in restore/README.md (#1044) 2022-12-26 10:10:47 +05:30
501277cfd7 README.md: remove outdated Windows cache tip link
As of 9b0be58 (Release compression related changes for windows (#1039),
2022-12-23), the section of tips-and-workarounds.md referring to
improving cache restore performance on Windows no longer exists, so
don't link to it from README.md.
2022-12-24 13:15:08 +00:00
c1a5de879e Upgrade codeql to v2 (#1023)
* Upgrade codeql to v2

* Update codeql.yml

Co-authored-by: Sankalp Kotewar <98868223+kotewar@users.noreply.github.com>
2022-12-23 11:58:28 +05:30
9b0be58822 Release compression related changes for windows (#1039)
* Release compression related changes for windows

* Update license
2022-12-23 11:49:17 +05:30
c17f4bf466 GA for granular cache (#1035)
* Add example for Haskell Stack

* Revert "Add example for Haskell Stack"

* Basic implementation

* Updated variable name

* Adding wrapper class

* Changed logs to warnings

* added debug logs

* experimenting

* Test

* test

* new try

* test

* Impl separated

* Reverted wrapper changes

* Added test cases

* Some cleanup

* Formatted document

* Fixed test cases issues

* Slight modification for test cases check

* Updated new actions' input descriptions

* Reverted custom asks implemented and added wrapper

* refactor into a generic outputter

* Readme draft for new actions

* Generated dist

* Fixed breaking test case

* Removed return type in promise

* Removed commented lines

* Calling methods from same file

* dist

* update save as well

* fix merge

* Changes for beta release

* Update dist folder

* Fixed formatting

* dist

* Add support for gzip fallback for restore of old cache on windows

* Fixed test cases

* Fixed test cases

* Added restore only and save only test cases

* Updated new actions dist files

* Removed comments

* Fixed inputs

* Renamed variables and added tests

* Fixed breaking test case

* Fixed review comments and tests

* added stateprovider changes

* Deleted stateprovider tests until added

* Added stateprovider test cases

* Fixed breaking test case

* Updated outputs of restore action

* Changes for beta release

* Update dist folder

* Add support for gzip fallback for restore of old cache on windows

* update for new beta release

* Update save/action.yml

Co-authored-by: Bishal Prasad <bishal-pdmsft@github.com>

* Update restore/action.yml

Co-authored-by: Bishal Prasad <bishal-pdmsft@github.com>

* Update restore/action.yml

Co-authored-by: Bishal Prasad <bishal-pdmsft@github.com>

* Update restore/action.yml

Co-authored-by: Bishal Prasad <bishal-pdmsft@github.com>

* Update restore/action.yml

Co-authored-by: Bishal Prasad <bishal-pdmsft@github.com>

* Added more assertions as values can't be checked

* Removed unused code

* Merged beta branch and resolved conflicts

* Added save readme

* Updates to save readme

* Renamed output

* Added cache hit info in readme

* Update restore/README.md

Co-authored-by: Bishal Prasad <bishal-pdmsft@github.com>

* Update restore/README.md

Co-authored-by: Bishal Prasad <bishal-pdmsft@github.com>

* Update restore/README.md

Co-authored-by: Bishal Prasad <bishal-pdmsft@github.com>

* Update save/README.md

Co-authored-by: Bishal Prasad <bishal-pdmsft@github.com>

* Update save/README.md

Co-authored-by: Bishal Prasad <bishal-pdmsft@github.com>

* Removed verbose statements

* Repositioned new actions introduction

* Added test case for restore state

* Addressed review comments

* nit

* nit: added language to code blocks

* Updated beta version to 3.2.0-beta.1

* Added stateprovider mock implementations

* Linting errors fixed

* Save-only warning added

* Updated return ID to -2

* Removed -2 error code

* Removed comment

* Updated cache npm lib version

* Updated license version

* Updated releases.md

* Updated readme with the new actions in what's new

Co-authored-by: Malo Bourgon <mbourgon@gmail.com>
Co-authored-by: Vipul <vsvipul@github.com>
Co-authored-by: Bishal Prasad <bishal-pdmsft@github.com>
Co-authored-by: Tanuj Kumar Mishra <tanuj077@users.noreply.github.com>
Co-authored-by: Sampark Sharma <phantsure@github.com>
2022-12-21 19:38:44 +05:30
35f35f44e8 Update examples.md 2020-07-19 19:52:18 +09:00
ca1c035094 Update README.md 2020-07-19 19:46:58 +09:00
31 changed files with 629 additions and 255 deletions

View File

@ -6,7 +6,7 @@
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "npm install && npm run build"
"postCreateCommand": "npm install"
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.

View File

@ -8,45 +8,39 @@ on:
jobs:
CodeQL-Build:
# CodeQL runs on ubuntu-latest and windows-latest
# CodeQL runs on ubuntu-latest, windows-latest, and macos-latest
runs-on: ubuntu-latest
permissions:
# required for all workflows
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@v2
# Override language selection by uncommenting this and choosing your languages
# with:
# languages: go, javascript, csharp, python, cpp, java
# languages: go, javascript, csharp, python, cpp, java, ruby
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
# 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).
- name: Autobuild
uses: github/codeql-action/autobuild@v1
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
# ✏️ If the Autobuild fails above, remove it and uncomment the following
# three lines and modify them (or add more) to build your code if your
# project uses a compiled language
#- run: |
# make bootstrap
# make release
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@v2

Binary file not shown.

View File

@ -27,6 +27,7 @@ See ["Caching dependencies to speed up workflows"](https://docs.github.com/en/ac
* 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.
* 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.
* Two new actions available for granular control over caches - [restore](restore/action.yml) and [save](save/action.yml)
Refer [here](https://github.com/actions/cache/blob/v2/README.md) for previous versions
@ -44,7 +45,7 @@ If you are using this inside a container, a POSIX-compliant `tar` needs to be in
* `restore-keys` - An ordered list of prefix-matched keys to use for restoring stale cache if no cache hit occurred for key.
#### Environment Variables
* `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/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
@ -120,6 +121,7 @@ See [Examples](examples.md) for a list of `actions/cache` implementations for us
- [Swift, Objective-C - Carthage](./examples.md#swift-objective-c---carthage)
- [Swift, Objective-C - CocoaPods](./examples.md#swift-objective-c---cocoapods)
- [Swift - Swift Package Manager](./examples.md#swift---swift-package-manager)
- [Swift - Mint](./examples.md#swift---mint)
## Creating a cache key

View File

@ -52,3 +52,14 @@
### 3.2.0-beta.1
- Added two new actions - [restore](restore/action.yml) and [save](save/action.yml) for granular control on cache.
### 3.2.0
- Released the two new actions - [restore](restore/action.yml) and [save](save/action.yml) for granular control on cache
### 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))
- Added support for fallback to gzip to restore old caches on windows.
- Added logs for cache version in case of a cache miss.
### 3.2.2
- Reverted the changes made in 3.2.1 to use gnu tar and zstd by default on windows.

View File

@ -174,6 +174,26 @@ test("getInputAsInt throws if required and value missing", () => {
).toThrowError();
});
test("getInputAsBool returns false if input not set", () => {
expect(actionUtils.getInputAsBool("undefined")).toBe(false);
});
test("getInputAsBool returns value if input is valid", () => {
testUtils.setInput("foo", "true");
expect(actionUtils.getInputAsBool("foo")).toBe(true);
});
test("getInputAsBool returns false if input is invalid or NaN", () => {
testUtils.setInput("foo", "bar");
expect(actionUtils.getInputAsBool("foo")).toBe(false);
});
test("getInputAsBool throws if required and value missing", () => {
expect(() =>
actionUtils.getInputAsBool("undefined2", { required: true })
).toThrowError();
});
test("isCacheFeatureAvailable for ac enabled", () => {
jest.spyOn(cache, "isFeatureAvailable").mockImplementation(() => true);

View File

@ -27,9 +27,17 @@ beforeAll(() => {
return actualUtils.getInputAsArray(name, options);
}
);
jest.spyOn(actionUtils, "getInputAsBool").mockImplementation(
(name, options) => {
const actualUtils = jest.requireActual("../src/utils/actionUtils");
return actualUtils.getInputAsBool(name, options);
}
);
});
beforeEach(() => {
jest.restoreAllMocks();
process.env[Events.Key] = Events.Push;
process.env[RefKey] = "refs/heads/feature-branch";
@ -50,7 +58,8 @@ test("restore with no cache found", async () => {
const key = "node-test";
testUtils.setInputs({
path: path,
key
key,
enableCrossOsArchive: false
});
const infoMock = jest.spyOn(core, "info");
@ -65,7 +74,7 @@ test("restore with no cache found", async () => {
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, [], {}, false);
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
expect(stateMock).toHaveBeenCalledTimes(1);
@ -84,7 +93,8 @@ test("restore with restore keys and no cache found", async () => {
testUtils.setInputs({
path: path,
key,
restoreKeys: [restoreKey]
restoreKeys: [restoreKey],
enableCrossOsArchive: false
});
const infoMock = jest.spyOn(core, "info");
@ -99,7 +109,13 @@ test("restore with restore keys and no cache found", async () => {
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, [restoreKey]);
expect(restoreCacheMock).toHaveBeenCalledWith(
[path],
key,
[restoreKey],
{},
false
);
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
expect(stateMock).toHaveBeenCalledTimes(1);
@ -116,7 +132,8 @@ test("restore with cache found for key", async () => {
const key = "node-test";
testUtils.setInputs({
path: path,
key
key,
enableCrossOsArchive: false
});
const infoMock = jest.spyOn(core, "info");
@ -132,7 +149,7 @@ test("restore with cache found for key", async () => {
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, [], {}, false);
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
expect(stateMock).toHaveBeenCalledWith("CACHE_RESULT", key);
@ -152,7 +169,8 @@ test("restore with cache found for restore key", async () => {
testUtils.setInputs({
path: path,
key,
restoreKeys: [restoreKey]
restoreKeys: [restoreKey],
enableCrossOsArchive: false
});
const infoMock = jest.spyOn(core, "info");
@ -168,7 +186,13 @@ test("restore with cache found for restore key", async () => {
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, [restoreKey]);
expect(restoreCacheMock).toHaveBeenCalledWith(
[path],
key,
[restoreKey],
{},
false
);
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
expect(stateMock).toHaveBeenCalledWith("CACHE_RESULT", restoreKey);

View File

@ -28,9 +28,17 @@ beforeAll(() => {
return actualUtils.getInputAsArray(name, options);
}
);
jest.spyOn(actionUtils, "getInputAsBool").mockImplementation(
(name, options) => {
const actualUtils = jest.requireActual("../src/utils/actionUtils");
return actualUtils.getInputAsBool(name, options);
}
);
});
beforeEach(() => {
jest.restoreAllMocks();
process.env[Events.Key] = Events.Push;
process.env[RefKey] = "refs/heads/feature-branch";
@ -97,7 +105,8 @@ test("restore on GHES with AC available ", async () => {
const key = "node-test";
testUtils.setInputs({
path: path,
key
key,
enableCrossOsArchive: false
});
const infoMock = jest.spyOn(core, "info");
@ -113,7 +122,7 @@ test("restore on GHES with AC available ", async () => {
await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, [], {}, false);
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
@ -152,13 +161,20 @@ test("restore with too many keys should fail", async () => {
testUtils.setInputs({
path: path,
key,
restoreKeys
restoreKeys,
enableCrossOsArchive: false
});
const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, restoreKeys);
expect(restoreCacheMock).toHaveBeenCalledWith(
[path],
key,
restoreKeys,
{},
false
);
expect(failedMock).toHaveBeenCalledWith(
`Key Validation Error: Keys are limited to a maximum of 10.`
);
@ -169,13 +185,14 @@ test("restore with large key should fail", async () => {
const key = "foo".repeat(512); // Over the 512 character limit
testUtils.setInputs({
path: path,
key
key,
enableCrossOsArchive: false
});
const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, [], {}, false);
expect(failedMock).toHaveBeenCalledWith(
`Key Validation Error: ${key} cannot be larger than 512 characters.`
);
@ -186,13 +203,14 @@ test("restore with invalid key should fail", async () => {
const key = "comma,comma";
testUtils.setInputs({
path: path,
key
key,
enableCrossOsArchive: false
});
const failedMock = jest.spyOn(core, "setFailed");
const restoreCacheMock = jest.spyOn(cache, "restoreCache");
await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, [], {}, false);
expect(failedMock).toHaveBeenCalledWith(
`Key Validation Error: ${key} cannot contain commas.`
);
@ -203,7 +221,8 @@ test("restore with no cache found", async () => {
const key = "node-test";
testUtils.setInputs({
path: path,
key
key,
enableCrossOsArchive: false
});
const infoMock = jest.spyOn(core, "info");
@ -218,7 +237,7 @@ test("restore with no cache found", async () => {
await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, [], {}, false);
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
expect(failedMock).toHaveBeenCalledTimes(0);
@ -235,7 +254,8 @@ test("restore with restore keys and no cache found", async () => {
testUtils.setInputs({
path: path,
key,
restoreKeys: [restoreKey]
restoreKeys: [restoreKey],
enableCrossOsArchive: false
});
const infoMock = jest.spyOn(core, "info");
@ -250,7 +270,13 @@ test("restore with restore keys and no cache found", async () => {
await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, [restoreKey]);
expect(restoreCacheMock).toHaveBeenCalledWith(
[path],
key,
[restoreKey],
{},
false
);
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
expect(failedMock).toHaveBeenCalledTimes(0);
@ -265,7 +291,8 @@ test("restore with cache found for key", async () => {
const key = "node-test";
testUtils.setInputs({
path: path,
key
key,
enableCrossOsArchive: false
});
const infoMock = jest.spyOn(core, "info");
@ -281,7 +308,7 @@ test("restore with cache found for key", async () => {
await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, [], {}, false);
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
@ -298,7 +325,8 @@ test("restore with cache found for restore key", async () => {
testUtils.setInputs({
path: path,
key,
restoreKeys: [restoreKey]
restoreKeys: [restoreKey],
enableCrossOsArchive: false
});
const infoMock = jest.spyOn(core, "info");
@ -314,7 +342,13 @@ test("restore with cache found for restore key", async () => {
await run(new StateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, [restoreKey]);
expect(restoreCacheMock).toHaveBeenCalledWith(
[path],
key,
[restoreKey],
{},
false
);
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);

View File

@ -27,9 +27,18 @@ beforeAll(() => {
return actualUtils.getInputAsArray(name, options);
}
);
jest.spyOn(actionUtils, "getInputAsBool").mockImplementation(
(name, options) => {
return jest
.requireActual("../src/utils/actionUtils")
.getInputAsBool(name, options);
}
);
});
beforeEach(() => {
jest.restoreAllMocks();
process.env[Events.Key] = Events.Push;
process.env[RefKey] = "refs/heads/feature-branch";
@ -50,7 +59,8 @@ test("restore with no cache found", async () => {
const key = "node-test";
testUtils.setInputs({
path: path,
key
key,
enableCrossOsArchive: false
});
const infoMock = jest.spyOn(core, "info");
@ -65,7 +75,7 @@ test("restore with no cache found", async () => {
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, [], {}, false);
expect(outputMock).toHaveBeenCalledWith("cache-primary-key", key);
expect(outputMock).toHaveBeenCalledTimes(1);
@ -83,7 +93,8 @@ test("restore with restore keys and no cache found", async () => {
testUtils.setInputs({
path: path,
key,
restoreKeys: [restoreKey]
restoreKeys: [restoreKey],
enableCrossOsArchive: false
});
const infoMock = jest.spyOn(core, "info");
@ -98,7 +109,13 @@ test("restore with restore keys and no cache found", async () => {
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, [restoreKey]);
expect(restoreCacheMock).toHaveBeenCalledWith(
[path],
key,
[restoreKey],
{},
false
);
expect(outputMock).toHaveBeenCalledWith("cache-primary-key", key);
expect(failedMock).toHaveBeenCalledTimes(0);
@ -113,7 +130,8 @@ test("restore with cache found for key", async () => {
const key = "node-test";
testUtils.setInputs({
path: path,
key
key,
enableCrossOsArchive: false
});
const infoMock = jest.spyOn(core, "info");
@ -128,7 +146,7 @@ test("restore with cache found for key", async () => {
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, [], {}, false);
expect(outputMock).toHaveBeenCalledWith("cache-primary-key", key);
expect(outputMock).toHaveBeenCalledWith("cache-hit", "true");
@ -147,7 +165,8 @@ test("restore with cache found for restore key", async () => {
testUtils.setInputs({
path: path,
key,
restoreKeys: [restoreKey]
restoreKeys: [restoreKey],
enableCrossOsArchive: false
});
const infoMock = jest.spyOn(core, "info");
@ -162,7 +181,13 @@ test("restore with cache found for restore key", async () => {
await run();
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, [restoreKey]);
expect(restoreCacheMock).toHaveBeenCalledWith(
[path],
key,
[restoreKey],
{},
false
);
expect(outputMock).toHaveBeenCalledWith("cache-primary-key", key);
expect(outputMock).toHaveBeenCalledWith("cache-hit", "false");

View File

@ -35,6 +35,14 @@ beforeAll(() => {
}
);
jest.spyOn(actionUtils, "getInputAsBool").mockImplementation(
(name, options) => {
return jest
.requireActual("../src/utils/actionUtils")
.getInputAsBool(name, options);
}
);
jest.spyOn(actionUtils, "isExactKeyMatch").mockImplementation(
(key, cacheResult) => {
return jest
@ -95,9 +103,14 @@ test("save with valid inputs uploads a cache", async () => {
await run();
expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith([inputPath], primaryKey, {
uploadChunkSize: 4000000
});
expect(saveCacheMock).toHaveBeenCalledWith(
[inputPath],
primaryKey,
{
uploadChunkSize: 4000000
},
false
);
expect(failedMock).toHaveBeenCalledTimes(0);
});

View File

@ -32,6 +32,14 @@ beforeAll(() => {
}
);
jest.spyOn(actionUtils, "getInputAsBool").mockImplementation(
(name, options) => {
return jest
.requireActual("../src/utils/actionUtils")
.getInputAsBool(name, options);
}
);
jest.spyOn(actionUtils, "isExactKeyMatch").mockImplementation(
(key, cacheResult) => {
return jest
@ -47,6 +55,7 @@ beforeAll(() => {
});
beforeEach(() => {
jest.restoreAllMocks();
process.env[Events.Key] = Events.Push;
process.env[RefKey] = "refs/heads/feature-branch";
@ -155,9 +164,14 @@ test("save on GHES with AC available", async () => {
await run(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith([inputPath], primaryKey, {
uploadChunkSize: 4000000
});
expect(saveCacheMock).toHaveBeenCalledWith(
[inputPath],
primaryKey,
{
uploadChunkSize: 4000000
},
false
);
expect(failedMock).toHaveBeenCalledTimes(0);
});
@ -251,7 +265,8 @@ test("save with large cache outputs warning", async () => {
expect(saveCacheMock).toHaveBeenCalledWith(
[inputPath],
primaryKey,
expect.anything()
expect.anything(),
false
);
expect(logWarningMock).toHaveBeenCalledTimes(1);
@ -297,7 +312,8 @@ test("save with reserve cache failure outputs warning", async () => {
expect(saveCacheMock).toHaveBeenCalledWith(
[inputPath],
primaryKey,
expect.anything()
expect.anything(),
false
);
expect(logWarningMock).toHaveBeenCalledWith(
@ -339,7 +355,8 @@ test("save with server error outputs warning", async () => {
expect(saveCacheMock).toHaveBeenCalledWith(
[inputPath],
primaryKey,
expect.anything()
expect.anything(),
false
);
expect(logWarningMock).toHaveBeenCalledTimes(1);
@ -378,9 +395,14 @@ test("save with valid inputs uploads a cache", async () => {
await run(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith([inputPath], primaryKey, {
uploadChunkSize: 4000000
});
expect(saveCacheMock).toHaveBeenCalledWith(
[inputPath],
primaryKey,
{
uploadChunkSize: 4000000
},
false
);
expect(failedMock).toHaveBeenCalledTimes(0);
});

View File

@ -35,6 +35,14 @@ beforeAll(() => {
}
);
jest.spyOn(actionUtils, "getInputAsBool").mockImplementation(
(name, options) => {
return jest
.requireActual("../src/utils/actionUtils")
.getInputAsBool(name, options);
}
);
jest.spyOn(actionUtils, "isExactKeyMatch").mockImplementation(
(key, cacheResult) => {
return jest
@ -85,9 +93,47 @@ test("save with valid inputs uploads a cache", async () => {
await run();
expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith([inputPath], primaryKey, {
uploadChunkSize: 4000000
});
expect(saveCacheMock).toHaveBeenCalledWith(
[inputPath],
primaryKey,
{
uploadChunkSize: 4000000
},
false
);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save failing logs the warning message", async () => {
const warningMock = jest.spyOn(core, "warning");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const inputPath = "node_modules";
testUtils.setInput(Inputs.Key, primaryKey);
testUtils.setInput(Inputs.Path, inputPath);
testUtils.setInput(Inputs.UploadChunkSize, "4000000");
const cacheId = -1;
const saveCacheMock = jest
.spyOn(cache, "saveCache")
.mockImplementationOnce(() => {
return Promise.resolve(cacheId);
});
await run();
expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith(
[inputPath],
primaryKey,
{
uploadChunkSize: 4000000
},
false
);
expect(warningMock).toHaveBeenCalledTimes(1);
expect(warningMock).toHaveBeenCalledWith("Cache save failed.");
});

View File

@ -25,16 +25,15 @@ afterEach(() => {
});
test("StateProvider saves states", async () => {
const states = new Map<string, string>();
const getStateMock = jest
.spyOn(core, "getState")
.mockImplementation(name =>
jest.requireActual("@actions/core").getState(name)
);
.mockImplementation(key => states.get(key) || "");
const saveStateMock = jest
.spyOn(core, "saveState")
.mockImplementation((key, value) => {
return jest.requireActual("@actions/core").saveState(key, value);
states.set(key, value);
});
const setOutputMock = jest
@ -48,9 +47,11 @@ test("StateProvider saves states", async () => {
const stateProvider: IStateProvider = new StateProvider();
stateProvider.setState("stateKey", "stateValue");
stateProvider.setState(State.CacheMatchedKey, cacheMatchedKey);
stateProvider.getState("stateKey");
stateProvider.getCacheState();
const stateValue = stateProvider.getState("stateKey");
const cacheStateValue = stateProvider.getCacheState();
expect(stateValue).toBe("stateValue");
expect(cacheStateValue).toBe(cacheMatchedKey);
expect(getStateMock).toHaveBeenCalledTimes(2);
expect(saveStateMock).toHaveBeenCalledTimes(2);
expect(setOutputMock).toHaveBeenCalledTimes(0);

View File

@ -14,6 +14,10 @@ inputs:
upload-chunk-size:
description: 'The chunk size used to split up large files during upload, in bytes'
required: false
enableCrossOsArchive:
description: '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'
required: false
outputs:
cache-hit:
description: 'A boolean value to indicate an exact match was found for the primary key'

View File

@ -3383,7 +3383,6 @@ const crypto = __importStar(__webpack_require__(417));
const fs = __importStar(__webpack_require__(747));
const url_1 = __webpack_require__(414);
const utils = __importStar(__webpack_require__(15));
const constants_1 = __webpack_require__(931);
const downloadUtils_1 = __webpack_require__(251);
const options_1 = __webpack_require__(538);
const requestUtils_1 = __webpack_require__(899);
@ -3413,10 +3412,17 @@ function createHttpClient() {
const bearerCredentialHandler = new auth_1.BearerCredentialHandler(token);
return new http_client_1.HttpClient('actions/cache', [bearerCredentialHandler], getRequestOptions());
}
function getCacheVersion(paths, compressionMethod) {
const components = paths.concat(!compressionMethod || compressionMethod === constants_1.CompressionMethod.Gzip
? []
: [compressionMethod]);
function getCacheVersion(paths, compressionMethod, enableCrossOsArchive = false) {
const components = paths;
// Add compression method to cache version to restore
// compressed cache as per compression method
if (compressionMethod) {
components.push(compressionMethod);
}
// Only check for windows platforms if enableCrossOsArchive is false
if (process.platform === 'win32' && !enableCrossOsArchive) {
components.push('windows-only');
}
// Add salt to cache version to support breaking changes in cache entry
components.push(versionSalt);
return crypto
@ -3428,11 +3434,15 @@ exports.getCacheVersion = getCacheVersion;
function getCacheEntry(keys, paths, options) {
return __awaiter(this, void 0, void 0, function* () {
const httpClient = createHttpClient();
const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod);
const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive);
const resource = `cache?keys=${encodeURIComponent(keys.join(','))}&version=${version}`;
const response = yield requestUtils_1.retryTypedResponse('getCacheEntry', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); }));
// Cache not found
if (response.statusCode === 204) {
// Cache not found
// List cache for primary key only if cache miss occurs
if (core.isDebug()) {
yield printCachesListForDiagnostics(keys[0], httpClient, version);
}
return null;
}
if (!requestUtils_1.isSuccessStatusCode(response.statusCode)) {
@ -3451,6 +3461,22 @@ function getCacheEntry(keys, paths, options) {
});
}
exports.getCacheEntry = getCacheEntry;
function printCachesListForDiagnostics(key, httpClient, version) {
return __awaiter(this, void 0, void 0, function* () {
const resource = `caches?key=${encodeURIComponent(key)}`;
const response = yield requestUtils_1.retryTypedResponse('listCache', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); }));
if (response.statusCode === 200) {
const cacheListResult = response.result;
const totalCount = cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.totalCount;
if (totalCount && totalCount > 0) {
core.debug(`No matching cache found for cache key '${key}', version '${version} and scope ${process.env['GITHUB_REF']}. There exist one or more cache(s) with similar key but they have different version or scope. See more info on cache matching here: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key \nOther caches with similar key:`);
for (const cacheEntry of (cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.artifactCaches) || []) {
core.debug(`Cache Key: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheKey}, Cache Version: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheVersion}, Cache Scope: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.scope}, Cache Created: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.creationTime}`);
}
}
}
});
}
function downloadCache(archiveLocation, archivePath, options) {
return __awaiter(this, void 0, void 0, function* () {
const archiveUrl = new url_1.URL(archiveLocation);
@ -3471,7 +3497,7 @@ exports.downloadCache = downloadCache;
function reserveCache(key, paths, options) {
return __awaiter(this, void 0, void 0, function* () {
const httpClient = createHttpClient();
const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod);
const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive);
const reserveCacheRequest = {
key,
version,
@ -4951,7 +4977,8 @@ var Inputs;
Inputs["Key"] = "key";
Inputs["Path"] = "path";
Inputs["RestoreKeys"] = "restore-keys";
Inputs["UploadChunkSize"] = "upload-chunk-size"; // Input for cache, save action
Inputs["UploadChunkSize"] = "upload-chunk-size";
Inputs["EnableCrossOsArchive"] = "enableCrossOsArchive"; // Input for cache, restore, save action
})(Inputs = exports.Inputs || (exports.Inputs = {}));
var Outputs;
(function (Outputs) {
@ -10047,7 +10074,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.isCacheFeatureAvailable = exports.getInputAsInt = exports.getInputAsArray = exports.isValidEvent = exports.logWarning = exports.isExactKeyMatch = exports.isGhes = void 0;
exports.isCacheFeatureAvailable = exports.getInputAsBool = exports.getInputAsInt = exports.getInputAsArray = exports.isValidEvent = exports.logWarning = exports.isExactKeyMatch = exports.isGhes = void 0;
const cache = __importStar(__webpack_require__(692));
const core = __importStar(__webpack_require__(470));
const constants_1 = __webpack_require__(196);
@ -10090,6 +10117,11 @@ function getInputAsInt(name, options) {
return value;
}
exports.getInputAsInt = getInputAsInt;
function getInputAsBool(name, options) {
const result = core.getInput(name, options);
return result.toLowerCase() === "true";
}
exports.getInputAsBool = getInputAsBool;
function isCacheFeatureAvailable() {
if (cache.isFeatureAvailable()) {
return true;
@ -38197,12 +38229,14 @@ var __importStar = (this && this.__importStar) || function (mod) {
};
Object.defineProperty(exports, "__esModule", { value: true });
const exec_1 = __webpack_require__(986);
const core_1 = __webpack_require__(470);
const io = __importStar(__webpack_require__(1));
const fs_1 = __webpack_require__(747);
const path = __importStar(__webpack_require__(622));
const utils = __importStar(__webpack_require__(15));
const constants_1 = __webpack_require__(931);
const IS_WINDOWS = process.platform === 'win32';
core_1.exportVariable('MSYS', 'winsymlinks:nativestrict');
// Returns tar path and type: BSD or GNU
function getTarPath() {
return __awaiter(this, void 0, void 0, function* () {
@ -38329,7 +38363,7 @@ function getDecompressionProgram(tarPath, compressionMethod, archivePath) {
case constants_1.CompressionMethod.Zstd:
return BSD_TAR_ZSTD
? [
'zstd -d --long=30 -o',
'zstd -d --long=30 --force -o',
constants_1.TarFilename,
archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/')
]
@ -38340,7 +38374,7 @@ function getDecompressionProgram(tarPath, compressionMethod, archivePath) {
case constants_1.CompressionMethod.ZstdWithoutLong:
return BSD_TAR_ZSTD
? [
'zstd -d -o',
'zstd -d --force -o',
constants_1.TarFilename,
archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/')
]
@ -38366,7 +38400,7 @@ function getCompressionProgram(tarPath, compressionMethod) {
case constants_1.CompressionMethod.Zstd:
return BSD_TAR_ZSTD
? [
'zstd -T0 --long=30 -o',
'zstd -T0 --long=30 --force -o',
cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
constants_1.TarFilename
]
@ -38377,7 +38411,7 @@ function getCompressionProgram(tarPath, compressionMethod) {
case constants_1.CompressionMethod.ZstdWithoutLong:
return BSD_TAR_ZSTD
? [
'zstd -T0 -o',
'zstd -T0 --force -o',
cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
constants_1.TarFilename
]
@ -47175,7 +47209,6 @@ const path = __importStar(__webpack_require__(622));
const utils = __importStar(__webpack_require__(15));
const cacheHttpClient = __importStar(__webpack_require__(114));
const tar_1 = __webpack_require__(434);
const constants_1 = __webpack_require__(931);
class ValidationError extends Error {
constructor(message) {
super(message);
@ -47222,9 +47255,10 @@ exports.isFeatureAvailable = isFeatureAvailable;
* @param primaryKey an explicit key for restoring the cache
* @param restoreKeys an optional ordered list of keys to use for restoring the cache if no cache hit occurred for key
* @param downloadOptions cache download options
* @param enableCrossOsArchive an optional boolean enabled to restore on windows any cache created on any platform
* @returns string returns the key for the cache hit, otherwise returns undefined
*/
function restoreCache(paths, primaryKey, restoreKeys, options) {
function restoreCache(paths, primaryKey, restoreKeys, options, enableCrossOsArchive = false) {
return __awaiter(this, void 0, void 0, function* () {
checkPaths(paths);
restoreKeys = restoreKeys || [];
@ -47237,31 +47271,17 @@ function restoreCache(paths, primaryKey, restoreKeys, options) {
for (const key of keys) {
checkKey(key);
}
let cacheEntry;
let compressionMethod = yield utils.getCompressionMethod();
const compressionMethod = yield utils.getCompressionMethod();
let archivePath = '';
try {
// path are needed to compute version
cacheEntry = yield cacheHttpClient.getCacheEntry(keys, paths, {
compressionMethod
const cacheEntry = yield cacheHttpClient.getCacheEntry(keys, paths, {
compressionMethod,
enableCrossOsArchive
});
if (!(cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.archiveLocation)) {
// This is to support the old cache entry created by gzip on windows.
if (process.platform === 'win32' &&
compressionMethod !== constants_1.CompressionMethod.Gzip) {
compressionMethod = constants_1.CompressionMethod.Gzip;
cacheEntry = yield cacheHttpClient.getCacheEntry(keys, paths, {
compressionMethod
});
if (!(cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.archiveLocation)) {
return undefined;
}
core.debug("Couldn't find cache entry with zstd compression, falling back to gzip compression.");
}
else {
// Cache not found
return undefined;
}
// Cache not found
return undefined;
}
archivePath = path.join(yield utils.createTempDirectory(), utils.getCacheFileName(compressionMethod));
core.debug(`Archive Path: ${archivePath}`);
@ -47304,10 +47324,11 @@ exports.restoreCache = restoreCache;
*
* @param paths a list of file paths to be cached
* @param key an explicit key for restoring the cache
* @param enableCrossOsArchive an optional boolean enabled to save cache on windows which could be restored on any platform
* @param options cache upload options
* @returns number returns cacheId if the cache was saved successfully and throws an error if save fails
*/
function saveCache(paths, key, options) {
function saveCache(paths, key, options, enableCrossOsArchive = false) {
var _a, _b, _c, _d, _e;
return __awaiter(this, void 0, void 0, function* () {
checkPaths(paths);
@ -47338,6 +47359,7 @@ function saveCache(paths, key, options) {
core.debug('Reserving Cache');
const reserveCacheResponse = yield cacheHttpClient.reserveCache(key, paths, {
compressionMethod,
enableCrossOsArchive,
cacheSize: archiveFileSize
});
if ((_a = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.result) === null || _a === void 0 ? void 0 : _a.cacheId) {
@ -50472,7 +50494,8 @@ function restoreImpl(stateProvider) {
const cachePaths = utils.getInputAsArray(constants_1.Inputs.Path, {
required: true
});
const cacheKey = yield cache.restoreCache(cachePaths, primaryKey, restoreKeys);
const enableCrossOsArchive = utils.getInputAsBool(constants_1.Inputs.EnableCrossOsArchive);
const cacheKey = yield cache.restoreCache(cachePaths, primaryKey, restoreKeys, {}, enableCrossOsArchive);
if (!cacheKey) {
core.info(`Cache not found for input keys: ${[
primaryKey,

99
dist/restore/index.js vendored
View File

@ -3383,7 +3383,6 @@ const crypto = __importStar(__webpack_require__(417));
const fs = __importStar(__webpack_require__(747));
const url_1 = __webpack_require__(414);
const utils = __importStar(__webpack_require__(15));
const constants_1 = __webpack_require__(931);
const downloadUtils_1 = __webpack_require__(251);
const options_1 = __webpack_require__(538);
const requestUtils_1 = __webpack_require__(899);
@ -3413,10 +3412,17 @@ function createHttpClient() {
const bearerCredentialHandler = new auth_1.BearerCredentialHandler(token);
return new http_client_1.HttpClient('actions/cache', [bearerCredentialHandler], getRequestOptions());
}
function getCacheVersion(paths, compressionMethod) {
const components = paths.concat(!compressionMethod || compressionMethod === constants_1.CompressionMethod.Gzip
? []
: [compressionMethod]);
function getCacheVersion(paths, compressionMethod, enableCrossOsArchive = false) {
const components = paths;
// Add compression method to cache version to restore
// compressed cache as per compression method
if (compressionMethod) {
components.push(compressionMethod);
}
// Only check for windows platforms if enableCrossOsArchive is false
if (process.platform === 'win32' && !enableCrossOsArchive) {
components.push('windows-only');
}
// Add salt to cache version to support breaking changes in cache entry
components.push(versionSalt);
return crypto
@ -3428,11 +3434,15 @@ exports.getCacheVersion = getCacheVersion;
function getCacheEntry(keys, paths, options) {
return __awaiter(this, void 0, void 0, function* () {
const httpClient = createHttpClient();
const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod);
const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive);
const resource = `cache?keys=${encodeURIComponent(keys.join(','))}&version=${version}`;
const response = yield requestUtils_1.retryTypedResponse('getCacheEntry', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); }));
// Cache not found
if (response.statusCode === 204) {
// Cache not found
// List cache for primary key only if cache miss occurs
if (core.isDebug()) {
yield printCachesListForDiagnostics(keys[0], httpClient, version);
}
return null;
}
if (!requestUtils_1.isSuccessStatusCode(response.statusCode)) {
@ -3451,6 +3461,22 @@ function getCacheEntry(keys, paths, options) {
});
}
exports.getCacheEntry = getCacheEntry;
function printCachesListForDiagnostics(key, httpClient, version) {
return __awaiter(this, void 0, void 0, function* () {
const resource = `caches?key=${encodeURIComponent(key)}`;
const response = yield requestUtils_1.retryTypedResponse('listCache', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); }));
if (response.statusCode === 200) {
const cacheListResult = response.result;
const totalCount = cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.totalCount;
if (totalCount && totalCount > 0) {
core.debug(`No matching cache found for cache key '${key}', version '${version} and scope ${process.env['GITHUB_REF']}. There exist one or more cache(s) with similar key but they have different version or scope. See more info on cache matching here: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key \nOther caches with similar key:`);
for (const cacheEntry of (cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.artifactCaches) || []) {
core.debug(`Cache Key: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheKey}, Cache Version: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheVersion}, Cache Scope: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.scope}, Cache Created: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.creationTime}`);
}
}
}
});
}
function downloadCache(archiveLocation, archivePath, options) {
return __awaiter(this, void 0, void 0, function* () {
const archiveUrl = new url_1.URL(archiveLocation);
@ -3471,7 +3497,7 @@ exports.downloadCache = downloadCache;
function reserveCache(key, paths, options) {
return __awaiter(this, void 0, void 0, function* () {
const httpClient = createHttpClient();
const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod);
const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive);
const reserveCacheRequest = {
key,
version,
@ -4951,7 +4977,8 @@ var Inputs;
Inputs["Key"] = "key";
Inputs["Path"] = "path";
Inputs["RestoreKeys"] = "restore-keys";
Inputs["UploadChunkSize"] = "upload-chunk-size"; // Input for cache, save action
Inputs["UploadChunkSize"] = "upload-chunk-size";
Inputs["EnableCrossOsArchive"] = "enableCrossOsArchive"; // Input for cache, restore, save action
})(Inputs = exports.Inputs || (exports.Inputs = {}));
var Outputs;
(function (Outputs) {
@ -38110,12 +38137,14 @@ var __importStar = (this && this.__importStar) || function (mod) {
};
Object.defineProperty(exports, "__esModule", { value: true });
const exec_1 = __webpack_require__(986);
const core_1 = __webpack_require__(470);
const io = __importStar(__webpack_require__(1));
const fs_1 = __webpack_require__(747);
const path = __importStar(__webpack_require__(622));
const utils = __importStar(__webpack_require__(15));
const constants_1 = __webpack_require__(931);
const IS_WINDOWS = process.platform === 'win32';
core_1.exportVariable('MSYS', 'winsymlinks:nativestrict');
// Returns tar path and type: BSD or GNU
function getTarPath() {
return __awaiter(this, void 0, void 0, function* () {
@ -38242,7 +38271,7 @@ function getDecompressionProgram(tarPath, compressionMethod, archivePath) {
case constants_1.CompressionMethod.Zstd:
return BSD_TAR_ZSTD
? [
'zstd -d --long=30 -o',
'zstd -d --long=30 --force -o',
constants_1.TarFilename,
archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/')
]
@ -38253,7 +38282,7 @@ function getDecompressionProgram(tarPath, compressionMethod, archivePath) {
case constants_1.CompressionMethod.ZstdWithoutLong:
return BSD_TAR_ZSTD
? [
'zstd -d -o',
'zstd -d --force -o',
constants_1.TarFilename,
archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/')
]
@ -38279,7 +38308,7 @@ function getCompressionProgram(tarPath, compressionMethod) {
case constants_1.CompressionMethod.Zstd:
return BSD_TAR_ZSTD
? [
'zstd -T0 --long=30 -o',
'zstd -T0 --long=30 --force -o',
cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
constants_1.TarFilename
]
@ -38290,7 +38319,7 @@ function getCompressionProgram(tarPath, compressionMethod) {
case constants_1.CompressionMethod.ZstdWithoutLong:
return BSD_TAR_ZSTD
? [
'zstd -T0 -o',
'zstd -T0 --force -o',
cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
constants_1.TarFilename
]
@ -38573,7 +38602,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.isCacheFeatureAvailable = exports.getInputAsInt = exports.getInputAsArray = exports.isValidEvent = exports.logWarning = exports.isExactKeyMatch = exports.isGhes = void 0;
exports.isCacheFeatureAvailable = exports.getInputAsBool = exports.getInputAsInt = exports.getInputAsArray = exports.isValidEvent = exports.logWarning = exports.isExactKeyMatch = exports.isGhes = void 0;
const cache = __importStar(__webpack_require__(692));
const core = __importStar(__webpack_require__(470));
const constants_1 = __webpack_require__(196);
@ -38616,6 +38645,11 @@ function getInputAsInt(name, options) {
return value;
}
exports.getInputAsInt = getInputAsInt;
function getInputAsBool(name, options) {
const result = core.getInput(name, options);
return result.toLowerCase() === "true";
}
exports.getInputAsBool = getInputAsBool;
function isCacheFeatureAvailable() {
if (cache.isFeatureAvailable()) {
return true;
@ -47146,7 +47180,6 @@ const path = __importStar(__webpack_require__(622));
const utils = __importStar(__webpack_require__(15));
const cacheHttpClient = __importStar(__webpack_require__(114));
const tar_1 = __webpack_require__(434);
const constants_1 = __webpack_require__(931);
class ValidationError extends Error {
constructor(message) {
super(message);
@ -47193,9 +47226,10 @@ exports.isFeatureAvailable = isFeatureAvailable;
* @param primaryKey an explicit key for restoring the cache
* @param restoreKeys an optional ordered list of keys to use for restoring the cache if no cache hit occurred for key
* @param downloadOptions cache download options
* @param enableCrossOsArchive an optional boolean enabled to restore on windows any cache created on any platform
* @returns string returns the key for the cache hit, otherwise returns undefined
*/
function restoreCache(paths, primaryKey, restoreKeys, options) {
function restoreCache(paths, primaryKey, restoreKeys, options, enableCrossOsArchive = false) {
return __awaiter(this, void 0, void 0, function* () {
checkPaths(paths);
restoreKeys = restoreKeys || [];
@ -47208,31 +47242,17 @@ function restoreCache(paths, primaryKey, restoreKeys, options) {
for (const key of keys) {
checkKey(key);
}
let cacheEntry;
let compressionMethod = yield utils.getCompressionMethod();
const compressionMethod = yield utils.getCompressionMethod();
let archivePath = '';
try {
// path are needed to compute version
cacheEntry = yield cacheHttpClient.getCacheEntry(keys, paths, {
compressionMethod
const cacheEntry = yield cacheHttpClient.getCacheEntry(keys, paths, {
compressionMethod,
enableCrossOsArchive
});
if (!(cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.archiveLocation)) {
// This is to support the old cache entry created by gzip on windows.
if (process.platform === 'win32' &&
compressionMethod !== constants_1.CompressionMethod.Gzip) {
compressionMethod = constants_1.CompressionMethod.Gzip;
cacheEntry = yield cacheHttpClient.getCacheEntry(keys, paths, {
compressionMethod
});
if (!(cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.archiveLocation)) {
return undefined;
}
core.debug("Couldn't find cache entry with zstd compression, falling back to gzip compression.");
}
else {
// Cache not found
return undefined;
}
// Cache not found
return undefined;
}
archivePath = path.join(yield utils.createTempDirectory(), utils.getCacheFileName(compressionMethod));
core.debug(`Archive Path: ${archivePath}`);
@ -47275,10 +47295,11 @@ exports.restoreCache = restoreCache;
*
* @param paths a list of file paths to be cached
* @param key an explicit key for restoring the cache
* @param enableCrossOsArchive an optional boolean enabled to save cache on windows which could be restored on any platform
* @param options cache upload options
* @returns number returns cacheId if the cache was saved successfully and throws an error if save fails
*/
function saveCache(paths, key, options) {
function saveCache(paths, key, options, enableCrossOsArchive = false) {
var _a, _b, _c, _d, _e;
return __awaiter(this, void 0, void 0, function* () {
checkPaths(paths);
@ -47309,6 +47330,7 @@ function saveCache(paths, key, options) {
core.debug('Reserving Cache');
const reserveCacheResponse = yield cacheHttpClient.reserveCache(key, paths, {
compressionMethod,
enableCrossOsArchive,
cacheSize: archiveFileSize
});
if ((_a = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.result) === null || _a === void 0 ? void 0 : _a.cacheId) {
@ -50472,7 +50494,8 @@ function restoreImpl(stateProvider) {
const cachePaths = utils.getInputAsArray(constants_1.Inputs.Path, {
required: true
});
const cacheKey = yield cache.restoreCache(cachePaths, primaryKey, restoreKeys);
const enableCrossOsArchive = utils.getInputAsBool(constants_1.Inputs.EnableCrossOsArchive);
const cacheKey = yield cache.restoreCache(cachePaths, primaryKey, restoreKeys, {}, enableCrossOsArchive);
if (!cacheKey) {
core.info(`Cache not found for input keys: ${[
primaryKey,

View File

@ -1043,6 +1043,29 @@ class ExecState extends events.EventEmitter {
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
@ -1056,11 +1079,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(__webpack_require__(470));
const saveImpl_1 = __importDefault(__webpack_require__(471));
const stateProvider_1 = __webpack_require__(309);
function run() {
return __awaiter(this, void 0, void 0, function* () {
yield (0, saveImpl_1.default)(new stateProvider_1.NullStateProvider());
const cacheId = yield (0, saveImpl_1.default)(new stateProvider_1.NullStateProvider());
if (cacheId === -1) {
core.warning(`Cache save failed.`);
}
});
}
run();
@ -3412,7 +3439,6 @@ const crypto = __importStar(__webpack_require__(417));
const fs = __importStar(__webpack_require__(747));
const url_1 = __webpack_require__(835);
const utils = __importStar(__webpack_require__(15));
const constants_1 = __webpack_require__(931);
const downloadUtils_1 = __webpack_require__(251);
const options_1 = __webpack_require__(538);
const requestUtils_1 = __webpack_require__(899);
@ -3442,10 +3468,17 @@ function createHttpClient() {
const bearerCredentialHandler = new auth_1.BearerCredentialHandler(token);
return new http_client_1.HttpClient('actions/cache', [bearerCredentialHandler], getRequestOptions());
}
function getCacheVersion(paths, compressionMethod) {
const components = paths.concat(!compressionMethod || compressionMethod === constants_1.CompressionMethod.Gzip
? []
: [compressionMethod]);
function getCacheVersion(paths, compressionMethod, enableCrossOsArchive = false) {
const components = paths;
// Add compression method to cache version to restore
// compressed cache as per compression method
if (compressionMethod) {
components.push(compressionMethod);
}
// Only check for windows platforms if enableCrossOsArchive is false
if (process.platform === 'win32' && !enableCrossOsArchive) {
components.push('windows-only');
}
// Add salt to cache version to support breaking changes in cache entry
components.push(versionSalt);
return crypto
@ -3457,11 +3490,15 @@ exports.getCacheVersion = getCacheVersion;
function getCacheEntry(keys, paths, options) {
return __awaiter(this, void 0, void 0, function* () {
const httpClient = createHttpClient();
const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod);
const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive);
const resource = `cache?keys=${encodeURIComponent(keys.join(','))}&version=${version}`;
const response = yield requestUtils_1.retryTypedResponse('getCacheEntry', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); }));
// Cache not found
if (response.statusCode === 204) {
// Cache not found
// List cache for primary key only if cache miss occurs
if (core.isDebug()) {
yield printCachesListForDiagnostics(keys[0], httpClient, version);
}
return null;
}
if (!requestUtils_1.isSuccessStatusCode(response.statusCode)) {
@ -3480,6 +3517,22 @@ function getCacheEntry(keys, paths, options) {
});
}
exports.getCacheEntry = getCacheEntry;
function printCachesListForDiagnostics(key, httpClient, version) {
return __awaiter(this, void 0, void 0, function* () {
const resource = `caches?key=${encodeURIComponent(key)}`;
const response = yield requestUtils_1.retryTypedResponse('listCache', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); }));
if (response.statusCode === 200) {
const cacheListResult = response.result;
const totalCount = cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.totalCount;
if (totalCount && totalCount > 0) {
core.debug(`No matching cache found for cache key '${key}', version '${version} and scope ${process.env['GITHUB_REF']}. There exist one or more cache(s) with similar key but they have different version or scope. See more info on cache matching here: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key \nOther caches with similar key:`);
for (const cacheEntry of (cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.artifactCaches) || []) {
core.debug(`Cache Key: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheKey}, Cache Version: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheVersion}, Cache Scope: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.scope}, Cache Created: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.creationTime}`);
}
}
}
});
}
function downloadCache(archiveLocation, archivePath, options) {
return __awaiter(this, void 0, void 0, function* () {
const archiveUrl = new url_1.URL(archiveLocation);
@ -3500,7 +3553,7 @@ exports.downloadCache = downloadCache;
function reserveCache(key, paths, options) {
return __awaiter(this, void 0, void 0, function* () {
const httpClient = createHttpClient();
const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod);
const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive);
const reserveCacheRequest = {
key,
version,
@ -4980,7 +5033,8 @@ var Inputs;
Inputs["Key"] = "key";
Inputs["Path"] = "path";
Inputs["RestoreKeys"] = "restore-keys";
Inputs["UploadChunkSize"] = "upload-chunk-size"; // Input for cache, save action
Inputs["UploadChunkSize"] = "upload-chunk-size";
Inputs["EnableCrossOsArchive"] = "enableCrossOsArchive"; // Input for cache, restore, save action
})(Inputs = exports.Inputs || (exports.Inputs = {}));
var Outputs;
(function (Outputs) {
@ -38134,12 +38188,14 @@ var __importStar = (this && this.__importStar) || function (mod) {
};
Object.defineProperty(exports, "__esModule", { value: true });
const exec_1 = __webpack_require__(986);
const core_1 = __webpack_require__(470);
const io = __importStar(__webpack_require__(1));
const fs_1 = __webpack_require__(747);
const path = __importStar(__webpack_require__(622));
const utils = __importStar(__webpack_require__(15));
const constants_1 = __webpack_require__(931);
const IS_WINDOWS = process.platform === 'win32';
core_1.exportVariable('MSYS', 'winsymlinks:nativestrict');
// Returns tar path and type: BSD or GNU
function getTarPath() {
return __awaiter(this, void 0, void 0, function* () {
@ -38266,7 +38322,7 @@ function getDecompressionProgram(tarPath, compressionMethod, archivePath) {
case constants_1.CompressionMethod.Zstd:
return BSD_TAR_ZSTD
? [
'zstd -d --long=30 -o',
'zstd -d --long=30 --force -o',
constants_1.TarFilename,
archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/')
]
@ -38277,7 +38333,7 @@ function getDecompressionProgram(tarPath, compressionMethod, archivePath) {
case constants_1.CompressionMethod.ZstdWithoutLong:
return BSD_TAR_ZSTD
? [
'zstd -d -o',
'zstd -d --force -o',
constants_1.TarFilename,
archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/')
]
@ -38303,7 +38359,7 @@ function getCompressionProgram(tarPath, compressionMethod) {
case constants_1.CompressionMethod.Zstd:
return BSD_TAR_ZSTD
? [
'zstd -T0 --long=30 -o',
'zstd -T0 --long=30 --force -o',
cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
constants_1.TarFilename
]
@ -38314,7 +38370,7 @@ function getCompressionProgram(tarPath, compressionMethod) {
case constants_1.CompressionMethod.ZstdWithoutLong:
return BSD_TAR_ZSTD
? [
'zstd -T0 -o',
'zstd -T0 --force -o',
cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
constants_1.TarFilename
]
@ -38597,7 +38653,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.isCacheFeatureAvailable = exports.getInputAsInt = exports.getInputAsArray = exports.isValidEvent = exports.logWarning = exports.isExactKeyMatch = exports.isGhes = void 0;
exports.isCacheFeatureAvailable = exports.getInputAsBool = exports.getInputAsInt = exports.getInputAsArray = exports.isValidEvent = exports.logWarning = exports.isExactKeyMatch = exports.isGhes = void 0;
const cache = __importStar(__webpack_require__(692));
const core = __importStar(__webpack_require__(470));
const constants_1 = __webpack_require__(196);
@ -38640,6 +38696,11 @@ function getInputAsInt(name, options) {
return value;
}
exports.getInputAsInt = getInputAsInt;
function getInputAsBool(name, options) {
const result = core.getInput(name, options);
return result.toLowerCase() === "true";
}
exports.getInputAsBool = getInputAsBool;
function isCacheFeatureAvailable() {
if (cache.isFeatureAvailable()) {
return true;
@ -41092,6 +41153,7 @@ const utils = __importStar(__webpack_require__(443));
process.on("uncaughtException", e => utils.logWarning(e.message));
function saveImpl(stateProvider) {
return __awaiter(this, void 0, void 0, function* () {
let cacheId = -1;
try {
if (!utils.isCacheFeatureAvailable()) {
return;
@ -41118,9 +41180,8 @@ function saveImpl(stateProvider) {
const cachePaths = utils.getInputAsArray(constants_1.Inputs.Path, {
required: true
});
const cacheId = yield cache.saveCache(cachePaths, primaryKey, {
uploadChunkSize: utils.getInputAsInt(constants_1.Inputs.UploadChunkSize)
});
const enableCrossOsArchive = utils.getInputAsBool(constants_1.Inputs.EnableCrossOsArchive);
cacheId = yield cache.saveCache(cachePaths, primaryKey, { uploadChunkSize: utils.getInputAsInt(constants_1.Inputs.UploadChunkSize) }, enableCrossOsArchive);
if (cacheId != -1) {
core.info(`Cache saved with key: ${primaryKey}`);
}
@ -41128,6 +41189,7 @@ function saveImpl(stateProvider) {
catch (error) {
utils.logWarning(error.message);
}
return cacheId;
});
}
exports.default = saveImpl;
@ -47259,7 +47321,6 @@ const path = __importStar(__webpack_require__(622));
const utils = __importStar(__webpack_require__(15));
const cacheHttpClient = __importStar(__webpack_require__(114));
const tar_1 = __webpack_require__(434);
const constants_1 = __webpack_require__(931);
class ValidationError extends Error {
constructor(message) {
super(message);
@ -47306,9 +47367,10 @@ exports.isFeatureAvailable = isFeatureAvailable;
* @param primaryKey an explicit key for restoring the cache
* @param restoreKeys an optional ordered list of keys to use for restoring the cache if no cache hit occurred for key
* @param downloadOptions cache download options
* @param enableCrossOsArchive an optional boolean enabled to restore on windows any cache created on any platform
* @returns string returns the key for the cache hit, otherwise returns undefined
*/
function restoreCache(paths, primaryKey, restoreKeys, options) {
function restoreCache(paths, primaryKey, restoreKeys, options, enableCrossOsArchive = false) {
return __awaiter(this, void 0, void 0, function* () {
checkPaths(paths);
restoreKeys = restoreKeys || [];
@ -47321,31 +47383,17 @@ function restoreCache(paths, primaryKey, restoreKeys, options) {
for (const key of keys) {
checkKey(key);
}
let cacheEntry;
let compressionMethod = yield utils.getCompressionMethod();
const compressionMethod = yield utils.getCompressionMethod();
let archivePath = '';
try {
// path are needed to compute version
cacheEntry = yield cacheHttpClient.getCacheEntry(keys, paths, {
compressionMethod
const cacheEntry = yield cacheHttpClient.getCacheEntry(keys, paths, {
compressionMethod,
enableCrossOsArchive
});
if (!(cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.archiveLocation)) {
// This is to support the old cache entry created by gzip on windows.
if (process.platform === 'win32' &&
compressionMethod !== constants_1.CompressionMethod.Gzip) {
compressionMethod = constants_1.CompressionMethod.Gzip;
cacheEntry = yield cacheHttpClient.getCacheEntry(keys, paths, {
compressionMethod
});
if (!(cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.archiveLocation)) {
return undefined;
}
core.debug("Couldn't find cache entry with zstd compression, falling back to gzip compression.");
}
else {
// Cache not found
return undefined;
}
// Cache not found
return undefined;
}
archivePath = path.join(yield utils.createTempDirectory(), utils.getCacheFileName(compressionMethod));
core.debug(`Archive Path: ${archivePath}`);
@ -47388,10 +47436,11 @@ exports.restoreCache = restoreCache;
*
* @param paths a list of file paths to be cached
* @param key an explicit key for restoring the cache
* @param enableCrossOsArchive an optional boolean enabled to save cache on windows which could be restored on any platform
* @param options cache upload options
* @returns number returns cacheId if the cache was saved successfully and throws an error if save fails
*/
function saveCache(paths, key, options) {
function saveCache(paths, key, options, enableCrossOsArchive = false) {
var _a, _b, _c, _d, _e;
return __awaiter(this, void 0, void 0, function* () {
checkPaths(paths);
@ -47422,6 +47471,7 @@ function saveCache(paths, key, options) {
core.debug('Reserving Cache');
const reserveCacheResponse = yield cacheHttpClient.reserveCache(key, paths, {
compressionMethod,
enableCrossOsArchive,
cacheSize: archiveFileSize
});
if ((_a = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.result) === null || _a === void 0 ? void 0 : _a.cacheId) {

103
dist/save/index.js vendored
View File

@ -3383,7 +3383,6 @@ const crypto = __importStar(__webpack_require__(417));
const fs = __importStar(__webpack_require__(747));
const url_1 = __webpack_require__(835);
const utils = __importStar(__webpack_require__(15));
const constants_1 = __webpack_require__(931);
const downloadUtils_1 = __webpack_require__(251);
const options_1 = __webpack_require__(538);
const requestUtils_1 = __webpack_require__(899);
@ -3413,10 +3412,17 @@ function createHttpClient() {
const bearerCredentialHandler = new auth_1.BearerCredentialHandler(token);
return new http_client_1.HttpClient('actions/cache', [bearerCredentialHandler], getRequestOptions());
}
function getCacheVersion(paths, compressionMethod) {
const components = paths.concat(!compressionMethod || compressionMethod === constants_1.CompressionMethod.Gzip
? []
: [compressionMethod]);
function getCacheVersion(paths, compressionMethod, enableCrossOsArchive = false) {
const components = paths;
// Add compression method to cache version to restore
// compressed cache as per compression method
if (compressionMethod) {
components.push(compressionMethod);
}
// Only check for windows platforms if enableCrossOsArchive is false
if (process.platform === 'win32' && !enableCrossOsArchive) {
components.push('windows-only');
}
// Add salt to cache version to support breaking changes in cache entry
components.push(versionSalt);
return crypto
@ -3428,11 +3434,15 @@ exports.getCacheVersion = getCacheVersion;
function getCacheEntry(keys, paths, options) {
return __awaiter(this, void 0, void 0, function* () {
const httpClient = createHttpClient();
const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod);
const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive);
const resource = `cache?keys=${encodeURIComponent(keys.join(','))}&version=${version}`;
const response = yield requestUtils_1.retryTypedResponse('getCacheEntry', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); }));
// Cache not found
if (response.statusCode === 204) {
// Cache not found
// List cache for primary key only if cache miss occurs
if (core.isDebug()) {
yield printCachesListForDiagnostics(keys[0], httpClient, version);
}
return null;
}
if (!requestUtils_1.isSuccessStatusCode(response.statusCode)) {
@ -3451,6 +3461,22 @@ function getCacheEntry(keys, paths, options) {
});
}
exports.getCacheEntry = getCacheEntry;
function printCachesListForDiagnostics(key, httpClient, version) {
return __awaiter(this, void 0, void 0, function* () {
const resource = `caches?key=${encodeURIComponent(key)}`;
const response = yield requestUtils_1.retryTypedResponse('listCache', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); }));
if (response.statusCode === 200) {
const cacheListResult = response.result;
const totalCount = cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.totalCount;
if (totalCount && totalCount > 0) {
core.debug(`No matching cache found for cache key '${key}', version '${version} and scope ${process.env['GITHUB_REF']}. There exist one or more cache(s) with similar key but they have different version or scope. See more info on cache matching here: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key \nOther caches with similar key:`);
for (const cacheEntry of (cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.artifactCaches) || []) {
core.debug(`Cache Key: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheKey}, Cache Version: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheVersion}, Cache Scope: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.scope}, Cache Created: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.creationTime}`);
}
}
}
});
}
function downloadCache(archiveLocation, archivePath, options) {
return __awaiter(this, void 0, void 0, function* () {
const archiveUrl = new url_1.URL(archiveLocation);
@ -3471,7 +3497,7 @@ exports.downloadCache = downloadCache;
function reserveCache(key, paths, options) {
return __awaiter(this, void 0, void 0, function* () {
const httpClient = createHttpClient();
const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod);
const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive);
const reserveCacheRequest = {
key,
version,
@ -4951,7 +4977,8 @@ var Inputs;
Inputs["Key"] = "key";
Inputs["Path"] = "path";
Inputs["RestoreKeys"] = "restore-keys";
Inputs["UploadChunkSize"] = "upload-chunk-size"; // Input for cache, save action
Inputs["UploadChunkSize"] = "upload-chunk-size";
Inputs["EnableCrossOsArchive"] = "enableCrossOsArchive"; // Input for cache, restore, save action
})(Inputs = exports.Inputs || (exports.Inputs = {}));
var Outputs;
(function (Outputs) {
@ -38105,12 +38132,14 @@ var __importStar = (this && this.__importStar) || function (mod) {
};
Object.defineProperty(exports, "__esModule", { value: true });
const exec_1 = __webpack_require__(986);
const core_1 = __webpack_require__(470);
const io = __importStar(__webpack_require__(1));
const fs_1 = __webpack_require__(747);
const path = __importStar(__webpack_require__(622));
const utils = __importStar(__webpack_require__(15));
const constants_1 = __webpack_require__(931);
const IS_WINDOWS = process.platform === 'win32';
core_1.exportVariable('MSYS', 'winsymlinks:nativestrict');
// Returns tar path and type: BSD or GNU
function getTarPath() {
return __awaiter(this, void 0, void 0, function* () {
@ -38237,7 +38266,7 @@ function getDecompressionProgram(tarPath, compressionMethod, archivePath) {
case constants_1.CompressionMethod.Zstd:
return BSD_TAR_ZSTD
? [
'zstd -d --long=30 -o',
'zstd -d --long=30 --force -o',
constants_1.TarFilename,
archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/')
]
@ -38248,7 +38277,7 @@ function getDecompressionProgram(tarPath, compressionMethod, archivePath) {
case constants_1.CompressionMethod.ZstdWithoutLong:
return BSD_TAR_ZSTD
? [
'zstd -d -o',
'zstd -d --force -o',
constants_1.TarFilename,
archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/')
]
@ -38274,7 +38303,7 @@ function getCompressionProgram(tarPath, compressionMethod) {
case constants_1.CompressionMethod.Zstd:
return BSD_TAR_ZSTD
? [
'zstd -T0 --long=30 -o',
'zstd -T0 --long=30 --force -o',
cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
constants_1.TarFilename
]
@ -38285,7 +38314,7 @@ function getCompressionProgram(tarPath, compressionMethod) {
case constants_1.CompressionMethod.ZstdWithoutLong:
return BSD_TAR_ZSTD
? [
'zstd -T0 -o',
'zstd -T0 --force -o',
cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
constants_1.TarFilename
]
@ -38568,7 +38597,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.isCacheFeatureAvailable = exports.getInputAsInt = exports.getInputAsArray = exports.isValidEvent = exports.logWarning = exports.isExactKeyMatch = exports.isGhes = void 0;
exports.isCacheFeatureAvailable = exports.getInputAsBool = exports.getInputAsInt = exports.getInputAsArray = exports.isValidEvent = exports.logWarning = exports.isExactKeyMatch = exports.isGhes = void 0;
const cache = __importStar(__webpack_require__(692));
const core = __importStar(__webpack_require__(470));
const constants_1 = __webpack_require__(196);
@ -38611,6 +38640,11 @@ function getInputAsInt(name, options) {
return value;
}
exports.getInputAsInt = getInputAsInt;
function getInputAsBool(name, options) {
const result = core.getInput(name, options);
return result.toLowerCase() === "true";
}
exports.getInputAsBool = getInputAsBool;
function isCacheFeatureAvailable() {
if (cache.isFeatureAvailable()) {
return true;
@ -41063,6 +41097,7 @@ const utils = __importStar(__webpack_require__(443));
process.on("uncaughtException", e => utils.logWarning(e.message));
function saveImpl(stateProvider) {
return __awaiter(this, void 0, void 0, function* () {
let cacheId = -1;
try {
if (!utils.isCacheFeatureAvailable()) {
return;
@ -41089,9 +41124,8 @@ function saveImpl(stateProvider) {
const cachePaths = utils.getInputAsArray(constants_1.Inputs.Path, {
required: true
});
const cacheId = yield cache.saveCache(cachePaths, primaryKey, {
uploadChunkSize: utils.getInputAsInt(constants_1.Inputs.UploadChunkSize)
});
const enableCrossOsArchive = utils.getInputAsBool(constants_1.Inputs.EnableCrossOsArchive);
cacheId = yield cache.saveCache(cachePaths, primaryKey, { uploadChunkSize: utils.getInputAsInt(constants_1.Inputs.UploadChunkSize) }, enableCrossOsArchive);
if (cacheId != -1) {
core.info(`Cache saved with key: ${primaryKey}`);
}
@ -41099,6 +41133,7 @@ function saveImpl(stateProvider) {
catch (error) {
utils.logWarning(error.message);
}
return cacheId;
});
}
exports.default = saveImpl;
@ -47259,7 +47294,6 @@ const path = __importStar(__webpack_require__(622));
const utils = __importStar(__webpack_require__(15));
const cacheHttpClient = __importStar(__webpack_require__(114));
const tar_1 = __webpack_require__(434);
const constants_1 = __webpack_require__(931);
class ValidationError extends Error {
constructor(message) {
super(message);
@ -47306,9 +47340,10 @@ exports.isFeatureAvailable = isFeatureAvailable;
* @param primaryKey an explicit key for restoring the cache
* @param restoreKeys an optional ordered list of keys to use for restoring the cache if no cache hit occurred for key
* @param downloadOptions cache download options
* @param enableCrossOsArchive an optional boolean enabled to restore on windows any cache created on any platform
* @returns string returns the key for the cache hit, otherwise returns undefined
*/
function restoreCache(paths, primaryKey, restoreKeys, options) {
function restoreCache(paths, primaryKey, restoreKeys, options, enableCrossOsArchive = false) {
return __awaiter(this, void 0, void 0, function* () {
checkPaths(paths);
restoreKeys = restoreKeys || [];
@ -47321,31 +47356,17 @@ function restoreCache(paths, primaryKey, restoreKeys, options) {
for (const key of keys) {
checkKey(key);
}
let cacheEntry;
let compressionMethod = yield utils.getCompressionMethod();
const compressionMethod = yield utils.getCompressionMethod();
let archivePath = '';
try {
// path are needed to compute version
cacheEntry = yield cacheHttpClient.getCacheEntry(keys, paths, {
compressionMethod
const cacheEntry = yield cacheHttpClient.getCacheEntry(keys, paths, {
compressionMethod,
enableCrossOsArchive
});
if (!(cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.archiveLocation)) {
// This is to support the old cache entry created by gzip on windows.
if (process.platform === 'win32' &&
compressionMethod !== constants_1.CompressionMethod.Gzip) {
compressionMethod = constants_1.CompressionMethod.Gzip;
cacheEntry = yield cacheHttpClient.getCacheEntry(keys, paths, {
compressionMethod
});
if (!(cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.archiveLocation)) {
return undefined;
}
core.debug("Couldn't find cache entry with zstd compression, falling back to gzip compression.");
}
else {
// Cache not found
return undefined;
}
// Cache not found
return undefined;
}
archivePath = path.join(yield utils.createTempDirectory(), utils.getCacheFileName(compressionMethod));
core.debug(`Archive Path: ${archivePath}`);
@ -47388,10 +47409,11 @@ exports.restoreCache = restoreCache;
*
* @param paths a list of file paths to be cached
* @param key an explicit key for restoring the cache
* @param enableCrossOsArchive an optional boolean enabled to save cache on windows which could be restored on any platform
* @param options cache upload options
* @returns number returns cacheId if the cache was saved successfully and throws an error if save fails
*/
function saveCache(paths, key, options) {
function saveCache(paths, key, options, enableCrossOsArchive = false) {
var _a, _b, _c, _d, _e;
return __awaiter(this, void 0, void 0, function* () {
checkPaths(paths);
@ -47422,6 +47444,7 @@ function saveCache(paths, key, options) {
core.debug('Reserving Cache');
const reserveCacheResponse = yield cacheHttpClient.reserveCache(key, paths, {
compressionMethod,
enableCrossOsArchive,
cacheSize: archiveFileSize
});
if ((_a = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.result) === null || _a === void 0 ? void 0 : _a.cacheId) {

View File

@ -38,6 +38,7 @@
- [Swift, Objective-C - Carthage](#swift-objective-c---carthage)
- [Swift, Objective-C - CocoaPods](#swift-objective-c---cocoapods)
- [Swift - Swift Package Manager](#swift---swift-package-manager)
- [Swift - Mint](#swift---mint)
## C# - NuGet
@ -641,3 +642,18 @@ whenever possible:
restore-keys: |
${{ runner.os }}-spm-
```
## Swift - Mint
```yaml
env:
MINT_PATH: .mint/lib
MINT_LINK_PATH: .mint/bin
steps:
- uses: actions/cache@v3
with:
path: .mint
key: ${{ runner.os }}-mint-${{ hashFiles('**/Mintfile') }}
restore-keys: |
${{ runner.os }}-mint-
```

18
package-lock.json generated
View File

@ -1,15 +1,15 @@
{
"name": "cache",
"version": "3.2.0-beta.1",
"version": "3.2.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "cache",
"version": "3.2.0-beta.1",
"version": "3.2.2",
"license": "MIT",
"dependencies": {
"@actions/cache": "3.1.0-beta.3",
"@actions/cache": "^3.1.2",
"@actions/core": "^1.10.0",
"@actions/exec": "^1.1.1",
"@actions/io": "^1.1.2"
@ -36,9 +36,9 @@
}
},
"node_modules/@actions/cache": {
"version": "3.1.0-beta.3",
"resolved": "https://registry.npmjs.org/@actions/cache/-/cache-3.1.0-beta.3.tgz",
"integrity": "sha512-71S1vd0WKLbC2lAe04pCYqTLBjSa8gURtiqnVBCYAt8QVBjOfwa2D3ESf2m8K2xjUxman/Yimdp7CPJDyFnxZg==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@actions/cache/-/cache-3.1.2.tgz",
"integrity": "sha512-3XeKcXIonfIbqvW7gPm/VLOhv1RHQ1dtTgSBCH6OUhCgSTii9bEVgu0PIms7UbLnXeMCKFzECfpbud8fJEvBbQ==",
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/exec": "^1.0.1",
@ -9722,9 +9722,9 @@
},
"dependencies": {
"@actions/cache": {
"version": "3.1.0-beta.3",
"resolved": "https://registry.npmjs.org/@actions/cache/-/cache-3.1.0-beta.3.tgz",
"integrity": "sha512-71S1vd0WKLbC2lAe04pCYqTLBjSa8gURtiqnVBCYAt8QVBjOfwa2D3ESf2m8K2xjUxman/Yimdp7CPJDyFnxZg==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@actions/cache/-/cache-3.1.2.tgz",
"integrity": "sha512-3XeKcXIonfIbqvW7gPm/VLOhv1RHQ1dtTgSBCH6OUhCgSTii9bEVgu0PIms7UbLnXeMCKFzECfpbud8fJEvBbQ==",
"requires": {
"@actions/core": "^1.10.0",
"@actions/exec": "^1.0.1",

View File

@ -1,6 +1,6 @@
{
"name": "cache",
"version": "3.2.0-beta.1",
"version": "3.2.2",
"private": true,
"description": "Cache dependencies and build outputs",
"main": "dist/restore/index.js",
@ -23,7 +23,7 @@
"author": "GitHub",
"license": "MIT",
"dependencies": {
"@actions/cache": "3.1.0-beta.3",
"@actions/cache": "^3.1.2",
"@actions/core": "^1.10.0",
"@actions/exec": "^1.1.1",
"@actions/io": "^1.1.2"

View File

@ -120,7 +120,7 @@ steps:
#### Reusing primary key and restored key in the save action
Usually you may want to use same `key` in both actions/cache/restore` and `actions/cache/save` action. To achieve this, use `outputs` from the restore action to reuse the same primary key (or the key of the cache that was restored).
Usually you may want to use same `key` in both `actions/cache/restore` and `actions/cache/save` action. To achieve this, use `outputs` from the restore action to reuse the same primary key (or the key of the cache that was restored).
#### Using restore action outputs to make save action behave just like the cache action

View File

@ -11,6 +11,10 @@ inputs:
restore-keys:
description: 'An ordered list of keys to use for restoring stale cache if no cache hit occurred for key. Note `cache-hit` returns false in this case.'
required: false
enableCrossOsArchive:
description: 'An optional boolean when enabled, allows windows runners to restore caches that were saved on other platforms'
default: 'false'
required: false
outputs:
cache-hit:
description: 'A boolean value to indicate an exact match was found for the primary key'

View File

@ -54,7 +54,7 @@ Case 1: Where an user would want to reuse the key as it is
```yaml
uses: actions/cache/save@v3
with:
key: steps.restore-cache.output.key
key: ${{ steps.restore-cache.outputs.key }}
```
Case 2: Where the user would want to re-evaluate the key

View File

@ -11,6 +11,10 @@ inputs:
upload-chunk-size:
description: 'The chunk size used to split up large files during upload, in bytes'
required: false
enableCrossOsArchive:
description: 'An optional boolean when enabled, allows windows runners to save caches that can be restored on other platforms'
default: 'false'
required: false
runs:
using: 'node16'
main: '../dist/save-only/index.js'

View File

@ -2,7 +2,8 @@ export enum Inputs {
Key = "key", // Input for cache, restore, save action
Path = "path", // Input for cache, restore, save action
RestoreKeys = "restore-keys", // Input for cache, restore action
UploadChunkSize = "upload-chunk-size" // Input for cache, save action
UploadChunkSize = "upload-chunk-size", // Input for cache, save action
EnableCrossOsArchive = "enableCrossOsArchive" // Input for cache, restore, save action
}
export enum Outputs {

View File

@ -31,11 +31,16 @@ async function restoreImpl(
const cachePaths = utils.getInputAsArray(Inputs.Path, {
required: true
});
const enableCrossOsArchive = utils.getInputAsBool(
Inputs.EnableCrossOsArchive
);
const cacheKey = await cache.restoreCache(
cachePaths,
primaryKey,
restoreKeys
restoreKeys,
{},
enableCrossOsArchive
);
if (!cacheKey) {

View File

@ -10,7 +10,8 @@ import * as utils from "./utils/actionUtils";
// throw an uncaught exception. Instead of failing this action, just warn.
process.on("uncaughtException", e => utils.logWarning(e.message));
async function saveImpl(stateProvider: IStateProvider): Promise<void> {
async function saveImpl(stateProvider: IStateProvider): Promise<number | void> {
let cacheId = -1;
try {
if (!utils.isCacheFeatureAvailable()) {
return;
@ -51,9 +52,16 @@ async function saveImpl(stateProvider: IStateProvider): Promise<void> {
required: true
});
const cacheId = await cache.saveCache(cachePaths, primaryKey, {
uploadChunkSize: utils.getInputAsInt(Inputs.UploadChunkSize)
});
const enableCrossOsArchive = utils.getInputAsBool(
Inputs.EnableCrossOsArchive
);
cacheId = await cache.saveCache(
cachePaths,
primaryKey,
{ uploadChunkSize: utils.getInputAsInt(Inputs.UploadChunkSize) },
enableCrossOsArchive
);
if (cacheId != -1) {
core.info(`Cache saved with key: ${primaryKey}`);
@ -61,6 +69,7 @@ async function saveImpl(stateProvider: IStateProvider): Promise<void> {
} catch (error: unknown) {
utils.logWarning((error as Error).message);
}
return cacheId;
}
export default saveImpl;

View File

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

View File

@ -52,6 +52,14 @@ export function getInputAsInt(
return value;
}
export function getInputAsBool(
name: string,
options?: core.InputOptions
): boolean {
const result = core.getInput(name, options);
return result.toLowerCase() === "true";
}
export function isCacheFeatureAvailable(): boolean {
if (cache.isFeatureAvailable()) {
return true;

View File

@ -13,6 +13,7 @@ interface CacheInput {
path: string;
key: string;
restoreKeys?: string[];
enableCrossOsArchive?: boolean;
}
export function setInputs(input: CacheInput): void {
@ -20,6 +21,11 @@ export function setInputs(input: CacheInput): void {
setInput(Inputs.Key, input.key);
input.restoreKeys &&
setInput(Inputs.RestoreKeys, input.restoreKeys.join("\n"));
input.enableCrossOsArchive !== undefined &&
setInput(
Inputs.EnableCrossOsArchive,
input.enableCrossOsArchive.toString()
);
}
export function clearInputs(): void {
@ -27,4 +33,5 @@ export function clearInputs(): void {
delete process.env[getInputName(Inputs.Key)];
delete process.env[getInputName(Inputs.RestoreKeys)];
delete process.env[getInputName(Inputs.UploadChunkSize)];
delete process.env[getInputName(Inputs.EnableCrossOsArchive)];
}