mirror of https://github.com/actions/cache.git
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.pull/474/head
parent
9c77c9dbfc
commit
b917253c33
|
@ -308,3 +308,35 @@ test("restore with cache found for restore key", async () => {
|
||||||
);
|
);
|
||||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
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(
|
jest.spyOn(actionUtils, "isExactKeyMatch").mockImplementation(
|
||||||
(key, cacheResult) => {
|
(key, cacheResult) => {
|
||||||
return jest
|
return jest
|
||||||
|
@ -338,3 +346,32 @@ test("save with valid inputs uploads a cache", async () => {
|
||||||
|
|
||||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
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:
|
upload-chunk-size:
|
||||||
description: 'The chunk size used to split up large files during upload, in bytes'
|
description: 'The chunk size used to split up large files during upload, in bytes'
|
||||||
required: false
|
required: false
|
||||||
|
read-only:
|
||||||
|
description: 'Set to true to never save the cache'
|
||||||
|
required: false
|
||||||
outputs:
|
outputs:
|
||||||
cache-hit:
|
cache-hit:
|
||||||
description: 'A boolean value to indicate an exact match was found for the primary key'
|
description: 'A boolean value to indicate an exact match was found for the primary key'
|
||||||
|
|
|
@ -4595,6 +4595,7 @@ var Inputs;
|
||||||
Inputs["Path"] = "path";
|
Inputs["Path"] = "path";
|
||||||
Inputs["RestoreKeys"] = "restore-keys";
|
Inputs["RestoreKeys"] = "restore-keys";
|
||||||
Inputs["UploadChunkSize"] = "upload-chunk-size";
|
Inputs["UploadChunkSize"] = "upload-chunk-size";
|
||||||
|
Inputs["ReadOnly"] = "read-only";
|
||||||
})(Inputs = exports.Inputs || (exports.Inputs = {}));
|
})(Inputs = exports.Inputs || (exports.Inputs = {}));
|
||||||
var Outputs;
|
var Outputs;
|
||||||
(function (Outputs) {
|
(function (Outputs) {
|
||||||
|
@ -36910,7 +36911,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
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 core = __importStar(__webpack_require__(470));
|
||||||
const constants_1 = __webpack_require__(196);
|
const constants_1 = __webpack_require__(196);
|
||||||
function isGhes() {
|
function isGhes() {
|
||||||
|
@ -36975,6 +36976,10 @@ function getInputAsInt(name, options) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
exports.getInputAsInt = getInputAsInt;
|
exports.getInputAsInt = getInputAsInt;
|
||||||
|
function getInputAsBoolean(name, options) {
|
||||||
|
return core.getInput(name, options) === "true";
|
||||||
|
}
|
||||||
|
exports.getInputAsBoolean = getInputAsBoolean;
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
|
@ -4595,6 +4595,7 @@ var Inputs;
|
||||||
Inputs["Path"] = "path";
|
Inputs["Path"] = "path";
|
||||||
Inputs["RestoreKeys"] = "restore-keys";
|
Inputs["RestoreKeys"] = "restore-keys";
|
||||||
Inputs["UploadChunkSize"] = "upload-chunk-size";
|
Inputs["UploadChunkSize"] = "upload-chunk-size";
|
||||||
|
Inputs["ReadOnly"] = "read-only";
|
||||||
})(Inputs = exports.Inputs || (exports.Inputs = {}));
|
})(Inputs = exports.Inputs || (exports.Inputs = {}));
|
||||||
var Outputs;
|
var Outputs;
|
||||||
(function (Outputs) {
|
(function (Outputs) {
|
||||||
|
@ -36910,7 +36911,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
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 core = __importStar(__webpack_require__(470));
|
||||||
const constants_1 = __webpack_require__(196);
|
const constants_1 = __webpack_require__(196);
|
||||||
function isGhes() {
|
function isGhes() {
|
||||||
|
@ -36975,6 +36976,10 @@ function getInputAsInt(name, options) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
exports.getInputAsInt = getInputAsInt;
|
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");
|
utils.logWarning("Cache action is not supported on GHES");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (utils.getInputAsBoolean(constants_1.Inputs.ReadOnly)) {
|
||||||
|
core.info("Cache running in read-only mode, not saving cache.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!utils.isValidEvent()) {
|
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.`);
|
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;
|
return;
|
||||||
|
|
|
@ -2,7 +2,8 @@ export enum Inputs {
|
||||||
Key = "key",
|
Key = "key",
|
||||||
Path = "path",
|
Path = "path",
|
||||||
RestoreKeys = "restore-keys",
|
RestoreKeys = "restore-keys",
|
||||||
UploadChunkSize = "upload-chunk-size"
|
UploadChunkSize = "upload-chunk-size",
|
||||||
|
ReadOnly = "read-only"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum Outputs {
|
export enum Outputs {
|
||||||
|
|
|
@ -11,6 +11,11 @@ async function run(): Promise<void> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (utils.getInputAsBoolean(Inputs.ReadOnly)) {
|
||||||
|
core.info("Cache running in read-only mode, not saving cache.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!utils.isValidEvent()) {
|
if (!utils.isValidEvent()) {
|
||||||
utils.logWarning(
|
utils.logWarning(
|
||||||
`Event Validation Error: The event type ${
|
`Event Validation Error: The event type ${
|
||||||
|
|
|
@ -74,3 +74,10 @@ export function getInputAsInt(
|
||||||
}
|
}
|
||||||
return value;
|
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;
|
path: string;
|
||||||
key: string;
|
key: string;
|
||||||
restoreKeys?: string[];
|
restoreKeys?: string[];
|
||||||
|
readOnly?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setInputs(input: CacheInput): void {
|
export function setInputs(input: CacheInput): void {
|
||||||
|
@ -20,6 +21,7 @@ export function setInputs(input: CacheInput): void {
|
||||||
setInput(Inputs.Key, input.key);
|
setInput(Inputs.Key, input.key);
|
||||||
input.restoreKeys &&
|
input.restoreKeys &&
|
||||||
setInput(Inputs.RestoreKeys, input.restoreKeys.join("\n"));
|
setInput(Inputs.RestoreKeys, input.restoreKeys.join("\n"));
|
||||||
|
setInput(Inputs.ReadOnly, input.readOnly ? "true" : "false");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clearInputs(): void {
|
export function clearInputs(): void {
|
||||||
|
@ -27,4 +29,5 @@ export function clearInputs(): void {
|
||||||
delete process.env[getInputName(Inputs.Key)];
|
delete process.env[getInputName(Inputs.Key)];
|
||||||
delete process.env[getInputName(Inputs.RestoreKeys)];
|
delete process.env[getInputName(Inputs.RestoreKeys)];
|
||||||
delete process.env[getInputName(Inputs.UploadChunkSize)];
|
delete process.env[getInputName(Inputs.UploadChunkSize)];
|
||||||
|
delete process.env[getInputName(Inputs.ReadOnly)];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue