This repository was archived by the owner on Jul 15, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjustfile
More file actions
290 lines (246 loc) · 13.3 KB
/
Copy pathjustfile
File metadata and controls
290 lines (246 loc) · 13.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# Clicker - Presentation Remote System
# Run `just` to see available commands
set shell := ["bash", "-uc"]
set dotenv-load
# Find the most recently modified DerivedData folder
derived_data := `ls -td ~/Library/Developer/Xcode/DerivedData/Clicker-* 2>/dev/null | head -1`
mac_app := derived_data + "/Build/Products/Debug/ClickerRemoteReceiver.app"
release_mac_app := derived_data + "/Build/Products/Release/ClickerRemoteReceiver.app"
ios_app := derived_data + "/Build/Products/Debug-iphoneos/ClickerRemote.app"
ios_sim_app := derived_data + "/Build/Products/Debug-iphonesimulator/ClickerRemote.app"
# Version from Info.plist
version := `/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" MacApp/Info.plist 2>/dev/null || echo "1.0.0"`
dmg_name := "ClickerRemoteReceiver"
mac_logo := "public/logo/mac-logo.png"
notary_profile := "notarytool-profile"
signing_identity := "Developer ID Application: DOU Inc. (HD35YQ72U4)"
# ─────────────────────────────────────────────────────────────────────────────
# Development
# ─────────────────────────────────────────────────────────────────────────────
# Show available commands
default:
@just --list
# Generate Xcode project from project.yml
generate:
xcodegen generate
# Build Mac menu bar app
build-mac:
xcodebuild -scheme ClickerMac build
# Build iOS app for physical device
build-ios:
@if [ -z "${XCODE_DEVICE_ID:-}" ]; then \
echo "Error: XCODE_DEVICE_ID not set. Create .env file with XCODE_DEVICE_ID=your-udid"; \
exit 1; \
fi
xcodebuild -scheme ClickeriOS -destination 'id={{ env("XCODE_DEVICE_ID", "") }}' build
# Build iOS app for simulator
build-sim:
xcodebuild -scheme ClickeriOS -destination 'platform=iOS Simulator,name=iPhone 16,OS=18.2' build
# Build and run iOS app in simulator
run-sim: build-sim
xcrun simctl boot "iPhone 16" 2>/dev/null || true
xcrun simctl install booted "{{ ios_sim_app }}"
xcrun simctl launch booted com.dou.clicker-ios
# Build and run Mac app
run-mac: build-mac
open "{{ mac_app }}"
# Build and install iOS app on device
install-ios: build-ios
@if [ -z "${DEVICECTL_ID:-}" ]; then \
echo "Error: DEVICECTL_ID not set. Run 'xcrun devicectl list devices' to find your device UUID"; \
exit 1; \
fi
xcrun devicectl device install app --device {{ env("DEVICECTL_ID", "") }} "{{ ios_app }}"
# Build, install, and launch iOS app on device
run-ios: install-ios
xcrun devicectl device process launch --device {{ env("DEVICECTL_ID", "") }} com.dou.clicker-ios
# Build and deploy iPhone + Watch apps to physical devices
deploy: build-ios
@echo "📱 Installing iPhone + Watch apps on device..."
xcrun devicectl device install app --device {{ env("DEVICECTL_ID", "") }} "{{ ios_app }}"
@echo ""
@echo "✅ Deployed! Watch app will sync to your paired Apple Watch automatically."
@echo " If it doesn't appear, open the Watch app on iPhone → My Watch → Installed Apps"
# Uninstall iOS app from device (removes app + all data)
uninstall-ios:
@if [ -z "${DEVICECTL_ID:-}" ]; then \
echo "Error: DEVICECTL_ID not set. Run 'xcrun devicectl list devices' to find your device UUID"; \
exit 1; \
fi
@echo "🗑️ Uninstalling ClickerRemote from device..."
xcrun devicectl device uninstall app --device {{ env("DEVICECTL_ID", "") }} com.dou.clicker-ios || echo "App not installed or already removed"
@echo "✅ App and all data removed"
# Complete clean reinstall: uninstall, clean build, install fresh
clean-install-ios: uninstall-ios clean build-ios install-ios
@echo "✅ Fresh installation complete"
# Build and run iOS app in screenshot mode (no trial banner)
screenshot:
xcodebuild -scheme ClickeriOS \
-destination 'platform=iOS Simulator,name=iPhone 16,OS=18.2' \
SWIFT_ACTIVE_COMPILATION_CONDITIONS='DEBUG SCREENSHOT_MODE' \
build
xcrun simctl boot "iPhone 16" 2>/dev/null || true
xcrun simctl install booted "{{ ios_sim_app }}"
xcrun simctl launch booted com.dou.clicker-ios
# Clean build artifacts
clean:
xcodebuild -scheme ClickerMac clean
xcodebuild -scheme ClickeriOS clean
@echo "🧹 Cleaning up mounted volumes and installed app (requires sudo)..."
-sudo umount -f /Volumes/ClickerRemoteReceiver 2>/dev/null || true
-sudo rm -rf /Volumes/ClickerRemoteReceiver 2>/dev/null || true
-sudo rm -rf /Applications/ClickerRemoteReceiver.app 2>/dev/null || true
@echo "✅ Clean complete"
# List connected iOS devices
list-devices:
xcrun xctrace list devices
# ─────────────────────────────────────────────────────────────────────────────
# iOS App Store Distribution
# ─────────────────────────────────────────────────────────────────────────────
# Archive iOS app for App Store
archive-ios:
xcodebuild archive \
-scheme ClickeriOS \
-archivePath ./build/Clicker.xcarchive \
-destination 'generic/platform=iOS'
# Export and upload iOS app to App Store Connect
upload-ios:
xcodebuild -exportArchive \
-archivePath ./build/Clicker.xcarchive \
-exportPath ./build/export \
-exportOptionsPlist ExportOptions.plist
# Archive and upload iOS app to App Store Connect
release-ios: archive-ios upload-ios
# ─────────────────────────────────────────────────────────────────────────────
# Mac GitHub Distribution (Signed + Notarized DMG)
# ─────────────────────────────────────────────────────────────────────────────
# Build Mac app in Release configuration
build-mac-release:
xcodebuild -scheme ClickerMac -configuration Release build
# Verify app is signed correctly for distribution
verify-signing: build-mac-release
@echo "🔍 Verifying code signature..."
codesign --verify --deep --strict --verbose=2 "{{ release_mac_app }}"
@echo ""
@echo "🔍 Checking signing identity..."
codesign -dvv "{{ release_mac_app }}" 2>&1 | grep "Authority"
@echo ""
@echo "🔍 Checking Hardened Runtime..."
codesign -dvv "{{ release_mac_app }}" 2>&1 | grep -E "(flags|runtime)"
@echo ""
@echo "✅ Signature verification complete"
# Create DMG for Mac app distribution (unsigned)
dmg: build-mac-release
#!/usr/bin/env bash
set -euo pipefail
mkdir -p ./build
rm -f "./build/{{ dmg_name }}-{{ version }}.dmg"
echo "Creating DMG with custom icon..."
# Create iconset from PNG
rm -rf ./build/dmg-icon.iconset
mkdir -p ./build/dmg-icon.iconset
sips -s format png -z 16 16 "{{ mac_logo }}" --out ./build/dmg-icon.iconset/icon_16x16.png
sips -s format png -z 32 32 "{{ mac_logo }}" --out ./build/dmg-icon.iconset/icon_16x16@2x.png
sips -s format png -z 32 32 "{{ mac_logo }}" --out ./build/dmg-icon.iconset/icon_32x32.png
sips -s format png -z 64 64 "{{ mac_logo }}" --out ./build/dmg-icon.iconset/icon_32x32@2x.png
sips -s format png -z 128 128 "{{ mac_logo }}" --out ./build/dmg-icon.iconset/icon_128x128.png
sips -s format png -z 256 256 "{{ mac_logo }}" --out ./build/dmg-icon.iconset/icon_128x128@2x.png
sips -s format png -z 256 256 "{{ mac_logo }}" --out ./build/dmg-icon.iconset/icon_256x256.png
sips -s format png -z 512 512 "{{ mac_logo }}" --out ./build/dmg-icon.iconset/icon_256x256@2x.png
sips -s format png -z 512 512 "{{ mac_logo }}" --out ./build/dmg-icon.iconset/icon_512x512.png
sips -s format png -z 1024 1024 "{{ mac_logo }}" --out ./build/dmg-icon.iconset/icon_512x512@2x.png
iconutil -c icns ./build/dmg-icon.iconset -o ./build/dmg-icon.icns
# Create temp folder for DMG contents
rm -rf ./build/dmg-temp
mkdir -p ./build/dmg-temp
cp -R "{{ release_mac_app }}" ./build/dmg-temp/
ln -s /Applications ./build/dmg-temp/Applications
# Create read-write DMG first
hdiutil create -volname "{{ dmg_name }}" \
-srcfolder ./build/dmg-temp \
-ov -format UDRW \
"./build/{{ dmg_name }}-rw.dmg"
# Mount, set volume icon, unmount
hdiutil attach "./build/{{ dmg_name }}-rw.dmg" -mountpoint "/Volumes/{{ dmg_name }}"
cp ./build/dmg-icon.icns "/Volumes/{{ dmg_name }}/.VolumeIcon.icns"
SetFile -a C "/Volumes/{{ dmg_name }}"
hdiutil detach "/Volumes/{{ dmg_name }}"
sleep 2
# Convert to compressed read-only DMG
hdiutil convert "./build/{{ dmg_name }}-rw.dmg" \
-format UDZO \
-o "./build/{{ dmg_name }}-{{ version }}.dmg"
# Cleanup
rm -rf ./build/dmg-temp ./build/dmg-icon.iconset ./build/dmg-icon.icns "./build/{{ dmg_name }}-rw.dmg"
echo "✅ DMG created: ./build/{{ dmg_name }}-{{ version }}.dmg"
# Sign the DMG with Developer ID
sign-dmg: dmg
@echo "🔏 Signing DMG..."
codesign --force --sign "{{ signing_identity }}" "./build/{{ dmg_name }}-{{ version }}.dmg"
@echo "✅ DMG signed"
# Submit DMG for notarization and wait for result
notarize: sign-dmg
@echo "📤 Submitting for notarization..."
xcrun notarytool submit "./build/{{ dmg_name }}-{{ version }}.dmg" \
--keychain-profile {{ notary_profile }} \
--wait
@echo ""
@echo "📎 Stapling notarization ticket..."
xcrun stapler staple "./build/{{ dmg_name }}-{{ version }}.dmg"
@echo ""
@echo "✅ Notarization complete!"
# Verify the DMG is properly notarized
verify-notarization:
@echo "🔍 Verifying notarization..."
spctl --assess --type open --context context:primary-signature --verbose=2 "./build/{{ dmg_name }}-{{ version }}.dmg"
@echo ""
xcrun stapler validate "./build/{{ dmg_name }}-{{ version }}.dmg"
@echo "✅ Notarization verified"
# Build, sign, notarize DMG and show GitHub release instructions
release-mac: notarize
@echo ""
@echo "📦 Mac DMG ready for GitHub release!"
@echo " File: ./build/{{ dmg_name }}-{{ version }}.dmg"
@echo ""
@echo "To create a GitHub release:"
@echo " gh release create v{{ version }} ./build/{{ dmg_name }}-{{ version }}.dmg \\"
@echo " --title 'Clicker v{{ version }}' \\"
@echo " --notes 'Release notes here'"
@echo ""
# ─────────────────────────────────────────────────────────────────────────────
# Notarization Setup
# ─────────────────────────────────────────────────────────────────────────────
# Check if Developer ID certificate is installed
check-signing:
@echo "🔍 Looking for Developer ID Application certificate..."
@security find-identity -v -p codesigning | grep "Developer ID Application" || \
(echo "❌ No Developer ID Application certificate found!" && \
echo "" && \
echo "To create one:" && \
echo " 1. Go to https://developer.apple.com/account/resources/certificates/list" && \
echo " 2. Click + and select 'Developer ID Application'" && \
echo " 3. Follow the prompts to create and download" && \
echo " 4. Double-click the .cer file to install" && \
exit 1)
@echo "✅ Developer ID certificate found"
# Store notarization credentials in keychain (interactive)
setup-notary:
@echo "📝 Setting up notarization credentials..."
@echo "You'll need:"
@echo " - Your Apple ID email"
@echo " - Team ID: HD35YQ72U4"
@echo " - An app-specific password from https://appleid.apple.com"
@echo ""
xcrun notarytool store-credentials {{ notary_profile }} --team-id HD35YQ72U4
# Show the log from the last notarization submission
notary-log:
@echo "📋 Recent notarization submissions:"
xcrun notarytool history --keychain-profile {{ notary_profile }}
# ─────────────────────────────────────────────────────────────────────────────
# Homebrew Tap
# ─────────────────────────────────────────────────────────────────────────────
# Trigger homebrew-tap update after GitHub release is created
update-tap:
@echo "🍺 Triggering homebrew-tap update for v{{ version }}..."
gh workflow run update-clicker-cask.yml --repo douinc/homebrew-tap -f version={{ version }}
@echo "✅ Workflow triggered! Check: https://github.com/douinc/homebrew-tap/actions"