name: Release on: push: tags: - 'v*' permissions: contents: write concurrency: group: release-${{ github.ref_name }} cancel-in-progress: false env: WAILS_VERSION: v3.0.0 NODE_VERSION: '24' jobs: build-windows: name: Build Windows x64 runs-on: windows-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 with: go-version-file: go.mod cache: true - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: npm cache-dependency-path: frontend/package-lock.json - name: Apply release version metadata shell: bash env: RELEASE_TAG: ${{ github.ref_name }} run: node scripts/set-build-version.js "${RELEASE_TAG#v}" - name: Install Wails CLI shell: bash run: | go install github.com/wailsapp/wails/v3/cmd/wails3@${WAILS_VERSION} echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" - name: Ensure NSIS is available shell: pwsh run: | if (-not (Get-Command makensis -ErrorAction SilentlyContinue)) { choco install nsis --yes --no-progress "${env:ProgramFiles(x86)}\NSIS" | Out-File -FilePath $env:GITHUB_PATH -Append } if (-not (Get-Command makensis -ErrorAction SilentlyContinue) -and -not (Test-Path "${env:ProgramFiles(x86)}\NSIS\makensis.exe")) { throw "makensis was not found" } - name: Build Windows installer shell: bash env: RELEASE_TAG: ${{ github.ref_name }} run: | VERSION="${RELEASE_TAG#v}" wails3 task windows:package \ ARCH=amd64 \ VERSION="$VERSION" \ REPOSITORY="$GITHUB_REPOSITORY" - name: Stage release asset shell: pwsh env: RELEASE_TAG: ${{ github.ref_name }} run: | $binarySource = "bin/pvfine.exe" $binaryTarget = "pvfine-$env:RELEASE_TAG-windows-amd64.exe" $installerSource = "bin/pvfine-amd64-installer.exe" $installerTarget = "pvfine-$env:RELEASE_TAG-windows-amd64-installer.exe" if (-not (Test-Path $binarySource)) { throw "Expected Windows executable was not generated: $binarySource" } if (-not (Test-Path $installerSource)) { throw "Expected Windows installer was not generated: $installerSource" } Copy-Item $binarySource $binaryTarget Copy-Item $installerSource $installerTarget - name: Upload Windows asset uses: actions/upload-artifact@v4 with: name: windows-release path: | pvfine-${{ github.ref_name }}-windows-amd64.exe pvfine-${{ github.ref_name }}-windows-amd64-installer.exe if-no-files-found: error build-macos: name: Build macOS Universal runs-on: macos-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 with: go-version-file: go.mod cache: true - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: npm cache-dependency-path: frontend/package-lock.json - name: Apply release version metadata shell: bash env: RELEASE_TAG: ${{ github.ref_name }} run: node scripts/set-build-version.js "${RELEASE_TAG#v}" - name: Install Wails CLI shell: bash run: | go install github.com/wailsapp/wails/v3/cmd/wails3@${WAILS_VERSION} echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" - name: Build macOS DMG shell: bash env: RELEASE_TAG: ${{ github.ref_name }} run: | VERSION="${RELEASE_TAG#v}" wails3 task darwin:package:universal \ VERSION="$VERSION" \ REPOSITORY="$GITHUB_REPOSITORY" wails3 task darwin:create:dmg mkdir -p release dmg="$(find bin -type f -name '*.dmg' -print -quit)" if [ -z "$dmg" ]; then echo "Expected macOS DMG was not generated" >&2 exit 1 fi cp "$dmg" "release/pvfine-${RELEASE_TAG}-macos-universal.dmg" ditto -c -k --sequesterRsrc --keepParent \ bin/pvfine.app \ "release/pvfine-${RELEASE_TAG}-darwin-universal-arm64-amd64.zip" - name: Upload macOS assets uses: actions/upload-artifact@v4 with: name: macos-release path: release/* if-no-files-found: error build-linux: name: Build Linux x64 runs-on: ubuntu-24.04 env: CGO_ENABLED: '1' GOARCH: amd64 steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 with: go-version-file: go.mod cache: true - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: npm cache-dependency-path: frontend/package-lock.json - name: Apply release version metadata shell: bash env: RELEASE_TAG: ${{ github.ref_name }} run: node scripts/set-build-version.js "${RELEASE_TAG#v}" - name: Install Linux desktop dependencies run: | sudo apt-get update sudo apt-get install -y \ build-essential \ libgtk-4-dev \ libwebkitgtk-6.0-dev \ pkg-config - name: Install Wails CLI shell: bash run: | go install github.com/wailsapp/wails/v3/cmd/wails3@${WAILS_VERSION} echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" - name: Build frontend before Go tests shell: bash run: wails3 task common:build:frontend - name: Run Go tests run: go test ./... - name: Build Linux AppImage shell: bash env: RELEASE_TAG: ${{ github.ref_name }} run: | VERSION="${RELEASE_TAG#v}" wails3 task linux:create:appimage \ ARCH=amd64 \ VERSION="$VERSION" \ REPOSITORY="$GITHUB_REPOSITORY" mkdir -p release appimage="$(find bin -type f -iname '*.AppImage' -print -quit)" if [ -z "$appimage" ]; then echo "Expected Linux AppImage was not generated" >&2 exit 1 fi cp "$appimage" "release/pvfine-${RELEASE_TAG}-linux-amd64.AppImage" chmod +x "release/pvfine-${RELEASE_TAG}-linux-amd64.AppImage" - name: Upload Linux asset uses: actions/upload-artifact@v4 with: name: linux-release path: release/* if-no-files-found: error publish: name: Publish GitHub Release runs-on: ubuntu-latest needs: - build-windows - build-macos - build-linux steps: - name: Download release assets uses: actions/download-artifact@v4 with: pattern: '*-release' path: release-assets merge-multiple: true - name: Verify release assets shell: bash run: | find release-assets -type f -print test "$(find release-assets -type f | wc -l)" -ge 5 - name: Generate checksums shell: bash run: | (cd release-assets && sha256sum -- * > SHA256SUMS) - name: Create or update GitHub Release env: GH_TOKEN: ${{ github.token }} RELEASE_TAG: ${{ github.ref_name }} shell: bash run: | if gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then gh release upload "$RELEASE_TAG" release-assets/* \ --repo "$GITHUB_REPOSITORY" \ --clobber gh release edit "$RELEASE_TAG" \ --repo "$GITHUB_REPOSITORY" \ --title "$RELEASE_TAG" \ --draft=false else gh release create "$RELEASE_TAG" release-assets/* \ --repo "$GITHUB_REPOSITORY" \ --title "$RELEASE_TAG" \ --generate-notes fi