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(() => {
process.env[Events.Key] = Events.Push;
process.env[RefKey] = "refs/heads/feature-branch";
delete process.env["MY_CACHE_ENV_VARIABLE"];
jest.spyOn(actionUtils, "isGhes").mockImplementation(() => false);
jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation(
() => true
@ -63,6 +63,7 @@ afterEach(() => {
testUtils.clearInputs();
delete process.env[Events.Key];
delete process.env[RefKey];
delete process.env["MY_CACHE_ENV_VARIABLE"];
});
test("save with invalid event outputs warning", async () => {
@ -194,6 +195,78 @@ test("save with exact match returns early", async () => {
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 () => {
const logWarningMock = jest.spyOn(actionUtils, "logWarning");
const failedMock = jest.spyOn(core, "setFailed");

View File

@ -14,6 +14,9 @@ inputs:
upload-chunk-size:
description: 'The chunk size used to split up large files during upload, in bytes'
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:
cache-hit:
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["RestoreKeys"] = "restore-keys";
Inputs["UploadChunkSize"] = "upload-chunk-size";
Inputs["UpdateEnvVariable"] = "update-env-variable";
})(Inputs = exports.Inputs || (exports.Inputs = {}));
var Outputs;
(function (Outputs) {

25
dist/save/index.js vendored
View File

@ -4931,6 +4931,7 @@ var Inputs;
Inputs["Path"] = "path";
Inputs["RestoreKeys"] = "restore-keys";
Inputs["UploadChunkSize"] = "upload-chunk-size";
Inputs["UpdateEnvVariable"] = "update-env-variable";
})(Inputs = exports.Inputs || (exports.Inputs = {}));
var Outputs;
(function (Outputs) {
@ -47288,9 +47289,27 @@ function run() {
utils.logWarning(`Error retrieving key from state.`);
return;
}
if (utils.isExactKeyMatch(primaryKey, state)) {
core.info(`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`);
return;
const envVarName = core.getInput(constants_1.Inputs.UpdateEnvVariable);
let envVarValue;
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, {
required: true

View File

@ -2,7 +2,8 @@ export enum Inputs {
Key = "key",
Path = "path",
RestoreKeys = "restore-keys",
UploadChunkSize = "upload-chunk-size"
UploadChunkSize = "upload-chunk-size",
UpdateEnvVariable = "update-env-variable"
}
export enum Outputs {

View File

@ -33,11 +33,36 @@ async function run(): Promise<void> {
return;
}
if (utils.isExactKeyMatch(primaryKey, state)) {
core.info(
`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`
);
return;
const envVarName = core.getInput(Inputs.UpdateEnvVariable);
let forceUpdate;
if (envVarName) {
let envVarValue;
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, {

View File

@ -13,11 +13,14 @@ interface CacheInput {
path: string;
key: string;
restoreKeys?: string[];
updateEnvVariable?: string;
}
export function setInputs(input: CacheInput): void {
setInput(Inputs.Path, input.path);
setInput(Inputs.Key, input.key);
input.updateEnvVariable &&
setInput(Inputs.UpdateEnvVariable, input.updateEnvVariable);
input.restoreKeys &&
setInput(Inputs.RestoreKeys, input.restoreKeys.join("\n"));
}
@ -25,6 +28,7 @@ export function setInputs(input: CacheInput): void {
export function clearInputs(): void {
delete process.env[getInputName(Inputs.Path)];
delete process.env[getInputName(Inputs.Key)];
delete process.env[getInputName(Inputs.UpdateEnvVariable)];
delete process.env[getInputName(Inputs.RestoreKeys)];
delete process.env[getInputName(Inputs.UploadChunkSize)];
}