From 342d73f5245038957c66fd5a467c076173de0c00 Mon Sep 17 00:00:00 2001 From: Alfonso Noriega Date: Fri, 13 Mar 2026 11:23:45 +0100 Subject: [PATCH 1/5] feat: default-on auto-upgrade with SHOPIFY_CLI_NO_AUTO_UPGRADE escape hatch (#22364) Auto-upgrade is now on by default. Users can opt out by setting SHOPIFY_CLI_NO_AUTO_UPGRADE=1. CI is still protected by isCI(). The SHOPIFY_CLI_FORCE_AUTO_UPGRADE override bypasses all guards (for testing). Removed the conf-store opt-in (getAutoUpgradeEnabled) and the promptAutoUpgrade() flow from the upgrade command. Co-Authored-By: Claude Sonnet 4.6 --- .changeset/auto-upgrade-escape-hatch.md | 6 +++++ .../cli-kit/src/private/node/conf-store.ts | 19 --------------- .../cli-kit/src/public/node/upgrade.test.ts | 19 ++++++++++----- packages/cli-kit/src/public/node/upgrade.ts | 23 +++---------------- packages/cli/src/cli/commands/upgrade.test.ts | 6 ++--- packages/cli/src/cli/commands/upgrade.ts | 3 +-- 6 files changed, 25 insertions(+), 51 deletions(-) create mode 100644 .changeset/auto-upgrade-escape-hatch.md diff --git a/.changeset/auto-upgrade-escape-hatch.md b/.changeset/auto-upgrade-escape-hatch.md new file mode 100644 index 00000000000..d7ec2b7a758 --- /dev/null +++ b/.changeset/auto-upgrade-escape-hatch.md @@ -0,0 +1,6 @@ +--- +'@shopify/cli-kit': patch +'@shopify/cli': patch +--- + +Auto-upgrade is now on by default. Set SHOPIFY_CLI_NO_AUTO_UPGRADE=1 to opt out. diff --git a/packages/cli-kit/src/private/node/conf-store.ts b/packages/cli-kit/src/private/node/conf-store.ts index 55d100b3ae7..1c585eae41a 100644 --- a/packages/cli-kit/src/private/node/conf-store.ts +++ b/packages/cli-kit/src/private/node/conf-store.ts @@ -32,7 +32,6 @@ export interface ConfSchema { devSessionStore?: string currentDevSessionId?: string cache?: Cache - autoUpgradeEnabled?: boolean } let _instance: LocalStorage | undefined @@ -268,24 +267,6 @@ export async function runWithRateLimit(options: RunWithRateLimitOptions, config return true } -/** - * Get auto-upgrade preference. - * - * @returns Whether auto-upgrade is enabled, or undefined if not set. - */ -export function getAutoUpgradeEnabled(config: LocalStorage = cliKitStore()): boolean | undefined { - return config.get('autoUpgradeEnabled') -} - -/** - * Set auto-upgrade preference. - * - * @param enabled - Whether auto-upgrade should be enabled. - */ -export function setAutoUpgradeEnabled(enabled: boolean, config: LocalStorage = cliKitStore()): void { - config.set('autoUpgradeEnabled', enabled) -} - export function getConfigStoreForPartnerStatus() { return new LocalStorage>({ projectName: 'shopify-cli-kit-partner-status', diff --git a/packages/cli-kit/src/public/node/upgrade.test.ts b/packages/cli-kit/src/public/node/upgrade.test.ts index ab6221aeaab..83a5d3d8b99 100644 --- a/packages/cli-kit/src/public/node/upgrade.test.ts +++ b/packages/cli-kit/src/public/node/upgrade.test.ts @@ -7,7 +7,6 @@ import {vi, describe, test, expect, beforeEach} from 'vitest' vi.mock('./is-global.js') vi.mock('./node-package-manager.js') vi.mock('./system.js') -vi.mock('../../private/node/conf-store.js') describe('cliInstallCommand', () => { test('returns undefined when process is not global', () => { @@ -69,6 +68,15 @@ describe('versionToAutoUpgrade', () => { expect(got).toBeUndefined() }) + test('returns undefined when SHOPIFY_CLI_NO_AUTO_UPGRADE is set', () => { + vi.mocked(checkForCachedNewVersion).mockReturnValue('3.100.0') + vi.stubEnv('SHOPIFY_CLI_NO_AUTO_UPGRADE', '1') + + const got = versionToAutoUpgrade() + + expect(got).toBeUndefined() + }) + test('returns version when SHOPIFY_CLI_FORCE_AUTO_UPGRADE is set', () => { vi.mocked(checkForCachedNewVersion).mockReturnValue('3.100.0') vi.stubEnv('SHOPIFY_CLI_FORCE_AUTO_UPGRADE', '1') @@ -78,15 +86,14 @@ describe('versionToAutoUpgrade', () => { expect(got).toBe('3.100.0') }) - test('returns undefined when auto-upgrade is not enabled', async () => { + test('FORCE_AUTO_UPGRADE overrides NO_AUTO_UPGRADE', () => { vi.mocked(checkForCachedNewVersion).mockReturnValue('3.100.0') - - const {getAutoUpgradeEnabled} = await import('../../private/node/conf-store.js') - vi.mocked(getAutoUpgradeEnabled).mockReturnValue(false) + vi.stubEnv('SHOPIFY_CLI_NO_AUTO_UPGRADE', '1') + vi.stubEnv('SHOPIFY_CLI_FORCE_AUTO_UPGRADE', '1') const got = versionToAutoUpgrade() - expect(got).toBeUndefined() + expect(got).toBe('3.100.0') }) }) diff --git a/packages/cli-kit/src/public/node/upgrade.ts b/packages/cli-kit/src/public/node/upgrade.ts index d845a77bdf9..b7dd8810166 100644 --- a/packages/cli-kit/src/public/node/upgrade.ts +++ b/packages/cli-kit/src/public/node/upgrade.ts @@ -13,9 +13,7 @@ import { import {outputContent, outputDebug, outputInfo, outputToken} from './output.js' import {cwd, moduleDirectory, sniffForPath} from './path.js' import {exec, isCI} from './system.js' -import {renderConfirmationPrompt} from './ui.js' import {isPreReleaseVersion} from './version.js' -import {getAutoUpgradeEnabled, setAutoUpgradeEnabled} from '../../private/node/conf-store.js' import {CLI_KIT_VERSION} from '../common/version.js' /** @@ -79,7 +77,7 @@ export async function runCLIUpgrade(): Promise { /** * Returns the version to auto-upgrade to, or undefined if auto-upgrade should be skipped. - * Auto-upgrade is disabled by default and must be enabled via `shopify upgrade`. + * Auto-upgrade is on by default. Set SHOPIFY_CLI_NO_AUTO_UPGRADE=1 to opt out. * Also skips for CI, pre-release versions, or when no newer version is available. * * @returns The version string to upgrade to, or undefined if no upgrade should happen. @@ -95,8 +93,8 @@ export function versionToAutoUpgrade(): string | undefined { outputDebug('Auto-upgrade: Forcing auto-upgrade because of SHOPIFY_CLI_FORCE_AUTO_UPGRADE.') return newerVersion } - if (!getAutoUpgradeEnabled()) { - outputDebug('Auto-upgrade: Skipping because auto-upgrade is not enabled.') + if (process.env.SHOPIFY_CLI_NO_AUTO_UPGRADE === '1') { + outputDebug('Auto-upgrade: Disabled via SHOPIFY_CLI_NO_AUTO_UPGRADE.') return undefined } if (isCI()) { @@ -125,21 +123,6 @@ export function getOutputUpdateCLIReminder(version: string): string { return outputContent`💡 Version ${version} available!`.value } -/** - * Prompts the user to enable or disable automatic upgrades, then persists their choice. - * - * @returns Whether the user chose to enable auto-upgrade. - */ -export async function promptAutoUpgrade(): Promise { - const enabled = await renderConfirmationPrompt({ - message: 'Enable automatic updates for Shopify CLI?', - confirmationMessage: 'Yes, automatically update', - cancellationMessage: "No, I'll update manually", - }) - setAutoUpgradeEnabled(enabled) - return enabled -} - async function upgradeLocalShopify(projectDir: string, currentVersion: string) { const packageJson = (await findUpAndReadPackageJson(projectDir)).content const packageJsonDependencies = packageJson.dependencies ?? {} diff --git a/packages/cli/src/cli/commands/upgrade.test.ts b/packages/cli/src/cli/commands/upgrade.test.ts index 61e04ba2059..e0e72458a1d 100644 --- a/packages/cli/src/cli/commands/upgrade.test.ts +++ b/packages/cli/src/cli/commands/upgrade.test.ts @@ -2,17 +2,15 @@ import Upgrade from './upgrade.js' import {describe, test, vi, expect} from 'vitest' vi.mock('@shopify/cli-kit/node/upgrade', () => ({ - promptAutoUpgrade: vi.fn().mockResolvedValue(true), runCLIUpgrade: vi.fn().mockResolvedValue(undefined), })) describe('upgrade command', () => { - test('calls promptAutoUpgrade and runCLIUpgrade', async () => { - const {promptAutoUpgrade, runCLIUpgrade} = await import('@shopify/cli-kit/node/upgrade') + test('calls runCLIUpgrade', async () => { + const {runCLIUpgrade} = await import('@shopify/cli-kit/node/upgrade') await Upgrade.run([], import.meta.url) - expect(promptAutoUpgrade).toHaveBeenCalledOnce() expect(runCLIUpgrade).toHaveBeenCalledOnce() }) }) diff --git a/packages/cli/src/cli/commands/upgrade.ts b/packages/cli/src/cli/commands/upgrade.ts index aeb5217ee2f..c8e39ffaf2c 100644 --- a/packages/cli/src/cli/commands/upgrade.ts +++ b/packages/cli/src/cli/commands/upgrade.ts @@ -1,5 +1,5 @@ import Command from '@shopify/cli-kit/node/base-command' -import {promptAutoUpgrade, runCLIUpgrade} from '@shopify/cli-kit/node/upgrade' +import {runCLIUpgrade} from '@shopify/cli-kit/node/upgrade' export default class Upgrade extends Command { static summary = 'Upgrades Shopify CLI.' @@ -9,7 +9,6 @@ export default class Upgrade extends Command { static description = this.descriptionWithoutMarkdown() async run(): Promise { - await promptAutoUpgrade() await runCLIUpgrade() } } From c52b249b7942d724c00612518da37e4bf7cb80ce Mon Sep 17 00:00:00 2001 From: Alfonso Noriega Date: Fri, 13 Mar 2026 12:51:37 +0100 Subject: [PATCH 2/5] fix: use static imports in upgrade.test.ts and fix prettier in postrun.test.ts --- packages/cli-kit/src/public/node/hooks/postrun.test.ts | 4 +++- packages/cli/src/cli/commands/upgrade.test.ts | 7 +++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/cli-kit/src/public/node/hooks/postrun.test.ts b/packages/cli-kit/src/public/node/hooks/postrun.test.ts index 4d2bc6ff6d6..a47e5a688ec 100644 --- a/packages/cli-kit/src/public/node/hooks/postrun.test.ts +++ b/packages/cli-kit/src/public/node/hooks/postrun.test.ts @@ -47,7 +47,9 @@ describe('autoUpgradeIfNeeded', () => { vi.mocked(versionToAutoUpgrade).mockReturnValue('3.100.0') vi.mocked(isMajorVersionChange).mockReturnValue(false) vi.mocked(runCLIUpgrade).mockRejectedValue(new Error('upgrade failed')) - vi.mocked(getOutputUpdateCLIReminder).mockReturnValue('💡 Version 3.100.0 available! Run `npm install -g @shopify/cli@latest`') + vi.mocked(getOutputUpdateCLIReminder).mockReturnValue( + '💡 Version 3.100.0 available! Run `npm install -g @shopify/cli@latest`', + ) await autoUpgradeIfNeeded() diff --git a/packages/cli/src/cli/commands/upgrade.test.ts b/packages/cli/src/cli/commands/upgrade.test.ts index e0e72458a1d..890b87ae6d0 100644 --- a/packages/cli/src/cli/commands/upgrade.test.ts +++ b/packages/cli/src/cli/commands/upgrade.test.ts @@ -1,13 +1,12 @@ import Upgrade from './upgrade.js' +import {runCLIUpgrade} from '@shopify/cli-kit/node/upgrade' import {describe, test, vi, expect} from 'vitest' -vi.mock('@shopify/cli-kit/node/upgrade', () => ({ - runCLIUpgrade: vi.fn().mockResolvedValue(undefined), -})) +vi.mock('@shopify/cli-kit/node/upgrade') describe('upgrade command', () => { test('calls runCLIUpgrade', async () => { - const {runCLIUpgrade} = await import('@shopify/cli-kit/node/upgrade') + vi.mocked(runCLIUpgrade).mockResolvedValue(undefined) await Upgrade.run([], import.meta.url) From aee38a07111cd48f50e8246c3c24dbcba6fc4fb6 Mon Sep 17 00:00:00 2001 From: Alfonso Noriega Date: Fri, 13 Mar 2026 12:54:51 +0100 Subject: [PATCH 3/5] fix: update OCLIF manifest and README for upgraded upgrade command Co-Authored-By: Claude Sonnet 4.6 --- packages/cli/README.md | 6 +++--- packages/cli/oclif.manifest.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/cli/README.md b/packages/cli/README.md index 0ef528b2e4d..07fb8dee74b 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -2765,16 +2765,16 @@ DESCRIPTION ## `shopify upgrade` -Shows details on how to upgrade Shopify CLI. +Upgrades Shopify CLI. ``` USAGE $ shopify upgrade DESCRIPTION - Shows details on how to upgrade Shopify CLI. + Upgrades Shopify CLI. - Shows details on how to upgrade Shopify CLI. + Upgrades Shopify CLI using your package manager. ``` ## `shopify version` diff --git a/packages/cli/oclif.manifest.json b/packages/cli/oclif.manifest.json index 8a053bd1ea3..46d246328f9 100644 --- a/packages/cli/oclif.manifest.json +++ b/packages/cli/oclif.manifest.json @@ -7822,8 +7822,8 @@ ], "args": { }, - "description": "Shows details on how to upgrade Shopify CLI.", - "descriptionWithMarkdown": "Shows details on how to upgrade Shopify CLI.", + "description": "Upgrades Shopify CLI.", + "descriptionWithMarkdown": "Upgrades Shopify CLI using your package manager.", "enableJsonFlag": false, "flags": { }, @@ -7835,7 +7835,7 @@ "pluginName": "@shopify/cli", "pluginType": "core", "strict": true, - "summary": "Shows details on how to upgrade Shopify CLI." + "summary": "Upgrades Shopify CLI." }, "version": { "aliases": [ From d521ef0568412906fa711634a37552d0cae850a8 Mon Sep 17 00:00:00 2001 From: Alfonso Noriega Date: Fri, 13 Mar 2026 13:08:03 +0100 Subject: [PATCH 4/5] fix: remove unused beforeEach import in is-global.test.ts Co-Authored-By: Claude Sonnet 4.6 --- packages/cli-kit/src/public/node/is-global.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli-kit/src/public/node/is-global.test.ts b/packages/cli-kit/src/public/node/is-global.test.ts index 5142ba9d77b..5ed18d5186c 100644 --- a/packages/cli-kit/src/public/node/is-global.test.ts +++ b/packages/cli-kit/src/public/node/is-global.test.ts @@ -2,7 +2,7 @@ import {currentProcessIsGlobal, inferPackageManagerForGlobalCLI, installGlobalCL import {terminalSupportsPrompting} from './system.js' import {renderSelectPrompt} from './ui.js' import {globalCLIVersion} from './version.js' -import {beforeEach, describe, expect, test, vi, afterEach} from 'vitest' +import {describe, expect, test, vi, afterEach} from 'vitest' vi.mock('./system.js') vi.mock('./ui.js') From 03f84cae05eaa163bc48f4bdb42a73fa5590caa6 Mon Sep 17 00:00:00 2001 From: Alfonso Noriega Date: Fri, 13 Mar 2026 13:15:36 +0100 Subject: [PATCH 5/5] fix: correct description field in oclif manifest for upgrade command --- packages/cli/oclif.manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/oclif.manifest.json b/packages/cli/oclif.manifest.json index 46d246328f9..78675c99fcf 100644 --- a/packages/cli/oclif.manifest.json +++ b/packages/cli/oclif.manifest.json @@ -7822,7 +7822,7 @@ ], "args": { }, - "description": "Upgrades Shopify CLI.", + "description": "Upgrades Shopify CLI using your package manager.", "descriptionWithMarkdown": "Upgrades Shopify CLI using your package manager.", "enableJsonFlag": false, "flags": {