From 42f1d05473885f55d17366719921310b62c73738 Mon Sep 17 00:00:00 2001 From: Bob Van Hove <1587584+bobvh@users.noreply.github.com> Date: Thu, 30 Jul 2026 20:27:04 +0200 Subject: [PATCH] Sign and notarize the bundled macOS client Tag-triggered wheel builds now codesign and notarize the staged gen binary before it's packed into the macOS wheel, then re-verify the signature on the extracted binary inside the built wheel. --- .github/workflows/python-wheels.yaml | 64 ++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/.github/workflows/python-wheels.yaml b/.github/workflows/python-wheels.yaml index ab4f9bd2..4b28f201 100644 --- a/.github/workflows/python-wheels.yaml +++ b/.github/workflows/python-wheels.yaml @@ -34,6 +34,61 @@ jobs: - name: Stage bundled client (macOS) if: runner.os == 'macOS' run: make stage-python-client + - name: Sign bundled client (macOS) + if: runner.os == 'macOS' && github.ref_type == 'tag' + env: + MACOS_CERTIFICATE: ${{ secrets.PROD_MACOS_APP_CERTIFICATE }} + MACOS_CERTIFICATE_PWD: ${{ secrets.PROD_MACOS_APP_CERTIFICATE_PWD }} + MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_APP_CERTIFICATE_NAME }} + MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }} + run: | + CERTIFICATE_PATH="$RUNNER_TEMP/gen-application-certificate.p12" + KEYCHAIN_PATH="$RUNNER_TEMP/gen-application-signing.keychain-db" + echo "$MACOS_CERTIFICATE" | base64 --decode > "$CERTIFICATE_PATH" + security create-keychain -p "$MACOS_CI_KEYCHAIN_PWD" "$KEYCHAIN_PATH" + security default-keychain -s "$KEYCHAIN_PATH" + security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" "$KEYCHAIN_PATH" + security import "$CERTIFICATE_PATH" \ + -k "$KEYCHAIN_PATH" \ + -P "$MACOS_CERTIFICATE_PWD" \ + -T /usr/bin/codesign + security set-key-partition-list \ + -S apple-tool:,apple:,codesign: \ + -s \ + -k "$MACOS_CI_KEYCHAIN_PWD" \ + "$KEYCHAIN_PATH" + codesign \ + --force \ + --timestamp \ + --options runtime \ + --sign "$MACOS_CERTIFICATE_NAME" \ + gen.gen.data/scripts/gen + codesign --verify --strict --verbose=2 gen.gen.data/scripts/gen + security delete-keychain "$KEYCHAIN_PATH" + - name: Notarize bundled client (macOS) + if: runner.os == 'macOS' && github.ref_type == 'tag' + env: + MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }} + PROD_MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }} + PROD_MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }} + PROD_MACOS_NOTARIZATION_PWD: ${{ secrets.PROD_MACOS_NOTARIZATION_PWD }} + run: | + KEYCHAIN_PATH="$RUNNER_TEMP/gen-notarization.keychain-db" + NOTARIZATION_ARCHIVE="$RUNNER_TEMP/gen-client.zip" + ditto -c -k --keepParent gen.gen.data/scripts/gen "$NOTARIZATION_ARCHIVE" + security create-keychain -p "$MACOS_CI_KEYCHAIN_PWD" "$KEYCHAIN_PATH" + security default-keychain -s "$KEYCHAIN_PATH" + security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" "$KEYCHAIN_PATH" + security list-keychain -d user -s "$KEYCHAIN_PATH" + xcrun notarytool store-credentials "gen-notarytool-profile" \ + --apple-id "$PROD_MACOS_NOTARIZATION_APPLE_ID" \ + --team-id "$PROD_MACOS_NOTARIZATION_TEAM_ID" \ + --password "$PROD_MACOS_NOTARIZATION_PWD" \ + --keychain "$KEYCHAIN_PATH" + xcrun notarytool submit "$NOTARIZATION_ARCHIVE" \ + --keychain-profile "gen-notarytool-profile" \ + --wait + security delete-keychain "$KEYCHAIN_PATH" - name: Stage bundled client (Windows) if: runner.os == 'Windows' shell: pwsh @@ -83,6 +138,15 @@ jobs: $ErrorActionPreference = 'Stop' $wheel = Get-ChildItem "gen-python/target/wheels/*.whl" python gen-python/scripts/verify_wheel.py "$($wheel.FullName)" + - name: Verify bundled client signature (macOS) + if: runner.os == 'macOS' && github.ref_type == 'tag' + run: | + CHECK_DIRECTORY="$RUNNER_TEMP/gen-wheel-check" + mkdir -p "$CHECK_DIRECTORY" + unzip -q gen-python/target/wheels/*.whl -d "$CHECK_DIRECTORY" + CLIENT_PATH=$(find "$CHECK_DIRECTORY" -type f -path "*/scripts/gen" -print -quit) + test -n "$CLIENT_PATH" + codesign --verify --strict --verbose=2 "$CLIENT_PATH" - name: Smoke test wheel (macOS, Linux) if: runner.os != 'Windows' run: |