@@ -301,8 +301,91 @@ jobs:
301301 ls -la cli-bundle/
302302 du -sh cli-bundle/
303303
304- - name : Update tauri.conf.json for CLI bundle
305- if : steps.check.outputs.skip != 'true'
304+ - name : Sign CLI bundle binaries (macOS)
305+ if : steps.check.outputs.skip != 'true' && startsWith(matrix.platform, 'macos')
306+ env :
307+ APPLE_CERTIFICATE : ${{ secrets.APPLE_CERTIFICATE }}
308+ APPLE_CERTIFICATE_PASSWORD : ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
309+ APPLE_SIGNING_IDENTITY : ${{ secrets.APPLE_SIGNING_IDENTITY }}
310+ shell : bash
311+ run : |
312+ # Import certificate
313+ CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
314+ KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
315+ KEYCHAIN_PASSWORD=$(openssl rand -base64 32)
316+
317+ # Decode and save certificate
318+ echo -n "$APPLE_CERTIFICATE" | base64 --decode -o $CERTIFICATE_PATH
319+
320+ # Create temporary keychain
321+ security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
322+ security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
323+ security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
324+
325+ # Import certificate to keychain
326+ security import $CERTIFICATE_PATH -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
327+ security list-keychain -d user -s $KEYCHAIN_PATH
328+
329+ # Sign all executable binaries in cli-bundle
330+ echo "Signing binaries in cli-bundle..."
331+ cd src-api/dist/cli-bundle
332+
333+ # Sign node binary first
334+ if [ -f "node" ]; then
335+ echo "Signing node..."
336+ codesign --force --options runtime --sign "$APPLE_SIGNING_IDENTITY" --timestamp node
337+ fi
338+
339+ # Sign ALL .dylib files (including @img/sharp-libvips-*)
340+ echo "Signing all .dylib files..."
341+ find . -name "*.dylib" -type f | while read -r file; do
342+ echo "Signing $file..."
343+ codesign --force --options runtime --sign "$APPLE_SIGNING_IDENTITY" --timestamp "$file" || true
344+ done
345+
346+ # Sign ALL .node files (including @img/sharp-*)
347+ echo "Signing all .node files..."
348+ find . -name "*.node" -type f | while read -r file; do
349+ echo "Signing $file..."
350+ codesign --force --options runtime --sign "$APPLE_SIGNING_IDENTITY" --timestamp "$file" || true
351+ done
352+
353+ # Sign ALL .so files
354+ echo "Signing all .so files..."
355+ find . -name "*.so" -type f | while read -r file; do
356+ echo "Signing $file..."
357+ codesign --force --options runtime --sign "$APPLE_SIGNING_IDENTITY" --timestamp "$file" || true
358+ done
359+
360+ # Sign ripgrep and other Mach-O executables
361+ echo "Signing Mach-O executable binaries..."
362+ find . -type f \( -name "rg" -o -name "node" -o -perm -u+x \) 2>/dev/null | while read -r file; do
363+ # Skip non-binary files
364+ case "$file" in
365+ *.js|*.json|*.md|*.txt|*.ts|*.mjs|*.cjs|*.map|*.yml|*.yaml) continue ;;
366+ esac
367+ # Check if it's a Mach-O binary
368+ if file "$file" 2>/dev/null | grep -q "Mach-O"; then
369+ echo "Signing Mach-O: $file..."
370+ codesign --force --options runtime --sign "$APPLE_SIGNING_IDENTITY" --timestamp "$file" || true
371+ fi
372+ done
373+
374+ cd ../../..
375+
376+ # Also sign the launcher scripts (they are executables)
377+ echo "Signing launcher scripts..."
378+ codesign --force --options runtime --sign "$APPLE_SIGNING_IDENTITY" --timestamp src-api/dist/claude
379+ codesign --force --options runtime --sign "$APPLE_SIGNING_IDENTITY" --timestamp src-api/dist/codex
380+
381+ echo "All binaries signed successfully"
382+
383+ # Cleanup
384+ security delete-keychain $KEYCHAIN_PATH
385+ rm -f $CERTIFICATE_PATH
386+
387+ - name : Update tauri.conf.json for CLI bundle (Unix)
388+ if : steps.check.outputs.skip != 'true' && matrix.platform != 'windows'
306389 shell : bash
307390 run : |
308391 node -e "
@@ -312,7 +395,7 @@ jobs:
312395 if (!config.bundle.externalBin) config.bundle.externalBin = [];
313396 if (!config.bundle.resources) config.bundle.resources = [];
314397
315- // Add CLI launchers
398+ // Add CLI launchers (Unix only - they are shell scripts)
316399 if (!config.bundle.externalBin.includes('../src-api/dist/claude')) {
317400 config.bundle.externalBin.unshift('../src-api/dist/claude');
318401 }
@@ -330,7 +413,38 @@ jobs:
330413 }
331414
332415 fs.writeFileSync('src-tauri/tauri.conf.json', JSON.stringify(config, null, 2));
333- console.log('Updated tauri.conf.json with CLI bundle config');
416+ console.log('Updated tauri.conf.json with CLI bundle config (Unix)');
417+ "
418+
419+ - name : Update tauri.conf.json for CLI bundle (Windows)
420+ if : steps.check.outputs.skip != 'true' && matrix.platform == 'windows'
421+ shell : bash
422+ run : |
423+ node -e "
424+ const fs = require('fs');
425+ const config = JSON.parse(fs.readFileSync('src-tauri/tauri.conf.json', 'utf8'));
426+
427+ if (!config.bundle.resources) config.bundle.resources = [];
428+
429+ // Windows: Don't add to externalBin (Tauri expects .exe files)
430+ // Remove any existing claude/codex from externalBin
431+ if (config.bundle.externalBin) {
432+ config.bundle.externalBin = config.bundle.externalBin.filter(bin =>
433+ !bin.includes('claude') && !bin.includes('codex')
434+ );
435+ }
436+
437+ // Add cli-bundle as resource (backend will call node directly)
438+ const cliResource = '../src-api/dist/cli-bundle/**/*';
439+ if (!config.bundle.resources.includes(cliResource)) {
440+ config.bundle.resources = config.bundle.resources.filter(r =>
441+ !r.includes('claude-bundle') && !r.includes('codex-bundle') && !r.includes('cli-bundle')
442+ );
443+ config.bundle.resources.push(cliResource);
444+ }
445+
446+ fs.writeFileSync('src-tauri/tauri.conf.json', JSON.stringify(config, null, 2));
447+ console.log('Updated tauri.conf.json with CLI bundle config (Windows - resources only)');
334448 "
335449
336450 - name : Build frontend
0 commit comments