From be2f27198bb400c71da3c7469625610e7b428355 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Mon, 8 Jun 2026 11:57:44 +1000 Subject: [PATCH 1/4] Use ci-toolkit sign_and_notarize for signing Replace the inline codesign + notarytool block with the shared `sign_and_notarize` command from the a8c-ci-toolkit plugin. The build (swift, lipo, strip) and cert delivery (`fastlane set_up_signing` in release-cli.sh) are unchanged; only the sign+notarize step is delegated. --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Opus 4.8 (1M context) --- scripts/sign-and-notarize-cli | 82 +++-------------------------------- 1 file changed, 7 insertions(+), 75 deletions(-) diff --git a/scripts/sign-and-notarize-cli b/scripts/sign-and-notarize-cli index 3b3085c6..e941910d 100755 --- a/scripts/sign-and-notarize-cli +++ b/scripts/sign-and-notarize-cli @@ -49,38 +49,17 @@ case "$arch" in ;; esac -key_id="${APP_STORE_CONNECT_API_KEY_KEY_ID-}" -issuer_id="${APP_STORE_CONNECT_API_KEY_ISSUER_ID-}" -key_pem="${APP_STORE_CONNECT_API_KEY_KEY-}" - -if [ -z "$key_id" ] || [ -z "$issuer_id" ] || [ -z "$key_pem" ]; then - printf >&2 "missing API key env: set APP_STORE_CONNECT_API_KEY_{KEY_ID,ISSUER_ID,KEY}\n" +# Signing and notarization are delegated to the a8c-ci-toolkit `sign_and_notarize` +# command, which the toolkit Buildkite plugin puts on PATH. The Developer ID cert +# must already be in the keychain (release-cli.sh runs `fastlane set_up_signing`). +if ! command -v sign_and_notarize >/dev/null 2>&1; then + printf >&2 "sign_and_notarize not on PATH (provided by the a8c-ci-toolkit plugin)\n" exit 1 fi repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$repo_root" -# Resolve the codesigning identity from the keychain by team id so the -# script works for any Developer ID cert, not just the hardcoded org name. -# `IDENTITY` env var bypasses the lookup if you need to force a specific cert. -identity="${IDENTITY:-}" -if [ -z "$identity" ]; then - identity="$(security find-identity -v -p codesigning | awk -v team="(${team_id})" ' - /Developer ID Application:/ && index($0, team) { - sub(/^[^"]*"/, "") - sub(/"[^"]*$/, "") - print - exit - } - ')" -fi -if [ -z "$identity" ]; then - printf >&2 "no Developer ID Application identity for team %s in keychain\n" "$team_id" - printf >&2 "(set IDENTITY=... to override)\n" - exit 1 -fi - entitlements="$repo_root/scripts/imessage-cli.entitlements" cli_swift_flags=(-Xswiftc -DIMESSAGE_DISABLE_PRIVATE_SPI_ASSETS) @@ -110,57 +89,10 @@ else fi printf "==> stripping debug symbols\n" -# strip must happen before codesign — stripping invalidates an existing +# strip must happen before signing — stripping invalidates an existing # signature, and the release asset shrinks meaningfully without it strip "$binary" -printf "==> codesigning %s\n" "$binary" -codesign --force \ - --options runtime \ - --timestamp \ - --entitlements "$entitlements" \ - --sign "$identity" \ - "$binary" - -printf "==> verifying codesign\n" -codesign --verify --strict --verbose=2 "$binary" -codesign --display --verbose=2 "$binary" 2>&1 | grep -E "Authority|TeamIdentifier|Signature|flags|Hash" || true - -work="$(mktemp -d)" -trap 'rm -rf "$work"' EXIT - -p8="$work/AuthKey_${key_id}.p8" -# the env var stores the PEM with `\n` as literal backslash-n; %b decodes -# the escapes into real newlines. Trailing \n is required — notarytool -# rejects PEM that doesn't end with a newline as `invalidPEMDocument` -printf '%b\n' "$key_pem" > "$p8" -chmod 600 "$p8" - -zip_path="$work/imessage-cli.zip" -ditto -c -k "$binary" "$zip_path" - -printf "==> submitting to notarytool (this can take a few minutes)\n" -submit_json="$work/submit.json" -xcrun notarytool submit "$zip_path" \ - --key "$p8" \ - --key-id "$key_id" \ - --issuer "$issuer_id" \ - --wait \ - --output-format json \ - > "$submit_json" - -cat "$submit_json" -printf "\n" - -status="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["status"])' "$submit_json")" -submission_id="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["id"])' "$submit_json")" - -if [ "$status" != "Accepted" ]; then - printf >&2 "==> notarization status: %s — fetching log\n" "$status" - xcrun notarytool log "$submission_id" \ - --key "$p8" --key-id "$key_id" --issuer "$issuer_id" - exit 1 -fi +sign_and_notarize --team-id "$team_id" --entitlements "$entitlements" "$binary" -printf "==> notarization accepted (id=%s)\n" "$submission_id" printf "==> signed binary at: %s\n" "$binary" From b0a120e33f4bd447fb0883ac467d3799315fb6a7 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Mon, 8 Jun 2026 11:57:44 +1000 Subject: [PATCH 2/4] TEMP: pin ci-toolkit to sign_and_notarize branch Points the toolkit plugin at `mokagio/macos-sign-and-notarize` so CI exercises the unreleased `sign_and_notarize` command. Revert to a released tag before merging. --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Opus 4.8 (1M context) --- .buildkite/shared-pipeline-vars | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.buildkite/shared-pipeline-vars b/.buildkite/shared-pipeline-vars index c2bb0116..1cf1d0b5 100755 --- a/.buildkite/shared-pipeline-vars +++ b/.buildkite/shared-pipeline-vars @@ -4,7 +4,9 @@ # rendered pipeline. Keeps plugin pins and the agent image id out of the # pipeline.yml itself. -CI_TOOLKIT_PLUGIN_VERSION='6.0.1' +# TEMPORARY: pinned to the branch adding the macOS `sign_and_notarize` command. +# Revert to a released tag (>= the version that ships it) before merging. +CI_TOOLKIT_PLUGIN_VERSION='mokagio/macos-sign-and-notarize' NVM_PLUGIN_VERSION='0.6.0' XCODE_VERSION=$(sed -E 's/^~> ?//' .xcode-version) From 00f9d6f5dc50a1efb76d09057f7362af592095de Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Tue, 9 Jun 2026 16:26:15 +1000 Subject: [PATCH 3/4] Pin CI toolkit to commit This avoids issue if/when the branch is deleted post merge --- .buildkite/shared-pipeline-vars | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.buildkite/shared-pipeline-vars b/.buildkite/shared-pipeline-vars index 1cf1d0b5..4abfa465 100755 --- a/.buildkite/shared-pipeline-vars +++ b/.buildkite/shared-pipeline-vars @@ -4,9 +4,10 @@ # rendered pipeline. Keeps plugin pins and the agent image id out of the # pipeline.yml itself. -# TEMPORARY: pinned to the branch adding the macOS `sign_and_notarize` command. -# Revert to a released tag (>= the version that ships it) before merging. -CI_TOOLKIT_PLUGIN_VERSION='mokagio/macos-sign-and-notarize' +# TEMPORARY: pinned to a commit on the `mokagio/macos-sign-and-notarize` branch +# adding the macOS `sign_and_notarize` command. Revert to a released tag +# (>= the version that ships it) before merging. +CI_TOOLKIT_PLUGIN_VERSION='6848c743aa50e332ec95809cf2c50eaf00cfcf1e' NVM_PLUGIN_VERSION='0.6.0' XCODE_VERSION=$(sed -E 's/^~> ?//' .xcode-version) From 4e2651fd48f601c203d3dc5cbd5fb96f818c2584 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Tue, 9 Jun 2026 16:27:40 +1000 Subject: [PATCH 4/4] Remove verbose AI comment --- scripts/sign-and-notarize-cli | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/sign-and-notarize-cli b/scripts/sign-and-notarize-cli index e941910d..265cd408 100755 --- a/scripts/sign-and-notarize-cli +++ b/scripts/sign-and-notarize-cli @@ -49,9 +49,7 @@ case "$arch" in ;; esac -# Signing and notarization are delegated to the a8c-ci-toolkit `sign_and_notarize` -# command, which the toolkit Buildkite plugin puts on PATH. The Developer ID cert -# must already be in the keychain (release-cli.sh runs `fastlane set_up_signing`). +# This comes from CI toolkit if ! command -v sign_and_notarize >/dev/null 2>&1; then printf >&2 "sign_and_notarize not on PATH (provided by the a8c-ci-toolkit plugin)\n" exit 1