diff --git a/__tests__/saveImpl.test.ts b/__tests__/saveImpl.test.ts index 42a4d4b..e3f0a7e 100644 --- a/__tests__/saveImpl.test.ts +++ b/__tests__/saveImpl.test.ts @@ -4,7 +4,7 @@ import nock from "nock"; import { Events, Inputs, RefKey } from "../src/constants"; import { saveImpl } from "../src/saveImpl"; -import { StateProvider } from "../src/stateProvider"; +import { NullStateProvider, StateProvider } from "../src/stateProvider"; import * as actionUtils from "../src/utils/actionUtils"; import * as testUtils from "../src/utils/testUtils"; @@ -471,7 +471,80 @@ test("save with cache hit and refresh-cache will try to delete and re-create ent .mockImplementationOnce(() => { return Promise.resolve(cacheId); }); - await run(new StateProvider()); + await saveImpl(new StateProvider()); + + expect(saveCacheMock).toHaveBeenCalledTimes(1); + expect(saveCacheMock).toHaveBeenCalledWith( + [inputPath], + primaryKey, + { + uploadChunkSize: 4000000 + }, + false + ); + + expect(logWarningMock).toHaveBeenCalledTimes(0); + expect(infoMock).toHaveBeenCalledTimes(3); + + expect(infoMock).toHaveBeenNthCalledWith( + 1, + `Cache hit occurred on the primary key ${primaryKey}, attempting to refresh the contents of the cache.` + ); + expect(infoMock).toHaveBeenNthCalledWith( + 2, + `Succesfully deleted cache with key: ${primaryKey}` + ); + expect(infoMock).toHaveBeenNthCalledWith( + 3, + `Cache saved with key: ${primaryKey}` + ); + + expect(failedMock).toHaveBeenCalledTimes(0); +}); + +test("Granular save will use lookup to determine if cache needs to be updated or (not) saved.", async () => { + process.env["GITHUB_REPOSITORY"] = "owner/repo"; + process.env["GITHUB_TOKEN"] = + "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"; + process.env["GITHUB_ACTION"] = "__owner___run-repo"; + + const infoMock = jest.spyOn(core, "info"); + const logWarningMock = jest.spyOn(actionUtils, "logWarning"); + const failedMock = jest.spyOn(core, "setFailed"); + + const primaryKey = testUtils.successCacheKey; + + const inputPath = "node_modules"; + testUtils.setInput(Inputs.Key, primaryKey); + testUtils.setInput(Inputs.RefreshCache, "true"); + testUtils.setInput(Inputs.Path, inputPath); + testUtils.setInput(Inputs.UploadChunkSize, "4000000"); + + const restoreCacheMock = jest + .spyOn(cache, "restoreCache") + .mockImplementation(() => { + return Promise.resolve(primaryKey); + }); + + const cacheId = 4; + const saveCacheMock = jest + .spyOn(cache, "saveCache") + .mockImplementationOnce(() => { + return Promise.resolve(cacheId); + }); + + await saveImpl(new NullStateProvider()); + + expect(restoreCacheMock).toHaveBeenCalledTimes(1); + expect(restoreCacheMock).toHaveBeenCalledWith( + [inputPath], + primaryKey, + [], + { + lookupOnly: true + }, + false + ); expect(saveCacheMock).toHaveBeenCalledTimes(1); expect(saveCacheMock).toHaveBeenCalledWith( @@ -524,7 +597,7 @@ test("save with cache hit and refresh-cache will throw a warning if there's no G }); const saveCacheMock = jest.spyOn(cache, "saveCache"); - await run(new StateProvider()); + await saveImpl(new StateProvider()); expect(saveCacheMock).toHaveBeenCalledTimes(0); expect(logWarningMock).toHaveBeenCalledWith( diff --git a/__tests__/saveOnly.test.ts b/__tests__/saveOnly.test.ts index 8589525..81e3a1f 100644 --- a/__tests__/saveOnly.test.ts +++ b/__tests__/saveOnly.test.ts @@ -111,6 +111,45 @@ test("save with valid inputs uploads a cache", async () => { expect(failedMock).toHaveBeenCalledTimes(0); }); +test("Granular save with refreshCache is able to save cache", async () => { + process.env["GITHUB_REPOSITORY"] = "owner/repo"; + process.env["GITHUB_TOKEN"] = + "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"; + process.env["GITHUB_ACTION"] = "__owner___run-repo"; + const failedMock = jest.spyOn(core, "setFailed"); + + const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43"; + + const inputPath = "node_modules"; + process.env.CACHE_RESTORE_ONLY_MATCHED_KEY = primaryKey; + testUtils.setInput(Inputs.Key, primaryKey); + testUtils.setInput(Inputs.RefreshCache, "true"); + testUtils.setInput(Inputs.Path, inputPath); + testUtils.setInput(Inputs.UploadChunkSize, "4000000"); + + const cacheId = 4; + + const saveCacheMock = jest + .spyOn(cache, "saveCache") + .mockImplementationOnce(() => { + return Promise.resolve(cacheId); + }); + + await saveOnlyRun(); + + expect(saveCacheMock).toHaveBeenCalledTimes(1); + expect(saveCacheMock).toHaveBeenCalledWith( + [inputPath], + primaryKey, + { + uploadChunkSize: 4000000 + }, + false + ); + + expect(failedMock).toHaveBeenCalledTimes(0); +}); + test("save failing logs the warning message", async () => { const warningMock = jest.spyOn(core, "warning"); diff --git a/dist/save-only/index.js b/dist/save-only/index.js index 77f5690..745a989 100644 --- a/dist/save-only/index.js +++ b/dist/save-only/index.js @@ -78737,8 +78737,16 @@ function saveImpl(stateProvider) { return; } const refreshCache = utils.getInputAsBool(constants_1.Inputs.RefreshCache, { required: false }); - // If matched restore key is same as primary key, either try to refresh the cache, or just notify and do not save (NO-OP in case of SaveOnly action) - const restoredKey = stateProvider.getCacheState(); + // If matched restore key is same as primary key, either try to refresh the cache, or just notify and do not save. + let restoredKey = stateProvider.getCacheState(); + if (refreshCache && !restoredKey) { + // If getCacheState didn't give us a key, we're likely using granular actions. Do a lookup to see if we need to refresh or just do a regular save. + const cachePaths = utils.getInputAsArray(constants_1.Inputs.Path, { + required: true + }); + const enableCrossOsArchive = utils.getInputAsBool(constants_1.Inputs.EnableCrossOsArchive); + restoredKey = yield cache.restoreCache(cachePaths, primaryKey, [], { lookupOnly: true }, enableCrossOsArchive); + } if (utils.isExactKeyMatch(primaryKey, restoredKey)) { const { GITHUB_TOKEN, GITHUB_REPOSITORY } = process.env || null; if (GITHUB_TOKEN && GITHUB_REPOSITORY && refreshCache === true) { diff --git a/dist/save/index.js b/dist/save/index.js index f227d0d..dcf53eb 100644 --- a/dist/save/index.js +++ b/dist/save/index.js @@ -78737,8 +78737,16 @@ function saveImpl(stateProvider) { return; } const refreshCache = utils.getInputAsBool(constants_1.Inputs.RefreshCache, { required: false }); - // If matched restore key is same as primary key, either try to refresh the cache, or just notify and do not save (NO-OP in case of SaveOnly action) - const restoredKey = stateProvider.getCacheState(); + // If matched restore key is same as primary key, either try to refresh the cache, or just notify and do not save. + let restoredKey = stateProvider.getCacheState(); + if (refreshCache && !restoredKey) { + // If getCacheState didn't give us a key, we're likely using granular actions. Do a lookup to see if we need to refresh or just do a regular save. + const cachePaths = utils.getInputAsArray(constants_1.Inputs.Path, { + required: true + }); + const enableCrossOsArchive = utils.getInputAsBool(constants_1.Inputs.EnableCrossOsArchive); + restoredKey = yield cache.restoreCache(cachePaths, primaryKey, [], { lookupOnly: true }, enableCrossOsArchive); + } if (utils.isExactKeyMatch(primaryKey, restoredKey)) { const { GITHUB_TOKEN, GITHUB_REPOSITORY } = process.env || null; if (GITHUB_TOKEN && GITHUB_REPOSITORY && refreshCache === true) { diff --git a/src/restoreImpl.ts b/src/restoreImpl.ts index 54bef49..dd3023a 100644 --- a/src/restoreImpl.ts +++ b/src/restoreImpl.ts @@ -82,7 +82,6 @@ export async function restoreImpl( } else { core.info(`Cache restored from key: ${cacheKey}`); } - return cacheKey; } catch (error: unknown) { core.setFailed((error as Error).message); diff --git a/src/saveImpl.ts b/src/saveImpl.ts index eb92bf8..1ac1882 100644 --- a/src/saveImpl.ts +++ b/src/saveImpl.ts @@ -48,10 +48,26 @@ export async function saveImpl( { required: false } ); - // If matched restore key is same as primary key, either try to refresh the cache, or just notify and do not save (NO-OP in case of SaveOnly action) + // If matched restore key is same as primary key, either try to refresh the cache, or just notify and do not save. - const restoredKey = stateProvider.getCacheState(); + let restoredKey = stateProvider.getCacheState(); + if (refreshCache && !restoredKey) { + // If getCacheState didn't give us a key, we're likely using granular actions. Do a lookup to see if we need to refresh or just do a regular save. + const cachePaths = utils.getInputAsArray(Inputs.Path, { + required: true + }); + const enableCrossOsArchive = utils.getInputAsBool( + Inputs.EnableCrossOsArchive + ); + restoredKey = await cache.restoreCache( + cachePaths, + primaryKey, + [], + { lookupOnly: true }, + enableCrossOsArchive + ); + } if (utils.isExactKeyMatch(primaryKey, restoredKey)) { const { GITHUB_TOKEN, GITHUB_REPOSITORY } = process.env || null; if (GITHUB_TOKEN && GITHUB_REPOSITORY && refreshCache === true) {