Skip to content

Commit 43b30d0

Browse files
committed
Add GitHub Actions workflow for macOS notarization
Introduces a manual workflow to notarize and staple macOS ARM64 builds. The workflow downloads a signed artifact, creates a DMG, submits it for notarization, staples the result, and uploads the notarized artifacts for distribution.
1 parent 2aa700c commit 43b30d0

1 file changed

Lines changed: 130 additions & 0 deletions

File tree

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
name: Notarize macOS (Manual)
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
run_id:
7+
description: "Build workflow run ID to pull artifacts from (optional)"
8+
required: false
9+
type: string
10+
artifact_name:
11+
description: "Artifact name from build workflow"
12+
required: false
13+
default: "roopik-macos-arm64"
14+
type: string
15+
branch:
16+
description: "Branch to pull artifacts from when run_id is empty"
17+
required: false
18+
default: "main"
19+
type: string
20+
21+
jobs:
22+
notarize-macos-arm64:
23+
name: Notarize macOS (ARM64)
24+
runs-on: macos-latest
25+
timeout-minutes: 60
26+
27+
steps:
28+
- name: Download signed artifact
29+
uses: dawidd6/action-download-artifact@v3
30+
with:
31+
workflow: build-macos.yml
32+
run_id: ${{ inputs.run_id }}
33+
branch: ${{ inputs.branch }}
34+
name: ${{ inputs.artifact_name }}
35+
workflow_conclusion: success
36+
github_token: ${{ secrets.GITHUB_TOKEN }}
37+
38+
- name: Notarize and staple
39+
env:
40+
APPLE_ID: ${{ secrets.APPLE_ID }}
41+
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
42+
APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }}
43+
run: |
44+
SIGNED_ZIP="./.artifacts/Roopik-macos-signed.zip"
45+
if [ ! -f "$SIGNED_ZIP" ]; then
46+
echo "Signed zip not found at $SIGNED_ZIP"
47+
exit 1
48+
fi
49+
50+
rm -rf .notarize
51+
mkdir -p .notarize
52+
ditto -x -k "$SIGNED_ZIP" .notarize
53+
54+
APP_PATH="$(find .notarize -maxdepth 3 -name "*.app" -print -quit)"
55+
if [ -z "$APP_PATH" ]; then
56+
echo "No .app found in notarize payload"
57+
exit 1
58+
fi
59+
60+
DMG_ROOT=".dmgroot"
61+
DMG_NAME="Roopik-macos.dmg"
62+
rm -rf "$DMG_ROOT"
63+
mkdir -p "$DMG_ROOT"
64+
cp -R "$APP_PATH" "$DMG_ROOT/"
65+
ln -s /Applications "$DMG_ROOT/Applications"
66+
67+
rm -f "$DMG_NAME"
68+
hdiutil detach "/Volumes/Roopik" -force || true
69+
hdiutil create -volname "Roopik" -srcfolder "$DMG_ROOT" -ov -format UDZO "$DMG_NAME"
70+
71+
SUBMIT_JSON="$(xcrun notarytool submit "$DMG_NAME" \
72+
--apple-id "$APPLE_ID" \
73+
--team-id "$APPLE_TEAM_ID" \
74+
--password "$APPLE_APP_PASSWORD" \
75+
--output-format json)"
76+
77+
echo "$SUBMIT_JSON"
78+
79+
REQUEST_ID="$(echo "$SUBMIT_JSON" | /usr/bin/python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")"
80+
echo "Notarization request id: $REQUEST_ID"
81+
82+
MAX_ATTEMPTS=60
83+
SLEEP_SECONDS=60
84+
ATTEMPT=1
85+
86+
while [ "$ATTEMPT" -le "$MAX_ATTEMPTS" ]; do
87+
LOG_JSON="$(xcrun notarytool log "$REQUEST_ID" \
88+
--apple-id "$APPLE_ID" \
89+
--team-id "$APPLE_TEAM_ID" \
90+
--password "$APPLE_APP_PASSWORD" \
91+
--output-format json || true)"
92+
93+
STATUS="$(echo "$LOG_JSON" | /usr/bin/python3 -c "import sys,json; print(json.load(sys.stdin).get('status', 'Unknown'))")"
94+
echo "Notarization status ($ATTEMPT/$MAX_ATTEMPTS): $STATUS"
95+
96+
if [ "$STATUS" = "Accepted" ]; then
97+
break
98+
fi
99+
100+
if [ "$STATUS" = "Invalid" ]; then
101+
echo "$LOG_JSON"
102+
exit 1
103+
fi
104+
105+
sleep "$SLEEP_SECONDS"
106+
ATTEMPT=$((ATTEMPT + 1))
107+
done
108+
109+
if [ "$STATUS" != "Accepted" ]; then
110+
echo "Notarization did not finish in time. Request id: $REQUEST_ID"
111+
exit 1
112+
fi
113+
114+
xcrun stapler staple "$APP_PATH"
115+
xcrun stapler staple "$DMG_NAME"
116+
117+
NOTARIZED_ZIP="./Roopik-macos-notarized.zip"
118+
ditto -c -k --sequesterRsrc --keepParent "$APP_PATH" "$NOTARIZED_ZIP"
119+
120+
NOTARIZED_DMG="./Roopik-macos-notarized.dmg"
121+
mv "$DMG_NAME" "$NOTARIZED_DMG"
122+
123+
- name: Upload notarized artifact (arm64)
124+
uses: actions/upload-artifact@v4
125+
with:
126+
name: roopik-macos-arm64-notarized
127+
path: |
128+
Roopik-macos-notarized.zip
129+
Roopik-macos-notarized.dmg
130+
retention-days: 30

0 commit comments

Comments
 (0)