Skip to content

Commit ad776c5

Browse files
Make notarization resilient to transient network errors
notarytool --wait hangs indefinitely on transient HTTPS errors when polling Apple's notary service (observed as NSURLErrorDomain -1009 in CI). Replace it with scripts/notarize.sh which submits without --wait, then polls notarytool info in a retry loop that tolerates network blips. Also disable tauri-action's built-in notarization (which uses --wait internally) and notarize/staple the .dmg manually after. Bump to 0.2.2. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a6d3db0 commit ad776c5

8 files changed

Lines changed: 121 additions & 29 deletions

File tree

.github/workflows/release.yml

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -93,21 +93,18 @@ jobs:
9393
--sign "$APPLE_SIGNING_IDENTITY" "$ARTIFACT"
9494
codesign --verify --verbose "$ARTIFACT"
9595
96-
# Notarize: zip, submit, wait. Bare binaries can't be stapled, so
97-
# Gatekeeper does an online check on first run instead.
98-
API_KEY_PATH="$RUNNER_TEMP/AuthKey.p8"
99-
echo "$APPLE_API_KEY_BASE64" | base64 --decode > "$API_KEY_PATH"
96+
# Notarize: zip the binary and submit to the notary service.
97+
# Bare binaries can't be stapled, so Gatekeeper does an online check
98+
# on first run instead.
99+
export APPLE_API_KEY_PATH="$RUNNER_TEMP/AuthKey.p8"
100+
echo "$APPLE_API_KEY_BASE64" | base64 --decode > "$APPLE_API_KEY_PATH"
100101
ZIP_PATH="$RUNNER_TEMP/$ARTIFACT.zip"
101102
/usr/bin/ditto -c -k --keepParent "$ARTIFACT" "$ZIP_PATH"
102-
xcrun notarytool submit "$ZIP_PATH" \
103-
--key "$API_KEY_PATH" \
104-
--key-id "$APPLE_API_KEY" \
105-
--issuer "$APPLE_API_ISSUER" \
106-
--wait
103+
./scripts/notarize.sh "$ZIP_PATH"
107104
108105
# Cleanup
109106
security delete-keychain "$KEYCHAIN_PATH"
110-
rm -f "$CERT_PATH" "$API_KEY_PATH" "$ZIP_PATH"
107+
rm -f "$CERT_PATH" "$APPLE_API_KEY_PATH" "$ZIP_PATH"
111108
112109
- name: Upload artifact
113110
uses: actions/upload-artifact@v4
@@ -153,29 +150,40 @@ jobs:
153150
sudo apt-get update
154151
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
155152
156-
- name: Write App Store Connect API key (macOS)
157-
if: runner.os == 'macOS'
158-
env:
159-
APPLE_API_KEY_BASE64: ${{ secrets.APPLE_API_KEY_BASE64 }}
160-
run: |
161-
mkdir -p ~/private_keys
162-
echo "$APPLE_API_KEY_BASE64" | base64 --decode > ~/private_keys/AuthKey.p8
163-
echo "APPLE_API_KEY_PATH=$HOME/private_keys/AuthKey.p8" >> $GITHUB_ENV
164-
165153
- name: Build Tauri app
166154
id: tauri
167155
uses: tauri-apps/tauri-action@v0
168156
env:
157+
# Signing only — notarization is done manually in the next step
158+
# because Tauri's built-in notarization uses `notarytool --wait`
159+
# which hangs on transient network errors in CI.
169160
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
170161
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
171162
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
172-
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
173-
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
174163
KEYCHAIN_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
175164
with:
176165
args: --target ${{ matrix.target }}
177166
includeUpdaterJson: false
178167

168+
- name: Notarize and staple DMG (macOS)
169+
if: runner.os == 'macOS'
170+
env:
171+
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
172+
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
173+
APPLE_API_KEY_BASE64: ${{ secrets.APPLE_API_KEY_BASE64 }}
174+
run: |
175+
set -euo pipefail
176+
export APPLE_API_KEY_PATH="$RUNNER_TEMP/AuthKey.p8"
177+
echo "$APPLE_API_KEY_BASE64" | base64 --decode > "$APPLE_API_KEY_PATH"
178+
179+
DMG=$(ls target/${{ matrix.target }}/release/bundle/dmg/*.dmg | head -n1)
180+
echo "Notarizing $DMG"
181+
./scripts/notarize.sh "$DMG"
182+
xcrun stapler staple "$DMG"
183+
xcrun stapler validate "$DMG"
184+
185+
rm -f "$APPLE_API_KEY_PATH"
186+
179187
- name: Upload desktop artifacts
180188
uses: actions/upload-artifact@v4
181189
with:

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ralph-cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ralph-cli"
3-
version = "0.2.1"
3+
version = "0.2.2"
44
edition = "2021"
55

66
[[bin]]

crates/ralph-core/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ralph-core"
3-
version = "0.2.1"
3+
version = "0.2.2"
44
edition = "2021"
55

66
[dependencies]

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "ralph-desktop",
33
"private": true,
4-
"version": "0.2.1",
4+
"version": "0.2.2",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

scripts/notarize.sh

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#!/usr/bin/env bash
2+
# Submit a file to Apple's notary service and poll until it reaches a terminal
3+
# state, tolerating transient network errors.
4+
#
5+
# `xcrun notarytool submit --wait` hangs indefinitely on transient HTTP errors
6+
# (observed as NSURLErrorDomain -1009 "Internet connection appears to be
7+
# offline" in CI). This script submits without --wait, then polls `notarytool
8+
# info` in a loop that treats network failures as retryable.
9+
#
10+
# Required env vars:
11+
# APPLE_API_KEY_PATH Path to the .p8 App Store Connect API key file
12+
# APPLE_API_KEY Key ID
13+
# APPLE_API_ISSUER Issuer ID
14+
#
15+
# Usage: notarize.sh <file-to-submit>
16+
set -euo pipefail
17+
18+
: "${APPLE_API_KEY_PATH:?}"
19+
: "${APPLE_API_KEY:?}"
20+
: "${APPLE_API_ISSUER:?}"
21+
22+
if [[ $# -ne 1 ]]; then
23+
echo "usage: $0 <file>" >&2
24+
exit 2
25+
fi
26+
FILE="$1"
27+
28+
notary() {
29+
xcrun notarytool "$@" \
30+
--key "$APPLE_API_KEY_PATH" \
31+
--key-id "$APPLE_API_KEY" \
32+
--issuer "$APPLE_API_ISSUER"
33+
}
34+
35+
echo "Submitting $FILE to notary service..."
36+
SUBMIT_JSON=$(notary submit "$FILE" --output-format json)
37+
SUBMISSION_ID=$(echo "$SUBMIT_JSON" | /usr/bin/python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])')
38+
echo "Submission ID: $SUBMISSION_ID"
39+
40+
# Poll up to ~60 minutes, tolerating transient failures
41+
DEADLINE=$(( $(date +%s) + 3600 ))
42+
CONSECUTIVE_ERRORS=0
43+
MAX_CONSECUTIVE_ERRORS=10
44+
45+
while true; do
46+
if (( $(date +%s) > DEADLINE )); then
47+
echo "Timed out waiting for notarization" >&2
48+
notary log "$SUBMISSION_ID" || true
49+
exit 1
50+
fi
51+
52+
sleep 30
53+
54+
if INFO_JSON=$(notary info "$SUBMISSION_ID" --output-format json 2>&1); then
55+
CONSECUTIVE_ERRORS=0
56+
STATUS=$(echo "$INFO_JSON" | /usr/bin/python3 -c 'import json,sys; print(json.load(sys.stdin)["status"])' 2>/dev/null || echo "Unknown")
57+
echo "Status: $STATUS"
58+
case "$STATUS" in
59+
Accepted)
60+
echo "Notarization succeeded"
61+
exit 0
62+
;;
63+
Invalid | Rejected)
64+
echo "Notarization failed: $STATUS" >&2
65+
notary log "$SUBMISSION_ID" || true
66+
exit 1
67+
;;
68+
In\ Progress | Unknown)
69+
continue
70+
;;
71+
*)
72+
echo "Unexpected status: $STATUS" >&2
73+
continue
74+
;;
75+
esac
76+
else
77+
CONSECUTIVE_ERRORS=$(( CONSECUTIVE_ERRORS + 1 ))
78+
echo "Poll failed (attempt $CONSECUTIVE_ERRORS/$MAX_CONSECUTIVE_ERRORS): $INFO_JSON" >&2
79+
if (( CONSECUTIVE_ERRORS >= MAX_CONSECUTIVE_ERRORS )); then
80+
echo "Too many consecutive polling errors, giving up" >&2
81+
exit 1
82+
fi
83+
fi
84+
done

src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ralph-desktop"
3-
version = "0.2.1"
3+
version = "0.2.2"
44
description = "Ralph - Autonomous Coding Loop GUI"
55
authors = ["you"]
66
edition = "2021"

src-tauri/tauri.conf.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://schema.tauri.app/config/2",
33
"productName": "ralph-desktop",
4-
"version": "0.2.1",
4+
"version": "0.2.2",
55
"identifier": "com.mikkeldamsgaard.ralph-desktop",
66
"build": {
77
"beforeDevCommand": "npm run dev",

0 commit comments

Comments
 (0)