Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,45 @@ jobs:
fi
shell: bash

test_bin_dest_output:
name: 'Test bin_dest output points to requested version (${{ matrix.version }}, ${{ matrix.os }})'
# Regression test for #247: invoking pnpm via the `bin_dest` output returned the
# bootstrap version because self-update writes the target to `${bin_dest}/bin/`,
# not directly into `${bin_dest}/`.

runs-on: ${{ matrix.os }}

strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- macos-latest
- windows-latest
version:
- '9.15.5'
- '10.33.2'

steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- id: pnpm
name: Run the action
uses: ./
with:
version: ${{ matrix.version }}

- name: 'Test: bin_dest/pnpm reports requested version'
run: |
required='${{ matrix.version }}'
actual="$(${{ steps.pnpm.outputs.bin_dest }}/pnpm --version)"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
echo "pnpm version via bin_dest: ${actual}"
if [ "${actual}" != "${required}" ]; then
echo "Expected pnpm version ${required}, but got ${actual}"
exit 1
fi
shell: bash

test_package_manager_field:
name: 'Test packageManager field is respected (${{ matrix.version }}, ${{ matrix.os }})'
# Reproduces #227: when `packageManager` is set in package.json and no `version:` input is given,
Expand Down
214 changes: 107 additions & 107 deletions dist/index.js

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ async function main() {
async function runMain(inputs: Inputs) {
saveState('is_post', 'true')

await installPnpm(inputs)
const binDest = await installPnpm(inputs)
if (binDest === undefined) return
console.log('Installation Completed!')
setOutputs(inputs)
setOutputs(inputs, binDest)

await restoreCache(inputs)

Expand Down
10 changes: 6 additions & 4 deletions src/install-pnpm/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import runSelfInstaller from './run'

export { runSelfInstaller }

export async function install(inputs: Inputs) {
export async function install(inputs: Inputs): Promise<string | undefined> {
startGroup('Running self-installer...')
const status = await runSelfInstaller(inputs)
const { exitCode, binDest } = await runSelfInstaller(inputs)
endGroup()
if (status) {
return setFailed(`Something went wrong, self-installer exits with code ${status}`)
if (exitCode) {
setFailed(`Something went wrong, self-installer exits with code ${exitCode}`)
return undefined
}
return binDest
}

export default install
20 changes: 16 additions & 4 deletions src/install-pnpm/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ import exeLock from './bootstrap/exe-lock.json'
const BOOTSTRAP_PNPM_PACKAGE_JSON = JSON.stringify({ private: true, dependencies: { pnpm: pnpmLock.packages['node_modules/pnpm'].version } })
const BOOTSTRAP_EXE_PACKAGE_JSON = JSON.stringify({ private: true, dependencies: { '@pnpm/exe': exeLock.packages['node_modules/@pnpm/exe'].version } })

export async function runSelfInstaller(inputs: Inputs): Promise<number> {
export interface SelfInstallerResult {
exitCode: number
binDest: string
}

export async function runSelfInstaller(inputs: Inputs): Promise<SelfInstallerResult> {
const { version, dest, packageJsonFile } = inputs

// pnpm v11 requires Node >= 22.13; use standalone (exe) bootstrap which
Expand Down Expand Up @@ -45,7 +50,7 @@ export async function runSelfInstaller(inputs: Inputs): Promise<number> {
const npmEnv = { ...process.env, [pathKey]: currentPath ? currentPath + path.delimiter + nodeDir : nodeDir }
const npmExitCode = await runCommand('npm', ['ci'], { cwd: dest, env: npmEnv })
if (npmExitCode !== 0) {
return npmExitCode
return { exitCode: npmExitCode, binDest: path.join(dest, 'node_modules', '.bin') }
}

// On Windows with standalone mode, npm's .bin shims can't properly
Expand Down Expand Up @@ -87,11 +92,18 @@ export async function runSelfInstaller(inputs: Inputs): Promise<number> {
const args = standalone ? ['self-update', targetVersion] : [bootstrapPnpm, 'self-update', targetVersion]
const exitCode = await runCommand(cmd, args, { cwd: dest })
if (exitCode !== 0) {
return exitCode
return { exitCode, binDest: pnpmHome }
}
// self-update writes the target pnpm/pnpx into PNPM_HOME/bin, leaving
// the bootstrap symlinks in pnpmHome pointing at the old version. Use
// PNPM_HOME/bin so consumers of the bin_dest output (e.g.
// `${steps.pnpm.outputs.bin_dest}/pnpm`) invoke the requested version.
return { exitCode: 0, binDest: path.join(pnpmHome, 'bin') }
}

return 0
// No explicit target version: rely on the bootstrap pnpm to switch to
// the version declared in packageManager/devEngines at runtime.
return { exitCode: 0, binDest: pnpmHome }
}

function readTargetVersion(opts: {
Expand Down
4 changes: 1 addition & 3 deletions src/outputs/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { setOutput } from '@actions/core'
import { Inputs } from '../inputs'
import { getBinDest } from '../utils'

export function setOutputs(inputs: Inputs) {
const binDest = getBinDest(inputs)
export function setOutputs(inputs: Inputs, binDest: string) {
// NOTE: addPath is already called in installPnpm — do not call it again
// here, as a second addPath would shadow the correct entry on Windows.
setOutput('dest', inputs.dest)
Expand Down
Loading