name: Build Electron App on: release: types: [published] permissions: contents: write # Required to upload release assets jobs: build: runs-on: macos-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup pnpm uses: pnpm/action-setup@v4 with: version: 9 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: 24 cache: 'pnpm' cache-dependency-path: 'apps/x/pnpm-lock.yaml' - name: Extract version from tag id: version run: | VERSION="${GITHUB_REF#refs/tags/v}" echo "version=${VERSION}" >> $GITHUB_OUTPUT echo "Extracted version: ${VERSION}" - name: Update package.json versions run: | node -e " const fs = require('fs'); const version = '${{ steps.version.outputs.version }}'; // Update apps/x/package.json const rootPackage = JSON.parse(fs.readFileSync('apps/x/package.json', 'utf8')); rootPackage.version = version; fs.writeFileSync('apps/x/package.json', JSON.stringify(rootPackage, null, 2) + '\n'); // Update apps/x/apps/main/package.json const mainPackage = JSON.parse(fs.readFileSync('apps/x/apps/main/package.json', 'utf8')); mainPackage.version = version; fs.writeFileSync('apps/x/apps/main/package.json', JSON.stringify(mainPackage, null, 2) + '\n'); console.log('Updated version to:', version); " - name: Import Code Signing Certificate env: APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} run: | # Create a 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" # Decode and 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" # Allow codesign to access the keychain security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" # Add keychain to search list security list-keychain -d user -s "$KEYCHAIN_PATH" login.keychain # Verify certificate was imported security find-identity -v "$KEYCHAIN_PATH" # Clean up certificate file rm -f $RUNNER_TEMP/certificate.p12 - name: Install dependencies run: pnpm install --frozen-lockfile working-directory: apps/x - name: Build distributables env: APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} run: npm run make working-directory: apps/x/apps/main - name: Diagnose built app run: | echo "=== Architecture Check ===" APP_PATH=$(find apps/x/apps/main/out -name "Rowboat.app" -type d | head -1) if [ -n "$APP_PATH" ]; then EXECUTABLE="$APP_PATH/Contents/MacOS/rowboat" if [ -f "$EXECUTABLE" ]; then echo "App executable found at: $EXECUTABLE" echo "Architecture:" lipo -info "$EXECUTABLE" || file "$EXECUTABLE" echo "" 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 echo "Executable not found at: $EXECUTABLE" fi else echo "App bundle not found" fi continue-on-error: true - name: Upload workflow artifacts uses: actions/upload-artifact@v4 with: name: distributables path: apps/x/apps/main/out/make/* retention-days: 30 - name: Attach files to GitHub Release uses: softprops/action-gh-release@v2 with: files: apps/x/apps/main/out/make/* env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Cleanup keychain if: always() run: | KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db if [ -f "$KEYCHAIN_PATH" ]; then security delete-keychain "$KEYCHAIN_PATH" || true fi