Skip to content

Commit e5fe105

Browse files
authored
feat: Add support for yarn berry (#229)
* fix: Do not exit in test environment * fix: Use --immutable instead of --frozen-lockfile when yarn version is not 1.x.x * fix: Fix input paths * test(action): Add test for yarn berry * fix: Remove env assignment from test script
1 parent e6e7443 commit e5fe105

File tree

4 files changed

+135
-6
lines changed

4 files changed

+135
-6
lines changed

dist/index.js

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ const install = (opts = {}) => {
110110
}
111111

112112
const shouldUseYarn = opts.useYarn
113+
const shouldUseYarnV1 = opts.useYarnV1 ?? true
113114
const shouldUsePackageLock = opts.usePackageLock
114115
const npmCacheFolder = opts.npmCacheFolder
115116
if (!npmCacheFolder) {
@@ -134,7 +135,9 @@ const install = (opts = {}) => {
134135
return io.which('yarn', true).then(yarnPath => {
135136
console.log('yarn at "%s"', yarnPath)
136137

137-
const args = shouldUsePackageLock ? ['--frozen-lockfile'] : []
138+
const args = shouldUsePackageLock
139+
? [shouldUseYarnV1 ? '--frozen-lockfile' : '--immutable']
140+
: []
138141
core.debug(
139142
`yarn command: "${yarnPath}" ${args} ${JSON.stringify(options)}`
140143
)
@@ -189,6 +192,7 @@ const getLockFilename = usePackageLock => workingDirectory => {
189192

190193
const getCacheParams = ({
191194
useYarn,
195+
useYarnV1,
192196
useRollingCache,
193197
homeDirectory,
194198
npmCacheFolder,
@@ -206,7 +210,9 @@ const getCacheParams = ({
206210
let inputPaths, restoreKeys
207211

208212
if (useYarn) {
209-
inputPaths = [path.join(homeDirectory, '.cache', 'yarn')]
213+
inputPaths = useYarnV1
214+
? [path.join(homeDirectory, '.cache', 'yarn')]
215+
: [path.join(homeDirectory, '.yarn', 'berry', 'cache')]
210216
primaryKeySegments.unshift('yarn')
211217
} else {
212218
inputPaths = [npmCacheFolder]
@@ -232,7 +238,7 @@ const getCacheParams = ({
232238
return { primaryKey: primaryKeySegments.join('-'), inputPaths, restoreKeys }
233239
}
234240

235-
const installInOneFolder = ({
241+
const installInOneFolder = async ({
236242
usePackageLock,
237243
workingDirectory,
238244
useRollingCache,
@@ -259,13 +265,21 @@ const installInOneFolder = ({
259265
core.debug('using NPM command, not using Yarn cache paths')
260266
useYarn = false
261267
}
268+
let useYarnV1 = true
269+
if (useYarn) {
270+
const { stdout: yarnVersion } = await exec.getExecOutput('yarn', [
271+
'--version'
272+
])
273+
useYarnV1 = /^1/.test(yarnVersion)
274+
}
262275

263276
// enforce the same NPM cache folder across different operating systems
264277
const homeDirectory = os.homedir()
265278
const NPM_CACHE_FOLDER = path.join(homeDirectory, '.npm')
266279

267280
const NPM_CACHE = getCacheParams({
268281
useYarn,
282+
useYarnV1,
269283
homeDirectory,
270284
useRollingCache,
271285
npmCacheFolder: NPM_CACHE_FOLDER,
@@ -275,6 +289,7 @@ const installInOneFolder = ({
275289

276290
const opts = {
277291
useYarn,
292+
useYarnV1,
278293
usePackageLock,
279294
workingDirectory,
280295
npmCacheFolder: NPM_CACHE_FOLDER,

index.js

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ const install = (opts = {}) => {
103103
}
104104

105105
const shouldUseYarn = opts.useYarn
106+
const shouldUseYarnV1 = opts.useYarnV1 ?? true
106107
const shouldUsePackageLock = opts.usePackageLock
107108
const npmCacheFolder = opts.npmCacheFolder
108109
if (!npmCacheFolder) {
@@ -127,7 +128,9 @@ const install = (opts = {}) => {
127128
return io.which('yarn', true).then(yarnPath => {
128129
console.log('yarn at "%s"', yarnPath)
129130

130-
const args = shouldUsePackageLock ? ['--frozen-lockfile'] : []
131+
const args = shouldUsePackageLock
132+
? [shouldUseYarnV1 ? '--frozen-lockfile' : '--immutable']
133+
: []
131134
core.debug(
132135
`yarn command: "${yarnPath}" ${args} ${JSON.stringify(options)}`
133136
)
@@ -182,6 +185,7 @@ const getLockFilename = usePackageLock => workingDirectory => {
182185

183186
const getCacheParams = ({
184187
useYarn,
188+
useYarnV1,
185189
useRollingCache,
186190
homeDirectory,
187191
npmCacheFolder,
@@ -199,7 +203,9 @@ const getCacheParams = ({
199203
let inputPaths, restoreKeys
200204

201205
if (useYarn) {
202-
inputPaths = [path.join(homeDirectory, '.cache', 'yarn')]
206+
inputPaths = useYarnV1
207+
? [path.join(homeDirectory, '.cache', 'yarn')]
208+
: [path.join(homeDirectory, '.yarn', 'berry', 'cache')]
203209
primaryKeySegments.unshift('yarn')
204210
} else {
205211
inputPaths = [npmCacheFolder]
@@ -225,7 +231,7 @@ const getCacheParams = ({
225231
return { primaryKey: primaryKeySegments.join('-'), inputPaths, restoreKeys }
226232
}
227233

228-
const installInOneFolder = ({
234+
const installInOneFolder = async ({
229235
usePackageLock,
230236
workingDirectory,
231237
useRollingCache,
@@ -252,13 +258,21 @@ const installInOneFolder = ({
252258
core.debug('using NPM command, not using Yarn cache paths')
253259
useYarn = false
254260
}
261+
let useYarnV1 = true
262+
if (useYarn) {
263+
const { stdout: yarnVersion } = await exec.getExecOutput('yarn', [
264+
'--version'
265+
])
266+
useYarnV1 = /^1/.test(yarnVersion)
267+
}
255268

256269
// enforce the same NPM cache folder across different operating systems
257270
const homeDirectory = os.homedir()
258271
const NPM_CACHE_FOLDER = path.join(homeDirectory, '.npm')
259272

260273
const NPM_CACHE = getCacheParams({
261274
useYarn,
275+
useYarnV1,
262276
homeDirectory,
263277
useRollingCache,
264278
npmCacheFolder: NPM_CACHE_FOLDER,
@@ -268,6 +282,7 @@ const installInOneFolder = ({
268282

269283
const opts = {
270284
useYarn,
285+
useYarnV1,
271286
usePackageLock,
272287
workingDirectory,
273288
npmCacheFolder: NPM_CACHE_FOLDER,

test/action-spec.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ describe('action', () => {
5555
.withArgs(yarnFilename)
5656
.returns('hash-from-yarn-lock-file')
5757

58+
sandbox
59+
.stub(exec, 'getExecOutput')
60+
.withArgs('yarn', ['--version'])
61+
.resolves({ stdout: '1.22.19' })
62+
5863
const cacheHit = false
5964
this.restoreCache = sandbox.stub(cache, 'restoreCache').resolves(cacheHit)
6065
this.saveCache = sandbox.stub(cache, 'saveCache').resolves()
@@ -80,6 +85,63 @@ describe('action', () => {
8085
})
8186
})
8287

88+
context('finds Yarn berry', function() {
89+
const pathToYarn = '/path/to/yarn'
90+
const yarnFilename = path.join(cwd, 'yarn.lock')
91+
const yarnCachePaths = [path.join(homedir, '.yarn', 'berry', 'cache')]
92+
const cacheKey = 'yarn-platform-arch-hash-from-yarn-lock-file'
93+
94+
beforeEach(function() {
95+
sandbox
96+
.stub(core, 'getInput')
97+
.withArgs('useLockFile')
98+
.returns()
99+
100+
sandbox
101+
.stub(fs, 'existsSync')
102+
.withArgs(yarnFilename)
103+
.returns(true)
104+
105+
sandbox
106+
.stub(io, 'which')
107+
.withArgs('yarn')
108+
.resolves(pathToYarn)
109+
110+
sandbox
111+
.stub(hasha, 'fromFileSync')
112+
.withArgs(yarnFilename)
113+
.returns('hash-from-yarn-lock-file')
114+
115+
sandbox
116+
.stub(exec, 'getExecOutput')
117+
.withArgs('yarn', ['--version'])
118+
.resolves({ stdout: '4.2.1' })
119+
120+
const cacheHit = false
121+
this.restoreCache = sandbox.stub(cache, 'restoreCache').resolves(cacheHit)
122+
this.saveCache = sandbox.stub(cache, 'saveCache').resolves()
123+
})
124+
125+
it('and uses lock file', async function() {
126+
await action.npmInstallAction()
127+
128+
expect(this.restoreCache).to.be.calledOnceWithExactly(
129+
yarnCachePaths,
130+
cacheKey,
131+
[cacheKey]
132+
)
133+
expect(this.exec).to.be.calledOnceWithExactly(
134+
quote(pathToYarn),
135+
['--immutable'],
136+
{ cwd }
137+
)
138+
expect(this.saveCache).to.be.calledOnceWithExactly(
139+
yarnCachePaths,
140+
cacheKey
141+
)
142+
})
143+
})
144+
83145
context('does not find Yarn and uses NPM', function() {
84146
const yarnFilename = path.join(cwd, 'yarn.lock')
85147
const npmShrinkwrapFilename = path.join(cwd, 'npm-shrinkwrap.json')
@@ -316,6 +378,11 @@ describe('action', () => {
316378
.withArgs(yarnFilename)
317379
.returns('hash-from-yarn-lock-file')
318380

381+
sandbox
382+
.stub(exec, 'getExecOutput')
383+
.withArgs('yarn', ['--version'])
384+
.resolves({ stdout: '1.22.19' })
385+
319386
const cacheHit = false
320387
this.restoreCache = sandbox.stub(cache, 'restoreCache').resolves(cacheHit)
321388
this.saveCache = sandbox.stub(cache, 'saveCache').resolves()
@@ -449,6 +516,11 @@ describe('action', () => {
449516
.withArgs(yarnFilename)
450517
.returns('hash-from-yarn-lock-file')
451518

519+
sandbox
520+
.stub(exec, 'getExecOutput')
521+
.withArgs('yarn', ['--version'])
522+
.resolves({ stdout: '1.22.19' })
523+
452524
const cacheHit = false
453525
this.restoreCache = sandbox.stub(cache, 'restoreCache').resolves(cacheHit)
454526
this.saveCache = sandbox.stub(cache, 'saveCache').resolves()

test/install-spec.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ describe('install command', () => {
2121
it('uses absolute working directory', async function() {
2222
const opts = {
2323
useYarn: true,
24+
useYarnV1: true,
2425
usePackageLock: true,
2526
// only use relative path
2627
workingDirectory: 'directory',
@@ -54,6 +55,7 @@ describe('install command', () => {
5455
it('and lock file', async function() {
5556
const opts = {
5657
useYarn: true,
58+
useYarnV1: true,
5759
usePackageLock: true,
5860
workingDirectory,
5961
npmCacheFolder
@@ -73,6 +75,7 @@ describe('install command', () => {
7375
it('without lock file', async function() {
7476
const opts = {
7577
useYarn: true,
78+
useYarnV1: true,
7679
usePackageLock: false,
7780
workingDirectory,
7881
npmCacheFolder
@@ -88,6 +91,26 @@ describe('install command', () => {
8891
{ cwd: workingDirectory }
8992
)
9093
})
94+
95+
it('uses Yarn berry', async function() {
96+
const opts = {
97+
useYarn: true,
98+
useYarnV1: false,
99+
usePackageLock: true,
100+
workingDirectory,
101+
npmCacheFolder
102+
}
103+
sandbox
104+
.stub(io, 'which')
105+
.withArgs('yarn')
106+
.resolves(pathToYarn)
107+
await action.utils.install(opts)
108+
expect(this.exec).to.have.been.calledOnceWithExactly(
109+
quote(pathToYarn),
110+
['--immutable'],
111+
{ cwd: workingDirectory }
112+
)
113+
})
91114
})
92115

93116
context('using NPM', () => {
@@ -100,6 +123,7 @@ describe('install command', () => {
100123
it('uses absolute working directory', async function() {
101124
const opts = {
102125
useYarn: false,
126+
useYarnV1: false,
103127
usePackageLock: true,
104128
// only use relative path
105129
workingDirectory: 'directory',
@@ -127,6 +151,7 @@ describe('install command', () => {
127151
it('installs using lock file', async function() {
128152
const opts = {
129153
useYarn: false,
154+
useYarnV1: false,
130155
usePackageLock: true,
131156
workingDirectory,
132157
npmCacheFolder
@@ -151,6 +176,7 @@ describe('install command', () => {
151176
it('installs without a lock file', async function() {
152177
const opts = {
153178
useYarn: false,
179+
useYarnV1: false,
154180
usePackageLock: false,
155181
workingDirectory,
156182
npmCacheFolder
@@ -177,6 +203,7 @@ describe('install command', () => {
177203
it('calls exec directly', async function() {
178204
const opts = {
179205
useYarn: true,
206+
useYarnV1: false,
180207
usePackageLock: true,
181208
// only use relative path
182209
workingDirectory: 'directory',

0 commit comments

Comments
 (0)