Skip to content

Add typed JSON output to Hydrogen maintenance commands - #4103

Draft
gonzaloriestra wants to merge 1 commit into
gonzalo/hydrogen-json-setupfrom
gonzalo/hydrogen-json-maintenance
Draft

gonzaloriestra wants to merge 1 commit into
gonzalo/hydrogen-json-setupfrom
gonzalo/hydrogen-json-maintenance

Conversation

@gonzaloriestra

@gonzaloriestra gonzaloriestra commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

WHY are these changes introduced?

Shortcut setup and upgrades complete the finite Hydrogen command migration.

WHAT is this pull request doing?

Add typed receipts to shortcut and upgrade, keeping upgrade execution separate from final terminal output. Report installed versions, package changes and the instructions file, and use the shared fatal JSON path for unsupported shells. Document the complete JSON interface and add one CLI changeset for the stack. Only streaming commands remain in the shared exception list.

Without --json, the existing terminal presentation remains. Example JSON result:

shopify hydrogen shortcut --json
{"alias":"h2","shells":["zsh"]}

HOW to test your changes?

pnpm --filter @shopify/cli-hydrogen test src/commands/hydrogen/maintenance-json.test.ts src/commands/hydrogen/upgrade.test.ts

Build and typecheck were checked at each layer. The full stack passed 498 tests (6 skipped), lint, and schema discovery for all 22 finite commands. Command-line smoke checks covered a successful unlink result, fatal project errors, watch-mode rejection and schema environment flags. Live authenticated Shopify operations have not been exercised.

Stack

Draft 9 of 9. Previous: #4102. One changeset is included in #4103.

  1. Prepare Hydrogen commands for typed JSON output
  2. Add typed JSON output to Hydrogen build tooling
  3. Add typed JSON output to hydrogen customer-account-push
  4. Add typed JSON output to hydrogen deploy
  5. Add typed JSON output to Hydrogen environment commands
  6. Add typed JSON output to Hydrogen route generation
  7. Add typed JSON output to Hydrogen project and account commands
  8. Add typed JSON output to Hydrogen setup commands
  9. Add typed JSON output to Hydrogen maintenance commands

@gonzaloriestra
gonzaloriestra force-pushed the gonzalo/hydrogen-json-maintenance branch from 6122bca to 5ff252b Compare September 29, 2026 14:55
@shopify

shopify Bot commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

Oxygen deployed a preview of your gonzalo/hydrogen-json-maintenance branch. Details:

Storefront Status Preview link Deployment details Last update (UTC)
Skeleton (skeleton.hydrogen.shop) ✅ Successful (Logs) Preview deployment Inspect deployment September 29, 2026 2:57 PM

Learn more about Hydrogen's GitHub integration.

@fredericoo fredericoo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice way to wrap up the stack - shortcut and upgrade follow the same execute/present split as the rest of the series, fatal errors go through the shared path, and the human output is unchanged. The changeset and README section cover the whole series well.

a couple of small comments but overall LGTM

await displayUpgradeSummary({
appPath: result.directory,
currentVersion: result.currentVersion,
selectedRelease: selectedRelease!,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

non-blocking: presentUpgradeResult is exported and accepts status: 'upgraded' without a selectedRelease, so the only thing stopping displayUpgradeSummary crashing on selectedRelease.version is this !. It works today because runUpgrade always passes them together, but it's easy to hold wrong.

Let's make executeUpgrade return a discriminated union so the types enforce the pairing, yeah? Something like:

type UpgradeExecution =
  | {result: UpgradeResult & {status: 'unchanged'}}
  | {result: UpgradeResult & {status: 'upgraded'}; selectedRelease: Release};

and have presentUpgradeResult take the whole UpgradeExecution. That also lets us swap the inline import('../../lib/maintenance/types.js').UpgradeResult annotations for a normal type import, since the module is already imported at the top.

expect(outputMock.info()).toMatch(
/ success.+ latest Hydrogen version/is,
);
const {stdout} = await captureJsonOutput(() => runUpgrade({appPath}));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

non-blocking: love that the unchanged path is covered end to end. The upgraded path is only covered with a hand-built result through the presenter though (in maintenance-json.test.ts), so nothing checks what executeUpgrade actually puts in packages, removedPackages and instructionsFile. Those are the new bits of logic in this PR.

Would be worth adding a JSON assertion to one of the existing real upgrade flows (e.g. the one around line 2418) so a regression in how the receipt is built gets caught.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants