diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9cf4377..1f1c7bd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,6 +28,7 @@ jobs: target: x86_64-pc-windows-msvc args: "--bundles nsis" runs-on: ${{ matrix.os }} + timeout-minutes: 120 steps: - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 @@ -58,18 +59,59 @@ jobs: test -n "$TAURI_SIGNING_PRIVATE_KEY" test -n "$TAURI_SIGNING_PRIVATE_KEY_PASSWORD" - - name: build ad-hoc signed macOS release + - name: verify Apple signing configuration + if: startsWith(matrix.os, 'macos') + shell: bash + env: + APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} + APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} + APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} + APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} + APPLE_API_PRIVATE_KEY: ${{ secrets.APPLE_API_PRIVATE_KEY }} + run: | + test -n "$APPLE_CERTIFICATE" + test -n "$APPLE_CERTIFICATE_PASSWORD" + test -n "$APPLE_SIGNING_IDENTITY" + test -n "$APPLE_API_KEY" + test -n "$APPLE_API_ISSUER" + test -n "$APPLE_API_PRIVATE_KEY" + + - name: prepare Apple notarization key + if: startsWith(matrix.os, 'macos') + shell: bash + env: + APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} + APPLE_API_PRIVATE_KEY: ${{ secrets.APPLE_API_PRIVATE_KEY }} + run: | + umask 077 + APPLE_API_KEY_PATH="$RUNNER_TEMP/AuthKey_${APPLE_API_KEY}.p8" + printf '%s' "$APPLE_API_PRIVATE_KEY" | base64 --decode -o "$APPLE_API_KEY_PATH" + test -s "$APPLE_API_KEY_PATH" + printf 'APPLE_API_KEY_PATH=%s\n' "$APPLE_API_KEY_PATH" >> "$GITHUB_ENV" + + - name: import Apple Developer ID certificate + if: startsWith(matrix.os, 'macos') + uses: apple-actions/import-codesign-certs@5142e029c445c10ffc7149d172e540235a065466 + with: + p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }} + p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + + - name: build signed and notarized macOS release if: startsWith(matrix.os, 'macos') env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - APPLE_SIGNING_IDENTITY: "-" + APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} + APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} + APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} + APPLE_API_KEY_PATH: ${{ env.APPLE_API_KEY_PATH }} TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} uses: tauri-apps/tauri-action@1deb371b0cd8bd54025b384f1cd735e725c4060f with: tagName: v__VERSION__ releaseName: proxybench v__VERSION__ - releaseBody: Preview builds made without certificates yet. macOS DMGs need right-click Open on first launch. The Windows NSIS setup wizard may trigger SmartScreen; choose More info, then Run anyway. + releaseBody: macOS DMGs are Developer ID signed and notarized by Apple. The Windows NSIS setup wizard is unsigned and may trigger SmartScreen; choose More info, then Run anyway. releaseDraft: true uploadUpdaterJson: true uploadUpdaterSignatures: true @@ -77,6 +119,55 @@ jobs: tauriScript: bun run tauri args: ${{ matrix.args }} + - name: notarize and verify macOS DMG + if: startsWith(matrix.os, 'macos') + shell: bash + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} + APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} + APPLE_API_KEY_PATH: ${{ env.APPLE_API_KEY_PATH }} + run: | + APP_PATH="src-tauri/target/${{ matrix.target }}/release/bundle/macos/proxybench.app" + DMG_DIR="src-tauri/target/${{ matrix.target }}/release/bundle/dmg" + shopt -s nullglob + DMGS=("$DMG_DIR"/*.dmg) + test -d "$APP_PATH" + test "${#DMGS[@]}" -eq 1 + DMG_PATH="${DMGS[0]}" + + codesign --verify --deep --strict --verbose=2 "$APP_PATH" + APP_SIGNATURE=$(codesign -d --verbose=4 "$APP_PATH" 2>&1) + printf '%s\n' "$APP_SIGNATURE" | grep -F "Authority=Developer ID Application:" + printf '%s\n' "$APP_SIGNATURE" | grep -E 'flags=.*\(.*runtime.*\)' + xcrun stapler validate -v "$APP_PATH" + spctl --assess --type execute --verbose=4 "$APP_PATH" + + NOTARY_RESULT=$(xcrun notarytool submit "$DMG_PATH" \ + --key "$APPLE_API_KEY_PATH" \ + --key-id "$APPLE_API_KEY" \ + --issuer "$APPLE_API_ISSUER" \ + --wait --timeout 30m --output-format json) + if ! printf '%s\n' "$NOTARY_RESULT" | jq -e '.status == "Accepted"'; then + SUBMISSION_ID=$(printf '%s\n' "$NOTARY_RESULT" | jq -er '.id') + xcrun notarytool log "$SUBMISSION_ID" \ + --key "$APPLE_API_KEY_PATH" \ + --key-id "$APPLE_API_KEY" \ + --issuer "$APPLE_API_ISSUER" + exit 1 + fi + xcrun stapler staple -v "$DMG_PATH" + + codesign --verify --strict --verbose=2 "$DMG_PATH" + xcrun stapler validate -v "$DMG_PATH" + spctl --assess --type open --context context:primary-signature --verbose=4 "$DMG_PATH" + gh release upload "$GITHUB_REF_NAME" "$DMG_PATH" --clobber + + - name: remove Apple notarization key + if: always() && startsWith(matrix.os, 'macos') + shell: bash + run: rm -f "$APPLE_API_KEY_PATH" + - name: build Windows release if: startsWith(matrix.os, 'windows') uses: tauri-apps/tauri-action@1deb371b0cd8bd54025b384f1cd735e725c4060f @@ -87,7 +178,7 @@ jobs: with: tagName: v__VERSION__ releaseName: proxybench v__VERSION__ - releaseBody: Preview builds made without certificates yet. macOS DMGs need right-click Open on first launch. The Windows NSIS setup wizard may trigger SmartScreen; choose More info, then Run anyway. + releaseBody: macOS DMGs are Developer ID signed and notarized by Apple. The Windows NSIS setup wizard is unsigned and may trigger SmartScreen; choose More info, then Run anyway. releaseDraft: true uploadUpdaterJson: true uploadUpdaterSignatures: true diff --git a/.gitignore b/.gitignore index ae5be03..3081e75 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .env.* !.env.example .DS_Store +.agents node_modules /build /.svelte-kit diff --git a/AGENTS.md b/AGENTS.md index 91dfca4..299409a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -118,11 +118,16 @@ Bump `package.json`, `src-tauri/tauri.conf.json`, and `src-tauri/Cargo.toml` to the same `X.Y.Z`, merge to `main`, then tag `vX.Y.Z`. GitHub Actions (`.github/workflows/ci.yml` on `main` and pull requests, -`.github/workflows/release.yml` on `vX.Y.Z`) builds macOS DMGs for Apple -Silicon and Intel and a Windows NSIS setup wizard. macOS uses ad-hoc signing; -Windows installers are unsigned. +`.github/workflows/release.yml` on `vX.Y.Z`) builds Developer ID signed and +Apple-notarized macOS DMGs for Apple Silicon and Intel and an unsigned Windows +NSIS setup wizard. Updater releases require `TAURI_SIGNING_PRIVATE_KEY` and `TAURI_SIGNING_PRIVATE_KEY_PASSWORD`. The workflow keeps releases as drafts until `latest.json` contains macOS arm64, macOS x64, and Windows x64. Never replace published assets; ship a higher patch version. + +macOS releases require `APPLE_CERTIFICATE`, `APPLE_CERTIFICATE_PASSWORD`, +`APPLE_SIGNING_IDENTITY`, `APPLE_API_KEY`, `APPLE_API_ISSUER`, and +`APPLE_API_PRIVATE_KEY`. The certificate and API private key are base64 +encoded. Apple code signing is independent from updater signing. diff --git a/README.md b/README.md index 654fff3..c2f66d6 100644 --- a/README.md +++ b/README.md @@ -42,21 +42,18 @@ You do not need Git, GitHub, or a developer setup. 1. Open the [latest release](https://github.com/Mathious6/proxybench/releases/latest). 2. Download the file for your computer. -| Your computer | File to download | -| --------------------------------------- | ---------------------------------------- | -| Mac with Apple Silicon (M1, M2, M3, M4) | macOS **arm64** `.dmg` | -| Mac with Intel | macOS **x64** `.dmg` | -| Windows 10 or 11 | Windows **x64** setup `.exe` | +| Your computer | File to download | +| --------------------------------------- | ---------------------------- | +| Mac with Apple Silicon (M1, M2, M3, M4) | macOS **arm64** `.dmg` | +| Mac with Intel | macOS **x64** `.dmg` | +| Windows 10 or 11 | Windows **x64** setup `.exe` | -On a Mac, open the DMG, drag **proxybench** into Applications, then right-click -it and choose **Open** the first time. You only do that once. +On a Mac, open the DMG, drag **proxybench** into Applications, then open it +normally. MacOS builds are Developer ID signed and notarized by Apple. On Windows, run the setup wizard. If SmartScreen appears, choose **More info**, then **Run anyway**. Windows needs WebView2; Windows 10 and 11 -usually already have it. - -macOS builds use ad-hoc signing and are not notarized. Windows installers are -unsigned. +usually already have it. Windows installers are unsigned. ## Use