From 951e6594a244a271ff888b248231098f5c207bfc Mon Sep 17 00:00:00 2001 From: LHagfoss Date: Sun, 18 Oct 2026 14:12:52 +0200 Subject: [PATCH 1/2] ci(macos): sign and notarize release assets when Developer ID secrets exist The swift-app job and the macOS CLI build now sign with a Developer ID (hardened runtime, secure timestamp), notarize with notarytool, staple the app and verify the downloaded zip with spctl, but only once all five MACOS_* secrets are set. Without them the release stays ad-hoc signed and the job prints a notice; a partial set fails the job. Refs #2034 Co-Authored-By: Claude Opus 5.5 --- .github/workflows/build.yml | 101 ++++++++++++++++++- .github/workflows/macos-swift-ci.yml | 11 ++ README.md | 2 +- docs/install.md | 7 +- docs/macos-release-signing.md | 86 ++++++++++++++++ scripts/macos-release-signing.sh | 144 +++++++++++++++++++++++++++ scripts/package-swift-app.sh | 22 +++- 7 files changed, 364 insertions(+), 9 deletions(-) create mode 100644 docs/macos-release-signing.md create mode 100755 scripts/macos-release-signing.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eabedea32..c46883e44 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -104,6 +104,53 @@ jobs: shell: bash run: ${SCCACHE_PATH} --show-stats + # Developer ID signing switches on once all five MACOS_* secrets + # exist (docs/macos-release-signing.md); until then the binary + # ships as the linker signed it and this prints a notice. + - name: Check macOS signing secrets + id: signing + if: (startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch') && runner.os == 'macOS' + shell: bash + env: + MACOS_DEVELOPER_ID_P12_BASE64: ${{ secrets.MACOS_DEVELOPER_ID_P12_BASE64 }} + MACOS_DEVELOPER_ID_P12_PASSWORD: ${{ secrets.MACOS_DEVELOPER_ID_P12_PASSWORD }} + MACOS_NOTARY_APPLE_ID: ${{ secrets.MACOS_NOTARY_APPLE_ID }} + MACOS_NOTARY_TEAM_ID: ${{ secrets.MACOS_NOTARY_TEAM_ID }} + MACOS_NOTARY_PASSWORD: ${{ secrets.MACOS_NOTARY_PASSWORD }} + run: scripts/macos-release-signing.sh check + + - name: Import Developer ID certificate + if: steps.signing.outputs.enabled == 'true' + shell: bash + env: + MACOS_DEVELOPER_ID_P12_BASE64: ${{ secrets.MACOS_DEVELOPER_ID_P12_BASE64 }} + MACOS_DEVELOPER_ID_P12_PASSWORD: ${{ secrets.MACOS_DEVELOPER_ID_P12_PASSWORD }} + MACOS_NOTARY_APPLE_ID: ${{ secrets.MACOS_NOTARY_APPLE_ID }} + MACOS_NOTARY_TEAM_ID: ${{ secrets.MACOS_NOTARY_TEAM_ID }} + MACOS_NOTARY_PASSWORD: ${{ secrets.MACOS_NOTARY_PASSWORD }} + run: scripts/macos-release-signing.sh import + + # A bare executable cannot be stapled: it is signed and submitted, + # and Gatekeeper finds the ticket online. + - name: Sign and notarize macOS binary + if: steps.signing.outputs.enabled == 'true' + shell: bash + env: + MACOS_DEVELOPER_ID_P12_BASE64: ${{ secrets.MACOS_DEVELOPER_ID_P12_BASE64 }} + MACOS_DEVELOPER_ID_P12_PASSWORD: ${{ secrets.MACOS_DEVELOPER_ID_P12_PASSWORD }} + MACOS_NOTARY_APPLE_ID: ${{ secrets.MACOS_NOTARY_APPLE_ID }} + MACOS_NOTARY_TEAM_ID: ${{ secrets.MACOS_NOTARY_TEAM_ID }} + MACOS_NOTARY_PASSWORD: ${{ secrets.MACOS_NOTARY_PASSWORD }} + run: | + scripts/macos-release-signing.sh sign target/${{ matrix.target }}/release/rustcode + ditto -c -k --keepParent target/${{ matrix.target }}/release/rustcode "$RUNNER_TEMP/rustcode-cli-notarize.zip" + scripts/macos-release-signing.sh notarize "$RUNNER_TEMP/rustcode-cli-notarize.zip" + + - name: Remove signing keychain + if: always() && steps.signing.outputs.enabled == 'true' + shell: bash + run: scripts/macos-release-signing.sh cleanup + - name: Package Linux / macOS binary if: (startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch') && runner.os != 'Windows' shell: bash @@ -148,12 +195,64 @@ jobs: - name: Test Swift package run: swift test --package-path macos + # Without the MACOS_* secrets the app stays ad-hoc signed and + # unnotarized (docs/macos-release-signing.md). + - name: Check macOS signing secrets + id: signing + env: + MACOS_DEVELOPER_ID_P12_BASE64: ${{ secrets.MACOS_DEVELOPER_ID_P12_BASE64 }} + MACOS_DEVELOPER_ID_P12_PASSWORD: ${{ secrets.MACOS_DEVELOPER_ID_P12_PASSWORD }} + MACOS_NOTARY_APPLE_ID: ${{ secrets.MACOS_NOTARY_APPLE_ID }} + MACOS_NOTARY_TEAM_ID: ${{ secrets.MACOS_NOTARY_TEAM_ID }} + MACOS_NOTARY_PASSWORD: ${{ secrets.MACOS_NOTARY_PASSWORD }} + run: scripts/macos-release-signing.sh check + + - name: Import Developer ID certificate + if: steps.signing.outputs.enabled == 'true' + env: + MACOS_DEVELOPER_ID_P12_BASE64: ${{ secrets.MACOS_DEVELOPER_ID_P12_BASE64 }} + MACOS_DEVELOPER_ID_P12_PASSWORD: ${{ secrets.MACOS_DEVELOPER_ID_P12_PASSWORD }} + MACOS_NOTARY_APPLE_ID: ${{ secrets.MACOS_NOTARY_APPLE_ID }} + MACOS_NOTARY_TEAM_ID: ${{ secrets.MACOS_NOTARY_TEAM_ID }} + MACOS_NOTARY_PASSWORD: ${{ secrets.MACOS_NOTARY_PASSWORD }} + run: scripts/macos-release-signing.sh import + + # Signs with MACOS_SIGN_IDENTITY when the import set it, else ad hoc. - name: Package SwiftUI app run: | tar xzf engine/rustcode-macos-aarch64.tar.gz -C engine chmod +x engine/rustcode-macos-aarch64 REQUIRE_COMPILED_ICON=1 scripts/package-swift-app.sh engine/rustcode-macos-aarch64 target/swift-release - ditto -c -k --sequesterRsrc --keepParent target/swift-release/RustCode.app rustcode-macos-aarch64-swift-app.zip + + - name: Notarize and staple SwiftUI app + if: steps.signing.outputs.enabled == 'true' + env: + MACOS_DEVELOPER_ID_P12_BASE64: ${{ secrets.MACOS_DEVELOPER_ID_P12_BASE64 }} + MACOS_DEVELOPER_ID_P12_PASSWORD: ${{ secrets.MACOS_DEVELOPER_ID_P12_PASSWORD }} + MACOS_NOTARY_APPLE_ID: ${{ secrets.MACOS_NOTARY_APPLE_ID }} + MACOS_NOTARY_TEAM_ID: ${{ secrets.MACOS_NOTARY_TEAM_ID }} + MACOS_NOTARY_PASSWORD: ${{ secrets.MACOS_NOTARY_PASSWORD }} + run: scripts/macos-release-signing.sh notarize target/swift-release/RustCode.app + + - name: Zip SwiftUI app + run: ditto -c -k --sequesterRsrc --keepParent target/swift-release/RustCode.app rustcode-macos-aarch64-swift-app.zip + + # Check the asset as a user gets it: unpacked from the zip, with + # the quarantine flag a browser download sets. + - name: Verify downloaded app with Gatekeeper + if: steps.signing.outputs.enabled == 'true' + run: | + check="$RUNNER_TEMP/gatekeeper-check" + rm -rf "$check" && mkdir -p "$check" + ditto -x -k rustcode-macos-aarch64-swift-app.zip "$check" + xattr -w com.apple.quarantine "0083;$(printf '%x' "$(date +%s)");Safari;" "$check/RustCode.app" + xcrun stapler validate "$check/RustCode.app" + spctl --assess --type execute --verbose=4 "$check/RustCode.app" + codesign --verify --deep --strict --verbose=2 "$check/RustCode.app" + + - name: Remove signing keychain + if: always() && steps.signing.outputs.enabled == 'true' + run: scripts/macos-release-signing.sh cleanup - name: Upload app artifact uses: actions/upload-artifact@v7 diff --git a/.github/workflows/macos-swift-ci.yml b/.github/workflows/macos-swift-ci.yml index d6306e24c..aa261cedc 100644 --- a/.github/workflows/macos-swift-ci.yml +++ b/.github/workflows/macos-swift-ci.yml @@ -9,6 +9,7 @@ on: - .github/workflows/macos-swift-ci.yml - macos/** - scripts/package-swift-app.sh + - scripts/macos-release-signing.sh workflow_dispatch: concurrency: @@ -31,3 +32,13 @@ jobs: - name: Test run: swift test --package-path macos + + # Exercise the unsigned (ad-hoc) packaging path release builds + # take until the Developer ID secrets exist; any executable + # stands in for the engine. + - name: Package ad-hoc signed app + run: | + scripts/package-swift-app.sh "$(command -v true)" target/swift-ci + codesign --verify --deep --strict --verbose=2 target/swift-ci/RustCode.app + bash -n scripts/macos-release-signing.sh + scripts/macos-release-signing.sh check diff --git a/README.md b/README.md index 9be466327..7b53eaa57 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ scrollback and motion settings — is in the | Topic | Guide | | --- | --- | -| Install, update, desktop app | [docs/install.md](docs/install.md) | +| Install, update, desktop app | [docs/install.md](docs/install.md) · [macOS release signing](docs/macos-release-signing.md) | | Configuration, skills, MCP, sync | [docs/configuration.md](docs/configuration.md) | | Providers and sign-in | [docs/provider-auth.md](docs/provider-auth.md) · [GitHub Copilot](docs/github-copilot.md) | | Approvals and sandbox modes | [docs/sandbox.md](docs/sandbox.md) · [shell approvals](docs/shell-approvals.md) | diff --git a/docs/install.md b/docs/install.md index f7b496baa..72eb754b9 100644 --- a/docs/install.md +++ b/docs/install.md @@ -81,10 +81,13 @@ open target/swift-app/RustCode.app ``` Tagged releases publish `rustcode-macos-aarch64-swift-app.zip` alongside the -CLI archives. The bundle is ad-hoc signed but not notarized, so macOS +CLI archives. The bundle is ad-hoc signed but not notarized yet, so macOS Gatekeeper warns when opening a downloaded copy; right-click it and choose Open, or clear the quarantine flag with -`xattr -d com.apple.quarantine RustCode.app`. +`xattr -d com.apple.quarantine RustCode.app`. The release workflow switches +to Developer ID signing and notarization once its signing secrets are set +(see [macOS release signing](macos-release-signing.md)); this workaround is +removed from here with the first notarized release. Questions the agent asks (`ask_question`) appear as cards: one button per option, ticks and Submit for multi-select questions, and a free-text answer. diff --git a/docs/macos-release-signing.md b/docs/macos-release-signing.md new file mode 100644 index 000000000..c9f709b55 --- /dev/null +++ b/docs/macos-release-signing.md @@ -0,0 +1,86 @@ +# macOS release signing and notarization + +Tagged releases publish two macOS assets: the CLI archive +`rustcode-macos-aarch64.tar.gz` (also used by the Homebrew formula) and the +SwiftUI app `rustcode-macos-aarch64-swift-app.zip`, which bundles the same +engine as `Contents/Helpers/rustcode`. + +The release workflow (`.github/workflows/build.yml`) signs both with a +Developer ID and notarizes them **only when all five secrets below are set**. +Until then it prints a notice and ships them as before: the app ad-hoc +signed, the CLI as the linker signed it, neither notarized. If only some of +the secrets are set the job fails, so a half-finished setup is not mistaken +for a signed release. + +## What the workflow does + +`scripts/macos-release-signing.sh` holds the steps: + +1. `check` decides whether signing is on and reports missing secrets. +2. `import` decodes the `.p12` into a temporary keychain under + `$RUNNER_TEMP` and exports its `Developer ID Application` identity as + `MACOS_SIGN_IDENTITY`. `cleanup` deletes the keychain at the end of the + job, also after a failure. +3. CLI (`build` job, macOS): sign `rustcode` with the hardened runtime and a + secure timestamp, then submit it to the notary service in a zip. A bare + executable cannot be stapled; Gatekeeper fetches its ticket online. +4. App (`swift-app` job): `scripts/package-swift-app.sh` sees + `MACOS_SIGN_IDENTITY` and signs `Contents/Helpers/rustcode`, then the + bundle, both with `--options runtime --timestamp`. `notarize` submits the + app with `xcrun notarytool submit --wait`, fails unless the result is + `Accepted` (printing the notary log), staples the ticket, then runs + `xcrun stapler validate` and `spctl --assess --type execute`. +5. The released zip is unpacked again, given the quarantine flag a browser + download sets, and checked with `stapler validate`, `spctl --assess` and + `codesign --verify --deep --strict`. + +No entitlements are needed: the app is not sandboxed, and neither executable +uses JIT or loads libraries signed by someone else. Add an entitlements file +only if that changes (for example `com.apple.security.cs.disable-library-validation` +for third-party plug-ins). + +## Secrets + +Notarization uses an Apple ID with an app-specific password. (An App Store +Connect API key would work too, but needs a different `notarytool` call; it +is not wired up.) + +| Secret | Value | +| --- | --- | +| `MACOS_DEVELOPER_ID_P12_BASE64` | The **Developer ID Application** certificate with its private key, exported as `.p12` and base64 encoded: `base64 -i DeveloperID.p12 \| pbcopy` | +| `MACOS_DEVELOPER_ID_P12_PASSWORD` | The password chosen when exporting the `.p12` | +| `MACOS_NOTARY_APPLE_ID` | The Apple ID email of an Apple Developer Program member of the team | +| `MACOS_NOTARY_TEAM_ID` | The 10-character Team ID (developer.apple.com → Membership details) | +| `MACOS_NOTARY_PASSWORD` | An app-specific password for that Apple ID (account.apple.com → Sign-In and Security → App-Specific Passwords) | + +## Setup + +1. In the Apple Developer account, create a **Developer ID Application** + certificate (Certificates, IDs & Profiles → +). Only the Account Holder + can create one. Install it in the login keychain. +2. In Keychain Access, select the certificate together with its private key + and export it as `DeveloperID.p12` with a strong password. +3. Create the app-specific password. +4. Add the secrets to the repository, for example: + + ```bash + gh secret set MACOS_DEVELOPER_ID_P12_BASE64 < <(base64 -i DeveloperID.p12) + gh secret set MACOS_DEVELOPER_ID_P12_PASSWORD + gh secret set MACOS_NOTARY_APPLE_ID + gh secret set MACOS_NOTARY_TEAM_ID + gh secret set MACOS_NOTARY_PASSWORD + ``` + +5. Run the Build workflow with **Run workflow** (`workflow_dispatch`) on + `main` and check that the `swift-app` job ends with `spctl` reporting + `accepted` and `source=Notarized Developer ID`. Delete the local `.p12`. +6. Once a release is signed, remove the quarantine workaround from + [install.md](install.md#native-desktop-app). + +To check a downloaded release by hand: + +```bash +ditto -x -k rustcode-macos-aarch64-swift-app.zip . +spctl --assess --type execute --verbose=4 RustCode.app +xcrun stapler validate RustCode.app +``` diff --git a/scripts/macos-release-signing.sh b/scripts/macos-release-signing.sh new file mode 100755 index 000000000..d09e0d76d --- /dev/null +++ b/scripts/macos-release-signing.sh @@ -0,0 +1,144 @@ +#!/usr/bin/env bash +# Developer ID signing and notarization for the macOS release assets. +# Used by .github/workflows/build.yml; see docs/macos-release-signing.md. +# +# Usage: +# scripts/macos-release-signing.sh check +# Exit 0 and print `enabled=true|false` (also to $GITHUB_OUTPUT) after +# checking which signing secrets are set. Fails when only some are. +# scripts/macos-release-signing.sh import +# Import the Developer ID certificate into a temporary keychain and +# export MACOS_SIGN_IDENTITY (to $GITHUB_ENV when set). +# scripts/macos-release-signing.sh sign +# Sign one executable with the hardened runtime and a secure timestamp. +# scripts/macos-release-signing.sh notarize +# Submit to the notary service and wait. An app is stapled and checked +# with spctl; a zip (a bare binary) cannot be stapled and is only +# submitted. +# scripts/macos-release-signing.sh cleanup +# Delete the temporary keychain. +# +# Secrets (environment): MACOS_DEVELOPER_ID_P12_BASE64, +# MACOS_DEVELOPER_ID_P12_PASSWORD, MACOS_NOTARY_APPLE_ID, +# MACOS_NOTARY_TEAM_ID, MACOS_NOTARY_PASSWORD (an app-specific password). +set -euo pipefail + +SECRETS=( + MACOS_DEVELOPER_ID_P12_BASE64 + MACOS_DEVELOPER_ID_P12_PASSWORD + MACOS_NOTARY_APPLE_ID + MACOS_NOTARY_TEAM_ID + MACOS_NOTARY_PASSWORD +) +KEYCHAIN="${RUNNER_TEMP:-${TMPDIR:-/tmp}}/rustcode-signing.keychain-db" + +usage() { + sed -n '2,24p' "$0" >&2 + exit 2 +} + +check() { + local present=() missing=() name + for name in "${SECRETS[@]}"; do + if [[ -n "${!name:-}" ]]; then present+=("$name"); else missing+=("$name"); fi + done + local enabled=false + if ((${#missing[@]} == 0)); then + enabled=true + elif ((${#present[@]} > 0)); then + echo "::error::Developer ID signing is half configured; missing secrets: ${missing[*]}" + exit 1 + else + echo "::notice::Developer ID signing secrets are not set; macOS assets stay ad-hoc signed and are not notarized. See docs/macos-release-signing.md." + fi + echo "enabled=$enabled" + if [[ -n "${GITHUB_OUTPUT:-}" ]]; then echo "enabled=$enabled" >>"$GITHUB_OUTPUT"; fi +} + +require_secrets() { + local name + for name in "${SECRETS[@]}"; do + [[ -n "${!name:-}" ]] || { echo "$name is not set" >&2; exit 1; } + done +} + +import() { + require_secrets + local password certificate + password="$(openssl rand -base64 24)" + certificate="$(mktemp "${RUNNER_TEMP:-${TMPDIR:-/tmp}}/developer-id.XXXXXX")" + printf '%s' "$MACOS_DEVELOPER_ID_P12_BASE64" | base64 --decode >"$certificate" + security delete-keychain "$KEYCHAIN" 2>/dev/null || true + security create-keychain -p "$password" "$KEYCHAIN" + security set-keychain-settings -lut 21600 "$KEYCHAIN" + security unlock-keychain -p "$password" "$KEYCHAIN" + security import "$certificate" -k "$KEYCHAIN" -P "$MACOS_DEVELOPER_ID_P12_PASSWORD" \ + -f pkcs12 -T /usr/bin/codesign -T /usr/bin/security + rm -f "$certificate" + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$password" "$KEYCHAIN" >/dev/null + # Put the temporary keychain first in the search list, keeping the others. + local existing=() + while IFS= read -r line; do + line="${line//\"/}" + line="${line#"${line%%[![:space:]]*}"}" + [[ -n "$line" ]] && existing+=("$line") + done < <(security list-keychains -d user) + security list-keychains -d user -s "$KEYCHAIN" ${existing[@]+"${existing[@]}"} + local identity + identity="$(security find-identity -v -p codesigning "$KEYCHAIN" \ + | awk -F'"' '/Developer ID Application/ {print $2; exit}')" + [[ -n "$identity" ]] || { echo "No Developer ID Application identity in the certificate" >&2; exit 1; } + echo "Signing identity: $identity" + if [[ -n "${GITHUB_ENV:-}" ]]; then echo "MACOS_SIGN_IDENTITY=$identity" >>"$GITHUB_ENV"; fi +} + +sign() { + local binary="${1:?binary}" + : "${MACOS_SIGN_IDENTITY:?run import first}" + codesign --force --options runtime --timestamp --sign "$MACOS_SIGN_IDENTITY" "$binary" + codesign --verify --strict --verbose=2 "$binary" +} + +notarize() { + local target="${1:?file.zip or App.app}" upload result status id + require_secrets + upload="$target" + if [[ "$target" == *.app ]]; then + upload="${RUNNER_TEMP:-${TMPDIR:-/tmp}}/$(basename "$target" .app)-notarize.zip" + ditto -c -k --sequesterRsrc --keepParent "$target" "$upload" + fi + result="$(xcrun notarytool submit "$upload" \ + --apple-id "$MACOS_NOTARY_APPLE_ID" \ + --team-id "$MACOS_NOTARY_TEAM_ID" \ + --password "$MACOS_NOTARY_PASSWORD" \ + --wait --timeout 30m --output-format json)" + status="$(printf '%s' "$result" | python3 -c 'import json,sys; print(json.load(sys.stdin).get("status",""))')" + id="$(printf '%s' "$result" | python3 -c 'import json,sys; print(json.load(sys.stdin).get("id",""))')" + echo "Notarization $id: $status" + if [[ "$status" != "Accepted" ]]; then + [[ -z "$id" ]] || xcrun notarytool log "$id" \ + --apple-id "$MACOS_NOTARY_APPLE_ID" \ + --team-id "$MACOS_NOTARY_TEAM_ID" \ + --password "$MACOS_NOTARY_PASSWORD" || true + exit 1 + fi + if [[ "$target" == *.app ]]; then + rm -f "$upload" + xcrun stapler staple "$target" + xcrun stapler validate "$target" + spctl --assess --type execute --verbose=4 "$target" + fi +} + +cleanup() { + security delete-keychain "$KEYCHAIN" 2>/dev/null || true +} + +case "${1:-}" in + check) check ;; + import) import ;; + sign) shift; sign "$@" ;; + notarize) shift; notarize "$@" ;; + cleanup) cleanup ;; + *) usage ;; +esac diff --git a/scripts/package-swift-app.sh b/scripts/package-swift-app.sh index d58951a39..9d5e36c23 100755 --- a/scripts/package-swift-app.sh +++ b/scripts/package-swift-app.sh @@ -66,11 +66,23 @@ cat > "$STAGING/Contents/Info.plist" < EOF plutil -lint "$STAGING/Contents/Info.plist" -# Ad-hoc sign inside out so the bundle is sealed. Apple Silicon refuses to run -# an unsealed quarantined bundle and reports it as damaged; sealed but +# Sign inside out so the bundle is sealed. With MACOS_SIGN_IDENTITY (a +# Developer ID Application identity in the keychain, set by +# scripts/macos-release-signing.sh import) both are signed for notarization: +# hardened runtime and a secure timestamp. Neither needs entitlements: the app +# is not sandboxed, and neither executable uses JIT or loads third-party +# libraries. Otherwise the bundle is ad-hoc signed: Apple Silicon refuses to +# run an unsealed quarantined bundle and reports it as damaged; sealed but # unnotarized, Gatekeeper shows a warning the user can bypass. -codesign --force --sign - "$STAGING/Contents/Helpers/rustcode" -codesign --force --sign - "$STAGING" +if [[ -n "${MACOS_SIGN_IDENTITY:-}" ]]; then + SIGNING="Developer ID signed" + SIGN_ARGS=(--force --options runtime --timestamp --sign "$MACOS_SIGN_IDENTITY") +else + SIGNING="ad-hoc signed" + SIGN_ARGS=(--force --sign -) +fi +codesign "${SIGN_ARGS[@]}" "$STAGING/Contents/Helpers/rustcode" +codesign "${SIGN_ARGS[@]}" "$STAGING" codesign --verify --deep --strict "$STAGING" rm -rf "$APP.previous" if [[ -e "$APP" ]]; then mv "$APP" "$APP.previous"; fi @@ -81,4 +93,4 @@ else [[ ! -e "$APP.previous" ]] || mv "$APP.previous" "$APP" exit 1 fi -printf 'Built ad-hoc signed %s (version %s)\n' "$APP" "$VERSION" +printf 'Built %s %s (version %s)\n' "$SIGNING" "$APP" "$VERSION" From 3178d46488e545705f21675a354d088fcbad6f36 Mon Sep 17 00:00:00 2001 From: LHagfoss Date: Sun, 18 Oct 2026 14:17:43 +0200 Subject: [PATCH 2/2] ci(macos): package with /usr/bin/true as the stand-in engine Co-Authored-By: Claude Opus 5.5 --- .github/workflows/macos-swift-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/macos-swift-ci.yml b/.github/workflows/macos-swift-ci.yml index aa261cedc..ce5e1ccaf 100644 --- a/.github/workflows/macos-swift-ci.yml +++ b/.github/workflows/macos-swift-ci.yml @@ -38,7 +38,7 @@ jobs: # stands in for the engine. - name: Package ad-hoc signed app run: | - scripts/package-swift-app.sh "$(command -v true)" target/swift-ci + scripts/package-swift-app.sh /usr/bin/true target/swift-ci codesign --verify --deep --strict --verbose=2 target/swift-ci/RustCode.app bash -n scripts/macos-release-signing.sh scripts/macos-release-signing.sh check