Skip to content

Commit c827fef

Browse files
committed
feat: add app lifecycle management and streamline release process
- Add stop_app_device, stop_app_sim, and stop_mac_app tools - Update launch tools to return process IDs for device apps - Fix WiFi device detection in device.ts - Add comprehensive release workflow with AXe bundling - Clean up package.json scripts and fix bin paths - Bundle AXe artifacts from GitHub releases for distribution - Streamline CI workflows for better performance
1 parent 508f112 commit c827fef

17 files changed

Lines changed: 699 additions & 36 deletions

File tree

.github/workflows/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Test workflow trigger

.github/workflows/release.yml

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
workflow_dispatch:
8+
inputs:
9+
version:
10+
description: 'Test version (e.g., 1.9.1-test)'
11+
required: true
12+
type: string
13+
14+
permissions:
15+
contents: write
16+
id-token: write
17+
18+
jobs:
19+
release:
20+
runs-on: macos-latest
21+
22+
steps:
23+
- name: Checkout code
24+
uses: actions/checkout@v4
25+
with:
26+
fetch-depth: 0
27+
28+
- name: Setup Node.js
29+
uses: actions/setup-node@v4
30+
with:
31+
node-version: '18'
32+
registry-url: 'https://registry.npmjs.org'
33+
34+
- name: Install dependencies
35+
run: npm ci --ignore-scripts
36+
37+
- name: Lint code
38+
run: npm run lint
39+
40+
- name: Check formatting
41+
run: npm run format:check
42+
43+
- name: Bundle AXe artifacts
44+
run: npm run bundle:axe
45+
46+
- name: Build TypeScript
47+
run: npm run build
48+
49+
- name: Get version from tag or input
50+
id: get_version
51+
run: |
52+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
53+
VERSION="${{ github.event.inputs.version }}"
54+
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
55+
echo "IS_TEST=true" >> $GITHUB_OUTPUT
56+
echo "📝 Test version: $VERSION"
57+
else
58+
VERSION=${GITHUB_REF#refs/tags/v}
59+
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
60+
echo "IS_TEST=false" >> $GITHUB_OUTPUT
61+
echo "🚀 Release version: $VERSION"
62+
# Update package.json version for production releases
63+
npm version $VERSION --no-git-tag-version
64+
fi
65+
66+
- name: Create package
67+
run: npm pack
68+
69+
- name: Test publish (dry run for manual triggers)
70+
if: github.event_name == 'workflow_dispatch'
71+
run: |
72+
echo "🧪 Testing package creation (dry run)"
73+
npm publish --dry-run --access public
74+
75+
- name: Publish to NPM (production releases only)
76+
if: github.event_name == 'push'
77+
run: npm publish --access public
78+
env:
79+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
80+
81+
- name: Create GitHub Release (production releases only)
82+
if: github.event_name == 'push'
83+
uses: softprops/action-gh-release@v1
84+
with:
85+
tag_name: v${{ steps.get_version.outputs.VERSION }}
86+
name: Release v${{ steps.get_version.outputs.VERSION }}
87+
body: |
88+
## Release v${{ steps.get_version.outputs.VERSION }}
89+
90+
### Features
91+
- Bundled AXe binary and frameworks for zero-setup UI automation
92+
- No manual installation required - works out of the box
93+
94+
### Installation
95+
```bash
96+
npm install -g xcodebuildmcp@${{ steps.get_version.outputs.VERSION }}
97+
```
98+
99+
Or use with npx:
100+
```bash
101+
npx xcodebuildmcp@${{ steps.get_version.outputs.VERSION }}
102+
```
103+
104+
📦 **NPM Package**: https://www.npmjs.com/package/xcodebuildmcp/v/${{ steps.get_version.outputs.VERSION }}
105+
106+
### What's Included
107+
- Latest AXe binary from [cameroncooke/axe](https://github.com/cameroncooke/axe)
108+
- All required frameworks (FBControlCore, FBDeviceControl, FBSimulatorControl, XCTestBootstrap)
109+
- Full XcodeBuildMCP functionality with UI automation support
110+
files: |
111+
xcodebuildmcp-${{ steps.get_version.outputs.VERSION }}.tgz
112+
draft: false
113+
prerelease: false
114+
115+
- name: Summary
116+
run: |
117+
if [ "${{ steps.get_version.outputs.IS_TEST }}" = "true" ]; then
118+
echo "🧪 Test completed for version: ${{ steps.get_version.outputs.VERSION }}"
119+
echo "Ready for production release!"
120+
else
121+
echo "🎉 Production release completed!"
122+
echo "Version: ${{ steps.get_version.outputs.VERSION }}"
123+
echo "📦 NPM: https://www.npmjs.com/package/xcodebuildmcp/v/${{ steps.get_version.outputs.VERSION }}"
124+
fi

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,6 @@ CLAUDE.md
9898
Makefile
9999
buildServer.json
100100

101+
# Bundled AXe artifacts (generated during build)
102+
bundled/
103+

example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,7 @@ struct CalculatorApp: App {
99
}
1010
}
1111
}
12+
13+
#Preview {
14+
ContentView()
15+
}

example_projects/iOS_Calculator/CalculatorAppPackage/Sources/CalculatorAppFeature/CalculatorDisplay.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import SwiftUI
44
struct CalculatorDisplay: View {
55
let expressionDisplay: String
66
let display: String
7+
var onDeleteLastDigit: (() -> Void)? = nil
78

89
var body: some View {
910
VStack(alignment: .trailing, spacing: 8) {
@@ -22,9 +23,24 @@ struct CalculatorDisplay: View {
2223
.frame(maxWidth: .infinity, alignment: .trailing)
2324
.lineLimit(1)
2425
.minimumScaleFactor(0.3)
26+
.gesture(DragGesture(minimumDistance: 20, coordinateSpace: .local)
27+
.onEnded { value in
28+
if value.translation.width < -20 || value.translation.width > 20 {
29+
onDeleteLastDigit?()
30+
}
31+
}
32+
)
2533
}
2634
.padding(.horizontal, 24)
2735
.padding(.bottom, 30)
2836
.frame(height: 140)
2937
}
30-
}
38+
}
39+
40+
struct CalculatorDisplay_Previews: PreviewProvider {
41+
static var previews: some View {
42+
CalculatorDisplay(expressionDisplay: "12 + 7", display: "19", onDeleteLastDigit: nil)
43+
.background(Color.black)
44+
.previewLayout(.sizeThatFits)
45+
}
46+
}

example_projects/iOS_Calculator/CalculatorAppPackage/Sources/CalculatorAppFeature/CalculatorInputHandler.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,8 @@ struct CalculatorInputHandler {
3131
break // Ignore unknown inputs
3232
}
3333
}
34-
}
34+
35+
func deleteLastDigit() {
36+
service.deleteLastDigit()
37+
}
38+
}

example_projects/iOS_Calculator/CalculatorAppPackage/Sources/CalculatorAppFeature/CalculatorService.swift

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,23 @@ public final class CalculatorService {
143143
isNewCalculation = true
144144
}
145145

146+
public func deleteLastDigit() {
147+
guard !hasError else { clear(); return }
148+
149+
if shouldResetDisplay || isNewCalculation {
150+
display = "0"
151+
shouldResetDisplay = false
152+
isNewCalculation = false
153+
} else if display.count > 1 {
154+
display.removeLast()
155+
if display == "-" { display = "0" }
156+
} else {
157+
display = "0"
158+
}
159+
currentNumber = Double(display) ?? 0
160+
updateExpressionDisplay()
161+
}
162+
146163
// MARK: - Private Helpers
147164
private func setError(_ message: String) {
148165
hasError = true
@@ -190,4 +207,4 @@ extension CalculatorService {
190207
public var previousValue: Double { previousNumber }
191208
public var currentOperation: Operation? { operation }
192209
public var willResetDisplay: Bool { shouldResetDisplay }
193-
}
210+
}

example_projects/iOS_Calculator/CalculatorAppPackage/Sources/CalculatorAppFeature/ContentView.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ public struct ContentView: View {
2020
// Display Section
2121
CalculatorDisplay(
2222
expressionDisplay: calculatorService.expressionDisplay,
23-
display: calculatorService.display
23+
display: calculatorService.display,
24+
onDeleteLastDigit: {
25+
inputHandler.deleteLastDigit()
26+
}
2427
)
2528

2629
// Button Grid

package.json

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
"main": "build/index.js",
66
"type": "module",
77
"bin": {
8-
"xcodebuildmcp": "./build/index.js",
9-
"xcodebuildmcp-diagnostic": "./build/diagnostic-cli.js"
8+
"xcodebuildmcp": "build/index.js",
9+
"xcodebuildmcp-diagnostic": "build/diagnostic-cli.js"
1010
},
1111
"scripts": {
12-
"prebuild": "node -e \"const fs = require('fs'); const pkg = require('./package.json'); fs.writeFileSync('src/version.ts', \\`export const version = '\\${pkg.version}';\\nexport const templateVersion = '\\${pkg.templateVersion}';\\n\\`)\"",
13-
"build": "npm run prebuild && tsup",
14-
"build:watch": "npm run prebuild && tsup --watch",
12+
"build": "node -e \"const fs = require('fs'); const pkg = require('./package.json'); fs.writeFileSync('src/version.ts', \\`export const version = '\\${pkg.version}';\\nexport const templateVersion = '\\${pkg.templateVersion}';\\n\\`)\" && tsup",
13+
"build:watch": "npm run build && tsup --watch",
14+
"bundle:axe": "scripts/build-axe.sh",
1515
"lint": "eslint 'src/**/*.{js,ts}'",
1616
"lint:fix": "eslint 'src/**/*.{js,ts}' --fix",
1717
"format": "prettier --write 'src/**/*.{js,ts}'",
@@ -20,7 +20,8 @@
2020
"diagnostic": "node build/diagnostic-cli.js"
2121
},
2222
"files": [
23-
"build"
23+
"build",
24+
"bundled"
2425
],
2526
"keywords": [
2627
"xcodebuild",

0 commit comments

Comments
 (0)