Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 84 additions & 42 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -714,18 +714,28 @@ jobs:
[ -n "${!v:-}" ] || { echo "missing $v secret (mac signing provisioning)"; exit 1; }
done
# Ephemeral keychain, alive for exactly this step (trap cleans
# up + restores the search list). The login keychain is
# deliberately left OFF the search list for the job: any copy
# of the identity it happens to hold must never satisfy (or
# shadow) the lookup — that is precisely the host-state
# dependency this provisioning replaces.
# up + removes it from the search list). The keychain is
# PREPENDED to the list, never swapped in as the whole list:
# concurrent darwin jobs share this runner user's keychain
# domain, and a replacing write (or a trap restoring login)
# takes the list back from under a sibling build that is still
# signing. Ours leads, so the identity found is THIS copy,
# imported from this step's p12 - not some host-state copy the
# login keychain happens to hold.
CI_TMP="$(mktemp -d)"
KC="$CI_TMP/ci-build.keychain-db"
KCPW="$(/usr/bin/openssl rand -hex 16)"
trap 'rm -rf "$CI_TMP"; security delete-keychain "$KC" 2>/dev/null || true; security list-keychains -d user -s "$HOME/Library/Keychains/login.keychain-db"' EXIT
trap 'rm -rf "$CI_TMP"; security delete-keychain "$KC" 2>/dev/null || true; REST="$(security list-keychains -d user | tr -d "\"" | grep -v "$KC" || true)"; [ -n "$REST" ] && security list-keychains -d user -s $REST || true' EXIT
security create-keychain -p "$KCPW" "$KC"
security list-keychains -d user -s "$KC"
security list-keychains -d user -s "$KC" $(security list-keychains -d user | tr -d '"')
security unlock-keychain -p "$KCPW" "$KC"
# A fresh keychain defaults to locking after five idle minutes.
# A slow cargo build reaches its codesign calls well past that
# and codesign then waits forever for an unlock no headless
# runner can answer (run 40328004030: both darwin-x64 shards
# hung ~8 minutes in, the arm64 ones signed inside the window).
# Clear the timers before anything can go idle.
security set-keychain-settings "$KC"
printf '%s' "$MACOS_CERT_P12_BASE64" | base64 -d > "$CI_TMP/cert.p12"
security import "$CI_TMP/cert.p12" -k "$KC" -P "$MACOS_CERT_PASSWORD" -T /usr/bin/codesign
rm -f "$CI_TMP/cert.p12"
Expand Down Expand Up @@ -898,30 +908,41 @@ jobs:
# time), and a release helper must carry the Developer ID, not an
# ad-hoc signature, because the permission grant follows the
# cdhash. Build a second keychain for exactly that one call and
# make it the SOLE entry of the user's search list, the same way
# the CLI shards provision theirs above: codesign resolves a
# named identity through the search list, not the --keychain
# argument (probe run 40325026239 on this image: the same
# keychain failed "item could not be found" while unlisted and
# signed cleanly once listed). Login stays off the list exactly
# as on the CLI path. When Tauri's bundle-time keychain takes the
# list over later, both hold the SAME identity from the SAME p12,
# so the tauri#11992 trap - DIFFERENT identities in one view -
# cannot bite. The trap restores the baseline at step end.
# PREPEND it to the user's search list, the same way the CLI
# shards provision theirs above: codesign resolves a named
# identity through the search list, not the --keychain argument
# (probe run 40325026239 on this image: the same keychain failed
# "item could not be found" while unlisted and signed cleanly
# once listed). Prepend, never own: concurrent shards share this
# runner user's keychain domain, and a write replacing the whole
# list (or a trap restoring login) takes it back from under a
# build still running - ours leads, so codesign finds THIS copy.
# When Tauri's bundle-time keychain takes the list over later,
# both hold the SAME identity from the SAME p12, so the
# tauri#11992 trap - DIFFERENT identities in one view - cannot
# bite. The trap removes only this keychain from the list at
# step end.
KC="$CI_TMP/helper-sign.keychain-db"
KCPW="$(/usr/bin/openssl rand -hex 16)"
security create-keychain -p "$KCPW" "$KC"
security list-keychains -d user -s "$KC"
security list-keychains -d user -s "$KC" $(security list-keychains -d user | tr -d '"')
security unlock-keychain -p "$KCPW" "$KC"
# A fresh keychain defaults to locking after five idle minutes.
# A slow cargo build reaches its codesign calls well past that
# and codesign then waits forever for an unlock no headless
# runner can answer (run 40328004030: both darwin-x64 shards
# hung ~8 minutes in, the arm64 ones signed inside the window).
# Clear the timers before anything can go idle.
security set-keychain-settings "$KC"
printf '%s' "$MACOS_CERT_P12_BASE64" | base64 -d > "$CI_TMP/cert.p12"
security import "$CI_TMP/cert.p12" -k "$KC" -P "$MACOS_CERT_PASSWORD" -T /usr/bin/codesign
rm -f "$CI_TMP/cert.p12"
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KCPW" "$KC" >/dev/null 2>&1
security find-identity -v -p codesigning "$KC" | grep -q "Developer ID Application" \
|| { echo "imported p12 yielded no valid Developer ID Application identity"; exit 1; }
# The keychain is the search list for this step (listed above);
# the trap restores the baseline when the step ends.
trap 'rm -rf "$CI_TMP"; security delete-keychain "$KC" 2>/dev/null || true; security list-keychains -d user -s "$HOME/Library/Keychains/login.keychain-db"' EXIT
# The keychain leads the search list for this step (prepended
# above); the trap removes only it when the step ends.
trap 'rm -rf "$CI_TMP"; security delete-keychain "$KC" 2>/dev/null || true; REST="$(security list-keychains -d user | tr -d "\"" | grep -v "$KC" || true)"; [ -n "$REST" ] && security list-keychains -d user -s $REST || true' EXIT
export APPLE_SIGNING_KEYCHAIN="$KC"
;;
esac
Expand Down Expand Up @@ -1378,18 +1399,28 @@ jobs:
[ -n "${!v:-}" ] || { echo "missing $v secret (mac signing provisioning)"; exit 1; }
done
# Ephemeral keychain, alive for exactly this step (trap cleans
# up + restores the search list). The login keychain is
# deliberately left OFF the search list for the job: any copy
# of the identity it happens to hold must never satisfy (or
# shadow) the lookup — that is precisely the host-state
# dependency this provisioning replaces.
# up + removes it from the search list). The keychain is
# PREPENDED to the list, never swapped in as the whole list:
# concurrent darwin jobs share this runner user's keychain
# domain, and a replacing write (or a trap restoring login)
# takes the list back from under a sibling build that is still
# signing. Ours leads, so the identity found is THIS copy,
# imported from this step's p12 - not some host-state copy the
# login keychain happens to hold.
CI_TMP="$(mktemp -d)"
KC="$CI_TMP/ci-build.keychain-db"
KCPW="$(/usr/bin/openssl rand -hex 16)"
trap 'rm -rf "$CI_TMP"; security delete-keychain "$KC" 2>/dev/null || true; security list-keychains -d user -s "$HOME/Library/Keychains/login.keychain-db"' EXIT
trap 'rm -rf "$CI_TMP"; security delete-keychain "$KC" 2>/dev/null || true; REST="$(security list-keychains -d user | tr -d "\"" | grep -v "$KC" || true)"; [ -n "$REST" ] && security list-keychains -d user -s $REST || true' EXIT
security create-keychain -p "$KCPW" "$KC"
security list-keychains -d user -s "$KC"
security list-keychains -d user -s "$KC" $(security list-keychains -d user | tr -d '"')
security unlock-keychain -p "$KCPW" "$KC"
# A fresh keychain defaults to locking after five idle minutes.
# A slow cargo build reaches its codesign calls well past that
# and codesign then waits forever for an unlock no headless
# runner can answer (run 40328004030: both darwin-x64 shards
# hung ~8 minutes in, the arm64 ones signed inside the window).
# Clear the timers before anything can go idle.
security set-keychain-settings "$KC"
printf '%s' "$MACOS_CERT_P12_BASE64" | base64 -d > "$CI_TMP/cert.p12"
security import "$CI_TMP/cert.p12" -k "$KC" -P "$MACOS_CERT_PASSWORD" -T /usr/bin/codesign
rm -f "$CI_TMP/cert.p12"
Expand Down Expand Up @@ -1562,30 +1593,41 @@ jobs:
# time), and a release helper must carry the Developer ID, not an
# ad-hoc signature, because the permission grant follows the
# cdhash. Build a second keychain for exactly that one call and
# make it the SOLE entry of the user's search list, the same way
# the CLI shards provision theirs above: codesign resolves a
# named identity through the search list, not the --keychain
# argument (probe run 40325026239 on this image: the same
# keychain failed "item could not be found" while unlisted and
# signed cleanly once listed). Login stays off the list exactly
# as on the CLI path. When Tauri's bundle-time keychain takes the
# list over later, both hold the SAME identity from the SAME p12,
# so the tauri#11992 trap - DIFFERENT identities in one view -
# cannot bite. The trap restores the baseline at step end.
# PREPEND it to the user's search list, the same way the CLI
# shards provision theirs above: codesign resolves a named
# identity through the search list, not the --keychain argument
# (probe run 40325026239 on this image: the same keychain failed
# "item could not be found" while unlisted and signed cleanly
# once listed). Prepend, never own: concurrent shards share this
# runner user's keychain domain, and a write replacing the whole
# list (or a trap restoring login) takes it back from under a
# build still running - ours leads, so codesign finds THIS copy.
# When Tauri's bundle-time keychain takes the list over later,
# both hold the SAME identity from the SAME p12, so the
# tauri#11992 trap - DIFFERENT identities in one view - cannot
# bite. The trap removes only this keychain from the list at
# step end.
KC="$CI_TMP/helper-sign.keychain-db"
KCPW="$(/usr/bin/openssl rand -hex 16)"
security create-keychain -p "$KCPW" "$KC"
security list-keychains -d user -s "$KC"
security list-keychains -d user -s "$KC" $(security list-keychains -d user | tr -d '"')
security unlock-keychain -p "$KCPW" "$KC"
# A fresh keychain defaults to locking after five idle minutes.
# A slow cargo build reaches its codesign calls well past that
# and codesign then waits forever for an unlock no headless
# runner can answer (run 40328004030: both darwin-x64 shards
# hung ~8 minutes in, the arm64 ones signed inside the window).
# Clear the timers before anything can go idle.
security set-keychain-settings "$KC"
printf '%s' "$MACOS_CERT_P12_BASE64" | base64 -d > "$CI_TMP/cert.p12"
security import "$CI_TMP/cert.p12" -k "$KC" -P "$MACOS_CERT_PASSWORD" -T /usr/bin/codesign
rm -f "$CI_TMP/cert.p12"
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KCPW" "$KC" >/dev/null 2>&1
security find-identity -v -p codesigning "$KC" | grep -q "Developer ID Application" \
|| { echo "imported p12 yielded no valid Developer ID Application identity"; exit 1; }
# The keychain is the search list for this step (listed above);
# the trap restores the baseline when the step ends.
trap 'rm -rf "$CI_TMP"; security delete-keychain "$KC" 2>/dev/null || true; security list-keychains -d user -s "$HOME/Library/Keychains/login.keychain-db"' EXIT
# The keychain leads the search list for this step (prepended
# above); the trap removes only it when the step ends.
trap 'rm -rf "$CI_TMP"; security delete-keychain "$KC" 2>/dev/null || true; REST="$(security list-keychains -d user | tr -d "\"" | grep -v "$KC" || true)"; [ -n "$REST" ] && security list-keychains -d user -s $REST || true' EXIT
export APPLE_SIGNING_KEYCHAIN="$KC"
;;
esac
Expand Down
26 changes: 16 additions & 10 deletions docs/release-and-ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -765,16 +765,22 @@ the experimental probe.
That signing runs during `cargo build`, before Tauri builds its own throwaway
keychain at bundle time, so the desktop shards build a dedicated keychain for it
and hand its path to the build script through `APPLE_SIGNING_KEYCHAIN`. The
shard then makes that keychain the sole entry of the user's search list, exactly
as the CLI darwin shards provision theirs: `codesign` resolves a named identity
through the search list, not the `--keychain` argument, and on this runner image
`security create-keychain` does not add the keychain to that list. Run
40306776766 failed with "item could not be found" for exactly that reason, and
probe run 40325026239 on the same image confirmed the split: the same keychain
refused the identity while unlisted and signed cleanly once listed. When Tauri's
bundle-time keychain takes the list over later it carries the same single
identity from the same p12, so the two views are never in conflict, and the
step's trap restores the baseline search list when it ends.
shard then PREPENDS that keychain to the user's search list: `codesign` resolves
a named identity through the search list, not the `--keychain` argument, and on
this runner image `security create-keychain` does not add the keychain to that
list (run 40306776766 failed with "item could not be found"; probe run
40325026239 confirmed the split: the same keychain refused the identity while
unlisted and signed cleanly once listed). Prepend, never own: the CLI shards
share this runner user's keychain domain, and a write that replaces the whole
list (or an EXIT trap restoring login) takes it back from under a build that is
still running. Ours leads, so the identity codesign finds is the copy imported
from this step's p12. A fresh keychain also locks after five idle minutes by
default, which a cargo build reaches its signing calls past (run 40328004030:
both `darwin-x64` desktop shards hung with codesign waiting on an unlock no
headless runner can answer); a bare `security set-keychain-settings` clears the
timers. When Tauri's bundle-time keychain takes the list over later it carries
the same single identity from the same p12, so the two views are never in
conflict. The trap removes only this step's keychain from the list when it ends.

The helper has its own `Info.plist` version, which must increase when its code
changes. Both apps reuse the newest installed helper; neither may downgrade it
Expand Down
5 changes: 4 additions & 1 deletion scripts/smoke-desktop-bundle.sh
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,10 @@ done

HELPER_BUNDLE="$APP_BUNDLE/Contents/Helpers/Subshell Display Helper.app"
[ -x "$HELPER_BUNDLE/Contents/MacOS/subshell-display-helper" ] || fail "the native display helper is missing"
codesign --verify --strict -R 'anchor apple generic and identifier "dev.subshell.display-helper" and certificate leaf[subject.OU] = "DVRYX6BV6J"' "$HELPER_BUNDLE" || fail "the display helper has the wrong signing identity"
# -R must carry the requirement as -R=<text>: the space-separated -R <text>
# form is ALWAYS read as a file path, so it fails "invalid requirement
# specification" on every run no matter what signed the helper.
codesign --verify --strict '-R=anchor apple generic and identifier "dev.subshell.display-helper" and certificate leaf[subject.OU] = "DVRYX6BV6J"' "$HELPER_BUNDLE" || fail "the display helper has the wrong signing identity"
helper_sig="$(codesign -dvv "$HELPER_BUNDLE" 2>&1 || true)"
case "$helper_sig" in *"flags="*"runtime"*) ;; *) fail "the display helper is missing the hardened runtime" ;; esac

Expand Down
Loading