Add update-env-variable to force/disable cache update.

This fixes https://github.com/actions/cache/issues/342
This commit is contained in:
eyal0 2022-09-10 16:43:05 -06:00
parent b195c997a4
commit 3d41dc5e6b
7 changed files with 136 additions and 10 deletions

View File

@ -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");

View File

@ -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'

View File

@ -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) {

25
dist/save/index.js vendored
View File

@ -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

View File

@ -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 {

View File

@ -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, {

View File

@ -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)];
} }