diff --git a/.github/workflows/electron-build.yml b/.github/workflows/electron-build.yml index 9452a803..1be5ef76 100644 --- a/.github/workflows/electron-build.yml +++ b/.github/workflows/electron-build.yml @@ -75,6 +75,17 @@ jobs: echo "=== Code Signing Check ===" codesign --verify --deep --strict --verbose=2 "$APP_PATH" || echo "App is not signed (this is OK if code signing secrets are not configured)" echo "" + echo "=== _CodeSignature Check ===" + if [ -d "$APP_PATH/Contents/_CodeSignature" ]; then + echo "WARNING: _CodeSignature directory still exists!" + ls -la "$APP_PATH/Contents/_CodeSignature" || true + else + echo "✓ No _CodeSignature directory (expected for unsigned app)" + fi + echo "" + echo "=== Extended Attributes Check ===" + xattr -l "$APP_PATH" | head -5 || echo "No extended attributes (or not on macOS)" + echo "" echo "=== Gatekeeper Check ===" spctl --assess --type execute --verbose "$APP_PATH" || echo "Gatekeeper assessment failed (expected for unsigned apps)" else diff --git a/apps/x/apps/main/forge.config.cjs b/apps/x/apps/main/forge.config.cjs index 72a2fcec..3186c15c 100644 --- a/apps/x/apps/main/forge.config.cjs +++ b/apps/x/apps/main/forge.config.cjs @@ -218,6 +218,67 @@ module.exports = { fs.rmSync(codeSignatureDir, { recursive: true }); } + // 8. Remove adhoc signatures from app bundle, executable, and helpers + // The app bundle has an adhoc signature (linker-signed) that references resources + // When we modify the bundle structure, this signature becomes invalid + // Removing it ensures the app is completely unsigned + try { + const { execSync } = require('child_process'); + + // Remove signature from the app bundle itself (this is the key step) + // This removes the bundle-level signature that causes the "code has no resources" error + execSync(`codesign --remove-signature "${appBundleRoot}"`, { stdio: 'ignore' }); + + // Also remove signature from main executable + const executablePath = path.join(appBundleRoot, 'Contents', 'MacOS', 'rowboat'); + if (fs.existsSync(executablePath)) { + execSync(`codesign --remove-signature "${executablePath}"`, { stdio: 'ignore' }); + } + + // Remove signatures from helper apps in Frameworks + const frameworksDir = path.join(appBundleRoot, 'Contents', 'Frameworks'); + if (fs.existsSync(frameworksDir)) { + const helpers = fs.readdirSync(frameworksDir, { withFileTypes: true }) + .filter(dirent => dirent.isDirectory()) + .map(dirent => { + const helperApp = path.join(frameworksDir, dirent.name, 'Contents', 'MacOS'); + if (fs.existsSync(helperApp)) { + const helpers = fs.readdirSync(helperApp, { withFileTypes: true }) + .filter(f => f.isFile()) + .map(f => path.join(helperApp, f.name)); + return helpers; + } + return []; + }) + .flat(); + + for (const helperExec of helpers) { + try { + execSync(`codesign --remove-signature "${helperExec}"`, { stdio: 'ignore' }); + } catch (e) { + // Ignore errors for helpers + } + } + } + + console.log('Removed adhoc signatures from app bundle, executable, and helpers'); + } catch (e) { + // Ignore errors - codesign might fail if signatures don't exist + } + + // 9. Clear any signature-related extended attributes + // Even without _CodeSignature or embedded signatures, extended attributes can contain invalid signature metadata + // This prevents "code has no resources but signature indicates they must be present" error + try { + const { execSync } = require('child_process'); + // Clear extended attributes from the entire app bundle + // This removes any signature metadata that might be stored in xattrs + execSync(`xattr -cr "${appBundleRoot}"`, { stdio: 'ignore' }); + console.log('Cleared extended attributes from app bundle'); + } catch (e) { + // Ignore errors - xattr might not be available or might fail silently + } + console.log('✅ Packaged app fixed with bundled code'); } }