fix: remove .exe files from the target .bin directory (#5450)

This commit is contained in:
Zoltan Kochan
2022-10-06 00:07:06 +03:00
committed by GitHub
parent 911d295846
commit 5eb41a5510
5 changed files with 47 additions and 2 deletions

View File

@@ -0,0 +1,8 @@
---
"@pnpm/link-bins": patch
"pnpm": patch
---
When linking commands to a directory, remove any .exe files that are already present in that target directory by the same name.
This fixes an issue with pnpm global update on Windows. If pnpm was installed with the standalone script and then updated with pnpm using `pnpm add --global pnpm`, the exe file initially created by the standalone script should be removed.

View File

@@ -39,6 +39,7 @@
"@pnpm/read-project-manifest": "workspace:*",
"@pnpm/types": "workspace:*",
"@zkochan/cmd-shim": "^5.3.1",
"@zkochan/rimraf": "^2.1.2",
"bin-links": "^3.0.3",
"is-subdir": "^1.2.0",
"is-windows": "^1.0.2",

View File

@@ -1,4 +1,4 @@
import { promises as fs } from 'fs'
import { promises as fs, existsSync } from 'fs'
import path from 'path'
import PnpmError from '@pnpm/error'
import logger, { globalWarn } from '@pnpm/logger'
@@ -9,6 +9,7 @@ import { fromDir as readPackageJsonFromDir } from '@pnpm/read-package-json'
import { safeReadProjectManifestOnly } from '@pnpm/read-project-manifest'
import { DependencyManifest, ProjectManifest } from '@pnpm/types'
import cmdShim from '@zkochan/cmd-shim'
import rimraf from '@zkochan/rimraf'
import isSubdir from 'is-subdir'
import isWindows from 'is-windows'
import normalizePath from 'normalize-path'
@@ -200,6 +201,13 @@ export interface LinkBinOptions {
async function linkBin (cmd: CommandInfo, binsDir: string, opts?: LinkBinOptions) {
const externalBinPath = path.join(binsDir, cmd.name)
if (IS_WINDOWS) {
const exePath = path.join(binsDir, `${cmd.name}${getExeExtension()}`)
if (existsSync(exePath)) {
globalWarn(`The target bin directory already contains an exe called ${cmd.name}, so removing ${exePath}`)
await rimraf(exePath)
}
}
if (opts?.preferSymlinkedExecutables && !IS_WINDOWS && cmd.nodeExecPath == null) {
await symlinkDir(cmd.path, externalBinPath)
@@ -226,6 +234,18 @@ async function linkBin (cmd: CommandInfo, binsDir: string, opts?: LinkBinOptions
}
}
function getExeExtension (): string {
let cmdExtension
if (process.env.PATHEXT) {
cmdExtension = process.env.PATHEXT
.split(path.delimiter)
.find(ext => ext.toUpperCase() === '.EXE')
}
return cmdExtension ?? '.exe'
}
async function safeReadPkgJson (pkgDir: string): Promise<DependencyManifest | null> {
try {
return await readPackageJsonFromDir(pkgDir) as DependencyManifest

View File

@@ -1,5 +1,5 @@
/// <reference path="../../../typings/index.d.ts"/>
import { promises as fs } from 'fs'
import { promises as fs, writeFileSync } from 'fs'
import path from 'path'
import logger, { globalWarn } from '@pnpm/logger'
import linkBins, {
@@ -36,6 +36,8 @@ const POWER_SHELL_IS_SUPPORTED = isWindows()
const IS_WINDOWS = isWindows()
const EXECUTABLE_SHEBANG_SUPPORTED = !IS_WINDOWS
const testOnWindows = IS_WINDOWS ? test : test.skip
function getExpectedBins (bins: string[]) {
const expectedBins = [...bins]
if (POWER_SHELL_IS_SUPPORTED) {
@@ -371,3 +373,14 @@ test("linkBins() emits global warning when bin points to path that doesn't exist
globalWarn
).toHaveBeenCalled()
})
testOnWindows('linkBins() shoud remove an existing .exe file from the target directory', async () => {
const binTarget = tempy.directory()
writeFileSync(path.join(binTarget, 'simple.exe'), '', 'utf8')
const warn = jest.fn()
const simpleFixture = f.prepare('simple-fixture')
await linkBins(path.join(simpleFixture, 'node_modules'), binTarget, { warn })
expect(await fs.readdir(binTarget)).toEqual(getExpectedBins(['simple']))
})

3
pnpm-lock.yaml generated
View File

@@ -1853,6 +1853,9 @@ importers:
'@zkochan/cmd-shim':
specifier: ^5.3.1
version: 5.3.1
'@zkochan/rimraf':
specifier: ^2.1.2
version: 2.1.2
bin-links:
specifier: ^3.0.3
version: 3.0.3