mirror of
https://github.com/pnpm/action-setup.git
synced 2026-04-04 19:42:36 +08:00
fix: force-replace npm's broken bin shims for pn/pnx aliases
npm creates bin shims in .bin/ that point to an isolated copy in .bin/.tools/. After self-update, setup.js fixes the main copy in node_modules/@pnpm/exe/ but the .tools copy retains stale placeholder files. Always replace the bin links so they point directly to the fixed files instead of npm's broken .tools shims.
This commit is contained in:
268
dist/index.js
vendored
268
dist/index.js
vendored
File diff suppressed because one or more lines are too long
@@ -121,25 +121,27 @@ describe('ensureAliasLinks', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('does not overwrite existing links', () => {
|
describe('overwrites existing broken links', () => {
|
||||||
it('preserves existing symlinks on unix', async () => {
|
it('replaces existing file with symlink on unix', async () => {
|
||||||
await setupStandaloneFixture(binDir)
|
await setupStandaloneFixture(binDir)
|
||||||
await writeFile(path.join(binDir, 'pn'), 'existing content')
|
// Simulate npm's broken shim (points to .tools/ placeholder)
|
||||||
|
await writeFile(path.join(binDir, 'pn'), '#!/bin/sh\nexec broken\n')
|
||||||
|
|
||||||
await ensureAliasLinks(binDir, true, 'linux')
|
await ensureAliasLinks(binDir, true, 'linux')
|
||||||
|
|
||||||
const content = await readFile(path.join(binDir, 'pn'), 'utf8')
|
// Should be replaced with a symlink to the real target
|
||||||
expect(content).toBe('existing content')
|
const target = await readlink(path.join(binDir, 'pn'))
|
||||||
|
expect(target).toBe(path.join('..', '@pnpm', 'exe', 'pn'))
|
||||||
})
|
})
|
||||||
|
|
||||||
it('preserves existing .cmd shims on windows', async () => {
|
it('replaces existing .cmd shims on windows', async () => {
|
||||||
await setupStandaloneFixture(binDir)
|
await setupStandaloneFixture(binDir)
|
||||||
await writeFile(path.join(binDir, 'pn.cmd'), 'existing shim')
|
await writeFile(path.join(binDir, 'pn.cmd'), 'broken shim')
|
||||||
|
|
||||||
await ensureAliasLinks(binDir, true, 'win32')
|
await ensureAliasLinks(binDir, true, 'win32')
|
||||||
|
|
||||||
const content = await readFile(path.join(binDir, 'pn.cmd'), 'utf8')
|
const content = await readFile(path.join(binDir, 'pn.cmd'), 'utf8')
|
||||||
expect(content).toBe('existing shim')
|
expect(content).toContain(path.join('..', '@pnpm', 'exe', 'pn'))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { writeFile, symlink } from 'fs/promises'
|
import { unlink, writeFile, symlink } from 'fs/promises'
|
||||||
import { existsSync } from 'fs'
|
import { existsSync } from 'fs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
|
||||||
@@ -30,10 +30,18 @@ function pwshShim (target: string): string {
|
|||||||
return `#!/usr/bin/env pwsh\n& "$PSScriptRoot\\${target}" @args\n`
|
return `#!/usr/bin/env pwsh\n& "$PSScriptRoot\\${target}" @args\n`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function forceSymlink (target: string, linkPath: string): Promise<void> {
|
||||||
|
try { await unlink(linkPath) } catch {}
|
||||||
|
await symlink(target, linkPath)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create pn/pnpx/pnx alias links in the bin directory.
|
* Create pn/pnpx/pnx alias links in the bin directory.
|
||||||
* On Unix, creates symlinks. On Windows, creates .cmd and .ps1 shims.
|
* On Unix, creates symlinks. On Windows, creates .cmd and .ps1 shims.
|
||||||
* Only creates links when the target file actually exists (pnpm v11+).
|
* 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.
|
||||||
*/
|
*/
|
||||||
export async function ensureAliasLinks (binDir: string, standalone: boolean, platform: NodeJS.Platform = process.platform): Promise<void> {
|
export async function ensureAliasLinks (binDir: string, standalone: boolean, platform: NodeJS.Platform = process.platform): Promise<void> {
|
||||||
const aliases = getAliases(standalone)
|
const aliases = getAliases(standalone)
|
||||||
@@ -44,19 +52,10 @@ export async function ensureAliasLinks (binDir: string, standalone: boolean, pla
|
|||||||
if (!existsSync(resolvedTarget)) continue
|
if (!existsSync(resolvedTarget)) continue
|
||||||
|
|
||||||
if (isWindows) {
|
if (isWindows) {
|
||||||
const cmdPath = path.join(binDir, `${name}.cmd`)
|
await writeFile(path.join(binDir, `${name}.cmd`), cmdShim(target))
|
||||||
if (!existsSync(cmdPath)) {
|
await writeFile(path.join(binDir, `${name}.ps1`), pwshShim(target))
|
||||||
await writeFile(cmdPath, cmdShim(target))
|
|
||||||
}
|
|
||||||
const ps1Path = path.join(binDir, `${name}.ps1`)
|
|
||||||
if (!existsSync(ps1Path)) {
|
|
||||||
await writeFile(ps1Path, pwshShim(target))
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const link = path.join(binDir, name)
|
await forceSymlink(target, path.join(binDir, name))
|
||||||
if (!existsSync(link)) {
|
|
||||||
await symlink(target, link)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user