Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
17 changes: 17 additions & 0 deletions packages/cli-kit/src/public/node/is-global.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,23 @@ describe('currentProcessIsGlobal', () => {
// Then
expect(got).toBeFalsy()
})

test('returns false on Windows when argv uses backslashes and projectDir uses forward slashes', () => {
// Given - mimic the exact Windows shape:
// projectDir comes from pathe -> normalized forward slashes
// argv[1] is OS-native -> backslash separated
const winProjectDir = 'C:/Users/me/project'
const winArgv1 = 'C:\\Users\\me\\project\\node_modules\\@shopify\\cli\\bin\\run.js'
vi.mocked(findPathUpSync).mockReturnValue(`${winProjectDir}/shopify.app.toml`)
const argv = ['node', winArgv1, 'shopify']

// When
const got = currentProcessIsGlobal(argv)

// Then - regression test for the path-separator bug that misclassified
// Windows local installs as global, triggering an unwanted `npm install -g`.
expect(got).toBeFalsy()
})
})

describe('inferPackageManagerForGlobalCLI', () => {
Expand Down
14 changes: 11 additions & 3 deletions packages/cli-kit/src/public/node/is-global.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {cwd, dirname, joinPath, sniffForPath} from './path.js'
import {cwd, dirname, isSubpath, joinPath, sniffForPath} from './path.js'
import {isUnitTest} from './context/local.js'
import {findPathUpSync, globSync} from './fs.js'
import {realpathSync} from 'fs'
Expand Down Expand Up @@ -27,9 +27,17 @@ export function currentProcessIsGlobal(argv = process.argv): boolean {

// From node docs: "The second element [of the array] will be the path to the JavaScript file being executed"
const binDir = argv[1] ?? ''
if (!binDir) {
return true
}

// If binDir starts with projectDir, then we are running a local CLI
const isLocal = binDir.startsWith(projectDir.trim())
// If binDir lives inside projectDir, we are running a local CLI.
// Use isSubpath (pathe.relative under the hood) instead of a raw
// string startsWith: projectDir flows through normalizePath and is
// forward-slash on every platform, while argv[1] is OS-native, so on
// Windows it arrives backslash-separated and a naive startsWith would
// misclassify a local install as global.
const isLocal = isSubpath(projectDir.trim(), binDir)

_isGlobal = !isLocal
return _isGlobal
Expand Down
Loading