6262 - name : Build app (ts + vite)
6363 run : npm run build
6464
65- - name : Rebuild native modules (per-arch)
66- run : |
67- set -euo pipefail
68- ELECTRON_VERSION=$(node -p "require('electron/package.json').version")
69- ARCH_INPUT="${{ github.event.inputs.arch }}"
70- if [ -z "$ARCH_INPUT" ] || [ "$ARCH_INPUT" = "both" ]; then ARGS=(x64 arm64); else ARGS=("$ARCH_INPUT"); fi
71- echo "Rebuilding sqlite3,node-pty,keytar for Electron $ELECTRON_VERSION: ${ARGS[*]}"
72- for A in "${ARGS[@]}"; do
73- npm_config_build_from_source=true npx @electron/rebuild -f -a "$A" -v "$ELECTRON_VERSION" -w sqlite3,node-pty,keytar
74- done
75-
76- - name : Rebuild native modules (per-arch)
77- run : |
78- set -euo pipefail
79- ELECTRON_VERSION=$(node -p "require('electron/package.json').version")
80- ARCH_INPUT="${{ github.event.inputs.arch }}"
81- if [ -z "$ARCH_INPUT" ] || [ "$ARCH_INPUT" = "both" ]; then ARGS=(x64 arm64); else ARGS=("$ARCH_INPUT"); fi
82- echo "Rebuilding sqlite3,node-pty,keytar for Electron $ELECTRON_VERSION: ${ARGS[*]}"
83- for A in "${ARGS[@]}"; do
84- npm_config_build_from_source=true npx @electron/rebuild -f -a "$A" -v "$ELECTRON_VERSION" -w sqlite3,node-pty,keytar
85- done
86-
8765 - name : Inject PostHog config (dev build job)
8866 env :
8967 PH_KEY : ${{ secrets.POSTHOG_PROJECT_API_KEY }}
@@ -109,18 +87,71 @@ jobs:
10987 env :
11088 CSC_IDENTITY_AUTO_DISCOVERY : ' false'
11189 run : |
90+ set -euo pipefail
11291 ARCH_INPUT="${{ github.event.inputs.arch }}"
11392 if [ -z "$ARCH_INPUT" ] || [ "$ARCH_INPUT" = "both" ]; then
114- FLAGS="-- x64 -- arm64"
93+ ARCHS=( x64 arm64)
11594 elif [ "$ARCH_INPUT" = "arm64" ]; then
116- FLAGS="-- arm64"
95+ ARCHS=( arm64)
11796 elif [ "$ARCH_INPUT" = "x64" ]; then
118- FLAGS="-- x64"
97+ ARCHS=( x64)
11998 else
12099 echo "Unknown arch input: $ARCH_INPUT" && exit 1
121100 fi
122- echo "Building for: $FLAGS"
123- npx electron-builder --mac dmg $FLAGS --publish never --config.npmRebuild=false
101+ ELECTRON_VERSION=$(node -p "require('electron/package.json').version")
102+ # Build each architecture separately to ensure native modules match.
103+ # Building both at once with --x64 --arm64 would use the same node_modules/
104+ # for both, causing architecture mismatches (issue #706).
105+ for A in "${ARCHS[@]}"; do
106+ echo "=== Building for $A ==="
107+ echo "Rebuilding native modules for $A (Electron $ELECTRON_VERSION)"
108+ npm_config_build_from_source=true npx @electron/rebuild -f -a "$A" -v "$ELECTRON_VERSION" -w sqlite3,node-pty,keytar
109+ echo "Packaging $A DMG"
110+ npx electron-builder --mac dmg --$A --publish never --config.npmRebuild=false
111+ done
112+
113+ - name : Verify native module architectures match Electron
114+ run : |
115+ set -euo pipefail
116+ # Verify that each build has native modules matching its Electron binary architecture.
117+ # This catches issues like #706 where x64 builds had arm64 native modules.
118+ VERIFIED_COUNT=0
119+ for APP_DIR in release/mac-*/emdash.app; do
120+ [ -d "$APP_DIR" ] || continue
121+ ARCH_DIR=$(basename "$(dirname "$APP_DIR")")
122+ case "$ARCH_DIR" in
123+ mac-arm64) EXPECTED_ARCH="arm64" ;;
124+ mac-x64|mac) EXPECTED_ARCH="x86_64" ;;
125+ *) echo "Unknown arch dir: $ARCH_DIR"; continue ;;
126+ esac
127+ echo "=== Checking $APP_DIR (expected: $EXPECTED_ARCH) ==="
128+ ELECTRON_BIN="$APP_DIR/Contents/MacOS/emdash"
129+ SQLITE_NODE="$APP_DIR/Contents/Resources/app.asar.unpacked/node_modules/sqlite3/build/Release/node_sqlite3.node"
130+ # Check Electron binary architecture
131+ ELECTRON_ARCH=$(file "$ELECTRON_BIN" | grep -o 'arm64\|x86_64' | head -1)
132+ echo "Electron binary: $ELECTRON_ARCH"
133+ if [ "$ELECTRON_ARCH" != "$EXPECTED_ARCH" ]; then
134+ echo "::error::Electron arch mismatch: got $ELECTRON_ARCH, expected $EXPECTED_ARCH"
135+ exit 1
136+ fi
137+ # Check sqlite3 native module architecture
138+ if [ -f "$SQLITE_NODE" ]; then
139+ SQLITE_ARCH=$(file "$SQLITE_NODE" | grep -o 'arm64\|x86_64' | head -1)
140+ echo "sqlite3 native module: $SQLITE_ARCH"
141+ if [ "$SQLITE_ARCH" != "$EXPECTED_ARCH" ]; then
142+ echo "::error::sqlite3 arch mismatch: got $SQLITE_ARCH, expected $EXPECTED_ARCH"
143+ exit 1
144+ fi
145+ else
146+ echo "::warning::sqlite3 native module not found at $SQLITE_NODE"
147+ fi
148+ VERIFIED_COUNT=$((VERIFIED_COUNT + 1))
149+ done
150+ if [ "$VERIFIED_COUNT" -eq 0 ]; then
151+ echo "::error::No app bundles found to verify"
152+ exit 1
153+ fi
154+ echo "Verified $VERIFIED_COUNT app bundle(s)"
124155
125156 - name : Smoke test sqlite3 in packaged app (arm64)
126157 if : ${{ github.event.inputs.arch == '' || github.event.inputs.arch == 'arm64' || github.event.inputs.arch == 'both' }}
@@ -322,6 +353,7 @@ jobs:
322353 APPLE_APP_SPECIFIC_PASSWORD : ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
323354 GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
324355 run : |
356+ set -euo pipefail
325357 # Guard against legacy env vars overriding cert-import flow
326358 unset CSC_LINK CSC_KEY_PASSWORD CSC_NAME
327359 # Force Apple ID notarization for the app build to avoid bad APPLE_API_KEY paths
@@ -331,30 +363,68 @@ jobs:
331363 echo "App notarization method: Apple ID (TEAM_ID=$TEAM_ID)"
332364 ARCH_INPUT="${{ github.event.inputs.arch }}"
333365 if [ -z "$ARCH_INPUT" ] || [ "$ARCH_INPUT" = "both" ]; then
334- FLAGS="-- x64 -- arm64"
366+ ARCHS=( x64 arm64)
335367 elif [ "$ARCH_INPUT" = "arm64" ]; then
336- FLAGS="-- arm64"
368+ ARCHS=( arm64)
337369 elif [ "$ARCH_INPUT" = "x64" ]; then
338- FLAGS="-- x64"
370+ ARCHS=( x64)
339371 else
340372 echo "Unknown arch input: $ARCH_INPUT" && exit 1
341373 fi
342- # Rebuild native modules per-arch before packaging
343374 ELECTRON_VERSION=$(node -p "require('electron/package.json').version")
344- for flag in $FLAGS; do
345- case "$flag" in
346- --x64) A=x64 ;;
347- --arm64) A=arm64 ;;
348- *) continue ;;
349- esac
350- echo "electron-rebuild for $A (Electron $ELECTRON_VERSION)"
375+ # Build each architecture separately to ensure native modules match.
376+ # Building both at once with --x64 --arm64 would use the same node_modules/
377+ # for both, causing architecture mismatches (issue #706).
378+ for A in "${ARCHS[@]}"; do
379+ echo "=== Building for $A ==="
380+ echo "Rebuilding native modules for $A (Electron $ELECTRON_VERSION)"
351381 npm_config_build_from_source=true npx @electron/rebuild -f -a "$A" -v "$ELECTRON_VERSION" -w sqlite3,node-pty,keytar
382+ echo "Packaging $A DMG and ZIP"
383+ npx electron-builder --mac dmg zip --$A --publish never --config.npmRebuild=false
352384 done
353- echo "Building signed for: $FLAGS"
354- # Build signed + notarized artifacts locally but DO NOT publish yet.
355- # We will staple and validate before uploading to the GitHub Release.
356- # Build both dmg (for manual download) and zip (for auto-update)
357- npx electron-builder --mac dmg zip $FLAGS --publish never --config.npmRebuild=false
385+
386+ - name : Verify native module architectures match Electron
387+ run : |
388+ set -euo pipefail
389+ # Verify that each build has native modules matching its Electron binary architecture.
390+ # This catches issues like #706 where x64 builds had arm64 native modules.
391+ VERIFIED_COUNT=0
392+ for APP_DIR in release/mac-*/emdash.app; do
393+ [ -d "$APP_DIR" ] || continue
394+ ARCH_DIR=$(basename "$(dirname "$APP_DIR")")
395+ case "$ARCH_DIR" in
396+ mac-arm64) EXPECTED_ARCH="arm64" ;;
397+ mac-x64|mac) EXPECTED_ARCH="x86_64" ;;
398+ *) echo "Unknown arch dir: $ARCH_DIR"; continue ;;
399+ esac
400+ echo "=== Checking $APP_DIR (expected: $EXPECTED_ARCH) ==="
401+ ELECTRON_BIN="$APP_DIR/Contents/MacOS/emdash"
402+ SQLITE_NODE="$APP_DIR/Contents/Resources/app.asar.unpacked/node_modules/sqlite3/build/Release/node_sqlite3.node"
403+ # Check Electron binary architecture
404+ ELECTRON_ARCH=$(file "$ELECTRON_BIN" | grep -o 'arm64\|x86_64' | head -1)
405+ echo "Electron binary: $ELECTRON_ARCH"
406+ if [ "$ELECTRON_ARCH" != "$EXPECTED_ARCH" ]; then
407+ echo "::error::Electron arch mismatch: got $ELECTRON_ARCH, expected $EXPECTED_ARCH"
408+ exit 1
409+ fi
410+ # Check sqlite3 native module architecture
411+ if [ -f "$SQLITE_NODE" ]; then
412+ SQLITE_ARCH=$(file "$SQLITE_NODE" | grep -o 'arm64\|x86_64' | head -1)
413+ echo "sqlite3 native module: $SQLITE_ARCH"
414+ if [ "$SQLITE_ARCH" != "$EXPECTED_ARCH" ]; then
415+ echo "::error::sqlite3 arch mismatch: got $SQLITE_ARCH, expected $EXPECTED_ARCH"
416+ exit 1
417+ fi
418+ else
419+ echo "::warning::sqlite3 native module not found at $SQLITE_NODE"
420+ fi
421+ VERIFIED_COUNT=$((VERIFIED_COUNT + 1))
422+ done
423+ if [ "$VERIFIED_COUNT" -eq 0 ]; then
424+ echo "::error::No app bundles found to verify"
425+ exit 1
426+ fi
427+ echo "Verified $VERIFIED_COUNT app bundle(s)"
358428
359429 - name : Smoke test sqlite3 in packaged app (arm64, signed)
360430 if : ${{ github.event.inputs.arch == '' || github.event.inputs.arch == 'arm64' || github.event.inputs.arch == 'both' }}
0 commit comments