Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## [Unreleased]

### Added

- Added `xcodebuildmcp upgrade` command to check for updates and upgrade in place. Supports `--check` (report-only) and `--yes`/`-y` (skip confirmation). Detects install method (Homebrew, npm-global, npx) and queries the appropriate channel source (`brew info`, `npm view`, or GitHub Releases) for the latest version. Non-interactive environments exit 1 when an auto-upgrade is possible but `--yes` was not supplied.

## [2.3.2]

### Fixed
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,13 @@ xcodebuildmcp tools
xcodebuildmcp simulator build --scheme MyApp --project-path ./MyApp.xcodeproj
```

Check for updates and upgrade in place:

```bash
xcodebuildmcp upgrade --check
xcodebuildmcp upgrade --yes
```

The CLI uses a per-workspace daemon for stateful operations (log capture, debugging, etc.) that auto-starts when needed. See [docs/CLI.md](docs/CLI.md) for full documentation.

## Documentation
Expand Down
51 changes: 51 additions & 0 deletions docs/CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,59 @@ xcodebuildmcp <workflow> <tool> --help

# Run interactive setup for .xcodebuildmcp/config.yaml
xcodebuildmcp setup

# Check for updates
xcodebuildmcp upgrade --check
```

## Upgrade

`xcodebuildmcp upgrade` checks for a newer release and optionally runs the upgrade.

```bash
# Check for updates without upgrading
xcodebuildmcp upgrade --check

# Upgrade automatically (skip confirmation prompt)
xcodebuildmcp upgrade --yes
```

### Flags

| Flag | Description |
|------|-------------|
| `--check` | Report the latest version and exit. Never prompts or runs an upgrade. |
| `--yes` / `-y` | Skip the confirmation prompt and run the upgrade command automatically. |

When both `--check` and `--yes` are supplied, `--check` wins.

### Channel-aware version lookup

The version check queries the source of truth for your install channel — `brew info` for Homebrew, `npm view` for npm/npx, or GitHub Releases for unknown installs. This avoids misleading results when release channels drift (e.g. GitHub may publish a version before the Homebrew tap bumps). If the channel-specific lookup fails, the command does not fall back to another source; it reports the error and exits 1.

### Install method behavior

The command detects how XcodeBuildMCP was installed and adapts accordingly:

| Method | Auto-upgrade | Command |
|--------|--------------|----------|
| Homebrew | Yes | `brew update && brew upgrade xcodebuildmcp` |
| npm global | Yes | `npm install -g xcodebuildmcp@latest` |
| npx | No | npx resolves `@latest` on each run; update the pinned version in your client config if needed. |
| Unknown | No | Manual instructions for all supported channels are shown. |

### Non-interactive mode

When stdin is not a TTY (CI, pipes, scripts):

- `--check` works normally and exits 0.
- `--yes` runs the upgrade for Homebrew and npm-global installs.
- Without `--check` or `--yes`, the command prints the manual upgrade command and exits 1 (it cannot prompt for confirmation).

### Lookup failures

If the channel-specific version check fails (network error, rate limit, timeout, missing formula), the command prints the detected install method and manual upgrade instructions, then exits 1.

## Tool Options

Each tool supports `--help` for detailed options:
Expand Down
10 changes: 10 additions & 0 deletions docs/GETTING_STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ Using `@latest` ensures clients resolve the newest version on each run.

See [CLI.md](CLI.md) for full CLI documentation.

### Checking for updates

After installing, check for newer releases at any time:

```bash
xcodebuildmcp upgrade --check
```

Homebrew and npm-global installs can auto-upgrade with `xcodebuildmcp upgrade --yes`. npx users don't need to upgrade explicitly — `@latest` resolves the newest version on each run. If you pinned a specific version in your MCP client config, update the version there instead.

## Project config (optional)
For deterministic session defaults and runtime configuration, add a config file at:

Expand Down
2 changes: 1 addition & 1 deletion scripts/check-docs-cli-commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ function extractCommandCandidates(content) {
}

function findInvalidCommands(files, validPairs, validWorkflows) {
const validTopLevel = new Set(['mcp', 'tools', 'daemon', 'init', 'setup']);
const validTopLevel = new Set(['mcp', 'tools', 'daemon', 'init', 'setup', 'upgrade']);
const validDaemonActions = new Set(['status', 'start', 'stop', 'restart', 'list']);
const findings = [];

Expand Down
24 changes: 23 additions & 1 deletion scripts/generate-version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,21 @@ import { readFile, writeFile } from 'node:fs/promises';
import path from 'node:path';

interface PackageJson {
name: string;
version: string;
iOSTemplateVersion: string;
macOSTemplateVersion: string;
repository?: {
url?: string;
};
}

function parseGitHubOwnerAndName(url: string): { owner: string; name: string } {
const match = url.match(/github\.com[/:]([^/]+)\/([^/.]+)/);
if (!match) {
throw new Error(`Cannot parse GitHub owner/name from repository URL: ${url}`);
}
return { owner: match[1], name: match[2] };
}

async function main(): Promise<void> {
Expand All @@ -15,10 +27,20 @@ async function main(): Promise<void> {
const raw = await readFile(packagePath, 'utf8');
const pkg = JSON.parse(raw) as PackageJson;

const repoUrl = pkg.repository?.url;
if (!repoUrl) {
throw new Error('package.json must have a repository.url field');
}

const repo = parseGitHubOwnerAndName(repoUrl);

const content =
`export const version = '${pkg.version}';\n` +
`export const iOSTemplateVersion = '${pkg.iOSTemplateVersion}';\n` +
`export const macOSTemplateVersion = '${pkg.macOSTemplateVersion}';\n`;
`export const macOSTemplateVersion = '${pkg.macOSTemplateVersion}';\n` +
`export const packageName = '${pkg.name}';\n` +
`export const repositoryOwner = '${repo.owner}';\n` +
`export const repositoryName = '${repo.name}';\n`;

await writeFile(versionPath, content, 'utf8');
}
Expand Down
11 changes: 11 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ async function runSetupCommand(): Promise<void> {
await app.parseAsync();
}

async function runUpgradeCommand(): Promise<void> {
const { registerUpgradeCommand } = await import('./cli/commands/upgrade.ts');
const app = await buildLightweightYargsApp();
registerUpgradeCommand(app);
await app.parseAsync();
}

async function main(): Promise<void> {
const cliBootstrapStartedAt = Date.now();
const earlyCommand = findTopLevelCommand(process.argv.slice(2));
Expand All @@ -95,6 +102,10 @@ async function main(): Promise<void> {
await runSetupCommand();
return;
}
if (earlyCommand === 'upgrade') {
await runUpgradeCommand();
return;
}

await hydrateSentryDisabledEnvFromProjectConfig();
initSentry({ mode: 'cli' });
Expand Down
Loading
Loading