From b8d557e7c2fc4676fa20abb0efa2f2c436f59bdd Mon Sep 17 00:00:00 2001 From: Pedram Amini Date: Fri, 12 Dec 2025 18:27:06 -0600 Subject: [PATCH] Apple signed binaries --- .github/workflows/release.yml | 54 ++++++++-------- build/entitlements.mac.plist | 14 ++++ package-lock.json | 90 ++++++++++++-------------- package.json | 12 +++- scripts/notarize.js | 34 ++++++++++ src/renderer/components/AboutModal.tsx | 4 +- 6 files changed, 127 insertions(+), 81 deletions(-) create mode 100644 build/entitlements.mac.plist create mode 100644 scripts/notarize.js diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4a0a5e63..3f18fe75 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -109,38 +109,38 @@ jobs: run: mkdir -p release shell: bash + # Import Apple certificate for code signing + - name: Import Apple certificate + if: matrix.platform == 'mac' + env: + APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} + APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + run: | + # Create temporary keychain + KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + KEYCHAIN_PASSWORD=$(openssl rand -base64 32) + + # Create keychain + security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" + security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH" + security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" + + # Import certificate + echo "$APPLE_CERTIFICATE" | base64 --decode > $RUNNER_TEMP/certificate.p12 + security import $RUNNER_TEMP/certificate.p12 -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k "$KEYCHAIN_PATH" + security list-keychain -d user -s "$KEYCHAIN_PATH" + + # Allow codesign to access the key + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" + - name: Package for macOS if: matrix.platform == 'mac' run: npx electron-builder --mac --publish never --config.extraMetadata.version=${{ steps.version.outputs.VERSION }} env: - CSC_IDENTITY_AUTO_DISCOVERY: false - CSC_LINK: "" DEBUG: electron-builder - - # Ad-hoc sign macOS apps and re-create archives - # Fixes "code has no resources but signature indicates they must be present" - - name: Ad-hoc sign macOS apps - if: matrix.platform == 'mac' - run: | - VERSION=${{ steps.version.outputs.VERSION }} - - # Sign x64 app and recreate zip - if [ -d "release/mac/Maestro.app" ]; then - echo "Ad-hoc signing: release/mac/Maestro.app" - codesign --sign - --deep --force "release/mac/Maestro.app" - echo "Re-creating ZIP for x64..." - rm -f "release/Maestro-${VERSION}-mac.zip" - cd release/mac && zip -r -y "../Maestro-${VERSION}-mac.zip" Maestro.app && cd ../.. - fi - - # Sign arm64 app and recreate zip - if [ -d "release/mac-arm64/Maestro.app" ]; then - echo "Ad-hoc signing: release/mac-arm64/Maestro.app" - codesign --sign - --deep --force "release/mac-arm64/Maestro.app" - echo "Re-creating ZIP for arm64..." - rm -f "release/Maestro-${VERSION}-arm64-mac.zip" - cd release/mac-arm64 && zip -r -y "../Maestro-${VERSION}-arm64-mac.zip" Maestro.app && cd ../.. - fi + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} - name: Package for Windows if: matrix.platform == 'win' diff --git a/build/entitlements.mac.plist b/build/entitlements.mac.plist new file mode 100644 index 00000000..d29ffd47 --- /dev/null +++ b/build/entitlements.mac.plist @@ -0,0 +1,14 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.disable-library-validation + + com.apple.security.device.audio-input + + + diff --git a/package-lock.json b/package-lock.json index a72e6410..98d865bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,14 @@ { "name": "maestro", - "version": "0.7.0", + "version": "0.8.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "maestro", - "version": "0.7.0", + "version": "0.8.0", "hasInstallScript": true, - "license": "MIT", + "license": "AGPL 3.0", "dependencies": { "@emoji-mart/data": "^1.2.1", "@emoji-mart/react": "^1.1.1", @@ -42,6 +42,7 @@ "maestro-cli": "dist/cli/maestro-cli.js" }, "devDependencies": { + "@electron/notarize": "^3.1.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.0", "@types/adm-zip": "^0.5.7", @@ -755,57 +756,17 @@ } }, "node_modules/@electron/notarize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.2.1.tgz", - "integrity": "sha512-aL+bFMIkpR0cmmj5Zgy0LMKEpgy43/hw5zadEArgmAMWWlKc5buwFvFT9G/o/YJkvXAJm5q3iuTuLaiaXW39sg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-3.1.1.tgz", + "integrity": "sha512-uQQSlOiJnqRkTL1wlEBAxe90nVN/Fc/hEmk0bqpKk8nKjV1if/tXLHKUPePtv9Xsx90PtZU8aidx5lAiOpjkQQ==", "dev": true, "license": "MIT", "dependencies": { - "debug": "^4.1.1", - "fs-extra": "^9.0.1", + "debug": "^4.4.0", "promise-retry": "^2.0.1" }, "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@electron/notarize/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@electron/notarize/node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/notarize/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" + "node": ">= 22.12.0" } }, "node_modules/@electron/osx-sign": { @@ -3448,6 +3409,37 @@ "electron-builder-squirrel-windows": "24.13.3" } }, + "node_modules/app-builder-lib/node_modules/@electron/notarize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.2.1.tgz", + "integrity": "sha512-aL+bFMIkpR0cmmj5Zgy0LMKEpgy43/hw5zadEArgmAMWWlKc5buwFvFT9G/o/YJkvXAJm5q3iuTuLaiaXW39sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.1", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/notarize/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/app-builder-lib/node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -8510,7 +8502,6 @@ "integrity": "sha512-454TI39PeRDW1LgpyLPyURtB4Zx1tklSr6+OFOipsxGUH1WMTvk6C65JQdrj455+DP2uJ1+veBEHTGFKWVLFoA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@acemir/cssom": "^0.9.23", "@asamuzakjp/dom-selector": "^6.7.4", @@ -14160,6 +14151,7 @@ "integrity": "sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vitest/expect": "4.0.15", "@vitest/mocker": "4.0.15", diff --git a/package.json b/package.json index f562f2ab..29837798 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "maestro", - "version": "0.8.0", + "version": "0.8.1", "description": "Run AI coding agents autonomously for days.", "main": "dist/main/index.js", "author": { @@ -10,7 +10,7 @@ "license": "AGPL 3.0", "repository": { "type": "git", - "url": "https://github.com/yourusername/maestro.git" + "url": "https://github.com/pedramamini/maestro.git" }, "bin": { "maestro-cli": "./dist/cli/maestro-cli.js" @@ -51,7 +51,11 @@ ], "mac": { "category": "public.app-category.developer-tools", - "identity": null, + "hardenedRuntime": true, + "gatekeeperAssess": false, + "notarize": false, + "entitlements": "build/entitlements.mac.plist", + "entitlementsInherit": "build/entitlements.mac.plist", "target": [ { "target": "dmg", @@ -76,6 +80,7 @@ } ] }, + "afterSign": "scripts/notarize.js", "win": { "target": [ { @@ -149,6 +154,7 @@ "ws": "^8.16.0" }, "devDependencies": { + "@electron/notarize": "^3.1.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.0", "@types/adm-zip": "^0.5.7", diff --git a/scripts/notarize.js b/scripts/notarize.js new file mode 100644 index 00000000..1969cb39 --- /dev/null +++ b/scripts/notarize.js @@ -0,0 +1,34 @@ +const { notarize } = require('@electron/notarize'); + +exports.default = async function notarizing(context) { + const { electronPlatformName, appOutDir } = context; + + // Only notarize macOS builds + if (electronPlatformName !== 'darwin') { + return; + } + + // Skip if not signing (CI without credentials, etc.) + if (process.env.CSC_IDENTITY_AUTO_DISCOVERY === 'false') { + console.log('Skipping notarization: CSC_IDENTITY_AUTO_DISCOVERY is false'); + return; + } + + const appName = context.packager.appInfo.productFilename; + const appPath = `${appOutDir}/${appName}.app`; + + console.log(`Notarizing ${appPath}...`); + + try { + await notarize({ + appPath, + appleId: process.env.APPLE_ID, + appleIdPassword: process.env.APPLE_APP_SPECIFIC_PASSWORD, + teamId: process.env.APPLE_TEAM_ID, + }); + console.log('Notarization complete!'); + } catch (error) { + console.error('Notarization failed:', error); + throw error; + } +}; diff --git a/src/renderer/components/AboutModal.tsx b/src/renderer/components/AboutModal.tsx index cf47cb73..ded8e1a3 100644 --- a/src/renderer/components/AboutModal.tsx +++ b/src/renderer/components/AboutModal.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState, useCallback } from 'react'; -import { X, Wand2, ExternalLink, FileCode, BarChart3, Loader2, Trophy, Globe } from 'lucide-react'; +import { X, Wand2, ExternalLink, FileCode, BarChart3, Loader2, Trophy, Globe, Check } from 'lucide-react'; import type { Theme, Session, AutoRunStats } from '../types'; import { useLayerStack } from '../contexts/LayerStackContext'; import { MODAL_PRIORITIES } from '../constants/modalPriorities'; @@ -329,7 +329,7 @@ export function AboutModal({ theme, sessions, autoRunStats, onClose, onOpenLeade {isLeaderboardRegistered ? ( - Active + ) : ( )}