Skip to content

Commit 7d7fbcc

Browse files
Copilotlmangani
andcommitted
feat: replace Apple ID signing with ad-hoc codesign (no Apple account required)
Co-authored-by: lmangani <1423657+lmangani@users.noreply.github.com>
1 parent 86ecbe8 commit 7d7fbcc

2 files changed

Lines changed: 164 additions & 25 deletions

File tree

.github/workflows/build-mac-dmg.yml

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -54,39 +54,55 @@ jobs:
5454
- name: Build frontend
5555
run: pnpm run build:frontend
5656

57-
- name: Build macOS DMG
57+
- name: Build macOS app bundle (unsigned)
58+
# Build the unpacked .app only — electron-builder signing is disabled entirely.
59+
# Ad-hoc signing is applied in the next step via build/macos/codesign.sh.
60+
id: build-app
61+
env:
62+
CSC_IDENTITY_AUTO_DISCOVERY: "false"
63+
run: |
64+
pnpm exec electron-builder --mac --dir \
65+
--config.mac.notarize=false \
66+
--config.publish.owner=${{ github.repository_owner }} \
67+
--config.publish.repo=${{ github.event.repository.name }}
68+
69+
APP_PATH=$(find release/mac-* -name "*.app" -maxdepth 1 | head -1)
70+
if [ -z "$APP_PATH" ]; then
71+
echo "Error: No .app bundle found in release/"
72+
exit 1
73+
fi
74+
echo "app_path=$APP_PATH" >> "$GITHUB_OUTPUT"
75+
76+
- name: Ad-hoc code sign the app bundle
77+
# Signs all Mach-O binaries (Python natives, Electron frameworks, executables)
78+
# and the app bundle using identity "-" (ad-hoc / self-signed).
79+
# No Apple ID, no certificate, no notarization required.
80+
# To use a real Developer ID certificate, set the MACOS_SIGNING_IDENTITY secret.
81+
run: |
82+
chmod +x build/macos/codesign.sh
83+
./build/macos/codesign.sh "${{ steps.build-app.outputs.app_path }}"
84+
env:
85+
MACOS_SIGNING_IDENTITY: ${{ secrets.MACOS_SIGNING_IDENTITY || '-' }}
86+
87+
- name: Create DMG from signed app
88+
# Packages the signed .app into a DMG using the electron-builder.yml layout.
89+
# --prepackaged skips the build phase; electron-builder only creates the DMG.
90+
# CSC_IDENTITY_AUTO_DISCOVERY=false prevents any re-signing attempt.
5891
env:
5992
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
60-
CSC_LINK: ${{ secrets.CSC_LINK }}
61-
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
62-
APPLE_ID: ${{ secrets.APPLE_ID }}
63-
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
64-
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
93+
CSC_IDENTITY_AUTO_DISCOVERY: "false"
6594
run: |
66-
# Publish to the GitHub Release only when triggered by a release event.
67-
# For workflow_dispatch, the DMG is available as a workflow artifact below.
6895
PUBLISH_MODE="never"
6996
if [ "${{ github.event_name }}" = "release" ]; then
7097
PUBLISH_MODE="always"
7198
fi
7299
73-
# Build electron-builder argument list
74-
BUILD_ARGS=(
75-
"--mac"
76-
"--publish" "$PUBLISH_MODE"
77-
"--config.publish.owner=${{ github.repository_owner }}"
78-
"--config.publish.repo=${{ github.event.repository.name }}"
79-
)
80-
81-
# Skip code signing and notarization when secrets are not configured.
82-
# This allows unsigned DMG builds without requiring an Apple Developer account.
83-
if [ -z "$CSC_LINK" ] || [ -z "$APPLE_ID" ]; then
84-
echo "::notice::Code signing secrets not configured — building unsigned DMG"
85-
export CSC_IDENTITY_AUTO_DISCOVERY=false
86-
BUILD_ARGS+=("--config.mac.notarize=false")
87-
fi
88-
89-
pnpm exec electron-builder "${BUILD_ARGS[@]}"
100+
pnpm exec electron-builder --mac dmg \
101+
--prepackaged "${{ steps.build-app.outputs.app_path }}" \
102+
--config.mac.notarize=false \
103+
--config.publish.owner=${{ github.repository_owner }} \
104+
--config.publish.repo=${{ github.event.repository.name }} \
105+
--publish "$PUBLISH_MODE"
90106
91107
- name: Upload DMG artifact
92108
uses: actions/upload-artifact@v4

build/macos/codesign.sh

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
#!/usr/bin/env bash
2+
# build/macos/codesign.sh
3+
# Ad-hoc code signing for LTX Desktop macOS app bundle.
4+
#
5+
# No Apple Developer account or Apple ID is required. By default this script
6+
# uses ad-hoc signing (identity "-"), which prevents the "app is damaged" Gatekeeper
7+
# warning without needing a paid Apple Developer certificate.
8+
#
9+
# For production releases with a real Developer ID, set the MACOS_SIGNING_IDENTITY
10+
# environment variable to your certificate name (e.g. "Developer ID Application: …").
11+
#
12+
# Adapted from: https://github.com/audiohacking/AceForge/blob/main/build/macos/codesign.sh
13+
# Original reference: https://github.com/dylanwh/lilguy/blob/main/macos/build.sh
14+
#
15+
# Usage:
16+
# bash build/macos/codesign.sh <path-to-app.bundle>
17+
18+
set -euo pipefail
19+
20+
APP_PATH="${1:?Usage: $0 <path-to-app.bundle>}"
21+
SIGNING_IDENTITY="${MACOS_SIGNING_IDENTITY:--}" # Default: ad-hoc ("-")
22+
ENTITLEMENTS_PATH="resources/entitlements.mac.plist"
23+
24+
echo "=================================================="
25+
echo " LTX Desktop macOS Code Signing"
26+
echo "=================================================="
27+
echo " App: $APP_PATH"
28+
echo " Identity: $SIGNING_IDENTITY"
29+
echo " Entitlements: $ENTITLEMENTS_PATH"
30+
echo ""
31+
32+
# ── Pre-flight checks ────────────────────────────────────────────────────────
33+
if [ ! -d "$APP_PATH" ]; then
34+
echo "Error: App bundle not found at $APP_PATH"
35+
exit 1
36+
fi
37+
38+
if [ ! -f "$ENTITLEMENTS_PATH" ]; then
39+
echo "Error: Entitlements file not found at $ENTITLEMENTS_PATH"
40+
echo " Run this script from the project root."
41+
exit 1
42+
fi
43+
44+
# ── Signing helper ────────────────────────────────────────────────────────────
45+
# Ad-hoc signing ("-") does not support --timestamp (requires a CA).
46+
sign_target() {
47+
local target="$1"
48+
echo " Signing: $(basename "$target")"
49+
50+
if [ "$SIGNING_IDENTITY" = "-" ]; then
51+
xcrun codesign \
52+
--sign "$SIGNING_IDENTITY" \
53+
--force \
54+
--options runtime \
55+
--entitlements "$ENTITLEMENTS_PATH" \
56+
--deep \
57+
"$target"
58+
else
59+
xcrun codesign \
60+
--sign "$SIGNING_IDENTITY" \
61+
--force \
62+
--options runtime \
63+
--entitlements "$ENTITLEMENTS_PATH" \
64+
--deep \
65+
--timestamp \
66+
"$target"
67+
fi
68+
69+
echo " ✓ Signed: $(basename "$target")"
70+
}
71+
72+
# ── Step 1: Sign bundled Python native libraries ──────────────────────────────
73+
# Sign leaf Mach-O binaries first so the bundle signature remains valid.
74+
# The Python embed directory contains .dylib/.so native extensions.
75+
echo "Step 1: Signing bundled Python native libraries..."
76+
PYTHON_DIR="$APP_PATH/Contents/Resources/python"
77+
if [ -d "$PYTHON_DIR" ]; then
78+
find "$PYTHON_DIR" -type f \( -name "*.dylib" -o -name "*.so" \) -print0 | \
79+
while IFS= read -r -d '' lib; do
80+
sign_target "$lib" || true # keep going on individual failures
81+
done
82+
echo " Python native libraries signed."
83+
else
84+
echo " No bundled Python directory found at Contents/Resources/python — skipping."
85+
fi
86+
87+
# ── Step 2: Sign Electron framework libraries ────────────────────────────────
88+
echo ""
89+
echo "Step 2: Signing Electron framework libraries..."
90+
if [ -d "$APP_PATH/Contents/Frameworks" ]; then
91+
find "$APP_PATH/Contents/Frameworks" -type f \( -name "*.dylib" -o -name "*.so" \) -print0 | \
92+
while IFS= read -r -d '' f; do
93+
sign_target "$f" || true
94+
done
95+
fi
96+
97+
# ── Step 3: Sign main executables ────────────────────────────────────────────
98+
echo ""
99+
echo "Step 3: Signing main executables..."
100+
for exe in "$APP_PATH/Contents/MacOS/"*; do
101+
if [ -f "$exe" ] && [ -x "$exe" ]; then
102+
sign_target "$exe"
103+
fi
104+
done
105+
106+
# ── Step 4: Sign the whole app bundle ────────────────────────────────────────
107+
echo ""
108+
echo "Step 4: Signing app bundle..."
109+
sign_target "$APP_PATH"
110+
111+
# ── Verification ─────────────────────────────────────────────────────────────
112+
echo ""
113+
echo "Verification:"
114+
xcrun codesign --verify --deep --strict --verbose=2 "$APP_PATH" 2>&1 || true
115+
116+
echo ""
117+
echo "Signature info:"
118+
xcrun codesign -dv "$APP_PATH" 2>&1 || true
119+
120+
echo ""
121+
echo "=================================================="
122+
echo " ✓ Code signing complete!"
123+
echo "=================================================="

0 commit comments

Comments
 (0)