diff --git a/.github/workflows/example-yarn-modern-pnp.yml b/.github/workflows/example-yarn-modern-pnp.yml index 2d86b860b..f9ffab83c 100644 --- a/.github/workflows/example-yarn-modern-pnp.yml +++ b/.github/workflows/example-yarn-modern-pnp.yml @@ -28,10 +28,8 @@ jobs: run: | npm install -g corepack corepack enable yarn - - name: Custom Yarn command + - name: Custom Yarn Modern command uses: ./ # if copying, replace with cypress-io/github-action@v7 with: working-directory: examples/yarn-modern-pnp - # https://yarnpkg.com/cli/install - install-command: yarn install command: yarn run --binaries-only cypress run diff --git a/.github/workflows/example-yarn-modern.yml b/.github/workflows/example-yarn-modern.yml index 47060e03e..71799422c 100644 --- a/.github/workflows/example-yarn-modern.yml +++ b/.github/workflows/example-yarn-modern.yml @@ -28,9 +28,7 @@ jobs: run: | npm install -g corepack corepack enable yarn - - name: Custom Yarn command + - name: Test with Yarn Modern uses: ./ # if copying, replace with cypress-io/github-action@v7 with: working-directory: examples/yarn-modern - # https://yarnpkg.com/cli/install - install-command: yarn install diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cad22008..94ddec395 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ See [Releases](https://github.com/cypress-io/github-action/releases) for full de | Version | Changes | | ------- | ------------------------------------------------------------------------------------------------------------ | +| v7.5.0 | Add Yarn Modern `yarn install --immutable` | | v7.4.0 | Examples remove Node.js 25. End of support for Node.js 25. | | v7.3.0 | Add parameter `expose` for [`Cypress.expose()`](https://docs.cypress.io/api/cypress-api/expose) support | | v7.2.0 | Examples remove Node.js 20. End of support for Node.js 20. | diff --git a/README.md b/README.md index 84e3eafc6..f725b9d31 100644 --- a/README.md +++ b/README.md @@ -1262,7 +1262,7 @@ See the example project [start-and-pnpm-workspaces](examples/start-and-pnpm-work ### Yarn Classic -If a `yarn.lock` file is found, the action uses the [Yarn 1 (Classic)](https://classic.yarnpkg.com/) command `yarn --frozen-lockfile` by default to install dependencies. +If a `yarn.lock` file is found, the action uses the [Yarn 1 (Classic)](https://classic.yarnpkg.com/) command `yarn --frozen-lockfile` by default to install dependencies, unless [Yarn Modern](#yarn-modern) is detected. ```yaml name: example-yarn-classic @@ -1283,7 +1283,7 @@ jobs: ### Yarn Modern -To install dependencies using a `yarn.lock` file from [Yarn Modern](https://yarnpkg.com/) (Yarn 2 and later) you need to override the default [Yarn 1 (Classic)](https://classic.yarnpkg.com/) installation command `yarn --frozen-lockfile`. You can do this by using the `install-command` parameter and specifying `yarn install` as in the example below. +If a `yarn.lock` file is found with a Yarn Modern format, the [Yarn Modern install](https://yarnpkg.com/cli/install) command, `yarn install --immutable`, is used to install dependencies. The action supports built-in caching of Yarn Classic dependencies only. To cache Yarn Modern dependencies additionally use [actions/setup-node](https://github.com/actions/setup-node) and specify `cache: yarn`. @@ -1307,7 +1307,6 @@ jobs: uses: cypress-io/github-action@v7 with: working-directory: examples/yarn-modern - install-command: yarn install ``` This example covers the [`.yarnrc.yml`](https://yarnpkg.com/configuration/yarnrc#nodeLinker) configuration `nodeLinker: node-modules` which Yarn uses by default for projects updated from Yarn Classic. For `nodeLinker: pnp` see [Yarn Plug'n'Play](#yarn-plugnplay) below. @@ -1325,7 +1324,7 @@ See the above [Yarn Modern](#yarn-modern) section for information on caching Yar name: example-yarn-modern-pnp on: push jobs: - yarn-classic: + yarn-modern-pnp: runs-on: ubuntu-24.04 steps: - name: Checkout @@ -1334,7 +1333,6 @@ jobs: uses: cypress-io/github-action@v7 with: working-directory: examples/yarn-modern-pnp - install-command: yarn install command: yarn run --binaries-only cypress run ``` @@ -1668,8 +1666,7 @@ This action installs local dependencies using lock files. Ensure that exactly on | `package-lock.json` | [npm](https://docs.npmjs.com/cli/v9/commands/npm-ci) | `npm ci` | | `pnpm-lock.yaml` | [pnpm](https://pnpm.io/cli/install#--frozen-lockfile) | `pnpm install --frozen-lockfile` | | `yarn.lock` | [Yarn Classic](https://classic.yarnpkg.com/en/docs/cli/install#toc-yarn-install-frozen-lockfile) | `yarn --frozen-lockfile` | - -See section [Yarn Modern](#yarn-modern) for information about using Yarn version 2 and later. +| `yarn.lock` | [Yarn Modern](https://yarnpkg.com/cli/install) | `yarn install --immutable` | ### Caching diff --git a/dist/index.js b/dist/index.js index 90a5b0467..adeb7ed2d 100644 --- a/dist/index.js +++ b/dist/index.js @@ -101888,6 +101888,9 @@ const packageLockFilename = path.join( ) const useYarn = () => fs.existsSync(yarnFilename) +const isYarnModern = () => + useYarn() && + fs.readFileSync(yarnFilename, 'utf8').includes('__metadata:') const usePnpm = () => fs.existsSync(pnpmLockFilename) const useNpm = () => fs.existsSync(packageLockFilename) @@ -102030,15 +102033,27 @@ const install = () => { } if (useYarn()) { - debug('installing npm dependencies using Yarn') - return io.which('yarn', true).then((yarnPath) => { - debug(`yarn at "${yarnPath}"`) - return exec.exec( - quote(yarnPath), - ['--frozen-lockfile'], - cypressCommandOptions - ) - }) + if (isYarnModern()) { + debug('installing npm dependencies using Yarn Modern') + return io.which('yarn', true).then((yarnPath) => { + debug(`yarn at "${yarnPath}"`) + return exec.exec( + quote(yarnPath), + ['install', '--immutable'], + cypressCommandOptions + ) + }) + } else { + debug('installing npm dependencies using Yarn Classic') + return io.which('yarn', true).then((yarnPath) => { + debug(`yarn at "${yarnPath}"`) + return exec.exec( + quote(yarnPath), + ['--frozen-lockfile'], + cypressCommandOptions + ) + }) + } } else if (usePnpm()) { debug('installing npm dependencies using pnpm') return io.which('pnpm', true).then((pnpmPath) => { diff --git a/index.js b/index.js index 75b790792..bd3b6fed9 100644 --- a/index.js +++ b/index.js @@ -103,6 +103,9 @@ const packageLockFilename = path.join( ) const useYarn = () => fs.existsSync(yarnFilename) +const isYarnModern = () => + useYarn() && + fs.readFileSync(yarnFilename, 'utf8').includes('__metadata:') const usePnpm = () => fs.existsSync(pnpmLockFilename) const useNpm = () => fs.existsSync(packageLockFilename) @@ -245,15 +248,27 @@ const install = () => { } if (useYarn()) { - debug('installing npm dependencies using Yarn') - return io.which('yarn', true).then((yarnPath) => { - debug(`yarn at "${yarnPath}"`) - return exec.exec( - quote(yarnPath), - ['--frozen-lockfile'], - cypressCommandOptions - ) - }) + if (isYarnModern()) { + debug('installing npm dependencies using Yarn Modern') + return io.which('yarn', true).then((yarnPath) => { + debug(`yarn at "${yarnPath}"`) + return exec.exec( + quote(yarnPath), + ['install', '--immutable'], + cypressCommandOptions + ) + }) + } else { + debug('installing npm dependencies using Yarn Classic') + return io.which('yarn', true).then((yarnPath) => { + debug(`yarn at "${yarnPath}"`) + return exec.exec( + quote(yarnPath), + ['--frozen-lockfile'], + cypressCommandOptions + ) + }) + } } else if (usePnpm()) { debug('installing npm dependencies using pnpm') return io.which('pnpm', true).then((pnpmPath) => {