a simple rust terminal ui (tui) for setting up alternative plc rotation keys driven by: secure enclave hardware (not synced) or software-based keys (synced to icloud)
plc
secure-enclave
touchid
icloud
atproto
1#!/bin/bash
2set -e
3
4APP="target/release/plc-touch.app"
5
6# Load env vars from .env if it exists
7if [ -f .env ]; then
8 set -a
9 source .env
10 set +a
11fi
12
13# These must be set in .env or as env vars:
14# CODESIGN_IDENTITY="Apple Development: Your Name (XXXXXXXXXX)"
15# BUNDLE_ID="com.yourcompany.plc-touch"
16# TEAM_ID="XXXXXXXXXX"
17#
18# For release builds, also set:
19# DEVELOPER_ID="Developer ID Application: Your Name (XXXXXXXXXX)"
20# APPLE_ID="your@email.com"
21# NOTARIZE_PASSWORD="app-specific-password"
22BUNDLE_ID="${BUNDLE_ID:-com.example.plc-touch}"
23TEAM_ID="${TEAM_ID:-XXXXXXXXXX}"
24
25MODE="${1:-dev}"
26
27case "$MODE" in
28 dev)
29 IDENTITY="${CODESIGN_IDENTITY:?Set CODESIGN_IDENTITY in .env}"
30 ;;
31 release)
32 IDENTITY="${DEVELOPER_ID:?Set DEVELOPER_ID in .env for release builds}"
33 ;;
34 *)
35 echo "Usage: ./build.sh [dev|release]"
36 echo " dev — Development build with provisioning profile (default)"
37 echo " release — Developer ID build with notarization for distribution"
38 exit 1
39 ;;
40esac
41
42echo "Building plc-touch ($MODE)..."
43
44KEYCHAIN_ACCESS_GROUP="${TEAM_ID}.${BUNDLE_ID}" cargo build --release
45
46# Create .app bundle
47rm -rf "$APP"
48mkdir -p "$APP/Contents/MacOS"
49
50cp target/release/plc-touch "$APP/Contents/MacOS/plc-touch"
51
52# Embed provisioning profile
53if [ "$MODE" = "dev" ] && [ -f embedded.provisionprofile ]; then
54 cp embedded.provisionprofile "$APP/Contents/embedded.provisionprofile"
55elif [ "$MODE" = "release" ] && [ -f release.provisionprofile ]; then
56 cp release.provisionprofile "$APP/Contents/embedded.provisionprofile"
57fi
58
59cat > "$APP/Contents/Info.plist" << EOF
60<?xml version="1.0" encoding="UTF-8"?>
61<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
62<plist version="1.0">
63<dict>
64 <key>CFBundleIdentifier</key>
65 <string>${BUNDLE_ID}</string>
66 <key>CFBundleName</key>
67 <string>plc-touch</string>
68 <key>CFBundleExecutable</key>
69 <string>plc-touch</string>
70 <key>CFBundleVersion</key>
71 <string>0.1.0</string>
72 <key>CFBundleShortVersionString</key>
73 <string>0.1.0</string>
74 <key>CFBundlePackageType</key>
75 <string>APPL</string>
76 <key>LSMinimumSystemVersion</key>
77 <string>13.0</string>
78</dict>
79</plist>
80EOF
81
82# Generate entitlements
83ENTITLEMENTS_FILE=$(mktemp)
84if [ "$MODE" = "release" ]; then
85 # Developer ID with provisioning profile: includes application-identifier + keychain-access-groups
86 cat > "$ENTITLEMENTS_FILE" << EOF
87<?xml version="1.0" encoding="UTF-8"?>
88<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
89<plist version="1.0">
90<dict>
91 <key>com.apple.application-identifier</key>
92 <string>${TEAM_ID}.${BUNDLE_ID}</string>
93 <key>keychain-access-groups</key>
94 <array>
95 <string>${TEAM_ID}.*</string>
96 </array>
97</dict>
98</plist>
99EOF
100else
101 # Dev: needs application-identifier for provisioning profile
102 cat > "$ENTITLEMENTS_FILE" << EOF
103<?xml version="1.0" encoding="UTF-8"?>
104<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
105<plist version="1.0">
106<dict>
107 <key>com.apple.application-identifier</key>
108 <string>${TEAM_ID}.${BUNDLE_ID}</string>
109 <key>keychain-access-groups</key>
110 <array>
111 <string>${TEAM_ID}.*</string>
112 </array>
113</dict>
114</plist>
115EOF
116fi
117
118if [ "$MODE" = "release" ]; then
119 # Release: Developer ID signing with hardened runtime (required for notarization)
120 codesign --force --sign "$IDENTITY" \
121 --options runtime \
122 --timestamp \
123 --entitlements "$ENTITLEMENTS_FILE" \
124 "$APP"
125
126 rm -f "$ENTITLEMENTS_FILE"
127
128 echo "✓ Signed with Developer ID"
129
130 # Create zip for notarization
131 ZIP="target/release/plc-touch.zip"
132 rm -f "$ZIP"
133 ditto -c -k --keepParent "$APP" "$ZIP"
134
135 echo "Submitting for notarization..."
136 APPLE_ID_ARG="${APPLE_ID:?Set APPLE_ID in .env for notarization}"
137 PASS_ARG="${NOTARIZE_PASSWORD:?Set NOTARIZE_PASSWORD in .env (app-specific password)}"
138
139 xcrun notarytool submit "$ZIP" \
140 --apple-id "$APPLE_ID_ARG" \
141 --team-id "$TEAM_ID" \
142 --password "$PASS_ARG" \
143 --wait
144
145 # Staple the notarization ticket to the app
146 xcrun stapler staple "$APP"
147
148 # Re-create zip with stapled app
149 rm -f "$ZIP"
150 ditto -c -k --keepParent "$APP" "$ZIP"
151
152 echo ""
153 echo "✓ Built, signed, notarized, and stapled"
154 echo " Distribute: $ZIP"
155 echo " Run with: $APP/Contents/MacOS/plc-touch"
156else
157 # Dev: Apple Development signing
158 codesign --force --sign "$IDENTITY" \
159 --entitlements "$ENTITLEMENTS_FILE" \
160 "$APP"
161
162 rm -f "$ENTITLEMENTS_FILE"
163
164 echo ""
165 echo "✓ Built and signed (dev)"
166 echo " Run with: $APP/Contents/MacOS/plc-touch"
167fi