mirror of
https://github.com/pnpm/action-setup
synced 2026-05-12 16:21:08 +02:00
fix: point pn to pnpm binary directly, not to @pnpm/exe/pn
pnpm self-update only replaces the pnpm binary — it does not update other files in the @pnpm/exe package (setup.js, pn, pnpx, pnx all remain from the v10 bootstrap). So @pnpm/exe/pn may not exist at all. Instead of relying on @pnpm/exe/pn, create the aliases directly: - pn → symlink to the pnpm binary - pnpx/pnx → shell scripts that exec "pnpm dlx" Also remove the setup.js call after self-update since it's no longer needed and would run the v10 version which doesn't know about pn.
This commit is contained in:
@@ -2,32 +2,16 @@ import { unlink, writeFile, symlink } from 'fs/promises'
|
||||
import { existsSync } from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
interface AliasDefinition {
|
||||
name: string
|
||||
target: string
|
||||
function shScript (command: string): string {
|
||||
return `#!/bin/sh\nexec ${command} "$@"\n`
|
||||
}
|
||||
|
||||
function getAliases (standalone: boolean): AliasDefinition[] {
|
||||
if (standalone) {
|
||||
return [
|
||||
{ name: 'pn', target: path.join('..', '@pnpm', 'exe', 'pn') },
|
||||
{ name: 'pnpx', target: path.join('..', '@pnpm', 'exe', 'pnpx') },
|
||||
{ name: 'pnx', target: path.join('..', '@pnpm', 'exe', 'pnx') },
|
||||
]
|
||||
}
|
||||
return [
|
||||
{ name: 'pn', target: path.join('..', 'pnpm', 'bin', 'pnpm.cjs') },
|
||||
{ name: 'pnpx', target: path.join('..', 'pnpm', 'bin', 'pnpx.cjs') },
|
||||
{ name: 'pnx', target: path.join('..', 'pnpm', 'bin', 'pnpx.cjs') },
|
||||
]
|
||||
function cmdShim (command: string): string {
|
||||
return `@ECHO off\r\n${command} %*\r\n`
|
||||
}
|
||||
|
||||
function cmdShim (target: string): string {
|
||||
return `@ECHO off\r\n"%~dp0\\${target}" %*\r\n`
|
||||
}
|
||||
|
||||
function pwshShim (target: string): string {
|
||||
return `#!/usr/bin/env pwsh\n& "$PSScriptRoot\\${target}" @args\n`
|
||||
function pwshShim (command: string): string {
|
||||
return `#!/usr/bin/env pwsh\n${command} @args\n`
|
||||
}
|
||||
|
||||
async function forceSymlink (target: string, linkPath: string): Promise<void> {
|
||||
@@ -35,27 +19,52 @@ async function forceSymlink (target: string, linkPath: string): Promise<void> {
|
||||
await symlink(target, linkPath)
|
||||
}
|
||||
|
||||
async function forceWriteFile (filePath: string, content: string, mode?: number): Promise<void> {
|
||||
try { await unlink(filePath) } catch {}
|
||||
await writeFile(filePath, content, { mode })
|
||||
}
|
||||
|
||||
/**
|
||||
* Create pn/pnpx/pnx alias links in the bin directory.
|
||||
* On Unix, creates symlinks. On Windows, creates .cmd and .ps1 shims.
|
||||
* Only creates links when the target file actually exists (pnpm v11+).
|
||||
*
|
||||
* Existing links are always replaced because npm may have created shims
|
||||
* pointing to an isolated .tools/ copy that has stale placeholder files.
|
||||
* pn is an alias for pnpm, so it symlinks (or shims) to the pnpm binary.
|
||||
* pnpx/pnx are aliases for "pnpm dlx", created as shell scripts.
|
||||
*
|
||||
* This does NOT rely on the @pnpm/exe package having pn/pnx files, because
|
||||
* pnpm self-update only replaces the pnpm binary — it doesn't update other
|
||||
* files in the package. The aliases are created by pointing pn directly to
|
||||
* the pnpm binary, and pnpx/pnx as scripts that exec "pnpm dlx".
|
||||
*
|
||||
* Only creates links when the pnpm binary exists in the expected location
|
||||
* (i.e. the package has been installed). This is always true after bootstrap.
|
||||
*/
|
||||
export async function ensureAliasLinks (binDir: string, standalone: boolean, platform: NodeJS.Platform = process.platform): Promise<void> {
|
||||
const aliases = getAliases(standalone)
|
||||
const isWindows = platform === 'win32'
|
||||
|
||||
for (const { name, target } of aliases) {
|
||||
const resolvedTarget = path.resolve(binDir, target)
|
||||
if (!existsSync(resolvedTarget)) continue
|
||||
// Determine the pnpm binary path relative to binDir
|
||||
const pnpmTarget = standalone
|
||||
? path.join('..', '@pnpm', 'exe', 'pnpm')
|
||||
: path.join('..', 'pnpm', 'bin', 'pnpm.cjs')
|
||||
|
||||
if (isWindows) {
|
||||
await writeFile(path.join(binDir, `${name}.cmd`), cmdShim(target))
|
||||
await writeFile(path.join(binDir, `${name}.ps1`), pwshShim(target))
|
||||
} else {
|
||||
await forceSymlink(target, path.join(binDir, name))
|
||||
const resolvedPnpm = path.resolve(binDir, pnpmTarget)
|
||||
if (!existsSync(resolvedPnpm)) return
|
||||
|
||||
if (isWindows) {
|
||||
// pn → calls pnpm directly
|
||||
await writeFile(path.join(binDir, 'pn.cmd'), cmdShim(`"%~dp0\\${pnpmTarget}"`))
|
||||
await writeFile(path.join(binDir, 'pn.ps1'), pwshShim(`& "$PSScriptRoot\\${pnpmTarget}"`))
|
||||
// pnpx/pnx → calls pnpm dlx
|
||||
for (const name of ['pnpx', 'pnx']) {
|
||||
await writeFile(path.join(binDir, `${name}.cmd`), cmdShim(`"%~dp0\\${pnpmTarget}" dlx`))
|
||||
await writeFile(path.join(binDir, `${name}.ps1`), pwshShim(`& "$PSScriptRoot\\${pnpmTarget}" dlx`))
|
||||
}
|
||||
} else {
|
||||
// pn → symlink to pnpm binary
|
||||
await forceSymlink(pnpmTarget, path.join(binDir, 'pn'))
|
||||
// pnpx/pnx → shell scripts that exec pnpm dlx
|
||||
for (const name of ['pnpx', 'pnx']) {
|
||||
const pnpmPath = `"$(dirname "$0")/${pnpmTarget}"`
|
||||
await forceWriteFile(path.join(binDir, name), shScript(`${pnpmPath} dlx`), 0o755)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user