Skip to content

Commit 3d0d4ed

Browse files
Add node's caching implementation (#2)
* first iteration for implementation of caching * add logs * add debug line * fix build command * fix path * add possible post-if * remove braces * test new action post-if variant * work on built-in caching * remove post-if * pass version * work on yarn support * fix return value * change names and remove logs * worked on resolving comments * check post-if for null * add success() condition * remove primary key field * work on resolving comments * remove logs * resolving comments * resolving comments * resolving comments * resolving comments * fix getpackageManagerVersion * run clean for unstaged changes * fix falling version tests * work on resolving comments * resolving comments * fix comment * resolve comments * Add tests to cover node's caching (#3) * add tests to cover node's caching * work on fixing tests * fix e2e tests * rebuild and fix test * fixing tests * change name of describes, it and fix test * add names for jobs * fix issue
1 parent 5c355be commit 3d0d4ed

20 files changed

+149068
-21767
lines changed

.github/workflows/build-test.yml

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ jobs:
2727
- run: npm run build
2828
- run: npm run format-check
2929
- run: npm test
30+
- run: git add .
3031
- name: Verify no unstaged changes
3132
if: runner.os != 'windows'
3233
run: __tests__/verify-no-unstaged-changes.sh

.github/workflows/e2e-cache.yml

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
name: e2e-cache
2+
3+
on:
4+
pull_request:
5+
paths-ignore:
6+
- '**.md'
7+
push:
8+
branches:
9+
- main
10+
- releases/*
11+
paths-ignore:
12+
- '**.md'
13+
14+
jobs:
15+
node-npm-depencies-caching:
16+
name: Test npm (Node ${{ matrix.node-version}}, ${{ matrix.os }})
17+
runs-on: ${{ matrix.os }}
18+
strategy:
19+
fail-fast: false
20+
matrix:
21+
os: [ubuntu-latest, windows-latest, macos-latest]
22+
node-version: [10, 12, 14]
23+
steps:
24+
- uses: actions/checkout@v2
25+
- name: Clean global cache
26+
run: npm cache clean --force
27+
- name: Setup Node
28+
uses: ./
29+
with:
30+
node-version: ${{ matrix.node-version }}
31+
cache: 'npm'
32+
- name: Install dependencies
33+
run: npm install
34+
- name: Verify node and npm
35+
run: __tests__/verify-node.sh "${{ matrix.node-version }}"
36+
shell: bash
37+
38+
node-yarn1-depencies-caching:
39+
name: Test yarn 1 (Node ${{ matrix.node-version}}, ${{ matrix.os }})
40+
runs-on: ${{ matrix.os }}
41+
strategy:
42+
fail-fast: false
43+
matrix:
44+
os: [ubuntu-latest, windows-latest, macos-latest]
45+
node-version: [10, 12, 14]
46+
steps:
47+
- uses: actions/checkout@v2
48+
- name: Yarn version
49+
run: yarn --version
50+
- name: Generate yarn file
51+
run: yarn install
52+
- name: Remove dependencies
53+
shell: pwsh
54+
run: Remove-Item node_modules -Force -Recurse
55+
- name: Clean global cache
56+
run: yarn cache clean
57+
- name: Setup Node
58+
uses: ./
59+
with:
60+
node-version: ${{ matrix.node-version }}
61+
cache: 'yarn'
62+
- name: Install dependencies
63+
run: yarn install
64+
- name: Verify node and yarn
65+
run: __tests__/verify-node.sh "${{ matrix.node-version }}"
66+
shell: bash
67+
68+
node-yarn2-depencies-caching:
69+
name: Test yarn 2 (Node ${{ matrix.node-version}}, ${{ matrix.os }})
70+
runs-on: ${{ matrix.os }}
71+
strategy:
72+
fail-fast: false
73+
matrix:
74+
os: [ubuntu-latest, windows-latest, macos-latest]
75+
node-version: [10, 12, 14]
76+
steps:
77+
- uses: actions/checkout@v2
78+
- name: Update yarn
79+
run: yarn set version berry
80+
- name: Yarn version
81+
run: yarn --version
82+
- name: Generate simple .yarnrc.yml
83+
run: |
84+
echo "nodeLinker: node-modules" >> .yarnrc.yml
85+
- name: Generate yarn file
86+
run: yarn install
87+
- name: Remove dependencies
88+
shell: pwsh
89+
run: Remove-Item node_modules -Force -Recurse
90+
- name: Clean global cache
91+
run: yarn cache clean --all
92+
- name: Setup Node
93+
uses: ./
94+
with:
95+
node-version: ${{ matrix.node-version }}
96+
cache: 'yarn'
97+
- name: Install dependencies
98+
run: yarn install
99+
- name: Verify node and yarn
100+
run: __tests__/verify-node.sh "${{ matrix.node-version }}"
101+
shell: bash

.github/workflows/versions.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,4 @@ jobs:
109109
architecture: 'x86'
110110
- name: Verify node
111111
run: __tests__/verify-arch.sh "ia32"
112-
shell: bash
112+
shell: bash

__tests__/cache-restore.test.ts

+168
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
import * as core from '@actions/core';
2+
import * as cache from '@actions/cache';
3+
import * as path from 'path';
4+
import * as glob from '@actions/glob';
5+
6+
import * as utils from '../src/cache-utils';
7+
import {restoreCache} from '../src/cache-restore';
8+
9+
describe('cache-restore', () => {
10+
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data');
11+
if (!process.env.RUNNER_OS) {
12+
process.env.RUNNER_OS = 'Linux';
13+
}
14+
const platform = process.env.RUNNER_OS;
15+
const commonPath = '/some/random/path';
16+
const npmCachePath = `${commonPath}/npm`;
17+
const yarn1CachePath = `${commonPath}/yarn1`;
18+
const yarn2CachePath = `${commonPath}/yarn2`;
19+
const yarnFileHash =
20+
'b8a0bae5243251f7c07dd52d1f78ff78281dfefaded700a176261b6b54fa245b';
21+
const npmFileHash =
22+
'abf7c9b306a3149dcfba4673e2362755503bcceaab46f0e4e6fee0ade493e20c';
23+
const cachesObject = {
24+
[npmCachePath]: npmFileHash,
25+
[yarn1CachePath]: yarnFileHash,
26+
[yarn2CachePath]: yarnFileHash
27+
};
28+
29+
function findCacheFolder(command: string) {
30+
switch (command) {
31+
case utils.supportedPackageManagers.npm.getCacheFolderCommand:
32+
return npmCachePath;
33+
case utils.supportedPackageManagers.yarn1.getCacheFolderCommand:
34+
return yarn1CachePath;
35+
case utils.supportedPackageManagers.yarn2.getCacheFolderCommand:
36+
return yarn2CachePath;
37+
default:
38+
return 'packge/not/found';
39+
}
40+
}
41+
42+
let saveStateSpy: jest.SpyInstance;
43+
let infoSpy: jest.SpyInstance;
44+
let debugSpy: jest.SpyInstance;
45+
let setOutputSpy: jest.SpyInstance;
46+
let getCommandOutputSpy: jest.SpyInstance;
47+
let restoreCacheSpy: jest.SpyInstance;
48+
let hashFilesSpy: jest.SpyInstance;
49+
50+
beforeEach(() => {
51+
// core
52+
infoSpy = jest.spyOn(core, 'info');
53+
infoSpy.mockImplementation(() => undefined);
54+
55+
debugSpy = jest.spyOn(core, 'debug');
56+
debugSpy.mockImplementation(() => undefined);
57+
58+
setOutputSpy = jest.spyOn(core, 'setOutput');
59+
setOutputSpy.mockImplementation(() => undefined);
60+
61+
saveStateSpy = jest.spyOn(core, 'saveState');
62+
saveStateSpy.mockImplementation(() => undefined);
63+
64+
// glob
65+
hashFilesSpy = jest.spyOn(glob, 'hashFiles');
66+
hashFilesSpy.mockImplementation((pattern: string) => {
67+
if (pattern.includes('package-lock.json')) {
68+
return npmFileHash;
69+
} else if (pattern.includes('yarn.lock')) {
70+
return yarnFileHash;
71+
} else {
72+
return '';
73+
}
74+
});
75+
76+
// cache
77+
restoreCacheSpy = jest.spyOn(cache, 'restoreCache');
78+
restoreCacheSpy.mockImplementation(
79+
(cachePaths: Array<string>, key: string) => {
80+
if (!cachePaths || cachePaths.length === 0) {
81+
return undefined;
82+
}
83+
84+
const cachPath = cachePaths[0];
85+
const fileHash = cachesObject[cachPath];
86+
87+
if (key.includes(fileHash)) {
88+
return key;
89+
}
90+
91+
return undefined;
92+
}
93+
);
94+
95+
// cache-utils
96+
getCommandOutputSpy = jest.spyOn(utils, 'getCommandOutput');
97+
});
98+
99+
describe('Validate provided package manager', () => {
100+
it.each([['npm7'], ['npm6'], ['yarn1'], ['yarn2'], ['random']])(
101+
'Throw an error because %s is not supported',
102+
async packageManager => {
103+
await expect(restoreCache(packageManager)).rejects.toThrowError(
104+
`Caching for '${packageManager}' is not supported`
105+
);
106+
}
107+
);
108+
});
109+
110+
describe('Restore dependencies', () => {
111+
it.each([
112+
['yarn', '2.1.2', yarnFileHash],
113+
['yarn', '1.2.3', yarnFileHash],
114+
['npm', '', npmFileHash]
115+
])(
116+
'restored dependencies for %s',
117+
async (packageManager, toolVersion, fileHash) => {
118+
getCommandOutputSpy.mockImplementation((command: string) => {
119+
if (command.includes('version')) {
120+
return toolVersion;
121+
} else {
122+
return findCacheFolder(command);
123+
}
124+
});
125+
126+
await restoreCache(packageManager);
127+
expect(hashFilesSpy).toHaveBeenCalled();
128+
expect(infoSpy).toHaveBeenCalledWith(
129+
`Cache restored from key: ${platform}-${packageManager}-${fileHash}`
130+
);
131+
expect(infoSpy).not.toHaveBeenCalledWith(
132+
`${packageManager} cache is not found`
133+
);
134+
}
135+
);
136+
});
137+
138+
describe('Dependencies changed', () => {
139+
it.each([
140+
['yarn', '2.1.2', yarnFileHash],
141+
['yarn', '1.2.3', yarnFileHash],
142+
['npm', '', npmFileHash]
143+
])(
144+
'dependencies are changed %s',
145+
async (packageManager, toolVersion, fileHash) => {
146+
getCommandOutputSpy.mockImplementation((command: string) => {
147+
if (command.includes('version')) {
148+
return toolVersion;
149+
} else {
150+
return findCacheFolder(command);
151+
}
152+
});
153+
154+
restoreCacheSpy.mockImplementationOnce(() => undefined);
155+
await restoreCache(packageManager);
156+
expect(hashFilesSpy).toHaveBeenCalled();
157+
expect(infoSpy).toHaveBeenCalledWith(
158+
`${packageManager} cache is not found`
159+
);
160+
}
161+
);
162+
});
163+
164+
afterEach(() => {
165+
jest.resetAllMocks();
166+
jest.clearAllMocks();
167+
});
168+
});

0 commit comments

Comments
 (0)