feat(cli): add non-interactive flag to hydrogen upgrade command#3744
feat(cli): add non-interactive flag to hydrogen upgrade command#3744z0n wants to merge 2 commits intoShopify:mainfrom
Conversation
| availableUpgrades: Array<Release>; | ||
| currentVersion: string; | ||
| currentDependencies: Record<string, string>; | ||
| nonInteractive?: boolean; |
There was a problem hiding this comment.
non-blocking: the caller guarantees that when nonInteractive is true, targetVersion is defined (via the precondition at the top of runUpgrade). the types don't express this constraint though -- a new call site could call getSelectedRelease({nonInteractive: true}) without the precondition, and the error message below would interpolate undefined. the runtime guard makes this safe in practice, just noting the type-level information leakage.
There was a problem hiding this comment.
I can fix it if necessary, would be a bit more work though.
| cumulativeRelease, | ||
| currentVersion, | ||
| selectedRelease, | ||
| nonInteractive: nonInteractive || targetVersion === 'next', |
There was a problem hiding this comment.
non-blocking: the caller computes the effective non-interactive state here (nonInteractive: nonInteractive || targetVersion === 'next'), but displayConfirmation handles it internally (if (targetVersion === 'next' || nonInteractive)). the same decision is split between two layers. this is pre-existing -- the targetVersion === 'next' check was already inside displayConfirmation -- and the PR correctly extends the pattern to generateUpgradeInstructionsFile. just noting the inconsistency for context.
There was a problem hiding this comment.
The better handling would probably be not having this targetVersion === 'next' special handling at all and just relying on nonInteractive.
As it was in the code before I don't want to break things, it's like this now.
| 'Run without any interactive prompts. Requires --version. Auto-accepts the upgrade confirmation and overwrites any existing upgrade instructions file.', | ||
| env: 'SHOPIFY_HYDROGEN_FLAG_NON_INTERACTIVE', | ||
| char: 'y', | ||
| aliases: ['yes'], |
There was a problem hiding this comment.
non-blocking: in the broader CLI ecosystem, --yes/-y traditionally means "auto-confirm" (think apt-get -y). here, --non-interactive goes a bit further -- it requires --version, auto-overwrites files, and changes TTY detection. a user who writes shopify hydrogen upgrade -y expecting auto-confirm behaviour will get a clear error message ("requires --version"), so it's mitigated in practice. just noting the potential UX friction.
There was a problem hiding this comment.
I think it's okay-ish here as you get immediate feedback if you've missed the --version.
WHY are these changes introduced?
Fixes #3743
I needed a non-interactive flag for the
shopify hydrogen upgradecommand to easily use the upgrade command via an agent skill.WHAT is this pull request doing?
This PR adds a
--non-interactive(alternatives:-y/--yes) flag which only works when a version is specified via--version. It then skips all interactive prompts and overwrites upgrade instructions if there are already some for that version upgrade.I worked around the
--version=nexta bit awkwardly as it already does a few things this flag does but I didn't want to break it as I'm not sure how it's used currently.HOW to test your changes?
In an outdated Hydrogen project, run
shopify hydrogen upgrade --version 2026.4.1 --non-interactive-> it should upgrade the project without any prompts.You can also try
shopify hydrogen upgrade --version 2026.4.1 --non-interactive < /dev/nullwhich would throw an error in previous versions as it would trigger aFailed to prompterror.Post-merge steps
Docs need to be updated with the new flag.
Checklist