API: backup forwarding toggle and status in /api/status #60
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - master | |
| tags: | |
| - 'v*' | |
| pull_request: | |
| branches: | |
| - main | |
| - master | |
| workflow_dispatch: | |
| jobs: | |
| build-macos: | |
| runs-on: macos-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Clean up problematic Icon files | |
| run: | | |
| # Some repos can accidentally contain a macOS Finder icon file named "Icon" with a carriage return ("Icon\r"), | |
| # which breaks checkout/builds on some runners. Remove ONLY those problematic paths. | |
| python3 - <<'PY' | |
| import os | |
| import subprocess | |
| out = subprocess.check_output(["git", "ls-files", "-z"]) | |
| paths = out.decode("utf-8", "surrogateescape").split("\0") | |
| bad = [] | |
| for p in paths: | |
| if not p: | |
| continue | |
| base = os.path.basename(p) | |
| # Typical problematic cases: "Icon" or "Icon\r" (Finder) | |
| if "\r" in p or base == "Icon" or base == "Icon\r": | |
| # Never remove our app icon assets | |
| if p.startswith("build/icon."): | |
| continue | |
| bad.append(p) | |
| if bad: | |
| for p in bad: | |
| subprocess.run(["git", "rm", "--cached", p], check=False) | |
| PY | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Build macOS app (unsigned) | |
| run: npm run build:mac | |
| env: | |
| ELECTRON_BUILDER_CACHE: ${{ runner.temp }}/electron-builder-cache | |
| - name: Verify app structure | |
| run: | | |
| echo "Checking app structure..." | |
| APP_PATH="dist/mac-arm64/Google Slides Opener.app" | |
| if [ -d "$APP_PATH" ]; then | |
| echo "App exists at: $APP_PATH" | |
| ls -la "$APP_PATH/Contents/" | |
| echo "Checking Electron Framework symlink..." | |
| ls -la "$APP_PATH/Contents/Frameworks/Electron Framework.framework/" || echo "Framework check failed" | |
| echo "Checking executable..." | |
| ls -la "$APP_PATH/Contents/MacOS/" || echo "MacOS check failed" | |
| else | |
| echo "ERROR: App not found at $APP_PATH" | |
| ls -la dist/ | |
| exit 1 | |
| fi | |
| - name: Remove quarantine and ad-hoc sign app bundle | |
| run: | | |
| APP_PATH="dist/mac-arm64/Google Slides Opener.app" | |
| if [ -d "$APP_PATH" ]; then | |
| echo "Removing quarantine attribute..." | |
| xattr -cr "$APP_PATH" | |
| # Sign all nested frameworks and helper apps first | |
| echo "Signing nested frameworks and helpers..." | |
| find "$APP_PATH" -name "*.framework" -exec codesign --force --sign - {} \; | |
| find "$APP_PATH" -name "*Helper*.app" -exec codesign --force --sign - {} \; | |
| # Then sign the main app bundle with entitlements | |
| echo "Ad-hoc signing main app with entitlements (allows it to run without real certificate)..." | |
| codesign --force --deep --sign - --entitlements entitlements.mac.plist "$APP_PATH" | |
| echo "Verifying signature..." | |
| codesign --verify --verbose "$APP_PATH" | |
| echo "Checking signature details..." | |
| codesign -dv "$APP_PATH" | |
| else | |
| echo "ERROR: App not found at $APP_PATH" | |
| exit 1 | |
| fi | |
| - name: Re-create ZIP with signed app | |
| run: | | |
| cd dist | |
| # Find the ZIP file that electron-builder created | |
| ZIP_FILE=$(ls -1 *.zip | head -1) | |
| if [ -z "$ZIP_FILE" ]; then | |
| echo "ERROR: No ZIP file found in dist/" | |
| ls -la | |
| exit 1 | |
| fi | |
| echo "Found ZIP file: $ZIP_FILE" | |
| # The app is already signed in dist/mac-arm64, so just re-zip it | |
| APP_PATH="mac-arm64/Google Slides Opener.app" | |
| if [ ! -d "$APP_PATH" ]; then | |
| echo "ERROR: Signed app not found at $APP_PATH" | |
| exit 1 | |
| fi | |
| # Remove old ZIP | |
| rm -f "$ZIP_FILE" | |
| # Create new ZIP with signed app using ditto (preserves symlinks and metadata) | |
| cd mac-arm64 | |
| ditto -c -k --keepParent "Google Slides Opener.app" "../$ZIP_FILE" | |
| cd .. | |
| echo "Created signed ZIP: $ZIP_FILE" | |
| ls -lh "$ZIP_FILE" | |
| # Verify the app in the new ZIP is signed | |
| TEMP_DIR=$(mktemp -d) | |
| unzip -q "$ZIP_FILE" -d "$TEMP_DIR" | |
| codesign --verify --verbose "$TEMP_DIR/Google Slides Opener.app" && echo "✓ App in ZIP is properly signed" | |
| rm -rf "$TEMP_DIR" | |
| - name: Upload macOS artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: gslide-opener-macos-zip | |
| path: dist/*.zip | |
| retention-days: 30 | |
| release: | |
| needs: [build-macos] | |
| runs-on: ubuntu-latest | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Download macOS artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: gslide-opener-macos-zip | |
| path: artifacts/macos | |
| - name: Create Release | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| files: | | |
| artifacts/macos/*.zip | |
| draft: false | |
| prerelease: false | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |