From 0f577d0a0a1efe56d5a8831cd85c73fda5395f91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isaac=20Rold=C3=A1n?= Date: Fri, 9 Oct 2026 10:14:22 +0200 Subject: [PATCH] Keep automatic upgrade output in JSON diagnostics --- .changeset/json-upgrade-output.md | 6 +++ .../cli-kit/src/public/node/upgrade.test.ts | 53 ++++++++++++++++++- packages/cli-kit/src/public/node/upgrade.ts | 30 ++++++++--- 3 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 .changeset/json-upgrade-output.md diff --git a/.changeset/json-upgrade-output.md b/.changeset/json-upgrade-output.md new file mode 100644 index 00000000000..b77f4549e5f --- /dev/null +++ b/.changeset/json-upgrade-output.md @@ -0,0 +1,6 @@ +--- +'@shopify/cli': patch +'@shopify/cli-kit': patch +--- + +Keep automatic upgrade output on stderr during JSON commands. diff --git a/packages/cli-kit/src/public/node/upgrade.test.ts b/packages/cli-kit/src/public/node/upgrade.test.ts index 29c3534374d..9aeaffa4138 100644 --- a/packages/cli-kit/src/public/node/upgrade.test.ts +++ b/packages/cli-kit/src/public/node/upgrade.test.ts @@ -11,7 +11,9 @@ import { } from './upgrade.js' import {Notification, fetchNotifications} from './notifications-system.js' import {globalCLIVersion, isPreReleaseVersion} from './version.js' -import {mockAndCaptureOutput} from './testing/output.js' +import {mockAndCaptureOutput, withCapturedStandardStreams} from './testing/output.js' +import {runWithCommandEventsForCommand} from './command-events.js' +import {outputResult} from './output.js' import {getAutoUpgradeEnabled} from '../../private/node/conf-store.js' import {CLI_KIT_VERSION} from '../common/version.js' import {SemVer} from 'semver' @@ -171,6 +173,55 @@ describe('runCLIUpgrade', () => { expect(exec).toHaveBeenCalledWith('npm', ['install', '-g', '@shopify/cli@latest'], {stdio: 'inherit'}) }) + test.each([false, true])('JSON auto-upgrade keeps stdout clear when the install fails: %s', async (fails) => { + vi.mocked(isUnitTest).mockReturnValue(false) + vi.mocked(currentProcessIsGlobal).mockReturnValue(true) + vi.mocked(inferPackageManagerForGlobalCLI).mockReturnValue('npm') + const failure = new Error('Install failed') + vi.mocked(exec).mockImplementationOnce(async (_command, _args, options) => { + if (options?.stdio === 'inherit') { + process.stdout.write('added 1 package\n') + process.stderr.write('package manager warning\n') + } else { + if (options?.stdout && options.stdout !== 'inherit') options.stdout.write('added 1 package\n') + if (options?.stderr && options.stderr !== 'inherit') options.stderr.write('package manager warning') + } + if (fails) throw failure + }) + + await withCapturedStandardStreams(async ({stdout, stderr}) => { + await runWithCommandEventsForCommand(['--json'], async () => { + outputResult('{"status":"success"}') + const upgrade = runCLIUpgrade({autoupgrade: true}) + if (fails) await expect(upgrade).rejects.toBe(failure) + else await upgrade + }) + + expect(stdout()).toBe('{"status":"success"}\n') + const events = stderr() + .trim() + .split('\n') + .map((line) => JSON.parse(line)) + expect(events).toContainEqual(expect.objectContaining({type: 'diagnostic', message: 'npm: added 1 package'})) + expect(events).toContainEqual( + expect.objectContaining({type: 'diagnostic', message: 'npm: package manager warning'}), + ) + if (!fails) { + expect(events).toContainEqual( + expect.objectContaining({ + type: 'diagnostic', + message: `Shopify CLI upgraded. You're now on version ${CLI_KIT_VERSION}.`, + }), + ) + } + expect(exec).toHaveBeenCalledWith('npm', ['install', '-g', '@shopify/cli@latest'], { + stdin: 'inherit', + stdout: expect.anything(), + stderr: expect.anything(), + }) + }) + }) + test('runs the install command via exec for a global yarn install', async () => { // Given vi.mocked(currentProcessIsGlobal).mockReturnValue(true) diff --git a/packages/cli-kit/src/public/node/upgrade.ts b/packages/cli-kit/src/public/node/upgrade.ts index b1fcf2055c0..d9dc5e4d9c7 100644 --- a/packages/cli-kit/src/public/node/upgrade.ts +++ b/packages/cli-kit/src/public/node/upgrade.ts @@ -12,7 +12,8 @@ import { getPackageManager, } from './node-package-manager.js' import {outputContent, outputDebug, outputInfo, outputToken, outputWarn} from './output.js' -import {renderSuccess} from './ui.js' +import {renderConcurrent, renderSuccess} from './ui.js' +import {commandEventOutputMode} from './command-events.js' import {cwd, moduleDirectory, sniffForPath} from './path.js' import {exec, isCI} from './system.js' import {globalCLIVersion, isPreReleaseVersion} from './version.js' @@ -105,7 +106,20 @@ export async function runCLIUpgrade(options: RunCLIUpgradeOptions = {}): Promise outputContent`${headline} Now upgrading by running: ${outputToken.genericShellCommand(installCommand)}...`, ) - await exec(command, args, {stdio: 'inherit'}) + const jsonOutput = commandEventOutputMode() === 'json' + if (jsonOutput) { + await renderConcurrent({ + processes: [ + { + prefix: command, + action: async (stdout, stderr) => exec(command, args, {stdin: 'inherit', stdout, stderr}), + }, + ], + showTimestamps: false, + }) + } else { + await exec(command, args, {stdio: 'inherit'}) + } // A zero exit code doesn't guarantee the right version landed: the version check above // queries the public npm registry, while the install goes through whatever registry the @@ -126,10 +140,14 @@ export async function runCLIUpgrade(options: RunCLIUpgradeOptions = {}): Promise 'Your package manager may be resolving @shopify/cli from a registry with outdated versions. Check your npm registry configuration and try again.', ) } - renderSuccess({ - headline: 'Shopify CLI upgraded.', - body: `You're now on version ${installedVersion}.`, - }) + if (jsonOutput) { + outputInfo(`Shopify CLI upgraded. You're now on version ${installedVersion}.`) + } else { + renderSuccess({ + headline: 'Shopify CLI upgraded.', + body: `You're now on version ${installedVersion}.`, + }) + } } else if (projectDir) { await upgradeLocalShopify(projectDir, CLI_KIT_VERSION) } else {