diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0783436..b0ed71a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,11 +25,7 @@ jobs: body: | See the assets to download and install this version. - **macOS users:** The app is not yet signed/notarized. macOS will block it on first launch. To fix: - 1. Open **System Settings → Privacy & Security**, scroll down, and click **Open Anyway**, or - 2. Run `xattr -c Commonplace*.dmg` in Terminal before opening the DMG. - - Signing is in progress — this will be resolved in a future release. + macOS artifacts are signed and notarized by the release workflow. draft: true prerelease: false @@ -80,10 +76,78 @@ jobs: - name: Install frontend dependencies run: pnpm install + - name: Validate Apple signing secrets + if: matrix.platform == 'macos-latest' + env: + APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} + APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + run: | + missing=0 + for var in APPLE_CERTIFICATE APPLE_CERTIFICATE_PASSWORD APPLE_ID APPLE_PASSWORD APPLE_TEAM_ID; do + if [ -z "${!var}" ]; then + echo "::error::Missing required macOS signing secret: $var" + missing=1 + fi + done + + if [ "$missing" -ne 0 ]; then + exit 1 + fi + + - name: Import Apple Developer Certificate + if: matrix.platform == 'macos-latest' + env: + APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} + APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + run: | + CERT_PATH="$RUNNER_TEMP/apple-certificate.p12" + KEYCHAIN_PATH="$RUNNER_TEMP/app-signing.keychain-db" + KEYCHAIN_PASSWORD="$(uuidgen)" + + printf '%s' "$APPLE_CERTIFICATE" | base64 --decode > "$CERT_PATH" + + security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" + security default-keychain -s "$KEYCHAIN_PATH" + security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" + security set-keychain-settings -lut 3600 "$KEYCHAIN_PATH" + security import "$CERT_PATH" -k "$KEYCHAIN_PATH" -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign -T /usr/bin/security + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" + + echo "APPLE_KEYCHAIN_PATH=$KEYCHAIN_PATH" >> "$GITHUB_ENV" + security find-identity -v -p codesigning "$KEYCHAIN_PATH" + + - name: Resolve Apple signing identity + if: matrix.platform == 'macos-latest' + run: | + CERT_INFO="$(security find-identity -v -p codesigning "$APPLE_KEYCHAIN_PATH" | grep 'Developer ID Application' | head -n 1 || true)" + + if [ -z "$CERT_INFO" ]; then + echo "::error::No Developer ID Application signing identity found in the imported certificate." + security find-identity -v -p codesigning "$APPLE_KEYCHAIN_PATH" + exit 1 + fi + + CERT_ID="$(printf '%s\n' "$CERT_INFO" | awk -F'"' '{print $2}')" + echo "APPLE_SIGNING_IDENTITY=$CERT_ID" >> "$GITHUB_ENV" + echo "Using signing identity: $CERT_ID" + - name: Build and release uses: tauri-apps/tauri-action@44a5bf1eaeeebdabfd8951645f567c83b0ae02a3 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} + APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + APPLE_SIGNING_IDENTITY: ${{ env.APPLE_SIGNING_IDENTITY }} with: releaseId: ${{ needs.create-release.outputs.release-id }} args: ${{ matrix.args }} + + - name: Cleanup Apple keychain + if: always() && matrix.platform == 'macos-latest' && env.APPLE_KEYCHAIN_PATH != '' + run: security delete-keychain "$APPLE_KEYCHAIN_PATH" diff --git a/docs/release.md b/docs/release.md index 47b897e..fb7d06b 100644 --- a/docs/release.md +++ b/docs/release.md @@ -12,8 +12,20 @@ Releases are produced by GitHub Actions workflow: ## What the Workflow Does - Builds platform artifacts on macOS (arm64 + x64), Linux, and Windows. +- On macOS, validates the required Apple secrets, imports the Developer ID certificate into a temporary keychain, resolves the signing identity, and passes notarization credentials to Tauri.[^1][^2] - Runs `tauri-apps/tauri-action`. - Creates a draft GitHub Release with uploaded binaries. +- Produces signed/notarized macOS artifacts when the Apple secrets are configured. + +## Required GitHub Secrets + +- `APPLE_CERTIFICATE`: base64-encoded exported `.p12` certificate +- `APPLE_CERTIFICATE_PASSWORD`: password used when exporting the certificate +- `APPLE_ID`: Apple account email used for notarization +- `APPLE_PASSWORD`: app-specific password for the Apple account +- `APPLE_TEAM_ID`: Apple Developer Team ID + +The workflow resolves `APPLE_SIGNING_IDENTITY` from the imported certificate at runtime instead of committing a fixed identity to [`src-tauri/tauri.conf.json`](../src-tauri/tauri.conf.json). Tauri also supports inferring that identity directly from `APPLE_CERTIFICATE`, but the explicit resolution step makes CI failures easier to diagnose.[^2] ## Before Tagging @@ -29,6 +41,7 @@ Releases are produced by GitHub Actions workflow: - `pnpm build` - `cargo test` 4. Confirm the intended tag matches the app version exactly. +5. Confirm the Apple signing/notarization secrets above are configured in the repository before pushing a release tag. ## Triggering A Release @@ -53,3 +66,6 @@ When using manual dispatch, make sure: 3. Review the generated release title and notes. 4. Smoke-test at least one packaged build from the draft artifacts. 5. Publish the draft. + +[^1]: [Tauri macOS code signing and notarization](https://v2.tauri.app/distribute/sign/macos/) +[^2]: [Tauri environment variables reference](https://v2.tauri.app/reference/environment-variables/) diff --git a/docs/tasks/parking-lot.md b/docs/tasks/parking-lot.md index 50a6d81..d1e95b9 100644 --- a/docs/tasks/parking-lot.md +++ b/docs/tasks/parking-lot.md @@ -2,7 +2,7 @@ title: "Parking Lot" description: > A collection of ideas/proposals for new features and quick bug notes. -updated: 2026-03-25 +updated: 2026-04-01 --- - Consider using [ignore](https://crates.io/crates/ignore) crate for directory walking. @@ -25,15 +25,14 @@ updated: 2026-03-25 2. **Corrupted DMG / Gatekeeper quarantine**[^5][^6] - `release.yml` passes no Apple signing secrets to `tauri-action`[^7]; `tauri.conf.json` has no `macOS` signing block. Gatekeeper quarantines the unsigned DMG. + `release.yml` now imports the Apple Developer certificate on macOS runners, resolves a `Developer ID Application` signing identity, and passes the notarization environment to Tauri.[^7][^8] This still depends on the repository secrets being configured correctly and should be verified with a real test release. **Subtasks:** - [ ] Obtain Apple Developer credentials (Developer ID cert `.p12`, Apple ID, app-specific password, Team ID) - [ ] Add GitHub secrets: `APPLE_CERTIFICATE`, `APPLE_CERTIFICATE_PASSWORD`, `APPLE_ID`, `APPLE_PASSWORD`, `APPLE_TEAM_ID` - - [ ] Pass secrets as env vars to `tauri-apps/tauri-action` in `release.yml` - - [ ] Add `bundle.macOS.signingIdentity` to `tauri.conf.json` + - [x] Import the Apple certificate and resolve the signing identity in `release.yml` + - [x] Pass Apple signing and notarization env vars to `tauri-apps/tauri-action` in `release.yml` - [ ] Verify with `spctl --assess` after a test release - - [ ] Interim: add `xattr -c` workaround to release notes 3. **Outline utilization** - Use Rust-generated `metadata.outline` from `markdown_render` in the UI for document structure navigation/jump-to-heading behavior 4. **Perf** @@ -50,3 +49,4 @@ updated: 2026-03-25 [^5]: [Apple Gatekeeper and notarization](https://developer.apple.com/documentation/security/notarizing-macos-software-before-distribution) [^6]: [`xattr -c` workaround for quarantined DMGs](https://support.apple.com/en-us/102445) [^7]: [`tauri-apps/tauri-action` signing docs](https://v2.tauri.app/distribute/sign/macos/) +[^8]: [Tauri environment variables reference](https://v2.tauri.app/reference/environment-variables/)