Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pnpm caching support #278

Merged
merged 12 commits into from Jul 20, 2021
33 changes: 33 additions & 0 deletions .github/workflows/e2e-cache.yml
Expand Up @@ -35,6 +35,39 @@ jobs:
run: __tests__/verify-node.sh "${{ matrix.node-version }}"
shell: bash

node-pnpm-depencies-caching:
name: Test pnpm (Node ${{ matrix.node-version}}, ${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [12, 14, 16]
steps:
- uses: actions/checkout@v2
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 6.9.0
maxim-lobanov marked this conversation as resolved.
Show resolved Hide resolved
- name: Generate pnpm file
run: pnpm install
- name: Remove dependencies
shell: pwsh
run: Remove-Item node_modules -Force -Recurse
- name: Clean global cache
run: rm -rf ~/.pnpm-store
shell: bash
- name: Setup Node
uses: ./
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Verify node and pnpm
run: __tests__/verify-node.sh "${{ matrix.node-version }}"
shell: bash

node-yarn1-depencies-caching:
name: Test yarn 1 (Node ${{ matrix.node-version}}, ${{ matrix.os }})
runs-on: ${{ matrix.os }}
Expand Down
24 changes: 22 additions & 2 deletions README.md
Expand Up @@ -7,7 +7,7 @@
This action provides the following functionality for GitHub Actions users:

- Optionally downloading and caching distribution of the requested Node.js version, and adding it to the PATH
- Optionally caching npm/yarn dependencies
- Optionally caching npm/pnpm/yarn dependencies
- Registering problem matchers for error output
- Configuring authentication for GPR or npm

Expand Down Expand Up @@ -41,7 +41,7 @@ nvm lts syntax: `lts/erbium`, `lts/fermium`, `lts/*`

### Caching packages dependencies

The action has a built-in functionality for caching and restoring npm/yarn dependencies. Supported package managers are `npm`, `yarn`. The `cache` input is optional, and caching is turned off by default.
The action has a built-in functionality for caching and restoring npm/yarn dependencies. Supported package managers are `npm`, `pnpm`, `yarn`. The `cache` input is optional, and caching is turned off by default.

**Caching npm dependencies:**
```yaml
Expand All @@ -55,6 +55,26 @@ steps:
- run: npm test
```

**Caching pnpm dependencies:**
```yaml
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

steps:
- uses: actions/checkout@v2
- uses: pnpm/action-setup@646cdf48217256a3d0b80361c5a50727664284f2
with:
version: 6.9.0
- uses: actions/setup-node@v2
with:
node-version: '14'
cache: 'pnpm'
- run: pnpm install
- run: pnpm test
```

**Caching yarn dependencies:**
```yaml
steps:
Expand Down
16 changes: 13 additions & 3 deletions __tests__/cache-restore.test.ts
Expand Up @@ -14,14 +14,18 @@ describe('cache-restore', () => {
const platform = process.env.RUNNER_OS;
const commonPath = '/some/random/path';
const npmCachePath = `${commonPath}/npm`;
const pnpmCachePath = `${commonPath}/pnpm`;
const yarn1CachePath = `${commonPath}/yarn1`;
const yarn2CachePath = `${commonPath}/yarn2`;
const yarnFileHash =
'b8a0bae5243251f7c07dd52d1f78ff78281dfefaded700a176261b6b54fa245b';
const npmFileHash =
'abf7c9b306a3149dcfba4673e2362755503bcceaab46f0e4e6fee0ade493e20c';
const pnpmFileHash =
'26309058093e84713f38869c50cf1cee9b08155ede874ec1b44ce3fca8c68c70';
const cachesObject = {
[npmCachePath]: npmFileHash,
[pnpmCachePath]: pnpmFileHash,
[yarn1CachePath]: yarnFileHash,
[yarn2CachePath]: yarnFileHash
};
Expand All @@ -30,6 +34,8 @@ describe('cache-restore', () => {
switch (command) {
case utils.supportedPackageManagers.npm.getCacheFolderCommand:
return npmCachePath;
case utils.supportedPackageManagers.pnpm.getCacheFolderCommand:
return pnpmCachePath;
case utils.supportedPackageManagers.yarn1.getCacheFolderCommand:
return yarn1CachePath;
case utils.supportedPackageManagers.yarn2.getCacheFolderCommand:
Expand Down Expand Up @@ -66,6 +72,8 @@ describe('cache-restore', () => {
hashFilesSpy.mockImplementation((pattern: string) => {
if (pattern.includes('package-lock.json')) {
return npmFileHash;
} else if (pattern.includes('pnpm-lock.yaml')) {
return pnpmFileHash;
} else if (pattern.includes('yarn.lock')) {
return yarnFileHash;
} else {
Expand Down Expand Up @@ -97,7 +105,7 @@ describe('cache-restore', () => {
});

describe('Validate provided package manager', () => {
it.each([['npm7'], ['npm6'], ['yarn1'], ['yarn2'], ['random']])(
it.each([['npm7'], ['npm6'], ['pnpm6'], ['yarn1'], ['yarn2'], ['random']])(
'Throw an error because %s is not supported',
async packageManager => {
await expect(restoreCache(packageManager)).rejects.toThrowError(
Expand All @@ -111,7 +119,8 @@ describe('cache-restore', () => {
it.each([
['yarn', '2.1.2', yarnFileHash],
['yarn', '1.2.3', yarnFileHash],
['npm', '', npmFileHash]
['npm', '', npmFileHash],
['pnpm', '', pnpmFileHash]
])(
'restored dependencies for %s',
async (packageManager, toolVersion, fileHash) => {
Expand Down Expand Up @@ -139,7 +148,8 @@ describe('cache-restore', () => {
it.each([
['yarn', '2.1.2', yarnFileHash],
['yarn', '1.2.3', yarnFileHash],
['npm', '', npmFileHash]
['npm', '', npmFileHash],
['pnpm', '', pnpmFileHash]
])(
'dependencies are changed %s',
async (packageManager, toolVersion, fileHash) => {
Expand Down
46 changes: 46 additions & 0 deletions __tests__/cache-save.test.ts
Expand Up @@ -12,6 +12,8 @@ describe('run', () => {
'b8a0bae5243251f7c07dd52d1f78ff78281dfefaded700a176261b6b54fa245b';
const npmFileHash =
'abf7c9b306a3149dcfba4673e2362755503bcceaab46f0e4e6fee0ade493e20c';
const pnpmFileHash =
'26309058093e84713f38869c50cf1cee9b08155ede874ec1b44ce3fca8c68c70';
const commonPath = '/some/random/path';
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data');

Expand Down Expand Up @@ -150,6 +152,23 @@ describe('run', () => {
);
expect(setFailedSpy).not.toHaveBeenCalled();
});

it('should not save cache for pnpm', async () => {
inputs['cache'] = 'pnpm';
getStateSpy.mockImplementation(() => pnpmFileHash);
getCommandOutputSpy.mockImplementationOnce(() => `${commonPath}/pnpm`);

await run();

expect(getInputSpy).toHaveBeenCalled();
expect(getStateSpy).toHaveBeenCalledTimes(2);
expect(getCommandOutputSpy).toHaveBeenCalledTimes(1);
expect(debugSpy).toHaveBeenCalledWith(`pnpm path is ${commonPath}/pnpm`);
expect(infoSpy).toHaveBeenCalledWith(
`Cache hit occurred on the primary key ${pnpmFileHash}, not saving cache.`
);
expect(setFailedSpy).not.toHaveBeenCalled();
});
});

describe('action saves the cache', () => {
Expand Down Expand Up @@ -239,6 +258,33 @@ describe('run', () => {
);
expect(setFailedSpy).not.toHaveBeenCalled();
});

it('saves cache from pnpm', async () => {
inputs['cache'] = 'pnpm';
getStateSpy.mockImplementation((name: string) => {
if (name === State.CacheMatchedKey) {
return pnpmFileHash;
} else {
return npmFileHash;
}
});
getCommandOutputSpy.mockImplementationOnce(() => `${commonPath}/pnpm`);

await run();

expect(getInputSpy).toHaveBeenCalled();
expect(getStateSpy).toHaveBeenCalledTimes(2);
expect(getCommandOutputSpy).toHaveBeenCalledTimes(1);
expect(debugSpy).toHaveBeenCalledWith(`pnpm path is ${commonPath}/pnpm`);
expect(infoSpy).not.toHaveBeenCalledWith(
`Cache hit occurred on the primary key ${pnpmFileHash}, not saving cache.`
);
expect(saveCacheSpy).toHaveBeenCalled();
expect(infoSpy).toHaveBeenLastCalledWith(
`Cache saved with the key: ${npmFileHash}`
);
expect(setFailedSpy).not.toHaveBeenCalled();
});
});

afterEach(() => {
Expand Down
5 changes: 5 additions & 0 deletions __tests__/cache-utils.test.ts
Expand Up @@ -14,6 +14,10 @@ describe('cache-utils', () => {
function getPackagePath(name: string) {
if (name === utils.supportedPackageManagers.npm.getCacheFolderCommand) {
return `${commonPath}/npm`;
} else if (
name === utils.supportedPackageManagers.pnpm.getCacheFolderCommand
) {
return `${commonPath}/pnpm`;
} else {
if (name === utils.supportedPackageManagers.yarn1.getCacheFolderCommand) {
return `${commonPath}/yarn1`;
Expand All @@ -34,6 +38,7 @@ describe('cache-utils', () => {
describe('getPackageManagerInfo', () => {
it.each<[string, PackageManagerInfo | null]>([
['npm', utils.supportedPackageManagers.npm],
['pnpm', utils.supportedPackageManagers.pnpm],
['yarn', utils.supportedPackageManagers.yarn1],
['yarn1', null],
['yarn2', null],
Expand Down