diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 7223786d79..39b6394407 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -323,10 +323,31 @@ jobs: done < <(find "$IMG" -type f) codesign --verify --strict --verbose=2 "$IMG/runtime/bin/java" # Notarize: zip the signed image, submit, wait for Apple's verdict. - ditto -c -k --keepParent "$IMG" "$RUNNER_TEMP/amy-notarize.zip" - xcrun notarytool submit "$RUNNER_TEMP/amy-notarize.zip" \ - --apple-id "$NOTARY_APPLE_ID" --password "$NOTARY_PASSWORD" \ - --team-id "$NOTARY_TEAM_ID" --wait + # The notary service recursively inspects the lib/*.jar files, so any + # unsigned Mach-O embedded in them (secp256k1/jna/sqlite/skiko natives) + # can come back Invalid. Surface the per-file log so the first real run + # is diagnostic rather than a bare failure. + ZIP="$RUNNER_TEMP/amy-notarize.zip" + OUT="$RUNNER_TEMP/notary-submit.json" + ditto -c -k --keepParent "$IMG" "$ZIP" + if ! xcrun notarytool submit "$ZIP" \ + --apple-id "$NOTARY_APPLE_ID" --password "$NOTARY_PASSWORD" \ + --team-id "$NOTARY_TEAM_ID" --wait --output-format json > "$OUT"; then + echo "::warning::notarytool submit exited non-zero" + fi + cat "$OUT" + STATUS="$(jq -r '.status // "Unknown"' "$OUT" 2>/dev/null || echo Unknown)" + SUBMISSION_ID="$(jq -r '.id // empty' "$OUT" 2>/dev/null || true)" + if [ "$STATUS" != "Accepted" ]; then + echo "::error::Notarization status: $STATUS" + if [ -n "$SUBMISSION_ID" ]; then + echo "----- notary log -----" + xcrun notarytool log "$SUBMISSION_ID" \ + --apple-id "$NOTARY_APPLE_ID" --password "$NOTARY_PASSWORD" \ + --team-id "$NOTARY_TEAM_ID" || true + fi + exit 1 + fi # jpackage pins libicu to the build host's version (libicu74 on # ubuntu-24.04). Rewrite the .deb so it installs across Debian/Ubuntu. diff --git a/BUILDING.md b/BUILDING.md index aefce7849b..1fcd052bca 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -281,6 +281,28 @@ Gatekeeper verifies notarization **online** on first run — fine for a CLI. Note the Homebrew-core jvm bundle (`amy--jvm.tar.gz`) is **not** signed: Homebrew removes the quarantine attribute on its own downloads. +> **Validated (Developer ID `D77MCV9NZ7`):** signing every Mach-O in the bundled +> JRE with hardened runtime + `amy.entitlements` lets `amy init` derive a key via +> secp256k1 with no library-validation crash. Dropping `disable-library-validation` +> reproduces `UnsatisfiedLinkError: … different Team IDs` on the runtime-extracted +> `libsecp256k1-jni.dylib` — so that entitlement is load-bearing, not decorative. +> +> **Open risk — embedded jar natives.** The notary service unpacks `lib/*.jar` +> recursively and checks every Mach-O for a signature + hardened runtime. Our +> sign loop only touches loose files, so 9 unsigned natives ride along inside +> jars on a macOS build: `secp256k1` (1, required at runtime), `jna` (2), +> `sqlite` (2), and `skiko` (4, dead weight — Compose UI the CLI never renders). +> Whether `notarytool` returns `Accepted` or `Invalid` on these is **unverified** +> (the local validation had no notary creds). **Decide it with one run:** set the +> six `MAC_*` secrets and trigger `create-release.yml` via `workflow_dispatch` +> with `dry_run=true` — the sign+notarize step runs regardless of `dry_run` and +> now prints the per-file notary log on a non-`Accepted` verdict. If it comes +> back `Invalid`, the fix is to codesign the dylibs *inside* those jars before +> zipping (and/or strip the unused `skiko`/Compose jars from the CLI image — the +> `:commons` core/ui split the size budget already flags). The **desktop** app +> bundles the same jars through Compose/jpackage notarization, so run a desktop +> dry-run too; its in-jar handling differs and is likewise unverified. + Generating the values: ```bash