Release v3.8.34 (#4614) #290
Workflow file for this run
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 Electron Desktop App | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Release version (e.g., v1.6.8)" | |
| required: true | |
| type: string | |
| # Least-privilege default: read-only at the top level; each job grants the writes it | |
| # needs (build/release upload assets, publish-npm forwards npm provenance / packages | |
| # to the reusable workflow) — Scorecard TokenPermissions. | |
| permissions: | |
| contents: read | |
| jobs: | |
| validate: | |
| name: Validate version | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| outputs: | |
| version: ${{ steps.validate.outputs.version }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| fetch-depth: 0 | |
| - name: Validate version format | |
| id: validate | |
| env: | |
| # Pass workflow context via env (never interpolate ${{ ... }} straight | |
| # into the run: script body) so the shell receives variables, not | |
| # inlined text — zizmor template-injection mitigation. INPUT_VERSION is | |
| # the operator-supplied value and is regex-validated below before use. | |
| EVENT_NAME: ${{ github.event_name }} | |
| INPUT_VERSION: ${{ inputs.version }} | |
| run: | | |
| if [[ "$EVENT_NAME" == "push" ]]; then | |
| VERSION="${GITHUB_REF#refs/tags/}" | |
| else | |
| VERSION="$INPUT_VERSION" | |
| fi | |
| if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| echo "Error: Invalid version format. Expected: v1.6.8" | |
| exit 1 | |
| fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "✓ Valid version: $VERSION" | |
| build: | |
| name: Build Electron (${{ matrix.platform }}) | |
| needs: validate | |
| runs-on: ${{ matrix.runner }} | |
| permissions: | |
| contents: write # electron-builder may publish artifacts with GH_TOKEN | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - platform: windows | |
| runner: windows-latest | |
| target: win | |
| ext: .exe | |
| - platform: macos-intel | |
| runner: macos-15-intel | |
| target: mac-x64 | |
| ext: .dmg | |
| - platform: macos-arm64 | |
| runner: macos-latest | |
| target: mac-arm64 | |
| ext: -arm64.dmg | |
| - platform: linux | |
| runner: ubuntu-latest | |
| target: linux | |
| ext: .AppImage | |
| deb_ext: .deb | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| - name: Setup Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 24 | |
| cache: npm | |
| - name: Cache node_modules | |
| uses: actions/cache@v5.0.5 | |
| with: | |
| path: node_modules | |
| key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-node- | |
| - name: Install dependencies | |
| run: npm ci | |
| env: | |
| NPM_CONFIG_LEGACY_PEER_DEPS: true | |
| - name: Sanitize Windows home directory | |
| if: runner.os == 'Windows' | |
| shell: bash | |
| run: | | |
| # The default USERPROFILE contains junction points (Application Data) | |
| # that cause EPERM errors during Next.js standalone build glob scans. | |
| # Create a clean temp profile directory to avoid this. | |
| mkdir -p "$RUNNER_TEMP/home" | |
| echo "USERPROFILE=$RUNNER_TEMP/home" >> "$GITHUB_ENV" | |
| - name: Build Next.js standalone | |
| env: | |
| JWT_SECRET: ci-build-secret-with-sufficient-length-for-validation | |
| NODE_OPTIONS: "--max_old_space_size=6144" | |
| run: npm run build | |
| - name: Sync version in electron/package.json | |
| shell: bash | |
| env: | |
| # Pass the validated version via env (never interpolate ${{ ... }} | |
| # straight into the run: script body) — zizmor template-injection | |
| # mitigation. Already regex-validated (^v[0-9]+\.[0-9]+\.[0-9]+$) in | |
| # the `validate` job, so it cannot carry shell metacharacters. | |
| VERSION: ${{ needs.validate.outputs.version }} | |
| run: | | |
| VERSION_NO_V="${VERSION#v}" | |
| node -e " | |
| const fs = require('fs'); | |
| const pkg = JSON.parse(fs.readFileSync('electron/package.json')); | |
| pkg.version = '$VERSION_NO_V'; | |
| fs.writeFileSync('electron/package.json', JSON.stringify(pkg, null, 2) + '\\n'); | |
| " | |
| echo "✓ electron/package.json version set to $VERSION_NO_V" | |
| - name: Install fpm (Linux .deb packaging tool) | |
| if: matrix.platform == 'linux' | |
| run: sudo gem install fpm --no-document | |
| - name: Install Electron dependencies | |
| working-directory: electron | |
| run: npm install --no-audit --no-fund | |
| - name: Build Electron for ${{ matrix.platform }} | |
| working-directory: electron | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: npm run build:${{ matrix.target }} | |
| - name: Smoke packaged Electron app | |
| if: matrix.platform != 'linux' | |
| # Best-effort smoke on Windows + macos-arm64: | |
| # - Windows: requestSingleInstanceLock() fails due to USERPROFILE | |
| # sanitization needed for the build step. | |
| # - macos-arm64: the headless GitHub arm64 runner crashes Electron's GPU | |
| # process (gpu_process_host exit_code=15 → network service crash → | |
| # "No rendezvous client, terminating process"), so the app can't bind | |
| # 127.0.0.1:20128 in time. The identical bundle is smoke-gated on | |
| # macos-intel + linux, so packaging is still verified per-OS; we don't | |
| # let the arm64 runner's GPU flakiness block the desktop release. | |
| continue-on-error: ${{ matrix.platform == 'windows' || matrix.platform == 'macos-arm64' }} | |
| env: | |
| ELECTRON_SMOKE_TIMEOUT_MS: 60000 | |
| ELECTRON_SMOKE_STREAM_LOGS: "1" | |
| run: npm run electron:smoke:packaged | |
| - name: Smoke packaged Electron app (Linux) | |
| if: matrix.platform == 'linux' | |
| env: | |
| ELECTRON_SMOKE_TIMEOUT_MS: 60000 | |
| ELECTRON_SMOKE_STREAM_LOGS: "1" | |
| run: xvfb-run -a npm run electron:smoke:packaged | |
| - name: Collect installers | |
| shell: bash | |
| run: | | |
| mkdir -p release-assets | |
| cd electron/dist-electron | |
| # Copy only installer files for this platform | |
| for file in *${{ matrix.ext }}; do | |
| [ -f "$file" ] && cp "$file" ../../release-assets/ | |
| done | |
| # Linux: also copy .deb package | |
| if [ "${{ matrix.platform }}" = "linux" ]; then | |
| for file in *.deb; do | |
| [ -f "$file" ] && cp "$file" ../../release-assets/ | |
| done | |
| fi | |
| # Windows: also copy portable standalone exe as OmniRoute.exe | |
| if [ "${{ matrix.platform }}" = "windows" ]; then | |
| for file in *.exe; do | |
| # Skip the NSIS installer (contains "Setup") | |
| case "$file" in *Setup*) continue ;; esac | |
| [ -f "$file" ] && cp "$file" "../../release-assets/OmniRoute.exe" && break | |
| done | |
| fi | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: electron-${{ matrix.platform }} | |
| path: release-assets/ | |
| release: | |
| name: Create Release | |
| needs: [validate, build] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write # softprops/action-gh-release creates the GitHub Release | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| fetch-depth: 0 | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v8 | |
| with: | |
| path: release-assets | |
| merge-multiple: true | |
| - name: Create source archives | |
| env: | |
| # Pass the validated version via env (never interpolate ${{ ... }} | |
| # straight into the run: script body) — zizmor template-injection | |
| # mitigation. Already regex-validated (^v[0-9]+\.[0-9]+\.[0-9]+$) in | |
| # the `validate` job, so it cannot carry shell metacharacters. | |
| VERSION: ${{ needs.validate.outputs.version }} | |
| run: | | |
| # Create source code archives (excluding dev dependencies and build artifacts) | |
| export TARBALL="OmniRoute-${VERSION}.source.tar.gz" | |
| export ZIPBALL="OmniRoute-${VERSION}.source.zip" | |
| # Use git archive for clean source export | |
| git archive --format=tar.gz --prefix="OmniRoute-${VERSION}/" HEAD -o "release-assets/$TARBALL" | |
| git archive --format=zip --prefix="OmniRoute-${VERSION}/" HEAD -o "release-assets/$ZIPBALL" | |
| echo "✓ Created source archives:" | |
| ls -lh "release-assets/$TARBALL" "release-assets/$ZIPBALL" | |
| - name: List release files | |
| run: ls -la release-assets/ | |
| - name: Create Release | |
| uses: softprops/action-gh-release@v3 | |
| with: | |
| tag_name: ${{ needs.validate.outputs.version }} | |
| draft: false | |
| prerelease: false | |
| generate_release_notes: true | |
| fail_on_unmatched_files: false | |
| files: | | |
| release-assets/*.dmg | |
| release-assets/*.exe | |
| release-assets/*.AppImage | |
| release-assets/*.deb | |
| release-assets/*.blockmap | |
| release-assets/*.source.tar.gz | |
| release-assets/*.source.zip | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| publish-npm: | |
| name: Publish to npm | |
| needs: [validate, release] | |
| permissions: | |
| # Must be `write`, not `read`: this job calls the reusable npm-publish.yml whose | |
| # `publish` job needs `contents: write` (gh release upload — attach the SBOM, #3874). | |
| # A reusable workflow's job cannot request more permission than the caller grants, | |
| # so a `read` here makes GitHub reject the run at startup (startup_failure). | |
| contents: write | |
| id-token: write # npm provenance (forwarded to the reusable workflow) | |
| packages: write # publish to npm.pkg.github.com | |
| uses: ./.github/workflows/npm-publish.yml | |
| with: | |
| version: ${{ needs.validate.outputs.version }} | |
| tag: latest | |
| secrets: | |
| NPM_TOKEN: ${{ secrets.NPM_TOKEN }} |