Draft: UX effort #56
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 & Release | |
| on: | |
| push: | |
| branches: [main] | |
| tags: ['v*'] | |
| pull_request: | |
| branches: [main] | |
| permissions: | |
| contents: write | |
| jobs: | |
| # ── Build silex-server + silex-desktop per platform ────────────── | |
| build: | |
| name: build (${{ matrix.platform }}) | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 30 | |
| strategy: | |
| matrix: | |
| include: | |
| - platform: linux | |
| os: ubuntu-latest | |
| binary: silex-server | |
| - platform: macos-arm | |
| os: macos-latest | |
| binary: silex-server | |
| - platform: macos-x64 | |
| os: macos-latest | |
| rust-target: x86_64-apple-darwin | |
| binary: silex-server | |
| - platform: windows | |
| os: windows-latest | |
| binary: silex-server.exe | |
| steps: | |
| # Prevent CRLF conversion on Windows (eslint requires LF) | |
| - name: Force LF line endings | |
| run: git config --global core.autocrlf false | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: true | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.rust-target || '' }} | |
| - name: Rust cache | |
| uses: swatinem/rust-cache@v2 | |
| - name: Install system dependencies (Linux) | |
| if: matrix.os == 'ubuntu-latest' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf | |
| - name: Enable Corepack (provides yarn) | |
| run: corepack enable | |
| - name: Use npm registry (yarnpkg.com has intermittent 500s) | |
| run: yarn config set registry https://registry.npmjs.org | |
| # On Windows, npm/yarn default to cmd.exe for scripts, which breaks | |
| # Unix shell syntax (rm -rf, $var) used in package build scripts. | |
| - name: Use bash for package scripts (Windows) | |
| if: matrix.os == 'windows-latest' | |
| run: npm config set script-shell "C:\Program Files\Git\bin\bash.exe" | |
| - name: Install JS dependencies and build frontend | |
| shell: bash | |
| run: yarn install --ignore-scripts && yarn run build | |
| # Fail early if the frontend wasn't built (prevents broken embedded assets) | |
| - name: Verify frontend was built | |
| shell: bash | |
| run: | | |
| if [ ! -f packages/silex-lib/dist/client/index.html ]; then | |
| echo "::error::Frontend build failed — packages/silex-lib/dist/client/index.html not found" | |
| exit 1 | |
| fi | |
| # ── silex-desktop (Tauri) ──────────────────────────────────── | |
| # Build Tauri first — it compiles silex-server as a library dependency. | |
| # The standalone server build below reuses the cached compilation. | |
| # Sign updater artifacts only on tag pushes (secrets unavailable on fork PRs). | |
| - name: Build Tauri app (signed) | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| uses: tauri-apps/tauri-action@v0 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} | |
| TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} | |
| GLITCHTIP_DSN: ${{ secrets.GLITCHTIP_DSN }} | |
| with: | |
| projectPath: packages/silex-desktop | |
| tauriScript: yarn tauri | |
| args: ${{ matrix.rust-target && format('--target {0}', matrix.rust-target) || '' }} | |
| # Write a temp config to disable updater artifacts (they require signing keys) | |
| - name: Disable updater artifacts config | |
| if: ${{ !startsWith(github.ref, 'refs/tags/v') }} | |
| shell: bash | |
| run: echo '{"bundle":{"createUpdaterArtifacts":false}}' > packages/silex-desktop/tauri.ci.json | |
| - name: Build Tauri app (unsigned) | |
| if: ${{ !startsWith(github.ref, 'refs/tags/v') }} | |
| uses: tauri-apps/tauri-action@v0 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GLITCHTIP_DSN: ${{ secrets.GLITCHTIP_DSN }} | |
| with: | |
| projectPath: packages/silex-desktop | |
| tauriScript: yarn tauri | |
| args: --config tauri.ci.json ${{ matrix.rust-target && format('--target {0}', matrix.rust-target) || '' }} | |
| # target/release/bundle/ for native builds, target/<triple>/release/bundle/ for cross-builds. | |
| # The ** glob covers both paths without duplication. | |
| - name: Upload desktop artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: silex-desktop-${{ matrix.platform }} | |
| path: | | |
| target/**/release/bundle/**/*.dmg | |
| target/**/release/bundle/**/*.app | |
| target/**/release/bundle/**/*.deb | |
| target/**/release/bundle/**/*.rpm | |
| target/**/release/bundle/**/*.exe | |
| target/**/release/bundle/**/*.sig | |
| target/**/release/bundle/**/*.tar.gz | |
| target/**/release/bundle/**/*.zip | |
| # ── silex-server (single binary with embedded frontend) ───── | |
| # Built after Tauri to reuse the compiled silex-server library. | |
| - name: Build silex-server | |
| run: cargo build --release -p silex-server --features embed-frontend ${{ matrix.rust-target && format('--target {0}', matrix.rust-target) || '' }} | |
| - name: Upload server artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: silex-server-${{ matrix.platform }} | |
| path: target/**/release/${{ matrix.binary }} | |
| retention-days: 5 | |
| # ── Create GitHub release on tag push ──────────────────────────── | |
| release: | |
| needs: build | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: true | |
| fetch-depth: 0 | |
| - name: Find previous tag | |
| id: prev_tag | |
| run: | | |
| # Find the previous tag of the same type (prerelease or stable) | |
| CURRENT="${GITHUB_REF_NAME}" | |
| if [[ "$CURRENT" == *-* ]]; then | |
| # Prerelease: find previous prerelease tag | |
| PREV=$(git tag --sort=-version:refname | grep -E '^v.*-' | grep -v "^${CURRENT}$" | head -1) | |
| else | |
| # Stable: find previous stable tag | |
| PREV=$(git tag --sort=-version:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | grep -v "^${CURRENT}$" | head -1) | |
| fi | |
| echo "tag=${PREV:-}" >> "$GITHUB_OUTPUT" | |
| echo "Previous tag: ${PREV:-none}" | |
| - name: Generate release notes | |
| id: changelog | |
| run: | | |
| PREV="${{ steps.prev_tag.outputs.tag }}" | |
| if [[ -n "$PREV" ]]; then | |
| BODY=$(./scripts/generate-changelog.sh "${PREV}..${GITHUB_REF_NAME}") | |
| else | |
| BODY="## What's Changed"$'\n\n'"First release!" | |
| fi | |
| # Write to file (multiline output) | |
| echo "$BODY" > release-notes.md | |
| cat release-notes.md | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: Prepare release files | |
| env: | |
| VERSION: ${{ github.ref_name }} | |
| REPO: ${{ github.repository }} | |
| run: | | |
| mkdir -p release | |
| # Server binaries — find inside artifact dirs (upload preserves target/ path) | |
| find artifacts/silex-server-linux -name 'silex-server' -type f -exec cp {} release/silex-server-linux-amd64 \; | |
| find artifacts/silex-server-macos-arm -name 'silex-server' -type f -exec cp {} release/silex-server-macos-arm64 \; | |
| find artifacts/silex-server-macos-x64 -name 'silex-server' -type f -exec cp {} release/silex-server-macos-x64 \; | |
| find artifacts/silex-server-windows -name 'silex-server.exe' -type f -exec cp {} release/silex-server-windows-amd64.exe \; | |
| # Desktop installers + updater artifacts (flatten into release/) | |
| find artifacts/silex-desktop-* -type f \ | |
| \( -name '*.deb' -o -name '*.rpm' -o -name '*.dmg' -o -name '*.exe' \ | |
| -o -name '*.tar.gz' -o -name '*.tar.gz.sig' \ | |
| -o -name '*.zip' -o -name '*.zip.sig' \) \ | |
| -exec cp {} release/ \; | |
| # Helper: find file + read its .sig | |
| sig() { cat "$1.sig" 2>/dev/null || echo ""; } | |
| # Build latest.json for the Tauri updater | |
| BASE="https://github.com/${REPO}/releases/download/${VERSION}" | |
| LINUX_TAR=$(find release -name '*_amd64.tar.gz' ! -name '*.sig' | head -1) | |
| MACOS_ARM_TAR=$(find release -name '*_aarch64.app.tar.gz' | head -1) | |
| MACOS_X64_TAR=$(find release -name '*_x64.app.tar.gz' | head -1) | |
| WINDOWS_ZIP=$(find release -name '*_x64-setup*.zip' ! -name '*.sig' | head -1) | |
| jq -n \ | |
| --arg ver "${VERSION#v}" \ | |
| --arg date "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ | |
| --arg lu "${BASE}/$(basename "$LINUX_TAR")" \ | |
| --arg ls "$(sig "$LINUX_TAR")" \ | |
| --arg mau "${BASE}/$(basename "$MACOS_ARM_TAR")" \ | |
| --arg mas "$(sig "$MACOS_ARM_TAR")" \ | |
| --arg mxu "${BASE}/$(basename "$MACOS_X64_TAR")" \ | |
| --arg mxs "$(sig "$MACOS_X64_TAR")" \ | |
| --arg wu "${BASE}/$(basename "$WINDOWS_ZIP")" \ | |
| --arg ws "$(sig "$WINDOWS_ZIP")" \ | |
| '{ | |
| version: $ver, pub_date: $date, | |
| platforms: { | |
| "linux-x86_64": { url: $lu, signature: $ls }, | |
| "darwin-aarch64": { url: $mau, signature: $mas }, | |
| "darwin-x86_64": { url: $mxu, signature: $mxs }, | |
| "windows-x86_64": { url: $wu, signature: $ws } | |
| } | |
| }' > release/latest.json | |
| echo "--- latest.json ---" | |
| cat release/latest.json | |
| echo "--- release files ---" | |
| ls -lh release/ | |
| - name: Create GitHub release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| body_path: release-notes.md | |
| draft: true | |
| prerelease: ${{ contains(github.ref_name, 'canary') || contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') }} | |
| files: release/* |