diff --git a/README.md b/README.md index cf6f12c..57eec7e 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,8 @@ steps: - run: dotnet restore --locked-mode ``` +### Reduce caching size + **Note:** Use [`NUGET_PACKAGES`](https://learn.microsoft.com/nuget/reference/cli-reference/cli-ref-environment-variables) environment variable if available. Some action runners already has huge libraries. (ex. Xamarin) ```yaml @@ -112,6 +114,21 @@ steps: - run: dotnet restore --locked-mode ``` +### Caching NuGet packages in monorepos + +```yaml +env: + NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages +steps: +- uses: actions/checkout@v3 +- uses: actions/setup-dotnet@v3 + with: + dotnet-version: 6.x + cache: true + cache-dependency-path: subdir/packages.lock.json +- run: dotnet restore --locked-mode +``` + ## Matrix Testing Using `setup-dotnet` it's possible to use [matrix syntax](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrix) to install several versions of .NET SDK: ```yml diff --git a/__tests__/cache-restore.test.ts b/__tests__/cache-restore.test.ts index 817c6c4..8b5f8d5 100644 --- a/__tests__/cache-restore.test.ts +++ b/__tests__/cache-restore.test.ts @@ -3,6 +3,7 @@ import * as core from '@actions/core'; import * as glob from '@actions/glob'; import {restoreCache} from '../src/cache-restore'; import {getNuGetFolderPath} from '../src/cache-utils'; +import {lockFilePattern} from '../src/constants'; jest.mock('@actions/cache'); jest.mock('@actions/core'); @@ -20,15 +21,16 @@ describe('cache-restore tests', () => { }); }); beforeEach(() => { + jest.mocked(glob.hashFiles).mockClear(); jest.mocked(core.saveState).mockClear(); jest.mocked(core.setOutput).mockClear(); jest.mocked(cache.restoreCache).mockClear(); }); - it('does not call cache.restoreCache() when lock file is not found', async () => { + it('throws error when lock file is not found', async () => { jest.mocked(glob.hashFiles).mockResolvedValue(''); - await restoreCache(); + await expect(restoreCache(lockFilePattern)).rejects.toThrow(); expect(jest.mocked(core.saveState)).not.toHaveBeenCalled(); expect(jest.mocked(core.setOutput)).not.toHaveBeenCalled(); @@ -39,7 +41,7 @@ describe('cache-restore tests', () => { jest.mocked(glob.hashFiles).mockResolvedValue('hash'); jest.mocked(cache.restoreCache).mockResolvedValue(undefined); - await restoreCache(); + await restoreCache(lockFilePattern); const expectedKey = `dotnet-cache-${process.env.RUNNER_OS}-hash`; expect(jest.mocked(core.saveState)).toHaveBeenCalledWith( @@ -61,7 +63,7 @@ describe('cache-restore tests', () => { jest.mocked(glob.hashFiles).mockResolvedValue('hash'); jest.mocked(cache.restoreCache).mockResolvedValue(expectedKey); - await restoreCache(); + await restoreCache(lockFilePattern); expect(jest.mocked(core.saveState)).toHaveBeenCalledWith( 'CACHE_KEY', @@ -76,5 +78,16 @@ describe('cache-restore tests', () => { true ); }); + + it('calls glob.hashFiles("**/packages.lock.json") if cacheDependencyPath is falsy', async () => { + const expectedKey = `dotnet-cache-${process.env.RUNNER_OS}-hash`; + jest.mocked(glob.hashFiles).mockResolvedValue('hash'); + jest.mocked(cache.restoreCache).mockResolvedValue(expectedKey); + + await restoreCache(''); + + expect(jest.mocked(glob.hashFiles)).not.toHaveBeenCalledWith(''); + expect(jest.mocked(glob.hashFiles)).toHaveBeenCalledWith(lockFilePattern); + }); }); }); diff --git a/action.yml b/action.yml index a10d16f..128fd2c 100644 --- a/action.yml +++ b/action.yml @@ -21,6 +21,9 @@ inputs: description: 'Optional input to enable caching of the NuGet global-packages folder' required: false default: false + cache-dependency-path: + description: 'Used to specify the path to a dependency file: packages.lock.json. Supports wildcards or a list of file names for caching multiple dependencies.' + required: false outputs: cache-hit: description: 'A boolean value to indicate if a cache was hit.' diff --git a/src/cache-restore.ts b/src/cache-restore.ts index 764b131..d4a5b2d 100644 --- a/src/cache-restore.ts +++ b/src/cache-restore.ts @@ -5,11 +5,12 @@ import * as glob from '@actions/glob'; import {getNuGetFolderPath} from './cache-utils'; import {lockFilePattern, State, Outputs} from './constants'; -export const restoreCache = async () => { - const fileHash = await glob.hashFiles(lockFilePattern); +export const restoreCache = async (cacheDependencyPath?: string) => { + const fileHash = await glob.hashFiles(cacheDependencyPath || lockFilePattern); if (!fileHash) { - core.warning(`No matches found for glob: ${lockFilePattern}`); - return; + throw new Error( + 'Some specified paths were not resolved, unable to cache dependencies.' + ); } const platform = process.env.RUNNER_OS; diff --git a/src/setup-dotnet.ts b/src/setup-dotnet.ts index e8374fa..01ca61a 100644 --- a/src/setup-dotnet.ts +++ b/src/setup-dotnet.ts @@ -96,7 +96,8 @@ export async function run() { core.setOutput(Outputs.DotnetVersion, versionToOutput); if (core.getBooleanInput('cache') && isCacheFeatureAvailable()) { - await restoreCache(); + const cacheDependencyPath = core.getInput('cache-dependency-path'); + await restoreCache(cacheDependencyPath); } else { core.setOutput(Outputs.CacheHit, false); }