From d94bcaabfe169fe2db17c5d2052726a98578de7e Mon Sep 17 00:00:00 2001 From: huntharo Date: Fri, 10 Apr 2026 16:55:50 -0400 Subject: [PATCH 1/3] ci: dogfood configure-nodejs in cache primer flow --- .github/workflows/ci.yml | 16 +++++++++++++++- action.yml | 21 ++++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 304f98f..82ed27a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,13 +7,27 @@ on: pull_request: jobs: + install-deps: + name: Install Deps + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Configure Node.js + uses: ./ + with: + node-version: 22.x + lookup-only: "true" + unit: name: Unit Tests + needs: install-deps runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - - uses: ./ + - name: Configure Node.js + uses: ./ with: node-version: 22.x diff --git a/action.yml b/action.yml index 2a72ad4..2c43e1a 100644 --- a/action.yml +++ b/action.yml @@ -84,7 +84,7 @@ runs: - name: Restore dependencies from cache id: cache-node-modules - uses: actions/cache@v5 + uses: actions/cache/restore@v5 with: path: ${{ steps.resolve-cache-paths.outputs.cachePaths }} key: node-modules-${{ inputs.node-version }}-${{ runner.os }}-${{ runner.arch }}-${{ steps.resolve-cache-paths.outputs.workingDirectoryKey }}-${{ steps.resolve-package-manager.outputs.managerCacheKey }}-${{ steps.resolve-package-manager.outputs.lockfileSha }}${{ steps.resolve-cache-paths.outputs.cacheKeySuffixSegment }} @@ -95,3 +95,22 @@ runs: shell: bash working-directory: ${{ steps.resolve-cache-paths.outputs.workingDirectory }} run: ${{ steps.resolve-package-manager.outputs.installCommand }} + + - name: Detect cacheable dependency paths + id: detect-cache-paths + if: steps.cache-node-modules.outputs.cache-hit != 'true' + shell: bash + working-directory: ${{ steps.resolve-cache-paths.outputs.workingDirectory }} + run: | + if find . -type d -name node_modules -print -quit | grep -q .; then + echo "exists=true" >> "$GITHUB_OUTPUT" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + fi + + - name: Save dependencies to cache + if: steps.cache-node-modules.outputs.cache-hit != 'true' && steps.detect-cache-paths.outputs.exists == 'true' + uses: actions/cache/save@v5 + with: + path: ${{ steps.resolve-cache-paths.outputs.cachePaths }} + key: node-modules-${{ inputs.node-version }}-${{ runner.os }}-${{ runner.arch }}-${{ steps.resolve-cache-paths.outputs.workingDirectoryKey }}-${{ steps.resolve-package-manager.outputs.managerCacheKey }}-${{ steps.resolve-package-manager.outputs.lockfileSha }}${{ steps.resolve-cache-paths.outputs.cacheKeySuffixSegment }} From 254a95dcf81d2feb2524363acdacf199e297085e Mon Sep 17 00:00:00 2001 From: huntharo Date: Fri, 10 Apr 2026 16:57:40 -0400 Subject: [PATCH 2/3] docs: clarify package-manager override is optional --- README.md | 2 +- action.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bd4e8dd..70541e9 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ steps: | Input | Default | Description | | --- | --- | --- | | `node-version` | `22.x` | Node.js version to install with `actions/setup-node` | -| `package-manager` | `""` | Explicit override for `npm`, `pnpm`, or `yarn` | +| `package-manager` | `""` | Optional override for `npm`, `pnpm`, or `yarn`; by default the action follows the package manager inferred from `package.json` and the lockfile present | | `working-directory` | `"."` | Repository-relative directory containing `package.json` and the lockfile | | `cache-key-suffix` | `""` | Optional suffix appended to the dependency cache key when you want to namespace cache entries | | `lookup-only` | `"false"` | When `true`, only checks whether the cache exists and skips downloading it | diff --git a/action.yml b/action.yml index 2c43e1a..e9080b0 100644 --- a/action.yml +++ b/action.yml @@ -6,7 +6,7 @@ inputs: description: "Node.js version to install" default: "22.x" package-manager: - description: "Explicit package manager override (npm, yarn, pnpm)" + description: "Optional override for npm, yarn, or pnpm; defaults to the package manager inferred from package.json and lockfiles" default: "" working-directory: description: "Repository-relative directory containing package.json and lockfile" From 2daf4e2e8194e8daf07740d5f35e155d406dce87 Mon Sep 17 00:00:00 2001 From: huntharo Date: Fri, 10 Apr 2026 17:06:07 -0400 Subject: [PATCH 3/3] chore: move dogfood coverage to configure-nodejs-test --- .github/workflows/ci.yml | 140 -------------------------- .gitignore | 6 -- README.md | 10 +- fixtures/npm-basic/check.mjs | 7 -- fixtures/npm-basic/package-lock.json | 19 ---- fixtures/npm-basic/package.json | 8 -- fixtures/pnpm-basic/check.mjs | 7 -- fixtures/pnpm-basic/package.json | 9 -- fixtures/pnpm-basic/pnpm-lock.yaml | 22 ---- fixtures/yarn-basic/.yarnrc.yml | 1 - fixtures/yarn-basic/check.mjs | 7 -- fixtures/yarn-basic/package.json | 9 -- fixtures/yarn-basic/yarn.lock | 21 ---- package-lock.json | 10 -- package.json | 8 -- test/resolve-cache-paths.test.mjs | 109 -------------------- test/resolve-manager.test.mjs | 144 --------------------------- 17 files changed, 3 insertions(+), 534 deletions(-) delete mode 100644 .github/workflows/ci.yml delete mode 100644 fixtures/npm-basic/check.mjs delete mode 100644 fixtures/npm-basic/package-lock.json delete mode 100644 fixtures/npm-basic/package.json delete mode 100644 fixtures/pnpm-basic/check.mjs delete mode 100644 fixtures/pnpm-basic/package.json delete mode 100644 fixtures/pnpm-basic/pnpm-lock.yaml delete mode 100644 fixtures/yarn-basic/.yarnrc.yml delete mode 100644 fixtures/yarn-basic/check.mjs delete mode 100644 fixtures/yarn-basic/package.json delete mode 100644 fixtures/yarn-basic/yarn.lock delete mode 100644 package-lock.json delete mode 100644 package.json delete mode 100644 test/resolve-cache-paths.test.mjs delete mode 100644 test/resolve-manager.test.mjs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 82ed27a..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,140 +0,0 @@ -name: CI - -on: - push: - branches: - - main - pull_request: - -jobs: - install-deps: - name: Install Deps - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - - name: Configure Node.js - uses: ./ - with: - node-version: 22.x - lookup-only: "true" - - unit: - name: Unit Tests - needs: install-deps - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - - name: Configure Node.js - uses: ./ - with: - node-version: 22.x - - - name: Run unit tests - run: npm test - - fixture-lookup: - name: Fixture Lookup (${{ matrix.package-manager }}) - needs: unit - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - include: - - package-manager: npm - fixture: fixtures/npm-basic - install-command: npm ci - lockfile-name: package-lock.json - - package-manager: pnpm - fixture: fixtures/pnpm-basic - install-command: pnpm install --frozen-lockfile - lockfile-name: pnpm-lock.yaml - - package-manager: yarn - fixture: fixtures/yarn-basic - install-command: yarn install --immutable - lockfile-name: yarn.lock - steps: - - uses: actions/checkout@v6 - with: - sparse-checkout: | - action.yml - scripts - ${{ matrix.fixture }} - sparse-checkout-cone-mode: false - - - name: Configure Node.js - id: configure-nodejs - uses: ./ - with: - working-directory: ${{ matrix.fixture }} - cache-key-suffix: fixture-tests - lookup-only: "true" - - - name: Assert resolved action outputs - shell: bash - run: | - test "${{ steps.configure-nodejs.outputs.package-manager }}" = "${{ matrix.package-manager }}" - test "${{ steps.configure-nodejs.outputs.install-command }}" = "${{ matrix.install-command }}" - test "${{ steps.configure-nodejs.outputs.working-directory }}" = "${{ matrix.fixture }}" - test "${{ steps.configure-nodejs.outputs.lockfile-name }}" = "${{ matrix.lockfile-name }}" - - - name: Assert lookup-only behavior - working-directory: ${{ matrix.fixture }} - shell: bash - run: | - if [ "${{ steps.configure-nodejs.outputs.cache-hit }}" = "true" ]; then - test ! -d node_modules - else - test -d node_modules - node check.mjs - fi - - fixture-validate: - name: Fixture Validate (${{ matrix.package-manager }}) - needs: fixture-lookup - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - include: - - package-manager: npm - fixture: fixtures/npm-basic - install-command: npm ci - lockfile-name: package-lock.json - - package-manager: pnpm - fixture: fixtures/pnpm-basic - install-command: pnpm install --frozen-lockfile - lockfile-name: pnpm-lock.yaml - - package-manager: yarn - fixture: fixtures/yarn-basic - install-command: yarn install --immutable - lockfile-name: yarn.lock - steps: - - uses: actions/checkout@v6 - with: - sparse-checkout: | - action.yml - scripts - ${{ matrix.fixture }} - sparse-checkout-cone-mode: false - - - name: Configure Node.js - id: configure-nodejs - uses: ./ - with: - working-directory: ${{ matrix.fixture }} - cache-key-suffix: fixture-tests - - - name: Assert restore behavior - shell: bash - run: | - test "${{ steps.configure-nodejs.outputs.package-manager }}" = "${{ matrix.package-manager }}" - test "${{ steps.configure-nodejs.outputs.install-command }}" = "${{ matrix.install-command }}" - test "${{ steps.configure-nodejs.outputs.working-directory }}" = "${{ matrix.fixture }}" - test "${{ steps.configure-nodejs.outputs.lockfile-name }}" = "${{ matrix.lockfile-name }}" - test "${{ steps.configure-nodejs.outputs.cache-hit }}" = "true" - - - name: Validate installed dependency - working-directory: ${{ matrix.fixture }} - run: node check.mjs diff --git a/.gitignore b/.gitignore index d4f6654..e43b0f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1 @@ -node_modules/ .DS_Store - -fixtures/*/node_modules/ -fixtures/*/.pnp.* -fixtures/*/.yarn/install-state.gz -fixtures/*/.yarn/cache/ diff --git a/README.md b/README.md index 70541e9..42f659d 100644 --- a/README.md +++ b/README.md @@ -100,19 +100,15 @@ The first release targets Yarn repositories that install into `node_modules`. - Yarn 2+ uses `yarn install --immutable` - Yarn 1 uses `yarn install --frozen-lockfile` -- the fixture coverage in this repository uses Yarn 4 with `nodeLinker: node-modules` +- the dogfood coverage in `pwrdrvr/configure-nodejs-test` uses Yarn 4 with `nodeLinker: node-modules` Plug'n'Play-specific caching is intentionally out of scope for `v1`. ## Development -This repository includes three end-to-end fixtures under `fixtures/`: +Dogfood coverage for this action lives in [`pwrdrvr/configure-nodejs-test`](https://github.com/pwrdrvr/configure-nodejs-test). -- `fixtures/npm-basic` -- `fixtures/pnpm-basic` -- `fixtures/yarn-basic` - -The CI workflow runs local unit tests plus end-to-end matrix coverage for those fixtures, followed by cache-lookup verification in a second matrix job. +That repository checks out `pwrdrvr/configure-nodejs` at a configurable ref, runs the unit tests for the helper scripts, and exercises npm, pnpm, and Yarn fixtures through the action entrypoint. ## Releases diff --git a/fixtures/npm-basic/check.mjs b/fixtures/npm-basic/check.mjs deleted file mode 100644 index 129ef92..0000000 --- a/fixtures/npm-basic/check.mjs +++ /dev/null @@ -1,7 +0,0 @@ -import pc from 'picocolors'; - -if (typeof pc.green !== 'function') { - throw new Error('Expected picocolors.green to be available after npm install.'); -} - -console.log(pc.green('npm fixture dependency loaded')); diff --git a/fixtures/npm-basic/package-lock.json b/fixtures/npm-basic/package-lock.json deleted file mode 100644 index 475bb1e..0000000 --- a/fixtures/npm-basic/package-lock.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "npm-basic-fixture", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "npm-basic-fixture", - "dependencies": { - "picocolors": "1.1.1" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - } - } -} diff --git a/fixtures/npm-basic/package.json b/fixtures/npm-basic/package.json deleted file mode 100644 index b6d1cd8..0000000 --- a/fixtures/npm-basic/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "npm-basic-fixture", - "private": true, - "type": "module", - "dependencies": { - "picocolors": "1.1.1" - } -} diff --git a/fixtures/pnpm-basic/check.mjs b/fixtures/pnpm-basic/check.mjs deleted file mode 100644 index 5e0fd88..0000000 --- a/fixtures/pnpm-basic/check.mjs +++ /dev/null @@ -1,7 +0,0 @@ -import pc from 'picocolors'; - -if (typeof pc.green !== 'function') { - throw new Error('Expected picocolors.green to be available after pnpm install.'); -} - -console.log(pc.green('pnpm fixture dependency loaded')); diff --git a/fixtures/pnpm-basic/package.json b/fixtures/pnpm-basic/package.json deleted file mode 100644 index 7e4d060..0000000 --- a/fixtures/pnpm-basic/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "pnpm-basic-fixture", - "private": true, - "type": "module", - "packageManager": "pnpm@10.12.1", - "dependencies": { - "picocolors": "1.1.1" - } -} diff --git a/fixtures/pnpm-basic/pnpm-lock.yaml b/fixtures/pnpm-basic/pnpm-lock.yaml deleted file mode 100644 index 8a296ba..0000000 --- a/fixtures/pnpm-basic/pnpm-lock.yaml +++ /dev/null @@ -1,22 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - dependencies: - picocolors: - specifier: 1.1.1 - version: 1.1.1 - -packages: - - picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - -snapshots: - - picocolors@1.1.1: {} diff --git a/fixtures/yarn-basic/.yarnrc.yml b/fixtures/yarn-basic/.yarnrc.yml deleted file mode 100644 index 3186f3f..0000000 --- a/fixtures/yarn-basic/.yarnrc.yml +++ /dev/null @@ -1 +0,0 @@ -nodeLinker: node-modules diff --git a/fixtures/yarn-basic/check.mjs b/fixtures/yarn-basic/check.mjs deleted file mode 100644 index 663fc86..0000000 --- a/fixtures/yarn-basic/check.mjs +++ /dev/null @@ -1,7 +0,0 @@ -import pc from 'picocolors'; - -if (typeof pc.green !== 'function') { - throw new Error('Expected picocolors.green to be available after yarn install.'); -} - -console.log(pc.green('yarn fixture dependency loaded')); diff --git a/fixtures/yarn-basic/package.json b/fixtures/yarn-basic/package.json deleted file mode 100644 index 94bd3bc..0000000 --- a/fixtures/yarn-basic/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "yarn-basic-fixture", - "private": true, - "type": "module", - "packageManager": "yarn@4.6.0", - "dependencies": { - "picocolors": "1.1.1" - } -} diff --git a/fixtures/yarn-basic/yarn.lock b/fixtures/yarn-basic/yarn.lock deleted file mode 100644 index b5df1ae..0000000 --- a/fixtures/yarn-basic/yarn.lock +++ /dev/null @@ -1,21 +0,0 @@ -# This file is generated by running "yarn install" inside your project. -# Manual changes might be lost - proceed with caution! - -__metadata: - version: 8 - cacheKey: 10c0 - -"picocolors@npm:1.1.1": - version: 1.1.1 - resolution: "picocolors@npm:1.1.1" - checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58 - languageName: node - linkType: hard - -"yarn-basic-fixture@workspace:.": - version: 0.0.0-use.local - resolution: "yarn-basic-fixture@workspace:." - dependencies: - picocolors: "npm:1.1.1" - languageName: unknown - linkType: soft diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 7fb649d..0000000 --- a/package-lock.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "@pwrdrvr/configure-nodejs-action", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@pwrdrvr/configure-nodejs-action" - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index b8fefa9..0000000 --- a/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "@pwrdrvr/configure-nodejs-action", - "private": true, - "type": "module", - "scripts": { - "test": "node --test" - } -} diff --git a/test/resolve-cache-paths.test.mjs b/test/resolve-cache-paths.test.mjs deleted file mode 100644 index 261a672..0000000 --- a/test/resolve-cache-paths.test.mjs +++ /dev/null @@ -1,109 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; -import fs from 'fs'; -import os from 'os'; -import path from 'path'; - -import { - buildCachePaths, - buildResult, - buildWorkingDirectoryKey, - normalizeCacheKeySuffix, - resolveWorkingDirectory, -} from '../scripts/resolve-cache-paths.mjs'; - -function withTempDir(callback) { - const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'configure-nodejs-paths-')); - - try { - callback(tempDir); - } finally { - fs.rmSync(tempDir, { recursive: true, force: true }); - } -} - -test('resolveWorkingDirectory preserves root-relative usage', () => { - withTempDir((tempDir) => { - const result = resolveWorkingDirectory(tempDir, '.'); - - assert.equal(result.workingDirectory, '.'); - assert.equal(result.absoluteWorkingDirectory, tempDir); - }); -}); - -test('resolveWorkingDirectory resolves nested directories relative to the repository root', () => { - withTempDir((tempDir) => { - fs.mkdirSync(path.join(tempDir, 'fixtures', 'npm-basic'), { recursive: true }); - - const result = resolveWorkingDirectory(tempDir, 'fixtures/npm-basic'); - - assert.equal(result.workingDirectory, 'fixtures/npm-basic'); - assert.match(result.absoluteWorkingDirectory, /fixtures\/npm-basic$/); - }); -}); - -test('resolveWorkingDirectory rejects paths that escape the repository root', () => { - withTempDir((tempDir) => { - assert.throws( - () => resolveWorkingDirectory(tempDir, '../outside'), - /resolves outside the repository root/, - ); - }); -}); - -test('resolveWorkingDirectory rejects missing paths', () => { - withTempDir((tempDir) => { - assert.throws( - () => resolveWorkingDirectory(tempDir, 'missing'), - /does not exist/, - ); - }); -}); - -test('buildWorkingDirectoryKey generates a stable root key and nested slug', () => { - assert.equal(buildWorkingDirectoryKey('.'), 'root'); - assert.match( - buildWorkingDirectoryKey('fixtures/yarn-basic'), - /^fixtures__yarn-basic-[a-f0-9]{8}$/, - ); -}); - -test('normalizeCacheKeySuffix preserves safe values and normalizes separators', () => { - assert.equal(normalizeCacheKeySuffix('fixture-tests'), 'fixture-tests'); - assert.equal(normalizeCacheKeySuffix(' fixture tests / ci '), 'fixture-tests-ci'); - assert.equal(normalizeCacheKeySuffix(' '), ''); -}); - -test('buildCachePaths scopes cache globs to the resolved working directory', () => { - assert.deepEqual(buildCachePaths('.'), [ - 'node_modules', - '**/node_modules', - '!node_modules/.cache', - '!**/node_modules/.cache', - ]); - - assert.deepEqual(buildCachePaths('fixtures/pnpm-basic'), [ - 'fixtures/pnpm-basic/node_modules', - 'fixtures/pnpm-basic/**/node_modules', - '!fixtures/pnpm-basic/node_modules/.cache', - '!fixtures/pnpm-basic/**/node_modules/.cache', - ]); -}); - -test('buildResult combines normalized working-directory and cache metadata', () => { - withTempDir((tempDir) => { - fs.mkdirSync(path.join(tempDir, 'fixtures', 'npm-basic'), { recursive: true }); - - const result = buildResult({ - cwd: tempDir, - workingDirectory: 'fixtures/npm-basic', - cacheKeySuffix: 'fixture tests', - }); - - assert.equal(result.workingDirectory, 'fixtures/npm-basic'); - assert.match(result.workingDirectoryKey, /^fixtures__npm-basic-[a-f0-9]{8}$/); - assert.equal(result.cachePaths[0], 'fixtures/npm-basic/node_modules'); - assert.equal(result.cacheKeySuffix, 'fixture-tests'); - assert.equal(result.cacheKeySuffixSegment, '-fixture-tests'); - }); -}); diff --git a/test/resolve-manager.test.mjs b/test/resolve-manager.test.mjs deleted file mode 100644 index b37748f..0000000 --- a/test/resolve-manager.test.mjs +++ /dev/null @@ -1,144 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; -import fs from 'fs'; -import os from 'os'; -import path from 'path'; - -import { buildResult, normalizeManager } from '../scripts/resolve-manager.mjs'; - -function withTempDir(callback) { - const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'configure-nodejs-manager-')); - - try { - callback(tempDir); - } finally { - fs.rmSync(tempDir, { recursive: true, force: true }); - } -} - -test('normalizeManager validates the supported values', () => { - assert.equal(normalizeManager(' PNPM '), 'pnpm'); - assert.throws( - () => normalizeManager('bun'), - /Unsupported package manager "bun"/, - ); -}); - -test('buildResult resolves npm from an explicit package-manager override', () => { - withTempDir((tempDir) => { - fs.writeFileSync( - path.join(tempDir, 'package.json'), - JSON.stringify({ name: 'fixture', version: '1.0.0' }), - ); - fs.writeFileSync(path.join(tempDir, 'package-lock.json'), '{}\n'); - - const result = buildResult({ cwd: tempDir, explicitManager: 'npm' }); - - assert.equal(result.packageManager, 'npm'); - assert.equal(result.installCommand, 'npm ci'); - assert.equal(result.lockfileName, 'package-lock.json'); - }); -}); - -test('buildResult resolves pnpm from packageManager metadata', () => { - withTempDir((tempDir) => { - fs.writeFileSync( - path.join(tempDir, 'package.json'), - JSON.stringify({ - name: 'fixture', - version: '1.0.0', - packageManager: 'pnpm@10.12.1', - }), - ); - fs.writeFileSync(path.join(tempDir, 'pnpm-lock.yaml'), 'lockfileVersion: 9.0\n'); - - const result = buildResult({ cwd: tempDir, explicitManager: '' }); - - assert.equal(result.packageManager, 'pnpm'); - assert.equal(result.packageManagerVersion, '10.12.1'); - assert.equal(result.installCommand, 'pnpm install --frozen-lockfile'); - assert.equal(result.managerCacheKey, 'pnpm-10.12.1'); - }); -}); - -test('buildResult resolves modern Yarn installs with --immutable', () => { - withTempDir((tempDir) => { - fs.writeFileSync( - path.join(tempDir, 'package.json'), - JSON.stringify({ - name: 'fixture', - version: '1.0.0', - packageManager: 'yarn@4.6.0', - }), - ); - fs.writeFileSync(path.join(tempDir, 'yarn.lock'), '# yarn lockfile\n'); - fs.writeFileSync(path.join(tempDir, '.yarnrc.yml'), 'nodeLinker: node-modules\n'); - - const result = buildResult({ cwd: tempDir, explicitManager: '' }); - - assert.equal(result.packageManager, 'yarn'); - assert.equal(result.installCommand, 'yarn install --immutable'); - }); -}); - -test('buildResult falls back to classic Yarn install flags when no Berry signal exists', () => { - withTempDir((tempDir) => { - fs.writeFileSync( - path.join(tempDir, 'package.json'), - JSON.stringify({ name: 'fixture', version: '1.0.0' }), - ); - fs.writeFileSync(path.join(tempDir, 'yarn.lock'), '# yarn classic lockfile\n'); - - const result = buildResult({ cwd: tempDir, explicitManager: 'yarn' }); - - assert.equal(result.packageManager, 'yarn'); - assert.equal(result.installCommand, 'yarn install --frozen-lockfile'); - }); -}); - -test('buildResult rejects multiple lockfiles in one working directory', () => { - withTempDir((tempDir) => { - fs.writeFileSync( - path.join(tempDir, 'package.json'), - JSON.stringify({ name: 'fixture', version: '1.0.0' }), - ); - fs.writeFileSync(path.join(tempDir, 'package-lock.json'), '{}\n'); - fs.writeFileSync(path.join(tempDir, 'pnpm-lock.yaml'), 'lockfileVersion: 9.0\n'); - - assert.throws( - () => buildResult({ cwd: tempDir, explicitManager: '' }), - /Found multiple lockfiles/, - ); - }); -}); - -test('buildResult honors an explicit manager when multiple lockfiles are present', () => { - withTempDir((tempDir) => { - fs.writeFileSync( - path.join(tempDir, 'package.json'), - JSON.stringify({ name: 'fixture', version: '1.0.0' }), - ); - fs.writeFileSync(path.join(tempDir, 'package-lock.json'), '{}\n'); - fs.writeFileSync(path.join(tempDir, 'pnpm-lock.yaml'), 'lockfileVersion: 9.0\n'); - - const result = buildResult({ cwd: tempDir, explicitManager: 'npm' }); - - assert.equal(result.packageManager, 'npm'); - assert.equal(result.lockfileName, 'package-lock.json'); - assert.equal(result.installCommand, 'npm ci'); - }); -}); - -test('buildResult rejects missing lockfiles when the manager cannot be resolved', () => { - withTempDir((tempDir) => { - fs.writeFileSync( - path.join(tempDir, 'package.json'), - JSON.stringify({ name: 'fixture', version: '1.0.0' }), - ); - - assert.throws( - () => buildResult({ cwd: tempDir, explicitManager: '' }), - /Could not determine package manager/, - ); - }); -});