mirror of https://github.com/actions/cache.git
Add update-env-variable to force/disable cache update.
This fixes https://github.com/actions/cache/issues/342
This commit is contained in:
parent
b195c997a4
commit
3d41dc5e6b
|
@ -52,7 +52,7 @@ beforeAll(() => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
process.env[Events.Key] = Events.Push;
|
process.env[Events.Key] = Events.Push;
|
||||||
process.env[RefKey] = "refs/heads/feature-branch";
|
process.env[RefKey] = "refs/heads/feature-branch";
|
||||||
|
delete process.env["MY_CACHE_ENV_VARIABLE"];
|
||||||
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => false);
|
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => false);
|
||||||
jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation(
|
jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation(
|
||||||
() => true
|
() => true
|
||||||
|
@ -63,6 +63,7 @@ afterEach(() => {
|
||||||
testUtils.clearInputs();
|
testUtils.clearInputs();
|
||||||
delete process.env[Events.Key];
|
delete process.env[Events.Key];
|
||||||
delete process.env[RefKey];
|
delete process.env[RefKey];
|
||||||
|
delete process.env["MY_CACHE_ENV_VARIABLE"];
|
||||||
});
|
});
|
||||||
|
|
||||||
test("save with invalid event outputs warning", async () => {
|
test("save with invalid event outputs warning", async () => {
|
||||||
|
@ -194,6 +195,78 @@ test("save with exact match returns early", async () => {
|
||||||
expect(failedMock).toHaveBeenCalledTimes(0);
|
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("save with UpdateEnvVariable true updates the cache despite exact match", 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 inputPath = "node_modules";
|
||||||
|
testUtils.setInput(Inputs.Path, inputPath);
|
||||||
|
testUtils.setInput(Inputs.UpdateEnvVariable, "MY_CACHE_ENV_VARIABLE");
|
||||||
|
|
||||||
|
const cacheId = 4;
|
||||||
|
const saveCacheMock = jest
|
||||||
|
.spyOn(cache, "saveCache")
|
||||||
|
.mockImplementationOnce(() => {
|
||||||
|
return Promise.resolve(cacheId);
|
||||||
|
});
|
||||||
|
process.env["MY_CACHE_ENV_VARIABLE"] = "true";
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(infoMock).toHaveBeenCalledWith(
|
||||||
|
'Cache saving was forced by setting "MY_CACHE_ENV_VARIABLE" to "true".'
|
||||||
|
);
|
||||||
|
expect(saveCacheMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(saveCacheMock).toHaveBeenCalledWith([inputPath], primaryKey, {
|
||||||
|
uploadChunkSize: undefined
|
||||||
|
});
|
||||||
|
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("save with UpdateEnvVariable false doesn't update the 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 inputPath = "node_modules";
|
||||||
|
testUtils.setInput(Inputs.Path, inputPath);
|
||||||
|
testUtils.setInput(Inputs.UpdateEnvVariable, "MY_CACHE_ENV_VARIABLE");
|
||||||
|
|
||||||
|
const saveCacheMock = jest.spyOn(cache, "saveCache");
|
||||||
|
process.env["MY_CACHE_ENV_VARIABLE"] = "no";
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(infoMock).toHaveBeenCalledWith(
|
||||||
|
'Cache saving was disabled by setting "MY_CACHE_ENV_VARIABLE" to "no".'
|
||||||
|
);
|
||||||
|
expect(saveCacheMock).toHaveBeenCalledTimes(0);
|
||||||
|
expect(failedMock).toHaveBeenCalledTimes(0);
|
||||||
|
});
|
||||||
|
|
||||||
test("save with missing input outputs warning", async () => {
|
test("save with missing input outputs warning", async () => {
|
||||||
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
|
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
|
||||||
const failedMock = jest.spyOn(core, "setFailed");
|
const failedMock = jest.spyOn(core, "setFailed");
|
||||||
|
|
|
@ -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
|
||||||
|
update-env-variable:
|
||||||
|
description: 'The name of an environment variable. If the environment variable is set to "true" by the end of the job, force updating the cache. If this environment variable is set to "false" by the end of the job, disable updating the cache. Default is to update only if there was no primary-key exact cache hit.'
|
||||||
|
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'
|
||||||
|
|
|
@ -4931,6 +4931,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["UpdateEnvVariable"] = "update-env-variable";
|
||||||
})(Inputs = exports.Inputs || (exports.Inputs = {}));
|
})(Inputs = exports.Inputs || (exports.Inputs = {}));
|
||||||
var Outputs;
|
var Outputs;
|
||||||
(function (Outputs) {
|
(function (Outputs) {
|
||||||
|
|
|
@ -4931,6 +4931,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["UpdateEnvVariable"] = "update-env-variable";
|
||||||
})(Inputs = exports.Inputs || (exports.Inputs = {}));
|
})(Inputs = exports.Inputs || (exports.Inputs = {}));
|
||||||
var Outputs;
|
var Outputs;
|
||||||
(function (Outputs) {
|
(function (Outputs) {
|
||||||
|
@ -47288,9 +47289,27 @@ function run() {
|
||||||
utils.logWarning(`Error retrieving key from state.`);
|
utils.logWarning(`Error retrieving key from state.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (utils.isExactKeyMatch(primaryKey, state)) {
|
const envVarName = core.getInput(constants_1.Inputs.UpdateEnvVariable);
|
||||||
core.info(`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`);
|
let envVarValue;
|
||||||
return;
|
if (envVarName) {
|
||||||
|
envVarValue = process.env[envVarName];
|
||||||
|
}
|
||||||
|
if (envVarValue !== undefined) {
|
||||||
|
const forceUpdate = ["true", "yes"].includes(envVarValue.toLowerCase());
|
||||||
|
if (forceUpdate) {
|
||||||
|
core.info(`Cache saving was forced by setting "${envVarName}" to "${envVarValue}".`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
core.info(`Cache saving was disabled by setting "${envVarName}" to "${envVarValue}".`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
core.info(`"${envVarName}" is not set.`);
|
||||||
|
if (utils.isExactKeyMatch(primaryKey, state)) {
|
||||||
|
core.info(`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const cachePaths = utils.getInputAsArray(constants_1.Inputs.Path, {
|
const cachePaths = utils.getInputAsArray(constants_1.Inputs.Path, {
|
||||||
required: true
|
required: true
|
||||||
|
|
|
@ -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",
|
||||||
|
UpdateEnvVariable = "update-env-variable"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum Outputs {
|
export enum Outputs {
|
||||||
|
|
35
src/save.ts
35
src/save.ts
|
@ -33,11 +33,36 @@ async function run(): Promise<void> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (utils.isExactKeyMatch(primaryKey, state)) {
|
const envVarName = core.getInput(Inputs.UpdateEnvVariable);
|
||||||
core.info(
|
let forceUpdate;
|
||||||
`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`
|
if (envVarName) {
|
||||||
);
|
let envVarValue;
|
||||||
return;
|
envVarValue = process.env[envVarName];
|
||||||
|
if (["true"].includes(envVarValue.toLowerCase())) {
|
||||||
|
forcedUpdate = true;
|
||||||
|
} else if (["false"].includes(envVarValue.toLowerCase())) {
|
||||||
|
forcedUpdate = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (forcedUpdate !== undefined) {
|
||||||
|
if (forceUpdate) {
|
||||||
|
core.info(
|
||||||
|
`Cache saving was forced by setting "${envVarName}" to "${envVarValue}".`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
core.info(
|
||||||
|
`Cache saving was disabled by setting "${envVarName}" to "${envVarValue}".`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
core.info(`"${envVarName}" is not set.`);
|
||||||
|
if (utils.isExactKeyMatch(primaryKey, state)) {
|
||||||
|
core.info(
|
||||||
|
`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const cachePaths = utils.getInputAsArray(Inputs.Path, {
|
const cachePaths = utils.getInputAsArray(Inputs.Path, {
|
||||||
|
|
|
@ -13,11 +13,14 @@ interface CacheInput {
|
||||||
path: string;
|
path: string;
|
||||||
key: string;
|
key: string;
|
||||||
restoreKeys?: string[];
|
restoreKeys?: string[];
|
||||||
|
updateEnvVariable?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setInputs(input: CacheInput): void {
|
export function setInputs(input: CacheInput): void {
|
||||||
setInput(Inputs.Path, input.path);
|
setInput(Inputs.Path, input.path);
|
||||||
setInput(Inputs.Key, input.key);
|
setInput(Inputs.Key, input.key);
|
||||||
|
input.updateEnvVariable &&
|
||||||
|
setInput(Inputs.UpdateEnvVariable, input.updateEnvVariable);
|
||||||
input.restoreKeys &&
|
input.restoreKeys &&
|
||||||
setInput(Inputs.RestoreKeys, input.restoreKeys.join("\n"));
|
setInput(Inputs.RestoreKeys, input.restoreKeys.join("\n"));
|
||||||
}
|
}
|
||||||
|
@ -25,6 +28,7 @@ export function setInputs(input: CacheInput): void {
|
||||||
export function clearInputs(): void {
|
export function clearInputs(): void {
|
||||||
delete process.env[getInputName(Inputs.Path)];
|
delete process.env[getInputName(Inputs.Path)];
|
||||||
delete process.env[getInputName(Inputs.Key)];
|
delete process.env[getInputName(Inputs.Key)];
|
||||||
|
delete process.env[getInputName(Inputs.UpdateEnvVariable)];
|
||||||
delete process.env[getInputName(Inputs.RestoreKeys)];
|
delete process.env[getInputName(Inputs.RestoreKeys)];
|
||||||
delete process.env[getInputName(Inputs.UploadChunkSize)];
|
delete process.env[getInputName(Inputs.UploadChunkSize)];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue