feat(cli): Add upgrade command for CLI self-update#348
Merged
cameroncooke merged 4 commits intomainfrom Apr 19, 2026
Merged
Conversation
Add a new top-level `xcodebuildmcp upgrade` command that compares the installed version against the latest GitHub release, detects the install method (Homebrew, npm-global, npx, or unknown), shows a truncated release-notes excerpt, and either runs the correct upgrade command with inherited stdio or prints the manual command for unsupported channels. Supports `--check` to report versions without prompting and `--yes`/`-y` to skip the interactive confirmation for scripted use. Non-TTY callers that could auto-upgrade but omit `--yes` exit with status 1 so scripts do not hang. Repo owner/name and package name are now emitted from the version generator so the command reads them from package.json rather than hardcoding. The command short-circuits in src/cli.ts to avoid the full runtime bootstrap, mirroring `init` and `setup`. Co-Authored-By: Claude <noreply@anthropic.com>
commit: |
Each install channel now queries its own source of truth for the latest version instead of always using GitHub Releases. This prevents misleading results when release channels drift — e.g. GitHub may publish a version before the Homebrew tap bumps, or npm dist-tags may lag a pre-release. - Homebrew: `brew info --json=v2` (handles exit-0-with-empty-formulae) - npm-global / npx: `npm view <pkg>@latest version --json` - Unknown: falls back to GitHub Releases latest endpoint Release notes are fetched separately by tag from GitHub and are non-fatal if unavailable (404, timeout, network error). New DI surface: fetchLatestVersionForChannel, fetchReleaseNotesForTag, runChannelLookupCommand (for test isolation). The dependency factory rebuilds derived defaults from merged overrides so tests can mock at any level. Co-Authored-By: Claude <noreply@anthropic.com>
Fix parseVersion regex to accept hyphens in prerelease and build-metadata identifiers as required by SemVer 2.0.0, so versions like 1.0.0-alpha-1 or 1.0.0-rc-1+build-hash no longer fail to parse. Check the exit code of `brew info` before attempting to parse its stdout. Homebrew exits 0 for missing formulas (a quirk the existing empty-formulae path handles), but other failure modes like a broken brew install or permission errors exit non-zero with empty stdout — those now surface a specific exit-code error instead of a misleading "invalid JSON output". Reported in PR review by Cursor Bugbot and Sentry. Co-Authored-By: Claude <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 59d3e80. Configure here.
SemVer 2.0.0 §11 requires lexical comparison of prerelease identifiers in
ASCII sort order. The previous implementation used String.prototype.localeCompare,
which applies locale-dependent collation and can differ from ASCII ordering.
For example, "Alpha".localeCompare("alpha") returns 1 in common locales while
ASCII comparison returns -1.
Replace localeCompare with a UTF-16 code-unit comparison, which matches
ASCII ordering for the [0-9A-Za-z-] character set SemVer permits.
Reported in PR review by Cursor Bugbot.
Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Adds a new top-level
xcodebuildmcp upgradecommand that compares theinstalled version against the latest GitHub release, detects the install
method (Homebrew, npm-global, npx, or unknown), shows a truncated release
notes excerpt, and either runs the correct upgrade command with inherited
stdio or prints the manual command for unsupported channels.
Behavior
--checkreports current vs. latest without prompting (CI-friendly).--yes/-yskips the confirmation and auto-runs the upgrade.brew updatethenbrew upgrade xcodebuildmcp.npm install -g xcodebuildmcp@latest.so
--yesdoes not error in scripts.--yesexit 1 soscripts do not hang on an invisible confirm.
reported with a clear message plus the manual upgrade command when
detection succeeded.
Implementation notes
src/cli.tsmirroringinit/setup,so no runtime/daemon bootstrap runs for this path.
scripts/generate-version.ts) now emitspackageName,repositoryOwner,repositoryNameparsed frompackage.json; the command reads these fromsrc/utils/versioninstead of hardcoding identifiers. Generator fails the build if
repository.urlis not a parseable GitHub URL.spawn(..., { stdio: 'inherit' })withSIGINT/SIGTERM forwarding; the buffered
CommandExecutorisdeliberately not used because package-manager output and prompts need
to stream live.
vstripped, prereleasesuffixes supported) to avoid adding a
semverdependency for onecommand.
process.argv[1]andprocess.execPath, matching Homebrew Cellarpaths (Intel and Apple Silicon), global
node_modules/xcodebuildmcp,and the npx
_npxcache. Unknown paths never auto-run.Tests
67 unit tests cover the full matrix: version parsing/compare edge cases,
detection for each channel with exact argv assertions, TTY and non-TTY
paths for every flag combination, release-notes truncation at both the
20-line and 2000-char caps, and every GitHub failure class. All
dependencies are injected through
UpgradeDependencies— no realnetwork, spawn, or filesystem access in tests.