# github-actions-exe-builder > Use when configuring GitHub Actions to build, package, and publish applications (especially Electron) as Windows executables, or when encountering permission (403 Forbidden) and PowerShell environment issues in CI runners. - Author: Hopemyl - Repository: lim12137/ttyd-claude - Version: 20260124141237 - Stars: 0 - Forks: 0 - Last Updated: 2026-02-06 - Source: https://github.com/lim12137/ttyd-claude - Web: https://mule.run/skillshub/@@lim12137/ttyd-claude~github-actions-exe-builder:20260124141237 --- --- name: github-actions-exe-builder description: Use when configuring GitHub Actions to build, package, and publish applications (especially Electron) as Windows executables, or when encountering permission (403 Forbidden) and PowerShell environment issues in CI runners. --- # GitHub Actions EXE Builder ## Overview Automating the build and release process for Windows executables (.exe) requires careful configuration. The most robust approach decouples the **Build** (compilation on Windows) from the **Release** (uploading via Ubuntu), ensuring clean artifact handling and preventing platform-specific API issues. ## When to Use - You need to build a Windows .exe from a Node.js/Electron project. - You encounter `HttpError: 403 Forbidden` ("Resource not accessible by integration"). - You want to ensure Releases process only succeeds if the Build completely succeeds. - You need to handle Git Tags (e.g., v1.0) and convert them to valid SemVer for Electron/NPM. ## Core Patterns ### 1. The Robust "Build-Handoff-Release" Pattern (Recommended) This pattern separates the heavy build process from the API-heavy release process. **Job 1: Build (Windows)** - Runs on `windows-latest`. - Compiles the code. - Uploads the binary as an artifact. **Job 2: Release (Ubuntu)** - Runs on `ubuntu-latest` (cheaper, faster for API calls). - Needs `setup-node` or similar? No, just needs artifacts. - Downloads the artifact. - Creates the GitHub Release. ```yaml jobs: build: runs-on: windows-latest steps: - uses: actions/checkout@v4 # [Setup & Install Steps...] # CRITICAL: Fix SemVer for Electron (Git tag v1.0 -> 1.0.0) - name: Normalize Version shell: bash run: | VERSION=$(git describe --tags) VERSION=${VERSION#v} # Remove 'v' IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION" [ -z "$PATCH" ] && PATCH=0 npm version "$MAJOR.$MINOR.$PATCH" --no-git-tag-version --allow-same-version - name: Build run: npm run build:win # CRITICAL: Persist binary for the Release job - name: Upload Artifact uses: actions/upload-artifact@v4 with: name: windows-build path: dist/*.exe release: needs: build if: startsWith(github.ref, 'refs/tags/') runs-on: ubuntu-latest permissions: contents: write # CRITICAL: Required to create Releases steps: - name: Download Artifact uses: actions/download-artifact@v4 with: path: artifacts # Downloads to ./artifacts/windows-build/ - name: Release uses: softprops/action-gh-release@v2 with: files: artifacts/windows-build/* env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ``` ### 2. Electron-Builder Direct Publish (Simplest) Use this only for simple workflows where build and release happen on the same runner. ```yaml jobs: build-and-publish: runs-on: windows-latest permissions: contents: write steps: - uses: actions/checkout@v4 - run: npm install - name: Build & Publish env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: npx electron-builder --win --publish always ``` ## Quick Reference | Category | Key Configuration / Command | | :--- | :--- | | **Permissions** | `permissions: contents: write` (Required for Release job) | | **Separation** | Use `needs: build` in release job + `upload/download-artifact` | | **SemVer Fix** | Electron requires X.Y.Z. Git tags often V.X.Y. See normalization script above. | | **Release Action** | `softprops/action-gh-release@v2` is the gold standard for independent releases. | ## Common Mistakes & Troubleshooting ### HttpError: 403 Forbidden during Release - **Cause**: The Agent doesn't have permission to write to the repository's Releases. - **Fix**: Add `permissions: contents: write` to the Release job. ### "Invalid Version" Error - **Cause**: `npm version` or `electron-builder` fails if tag is `v1.2` instead of `1.2.0`. - **Fix**: Use the Bash normalization script in the "Robust" pattern to ensure 3-part SemVer. ### Missing Artifacts in Release - **Cause**: Release job runs on a different machine than Build job. - **Fix**: You MUST use `upload-artifact` in Build and `download-artifact` in Release. File paths in Release job will be relative to where `download-artifact` placed them. ## Implementation Checklist - [ ] **Permissions**: `contents: write` is explicitly set on the publishing job. - [ ] **Artifacts**: If splitting jobs, verify `path` matches exactly between upload and download. - [ ] **SemVer**: Add the normalization script if git tags might effectively be short (e.g., `v1.0`). - [ ] **Trigger**: Ensure `on: push: tags: - '*'` is set so it only runs on tags.