Skip to content

Commit bb9adac

Browse files
committed
chore: add macOS release script + entitlements
1 parent 4fc0b2f commit bb9adac

2 files changed

Lines changed: 189 additions & 13 deletions

File tree

scripts/release-macos.sh

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# ─── Knot Code macOS Release Script ─────────────────────────────────
5+
# Usage: ./scripts/release-macos.sh <version>
6+
# Example: ./scripts/release-macos.sh 1.3.0
7+
#
8+
# Prerequisites:
9+
# - Developer ID cert in keychain (Soul Protocol LLC)
10+
# - Notary keychain profile "notary" configured
11+
# - gh CLI authenticated
12+
# - Entitlements.plist at src-tauri/Entitlements.plist
13+
# ─────────────────────────────────────────────────────────────────────
14+
15+
VERSION="${1:?Usage: $0 <version> (e.g. 1.3.0)}"
16+
IDENTITY="Developer ID Application: Soul Protocol LLC (9LR8Z8UQ9X)"
17+
ENTITLEMENTS="$(cd "$(dirname "$0")/.." && pwd)/src-tauri/Entitlements.plist"
18+
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
19+
DMG_NAME="KnotCode_${VERSION}_aarch64.dmg"
20+
WORK="/tmp/knotcode-release-${VERSION}"
21+
22+
cd "$REPO_ROOT"
23+
24+
echo "═══════════════════════════════════════════════════════"
25+
echo " Knot Code Release — v${VERSION} (macOS aarch64)"
26+
echo "═══════════════════════════════════════════════════════"
27+
28+
# ── 1. Preflight checks ─────────────────────────────────────────────
29+
echo ""
30+
echo "▸ [1/10] Preflight checks..."
31+
32+
if [ ! -f "$ENTITLEMENTS" ]; then
33+
echo " ✗ Entitlements.plist not found at $ENTITLEMENTS"
34+
echo " Creating default entitlements..."
35+
cat > "$ENTITLEMENTS" << 'PLIST'
36+
<?xml version="1.0" encoding="UTF-8"?>
37+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
38+
<plist version="1.0">
39+
<dict>
40+
<key>com.apple.security.cs.allow-jit</key>
41+
<true/>
42+
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
43+
<true/>
44+
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
45+
<true/>
46+
<key>com.apple.security.network.client</key>
47+
<true/>
48+
<key>com.apple.security.network.server</key>
49+
<true/>
50+
<key>com.apple.security.files.user-selected.read-write</key>
51+
<true/>
52+
</dict>
53+
</plist>
54+
PLIST
55+
fi
56+
57+
security find-identity -v -p codesigning | grep -q "9LR8Z8UQ9X" || {
58+
echo " ✗ Developer ID certificate not found in keychain"; exit 1
59+
}
60+
xcrun notarytool store-credentials --help > /dev/null 2>&1 || {
61+
echo " ✗ notarytool not available"; exit 1
62+
}
63+
command -v gh > /dev/null || { echo " ✗ gh CLI not found"; exit 1; }
64+
echo " ✓ All checks passed"
65+
66+
# ── 2. Bump versions ────────────────────────────────────────────────
67+
echo ""
68+
echo "▸ [2/10] Bumping version to ${VERSION}..."
69+
70+
# package.json
71+
sed -i '' "s/\"version\": \"[^\"]*\"/\"version\": \"${VERSION}\"/" package.json
72+
73+
# tauri.conf.json
74+
sed -i '' "s/\"version\": \"[^\"]*\"/\"version\": \"${VERSION}\"/" src-tauri/tauri.conf.json
75+
76+
# Cargo.toml (only the first version line)
77+
sed -i '' "0,/^version = \".*\"/s//version = \"${VERSION}\"/" src-tauri/Cargo.toml
78+
79+
echo " ✓ package.json, tauri.conf.json, Cargo.toml → ${VERSION}"
80+
81+
# ── 3. Build frontend + Tauri ───────────────────────────────────────
82+
echo ""
83+
echo "▸ [3/10] Building Tauri app..."
84+
85+
# Clean Rust cache to force Info.plist regeneration
86+
cargo clean --manifest-path src-tauri/Cargo.toml --release -p app 2>/dev/null || true
87+
88+
npx tauri build --bundles app 2>&1 | tail -3
89+
echo " ✓ Build complete"
90+
91+
# ── 4. Prepare app bundle ───────────────────────────────────────────
92+
echo ""
93+
echo "▸ [4/10] Preparing app bundle..."
94+
95+
rm -rf "$WORK"
96+
mkdir -p "$WORK"
97+
98+
APP_SRC="src-tauri/target/release/bundle/macos/KnotCode.app"
99+
APP="$WORK/KnotCode.app"
100+
ditto "$APP_SRC" "$APP"
101+
102+
# Patch version in Info.plist (Tauri sometimes caches old values)
103+
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString ${VERSION}" "$APP/Contents/Info.plist"
104+
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion ${VERSION}" "$APP/Contents/Info.plist"
105+
106+
ACTUAL_VER=$(defaults read "$APP/Contents/Info.plist" CFBundleShortVersionString)
107+
echo " ✓ App bundle ready (CFBundleShortVersionString: ${ACTUAL_VER})"
108+
109+
# ── 5. Code sign ────────────────────────────────────────────────────
110+
echo ""
111+
echo "▸ [5/10] Signing..."
112+
113+
codesign --force --options runtime \
114+
--sign "$IDENTITY" \
115+
--entitlements "$ENTITLEMENTS" \
116+
"$APP/Contents/MacOS/app"
117+
118+
codesign --force --options runtime \
119+
--sign "$IDENTITY" \
120+
--entitlements "$ENTITLEMENTS" \
121+
"$APP"
122+
123+
codesign --verify --deep --strict "$APP" 2>&1 | tail -1
124+
echo " ✓ Signed with Developer ID"
125+
126+
# ── 6. Notarize app ─────────────────────────────────────────────────
127+
echo ""
128+
echo "▸ [6/10] Notarizing app..."
129+
130+
ditto -c -k --keepParent "$APP" "$WORK/KnotCode.zip"
131+
xcrun notarytool submit "$WORK/KnotCode.zip" --keychain-profile "notary" --wait 2>&1 | tail -3
132+
xcrun stapler staple "$APP" 2>&1 | tail -1
133+
134+
# Verify Gatekeeper
135+
SPCTL=$(/usr/sbin/spctl --assess --type exec --verbose "$APP" 2>&1)
136+
echo " $SPCTL"
137+
echo "$SPCTL" | grep -q "accepted" || { echo " ✗ Gatekeeper rejected!"; exit 1; }
138+
139+
# ── 7. Create DMG ───────────────────────────────────────────────────
140+
echo ""
141+
echo "▸ [7/10] Creating DMG..."
142+
143+
DMG_PATH="$WORK/$DMG_NAME"
144+
hdiutil create -volname "KnotCode" -srcfolder "$APP" -ov -format UDZO "$DMG_PATH" 2>&1 | tail -1
145+
146+
# ── 8. Notarize DMG ─────────────────────────────────────────────────
147+
echo ""
148+
echo "▸ [8/10] Notarizing DMG..."
149+
150+
xcrun notarytool submit "$DMG_PATH" --keychain-profile "notary" --wait 2>&1 | tail -3
151+
xcrun stapler staple "$DMG_PATH" 2>&1 | tail -1
152+
153+
DMG_SIZE=$(ls -lh "$DMG_PATH" | awk '{print $5}')
154+
echo " ✓ DMG ready: ${DMG_NAME} (${DMG_SIZE})"
155+
156+
# ── 9. Git tag + push ───────────────────────────────────────────────
157+
echo ""
158+
echo "▸ [9/10] Committing and tagging..."
159+
160+
git add package.json src-tauri/tauri.conf.json src-tauri/Cargo.toml src-tauri/Cargo.lock src-tauri/Entitlements.plist 2>/dev/null || true
161+
git add package.json src-tauri/tauri.conf.json src-tauri/Cargo.toml
162+
git commit -m "chore: release v${VERSION}" --allow-empty 2>/dev/null || true
163+
git tag -f "v${VERSION}" -m "v${VERSION}"
164+
git push origin main 2>/dev/null || true
165+
git push origin "v${VERSION}" --force 2>/dev/null || true
166+
167+
echo " ✓ Tagged v${VERSION}"
168+
169+
# ── 10. Upload to GitHub Release ────────────────────────────────────
170+
echo ""
171+
echo "▸ [10/10] Uploading to GitHub Release..."
172+
173+
# Create release if it doesn't exist
174+
GH_TOKEN="" gh release create "v${VERSION}" \
175+
--title "Knot Code v${VERSION}" \
176+
--notes "Release v${VERSION} — see CHANGELOG.md for details." \
177+
--latest 2>/dev/null || true
178+
179+
# Upload DMG (clobber if exists)
180+
GH_TOKEN="" gh release upload "v${VERSION}" "$DMG_PATH" --clobber
181+
182+
echo ""
183+
echo "═══════════════════════════════════════════════════════"
184+
echo " ✅ Knot Code v${VERSION} released!"
185+
echo ""
186+
echo " DMG: ${DMG_PATH}"
187+
echo " Release: https://github.com/OpenKnots/code-editor/releases/tag/v${VERSION}"
188+
echo " Size: ${DMG_SIZE}"
189+
echo "═══════════════════════════════════════════════════════"

src-tauri/Entitlements.plist

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,18 @@
22
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
33
<plist version="1.0">
44
<dict>
5-
<!-- Required for notarized apps (hardened runtime) -->
65
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
76
<true/>
8-
9-
<!-- Allow JIT for V8/WebKit JavaScript engine (Tauri WebView) -->
107
<key>com.apple.security.cs.allow-jit</key>
118
<true/>
12-
13-
<!-- Allow loading unsigned dynamic libraries (Tauri plugins) -->
149
<key>com.apple.security.cs.disable-library-validation</key>
1510
<true/>
16-
17-
<!-- Network access (client — gateway WebSocket, API calls) -->
1811
<key>com.apple.security.network.client</key>
1912
<true/>
20-
21-
<!-- Network access (server — local dev server binding) -->
2213
<key>com.apple.security.network.server</key>
2314
<true/>
24-
25-
<!-- File access — user-selected files via open/save dialogs -->
2615
<key>com.apple.security.files.user-selected.read-write</key>
2716
<true/>
28-
29-
<!-- File access — read/write within the app's file-provider scope -->
3017
<key>com.apple.security.files.bookmarks.app-scope</key>
3118
<true/>
3219
</dict>

0 commit comments

Comments
 (0)