Skip to content
Closed
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
37 changes: 35 additions & 2 deletions packages/cli-kit/src/public/node/version.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {captureOutput} from './system.js'
import {localCLIVersion, globalCLIVersion, isPreReleaseVersion} from './version.js'
import {localCLIVersion, globalCLIVersion, isPreReleaseVersion, _resetGlobalCLIVersionCache} from './version.js'
import {inTemporaryDirectory} from './fs.js'
import {describe, expect, test, vi} from 'vitest'
import {describe, expect, test, vi, beforeEach} from 'vitest'

import which from 'which'

Expand Down Expand Up @@ -39,6 +39,10 @@ describe('localCLIVersion', () => {
})

describe('globalCLIVersion', () => {
beforeEach(() => {
_resetGlobalCLIVersionCache()
})

test('returns the version when a recent CLI is installed globally', async () => {
// Given
// TS is not detecting the return type correctly, so we need to cast it
Expand Down Expand Up @@ -78,6 +82,35 @@ describe('globalCLIVersion', () => {
expect(got).toBeUndefined()
expect(captureOutput).not.toHaveBeenCalled()
})

test('memoizes the result', async () => {
// Given
vi.mocked(which.sync).mockReturnValue(['path/to/shopify'] as unknown as string)
vi.mocked(captureOutput).mockResolvedValue('@shopify/cli/3.65.0')

// When
const got1 = await globalCLIVersion()
const got2 = await globalCLIVersion()

// Then
expect(got1).toBe('3.65.0')
expect(got2).toBe('3.65.0')
expect(captureOutput).toHaveBeenCalledTimes(1)
})

test('resets the cache when _resetGlobalCLIVersionCache is called', async () => {
// Given
vi.mocked(which.sync).mockReturnValue(['path/to/shopify'] as unknown as string)
vi.mocked(captureOutput).mockResolvedValue('@shopify/cli/3.65.0')

// When
await globalCLIVersion()
_resetGlobalCLIVersionCache()
await globalCLIVersion()

// Then
expect(captureOutput).toHaveBeenCalledTimes(2)
})
})

describe('isPreReleaseVersion', () => {
Expand Down
55 changes: 38 additions & 17 deletions packages/cli-kit/src/public/node/version.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import {captureOutput} from './system.js'
import which from 'which'
import {satisfies, SemVer} from 'semver'

/**
* Memoized value for the global CLI version.
*/
let memoizedGlobalCLIVersion: Promise<string | undefined> | undefined

/**
* Returns the version of the local dependency of the CLI if it's installed in the provided directory.
*
Expand All @@ -22,25 +28,40 @@ export async function localCLIVersion(directory: string): Promise<string | undef
*
* @returns The version of the CLI if it is globally installed or undefined.
*/
export async function globalCLIVersion(): Promise<string | undefined> {
try {
const env = {...process.env, SHOPIFY_CLI_NO_ANALYTICS: '1'}
// Both execa and which find the project dependency. We need to exclude it.
const shopifyBinaries = which.sync('shopify', {all: true}).filter((path) => !path.includes('node_modules'))
if (!shopifyBinaries[0]) return undefined
const output = await captureOutput(shopifyBinaries[0], [], {env})
const versionMatch = output.match(/@shopify\/cli\/([^\s]+)/)
if (versionMatch && versionMatch[1]) {
const version = versionMatch[1]
if (satisfies(version, `>=3.59.0`) || isPreReleaseVersion(version)) {
return version
export function globalCLIVersion(): Promise<string | undefined> {
if (memoizedGlobalCLIVersion) {
return memoizedGlobalCLIVersion
}
memoizedGlobalCLIVersion = (async () => {
try {
const env = {...process.env, SHOPIFY_CLI_NO_ANALYTICS: '1'}
// Both execa and which find the project dependency. We need to exclude it.
const shopifyBinaries = which.sync('shopify', {all: true}).filter((path) => !path.includes('node_modules'))
const binary = shopifyBinaries[0]
if (binary) {
const output = await captureOutput(binary, [], {env})
const versionMatch = output.match(/@shopify\/cli\/([^\s]+)/)
if (versionMatch && versionMatch[1]) {
const version = versionMatch[1]
if (satisfies(version, `>=3.59.0`) || isPreReleaseVersion(version)) {
return version
}
}
}
return undefined
// eslint-disable-next-line no-catch-all/no-catch-all
} catch {
return undefined
}
return undefined
// eslint-disable-next-line no-catch-all/no-catch-all
} catch {
return undefined
}
})()
return memoizedGlobalCLIVersion
}

/**
* Resets the memoized value for the global CLI version.
*/
export function _resetGlobalCLIVersionCache(): void {
memoizedGlobalCLIVersion = undefined
}

/**
Expand Down
Loading