Skip to content

feat: Add macOS code signing and notarization#14

Merged
tphakala merged 3 commits into
mainfrom
feat/macos-code-signing
Feb 15, 2026
Merged

feat: Add macOS code signing and notarization#14
tphakala merged 3 commits into
mainfrom
feat/macos-code-signing

Conversation

@tphakala
Copy link
Copy Markdown
Owner

@tphakala tphakala commented Feb 15, 2026

Summary

  • Configure GitHub Actions to sign macOS builds with Apple Developer ID certificate
  • Enable automatic notarization for macOS releases
  • Add required entitlements file for Electron security requirements
  • Remove "not code-signed" warning from release notes

Changes

Workflow Updates (.github/workflows/release.yml)

  • Certificate import: Creates temporary keychain, decodes and imports Developer ID certificate
  • Environment variables: Added APPLE_TEAM_ID, APPLE_ID, and APPLE_APP_PASSWORD for notarization
  • Cleanup step: Removes temporary keychain after build (even on failure)

electron-builder Configuration (package.json)

  • hardenedRuntime: true - Required for modern macOS security
  • gatekeeperAssess: false - Disables local assessment (CI builds are already signed)
  • entitlements configuration - Points to entitlements file
  • notarize configuration - Enables automatic notarization with Team ID

Entitlements File (build/entitlements.mac.plist)

New file with required permissions for Electron apps:

  • JIT compilation support
  • Unsigned executable memory (required for V8)
  • Dynamic library loading
  • DYLD environment variables

Required GitHub Secrets

The following secrets must be configured in repository settings:

  • APPLE_CERTIFICATE_BASE64 - Developer ID certificate (base64 encoded)
  • APPLE_CERTIFICATE_PASSWORD - Certificate password
  • APPLE_TEAM_ID - Apple Team ID
  • APPLE_ID - Apple ID email
  • APPLE_APP_SPECIFIC_PASSWORD - App-specific password for notarization

Testing

This can only be fully tested on the next release. The workflow will:

  1. Sign the macOS app with the Developer ID certificate
  2. Submit the signed app to Apple's notarization service
  3. Staple the notarization ticket to the app
  4. Upload the signed and notarized DMG to the GitHub release

User Impact

Users will no longer see Gatekeeper warnings when opening the macOS version of Birda GUI. The app will install and run without requiring "Open anyway" or right-click workarounds.

Summary by CodeRabbit

  • Chores
    • Enhanced macOS build pipeline with app signing and notarization support to enable compliant macOS distribution.
    • Added macOS security entitlements to improve runtime permissions for the app.
    • Introduced temporary credential handling and cleanup during macOS builds.
    • Removed an outdated macOS Gatekeeper note from release notes.

- Add certificate import and keychain setup in release workflow
- Configure electron-builder with hardenedRuntime and entitlements
- Enable notarization with Apple Developer ID
- Add macOS entitlements file for Electron security requirements
- Remove "not code-signed" warning from release notes

The macOS build will now be signed with Apple Developer ID certificate
and automatically notarized, eliminating Gatekeeper warnings for users.
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @tphakala, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the macOS build process by integrating Apple's code signing and notarization requirements. The changes ensure that the application is properly signed with a Developer ID certificate and automatically notarized, which will prevent Gatekeeper warnings and provide a smoother installation experience for macOS users.

Highlights

  • GitHub Actions Configuration: Configured GitHub Actions to sign macOS builds with an Apple Developer ID certificate and enable automatic notarization for releases.
  • Electron Builder Configuration: Updated electron-builder settings in package.json to enable hardened runtime, disable local Gatekeeper assessment, and integrate with the new entitlements file and notarization process.
  • macOS Entitlements: Introduced a new build/entitlements.mac.plist file, defining required permissions for Electron applications, such as JIT compilation and dynamic library loading.
  • User Experience: Eliminated Gatekeeper warnings for macOS users by ensuring the application is properly signed and notarized, improving the installation and launch experience.
Changelog
  • build/entitlements.mac.plist
    • Added a new entitlements file.
  • package.json
    • Enabled hardenedRuntime for macOS builds.
    • Disabled gatekeeperAssess.
    • Configured entitlements and entitlementsInherit to point to the new entitlements file.
    • Added notarize configuration with teamId.
Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/release.yml
Activity
  • No human activity (comments, reviews, etc.) has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 15, 2026

Warning

Rate limit exceeded

@tphakala has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 19 minutes and 0 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

Walkthrough

Adds macOS code signing and notarization to CI and build config: CI workflow steps to create/import a temporary keychain, codesign and clean up; a new macOS entitlements plist; and package.json updates to enable hardened runtime, entitlements, and notarization metadata.

Changes

Cohort / File(s) Summary
CI/CD Release Workflow
​.github/workflows/release.yml
Added macOS code-signing steps: create temporary keychain, import Apple certificate, set codesign permissions, set keychain default, pass Apple credentials to macOS Package step conditionally, and remove keychain in cleanup. Removed post-release Gatekeeper note.
macOS Entitlements
build/entitlements.mac.plist
Added new entitlements plist enabling: com.apple.security.cs.allow-jit, com.apple.security.cs.allow-unsigned-executable-memory, and com.apple.security.cs.disable-library-validation.
macOS build metadata
package.json
Updated electron-builder mac target: enabled hardenedRuntime, disabled gatekeeperAssess, set entitlements and entitlementsInherit paths, and added a notarization block with teamId placeholder.

Sequence Diagram(s)

sequenceDiagram
    participant CI as CI Workflow
    participant Keychain as Temporary Keychain
    participant Codesign as codesign / electron-builder
    participant Apple as Apple Notary/ID
    participant Repo as Release artifact store

    CI->>Keychain: create temporary keychain & import cert
    CI->>Codesign: invoke package step with APPLE_* creds
    Codesign->>Keychain: access private key for signing
    Codesign->>Apple: notarize uploaded artifact (teamId, app password)
    Apple-->>Codesign: notarization result
    Codesign->>Keychain: remove imported cert
    CI->>Repo: publish signed/notarized release
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately and concisely describes the primary objective: adding macOS code signing and notarization functionality.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/macos-code-signing

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces macOS code signing and notarization, which is a great improvement for macOS users. However, the build/entitlements.mac.plist file includes highly permissive flags such as allow-dyld-environment-variables and disable-library-validation that weaken macOS Hardened Runtime protections, increasing the risk of library injection attacks. It is recommended to review these entitlements and remove any that are not strictly necessary for the application's core functionality. Additionally, there is a minor suggestion to simplify the configuration in package.json by removing a redundant property.

Comment thread build/entitlements.mac.plist Outdated
Comment on lines +9 to +12
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The entitlements com.apple.security.cs.allow-dyld-environment-variables and com.apple.security.cs.disable-library-validation are enabled.

  • com.apple.security.cs.allow-dyld-environment-variables (lines 9-10) allows the application to be influenced by DYLD_* environment variables, which can be used to inject malicious libraries into the application's process. This is rarely required for production applications and significantly weakens the Hardened Runtime protections.
  • com.apple.security.cs.disable-library-validation (lines 11-12) allows the application to load any signed library, even if not signed by the same developer or Apple. This should only be enabled if the application must load third-party native modules that cannot be signed with your own certificate.

Unless these are strictly required for the application to function in production, they should be removed to maintain a stronger security posture and adhere to the Principle of Least Privilege.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partially correct.

allow-dyld-environment-variables: Agree - removing this (not needed, security risk).

disable-library-validation: Required for this app. We use better-sqlite3, a native Node.js addon that requires this entitlement to load properly. Without it, macOS will reject the native module during hardened runtime. This is standard practice for Electron apps with native dependencies (electron-builder #4616, LM Studio #1494).

Keeping allow-jit, allow-unsigned-executable-memory, and disable-library-validation. Removing only allow-dyld-environment-variables.

Comment thread package.json
"hardenedRuntime": true,
"gatekeeperAssess": false,
"entitlements": "build/entitlements.mac.plist",
"entitlementsInherit": "build/entitlements.mac.plist",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The entitlementsInherit property is redundant here. According to the electron-builder documentation, entitlementsInherit defaults to the value of entitlements if not specified. Since you are setting it to the same value, you can remove this line to simplify the configuration.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not redundant - these serve different purposes:

  • entitlements: Applied to the main app binary
  • entitlementsInherit: Applied to child processes, frameworks, and bundles

Using the same file for both is intentional (we want consistent permissions across all processes). The default is build/entitlements.mac.inherit.plist, not the value of entitlements (electron-builder docs).

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @.github/workflows/release.yml:
- Around line 142-144: The workflow sets the wrong environment variable name for
electron-builder notarization: replace the APPLE_APP_PASSWORD key with
APPLE_APP_SPECIFIC_PASSWORD so the notarize integration sees the expected
variable; update the environment mapping where matrix.platform is checked (the
entries using APPLE_TEAM_ID and APPLE_ID) to export APPLE_APP_SPECIFIC_PASSWORD
instead of APPLE_APP_PASSWORD so the notarization step
(electron-builder/@electron/notarize) receives the correct secret.

In `@package.json`:
- Around line 77-79: Replace the deprecated object form of the notarize config
in package.json (the "notarize": { "teamId": "${APPLE_TEAM_ID}" } entry) with
the boolean form expected by newer electron-builder versions by setting
"notarize": true and relying on the APPLE_TEAM_ID environment variable at build
time; update any build docs or CI to ensure APPLE_TEAM_ID is exported in the
environment before running electron-builder so the notarization step can read
it.
🧹 Nitpick comments (1)
.github/workflows/release.yml (1)

109-133: Keychain setup follows a solid pattern overall.

The certificate import flow is well-structured: temporary keychain with scoped lifetime, proper partition-list configuration, and prompt cleanup of the .p12 file.

One minor note on Line 130: security list-keychain -d user -s "$KEYCHAIN_PATH" replaces the entire user keychain search list, dropping the default login keychain. On ephemeral CI runners this is usually fine, but if any downstream tool needs the login keychain (e.g., for Apple root CAs), it can cause subtle failures. The safer pattern prepends:

Safer keychain list management
-          security list-keychain -d user -s "$KEYCHAIN_PATH"
+          security list-keychain -d user -s "$KEYCHAIN_PATH" $(security list-keychain -d user | tr -d '"')

Comment thread .github/workflows/release.yml Outdated
Comment thread package.json Outdated
Removed com.apple.security.cs.allow-dyld-environment-variables as it's
not required for production and presents a security risk (allows DYLD_*
environment variable injection).

Kept the following entitlements required for Electron + native modules:
- allow-jit: Required for V8 JavaScript engine
- allow-unsigned-executable-memory: Required for V8 JIT compilation
- disable-library-validation: Required for better-sqlite3 native module
@tphakala
Copy link
Copy Markdown
Owner Author

Review Response Summary

Implemented:

  • ✅ Removed allow-dyld-environment-variables entitlement (security risk, not needed)

Not implemented (with reasoning):

  • ❌ Removing disable-library-validation: Required for better-sqlite3 native module to load under hardened runtime
  • ❌ Removing entitlementsInherit: Not redundant - applies to child processes/frameworks (different from entitlements which applies to main binary)

Final entitlements:

  • allow-jit - Required for V8 JavaScript engine
  • allow-unsigned-executable-memory - Required for V8 JIT compilation
  • disable-library-validation - Required for native Node.js addons (better-sqlite3)

See inline comment threads for technical details and sources.

Comment thread .github/workflows/release.yml Outdated
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
APPLE_TEAM_ID: ${{ matrix.platform == 'macos' && secrets.APPLE_TEAM_ID || '' }}
APPLE_ID: ${{ matrix.platform == 'macos' && secrets.APPLE_ID || '' }}
APPLE_APP_PASSWORD: ${{ matrix.platform == 'macos' && secrets.APPLE_APP_SPECIFIC_PASSWORD || '' }}

This comment was marked as outdated.

- Changed APPLE_APP_PASSWORD to APPLE_APP_SPECIFIC_PASSWORD (correct env var name expected by electron-builder)
- Changed notarize config from deprecated object form to boolean (electron-builder v26+ requirement)

The object form of notarize config was deprecated in v25 and removed in v26.
Modern electron-builder expects 'notarize: true' with APPLE_TEAM_ID provided
via environment variable.
@tphakala tphakala merged commit 00cea28 into main Feb 15, 2026
5 checks passed
@tphakala tphakala deleted the feat/macos-code-signing branch February 15, 2026 10:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant