mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-07-17 11:07:11 +08:00
fix: optimize notarize script
This commit is contained in:
13
.github/workflows/release.yml
vendored
13
.github/workflows/release.yml
vendored
@@ -152,10 +152,9 @@ jobs:
|
|||||||
echo "::notice::MAC_CSC_LINK not set — skipping notarization (unsigned build)."
|
echo "::notice::MAC_CSC_LINK not set — skipping notarization (unsigned build)."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Notarize + staple the dmg in a separate, retryable step. electron-builder's
|
# Notarize + staple the dmg: submit ONCE, then poll that submission id
|
||||||
# inline notarize aborts the whole build on a single notarytool poll timeout
|
# until Accepted/Invalid (see notarize-dmg.sh — never resubmits).
|
||||||
# (NSURLErrorDomain -1001); this step retries so a flaky poll doesn't discard
|
# Skipped for Windows and for unsigned builds (no cert).
|
||||||
# the signed build. Skipped for Windows and for unsigned builds (no cert).
|
|
||||||
- name: Notarize macOS dmg
|
- name: Notarize macOS dmg
|
||||||
if: matrix.platform == 'mac' && steps.macsign.outputs.enabled == 'true'
|
if: matrix.platform == 'mac' && steps.macsign.outputs.enabled == 'true'
|
||||||
working-directory: desktop
|
working-directory: desktop
|
||||||
@@ -175,7 +174,13 @@ jobs:
|
|||||||
bash build/notarize-dmg.sh "$dmg"
|
bash build/notarize-dmg.sh "$dmg"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Always upload artifacts, even when notarization timed out. The
|
||||||
|
# notarization ticket is keyed to the dmg's hash on Apple's servers: if
|
||||||
|
# the submission is eventually Accepted, THIS exact dmg becomes valid
|
||||||
|
# as-is (download it, `xcrun stapler staple`, publish manually) — no
|
||||||
|
# rebuild needed. Rebuilding would change the hash and waste the ticket.
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
|
if: always()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
# One bundle per platform/arch so the publish job can collect them all.
|
# One bundle per platform/arch so the publish job can collect them all.
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
#
|
#
|
||||||
# Notarize + staple a macOS dmg with retry, so a transient notarytool
|
# Notarize + staple a macOS dmg.
|
||||||
# status-polling timeout (NSURLErrorDomain -1001) doesn't throw away an
|
|
||||||
# otherwise-good signed build.
|
|
||||||
#
|
#
|
||||||
# electron-builder's inline notarize aborts the whole build on the first poll
|
# Submits ONCE and then polls that same submission id until it reaches a
|
||||||
# timeout. Here we retry the submit-and-wait, and before each retry we check
|
# terminal state. Never resubmits: a previous version resubmitted on every
|
||||||
# whether a previous submission already reached a terminal state (Accepted),
|
# wait timeout, which piled up duplicate "In Progress" submissions in Apple's
|
||||||
# so a flaky poll on an already-successful submission still passes.
|
# queue and made everything slower.
|
||||||
|
#
|
||||||
|
# Notarization is fully automated on Apple's side (malware scan, no humans);
|
||||||
|
# most submissions finish in minutes, but large bundles or Apple-side backlog
|
||||||
|
# can take much longer, so we poll patiently instead of failing fast.
|
||||||
#
|
#
|
||||||
# Usage: notarize-dmg.sh <path-to-dmg>
|
# Usage: notarize-dmg.sh <path-to-dmg>
|
||||||
# Requires env: APPLE_ID, APPLE_APP_SPECIFIC_PASSWORD, APPLE_TEAM_ID
|
# Requires env: APPLE_ID, APPLE_APP_SPECIFIC_PASSWORD, APPLE_TEAM_ID
|
||||||
|
# Tunables: NOTARIZE_MAX_WAIT_MINUTES (default 180), NOTARIZE_POLL_SECONDS (default 60)
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
DMG="${1:?usage: notarize-dmg.sh <dmg>}"
|
DMG="${1:?usage: notarize-dmg.sh <dmg>}"
|
||||||
@@ -19,7 +22,8 @@ DMG="${1:?usage: notarize-dmg.sh <dmg>}"
|
|||||||
: "${APPLE_APP_SPECIFIC_PASSWORD:?APPLE_APP_SPECIFIC_PASSWORD not set}"
|
: "${APPLE_APP_SPECIFIC_PASSWORD:?APPLE_APP_SPECIFIC_PASSWORD not set}"
|
||||||
: "${APPLE_TEAM_ID:?APPLE_TEAM_ID not set}"
|
: "${APPLE_TEAM_ID:?APPLE_TEAM_ID not set}"
|
||||||
|
|
||||||
MAX_ATTEMPTS="${NOTARIZE_MAX_ATTEMPTS:-5}"
|
MAX_WAIT_MINUTES="${NOTARIZE_MAX_WAIT_MINUTES:-180}"
|
||||||
|
POLL_SECONDS="${NOTARIZE_POLL_SECONDS:-60}"
|
||||||
|
|
||||||
auth=(
|
auth=(
|
||||||
--apple-id "$APPLE_ID"
|
--apple-id "$APPLE_ID"
|
||||||
@@ -27,57 +31,49 @@ auth=(
|
|||||||
--team-id "$APPLE_TEAM_ID"
|
--team-id "$APPLE_TEAM_ID"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check whether the most recent submission for this app is already Accepted.
|
json_field() {
|
||||||
# Used to short-circuit retries when the failure was only a status-poll timeout
|
# json_field <key> — read a top-level string field from JSON on stdin.
|
||||||
# on a submission that Apple actually accepted.
|
python3 -c "import json,sys; print(json.load(sys.stdin).get('$1',''))" 2>/dev/null
|
||||||
latest_status_is_accepted() {
|
|
||||||
local out
|
|
||||||
out="$(xcrun notarytool history "${auth[@]}" --output-format json 2>/dev/null || true)"
|
|
||||||
# Grab the status of the first (most recent) history entry.
|
|
||||||
echo "$out" | python3 -c '
|
|
||||||
import json, sys
|
|
||||||
try:
|
|
||||||
data = json.load(sys.stdin)
|
|
||||||
hist = data.get("history", [])
|
|
||||||
if hist and hist[0].get("status") == "Accepted":
|
|
||||||
sys.exit(0)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
sys.exit(1)
|
|
||||||
' 2>/dev/null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "==> Notarizing: $DMG"
|
echo "==> Submitting for notarization: $DMG"
|
||||||
|
submit_out="$(xcrun notarytool submit "$DMG" "${auth[@]}" --no-wait --output-format json)"
|
||||||
|
submission_id="$(echo "$submit_out" | json_field id)"
|
||||||
|
|
||||||
attempt=1
|
if [ -z "$submission_id" ]; then
|
||||||
while :; do
|
echo "::error::could not parse submission id from notarytool output"
|
||||||
echo "==> notarytool submit (attempt ${attempt}/${MAX_ATTEMPTS})"
|
echo "$submit_out"
|
||||||
if xcrun notarytool submit "$DMG" "${auth[@]}" --wait --timeout 30m; then
|
|
||||||
echo "==> Notarization Accepted"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "::warning::notarytool submit failed on attempt ${attempt}"
|
|
||||||
|
|
||||||
# The submit may have been accepted but the status poll timed out; verify.
|
|
||||||
if latest_status_is_accepted; then
|
|
||||||
echo "==> Latest submission already Accepted (poll timed out); continuing"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$attempt" -ge "$MAX_ATTEMPTS" ]; then
|
|
||||||
echo "::error::Notarization failed after ${MAX_ATTEMPTS} attempts"
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
echo "==> Submission id: $submission_id (uploaded OK, polling status...)"
|
||||||
|
|
||||||
sleep_s=$(( attempt * 30 ))
|
deadline=$(( $(date +%s) + MAX_WAIT_MINUTES * 60 ))
|
||||||
echo "==> Retrying in ${sleep_s}s..."
|
while :; do
|
||||||
sleep "$sleep_s"
|
# info can fail transiently (network); treat as unknown and keep polling.
|
||||||
attempt=$(( attempt + 1 ))
|
status="$(xcrun notarytool info "$submission_id" "${auth[@]}" --output-format json 2>/dev/null | json_field status || true)"
|
||||||
|
echo "==> [$(date '+%H:%M:%S')] status: ${status:-<query failed, will retry>}"
|
||||||
|
|
||||||
|
case "$status" in
|
||||||
|
Accepted)
|
||||||
|
echo "==> Notarization Accepted"
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
Invalid|Rejected)
|
||||||
|
echo "::error::Notarization ${status}; fetching log for diagnostics"
|
||||||
|
xcrun notarytool log "$submission_id" "${auth[@]}" || true
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ "$(date +%s)" -ge "$deadline" ]; then
|
||||||
|
echo "::error::Notarization still not finished after ${MAX_WAIT_MINUTES} minutes (id: $submission_id)."
|
||||||
|
echo "::error::NOT resubmitting. Check later with: xcrun notarytool info $submission_id"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sleep "$POLL_SECONDS"
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "==> Stapling ticket into dmg"
|
echo "==> Stapling ticket into dmg"
|
||||||
# Staple has its own transient failures; retry a few times.
|
|
||||||
staple_attempt=1
|
staple_attempt=1
|
||||||
while :; do
|
while :; do
|
||||||
if xcrun stapler staple "$DMG"; then
|
if xcrun stapler staple "$DMG"; then
|
||||||
|
|||||||
Reference in New Issue
Block a user