Release #18
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: Release | |
| on: | |
| push: | |
| tags: ['v*'] | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: 'Tag to release (e.g. v0.1.0)' | |
| required: true | |
| permissions: {} | |
| env: | |
| TAG: ${{ github.event.inputs.tag || github.ref_name }} | |
| jobs: | |
| # ── Prepare version info ────────────────────────────────── | |
| prepare: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.version.outputs.version }} | |
| electrobun-env: ${{ steps.version.outputs.electrobun_env }} | |
| is-prerelease: ${{ steps.version.outputs.is_prerelease }} | |
| npm-tag: ${{ steps.version.outputs.npm_tag }} | |
| steps: | |
| - name: Parse version from tag | |
| id: version | |
| run: | | |
| TAG="${{ env.TAG }}" | |
| VERSION="${TAG#v}" | |
| echo "version=${VERSION}" >> "$GITHUB_OUTPUT" | |
| if [[ "$TAG" == *"-beta"* ]] || [[ "$TAG" == *"-alpha"* ]] || [[ "$TAG" == *"-rc"* ]]; then | |
| echo "electrobun_env=canary" >> "$GITHUB_OUTPUT" | |
| echo "is_prerelease=true" >> "$GITHUB_OUTPUT" | |
| echo "npm_tag=beta" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "electrobun_env=stable" >> "$GITHUB_OUTPUT" | |
| echo "is_prerelease=false" >> "$GITHUB_OUTPUT" | |
| echo "npm_tag=latest" >> "$GITHUB_OUTPUT" | |
| fi | |
| # ── Desktop builds (Electrobun) ─────────────────────────── | |
| desktop: | |
| needs: prepare | |
| permissions: | |
| contents: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| platform: linux-x64 | |
| artifact: dotaz-linux-x64 | |
| - os: ubuntu-24.04-arm | |
| platform: linux-arm64 | |
| artifact: dotaz-linux-arm64 | |
| - os: macos-14 | |
| platform: macos-arm64 | |
| artifact: dotaz-macos-arm64 | |
| - os: macos-15-large | |
| platform: macos-x64 | |
| artifact: dotaz-macos-x64 | |
| - os: windows-latest | |
| platform: win-x64 | |
| artifact: dotaz-win-x64 | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: oven-sh/setup-bun@v2 | |
| - name: Install dependencies | |
| run: bun install --frozen-lockfile | |
| - name: Build frontend | |
| run: bunx vite build | |
| - name: Build desktop app | |
| run: bunx electrobun build --env=${{ needs.prepare.outputs.electrobun-env }} | |
| - name: Extract app from Electrobun installer (Linux) | |
| if: runner.os == 'Linux' | |
| run: | | |
| BUILD_DIR="build/${{ needs.prepare.outputs.electrobun-env }}-${{ matrix.platform }}" | |
| APP_DIR=$(find "$BUILD_DIR" -maxdepth 1 -type d -name "Dotaz*" | head -1) | |
| ARCHIVE=$(find "$APP_DIR/Resources" -name "*.tar.zst" 2>/dev/null | head -1) | |
| if [ -n "$ARCHIVE" ]; then | |
| echo "Extracting app from installer archive: $ARCHIVE" | |
| EXTRACT_DIR=$(mktemp -d) | |
| tar --zstd -xf "$ARCHIVE" -C "$EXTRACT_DIR" | |
| rm -rf "$APP_DIR" | |
| EXTRACTED=$(find "$EXTRACT_DIR" -maxdepth 1 -type d -name "Dotaz*" | head -1) | |
| mv "$EXTRACTED" "$APP_DIR" | |
| rm -rf "$EXTRACT_DIR" | |
| fi | |
| - name: Extract app from Electrobun installer (Windows) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| $buildDir = "build/${{ needs.prepare.outputs.electrobun-env }}-${{ matrix.platform }}" | |
| $appDir = Get-ChildItem -Path $buildDir -Directory -Filter "Dotaz*" | Select-Object -First 1 | |
| $archive = Get-ChildItem -Path "$($appDir.FullName)\Resources" -Filter "*.tar.zst" -ErrorAction SilentlyContinue | Select-Object -First 1 | |
| if ($archive) { | |
| Write-Host "Extracting app from installer archive: $($archive.FullName)" | |
| $extractDir = Join-Path ([System.IO.Path]::GetTempPath()) "dotaz-extract-$(Get-Random)" | |
| New-Item -ItemType Directory -Path $extractDir -Force | Out-Null | |
| tar --zstd -xf $archive.FullName -C $extractDir | |
| Remove-Item -Recurse -Force $appDir.FullName | |
| $extracted = Get-ChildItem -Path $extractDir -Directory -Filter "Dotaz*" | Select-Object -First 1 | |
| Move-Item -Path $extracted.FullName -Destination $appDir.FullName | |
| Remove-Item -Recurse -Force $extractDir | |
| } | |
| - name: Embed icon into Windows executables | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| $buildDir = "build/${{ needs.prepare.outputs.electrobun-env }}-${{ matrix.platform }}" | |
| $appDir = Get-ChildItem -Path $buildDir -Directory -Filter "Dotaz*" | Select-Object -First 1 | |
| $iconPng = Resolve-Path "assets\icon.png" | |
| # Install rcedit and png-to-ico in isolated temp dir (avoids workspace:* conflict) | |
| $tempDir = Join-Path ([System.IO.Path]::GetTempPath()) "icon-tools" | |
| New-Item -ItemType Directory -Path $tempDir -Force | Out-Null | |
| Push-Location $tempDir | |
| npm init -y | Out-Null | |
| npm install rcedit png-to-ico --silent | |
| Pop-Location | |
| # Convert PNG to ICO | |
| $iconIco = Join-Path $buildDir "app-icon.ico" | |
| node -e "const m=require('$($tempDir -replace '\\','/')/node_modules/png-to-ico');const p=m.default||m;const fs=require('fs');p('$($iconPng -replace '\\','/')').then(b=>fs.writeFileSync('$($iconIco -replace '\\','/')',b))" | |
| # Embed icon into executables and copy .ico for shortcuts | |
| $rcedit = Join-Path $tempDir "node_modules\rcedit\bin\rcedit-x64.exe" | |
| & $rcedit "$($appDir.FullName)\bin\launcher.exe" --set-icon $iconIco | |
| & $rcedit "$($appDir.FullName)\bin\bun.exe" --set-icon $iconIco | |
| Copy-Item $iconIco "$($appDir.FullName)\Resources\app.ico" | |
| Write-Host "Icon embedded into launcher.exe and bun.exe" | |
| - name: Package artifact (Unix) | |
| if: runner.os != 'Windows' | |
| run: | | |
| BUILD_DIR="build/${{ needs.prepare.outputs.electrobun-env }}-${{ matrix.platform }}" | |
| tar -czf ${{ matrix.artifact }}.tar.gz -C "$BUILD_DIR" . | |
| - name: Package artifact (Windows) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| $buildDir = "build/${{ needs.prepare.outputs.electrobun-env }}-${{ matrix.platform }}" | |
| Compress-Archive -Path "$buildDir\*" -DestinationPath "${{ matrix.artifact }}.zip" | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ matrix.artifact }} | |
| path: | | |
| ${{ matrix.artifact }}.tar.gz | |
| ${{ matrix.artifact }}.zip | |
| if-no-files-found: error | |
| - name: Upload update artifacts | |
| if: hashFiles('artifacts/*') != '' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: update-${{ matrix.platform }} | |
| path: artifacts/* | |
| # ── Docker image ────────────────────────────────────────── | |
| docker: | |
| needs: prepare | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Docker meta | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ghcr.io/${{ github.repository }} | |
| tags: | | |
| type=semver,pattern={{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| type=raw,value=latest,enable=${{ needs.prepare.outputs.is-prerelease == 'false' }} | |
| - name: Build and push | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| push: true | |
| platforms: linux/amd64,linux/arm64 | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| # ── npm package (trusted publisher — no NPM_TOKEN needed) ─ | |
| npm: | |
| needs: prepare | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: oven-sh/setup-bun@v2 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '24.x' | |
| registry-url: 'https://registry.npmjs.org' | |
| - name: Install dependencies | |
| run: bun install --frozen-lockfile | |
| - name: Build server package | |
| run: bun run build:server | |
| env: | |
| VERSION: ${{ needs.prepare.outputs.version }} | |
| - name: Publish to npm | |
| run: npm publish --tag ${{ needs.prepare.outputs.npm-tag }} --access public --provenance | |
| working-directory: dist-server | |
| # ── GitHub Release ──────────────────────────────────────── | |
| release: | |
| needs: [prepare, desktop, docker, npm] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Download all desktop artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| pattern: dotaz-* | |
| merge-multiple: true | |
| - name: Download update artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| pattern: update-* | |
| merge-multiple: true | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ env.TAG }} | |
| name: Dotaz ${{ needs.prepare.outputs.version }} | |
| prerelease: ${{ needs.prepare.outputs.is-prerelease == 'true' }} | |
| generate_release_notes: true | |
| files: | | |
| artifacts/* |