Compare commits

...

37 Commits

Author SHA1 Message Date
Gregorio Litenstein
87e656e41d
Merge 11a6324cc7 into 640a1c2554 2025-06-19 09:04:22 +02:00
Ben De St Paer-Gotch
640a1c2554
Merge pull request #1620 from actions/nebuk89-patch-1
Update README.md
2025-06-17 14:18:22 +01:00
Ben De St Paer-Gotch
c45d39173a
Update README.md 2025-06-16 10:08:03 +01:00
Gregorio Litenstein
11a6324cc7
Fix script invocations, lint and format.
Signed-off-by: Gregorio Litenstein <g.litenstein@gmail.com>
2025-04-25 20:58:15 -04:00
Gregorio Litenstein
8ac3d1967f
licenses
Signed-off-by: Gregorio Litenstein <g.litenstein@gmail.com>
2025-04-25 18:29:49 -04:00
Gregorio Litenstein
ceefdff90e
Update @octokit/action to 5.x
Signed-off-by: Gregorio Litenstein <g.litenstein@gmail.com>
2025-04-25 18:29:49 -04:00
Gregorio Litenstein
0e801000fd
Update licensed cache
Signed-off-by: Gregorio Litenstein <g.litenstein@gmail.com>
2025-04-25 17:42:27 -04:00
Gregorio Litenstein
da1b6c90c6
Update tests for deleteCacheByKey.
And rebuild.

Signed-off-by: Gregorio Litenstein <g.litenstein@gmail.com>
2025-04-25 17:38:55 -04:00
Gregorio Litenstein
0417c21165
Update documentation and licenses
Signed-off-by: Gregorio Litenstein <g.litenstein@gmail.com>
2025-04-25 17:38:13 -04:00
Gregorio Litenstein
203bc27916
Update some dependencies.
Signed-off-by: Gregorio Litenstein <g.litenstein@gmail.com>
2025-04-25 17:38:13 -04:00
Gregorio Litenstein
1328a18e7e
Don’t accidentally deleting cache from base branch
Signed-off-by: Gregorio Litenstein <g.litenstein@gmail.com>
2025-04-25 17:37:22 -04:00
Gregorio Litenstein
d35302b225
Allow refreshing cache also with granular save. 2025-04-25 17:37:22 -04:00
Gregorio Litenstein
34ae092a8c
Add tests for cache refreshing. 2025-04-25 17:37:17 -04:00
Gregorio Litenstein
39c1f23499
Allow updating caches 2025-04-25 17:16:35 -04:00
Salman Chishti
5a3ec84eff
Merge pull request #1577 from salmanmkc/salmanmkc/4-test
Update to use @actions/cache 4.0.3 package & prepare for new release
2025-03-19 18:06:32 +00:00
Salman Chishti
7de21022a7 Update releases.md 2025-03-18 08:00:50 -07:00
Salman Chishti
76d40dd347 Update to use the latest version of the cache package to obfuscate the SAS 2025-03-18 07:58:36 -07:00
Salman Chishti
76dd5eb692 update cache with main 2025-03-18 03:43:02 -07:00
Salman Chishti
8c80c27c5e new package 2025-03-13 04:55:45 -07:00
Salman Chishti
45cfd0e7ff updates 2025-03-13 04:30:51 -07:00
Salman Chishti
edd449b9cf updated cache with latest changes 2025-03-12 03:22:00 -07:00
Salman Chishti
0576707e37 latest test before pr 2025-03-10 06:54:42 -07:00
Salman Chishti
3105dc9754 update 2025-03-10 04:27:54 -07:00
Salman Chishti
9450d42d15 mask 2025-03-10 04:24:44 -07:00
Salman Chishti
7d05b27fb9 update cache package to mask whole sas to the end of the line 2025-03-10 04:20:14 -07:00
Salman Chishti
507b84c6a6 artifact changes 2025-03-07 08:47:30 -08:00
Salman Chishti
f248408e15 type 2025-03-07 08:12:07 -08:00
Salman Chishti
c30eb5542e debugging 2025-03-07 08:06:30 -08:00
Salman Chishti
5b6ae99d8b mask whole url 2025-03-07 08:02:01 -08:00
Salman Chishti
eca7c65a55 changed 2025-03-07 08:00:03 -08:00
Salman Chishti
1b5d75f5cf add changes 2025-03-07 07:56:21 -08:00
Rob Herley
d4323d4df1
Merge pull request #1560 from actions/robherley/v4.2.2
Bump @actions/cache to v4.0.2
2025-02-27 09:47:05 -05:00
Rob Herley
da26677639
bump @actions/cache to v4.0.2, prep for v4.2.2 release 2025-02-26 12:45:56 -05:00
Rob Herley
7921ae235b
Merge pull request #1557 from actions/robherley/ia-workflow-released
Update publish-immutable-actions.yml
2025-02-18 17:47:05 -05:00
Rob Herley
3937731706
Update publish-immutable-actions.yml 2025-02-18 17:43:49 -05:00
Rob Herley
0c907a75c2
Merge pull request #1554 from actions/robherley/v4.2.1
Bump @actions/cache to v4.0.1
2025-02-18 12:38:31 -05:00
Rob Herley
710893c236
bump @actions/cache to v4.0.1 2025-02-14 13:20:28 -05:00
111 changed files with 242262 additions and 175050 deletions

View File

@ -2,7 +2,7 @@ name: 'Publish Immutable Action Version'
on:
release:
types: [published]
types: [released]
jobs:
publish:

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.

BIN
.licenses/npm/@azure/core-xml.dep.yml generated Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
.licenses/npm/@fastify/busboy.dep.yml generated Normal file

Binary file not shown.

BIN
.licenses/npm/@octokit/action.dep.yml generated Normal file

Binary file not shown.

BIN
.licenses/npm/@octokit/auth-action.dep.yml generated Normal file

Binary file not shown.

BIN
.licenses/npm/@octokit/core.dep.yml generated Normal file

Binary file not shown.

BIN
.licenses/npm/@octokit/endpoint.dep.yml generated Normal file

Binary file not shown.

BIN
.licenses/npm/@octokit/graphql.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.

BIN
.licenses/npm/@octokit/request.dep.yml generated Normal file

Binary file not shown.

BIN
.licenses/npm/@octokit/tsconfig.dep.yml generated Normal file

Binary file not shown.

Binary file not shown.

BIN
.licenses/npm/@octokit/types-9.3.2.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.

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/debug.dep.yml generated Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
.licenses/npm/fast-xml-parser.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.

BIN
.licenses/npm/https-proxy-agent.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/ms.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.

Binary file not shown.

BIN
.licenses/npm/strnum.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/undici.dep.yml generated Normal file

Binary file not shown.

BIN
.licenses/npm/universal-user-agent.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.

View File

@ -7,5 +7,6 @@
"trailingComma": "none",
"bracketSpacing": true,
"arrowParens": "avoid",
"parser": "typescript"
"parser": "typescript",
"endOfLine": "lf"
}

View File

@ -38,6 +38,7 @@ Read more about the change & access the migration guide: [reference to the annou
### v3
* Added a workaround to allow updating/refreshing existing caches, via the `refresh-cache` option and requiring a valid Github API token.
* Integrated with the new cache service (v2) APIs.
* Added support for caching in GHES 3.5+.
* Fixed download issue for files > 2GB during restore.
@ -76,10 +77,12 @@ If you are using a `self-hosted` Windows runner, `GNU tar` and `zstd` are requir
* `enableCrossOsArchive` - An optional boolean when enabled, allows Windows runners to save or restore caches that can be restored or saved respectively on other platforms. Default: `false`
* `fail-on-cache-miss` - Fail the workflow if cache entry is not found. Default: `false`
* `lookup-only` - If true, only checks if cache entry exists and skips download. Does not change save cache behavior. Default: `false`
* `refresh-cache` - An optional boolean, when enabled it will result in a matched key being deleted after being restored, allowing it to be reused with refreshed/updated content. Default: false
#### 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)
* `GITHUB_TOKEN` - A Github API token, required for authenticating to the API when the `refresh-cache` option is enabled.
### Outputs
@ -335,9 +338,23 @@ There are a number of community practices/workarounds to fulfill specific requir
Please note that Windows environment variables (like `%LocalAppData%`) will NOT be expanded by this action. Instead, prefer using `~` in your paths which will expand to the HOME directory. For example, instead of `%LocalAppData%`, use `~\AppData\Local`. For a list of supported default environment variables, see the [Learn GitHub Actions: Variables](https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables) page.
## Contributing
## Note
We would love for you to contribute to `actions/cache`. Pull requests are welcome! Please see the [CONTRIBUTING.md](CONTRIBUTING.md) for more information.
Thank you for your interest in this GitHub repo, however, right now we are not taking contributions.
We continue to focus our resources on strategic areas that help our customers be successful while making developers' lives easier. While GitHub Actions remains a key part of this vision, we are allocating resources towards other areas of Actions and are not taking contributions to this repository at this time. The GitHub public roadmap is the best place to follow along for any updates on features were working on and what stage theyre in.
We are taking the following steps to better direct requests related to GitHub Actions, including:
1. We will be directing questions and support requests to our [Community Discussions area](https://github.com/orgs/community/discussions/categories/actions)
2. High Priority bugs can be reported through Community Discussions or you can report these to our support team https://support.github.com/contact/bug-report.
3. Security Issues should be handled as per our [security.md](SECURITY.md).
We will still provide security updates for this project and fix major breaking changes during this time.
You are welcome to still raise bugs in this repo.
## License

View File

@ -1,5 +1,17 @@
# Releases
### 4.2.3
- Bump `@actions/cache` to v4.0.3 (obfuscates SAS token in debug logs for cache entries)
### 4.2.2
- Bump `@actions/cache` to v4.0.2
### 4.2.1
- Bump `@actions/cache` to v4.0.1
### 4.2.0
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.

View File

@ -1,5 +1,6 @@
import * as cache from "@actions/cache";
import * as core from "@actions/core";
import nock from "nock";
import { Events, RefKey } from "../src/constants";
import * as actionUtils from "../src/utils/actionUtils";
@ -12,9 +13,13 @@ let pristineEnv: NodeJS.ProcessEnv;
beforeAll(() => {
pristineEnv = process.env;
nock.disableNetConnect();
jest.spyOn(core, "getInput").mockImplementation((name, options) => {
return jest.requireActual("@actions/core").getInput(name, options);
});
testUtils.mockServer.listen({
onUnhandledRequest: "warn"
});
});
beforeEach(() => {
@ -22,10 +27,15 @@ beforeEach(() => {
process.env = pristineEnv;
delete process.env[Events.Key];
delete process.env[RefKey];
delete process.env["GITHUB_REPOSITORY"];
delete process.env["GITHUB_TOKEN"];
delete process.env["GITHUB_ACTION"];
});
afterAll(() => {
process.env = pristineEnv;
testUtils.mockServer.close();
nock.enableNetConnect();
});
test("isGhes returns true if server url is not github.com", () => {
@ -203,6 +213,133 @@ test("getInputAsBool throws if required and value missing", () => {
).toThrowError();
});
test("deleteCacheByKey produces 'HttpError: 404' when cache is not found.", async () => {
const event = Events.Push;
process.env["GITHUB_REPOSITORY"] = "owner/repo";
process.env["GITHUB_TOKEN"] =
"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";
process.env["GITHUB_ACTION"] = "__owner___run-repo";
process.env[Events.Key] = event;
process.env[RefKey] = "ref/heads/feature";
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const response = await actionUtils.deleteCacheByKey(
testUtils.failureCacheKey,
"owner",
"repo"
);
expect(logWarningMock).toHaveBeenCalledWith(
expect.stringMatching(/404: Not Found/i)
);
expect(response).toBe(undefined);
});
test("deleteCacheByKey does not delete anything if it finds more than one entry for the given key.", async () => {
const event = Events.Push;
process.env["GITHUB_REPOSITORY"] = "owner/repo";
process.env["GITHUB_TOKEN"] =
"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";
process.env["GITHUB_ACTION"] = "__owner___run-repo";
process.env[Events.Key] = event;
process.env[RefKey] = "";
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const response = await actionUtils.deleteCacheByKey(
testUtils.failureCacheKey,
"owner",
"repo"
);
expect(logWarningMock).toHaveBeenCalledWith(
`More than one cache entry found for key ${testUtils.failureCacheKey}`
);
expect(response).toBe(undefined);
});
test("deleteCacheByKey does not delete anything if the key matches a cache belonging to another ref.", async () => {
const event = Events.Push;
process.env["GITHUB_REPOSITORY"] = "owner/repo";
process.env["GITHUB_TOKEN"] =
"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";
process.env["GITHUB_ACTION"] = "__owner___run-repo";
process.env[Events.Key] = event;
process.env[RefKey] = "ref/heads/feature";
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const response = await actionUtils.deleteCacheByKey(
testUtils.wrongRefCacheKey,
"owner",
"repo"
);
expect(logWarningMock).toHaveBeenCalledWith(
`No cache entries for key ${testUtils.wrongRefCacheKey} belong to gitref ${process.env[RefKey]}.`
);
expect(response).toBe(undefined);
});
test("deleteCacheByKey produces 'HttpError: 404' when cache is not found.", async () => {
const event = Events.Push;
process.env["GITHUB_REPOSITORY"] = "owner/repo";
process.env["GITHUB_TOKEN"] =
"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";
process.env["GITHUB_ACTION"] = "__owner___run-repo";
process.env[Events.Key] = event;
process.env[RefKey] = "ref/heads/feature";
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const response = await actionUtils.deleteCacheByKey(
testUtils.failureCacheKey,
"owner",
"repo"
);
expect(logWarningMock).toHaveBeenCalledWith(
expect.stringMatching(/404: Not Found/i)
);
expect(response).toBe(undefined);
});
test("deleteCacheByKey produces 'HttpError: 401' on an invalid non-mocked request.", async () => {
const event = Events.Push;
process.env["GITHUB_REPOSITORY"] = "owner/repo";
process.env["GITHUB_TOKEN"] =
"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";
process.env["GITHUB_ACTION"] = "__owner___run-repo";
process.env[Events.Key] = event;
process.env[RefKey] = "ref/heads/feature";
await nock.enableNetConnect();
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const response = await actionUtils.deleteCacheByKey(
testUtils.passThroughCacheKey,
"owner",
"repo"
);
expect(logWarningMock).toHaveBeenCalledWith(
expect.stringMatching(/401: Bad Credentials/i)
);
expect(response).toBe(undefined);
nock.disableNetConnect();
});
test("deleteCacheByKey returns 204 / No Content when successful.", async () => {
const event = Events.Push;
process.env["GITHUB_REPOSITORY"] = "owner/repo";
process.env["GITHUB_TOKEN"] =
"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";
process.env["GITHUB_ACTION"] = "__owner___run-repo";
process.env[Events.Key] = event;
process.env[RefKey] = "ref/heads/feature";
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const response = await actionUtils.deleteCacheByKey(
testUtils.successCacheKey,
"owner",
"repo"
);
expect(response).toBe(204);
expect(logWarningMock).toHaveBeenCalledTimes(0);
});
test("isCacheFeatureAvailable for ac enabled", () => {
jest.spyOn(cache, "isFeatureAvailable").mockImplementation(() => true);

View File

@ -1,5 +1,6 @@
import * as cache from "@actions/cache";
import * as core from "@actions/core";
import nock from "nock";
import { Events, RefKey } from "../src/constants";
import { restoreRun } from "../src/restoreImpl";
@ -9,6 +10,7 @@ import * as testUtils from "../src/utils/testUtils";
jest.mock("../src/utils/actionUtils");
beforeAll(() => {
nock.disableNetConnect();
jest.spyOn(actionUtils, "isExactKeyMatch").mockImplementation(
(key, cacheResult) => {
const actualUtils = jest.requireActual("../src/utils/actionUtils");
@ -53,6 +55,10 @@ afterEach(() => {
delete process.env[RefKey];
});
afterAll(() => {
nock.enableNetConnect();
});
test("restore with no cache found", async () => {
const path = "node_modules";
const key = "node-test";

View File

@ -1,5 +1,6 @@
import * as cache from "@actions/cache";
import * as core from "@actions/core";
import nock from "nock";
import { Events, Inputs, RefKey } from "../src/constants";
import { restoreImpl } from "../src/restoreImpl";
@ -10,6 +11,7 @@ import * as testUtils from "../src/utils/testUtils";
jest.mock("../src/utils/actionUtils");
beforeAll(() => {
nock.disableNetConnect();
jest.spyOn(actionUtils, "isExactKeyMatch").mockImplementation(
(key, cacheResult) => {
const actualUtils = jest.requireActual("../src/utils/actionUtils");
@ -54,6 +56,10 @@ afterEach(() => {
delete process.env[RefKey];
});
afterAll(() => {
nock.enableNetConnect();
});
test("restore with invalid event outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");

View File

@ -1,5 +1,6 @@
import * as cache from "@actions/cache";
import * as core from "@actions/core";
import nock from "nock";
import { Events, RefKey } from "../src/constants";
import { restoreOnlyRun } from "../src/restoreImpl";
@ -9,6 +10,7 @@ import * as testUtils from "../src/utils/testUtils";
jest.mock("../src/utils/actionUtils");
beforeAll(() => {
nock.disableNetConnect();
jest.spyOn(actionUtils, "isExactKeyMatch").mockImplementation(
(key, cacheResult) => {
const actualUtils = jest.requireActual("../src/utils/actionUtils");
@ -54,6 +56,10 @@ afterEach(() => {
delete process.env[RefKey];
});
afterAll(() => {
nock.enableNetConnect();
});
test("restore with no cache found", async () => {
const path = "node_modules";
const key = "node-test";

View File

@ -1,5 +1,6 @@
import * as cache from "@actions/cache";
import * as core from "@actions/core";
import nock from "nock";
import { Events, Inputs, RefKey } from "../src/constants";
import { saveRun } from "../src/saveImpl";
@ -11,6 +12,7 @@ jest.mock("@actions/cache");
jest.mock("../src/utils/actionUtils");
beforeAll(() => {
nock.disableNetConnect();
jest.spyOn(core, "getInput").mockImplementation((name, options) => {
return jest.requireActual("@actions/core").getInput(name, options);
});
@ -73,10 +75,14 @@ afterEach(() => {
delete process.env[RefKey];
});
afterAll(() => {
nock.enableNetConnect();
});
test("save with valid inputs uploads a cache", async () => {
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const primaryKey = testUtils.successCacheKey;
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")

View File

@ -1,9 +1,10 @@
import * as cache from "@actions/cache";
import * as core from "@actions/core";
import nock from "nock";
import { Events, Inputs, RefKey } from "../src/constants";
import { saveImpl } from "../src/saveImpl";
import { StateProvider } from "../src/stateProvider";
import { NullStateProvider, StateProvider } from "../src/stateProvider";
import * as actionUtils from "../src/utils/actionUtils";
import * as testUtils from "../src/utils/testUtils";
@ -12,6 +13,19 @@ jest.mock("@actions/cache");
jest.mock("../src/utils/actionUtils");
beforeAll(() => {
nock.disableNetConnect();
testUtils.mockServer.listen({
onUnhandledRequest: "warn"
});
jest.spyOn(actionUtils, "deleteCacheByKey").mockImplementation(
(key: string, owner: string, repo: string) => {
return jest
.requireActual("../src/utils/actionUtils")
.deleteCacheByKey(key, owner, repo);
}
);
jest.spyOn(core, "getInput").mockImplementation((name, options) => {
return jest.requireActual("@actions/core").getInput(name, options);
});
@ -52,6 +66,14 @@ beforeAll(() => {
const actualUtils = jest.requireActual("../src/utils/actionUtils");
return actualUtils.isValidEvent();
});
jest.spyOn(actionUtils, "logWarning").mockImplementation(
(message: string) => {
return jest
.requireActual("../src/utils/actionUtils")
.logWarning(message);
}
);
});
beforeEach(() => {
@ -69,6 +91,13 @@ afterEach(() => {
testUtils.clearInputs();
delete process.env[Events.Key];
delete process.env[RefKey];
delete process.env["GITHUB_TOKEN"];
delete process.env["GITHUB_REPOSITORY"];
});
afterAll(() => {
testUtils.mockServer.close();
nock.enableNetConnect();
});
test("save with invalid event outputs warning", async () => {
@ -88,7 +117,7 @@ test("save with no primary key in state outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const savedCacheKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const savedCacheKey = testUtils.successCacheKey;
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
@ -137,7 +166,7 @@ test("save on GHES with AC available", async () => {
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => true);
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const primaryKey = testUtils.successCacheKey;
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")
@ -179,8 +208,10 @@ test("save on GHES with AC available", async () => {
test("save with exact match returns early", async () => {
const infoMock = jest.spyOn(core, "info");
const failedMock = jest.spyOn(core, "setFailed");
testUtils.setInput(Inputs.RefreshCache, "false");
const primaryKey = testUtils.successCacheKey;
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const savedCacheKey = primaryKey;
jest.spyOn(core, "getState")
@ -207,7 +238,7 @@ test("save with missing input outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const primaryKey = testUtils.successCacheKey;
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")
@ -235,7 +266,7 @@ test("save with large cache outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const primaryKey = testUtils.successCacheKey;
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")
@ -280,7 +311,7 @@ test("save with reserve cache failure outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const primaryKey = testUtils.successCacheKey;
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")
@ -327,7 +358,7 @@ test("save with server error outputs warning", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const primaryKey = testUtils.successCacheKey;
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")
@ -368,7 +399,7 @@ test("save with server error outputs warning", async () => {
test("save with valid inputs uploads a cache", async () => {
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const primaryKey = testUtils.successCacheKey;
const savedCacheKey = "Linux-node-";
jest.spyOn(core, "getState")
@ -406,3 +437,179 @@ test("save with valid inputs uploads a cache", async () => {
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save with cache hit and refresh-cache will try to delete and re-create entry", async () => {
process.env["GITHUB_REPOSITORY"] = "owner/repo";
process.env["GITHUB_TOKEN"] =
"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";
process.env["GITHUB_ACTION"] = "__owner___run-repo";
const infoMock = jest.spyOn(core, "info");
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = testUtils.successCacheKey;
const savedCacheKey = primaryKey;
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return savedCacheKey;
})
.mockImplementationOnce(() => {
return primaryKey;
});
const inputPath = "node_modules";
testUtils.setInput(Inputs.RefreshCache, "true");
testUtils.setInput(Inputs.Path, inputPath);
testUtils.setInput(Inputs.UploadChunkSize, "4000000");
const cacheId = 4;
const saveCacheMock = jest
.spyOn(cache, "saveCache")
.mockImplementationOnce(() => {
return Promise.resolve(cacheId);
});
await saveImpl(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith(
[inputPath],
primaryKey,
{
uploadChunkSize: 4000000
},
false
);
expect(logWarningMock).toHaveBeenCalledTimes(0);
expect(infoMock).toHaveBeenCalledTimes(3);
expect(infoMock).toHaveBeenNthCalledWith(
1,
`Cache hit occurred on the primary key ${primaryKey}, attempting to refresh the contents of the cache.`
);
expect(infoMock).toHaveBeenNthCalledWith(
2,
expect.stringMatching(
new RegExp(
`Succesfully deleted cache with key: ${primaryKey}, id: \\d+`
)
)
);
expect(infoMock).toHaveBeenNthCalledWith(
3,
`Cache saved with key: ${primaryKey}`
);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("Granular save will use lookup to determine if cache needs to be updated or (not) saved.", async () => {
process.env["GITHUB_REPOSITORY"] = "owner/repo";
process.env["GITHUB_TOKEN"] =
"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";
process.env["GITHUB_ACTION"] = "__owner___run-repo";
const infoMock = jest.spyOn(core, "info");
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = testUtils.successCacheKey;
const inputPath = "node_modules";
testUtils.setInput(Inputs.Key, primaryKey);
testUtils.setInput(Inputs.RefreshCache, "true");
testUtils.setInput(Inputs.Path, inputPath);
testUtils.setInput(Inputs.UploadChunkSize, "4000000");
const restoreCacheMock = jest
.spyOn(cache, "restoreCache")
.mockImplementation(() => {
return Promise.resolve(primaryKey);
});
const cacheId = 4;
const saveCacheMock = jest
.spyOn(cache, "saveCache")
.mockImplementationOnce(() => {
return Promise.resolve(cacheId);
});
await saveImpl(new NullStateProvider());
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
expect(restoreCacheMock).toHaveBeenCalledWith(
[inputPath],
primaryKey,
[],
{
lookupOnly: true
},
false
);
expect(saveCacheMock).toHaveBeenCalledTimes(1);
expect(saveCacheMock).toHaveBeenCalledWith(
[inputPath],
primaryKey,
{
uploadChunkSize: 4000000
},
false
);
expect(logWarningMock).toHaveBeenCalledTimes(0);
expect(infoMock).toHaveBeenCalledTimes(3);
expect(infoMock).toHaveBeenNthCalledWith(
1,
`Cache hit occurred on the primary key ${primaryKey}, attempting to refresh the contents of the cache.`
);
expect(infoMock).toHaveBeenNthCalledWith(
2,
expect.stringMatching(
new RegExp(
`Succesfully deleted cache with key: ${primaryKey}, id: \\d+`
)
)
);
expect(infoMock).toHaveBeenNthCalledWith(
3,
`Cache saved with key: ${primaryKey}`
);
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("save with cache hit and refresh-cache will throw a warning if there's no GITHUB_TOKEN", async () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = testUtils.successCacheKey;
const savedCacheKey = primaryKey;
const inputPath = "node_modules";
testUtils.setInput(Inputs.Path, inputPath);
testUtils.setInput(Inputs.RefreshCache, "true");
jest.spyOn(core, "getState")
// Cache Entry State
.mockImplementationOnce(() => {
return savedCacheKey;
})
// Cache Key State
.mockImplementationOnce(() => {
return primaryKey;
});
const saveCacheMock = jest.spyOn(cache, "saveCache");
await saveImpl(new StateProvider());
expect(saveCacheMock).toHaveBeenCalledTimes(0);
expect(logWarningMock).toHaveBeenCalledWith(
`Can't refresh cache, either the repository info or a valid token are missing.`
);
expect(failedMock).toHaveBeenCalledTimes(0);
});

View File

@ -1,5 +1,6 @@
import * as cache from "@actions/cache";
import * as core from "@actions/core";
import nock from "nock";
import { Events, Inputs, RefKey } from "../src/constants";
import { saveOnlyRun } from "../src/saveImpl";
@ -11,6 +12,7 @@ jest.mock("@actions/cache");
jest.mock("../src/utils/actionUtils");
beforeAll(() => {
nock.disableNetConnect();
jest.spyOn(core, "getInput").mockImplementation((name, options) => {
return jest.requireActual("@actions/core").getInput(name, options);
});
@ -73,6 +75,10 @@ afterEach(() => {
delete process.env[RefKey];
});
afterAll(() => {
nock.enableNetConnect();
});
test("save with valid inputs uploads a cache", async () => {
const failedMock = jest.spyOn(core, "setFailed");
@ -105,6 +111,45 @@ test("save with valid inputs uploads a cache", async () => {
expect(failedMock).toHaveBeenCalledTimes(0);
});
test("Granular save with refreshCache is able to save cache", async () => {
process.env["GITHUB_REPOSITORY"] = "owner/repo";
process.env["GITHUB_TOKEN"] =
"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";
process.env["GITHUB_ACTION"] = "__owner___run-repo";
const failedMock = jest.spyOn(core, "setFailed");
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
const inputPath = "node_modules";
process.env.CACHE_RESTORE_ONLY_MATCHED_KEY = primaryKey;
testUtils.setInput(Inputs.Key, primaryKey);
testUtils.setInput(Inputs.RefreshCache, "true");
testUtils.setInput(Inputs.Path, inputPath);
testUtils.setInput(Inputs.UploadChunkSize, "4000000");
const cacheId = 4;
const saveCacheMock = jest
.spyOn(cache, "saveCache")
.mockImplementationOnce(() => {
return Promise.resolve(cacheId);
});
await saveOnlyRun();
expect(saveCacheMock).toHaveBeenCalledTimes(1);
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");

View File

@ -34,6 +34,10 @@ inputs:
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.
refresh-cache:
description: 'An optional boolean, when enabled it will result in a matched key being deleted after being restored, allowing it to be reused with refreshed/updated content. Default: false'
required: false
default: 'false'
outputs:
cache-hit:
description: 'A boolean value to indicate an exact match was found for the primary key'

File diff suppressed because one or more lines are too long

99319
dist/restore/index.js vendored

File diff suppressed because one or more lines are too long

99350
dist/save-only/index.js vendored

File diff suppressed because one or more lines are too long

99350
dist/save/index.js vendored

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More