Compare commits

..

35 Commits

Author SHA1 Message Date
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
Alessandro Sebastiani
9fa7e61ec7
Update force deletion docs due a recent deprecation (#1500)
* fix: update force deletion docs due a recent deprecation

* fix: applied josh's suggestions

---------

Co-authored-by: Josh Gross <joshmgross@github.com>
2025-02-12 10:49:34 -05:00
60 changed files with 7999 additions and 17965 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.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

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

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.

File diff suppressed because one or more lines are too long

5971
dist/restore/index.js vendored

File diff suppressed because one or more lines are too long

5971
dist/save-only/index.js vendored

File diff suppressed because one or more lines are too long

5971
dist/save/index.js vendored

File diff suppressed because one or more lines are too long

1768
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +1,15 @@
{
"name": "cache",
"version": "4.2.0",
"version": "4.2.3",
"private": true,
"description": "Cache dependencies and build outputs",
"main": "dist/restore/index.js",
"scripts": {
"build": "tsc && ncc build -o dist/restore src/restore.ts && ncc build -o dist/save src/save.ts && ncc build -o dist/restore-only src/restoreOnly.ts && ncc build -o dist/save-only src/saveOnly.ts",
"test": "tsc --noEmit && jest --coverage",
"lint": "eslint **/*.ts --cache",
"format": "prettier --write **/*.ts",
"format-check": "prettier --check **/*.ts"
"lint": "eslint src/**/*.ts --cache __tests__/*.ts --cache",
"format": "prettier --write **/**/*.ts",
"format-check": "prettier --check **/**/*.ts"
},
"repository": {
"type": "git",
@ -23,11 +23,11 @@
"author": "GitHub",
"license": "MIT",
"dependencies": {
"@actions/cache": "^4.0.0",
"@actions/cache": "^4.0.3",
"@actions/core": "^1.11.1",
"@actions/exec": "^1.1.1",
"@actions/io": "^1.1.2",
"@octokit/action": "^4.0.10"
"@octokit/action": "^5.0.6"
},
"devDependencies": {
"@types/jest": "^27.5.2",
@ -50,4 +50,4 @@
"ts-jest": "^28.0.8",
"typescript": "^4.9.5"
}
}
}

View File

@ -1,10 +1,9 @@
import * as cache from "@actions/cache";
import * as core from "@actions/core";
import { RequestError } from "@octokit/request-error"
import { OctokitResponse } from "@octokit/types"
import { Octokit } from "@octokit/action";
import { RequestError } from "@octokit/request-error";
import { RefKey } from "../constants";
const { Octokit } = require("@octokit/action");
export function isGhes(): boolean {
const ghUrl = new URL(
@ -33,54 +32,69 @@ export function logWarning(message: string): void {
core.info(`${warningPrefix}${message}`);
}
export async function deleteCacheByKey(key: string, owner: string, repo: string) : Promise <number | void> {
export async function deleteCacheByKey(
key: string,
owner: string,
repo: string
): Promise<number | void> {
const octokit = new Octokit();
let response;
try {
const gitRef = process.env[RefKey];
let cacheEntry = await octokit.rest.actions.getActionsCacheList({
const cacheEntry = await octokit.rest.actions.getActionsCacheList({
owner: owner,
repo: repo,
key: key,
ref: gitRef
});
const { data: {
total_count,
actions_caches
}
});
const {
data: { total_count, actions_caches }
} = cacheEntry;
if (total_count !== 1 || total_count !== actions_caches.length) { // leave all find logic to the actual cache implementation. We just want to make sure we're returned a single element so we don't accidentally delete an entry that belongs to a different gitref.
if (total_count !== 1 || total_count !== actions_caches.length) {
// leave all find logic to the actual cache implementation. We just want to make sure we're returned a single element so we don't accidentally delete an entry that belongs to a different gitref.
if (total_count > 1) {
exports.logWarning(`More than one cache entry found for key ${key}`);
}
else if (total_count === 0 || actions_caches.length === 0) {
exports.logWarning(`No cache entries for key ${key} belong to gitref ${gitRef}.`);
exports.logWarning(
`More than one cache entry found for key ${key}`
);
} else if (total_count === 0 || actions_caches.length === 0) {
exports.logWarning(
`No cache entries for key ${key} belong to gitref ${gitRef}.`
);
}
// This situation is likely never actually going to come up.
// Istanbul is being dumb and I can't ignore this path.
else if (total_count !== actions_caches.length) {
exports.logWarning(`Reported cache entry matches for ${key} does not match length of 'actions_caches' array in API response.`);
exports.logWarning(
`Reported cache entry matches for ${key} does not match length of 'actions_caches' array in API response.`
);
}
core.info(`Skip trying to delete cache entry for key ${key}.`)
core.info(`Skip trying to delete cache entry for key ${key}.`);
return;
}
let id = actions_caches[0].id;
response = await octokit.rest.actions.deleteActionsCacheById({
owner: owner,
repo: repo,
cache_id: id
const id = actions_caches[0].id;
if (id) {
response = await octokit.rest.actions.deleteActionsCacheById({
owner: owner,
repo: repo,
cache_id: id
});
if (response.status === 204) {
core.info(`Succesfully deleted cache with key: ${key}, id: ${id}`);
return 204;
if (response.status === 204) {
core.info(
`Succesfully deleted cache with key: ${key}, id: ${id}`
);
return 204;
}
}
} catch (e) {
if (e instanceof RequestError) {
let err = e as RequestError;
let errData = err.response?.data as any | undefined;
exports.logWarning(`Github API reported error: ${err.name} '${err.status}: ${errData?.message}'`);
const err = e as RequestError;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const errMessage = (err.response?.data as any).message;
exports.logWarning(
`Github API reported error: ${err.name} '${err.status}: ${errMessage}'`
);
}
core.info(`Couldn't delete cache entry for key ${key}.`)
core.info(`Couldn't delete cache entry for key ${key}.`);
return;
}
}

View File

@ -1,14 +1,18 @@
/* istanbul ignore file */
import { Inputs } from "../constants";
import { rest } from "msw";
import { setupServer } from "msw/node";
import nock from "nock";
export const successCacheKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
export const wrongRefCacheKey = "Linux-latest-node-bb828da54c148048dd17899ba9fda624811cfb43";
export const failureCacheKey = "Windows-node-bb828da54c148048dd17899ba9fda624811cfb43";
export const passThroughCacheKey = "macOS-node-bb828da54c148048dd17899ba9fda624811cfb43";
import { Inputs } from "../constants";
export const successCacheKey =
"Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
export const wrongRefCacheKey =
"Linux-latest-node-bb828da54c148048dd17899ba9fda624811cfb43";
export const failureCacheKey =
"Windows-node-bb828da54c148048dd17899ba9fda624811cfb43";
export const passThroughCacheKey =
"macOS-node-bb828da54c148048dd17899ba9fda624811cfb43";
const successCacheId = 1337;
const failureCacheId = 69;
@ -61,79 +65,109 @@ export function clearInputs(): void {
}
export const mockServer = setupServer(
rest.delete('https://api.github.com/repos/owner/repo/actions/caches/', (req, res, ctx) => {
return res(ctx.status(422),
ctx.json({
message: "Invalid request.\n\nMissing required query parameter key",
documentation_url: "https://docs.github.com/rest/actions/cache#delete-github-actions-caches-for-a-repository-using-a-cache-key",
})
)
}),
rest.delete('https://api.github.com/repos/owner/repo/actions/caches/:id', (req, res, ctx) => {
const { id } = req.params;
if (parseInt(id as string) === failureCacheId) {
return res(ctx.status(404),
rest.delete(
"https://api.github.com/repos/owner/repo/actions/caches/",
(req, res, ctx) => {
return res(
ctx.status(422),
ctx.json({
message: "Not Found",
documentation_url: "https://docs.github.com/rest/actions/cache#delete-a-github-actions-cache-for-a-repository-using-a-cache-id"
}));
message:
"Invalid request.\n\nMissing required query parameter key",
documentation_url:
"https://docs.github.com/rest/actions/cache#delete-github-actions-caches-for-a-repository-using-a-cache-key"
})
);
}
return res(ctx.status(204));
}),
),
rest.delete(
"https://api.github.com/repos/owner/repo/actions/caches/:id",
(req, res, ctx) => {
const { id } = req.params;
if (parseInt(id as string) === failureCacheId) {
return res(
ctx.status(404),
ctx.json({
message: "Not Found",
documentation_url:
"https://docs.github.com/rest/actions/cache#delete-a-github-actions-cache-for-a-repository-using-a-cache-id"
})
);
}
return res(ctx.status(204));
}
),
// This endpoint always returns 200/OK, what we're checking here is whether we can get a unique cache ID, to avoid deleting the wrong entry.
rest.get('https://api.github.com/repos/owner/repo/actions/caches', (req, res, ctx) => {
let key : string = req.url?.searchParams?.get('key') || '';
let ref : string = req.url?.searchParams?.get('ref') || '';
if (key === '' || ref === '') {
return res(ctx.status(200),
ctx.json({
total_count: 2,
actions_caches: [{
id: 15,
ref: "refs/heads/main",
key: failureCacheKey,
version: "73885106f58cc52a7df9ec4d4a5622a5614813162cb516c759a30af6bf56e6f0",
last_accessed_at: "2022-12-29T22:06:42.683333300Z",
created_at: "2022-12-29T22:06:42.683333300Z",
size_in_bytes: 6057793
},
{
id: 16,
ref: "refs/heads/another-feature-branch",
key: failureCacheKey,
version: "73885106f58cc52a7df9ec4d4a5622a5614813162cb516c759a30af6bf56e6f0",
last_accessed_at: "2022-12-29T22:06:42.683333300Z",
created_at: "2022-12-29T22:06:42.683333300Z",
size_in_bytes: 6057793
}]
})
);
rest.get(
"https://api.github.com/repos/owner/repo/actions/caches",
(req, res, ctx) => {
const key: string = req.url?.searchParams?.get("key") || "";
const ref: string = req.url?.searchParams?.get("ref") || "";
if (key === "" || ref === "") {
return res(
ctx.status(200),
ctx.json({
total_count: 2,
actions_caches: [
{
id: 15,
ref: "refs/heads/main",
key: failureCacheKey,
version:
"73885106f58cc52a7df9ec4d4a5622a5614813162cb516c759a30af6bf56e6f0",
last_accessed_at:
"2022-12-29T22:06:42.683333300Z",
created_at: "2022-12-29T22:06:42.683333300Z",
size_in_bytes: 6057793
},
{
id: 16,
ref: "refs/heads/another-feature-branch",
key: failureCacheKey,
version:
"73885106f58cc52a7df9ec4d4a5622a5614813162cb516c759a30af6bf56e6f0",
last_accessed_at:
"2022-12-29T22:06:42.683333300Z",
created_at: "2022-12-29T22:06:42.683333300Z",
size_in_bytes: 6057793
}
]
})
);
}
// This is the behavior seen when search doesn't find anything, but it is seen both when no key matches, as well as when the key matches but the entry belongs to another (likely the base) branch.
else if (key === wrongRefCacheKey) {
return res(
ctx.status(200),
ctx.json({
total_count: 0,
actions_caches: []
})
);
} else if (key === successCacheKey || key === failureCacheKey) {
return res(
ctx.status(200),
ctx.json({
total_count: 1,
actions_caches: [
{
id:
key === successCacheKey
? successCacheId
: failureCacheId,
ref: ref,
key: key,
version:
"93a0f912fdb70083e929c1bf564bca2050be1c4e0932f7f9e78465ddcfbcc8f6",
last_accessed_at:
"2022-12-29T22:06:42.683333300Z",
created_at: "2022-12-29T22:06:42.683333300Z",
size_in_bytes: 6057793
}
]
})
);
}
return req.passthrough();
}
// This is the behavior seen when search doesn't find anything, but it is seen both when no key matches, as well as when the key matches but the entry belongs to another (likely the base) branch.
else if (key === wrongRefCacheKey) {
return res(ctx.status(200),
ctx.json({
total_count: 0,
actions_caches: []
})
);
}
else if (key === successCacheKey || key === failureCacheKey) {
return res(ctx.status(200),
ctx.json({
total_count: 1,
actions_caches: [{
id: (key === successCacheKey ? successCacheId : failureCacheId),
ref: ref,
key: key,
version: "93a0f912fdb70083e929c1bf564bca2050be1c4e0932f7f9e78465ddcfbcc8f6",
last_accessed_at: "2022-12-29T22:06:42.683333300Z",
created_at: "2022-12-29T22:06:42.683333300Z",
size_in_bytes: 6057793
}]
})
);
}
return req.passthrough();
})
);
)
);

View File

@ -38,9 +38,8 @@ From `v3.2.3` cache is cross-os compatible when `enableCrossOsArchive` input is
## Force deletion of caches overriding default cache eviction policy
Caches have [branch scope restriction](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#restrictions-for-accessing-a-cache) in place. This means that if caches for a specific branch are using a lot of storage quota, it may result into more frequently used caches from `default` branch getting thrashed. For example, if there are many pull requests happening on a repo and are creating caches, these cannot be used in default branch scope but will still occupy a lot of space till they get cleaned up by [eviction policy](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy). But sometime we want to clean them up on a faster cadence so as to ensure default branch is not thrashing. In order to achieve this, [gh-actions-cache cli](https://github.com/actions/gh-actions-cache/) can be used to delete caches for specific branches.
Caches have [branch scope restriction](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#restrictions-for-accessing-a-cache) in place. This means that if caches for a specific branch are using a lot of storage quota, it may result into more frequently used caches from `default` branch getting thrashed. For example, if there are many pull requests happening on a repo and are creating caches, these cannot be used in default branch scope but will still occupy a lot of space till they get cleaned up by [eviction policy](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy). But sometime we want to clean them up on a faster cadence so as to ensure default branch is not thrashing.
This workflow uses `gh-actions-cache` to delete all the caches created by a branch.
<details>
<summary>Example</summary>
@ -61,29 +60,23 @@ jobs:
actions: write
contents: read
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Cleanup
run: |
gh extension install actions/gh-actions-cache
REPO=${{ github.repository }}
BRANCH=refs/pull/${{ github.event.pull_request.number }}/merge
echo "Fetching list of cache key"
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 )
cacheKeysForPR=$(gh cache list --ref $BRANCH --limit 100 --json id --jq '.[].id')
## Setting this to not fail the workflow while deleting cache keys.
## Setting this to not fail the workflow while deleting cache keys.
set +e
echo "Deleting caches..."
for cacheKey in $cacheKeysForPR
do
gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
gh cache delete $cacheKey
done
echo "Done"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge
```
</details>