1313 - windows
1414 - macos
1515 - linux
16- mac_notarize :
17- description : " mac notarization mode (arm64 only)"
18- required : false
19- default : " auto"
20- type : choice
21- options :
22- - auto
23- - " on"
24- - " off"
2516 push :
2617 tags :
2718 - " v*"
5647 name : build-windows
5748 path : |
5849 release/*.exe
59- release/*.exe.blockmap
60- release/latest.yml
6150 retention-days : 3
6251
6352 build-macos :
@@ -80,154 +69,51 @@ jobs:
8069 run : npm run electron:build
8170
8271 - name : Decode signing certificate
83- if : ${{ env.HAS_CERT == 'true' }}
8472 env :
8573 MAC_CERT_P12_BASE64 : ${{ secrets.MAC_CERT_P12_BASE64 }}
8674 MAC_CERT_PASSWORD : ${{ secrets.MAC_CERT_PASSWORD }}
8775 run : |
76+ if [ "${HAS_CERT}" != "true" ]; then
77+ echo "::error::Missing signing certificate (MAC_CERT_P12_BASE64 secret not set)"
78+ exit 1
79+ fi
8880 printf '%s' "$MAC_CERT_P12_BASE64" | tr -d '\r\n' | base64 --decode > /tmp/cert.p12
8981 openssl pkcs12 -in /tmp/cert.p12 -passin pass:"$MAC_CERT_PASSWORD" -noout
82+ echo "Certificate decoded and validated"
9083
91- - name : Resolve arm64 notarization strategy
92- id : mac_mode
93- shell : bash
94- env :
95- MANUAL_MODE : ${{ github.event_name == 'workflow_dispatch' && inputs.mac_notarize || 'auto' }}
96- REF_NAME : ${{ github.ref_name }}
97- run : |
98- set -euo pipefail
99- MODE="${MANUAL_MODE:-auto}"
100- NOTARIZE=false
101-
102- if [[ "$MODE" == "on" ]]; then
103- NOTARIZE=true
104- elif [[ "$MODE" == "off" ]]; then
105- NOTARIZE=false
106- else
107- # auto: vX.Y.0 公证;vX.Y.Z(Z>0) 只签名
108- if [[ "$REF_NAME" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
109- PATCH="${BASH_REMATCH[3]}"
110- if [[ "$PATCH" == "0" ]]; then
111- NOTARIZE=true
112- fi
113- fi
114- fi
115-
116- echo "mode=$MODE" >> "$GITHUB_OUTPUT"
117- echo "notarize=$NOTARIZE" >> "$GITHUB_OUTPUT"
118- echo "Resolved arm64 notarization: $NOTARIZE (mode=$MODE, ref=$REF_NAME)"
119-
120- - name : Validate notarization credentials
121- if : ${{ env.HAS_CERT == 'true' && steps.mac_mode.outputs.notarize == 'true' }}
122- env :
123- APPLE_ID : ${{ secrets.APPLE_ID }}
124- APPLE_APP_SPECIFIC_PASSWORD : ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
125- APPLE_TEAM_ID : ${{ secrets.APPLE_TEAM_ID }}
126- run : |
127- echo "APPLE_ID len=${#APPLE_ID}, TEAM=$APPLE_TEAM_ID, APP_PWD len=${#APPLE_APP_SPECIFIC_PASSWORD}"
128- xcrun notarytool history \
129- --apple-id "$APPLE_ID" \
130- --password "$APPLE_APP_SPECIFIC_PASSWORD" \
131- --team-id "$APPLE_TEAM_ID" >/dev/null
132-
133- # ── x64: sign only, no notarization ──────────────────────────────────
134- - name : Package for macOS (x64, sign only)
84+ - name : Package for macOS (x64 + arm64, sign only)
13585 timeout-minutes : 15
13686 env :
137- CSC_LINK : ${{ env.HAS_CERT == 'true' && '/tmp/cert.p12' || '' }}
138- CSC_KEY_PASSWORD : ${{ secrets.MAC_CERT_PASSWORD }}
139- run : |
140- set -euo pipefail
141- for i in 1 2 3; do
142- echo "electron-builder x64 attempt $i/3 (notarize=false)"
143- if npx electron-builder --mac --x64 --config electron-builder.yml -c.mac.notarize=false --publish never; then
144- exit 0
145- fi
146- if [ "$i" -lt 3 ]; then sleep $((i * 30)); fi
147- done
148- exit 1
149-
150- # Move x64 artifacts aside so arm64 build produces clean latest-mac.yml
151- - name : Stash x64 artifacts
152- run : |
153- mkdir -p release/x64-stash
154- mv release/*.dmg release/*.zip release/*.blockmap release/x64-stash/ 2>/dev/null || true
155- rm -f release/latest-mac.yml
156-
157- # ── arm64: sign + conditional notarization ───────────────────────────
158- - name : Package for macOS (arm64)
159- timeout-minutes : 35
160- env :
161- CSC_LINK : ${{ env.HAS_CERT == 'true' && '/tmp/cert.p12' || '' }}
87+ CSC_LINK : /tmp/cert.p12
16288 CSC_KEY_PASSWORD : ${{ secrets.MAC_CERT_PASSWORD }}
163- APPLE_ID : ${{ steps.mac_mode.outputs.notarize == 'true' && secrets.APPLE_ID || '' }}
164- APPLE_APP_SPECIFIC_PASSWORD : ${{ steps.mac_mode.outputs.notarize == 'true' && secrets.APPLE_APP_SPECIFIC_PASSWORD || '' }}
165- APPLE_TEAM_ID : ${{ steps.mac_mode.outputs.notarize == 'true' && secrets.APPLE_TEAM_ID || '' }}
166- DEBUG : electron-notarize*
167- run : |
168- set -euo pipefail
169-
170- NOTARIZE="${{ steps.mac_mode.outputs.notarize }}"
171-
172- for i in 1 2 3; do
173- echo "electron-builder arm64 attempt $i/3 (notarize=$NOTARIZE)"
174-
175- if [[ "$NOTARIZE" == "true" ]]; then
176- npx electron-builder --mac --arm64 --config electron-builder.yml --publish never
177- else
178- npx electron-builder --mac --arm64 --config electron-builder.yml -c.mac.notarize=false --publish never
179- fi
180-
181- if [ $? -eq 0 ]; then
182- exit 0
183- fi
184-
185- if [ "$i" -lt 3 ]; then sleep $((i * 60)); fi
186- done
187-
188- exit 1
189-
190- # Restore x64 artifacts alongside arm64 output
191- - name : Restore x64 artifacts
192- run : mv release/x64-stash/* release/ && rmdir release/x64-stash
89+ run : npx electron-builder --mac --config electron-builder.yml --publish never
19390
19491 - name : Verify code signature
195- if : ${{ env.HAS_CERT == 'true' }}
19692 run : |
197- # Verify every .app bundle produced (arm64 + x64)
19893 APPS=$(find release -name "CodePilot.app" -maxdepth 3)
19994 if [ -z "$APPS" ]; then
200- echo "::warning ::No CodePilot.app found, skipping signature verification "
201- exit 0
95+ echo "::error ::No CodePilot.app found after build "
96+ exit 1
20297 fi
20398
20499 FAILED=0
205100 while IFS= read -r APP_PATH; do
206101 echo "========== Verifying: $APP_PATH =========="
207-
208- # Display signature details
209102 codesign -dv --verbose=4 "$APP_PATH" 2>&1 | tee /tmp/codesign-info.txt || true
210103
211- # Assert Developer ID authority
212104 if ! grep -q 'Authority=Developer ID Application' /tmp/codesign-info.txt; then
213105 echo "::error::$APP_PATH is NOT signed with Developer ID Application"
214106 FAILED=1
215107 continue
216108 fi
217109
218- # Assert correct Team Identifier
219110 if ! grep -q 'TeamIdentifier=K9X599X9Q2' /tmp/codesign-info.txt; then
220111 echo "::error::$APP_PATH has unexpected TeamIdentifier"
221112 FAILED=1
222113 continue
223114 fi
224115
225- # Strict verification (must pass)
226116 codesign --verify --deep --strict --verbose=4 "$APP_PATH"
227-
228- # Gatekeeper assessment (best-effort, does not fail the build)
229- spctl -a -vvv --type execute "$APP_PATH" || echo "::warning::spctl assessment did not pass for $APP_PATH (may be expected in CI)"
230-
231117 echo "✓ $APP_PATH passed all checks"
232118 done <<< "$APPS"
233119
@@ -236,23 +122,13 @@ jobs:
236122 exit 1
237123 fi
238124
239- - name : Print mac release mode
240- run : |
241- echo "mac_notarize_mode=${{ steps.mac_mode.outputs.mode }}"
242- echo "arm64_notarize=${{ steps.mac_mode.outputs.notarize }}"
243- echo "x64_notarize=false (always sign-only)"
244- echo "latest-mac.yml comes from arm64 build"
245-
246125 - name : Upload artifacts
247126 uses : actions/upload-artifact@v4
248127 with :
249128 name : build-macos
250129 path : |
251130 release/*.dmg
252- release/*.dmg.blockmap
253131 release/*.zip
254- release/*.zip.blockmap
255- release/latest-mac.yml
256132 retention-days : 3
257133
258134 build-linux :
@@ -286,7 +162,6 @@ jobs:
286162 release/*.AppImage
287163 release/*.deb
288164 release/*.rpm
289- release/latest-linux*.yml
290165 retention-days : 3
291166
292167 release :
@@ -381,13 +256,12 @@ jobs:
381256 CHANGELOG_INLINE_EOF
382257 fi
383258
384- # Collect all release files (handle filenames with spaces )
259+ # Collect release files: installers only (no blockmap / update metadata )
385260 FILES=()
386261 while IFS= read -r f; do
387262 FILES+=("$f")
388- done < <(find artifacts -type f \( -name "*.dmg" -o -name "*.zip" -o -name "*.exe" -o -name "*.AppImage" -o -name "*.deb" -o -name "*.rpm" -o -name "*.blockmap" -o -name "*.yml" \) | sort)
263+ done < <(find artifacts -type f \( -name "*.dmg" -o -name "*.zip" -o -name "*.exe" -o -name "*.AppImage" -o -name "*.deb" -o -name "*.rpm" \) | sort)
389264
390- # Create release and upload all assets in one shot
391265 gh release create "${GITHUB_REF_NAME}" \
392266 --title "CodePilot v${VERSION}" \
393267 --notes-file release-notes.md \
0 commit comments