Skip to content

Finalize Notarization #634

Finalize Notarization

Finalize Notarization #634

name: Finalize Notarization
on:
schedule:
- cron: "*/30 * * * *"
workflow_dispatch:
permissions:
actions: write
contents: write
concurrency:
group: finalize-notarization
cancel-in-progress: false
jobs:
poll:
runs-on: macos-15
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
COMUX_NOTARY_APPLE_ID: ${{ secrets.APPLE_NOTARY_APPLE_ID }}
COMUX_NOTARY_TEAM_ID: ${{ secrets.APPLE_NOTARY_TEAM_ID }}
COMUX_NOTARY_PASSWORD: ${{ secrets.APPLE_NOTARY_PASSWORD }}
steps:
- name: Poll pending notarizations
shell: bash
run: |
set -euo pipefail
artifacts_path="$RUNNER_TEMP/comux-notarization-artifacts.tsv"
artifacts_loaded=0
for attempt in 1 2 3; do
if gh api --paginate \
"/repos/${GITHUB_REPOSITORY}/actions/artifacts?per_page=100" \
--jq '.artifacts[] | select(.expired == false and (.name | startswith("comux-notarization-"))) | [.id, .name, .workflow_run.id] | @tsv' \
> "$artifacts_path"; then
artifacts_loaded=1
break
fi
echo "::warning::Could not list notarization artifacts (attempt $attempt of 3)."
if [[ "$attempt" -lt 3 ]]; then
sleep "$((attempt * 4))"
fi
done
if [[ "$artifacts_loaded" -ne 1 ]]; then
echo "::warning::GitHub is unavailable; skipping this notarization poll."
exit 0
fi
if [[ ! -s "$artifacts_path" ]]; then
echo "No pending Comux notarization artifacts found."
exit 0
fi
failures=0
dispatched=0
has_asset() {
local asset_name="$1"
local assets_path="$2"
grep -Fx "$asset_name" "$assets_path" >/dev/null
}
while IFS=$'\t' read -r artifact_id artifact_name artifact_run_id; do
if [[ -z "$artifact_id" || -z "$artifact_name" || -z "$artifact_run_id" ]]; then
continue
fi
artifact_dir="$RUNNER_TEMP/${artifact_name}-${artifact_run_id}"
rm -rf "$artifact_dir"
mkdir -p "$artifact_dir"
echo "Inspecting $artifact_name from run $artifact_run_id."
if ! gh run download "$artifact_run_id" \
--repo "$GITHUB_REPOSITORY" \
--name "$artifact_name" \
--dir "$artifact_dir"; then
echo "::warning::Could not download $artifact_name from run $artifact_run_id."
failures=1
continue
fi
state_path="$artifact_dir/comux-notarization.txt"
if [[ ! -f "$state_path" ]]; then
echo "::warning::Missing notarization state file in $artifact_name."
failures=1
continue
fi
version=""
release_repository="$GITHUB_REPOSITORY"
submission_id=""
source_run_id="$artifact_run_id"
while IFS='=' read -r key value; do
case "$key" in
VERSION)
version="$value"
;;
REPOSITORY)
release_repository="$value"
;;
SUBMISSION_ID)
submission_id="$value"
;;
RUN_ID)
source_run_id="$value"
;;
esac
done < "$state_path"
if [[ -z "$version" || -z "$submission_id" || -z "$source_run_id" ]]; then
echo "::warning::Incomplete notarization state in $artifact_name."
failures=1
continue
fi
normalized_version="${version#v}"
tag="v${normalized_version}"
archive_name="comux-${normalized_version}.zip"
cask_name="comux.rb"
release_assets_path="$RUNNER_TEMP/${artifact_name}-${artifact_run_id}-release-assets.txt"
if ! gh release view "$tag" \
--repo "$release_repository" \
--json assets \
--jq '.assets[].name' > "$release_assets_path"; then
echo "::warning::Release $tag does not exist in $release_repository. Create the draft release before finalization."
failures=1
continue
fi
if has_asset "$archive_name" "$release_assets_path" && has_asset "$cask_name" "$release_assets_path"; then
echo "Release $tag is complete; retiring $artifact_name."
if ! gh api \
--method DELETE \
"/repos/${GITHUB_REPOSITORY}/actions/artifacts/${artifact_id}"; then
echo "::warning::Could not retire completed notarization artifact $artifact_name."
fi
continue
fi
if [[ -z "${COMUX_NOTARY_APPLE_ID:-}" || -z "${COMUX_NOTARY_TEAM_ID:-}" || -z "${COMUX_NOTARY_PASSWORD:-}" ]]; then
echo "::error::Apple notarization credentials are required to poll pending submission $submission_id for $tag."
failures=1
continue
fi
info_path="$RUNNER_TEMP/${artifact_name}-${artifact_run_id}-notary-info.json"
if ! xcrun notarytool info "$submission_id" \
--apple-id "$COMUX_NOTARY_APPLE_ID" \
--team-id "$COMUX_NOTARY_TEAM_ID" \
--password "$COMUX_NOTARY_PASSWORD" \
--no-progress \
--output-format json > "$info_path"; then
echo "::warning::Could not fetch Apple notarization status for $submission_id."
failures=1
continue
fi
status="$(plutil -extract status raw -o - "$info_path")"
case "$status" in
Accepted)
echo "Notarization accepted for $tag; dispatching release finalization."
gh workflow run release.yml \
--repo "$GITHUB_REPOSITORY" \
--ref "$DEFAULT_BRANCH" \
-f "version=$normalized_version" \
-f "repository=$release_repository" \
-f "notarization_submission_id=$submission_id" \
-f "notarization_run_id=$source_run_id"
dispatched=$((dispatched + 1))
;;
Invalid|Rejected)
log_path="$RUNNER_TEMP/${artifact_name}-${artifact_run_id}-notary-log.json"
xcrun notarytool log "$submission_id" "$log_path" \
--apple-id "$COMUX_NOTARY_APPLE_ID" \
--team-id "$COMUX_NOTARY_TEAM_ID" \
--password "$COMUX_NOTARY_PASSWORD" || true
echo "::error::Notarization $submission_id for $tag finished with status $status. Log: $log_path"
failures=1
;;
*)
echo "Notarization $submission_id for $tag is $status; will check again later."
;;
esac
done < "$artifacts_path"
echo "Dispatched $dispatched release finalization run(s)."
if [[ "$failures" -ne 0 ]]; then
exit 1
fi