Fix macOS notarization failures for production and nightly builds#371
Fix macOS notarization failures for production and nightly builds#371Power-Maverick merged 4 commits intodevfrom
Conversation
Co-authored-by: Power-Maverick <36135520+Power-Maverick@users.noreply.github.com>
|
@copilot does this change apply to nightly build |
Co-authored-by: Power-Maverick <36135520+Power-Maverick@users.noreply.github.com>
Yes, I've now applied the same fixes to the nightly build workflow (
Both production and nightly builds will now have proper notarization support. Commit: e840196 |
Bundle Size Report 📦
Bundle Analysis ReportsThe detailed bundle analysis reports are available in the workflow artifacts:
Download the artifacts from the workflow run to view interactive visualizations. Bundle size tracking is now active! This helps prevent bundle bloat. |
There was a problem hiding this comment.
Pull request overview
This PR updates the macOS signing/notarization configuration and GitHub Actions release workflows to address recurring notarization failures in the Electron build pipeline.
Changes:
- Adjust macOS electron-builder config to disable Gatekeeper assessment during build and ensure all binaries are signed.
- Add a macOS entitlement intended to support signing of specific third-party frameworks (Squirrel).
- Enhance stable and nightly release workflows with certificate visibility checks and expanded pre-notarization signature/timestamp verification.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 9 comments.
| File | Description |
|---|---|
| buildScripts/entitlements.mac.plist | Adds an entitlement affecting hardened runtime behavior to support framework signing requirements. |
| buildScripts/electron-builder-mac.json | Tweaks macOS signing configuration (Gatekeeper assessment + signing coverage). |
| .github/workflows/prod-release.yml | Adds certificate/verification steps and expands pre-notarization checks for macOS release packaging. |
| .github/workflows/nightly-release.yml | Mirrors the macOS release workflow verification/signing changes for nightly builds. |
| # Check dylibs | ||
| echo "" | ||
| echo "=== Checking dynamic libraries ===" | ||
| find "$APP_PATH" -name "*.dylib" -type f | head -5 | while read dylib; do | ||
| echo "Checking: $(basename "$dylib")" | ||
| codesign --verify --strict "$dylib" 2>&1 || echo "❌ Failed: $dylib" | ||
| done |
There was a problem hiding this comment.
The find ... | head -5 | while read dylib; do loop has two issues: (1) read will split paths on spaces (the app path contains spaces), so codesign may run on invalid truncated paths; and (2) head -5 means only 5 dylibs are verified, which undermines the goal of catching notarization failures early. Prefer a safe read method (e.g., -print0 + read -r -d '') and verify all dylibs (or clearly document why sampling is acceptable).
| echo "=== Verifying critical nested binaries ===" | ||
| # Check helper apps | ||
| for helper in "$APP_PATH/Contents/Frameworks/"*.app; do | ||
| if [ -d "$helper" ]; then | ||
| echo "Checking helper: $(basename "$helper")" | ||
| codesign --verify --strict "$helper" 2>&1 || echo "❌ Failed: $helper" | ||
| codesign -dvvv "$helper" 2>&1 | grep -i timestamp || echo "❌ No timestamp: $helper" | ||
| fi | ||
| done |
There was a problem hiding this comment.
This verification step masks failures for nested items by using || echo ... after codesign --verify and timestamp checks in loops. That means the job can pass even if a helper/framework/dylib is unsigned or missing timestamps. Consider collecting failures and exiting non-zero at the end (or fail immediately) so notarization problems are reliably caught pre-submit.
| - name: Verify certificate details | ||
| if: matrix.os == 'macos-latest' | ||
| shell: bash | ||
| run: | | ||
| echo "=== Checking available signing identities ===" | ||
| security find-identity -v -p codesigning | ||
| echo "" | ||
| echo "Note: Should show 'Developer ID Application' not 'Mac App Distribution'" |
There was a problem hiding this comment.
This step runs security find-identity before any certificate is imported into the keychain (the workflow only sets CSC_LINK/CSC_KEY_PASSWORD; electron-builder typically imports during packaging). As a result it may show no relevant identities and still won’t block misconfiguration. Consider importing the P12 into a temporary keychain for this check or inspecting the P12 directly, and fail the step if a "Developer ID Application" identity isn’t available.
| echo "=== Verifying critical nested binaries ===" | ||
| # Check helper apps | ||
| for helper in "$APP_PATH/Contents/Frameworks/"*.app; do | ||
| if [ -d "$helper" ]; then | ||
| echo "Checking helper: $(basename "$helper")" | ||
| codesign --verify --strict "$helper" 2>&1 || echo "❌ Failed: $helper" | ||
| codesign -dvvv "$helper" 2>&1 | grep -i timestamp || echo "❌ No timestamp: $helper" | ||
| fi | ||
| done |
There was a problem hiding this comment.
This verification step masks failures for nested items by using || echo ... after codesign --verify and timestamp checks in loops. That means the job can pass even if a helper/framework/dylib is unsigned or missing timestamps. Consider collecting failures and exiting non-zero at the end (or fail immediately) so notarization problems are reliably caught pre-submit.
| - name: Verify certificate details | ||
| if: matrix.os == 'macos-latest' | ||
| shell: bash | ||
| run: | | ||
| echo "=== Checking available signing identities ===" | ||
| security find-identity -v -p codesigning | ||
| echo "" | ||
| echo "Note: Should show 'Developer ID Application' not 'Mac App Distribution'" |
There was a problem hiding this comment.
This step runs security find-identity before any certificate is imported into the keychain (the workflow only sets CSC_LINK/CSC_KEY_PASSWORD; electron-builder typically imports during packaging). As a result it may show no relevant identities and still won’t block misconfiguration. Consider importing the P12 into a temporary keychain for this check or inspecting the P12 directly, and fail the step if a "Developer ID Application" identity isn’t available.
| find "$APP_PATH/Contents/Frameworks" -name "*.framework" -type d -maxdepth 1 | while read framework; do | ||
| echo "Checking: $(basename "$framework")" | ||
| codesign --verify --strict "$framework" 2>&1 || echo "❌ Failed: $framework" | ||
| done | ||
|
|
||
| # Check dylibs | ||
| echo "" | ||
| echo "=== Checking dynamic libraries ===" | ||
| find "$APP_PATH" -name "*.dylib" -type f | head -5 | while read dylib; do | ||
| echo "Checking: $(basename "$dylib")" | ||
| codesign --verify --strict "$dylib" 2>&1 || echo "❌ Failed: $dylib" | ||
| done | ||
|
|
There was a problem hiding this comment.
The find ... | while read framework; do loop will break on paths containing spaces (the app bundle name has spaces), causing framework to be truncated and codesign to run on the wrong path. Use a NUL-delimited pipeline (e.g., find ... -print0 + while IFS= read -r -d '' framework) or otherwise ensure paths with spaces are handled correctly.
| find "$APP_PATH/Contents/Frameworks" -name "*.framework" -type d -maxdepth 1 | while read framework; do | |
| echo "Checking: $(basename "$framework")" | |
| codesign --verify --strict "$framework" 2>&1 || echo "❌ Failed: $framework" | |
| done | |
| # Check dylibs | |
| echo "" | |
| echo "=== Checking dynamic libraries ===" | |
| find "$APP_PATH" -name "*.dylib" -type f | head -5 | while read dylib; do | |
| echo "Checking: $(basename "$dylib")" | |
| codesign --verify --strict "$dylib" 2>&1 || echo "❌ Failed: $dylib" | |
| done | |
| find "$APP_PATH/Contents/Frameworks" -maxdepth 1 -type d -name "*.framework" -print0 | while IFS= read -r -d '' framework; do | |
| echo "Checking: $(basename "$framework")" | |
| codesign --verify --strict "$framework" 2>&1 || echo "❌ Failed: $framework" | |
| done | |
| # Check dylibs | |
| echo "" | |
| echo "=== Checking dynamic libraries ===" | |
| find "$APP_PATH" -name "*.dylib" -type f -print0 | { | |
| count=0 | |
| while IFS= read -r -d '' dylib; do | |
| echo "Checking: $(basename "$dylib")" | |
| codesign --verify --strict "$dylib" 2>&1 || echo "❌ Failed: $dylib" | |
| count=$((count + 1)) | |
| if [ "$count" -ge 5 ]; then | |
| break | |
| fi | |
| done | |
| } | |
| # Check frameworks | ||
| echo "" | ||
| echo "=== Checking frameworks ===" | ||
| find "$APP_PATH/Contents/Frameworks" -name "*.framework" -type d -maxdepth 1 | while read framework; do |
There was a problem hiding this comment.
The find ... | while read framework; do loop will break on paths containing spaces (the app bundle name has spaces), causing framework to be truncated and codesign to run on the wrong path. Use a NUL-delimited pipeline (e.g., find ... -print0 + while IFS= read -r -d '' framework) or otherwise ensure paths with spaces are handled correctly.
| find "$APP_PATH/Contents/Frameworks" -name "*.framework" -type d -maxdepth 1 | while read framework; do | |
| find "$APP_PATH/Contents/Frameworks" -maxdepth 1 -type d -name "*.framework" -print0 | while IFS= read -r -d '' framework; do |
| # Check dylibs | ||
| echo "" | ||
| echo "=== Checking dynamic libraries ===" | ||
| find "$APP_PATH" -name "*.dylib" -type f | head -5 | while read dylib; do | ||
| echo "Checking: $(basename "$dylib")" | ||
| codesign --verify --strict "$dylib" 2>&1 || echo "❌ Failed: $dylib" | ||
| done |
There was a problem hiding this comment.
The find ... | head -5 | while read dylib; do loop has two issues: (1) read will split paths on spaces (the app path contains spaces), so codesign may run on invalid truncated paths; and (2) head -5 means only 5 dylibs are verified, which undermines the goal of catching notarization failures early. Prefer a safe read method (e.g., -print0 + read -r -d '') and verify all dylibs (or clearly document why sampling is acceptable).
| <key>com.apple.security.cs.allow-dyld-environment-variables</key> | ||
| <true/> |
There was a problem hiding this comment.
Adding com.apple.security.cs.allow-dyld-environment-variables weakens the hardened runtime by allowing DYLD_* environment variables (commonly restricted to prevent injection). If this is strictly required, consider documenting why in the plist and confirming it’s the minimal entitlement needed; otherwise, avoid enabling it globally for the whole app bundle.
| <key>com.apple.security.cs.allow-dyld-environment-variables</key> | |
| <true/> |
Bundle Size Report 📦
Bundle Analysis ReportsThe detailed bundle analysis reports are available in the workflow artifacts:
Download the artifacts from the workflow run to view interactive visualizations. Bundle size tracking is now active! This helps prevent bundle bloat. |
Description
macOS notarization failing with three critical errors: missing secure timestamps on all binaries (#3087733), certificate/signing validation failures (#3087721), and unsigned third-party frameworks (#3087724). This PR fixes these issues for both production releases and nightly insider builds.
Changes
Build Configuration (
buildScripts/electron-builder-mac.json)gatekeeperAssess: false- defer validation to Apple notarization servicesignIgnore: []- ensure all nested frameworks/dylibs are signedEntitlements (
buildScripts/entitlements.mac.plist)com.apple.security.cs.allow-dyld-environment-variables- required for Squirrel.framework auto-updaterCI Workflow (
.github/workflows/prod-release.yml)Pre-build verification:
security find-identity) to catch wrong cert type earlyPackaging environment:
CSC_FOR_PULL_REQUEST: trueandCSC_IDENTITY_AUTO_DISCOVERY: true- ensure electron-builder discovers Developer ID certPost-build verification:
CI Workflow (
.github/workflows/nightly-release.yml)Applied identical fixes to nightly build workflow:
Pre-build verification:
security find-identity) to catch wrong cert type earlyPackaging environment:
CSC_FOR_PULL_REQUEST: trueandCSC_IDENTITY_AUTO_DISCOVERY: true- ensure electron-builder discovers Developer ID certPost-build verification:
Testing Notes
When workflow runs, monitor:
Both production and nightly builds will benefit from these fixes.
Original prompt
Fix macOS Notarization Failures
Problem Summary
The macOS notarization process is failing for v1.1.2 with multiple errors from Apple's notarization service. Analysis of the GitHub Actions failure logs reveals three critical issues that must be addressed together:
Critical Issues Identified
Missing Secure Timestamps (Error #3087733)
Invalid Certificate or Signing Issues (Error #3087721)
Third-party Frameworks Not Properly Signed (Error #3087724)
Required Changes
1. Update
buildScripts/electron-builder-mac.jsonCurrent configuration has issues. Update to:
{ "extends": "buildScripts/electron-builder-base.json", "forceCodeSigning": true, "mac": { "target": [ { "target": "zip", "arch": ["x64", "arm64"] }, { "target": "dmg", "arch": ["x64", "arm64"] } ], "category": "public.app-category.developer-tools", "icon": "icons/icon.icns", "identity": "Developer ID Application", "gatekeeperAssess": false, "hardenedRuntime": true, "entitlements": "./buildScripts/entitlements.mac.plist", "entitlementsInherit": "./buildScripts/entitlements.mac.plist", "type": "distribution", "notarize": false, "signIgnore": [] }, "dmg": { "sign": true } }Key changes:
gatekeeperAssess: false → Let Apple handle this during notarizationsignIgnore: []→ Ensure nothing is skipped during signing2. Update
buildScripts/entitlements.mac.plistAdd missing entitlement for Squirrel framework:
Key addition:
com.apple.security.cs.allow-dyld-environment-variables→ Required for Squirrel auto-updater framework3. Update
.github/workflows/prod-release.ymlAdd certificate verification step (after line 171, before "Package application (macOS)"):
Update the "Package application (macOS)" step environment variables:
Add these environment variables to ensure proper signing with timestamps:
Key additions:
CSC_FOR_PULL_REQUEST: true→ Enables certificate signing in PR contextCSC_IDENTITY_AUTO_DISCOVERY: true→ Ensures electron...This pull request was created from Copilot chat.
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.