77 pull_request :
88 branches : [ main ]
99
10+ env :
11+ KEYCHAIN_NAME : build.keychain-db
12+ KEYCHAIN_PASSWORD : ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}
13+
1014jobs :
1115 test :
1216 name : Run Tests
@@ -39,24 +43,82 @@ jobs:
3943 - name : Select Xcode
4044 run : sudo xcode-select -s /Applications/Xcode_15.4.app/Contents/Developer || sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
4145
42- - name : Build arm64
46+ - name : Setup codesigning
47+ if : ${{ secrets.MACOS_CERTIFICATE_P12 != '' }}
48+ env :
49+ CERTIFICATE_P12 : ${{ secrets.MACOS_CERTIFICATE_P12 }}
50+ CERTIFICATE_PASSWORD : ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
4351 run : |
44- xcodebuild -project LangSwitcher.xcodeproj \
45- -scheme LangSwitcher \
46- -configuration Release \
47- -derivedDataPath build \
48- -arch arm64 \
49- ONLY_ACTIVE_ARCH=NO \
50- CODE_SIGN_IDENTITY="-" \
51- CODE_SIGNING_REQUIRED=NO \
52- CODE_SIGNING_ALLOWED=NO \
53- build
52+ # Decode certificate
53+ echo "$CERTIFICATE_P12" | base64 --decode > certificate.p12
54+
55+ # Create temporary keychain
56+ security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
57+ security set-keychain-settings -lut 21600 "$KEYCHAIN_NAME"
58+ security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
59+
60+ # Import certificate into keychain
61+ security import certificate.p12 \
62+ -k "$KEYCHAIN_NAME" \
63+ -P "$CERTIFICATE_PASSWORD" \
64+ -T /usr/bin/codesign \
65+ -T /usr/bin/security
66+
67+ # Allow codesign to access keychain without prompting
68+ security set-key-partition-list -S apple-tool:,apple:,codesign: \
69+ -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
70+
71+ # Add keychain to search list
72+ security list-keychains -d user -s "$KEYCHAIN_NAME" $(security list-keychains -d user | tr -d '"')
73+
74+ # Clean up certificate file
75+ rm certificate.p12
76+
77+ echo "CODESIGN_IDENTITY=${{ secrets.MACOS_SIGNING_IDENTITY }}" >> $GITHUB_ENV
78+ echo "CODESIGN_ENABLED=true" >> $GITHUB_ENV
5479
55- - name : Ad-hoc sign app bundle
80+ - name : Set ad-hoc signing (fallback)
81+ if : ${{ secrets.MACOS_CERTIFICATE_P12 == '' }}
82+ run : |
83+ echo "CODESIGN_IDENTITY=-" >> $GITHUB_ENV
84+ echo "CODESIGN_ENABLED=false" >> $GITHUB_ENV
85+
86+ - name : Build arm64
87+ run : |
88+ if [ "$CODESIGN_ENABLED" = "true" ]; then
89+ xcodebuild -project LangSwitcher.xcodeproj \
90+ -scheme LangSwitcher \
91+ -configuration Release \
92+ -derivedDataPath build \
93+ -arch arm64 \
94+ ONLY_ACTIVE_ARCH=NO \
95+ CODE_SIGN_IDENTITY="$CODESIGN_IDENTITY" \
96+ CODE_SIGN_STYLE=Manual \
97+ build
98+ else
99+ xcodebuild -project LangSwitcher.xcodeproj \
100+ -scheme LangSwitcher \
101+ -configuration Release \
102+ -derivedDataPath build \
103+ -arch arm64 \
104+ ONLY_ACTIVE_ARCH=NO \
105+ CODE_SIGN_IDENTITY="-" \
106+ CODE_SIGNING_REQUIRED=NO \
107+ CODE_SIGNING_ALLOWED=NO \
108+ build
109+ fi
110+
111+ - name : Sign app bundle
56112 run : |
57113 APP_PATH="build/Build/Products/Release/LangSwitcher.app"
58- # Strip linker-only signature and re-sign entire bundle (seals resources + Info.plist)
59- codesign --force --deep -s - "$APP_PATH"
114+ if [ "$CODESIGN_ENABLED" = "true" ]; then
115+ echo "=== Signing with Developer ID ==="
116+ codesign --force --options runtime --deep \
117+ -s "$CODESIGN_IDENTITY" "$APP_PATH"
118+ else
119+ echo "=== Ad-hoc signing ==="
120+ codesign --force --deep -s - "$APP_PATH"
121+ fi
60122 echo "=== Codesign verify ==="
61123 codesign -dvvv "$APP_PATH" 2>&1
62124 echo "=== spctl assess ==="
@@ -89,6 +151,11 @@ jobs:
89151 -ov -format UDZO \
90152 "$DMG_NAME"
91153
154+ # Sign the DMG itself if Developer ID is available
155+ if [ "$CODESIGN_ENABLED" = "true" ]; then
156+ codesign --force -s "$CODESIGN_IDENTITY" "$DMG_NAME"
157+ fi
158+
92159 echo "DMG_NAME=$DMG_NAME" >> $GITHUB_ENV
93160
94161 - name : Create ZIP with ditto (preserves permissions)
@@ -98,6 +165,28 @@ jobs:
98165 VERSION="${VERSION#v}"
99166 ditto -c -k --sequesterRsrc --keepParent "$APP_PATH" "LangSwitcher-${VERSION}-arm64.zip"
100167
168+ - name : Notarize DMG
169+ if : ${{ secrets.MACOS_CERTIFICATE_P12 != '' && secrets.MACOS_NOTARIZATION_APPLE_ID != '' }}
170+ env :
171+ APPLE_ID : ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }}
172+ APPLE_PASSWORD : ${{ secrets.MACOS_NOTARIZATION_PASSWORD }}
173+ TEAM_ID : ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
174+ run : |
175+ echo "=== Submitting DMG for notarization ==="
176+ xcrun notarytool submit "$DMG_NAME" \
177+ --apple-id "$APPLE_ID" \
178+ --password "$APPLE_PASSWORD" \
179+ --team-id "$TEAM_ID" \
180+ --wait
181+
182+ echo "=== Stapling notarization ticket to DMG ==="
183+ xcrun stapler staple "$DMG_NAME"
184+
185+ - name : Cleanup keychain
186+ if : ${{ always() && secrets.MACOS_CERTIFICATE_P12 != '' }}
187+ run : |
188+ security delete-keychain "$KEYCHAIN_NAME" 2>/dev/null || true
189+
101190 - name : Upload arm64 DMG
102191 uses : actions/upload-artifact@v4
103192 with :
@@ -123,23 +212,70 @@ jobs:
123212 - name : Select Xcode
124213 run : sudo xcode-select -s /Applications/Xcode_15.4.app/Contents/Developer || sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
125214
126- - name : Build x86_64
215+ - name : Setup codesigning
216+ if : ${{ secrets.MACOS_CERTIFICATE_P12 != '' }}
217+ env :
218+ CERTIFICATE_P12 : ${{ secrets.MACOS_CERTIFICATE_P12 }}
219+ CERTIFICATE_PASSWORD : ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
127220 run : |
128- xcodebuild -project LangSwitcher.xcodeproj \
129- -scheme LangSwitcher \
130- -configuration Release \
131- -derivedDataPath build \
132- -arch x86_64 \
133- ONLY_ACTIVE_ARCH=NO \
134- CODE_SIGN_IDENTITY="-" \
135- CODE_SIGNING_REQUIRED=NO \
136- CODE_SIGNING_ALLOWED=NO \
137- build
221+ echo "$CERTIFICATE_P12" | base64 --decode > certificate.p12
222+ security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
223+ security set-keychain-settings -lut 21600 "$KEYCHAIN_NAME"
224+ security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
225+ security import certificate.p12 \
226+ -k "$KEYCHAIN_NAME" \
227+ -P "$CERTIFICATE_PASSWORD" \
228+ -T /usr/bin/codesign \
229+ -T /usr/bin/security
230+ security set-key-partition-list -S apple-tool:,apple:,codesign: \
231+ -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
232+ security list-keychains -d user -s "$KEYCHAIN_NAME" $(security list-keychains -d user | tr -d '"')
233+ rm certificate.p12
234+ echo "CODESIGN_IDENTITY=${{ secrets.MACOS_SIGNING_IDENTITY }}" >> $GITHUB_ENV
235+ echo "CODESIGN_ENABLED=true" >> $GITHUB_ENV
236+
237+ - name : Set ad-hoc signing (fallback)
238+ if : ${{ secrets.MACOS_CERTIFICATE_P12 == '' }}
239+ run : |
240+ echo "CODESIGN_IDENTITY=-" >> $GITHUB_ENV
241+ echo "CODESIGN_ENABLED=false" >> $GITHUB_ENV
138242
139- - name : Ad-hoc sign app bundle
243+ - name : Build x86_64
244+ run : |
245+ if [ "$CODESIGN_ENABLED" = "true" ]; then
246+ xcodebuild -project LangSwitcher.xcodeproj \
247+ -scheme LangSwitcher \
248+ -configuration Release \
249+ -derivedDataPath build \
250+ -arch x86_64 \
251+ ONLY_ACTIVE_ARCH=NO \
252+ CODE_SIGN_IDENTITY="$CODESIGN_IDENTITY" \
253+ CODE_SIGN_STYLE=Manual \
254+ build
255+ else
256+ xcodebuild -project LangSwitcher.xcodeproj \
257+ -scheme LangSwitcher \
258+ -configuration Release \
259+ -derivedDataPath build \
260+ -arch x86_64 \
261+ ONLY_ACTIVE_ARCH=NO \
262+ CODE_SIGN_IDENTITY="-" \
263+ CODE_SIGNING_REQUIRED=NO \
264+ CODE_SIGNING_ALLOWED=NO \
265+ build
266+ fi
267+
268+ - name : Sign app bundle
140269 run : |
141270 APP_PATH="build/Build/Products/Release/LangSwitcher.app"
142- codesign --force --deep -s - "$APP_PATH"
271+ if [ "$CODESIGN_ENABLED" = "true" ]; then
272+ echo "=== Signing with Developer ID ==="
273+ codesign --force --options runtime --deep \
274+ -s "$CODESIGN_IDENTITY" "$APP_PATH"
275+ else
276+ echo "=== Ad-hoc signing ==="
277+ codesign --force --deep -s - "$APP_PATH"
278+ fi
143279 echo "=== Codesign verify ==="
144280 codesign -dvvv "$APP_PATH" 2>&1
145281 echo "=== spctl assess ==="
@@ -172,6 +308,10 @@ jobs:
172308 -ov -format UDZO \
173309 "$DMG_NAME"
174310
311+ if [ "$CODESIGN_ENABLED" = "true" ]; then
312+ codesign --force -s "$CODESIGN_IDENTITY" "$DMG_NAME"
313+ fi
314+
175315 echo "DMG_NAME=$DMG_NAME" >> $GITHUB_ENV
176316
177317 - name : Create ZIP with ditto (preserves permissions)
@@ -181,6 +321,28 @@ jobs:
181321 VERSION="${VERSION#v}"
182322 ditto -c -k --sequesterRsrc --keepParent "$APP_PATH" "LangSwitcher-${VERSION}-x86_64.zip"
183323
324+ - name : Notarize DMG
325+ if : ${{ secrets.MACOS_CERTIFICATE_P12 != '' && secrets.MACOS_NOTARIZATION_APPLE_ID != '' }}
326+ env :
327+ APPLE_ID : ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }}
328+ APPLE_PASSWORD : ${{ secrets.MACOS_NOTARIZATION_PASSWORD }}
329+ TEAM_ID : ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
330+ run : |
331+ echo "=== Submitting DMG for notarization ==="
332+ xcrun notarytool submit "$DMG_NAME" \
333+ --apple-id "$APPLE_ID" \
334+ --password "$APPLE_PASSWORD" \
335+ --team-id "$TEAM_ID" \
336+ --wait
337+
338+ echo "=== Stapling notarization ticket to DMG ==="
339+ xcrun stapler staple "$DMG_NAME"
340+
341+ - name : Cleanup keychain
342+ if : ${{ always() && secrets.MACOS_CERTIFICATE_P12 != '' }}
343+ run : |
344+ security delete-keychain "$KEYCHAIN_NAME" 2>/dev/null || true
345+
184346 - name : Upload x86_64 DMG
185347 uses : actions/upload-artifact@v4
186348 with :
@@ -206,23 +368,70 @@ jobs:
206368 - name : Select Xcode
207369 run : sudo xcode-select -s /Applications/Xcode_15.4.app/Contents/Developer || sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
208370
209- - name : Build Universal (arm64 + x86_64)
371+ - name : Setup codesigning
372+ if : ${{ secrets.MACOS_CERTIFICATE_P12 != '' }}
373+ env :
374+ CERTIFICATE_P12 : ${{ secrets.MACOS_CERTIFICATE_P12 }}
375+ CERTIFICATE_PASSWORD : ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
210376 run : |
211- xcodebuild -project LangSwitcher.xcodeproj \
212- -scheme LangSwitcher \
213- -configuration Release \
214- -derivedDataPath build \
215- -arch arm64 -arch x86_64 \
216- ONLY_ACTIVE_ARCH=NO \
217- CODE_SIGN_IDENTITY="-" \
218- CODE_SIGNING_REQUIRED=NO \
219- CODE_SIGNING_ALLOWED=NO \
220- build
377+ echo "$CERTIFICATE_P12" | base64 --decode > certificate.p12
378+ security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
379+ security set-keychain-settings -lut 21600 "$KEYCHAIN_NAME"
380+ security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
381+ security import certificate.p12 \
382+ -k "$KEYCHAIN_NAME" \
383+ -P "$CERTIFICATE_PASSWORD" \
384+ -T /usr/bin/codesign \
385+ -T /usr/bin/security
386+ security set-key-partition-list -S apple-tool:,apple:,codesign: \
387+ -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
388+ security list-keychains -d user -s "$KEYCHAIN_NAME" $(security list-keychains -d user | tr -d '"')
389+ rm certificate.p12
390+ echo "CODESIGN_IDENTITY=${{ secrets.MACOS_SIGNING_IDENTITY }}" >> $GITHUB_ENV
391+ echo "CODESIGN_ENABLED=true" >> $GITHUB_ENV
392+
393+ - name : Set ad-hoc signing (fallback)
394+ if : ${{ secrets.MACOS_CERTIFICATE_P12 == '' }}
395+ run : |
396+ echo "CODESIGN_IDENTITY=-" >> $GITHUB_ENV
397+ echo "CODESIGN_ENABLED=false" >> $GITHUB_ENV
221398
222- - name : Ad-hoc sign app bundle
399+ - name : Build Universal (arm64 + x86_64)
400+ run : |
401+ if [ "$CODESIGN_ENABLED" = "true" ]; then
402+ xcodebuild -project LangSwitcher.xcodeproj \
403+ -scheme LangSwitcher \
404+ -configuration Release \
405+ -derivedDataPath build \
406+ -arch arm64 -arch x86_64 \
407+ ONLY_ACTIVE_ARCH=NO \
408+ CODE_SIGN_IDENTITY="$CODESIGN_IDENTITY" \
409+ CODE_SIGN_STYLE=Manual \
410+ build
411+ else
412+ xcodebuild -project LangSwitcher.xcodeproj \
413+ -scheme LangSwitcher \
414+ -configuration Release \
415+ -derivedDataPath build \
416+ -arch arm64 -arch x86_64 \
417+ ONLY_ACTIVE_ARCH=NO \
418+ CODE_SIGN_IDENTITY="-" \
419+ CODE_SIGNING_REQUIRED=NO \
420+ CODE_SIGNING_ALLOWED=NO \
421+ build
422+ fi
423+
424+ - name : Sign app bundle
223425 run : |
224426 APP_PATH="build/Build/Products/Release/LangSwitcher.app"
225- codesign --force --deep -s - "$APP_PATH"
427+ if [ "$CODESIGN_ENABLED" = "true" ]; then
428+ echo "=== Signing with Developer ID ==="
429+ codesign --force --options runtime --deep \
430+ -s "$CODESIGN_IDENTITY" "$APP_PATH"
431+ else
432+ echo "=== Ad-hoc signing ==="
433+ codesign --force --deep -s - "$APP_PATH"
434+ fi
226435 echo "=== Codesign verify ==="
227436 codesign -dvvv "$APP_PATH" 2>&1
228437 echo "=== spctl assess ==="
@@ -255,6 +464,10 @@ jobs:
255464 -ov -format UDZO \
256465 "$DMG_NAME"
257466
467+ if [ "$CODESIGN_ENABLED" = "true" ]; then
468+ codesign --force -s "$CODESIGN_IDENTITY" "$DMG_NAME"
469+ fi
470+
258471 echo "DMG_NAME=$DMG_NAME" >> $GITHUB_ENV
259472
260473 - name : Create ZIP with ditto (preserves permissions)
@@ -264,6 +477,28 @@ jobs:
264477 VERSION="${VERSION#v}"
265478 ditto -c -k --sequesterRsrc --keepParent "$APP_PATH" "LangSwitcher-${VERSION}-universal.zip"
266479
480+ - name : Notarize DMG
481+ if : ${{ secrets.MACOS_CERTIFICATE_P12 != '' && secrets.MACOS_NOTARIZATION_APPLE_ID != '' }}
482+ env :
483+ APPLE_ID : ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }}
484+ APPLE_PASSWORD : ${{ secrets.MACOS_NOTARIZATION_PASSWORD }}
485+ TEAM_ID : ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
486+ run : |
487+ echo "=== Submitting DMG for notarization ==="
488+ xcrun notarytool submit "$DMG_NAME" \
489+ --apple-id "$APPLE_ID" \
490+ --password "$APPLE_PASSWORD" \
491+ --team-id "$TEAM_ID" \
492+ --wait
493+
494+ echo "=== Stapling notarization ticket to DMG ==="
495+ xcrun stapler staple "$DMG_NAME"
496+
497+ - name : Cleanup keychain
498+ if : ${{ always() && secrets.MACOS_CERTIFICATE_P12 != '' }}
499+ run : |
500+ security delete-keychain "$KEYCHAIN_NAME" 2>/dev/null || true
501+
267502 - name : Upload Universal DMG
268503 uses : actions/upload-artifact@v4
269504 with :
0 commit comments