From aa35d805f61f826f700c0b1148dd0c6afffe65cf Mon Sep 17 00:00:00 2001 From: Alfonso Noriega Date: Tue, 12 May 2026 18:21:53 +0200 Subject: [PATCH 1/3] Auto-upgrade: skip project-local installs in postrun hook When the postrun hook triggers auto-upgrade, it now passes `{autoupgrade: true}` to `runCLIUpgrade`. With that flag set, project-local installs are skipped so the silent background flow doesn't surprise users by mutating their app's `package.json` / lockfile. Explicit `shopify upgrade` invocations still upgrade local projects \u2014 this only changes the implicit postrun-triggered path. --- .changeset/autoupgrade-skip-local-projects.md | 5 ++++ .../cli-kit/src/public/node/hooks/postrun.ts | 2 +- .../cli-kit/src/public/node/upgrade.test.ts | 11 ++++++++ packages/cli-kit/src/public/node/upgrade.ts | 25 ++++++++++++++++++- 4 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 .changeset/autoupgrade-skip-local-projects.md diff --git a/.changeset/autoupgrade-skip-local-projects.md b/.changeset/autoupgrade-skip-local-projects.md new file mode 100644 index 0000000000..e432dfe5b9 --- /dev/null +++ b/.changeset/autoupgrade-skip-local-projects.md @@ -0,0 +1,5 @@ +--- +'@shopify/cli-kit': patch +--- + +Auto-upgrade now skips project-local installs when triggered by the postrun hook. Running `shopify upgrade` explicitly still upgrades the project's `package.json` / lockfile; only the silent background flow is affected, so users aren't surprised by unsolicited diffs in their app project. diff --git a/packages/cli-kit/src/public/node/hooks/postrun.ts b/packages/cli-kit/src/public/node/hooks/postrun.ts index a21bf49f89..52d600cdcb 100644 --- a/packages/cli-kit/src/public/node/hooks/postrun.ts +++ b/packages/cli-kit/src/public/node/hooks/postrun.ts @@ -96,7 +96,7 @@ async function performAutoUpgrade(newerVersion: string): Promise { } try { - await runCLIUpgrade() + await runCLIUpgrade({autoupgrade: true}) await metadata.addPublicMetadata(() => ({env_auto_upgrade_success: true})) // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { diff --git a/packages/cli-kit/src/public/node/upgrade.test.ts b/packages/cli-kit/src/public/node/upgrade.test.ts index eaa672632c..29f9128899 100644 --- a/packages/cli-kit/src/public/node/upgrade.test.ts +++ b/packages/cli-kit/src/public/node/upgrade.test.ts @@ -208,6 +208,17 @@ describe('runCLIUpgrade', () => { // Then expect(exec).not.toHaveBeenCalled() }) + + test('skips project-local upgrade when called from the auto-upgrade postrun hook', async () => { + // Given + vi.mocked(currentProcessIsGlobal).mockReturnValue(false) + + // When + await runCLIUpgrade({autoupgrade: true}) + + // Then + expect(exec).not.toHaveBeenCalled() + }) }) describe('versionToAutoUpgrade', () => { diff --git a/packages/cli-kit/src/public/node/upgrade.ts b/packages/cli-kit/src/public/node/upgrade.ts index 00916066bc..2a4be38e1b 100644 --- a/packages/cli-kit/src/public/node/upgrade.ts +++ b/packages/cli-kit/src/public/node/upgrade.ts @@ -41,13 +41,27 @@ export function cliInstallCommand(): string | undefined { } } +/** + * Options for {@link runCLIUpgrade}. + */ +export interface RunCLIUpgradeOptions { + /** + * `true` when the upgrade is being triggered by the automatic postrun hook. + * In that case we skip project-local upgrades — those should only happen when the + * user explicitly runs `shopify upgrade` so we don't surprise them by mutating + * their `package.json` / lockfile in the background. + */ + autoupgrade?: boolean +} + /** * Runs the CLI upgrade using the appropriate package manager. * Determines the install command and executes it. * + * @param options - See {@link RunCLIUpgradeOptions}. * @throws AbortError if the package manager or command cannot be determined. */ -export async function runCLIUpgrade(): Promise { +export async function runCLIUpgrade(options: RunCLIUpgradeOptions = {}): Promise { // Path where the current project is (app/hydrogen) const path = sniffForPath() ?? cwd() const projectDir = getProjectDir(path) @@ -61,6 +75,15 @@ export async function runCLIUpgrade(): Promise { return } + // When triggered by the automatic postrun hook, skip project-local upgrades. + // Bumping `package.json` / lockfile silently in the background would surprise users + // and produce noisy diffs; explicit `shopify upgrade` invocations still upgrade the + // local project. + if (options.autoupgrade && !isGlobal) { + outputDebug('Auto-upgrade: Skipping project-local upgrade triggered by the postrun hook.') + return + } + // Generate the install command for the global CLI and execute it if (isGlobal) { const installCommand = cliInstallCommand() From b6b849ebf281a2ff91ceeaa22f57a1eca5e5ace4 Mon Sep 17 00:00:00 2001 From: Alfonso Noriega Date: Tue, 12 May 2026 18:26:52 +0200 Subject: [PATCH 2/3] Drop changeset (internal change, not user-facing) --- .changeset/autoupgrade-skip-local-projects.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .changeset/autoupgrade-skip-local-projects.md diff --git a/.changeset/autoupgrade-skip-local-projects.md b/.changeset/autoupgrade-skip-local-projects.md deleted file mode 100644 index e432dfe5b9..0000000000 --- a/.changeset/autoupgrade-skip-local-projects.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@shopify/cli-kit': patch ---- - -Auto-upgrade now skips project-local installs when triggered by the postrun hook. Running `shopify upgrade` explicitly still upgrades the project's `package.json` / lockfile; only the silent background flow is affected, so users aren't surprised by unsolicited diffs in their app project. From 3840873f420a5c7041ac021482173fda686bbc72 Mon Sep 17 00:00:00 2001 From: Alfonso Noriega Date: Tue, 12 May 2026 18:28:07 +0200 Subject: [PATCH 3/3] Drop debug log in autoupgrade local-skip path --- packages/cli-kit/src/public/node/upgrade.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/cli-kit/src/public/node/upgrade.ts b/packages/cli-kit/src/public/node/upgrade.ts index 2a4be38e1b..979a1af2ac 100644 --- a/packages/cli-kit/src/public/node/upgrade.ts +++ b/packages/cli-kit/src/public/node/upgrade.ts @@ -80,7 +80,6 @@ export async function runCLIUpgrade(options: RunCLIUpgradeOptions = {}): Promise // and produce noisy diffs; explicit `shopify upgrade` invocations still upgrade the // local project. if (options.autoupgrade && !isGlobal) { - outputDebug('Auto-upgrade: Skipping project-local upgrade triggered by the postrun hook.') return }