From e82237840a772db06f2a347df2e4c0cb061bea31 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Fri, 19 Jun 2026 12:32:46 -0400 Subject: [PATCH] fix(ci): point Compose macOS signing at the CI keychain explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The desktop DMG release leg (build-desktop macos, packageReleaseDmg) has never produced a signed artifact: createReleaseDistributable fails with "Could not find certificate for '***' in keychain []". This is independent of the v1.12.3 notarization fix, which addressed the separate amy CLI leg. Root cause: Compose's MacSignerImpl maps the signing identity to a certificate by running `security find-certificate -a -c ` with no keychain argument. On the GitHub macOS runners that lookup does not resolve the cert that import-macos-cert imported into a throwaway keychain and added only to the user search list — even though bare `codesign --sign` (e.g. the signMacJarNatives task, which succeeds in the same job) finds it fine. The "keychain []" in the error is just the null settings.keychain being echoed. Fix: export the throwaway keychain path from the import-macos-cert action and feed it to Compose's `signing.keychain` via AMETHYST_MAC_SIGN_KEYCHAIN, so the certificate lookup searches that keychain directly. Also set it as the default keychain for good measure. No-op on local/PR builds (env unset -> Compose keeps its previous default-search-list behavior). Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/actions/import-macos-cert/action.yml | 14 ++++++++++++++ .github/workflows/create-release.yml | 5 +++++ desktopApp/build.gradle.kts | 10 ++++++++++ 3 files changed, 29 insertions(+) diff --git a/.github/actions/import-macos-cert/action.yml b/.github/actions/import-macos-cert/action.yml index e9ecb02761..c2808293d2 100644 --- a/.github/actions/import-macos-cert/action.yml +++ b/.github/actions/import-macos-cert/action.yml @@ -17,6 +17,13 @@ outputs: signing: description: "'true' if a certificate was imported, else 'false'" value: ${{ steps.import.outputs.signing }} + keychain: + description: > + Absolute path of the throwaway keychain holding the imported cert (empty + when signing=false). Pass to tools that need an explicit keychain — e.g. + Compose's macOS signing, whose certificate lookup doesn't resolve the + search list reliably on CI runners the way bare `codesign` does. + value: ${{ steps.import.outputs.keychain }} runs: using: composite @@ -48,5 +55,12 @@ runs: # Prepend our keychain to the user search list so codesign sees it. security list-keychains -d user -s "$KEYCHAIN" \ $(security list-keychains -d user | sed -e 's/[\"[:space:]]//g') + # Also make it the default keychain. Bare `codesign` resolves identities + # via the search list, but some tools (Compose's MacSigner runs + # `security find-certificate` to map the identity to a cert) don't find + # it on the search list alone on these runners. Callers that need an + # explicit keychain can read the `keychain` output below. + security default-keychain -d user -s "$KEYCHAIN" rm -f "$CERT_PATH" echo "signing=true" >> "$GITHUB_OUTPUT" + echo "keychain=$KEYCHAIN" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 5bcae99532..cc99337105 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -121,6 +121,11 @@ jobs: # configured — the gradle macOS{} block skips signing when the # identity is blank. AMETHYST_MAC_SIGN_IDENTITY: ${{ steps.mac_keychain.outputs.signing == 'true' && secrets.MAC_SIGN_IDENTITY || '' }} + # Explicit keychain for Compose's MacSigner. Its `security + # find-certificate` lookup doesn't resolve the imported cert via the + # search list on these runners ("Could not find certificate ... in + # keychain []"), so point it at the throwaway keychain directly. + AMETHYST_MAC_SIGN_KEYCHAIN: ${{ steps.mac_keychain.outputs.signing == 'true' && steps.mac_keychain.outputs.keychain || '' }} AMETHYST_NOTARY_APPLE_ID: ${{ secrets.MAC_NOTARY_APPLE_ID }} AMETHYST_NOTARY_PASSWORD: ${{ secrets.MAC_NOTARY_PASSWORD }} AMETHYST_NOTARY_TEAM_ID: ${{ secrets.MAC_NOTARY_TEAM_ID }} diff --git a/desktopApp/build.gradle.kts b/desktopApp/build.gradle.kts index 95ca8ab8df..b10a599656 100644 --- a/desktopApp/build.gradle.kts +++ b/desktopApp/build.gradle.kts @@ -159,6 +159,16 @@ compose.desktop { signing { sign.set(true) identity.set(macSignIdentity) + // Compose's MacSigner maps the identity to a certificate via + // `security find-certificate`, which doesn't reliably resolve + // the CI-imported cert through the keychain search list (fails + // with "Could not find certificate ... in keychain []"). When + // the workflow exports the throwaway keychain path, point the + // lookup straight at it. Empty/unset on local builds — Compose + // then falls back to the default search list as before. + System.getenv("AMETHYST_MAC_SIGN_KEYCHAIN") + ?.takeIf { it.isNotBlank() } + ?.let { keychain.set(it) } } notarization { appleID.set(System.getenv("AMETHYST_NOTARY_APPLE_ID"))