Repository navigation
macOS signing and notarization, plus the Homebrew cask - #1
Merged
Merged
Conversation
macOS builds went out unsigned, so every download opened to "mandalo is damaged and can't be opened". The Developer ID certificate now signs the .app, the .dmg, the standalone CLI and the copy inside the darwin VSIXs, and Apple notarizes all of them. Three things this turned up that only a real build shows: - Tauri rejects a sidecar named after the src-tauri crate, so the bundled CLI is `mandalo-cli` and the cask's `target:` puts it back on PATH as `mandalo`. - Tauri notarizes and staples the .app but leaves the .dmg carrying it merely signed, and Gatekeeper assesses the downloaded .dmg on its own: `spctl -a -t open` answered "Unnotarized Developer ID" and refused it. The release job now notarizes the disk image too and re-uploads the stapled copy before SHA256SUMS is computed over it. - The Linux desktop job had no pkg-config/libssl-dev/cmake, which building the CLI sidecar there needs. A tagged release fails loudly when the Apple credentials are missing rather than publishing artifacts Gatekeeper will reject; other runs stay unsigned. The app bundle now carries the CLI, so the Homebrew cask installs both from one command. The formula becomes `depends_on :linux` to match the sixtysix tap: a formula without a bottle takes the build-from-source path, which demands Command Line Tools to install an already-compiled binary. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds end-to-end macOS signing/notarization to the release pipeline (including DMG stapling) and introduces a Homebrew cask that installs the notarized app while exposing the bundled CLI via a symlink, aligning distribution artifacts with Gatekeeper expectations.
Changes:
- Bundle the CLI into the Tauri desktop app via
externalBinand stage it in CI with a new sidecar build script. - Add scripts and workflow steps to sign and notarize macOS artifacts (CLI tarball, VSIX-bundled CLI, app/DMG), including DMG notarization + re-upload.
- Add/extend packaging automation and docs: Homebrew cask + Linux-only formula, and sync tooling to rewrite versions/digests from
SHA256SUMS.
Reviewed changes
Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
src-tauri/tauri.conf.json |
Declares the CLI as a bundled Tauri sidecar via externalBin. |
scripts/build-sidecar.sh |
Builds/stages the CLI in the filename/location Tauri expects for sidecars. |
scripts/macos-sign.sh |
Signs standalone macOS binaries (e.g., CLI + VSIX-bundled CLI) with Developer ID. |
scripts/macos-notarize.sh |
Submits signed artifacts to Apple notarization and staples tickets where applicable. |
scripts/sync-packaging.sh |
Extends packaging sync to also rewrite the Homebrew cask version/sha256 from SHA256SUMS. |
packaging/homebrew/Casks/mandalo.rb |
New Homebrew cask for the universal DMG; exposes bundled mandalo CLI via binary ... target:. |
packaging/homebrew/Formula/mandalo.rb |
Converts formula to Linux-only CLI distribution (depends_on :linux). |
packaging/README.md |
Updates publishing instructions for formula+cask and documents universal DMG requirement. |
docs/macos-signing.md |
Documents required secrets and the signing/notarization behavior and verification steps. |
.github/actions/apple-credentials/action.yml |
Composite action to gate signing and materialize the App Store Connect .p8 on disk. |
.github/workflows/release.yml |
Wires in sidecar staging; adds macOS signing/notarization (including DMG stapling + re-upload). |
.github/workflows/ci.yml |
Extends shellcheck coverage to the new scripts. |
.gitignore |
Ignores the staged sidecar output directory under src-tauri/binaries/. |
Suppressed comments (2)
scripts/macos-sign.sh:35
base64 --decodeis a GNU option and is not available on the default macOS/BSDbase64, so this will fail on macOS runners (the primary place this script runs). Use a portable decoder (e.g.,openssl base64 -d).
printf '%s' "$APPLE_CERTIFICATE" | base64 --decode > "$p12"
.github/actions/apple-credentials/action.yml:49
base64 --decodeis not portable on macOS (BSDbase64typically uses-D). Since this action runs on macOS runners, decoding the .p8 should use a portable method (e.g.,openssl base64 -d).
printf '%s' "$API_KEY_P8" | base64 --decode > "$key_path"
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+38
to
+45
| if [ -z "$CERTIFICATE" ] || [ -z "$API_KEY_P8" ]; then | ||
| if [ "$REQUIRED" = "true" ]; then | ||
| echo "::error::A tagged release must ship signed macOS artifacts, but APPLE_CERTIFICATE and/or APPLE_API_KEY_P8 are missing. See docs/macos-signing.md." | ||
| exit 1 | ||
| fi | ||
| echo "sign=false" >> "$GITHUB_OUTPUT" | ||
| echo "No Apple credentials — this run produces unsigned macOS artifacts." | ||
| exit 0 |
Comment on lines
+23
to
+26
| if [ ! -f "$keychain" ]; then | ||
| umask 077 | ||
| openssl rand -hex 24 > "$password_file" | ||
| password=$(cat "$password_file") |
src-tauri is in this workspace, so `cargo test --workspace` compiles it, and
its build script refuses to run while the externalBin it declares is missing:
resource path `binaries/mandalo-cli-x86_64-unknown-linux-gnu` doesn't exist
Build it in the dev profile rather than release-cli — the test run then reuses
those debug artifacts instead of paying for a second full compile of the CLI
and its dependency tree.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
macOS builds went out unsigned, so every download opened to "mandalo is damaged and can't be opened". This signs the
.app, the.dmg, the standalone CLI and the copy inside the darwin VSIXs with the Developer ID certificate, and notarizes all of them.Verified against Apple with the real certificate, not just linted:
.tar.gz.app(sidecar included)codesign --verify --deep --strictvalid,spctl→source=Notarized Developer ID.dmgspctl -a -t open→acceptedbrew style --caskcleanThree things a real build turned up
src-tauricrate. The bundled CLI ismandalo-cli, and the cask'starget:puts it back onPATHasmandalo. This would have failed all five desktop jobs on a tag..appbut not the.dmgcarrying it, and Gatekeeper assesses the downloaded disk image on its own:spctl -a -t openansweredUnnotarized Developer IDand refused it. Users would have hit the exact error this PR set out to remove. The release job now notarizes the.dmgand re-uploads the stapled copy beforepublishhashes it intoSHA256SUMS.pkg-config/libssl-dev/cmake, which building the CLI sidecar there needs.Behaviour
A tagged release fails loudly when the Apple credentials are missing rather than publishing artifacts Gatekeeper will reject. Other runs build unsigned as before.
externalBinis global, so the CLI now also travels inside the.msi,.deb,.rpmand AppImage (~13 MB each) without landing onPATHthere. Tauri has no per-platformexternalBin; splitting it needs a separate merged config.Homebrew
The app bundle carries the CLI, so one command installs both:
The formula becomes
depends_on :linux, matching thesixtysixtap — a formula without a bottle takes the build-from-source path, which demands Command Line Tools to install an already-compiled binary. The two can then never both providebin/mandalo, so noconflicts_withis needed. The trade-off: on macOS there is no brew route to the CLI alone; that isinstall.shor the tarball.Still blocked on
Both releases are Drafts. Until one is published, neither the cask nor the formula nor the updater nor
install.shresolves for a real user, andsync-packaging.shcannot readSHA256SUMSto fill the digests.Secrets are set:
APPLE_CERTIFICATE,APPLE_CERTIFICATE_PASSWORD,APPLE_SIGNING_IDENTITY,APPLE_API_KEY,APPLE_API_ISSUER,APPLE_API_KEY_P8. Seedocs/macos-signing.md.🤖 Generated with Claude Code