diff --git a/.github/workflows/e2e-cache.yml b/.github/workflows/e2e-cache.yml index a0f20639..424dcaf4 100644 --- a/.github/workflows/e2e-cache.yml +++ b/.github/workflows/e2e-cache.yml @@ -135,6 +135,44 @@ jobs: run: __tests__/verify-node.sh "${{ matrix.node-version }}" shell: bash + node-yarn4-corepack-dependencies-caching: + name: Test yarn 4 (Node ${{ matrix.node-version}}, ${{ matrix.os }}) + runs-on: ${{ matrix.os }} + env: + YARN_ENABLE_IMMUTABLE_INSTALLS: false + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + node-version: [18, 20] + steps: + - uses: actions/checkout@v4 + - name: Update yarn + run: yarn set version 4.1.0 + - name: Yarn version + run: yarn --version + - name: Generate simple .yarnrc.yml + run: | + echo "nodeLinker: node-modules" >> .yarnrc.yml + - name: Generate yarn file + run: yarn install + - name: Remove dependencies + shell: pwsh + run: Remove-Item node_modules -Force -Recurse + - name: Clean global cache + run: yarn cache clean --all + - name: Setup Node + uses: ./ + with: + node-version: ${{ matrix.node-version }} + cache: 'yarn' + corepack: true + - name: Install dependencies + run: yarn install + - name: Verify node and yarn + run: __tests__/verify-node.sh "${{ matrix.node-version }}" + shell: bash + yarn-subprojects: name: Test yarn subprojects strategy: diff --git a/README.md b/README.md index 86236a49..ab11c819 100644 --- a/README.md +++ b/README.md @@ -26,22 +26,22 @@ See [action.yml](action.yml) node-version: '' # File containing the version Spec of the version to use. Examples: package.json, .nvmrc, .node-version, .tool-versions. - # If node-version and node-version-file are both provided the action will use version from node-version. + # If node-version and node-version-file are both provided the action will use version from node-version. node-version-file: '' - # Set this option if you want the action to check for the latest available version + # Set this option if you want the action to check for the latest available version # that satisfies the version spec. - # It will only get affect for lts Nodejs versions (12.x, >=10.15.0, lts/Hydrogen). + # It will only get affect for lts Nodejs versions (12.x, >=10.15.0, lts/Hydrogen). # Default: false check-latest: false # Target architecture for Node to use. Examples: x86, x64. Will use system architecture by default. - # Default: ''. The action use system architecture by default + # Default: ''. The action use system architecture by default architecture: '' - # Used to pull node distributions from https://github.com/actions/node-versions. - # Since there's a default, this is typically not supplied by the user. - # When running this action on github.com, the default value is sufficient. + # Used to pull node distributions from https://github.com/actions/node-versions. + # Since there's a default, this is typically not supplied by the user. + # When running this action on github.com, the default value is sufficient. # When running on GHES, you can pass a personal access token for github.com if you are experiencing rate limiting. # # We recommend using a service account with the least permissions necessary. Also @@ -57,18 +57,18 @@ See [action.yml](action.yml) # Default: '' cache: '' - # Used to specify the path to a dependency file: package-lock.json, yarn.lock, etc. - # It will generate hash from the target file for primary key. It works only If cache is specified. + # Used to specify the path to a dependency file: package-lock.json, yarn.lock, etc. + # It will generate hash from the target file for primary key. It works only If cache is specified. # Supports wildcards or a list of file names for caching multiple dependencies. # Default: '' cache-dependency-path: '' - # Optional registry to set up for auth. Will set the registry in a project level .npmrc and .yarnrc file, + # Optional registry to set up for auth. Will set the registry in a project level .npmrc and .yarnrc file, # and set up auth to read in from env.NODE_AUTH_TOKEN. # Default: '' registry-url: '' - # Optional scope for authenticating against scoped registries. + # Optional scope for authenticating against scoped registries. # Will fall back to the repository owner when using the GitHub Packages registry (https://npm.pkg.github.com/). # Default: '' scope: '' @@ -203,6 +203,7 @@ If the runner is not able to access github.com, any Nodejs versions requested du - [Publishing to npmjs and GPR with npm](docs/advanced-usage.md#publish-to-npmjs-and-gpr-with-npm) - [Publishing to npmjs and GPR with yarn](docs/advanced-usage.md#publish-to-npmjs-and-gpr-with-yarn) - [Using private packages](docs/advanced-usage.md#use-private-packages) + - [Enabling Corepack](docs/advanced-usage.md#enabling-corepack) ## License diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts index 501741a6..9aed769c 100644 --- a/__tests__/main.test.ts +++ b/__tests__/main.test.ts @@ -12,6 +12,7 @@ import each from 'jest-each'; import * as main from '../src/main'; import * as util from '../src/util'; +import * as cacheUtil from '../src/cache-utils'; import OfficialBuilds from '../src/distributions/official_builds/official_builds'; describe('main tests', () => { @@ -30,6 +31,7 @@ describe('main tests', () => { let existsSpy: jest.SpyInstance; let getExecOutputSpy: jest.SpyInstance; + let getCommandOutputSpy: jest.SpyInstance; let getNodeVersionFromFileSpy: jest.SpyInstance; let cnSpy: jest.SpyInstance; @@ -63,6 +65,7 @@ describe('main tests', () => { whichSpy = jest.spyOn(io, 'which'); getExecOutputSpy = jest.spyOn(exec, 'getExecOutput'); + getCommandOutputSpy = jest.spyOn(cacheUtil, 'getCommandOutput'); findSpy = jest.spyOn(tc, 'find'); @@ -280,4 +283,38 @@ describe('main tests', () => { ); }); }); + + describe('corepack flag', () => { + it('should not enable corepack when no input', async () => { + inputs['corepack'] = ''; + await main.run(); + expect(getCommandOutputSpy).not.toHaveBeenCalledWith(expect.stringContaining('corepack')); + }); + + it('should not enable corepack when input is "false"', async () => { + inputs['corepack'] = 'false'; + await main.run(); + expect(getCommandOutputSpy).not.toHaveBeenCalledWith(expect.stringContaining('corepack')); + }); + + it('should enable corepack when input is "true"', async () => { + inputs['corepack'] = 'true'; + await main.run(); + expect(getCommandOutputSpy).toHaveBeenCalledWith('corepack enable'); + }); + + it('should enable corepack with a single package manager', async () => { + inputs['corepack'] = 'npm'; + await main.run(); + expect(getCommandOutputSpy).toHaveBeenCalledWith('corepack enable npm'); + }); + + it('should enable corepack with multiple package managers', async () => { + inputs['corepack'] = 'npm yarn'; + await main.run(); + expect(getCommandOutputSpy).toHaveBeenCalledWith( + 'corepack enable npm yarn' + ); + }); + }); }); diff --git a/action.yml b/action.yml index 99db5869..78fbfbbb 100644 --- a/action.yml +++ b/action.yml @@ -25,10 +25,13 @@ inputs: description: 'Used to specify a package manager for caching in the default directory. Supported values: npm, yarn, pnpm.' cache-dependency-path: description: 'Used to specify the path to a dependency file: package-lock.json, yarn.lock, etc. Supports wildcards or a list of file names for caching multiple dependencies.' + corepack: + description: 'Used to specify whether to enable Corepack. Set to true to enable all package managers or set it to one or more package manager names separated by a space. Supported package manager names: npm, yarn, pnpm.' + default: 'false' # TODO: add input to control forcing to pull from cloud or dist. # escape valve for someone having issues or needing the absolute latest which isn't cached yet outputs: - cache-hit: + cache-hit: description: 'A boolean value to indicate if a cache was hit.' node-version: description: 'The installed node version.' diff --git a/dist/cache-save/index.js b/dist/cache-save/index.js index 270721c2..a0fde1b8 100644 --- a/dist/cache-save/index.js +++ b/dist/cache-save/index.js @@ -83849,12 +83849,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.unique = exports.printEnvDetailsAndSetOutput = exports.getNodeVersionFromFile = void 0; +exports.enableCorepack = exports.unique = exports.printEnvDetailsAndSetOutput = exports.getNodeVersionFromFile = void 0; const core = __importStar(__nccwpck_require__(2186)); const exec = __importStar(__nccwpck_require__(1514)); const io = __importStar(__nccwpck_require__(7436)); const fs_1 = __importDefault(__nccwpck_require__(7147)); const path_1 = __importDefault(__nccwpck_require__(1017)); +const cache_utils_1 = __nccwpck_require__(1678); function getNodeVersionFromFile(versionFilePath) { var _a, _b, _c, _d, _e; if (!fs_1.default.existsSync(versionFilePath)) { @@ -83947,6 +83948,19 @@ const unique = () => { }; }; exports.unique = unique; +function enableCorepack(input) { + return __awaiter(this, void 0, void 0, function* () { + if (input.length && input !== 'false') { + const corepackArgs = ['enable']; + if (input !== 'true') { + const packageManagers = input.split(' '); + corepackArgs.push(...packageManagers); + } + yield (0, cache_utils_1.getCommandOutput)(`corepack ${corepackArgs.join(' ')}`); + } + }); +} +exports.enableCorepack = enableCorepack; /***/ }), diff --git a/dist/setup/index.js b/dist/setup/index.js index 1f4334d3..3a792a74 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -94248,6 +94248,8 @@ function run() { if (registryUrl) { auth.configAuthentication(registryUrl, alwaysAuth); } + const corepack = core.getInput('corepack') || 'false'; + yield (0, util_1.enableCorepack)(corepack); if (cache && (0, cache_utils_1.isCacheFeatureAvailable)()) { core.saveState(constants_1.State.CachePackageManager, cache); const cacheDependencyPath = core.getInput('cache-dependency-path'); @@ -94331,12 +94333,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.unique = exports.printEnvDetailsAndSetOutput = exports.getNodeVersionFromFile = void 0; +exports.enableCorepack = exports.unique = exports.printEnvDetailsAndSetOutput = exports.getNodeVersionFromFile = void 0; const core = __importStar(__nccwpck_require__(2186)); const exec = __importStar(__nccwpck_require__(1514)); const io = __importStar(__nccwpck_require__(7436)); const fs_1 = __importDefault(__nccwpck_require__(7147)); const path_1 = __importDefault(__nccwpck_require__(1017)); +const cache_utils_1 = __nccwpck_require__(1678); function getNodeVersionFromFile(versionFilePath) { var _a, _b, _c, _d, _e; if (!fs_1.default.existsSync(versionFilePath)) { @@ -94429,6 +94432,19 @@ const unique = () => { }; }; exports.unique = unique; +function enableCorepack(input) { + return __awaiter(this, void 0, void 0, function* () { + if (input.length && input !== 'false') { + const corepackArgs = ['enable']; + if (input !== 'true') { + const packageManagers = input.split(' '); + corepackArgs.push(...packageManagers); + } + yield (0, cache_utils_1.getCommandOutput)(`corepack ${corepackArgs.join(' ')}`); + } + }); +} +exports.enableCorepack = enableCorepack; /***/ }), diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index bf62e071..a1e947cd 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -159,7 +159,7 @@ jobs: ## Nightly versions -You can specify a nightly version to download it from https://nodejs.org/download/nightly. +You can specify a nightly version to download it from https://nodejs.org/download/nightly. ### Install the nightly build for a major version @@ -267,7 +267,7 @@ steps: - run: pnpm test ``` -> **Note**: By default `--frozen-lockfile` option is passed starting from pnpm `6.10.x`. It will be automatically added if you run it on [CI](https://pnpm.io/cli/install#--frozen-lockfile). +> **Note**: By default `--frozen-lockfile` option is passed starting from pnpm `6.10.x`. It will be automatically added if you run it on [CI](https://pnpm.io/cli/install#--frozen-lockfile). > If the `pnpm-lock.yaml` file changes then pass `--frozen-lockfile` option. @@ -418,3 +418,30 @@ Please refer to the [Ensuring workflow access to your package - Configuring a pa ### always-auth input The always-auth input sets `always-auth=true` in .npmrc file. With this option set [npm](https://docs.npmjs.com/cli/v6/using-npm/config#always-auth)/yarn sends the authentication credentials when making a request to the registries. + +## Enabling Corepack +You can enable [Corepack](https://github.com/nodejs/corepack) by using the `corepack` input. You can then use `pnpm` and `yarn` commands in your project. + +```yaml +steps: +- uses: actions/checkout@v4 +- uses: actions/setup-node@v4 + with: + node-version: '18.x' + corepack: true +- name: Install dependencies + run: yarn install --immutable +``` + +You can also pass package manager names separated by a space to enable corepack for specific package managers only. + +```yaml +steps: +- uses: actions/checkout@v4 +- uses: actions/setup-node@v4 + with: + node-version: '18.x' + corepack: yarn pnpm +- name: Install dependencies + run: yarn install --immutable +``` diff --git a/src/main.ts b/src/main.ts index c55c3b00..c47b2e97 100644 --- a/src/main.ts +++ b/src/main.ts @@ -7,7 +7,11 @@ import * as path from 'path'; import {restoreCache} from './cache-restore'; import {isCacheFeatureAvailable} from './cache-utils'; import {getNodejsDistribution} from './distributions/installer-factory'; -import {getNodeVersionFromFile, printEnvDetailsAndSetOutput} from './util'; +import { + getNodeVersionFromFile, + printEnvDetailsAndSetOutput, + enableCorepack +} from './util'; import {State} from './constants'; export async function run() { @@ -59,6 +63,9 @@ export async function run() { auth.configAuthentication(registryUrl, alwaysAuth); } + const corepack = core.getInput('corepack') || 'false'; + await enableCorepack(corepack); + if (cache && isCacheFeatureAvailable()) { core.saveState(State.CachePackageManager, cache); const cacheDependencyPath = core.getInput('cache-dependency-path'); diff --git a/src/util.ts b/src/util.ts index bbe25ddf..af8f903a 100644 --- a/src/util.ts +++ b/src/util.ts @@ -4,6 +4,7 @@ import * as io from '@actions/io'; import fs from 'fs'; import path from 'path'; +import {getCommandOutput} from './cache-utils'; export function getNodeVersionFromFile(versionFilePath: string): string | null { if (!fs.existsSync(versionFilePath)) { @@ -106,3 +107,14 @@ export const unique = () => { return true; }; }; + +export async function enableCorepack(input: string): Promise { + if (input.length && input !== 'false') { + const corepackArgs = ['enable']; + if (input !== 'true') { + const packageManagers = input.split(' '); + corepackArgs.push(...packageManagers); + } + await getCommandOutput(`corepack ${corepackArgs.join(' ')}`); + } +}