Skip to content

Commit 64c749e

Browse files
committed
fix(pipeline): cache yarn deps with corepack
1 parent 78ac431 commit 64c749e

File tree

4 files changed

+134
-20
lines changed

4 files changed

+134
-20
lines changed

packages/@o3r/pipeline/schematics/ng-add/index.spec.ts

+68
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,71 @@ describe('ng-add', () => {
6565
});
6666

6767
});
68+
69+
describe('ng-add with yarn should generate a GitHub workflow', () => {
70+
71+
let initialTree: Tree;
72+
73+
beforeEach(() => {
74+
initialTree = Tree.empty();
75+
initialTree.create('angular.json', fs.readFileSync(path.resolve(__dirname, '..', '..', 'testing', 'mocks', 'angular.mocks.yarn.json')));
76+
});
77+
78+
it('when no yarnrc.yml', async () => {
79+
const runner = new SchematicTestRunner('@o3r/pipeline', collectionPath);
80+
const tree = await runner.runSchematic('ng-add', {
81+
toolchain: 'github'
82+
} as NgAddSchematicsSchema, initialTree);
83+
84+
expect(tree.exists('.github/actions/setup/action.yml')).toBe(true);
85+
expect(tree.exists('.github/workflows/main.yml')).toBe(true);
86+
expect(tree.exists('.npmrc')).toBe(false);
87+
88+
expect(tree.readText('.github/actions/setup/action.yml')).toContain('(yarn cache dir)');
89+
90+
});
91+
92+
it('with yarnrc.yml without yarnPath', async () => {
93+
const runner = new SchematicTestRunner('@o3r/pipeline', collectionPath);
94+
initialTree.create('.yarnrc.yml', '');
95+
const tree = await runner.runSchematic('ng-add', {
96+
toolchain: 'github'
97+
} as NgAddSchematicsSchema, initialTree);
98+
99+
expect(tree.exists('.github/actions/setup/action.yml')).toBe(true);
100+
expect(tree.exists('.github/workflows/main.yml')).toBe(true);
101+
expect(tree.exists('.npmrc')).toBe(false);
102+
103+
expect(tree.readText('.github/actions/setup/action.yml')).toContain('(yarn config get cacheFolder)');
104+
});
105+
106+
it('with yarnrc.yml with yarnPath v1', async () => {
107+
const runner = new SchematicTestRunner('@o3r/pipeline', collectionPath);
108+
initialTree.create('.yarnrc.yml', 'yarnPath: .yarn/releases/yarn-1.2.3.cjs');
109+
const tree = await runner.runSchematic('ng-add', {
110+
toolchain: 'github'
111+
} as NgAddSchematicsSchema, initialTree);
112+
113+
expect(tree.exists('.github/actions/setup/action.yml')).toBe(true);
114+
expect(tree.exists('.github/workflows/main.yml')).toBe(true);
115+
expect(tree.exists('.npmrc')).toBe(false);
116+
117+
expect(tree.readText('.github/actions/setup/action.yml')).toContain('(yarn cache dir)');
118+
expect(tree.readText('.github/actions/setup/action.yml')).not.toContain('.yarn/unplugged');
119+
});
120+
121+
it('with yarnrc.yml with yarnPath v2', async () => {
122+
const runner = new SchematicTestRunner('@o3r/pipeline', collectionPath);
123+
initialTree.create('.yarnrc.yml', 'yarnPath: .yarn/releases/yarn-3.2.1.cjs');
124+
const tree = await runner.runSchematic('ng-add', {
125+
toolchain: 'github'
126+
} as NgAddSchematicsSchema, initialTree);
127+
128+
expect(tree.exists('.github/actions/setup/action.yml')).toBe(true);
129+
expect(tree.exists('.github/workflows/main.yml')).toBe(true);
130+
expect(tree.exists('.npmrc')).toBe(false);
131+
132+
expect(tree.readText('.github/actions/setup/action.yml')).toContain('(yarn config get cacheFolder)');
133+
expect(tree.readText('.github/actions/setup/action.yml')).toContain('.yarn/unplugged');
134+
});
135+
});

packages/@o3r/pipeline/schematics/ng-add/index.ts

+26-13
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
1-
import { apply, chain, MergeStrategy, mergeWith, move, Rule, template, url } from '@angular-devkit/schematics';
2-
import type { NgAddSchematicsSchema } from './schema';
3-
import {dump, load} from 'js-yaml';
4-
import * as path from 'node:path';
1+
import { apply, chain, MergeStrategy, mergeWith, move, Rule, template, type Tree, url } from '@angular-devkit/schematics';
2+
import { dump, load } from 'js-yaml';
53
import * as fs from 'node:fs';
4+
import * as path from 'node:path';
65
import type { PackageJson } from 'type-fest';
6+
import type { NgAddSchematicsSchema } from './schema';
7+
8+
/**
9+
* Determines if the Yarn version is 2 or higher based on the contents of the .yarnrc.yml file.
10+
* @param tree tree
11+
*/
12+
function isYarn2(tree: Tree) {
13+
const yarnrcPath = '/.yarnrc.yml';
14+
if (tree.exists(yarnrcPath)) {
15+
// const yarnrcContent = load(tree.readText(yarnrcPath)) as any;
16+
const { yarnPath } = (load(tree.readText(yarnrcPath)) || {}) as { yarnPath?: string };
17+
return !yarnPath || !/yarn-1\./.test(yarnPath);
18+
}
19+
return false;
20+
}
721

822
/**
923
* Add an Otter CI pipeline to an Angular Project
@@ -26,12 +40,14 @@ function ngAddFn(options: NgAddSchematicsSchema): Rule {
2640
}
2741
context.logger.info(`Setting up pipeline for package manager: "${packageManager}" `);
2842
const setupCommand = packageManager === 'yarn' ? 'yarn install --immutable' : 'npm ci';
43+
const yarn2 = packageManager === 'yarn' && isYarn2(tree);
2944
const baseTemplateSource = apply(url(`./templates/${options.toolchain}`), [
3045
template({
3146
...options,
3247
packageManager,
3348
setupCommand,
3449
actionVersionString,
50+
yarn2,
3551
dot: '.'
3652
}),
3753
move(tree.root.path)
@@ -41,16 +57,13 @@ function ngAddFn(options: NgAddSchematicsSchema): Rule {
4157
if (!options.npmRegistry) {
4258
return tree;
4359
}
44-
if (packageManager === 'yarn') {
60+
if (yarn2) {
4561
const yarnrcPath = '/.yarnrc.yml';
46-
if (!tree.exists(yarnrcPath)) {
47-
tree.create(yarnrcPath, dump({'npmRegistryServer': options.npmRegistry}, {indent: 2}));
48-
} else {
49-
const yarnrcContent = load(tree.readText(yarnrcPath)) as any;
50-
yarnrcContent.npmRegistryServer = options.npmRegistry;
51-
tree.overwrite(yarnrcPath, dump(yarnrcContent, {indent: 2}));
52-
}
53-
} else if (packageManager === 'npm') {
62+
const yarnrcContent = load(tree.readText(yarnrcPath)) as { npmRegistryServer?: string };
63+
yarnrcContent.npmRegistryServer = options.npmRegistry;
64+
tree.overwrite(yarnrcPath, dump(yarnrcContent, {indent: 2}));
65+
} else {
66+
// both npm and yarn 1 use .npmrc for the registry
5467
const npmrcPath = '/.npmrc';
5568
if (!tree.exists(npmrcPath)) {
5669
tree.create(npmrcPath, `registry=${options.npmRegistry}`);

packages/@o3r/pipeline/schematics/ng-add/templates/github/__dot__github/actions/setup/action.yml

+21-7
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,30 @@ runs:
66
steps:
77
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
88
with:
9-
node-version: 20
10-
cache: <%= packageManager %>
9+
node-version: 20<% if (packageManager !== 'yarn') { %>
10+
cache: <%= packageManager %><% } %>
1111
- name: Enable Corepack
1212
shell: bash
13-
run: corepack enable
14-
- name: Install
15-
<% if (npmRegistry) { %>
13+
run: corepack enable<% if (packageManager === 'yarn') { %>
14+
- name: Get yarn cache directory path
15+
shell: bash
16+
id: yarn-cache-dir-path
17+
run: echo "dir=$(<% if (yarn2) { %>yarn config get cacheFolder<% } else { %>yarn cache dir<% } %>)" >> $GITHUB_OUTPUT
18+
- name: Cache dependencies
19+
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
20+
with:
21+
path: |
22+
${{ steps.yarn-cache-dir-path.outputs.dir }}
23+
<% if (yarn2) { %>.yarn/unplugged
24+
.pnp.cjs
25+
.pnp.loader.mjs<% } %>
26+
key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}
27+
restore-keys: |
28+
${{ runner.os }}-yarn
29+
${{ runner.os }}<% } %>
30+
- name: Install<% if (npmRegistry) { %>
1631
env:
1732
COREPACK_NPM_REGISTRY: <%= npmRegistry %>
18-
COREPACK_INTEGRITY_KEYS: ""
19-
<% } %>
33+
COREPACK_INTEGRITY_KEYS: ""<% } %>
2034
shell: bash
2135
run: <%= setupCommand %>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"$schema": "https://raw.githubusercontent.com/angular/angular-cli/master/packages/angular/cli/lib/config/workspace-schema.json",
3+
"version": 1,
4+
"newProjectRoot": ".",
5+
"cli": {
6+
"packageManager": "yarn"
7+
},
8+
"projects": {
9+
"test-project": {
10+
"projectType": "application",
11+
"root": ".",
12+
"sourceRoot": "./src",
13+
"prefix": "tst",
14+
"architect": {
15+
16+
}
17+
}
18+
}
19+
}

0 commit comments

Comments
 (0)