name: Build & Release on: push: tags: - 'v*' workflow_dispatch: inputs: publish: description: 'Publish to GitHub Releases' required: false type: boolean default: false concurrency: group: build-${{ github.ref }} cancel-in-progress: true env: PNPM_VERSION: '10.24.0' NODE_VERSION: '22' jobs: # ───────────────────────────────────────────── # Stage 1: Lint & Typecheck (fast feedback) # ───────────────────────────────────────────── check: name: Lint & Typecheck runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: version: ${{ env.PNPM_VERSION }} - uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: pnpm - run: pnpm install --frozen-lockfile - run: pnpm lint - run: pnpm exec tsc --noEmit --project tsconfig.check.json # ───────────────────────────────────────────── # Stage 2: Unit Tests # ───────────────────────────────────────────── test: name: Unit Tests runs-on: ubuntu-latest needs: check steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: version: ${{ env.PNPM_VERSION }} - uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: pnpm - run: pnpm install --frozen-lockfile - run: pnpm test # ───────────────────────────────────────────── # Stage 3: Build matrix (parallel per platform) # ───────────────────────────────────────────── build-macos: name: macOS (${{ matrix.arch }}) runs-on: ${{ matrix.runner }} needs: test strategy: fail-fast: false matrix: include: - arch: arm64 runner: macos-14 builder_args: --mac --arm64 - arch: x64 runner: macos-15 builder_args: --mac --x64 steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: version: ${{ env.PNPM_VERSION }} - uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: pnpm - run: pnpm install --frozen-lockfile - name: Decode signing certificate if: env.CERT_B64 != '' env: CERT_B64: ${{ secrets.MAC_CERTIFICATE_P12_BASE64 }} run: echo "$CERT_B64" | base64 --decode > /tmp/cert.p12 - name: Build electron-vite run: pnpm exec electron-vite build - name: Package & sign run: pnpm exec electron-builder ${{ matrix.builder_args }} --config electron-builder.yml --publish never env: CSC_LINK: ${{ secrets.MAC_CERTIFICATE_P12_BASE64 != '' && '/tmp/cert.p12' || '' }} CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTIFICATE_PASSWORD }} CSC_IDENTITY_AUTO_DISCOVERY: ${{ secrets.MAC_CERTIFICATE_P12_BASE64 != '' && 'true' || 'false' }} APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} - uses: actions/upload-artifact@v4 with: name: macos-${{ matrix.arch }} path: | build/release/*.dmg build/release/*.zip build/release/*.blockmap build/release/latest-mac.yml if-no-files-found: error build-windows: name: Windows (${{ matrix.arch }}) runs-on: windows-latest needs: test strategy: fail-fast: false matrix: include: - arch: x64 builder_args: --win --x64 - arch: arm64 builder_args: --win --arm64 steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: version: ${{ env.PNPM_VERSION }} - uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: pnpm - run: pnpm install --frozen-lockfile - name: Build electron-vite run: pnpm exec electron-vite build - name: Package run: pnpm exec electron-builder ${{ matrix.builder_args }} --config electron-builder.yml --publish never env: CSC_LINK: ${{ secrets.WIN_CERTIFICATE_P12_BASE64 }} CSC_KEY_PASSWORD: ${{ secrets.WIN_CERTIFICATE_PASSWORD }} - uses: actions/upload-artifact@v4 with: name: windows-${{ matrix.arch }} path: | build/release/*.exe build/release/*.blockmap build/release/latest.yml if-no-files-found: error build-linux: name: Linux (${{ matrix.arch }}) runs-on: ubuntu-latest needs: test strategy: fail-fast: false matrix: include: - arch: x64 builder_args: --linux --x64 - arch: arm64 builder_args: --linux --arm64 steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: version: ${{ env.PNPM_VERSION }} - uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: pnpm - run: pnpm install --frozen-lockfile - name: Install snapcraft (x64 only) if: matrix.arch == 'x64' run: sudo snap install snapcraft --classic - name: Build electron-vite run: pnpm exec electron-vite build - name: Package run: pnpm exec electron-builder ${{ matrix.builder_args }} --config electron-builder.yml --publish never - uses: actions/upload-artifact@v4 with: name: linux-${{ matrix.arch }} path: | build/release/*.AppImage build/release/*.deb build/release/*.rpm build/release/*.snap build/release/*.blockmap build/release/latest-linux.yml if-no-files-found: warn # ───────────────────────────────────────────── # Stage 4: Publish to GitHub Releases # ───────────────────────────────────────────── release: name: Publish Release runs-on: ubuntu-latest needs: [build-macos, build-windows, build-linux] if: startsWith(github.ref, 'refs/tags/v') || github.event.inputs.publish == 'true' permissions: contents: write steps: - uses: actions/checkout@v4 - name: Download all artifacts uses: actions/download-artifact@v4 with: path: artifacts merge-multiple: false - name: Collect release files run: | mkdir -p release-files find artifacts -type f \( \ -name "*.dmg" -o -name "*.zip" -o -name "*.exe" \ -o -name "*.AppImage" -o -name "*.deb" -o -name "*.rpm" \ -o -name "*.snap" -o -name "*.blockmap" \ -o -name "latest*.yml" \ \) -exec cp {} release-files/ \; ls -lh release-files/ - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: draft: true generate_release_notes: true files: release-files/* env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}