Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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