mirror of
https://github.com/actions/cache.git
synced 2025-03-13 11:47:00 +00:00
Add read-only feature
When `read-only` is `true`, the cache is only restored and not saved. This allows for sharing the cache with multiple steps even if these steps may change them, and speeds them up regardless.
This commit is contained in:
parent
9c77c9dbfc
commit
b917253c33
9 changed files with 2425 additions and 2323 deletions
|
@ -308,3 +308,35 @@ test("restore with cache found for restore key", async () => {
|
|||
);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test("restore with read-only with cache found for key", async () => {
|
||||
const path = "node_modules";
|
||||
const key = "node-test";
|
||||
testUtils.setInputs({
|
||||
path: path,
|
||||
key,
|
||||
readOnly: true
|
||||
});
|
||||
|
||||
const infoMock = jest.spyOn(core, "info");
|
||||
const failedMock = jest.spyOn(core, "setFailed");
|
||||
const stateMock = jest.spyOn(core, "saveState");
|
||||
const setCacheHitOutputMock = jest.spyOn(actionUtils, "setCacheHitOutput");
|
||||
const restoreCacheMock = jest
|
||||
.spyOn(cache, "restoreCache")
|
||||
.mockImplementationOnce(() => {
|
||||
return Promise.resolve(key);
|
||||
});
|
||||
|
||||
await run();
|
||||
|
||||
expect(restoreCacheMock).toHaveBeenCalledTimes(1);
|
||||
expect(restoreCacheMock).toHaveBeenCalledWith([path], key, []);
|
||||
|
||||
expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1);
|
||||
expect(setCacheHitOutputMock).toHaveBeenCalledWith(true);
|
||||
|
||||
expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
|
|
@ -35,6 +35,14 @@ beforeAll(() => {
|
|||
}
|
||||
);
|
||||
|
||||
jest.spyOn(actionUtils, "getInputAsBoolean").mockImplementation(
|
||||
(name, options) => {
|
||||
return jest
|
||||
.requireActual("../src/utils/actionUtils")
|
||||
.getInputAsBoolean(name, options);
|
||||
}
|
||||
);
|
||||
|
||||
jest.spyOn(actionUtils, "isExactKeyMatch").mockImplementation(
|
||||
(key, cacheResult) => {
|
||||
return jest
|
||||
|
@ -338,3 +346,32 @@ test("save with valid inputs uploads a cache", async () => {
|
|||
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test("save with read-only does not upload cache", async () => {
|
||||
const infoMock = jest.spyOn(core, "info");
|
||||
const failedMock = jest.spyOn(core, "setFailed");
|
||||
|
||||
const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43";
|
||||
const savedCacheKey = primaryKey;
|
||||
|
||||
jest.spyOn(core, "getState")
|
||||
// Cache Entry State
|
||||
.mockImplementationOnce(() => {
|
||||
return savedCacheKey;
|
||||
})
|
||||
// Cache Key State
|
||||
.mockImplementationOnce(() => {
|
||||
return primaryKey;
|
||||
});
|
||||
const saveCacheMock = jest.spyOn(cache, "saveCache");
|
||||
|
||||
testUtils.setInput(Inputs.ReadOnly, "true");
|
||||
|
||||
await run();
|
||||
|
||||
expect(saveCacheMock).toHaveBeenCalledTimes(0);
|
||||
expect(infoMock).toHaveBeenCalledWith(
|
||||
"Cache running in read-only mode, not saving cache."
|
||||
);
|
||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
|
|
@ -14,6 +14,9 @@ inputs:
|
|||
upload-chunk-size:
|
||||
description: 'The chunk size used to split up large files during upload, in bytes'
|
||||
required: false
|
||||
read-only:
|
||||
description: 'Set to true to never save the cache'
|
||||
required: false
|
||||
outputs:
|
||||
cache-hit:
|
||||
description: 'A boolean value to indicate an exact match was found for the primary key'
|
||||
|
|
7
dist/restore/index.js
vendored
7
dist/restore/index.js
vendored
|
@ -4595,6 +4595,7 @@ var Inputs;
|
|||
Inputs["Path"] = "path";
|
||||
Inputs["RestoreKeys"] = "restore-keys";
|
||||
Inputs["UploadChunkSize"] = "upload-chunk-size";
|
||||
Inputs["ReadOnly"] = "read-only";
|
||||
})(Inputs = exports.Inputs || (exports.Inputs = {}));
|
||||
var Outputs;
|
||||
(function (Outputs) {
|
||||
|
@ -36910,7 +36911,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getInputAsInt = exports.getInputAsArray = exports.isValidEvent = exports.logWarning = exports.getCacheState = exports.setOutputAndState = exports.setCacheHitOutput = exports.setCacheState = exports.isExactKeyMatch = exports.isGhes = void 0;
|
||||
exports.getInputAsBoolean = exports.getInputAsInt = exports.getInputAsArray = exports.isValidEvent = exports.logWarning = exports.getCacheState = exports.setOutputAndState = exports.setCacheHitOutput = exports.setCacheState = exports.isExactKeyMatch = exports.isGhes = void 0;
|
||||
const core = __importStar(__webpack_require__(470));
|
||||
const constants_1 = __webpack_require__(196);
|
||||
function isGhes() {
|
||||
|
@ -36975,6 +36976,10 @@ function getInputAsInt(name, options) {
|
|||
return value;
|
||||
}
|
||||
exports.getInputAsInt = getInputAsInt;
|
||||
function getInputAsBoolean(name, options) {
|
||||
return core.getInput(name, options) === "true";
|
||||
}
|
||||
exports.getInputAsBoolean = getInputAsBoolean;
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
|
11
dist/save/index.js
vendored
11
dist/save/index.js
vendored
|
@ -4595,6 +4595,7 @@ var Inputs;
|
|||
Inputs["Path"] = "path";
|
||||
Inputs["RestoreKeys"] = "restore-keys";
|
||||
Inputs["UploadChunkSize"] = "upload-chunk-size";
|
||||
Inputs["ReadOnly"] = "read-only";
|
||||
})(Inputs = exports.Inputs || (exports.Inputs = {}));
|
||||
var Outputs;
|
||||
(function (Outputs) {
|
||||
|
@ -36910,7 +36911,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getInputAsInt = exports.getInputAsArray = exports.isValidEvent = exports.logWarning = exports.getCacheState = exports.setOutputAndState = exports.setCacheHitOutput = exports.setCacheState = exports.isExactKeyMatch = exports.isGhes = void 0;
|
||||
exports.getInputAsBoolean = exports.getInputAsInt = exports.getInputAsArray = exports.isValidEvent = exports.logWarning = exports.getCacheState = exports.setOutputAndState = exports.setCacheHitOutput = exports.setCacheState = exports.isExactKeyMatch = exports.isGhes = void 0;
|
||||
const core = __importStar(__webpack_require__(470));
|
||||
const constants_1 = __webpack_require__(196);
|
||||
function isGhes() {
|
||||
|
@ -36975,6 +36976,10 @@ function getInputAsInt(name, options) {
|
|||
return value;
|
||||
}
|
||||
exports.getInputAsInt = getInputAsInt;
|
||||
function getInputAsBoolean(name, options) {
|
||||
return core.getInput(name, options) === "true";
|
||||
}
|
||||
exports.getInputAsBoolean = getInputAsBoolean;
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
@ -44211,6 +44216,10 @@ function run() {
|
|||
utils.logWarning("Cache action is not supported on GHES");
|
||||
return;
|
||||
}
|
||||
if (utils.getInputAsBoolean(constants_1.Inputs.ReadOnly)) {
|
||||
core.info("Cache running in read-only mode, not saving cache.");
|
||||
return;
|
||||
}
|
||||
if (!utils.isValidEvent()) {
|
||||
utils.logWarning(`Event Validation Error: The event type ${process.env[constants_1.Events.Key]} is not supported because it's not tied to a branch or tag ref.`);
|
||||
return;
|
||||
|
|
|
@ -2,7 +2,8 @@ export enum Inputs {
|
|||
Key = "key",
|
||||
Path = "path",
|
||||
RestoreKeys = "restore-keys",
|
||||
UploadChunkSize = "upload-chunk-size"
|
||||
UploadChunkSize = "upload-chunk-size",
|
||||
ReadOnly = "read-only"
|
||||
}
|
||||
|
||||
export enum Outputs {
|
||||
|
|
|
@ -11,6 +11,11 @@ async function run(): Promise<void> {
|
|||
return;
|
||||
}
|
||||
|
||||
if (utils.getInputAsBoolean(Inputs.ReadOnly)) {
|
||||
core.info("Cache running in read-only mode, not saving cache.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!utils.isValidEvent()) {
|
||||
utils.logWarning(
|
||||
`Event Validation Error: The event type ${
|
||||
|
|
|
@ -74,3 +74,10 @@ export function getInputAsInt(
|
|||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export function getInputAsBoolean(
|
||||
name: string,
|
||||
options?: core.InputOptions
|
||||
): boolean {
|
||||
return core.getInput(name, options) === "true";
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ interface CacheInput {
|
|||
path: string;
|
||||
key: string;
|
||||
restoreKeys?: string[];
|
||||
readOnly?: boolean;
|
||||
}
|
||||
|
||||
export function setInputs(input: CacheInput): void {
|
||||
|
@ -20,6 +21,7 @@ export function setInputs(input: CacheInput): void {
|
|||
setInput(Inputs.Key, input.key);
|
||||
input.restoreKeys &&
|
||||
setInput(Inputs.RestoreKeys, input.restoreKeys.join("\n"));
|
||||
setInput(Inputs.ReadOnly, input.readOnly ? "true" : "false");
|
||||
}
|
||||
|
||||
export function clearInputs(): void {
|
||||
|
@ -27,4 +29,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.ReadOnly)];
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue