diff --git a/__tests__/actionUtils.test.ts b/__tests__/actionUtils.test.ts index a6dc291..0a11d1f 100644 --- a/__tests__/actionUtils.test.ts +++ b/__tests__/actionUtils.test.ts @@ -194,3 +194,13 @@ test("getInputAsArray handles empty lines correctly", () => { testUtils.setInput("foo", "\n\nbar\n\nbaz\n\n"); expect(actionUtils.getInputAsArray("foo")).toEqual(["bar", "baz"]); }); + +test("getInputAsBoolean returns true if the value is set to 'true'", () => { + testUtils.setInput("foo", "true"); + expect(actionUtils.getInputAsBoolean("foo")).toEqual(true); +}); + +test("getInputAsBoolean returns false if the value is set to anything else", () => { + testUtils.setInput("foo", "false"); + expect(actionUtils.getInputAsBoolean("foo")).toEqual(false); +}); diff --git a/__tests__/save.test.ts b/__tests__/save.test.ts index 943a2bd..3c55645 100644 --- a/__tests__/save.test.ts +++ b/__tests__/save.test.ts @@ -118,6 +118,41 @@ test("save with exact match returns early", async () => { expect(failedMock).toHaveBeenCalledTimes(0); }); +test("save with exact match updates when configured", async () => { + 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.Update, "true"); + + const cacheId = 4; + const saveCacheMock = jest + .spyOn(cache, "saveCache") + .mockImplementationOnce(() => { + return Promise.resolve(cacheId); + }); + + await run(); + + expect(saveCacheMock).toHaveBeenCalledTimes(1); + expect(saveCacheMock).toHaveBeenCalledWith([inputPath], primaryKey); + + expect(failedMock).toHaveBeenCalledTimes(0); +}); + test("save with missing input outputs warning", async () => { const logWarningMock = jest.spyOn(actionUtils, "logWarning"); const failedMock = jest.spyOn(core, "setFailed"); diff --git a/action.yml b/action.yml index e50c38b..47fa26a 100644 --- a/action.yml +++ b/action.yml @@ -11,6 +11,9 @@ inputs: restore-keys: description: 'An ordered list of keys to use for restoring the cache if no cache hit occurred for key' required: false + update: + description: 'If true, the cache will be updated if the key already exists' + required: false outputs: cache-hit: description: 'A boolean value to indicate an exact match was found for the primary key' diff --git a/src/constants.ts b/src/constants.ts index a190e00..b32968e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,7 +1,8 @@ export enum Inputs { Key = "key", Path = "path", - RestoreKeys = "restore-keys" + RestoreKeys = "restore-keys", + Update = "update" } export enum Outputs { diff --git a/src/save.ts b/src/save.ts index 80b656c..6897a57 100644 --- a/src/save.ts +++ b/src/save.ts @@ -16,15 +16,17 @@ async function run(): Promise { } const state = utils.getCacheState(); - - // Inputs are re-evaluted before the post action, so we want the original key used for restore + // Inputs are re-evaluated before the post action, so we want the original key used for restore const primaryKey = core.getState(State.CachePrimaryKey); if (!primaryKey) { utils.logWarning(`Error retrieving key from state.`); return; } - if (utils.isExactKeyMatch(primaryKey, state)) { + if ( + utils.isExactKeyMatch(primaryKey, state) && + !utils.getInputAsBoolean(Inputs.Update) + ) { core.info( `Cache hit occurred on the primary key ${primaryKey}, not saving cache.` ); diff --git a/src/utils/actionUtils.ts b/src/utils/actionUtils.ts index 26bb8c1..256b8e6 100644 --- a/src/utils/actionUtils.ts +++ b/src/utils/actionUtils.ts @@ -56,3 +56,10 @@ export function getInputAsArray( .map(s => s.trim()) .filter(x => x !== ""); } + +export function getInputAsBoolean( + name: string, + options?: core.InputOptions +): boolean { + return core.getInput(name, options) === "true"; +}