Build #4
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 & Test | |
| # Runs on every push/PR touching the daemon, the greeter, or the UI, plus a | |
| # manual trigger. Three independent jobs (daemon / greeter / ui) so a | |
| # failure in one surface doesn't hide failures in the others, and so | |
| # they run in parallel rather than one long serial pipeline. | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| workflow_dispatch: | |
| env: | |
| CARGO_TERM_COLOR: always | |
| jobs: | |
| # ── daemon (Rust, no GUI toolkit deps — pure tokio + std) ───────────────── | |
| daemon: | |
| name: daemon (cargo fmt / clippy / test) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: rustfmt, clippy | |
| - name: Cache cargo registry & build artifacts | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: daemon | |
| - name: cargo fmt --check | |
| working-directory: daemon | |
| run: cargo fmt --check | |
| # --deny warnings: clippy found real, deny-by-default bugs in this | |
| # codebase before (identical if/else branches in pam_auth.rs) — this | |
| # flag is what makes clippy actually enforce that class of issue in | |
| # CI instead of only warning locally. | |
| - name: cargo clippy | |
| working-directory: daemon | |
| run: cargo clippy --all-targets -- -D warnings | |
| - name: cargo test | |
| working-directory: daemon | |
| run: cargo test --all-targets | |
| # ── greeter (Rust + Tauri v2 — needs the GTK/WebKitGTK dev headers) ─────── | |
| greeter: | |
| name: greeter (cargo fmt / clippy / check) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: rustfmt, clippy | |
| # Tauri v2's official Linux CI dependency list — without these, | |
| # anything that links webkit2gtk-sys / gtk-sys / javascriptcoregtk-sys | |
| # fails at the `cc`/`pkg-config` step, not at a Rust type-checking | |
| # step, which is a much more confusing failure to debug from a log. | |
| - name: Install Tauri Linux build dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| libwebkit2gtk-4.1-dev \ | |
| libgtk-3-dev \ | |
| librsvg2-dev \ | |
| libsoup-3.0-dev \ | |
| libjavascriptcoregtk-4.1-dev \ | |
| libayatana-appindicator3-dev \ | |
| patchelf \ | |
| build-essential \ | |
| curl \ | |
| wget \ | |
| file | |
| - name: Cache cargo registry & build artifacts | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: greeter | |
| - name: cargo fmt --check | |
| working-directory: greeter | |
| run: cargo fmt --check | |
| - name: cargo clippy | |
| working-directory: greeter | |
| run: cargo clippy --all-targets -- -D warnings | |
| # `cargo test` (not just `check`) here too: greeter/src/main.rs has | |
| # its own unit tests for the system-status parsers (any_interface_up, | |
| # parse_battery_info, parse_amixer_volume) that don't need a display | |
| # or real hardware to run in CI. | |
| - name: cargo test | |
| working-directory: greeter | |
| run: cargo test --all-targets | |
| # ── UI (Solid.js) ────────────────────────────────────────────────────── | |
| ui: | |
| name: ui (lint / test / build) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| cache-dependency-path: ui/package-lock.json | |
| - name: npm ci | |
| working-directory: ui | |
| run: npm ci | |
| - name: Formatting check (Prettier) | |
| working-directory: ui | |
| run: npm run format:check | |
| - name: Lint (ESLint + eslint-plugin-solid) | |
| working-directory: ui | |
| run: npm run lint | |
| - name: Unit tests (Vitest + @solidjs/testing-library) | |
| working-directory: ui | |
| run: npm run test | |
| - name: Type-check + production build | |
| working-directory: ui | |
| run: npm run build | |
| - name: Upload build artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ui-dist | |
| path: ui/dist | |
| retention-days: 7 | |
| # Convenience job so branch protection can require one green check | |
| # ("all-checks-passed") instead of hand-listing all three matrix jobs by | |
| # name, which silently stops protecting anything if a job is renamed. | |
| # ── binaries: full release build → downloadable artifact ───────────────── | |
| # | |
| # The three jobs above are fast correctness gates (fmt/clippy/lint/test). | |
| # This job is the actual "build everything into binaries" step: it | |
| # produces the real `bedm` (daemon) and `bedm-greeter` (greeter, with the | |
| # Solid.js UI bundled into it via Tauri's asset embedding — frontendDist | |
| # gets compiled into the executable, so ui/dist is not shipped | |
| # separately) executables, plus the packaging files needed to actually | |
| # install them (systemd unit, PAM service stub, default config), and | |
| # uploads the lot as one tarball. `needs: [daemon, greeter, ui]` means a | |
| # broken commit never gets this far — no point spending ~5 extra minutes | |
| # on a release build of code that already failed clippy or its tests. | |
| binaries: | |
| name: Build release binaries | |
| runs-on: ubuntu-latest | |
| needs: [daemon, greeter, ui] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Install Tauri Linux build dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| libwebkit2gtk-4.1-dev \ | |
| libgtk-3-dev \ | |
| librsvg2-dev \ | |
| libsoup-3.0-dev \ | |
| libjavascriptcoregtk-4.1-dev \ | |
| libayatana-appindicator3-dev \ | |
| patchelf \ | |
| build-essential | |
| - name: Install Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| cache-dependency-path: ui/package-lock.json | |
| - name: Cache cargo registry & build artifacts (release profile) | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: | | |
| daemon | |
| greeter | |
| key: release | |
| # ── daemon ────────────────────────────────────────────────────────── | |
| - name: cargo build --release (daemon) | |
| working-directory: daemon | |
| run: cargo build --release | |
| # ── ui + greeter ──────────────────────────────────────────────────── | |
| # `tauri build`'s beforeBuildCommand (see greeter/tauri.conf.json) | |
| # does `cd ../ui && npm run build` itself, but it does NOT run | |
| # `npm install` first — the ui/node_modules tree has to already be | |
| # there, or that step fails on the first missing package. | |
| - name: npm ci (ui) | |
| working-directory: ui | |
| run: npm ci | |
| - name: npm ci (greeter — installs @tauri-apps/cli) | |
| working-directory: greeter | |
| run: npm ci | |
| # bundle.active is false in tauri.conf.json (see greeter/tauri.conf.json) | |
| # — deliberately: BEDM ships as raw binaries + a systemd unit + | |
| # config, installed by build.hl / a .deb / an .rpm, not as a | |
| # self-contained Tauri .AppImage/.deb bundle. `tauri build` with | |
| # active:false just compiles the release binary and skips bundling. | |
| - name: tauri build (greeter — also builds the UI via beforeBuildCommand) | |
| working-directory: greeter | |
| run: npx tauri build | |
| - name: Verify both binaries exist and report their sizes | |
| run: | | |
| test -x daemon/target/release/bedm || { echo "::error::daemon binary missing"; exit 1; } | |
| test -x greeter/target/release/bedm-greeter || { echo "::error::greeter binary missing"; exit 1; } | |
| ls -lh daemon/target/release/bedm greeter/target/release/bedm-greeter | |
| # ── package everything needed for a real install ─────────────────── | |
| - name: Assemble release directory | |
| run: | | |
| set -euo pipefail | |
| VERSION="$(grep -m1 '^version' daemon/Cargo.toml | sed -E 's/version *= *"(.*)"/\1/')" | |
| echo "VERSION=$VERSION" >> "$GITHUB_ENV" | |
| OUT="bedm-${VERSION}-linux-x86_64" | |
| mkdir -p "$OUT" | |
| cp daemon/target/release/bedm "$OUT/bedm" | |
| cp greeter/target/release/bedm-greeter "$OUT/bedm-greeter" | |
| cp -r systemd "$OUT/" | |
| cp -r config "$OUT/" | |
| cp README.md LICENSE "$OUT/" | |
| tar -czf "${OUT}.tar.gz" "$OUT" | |
| echo "ARCHIVE=${OUT}.tar.gz" >> "$GITHUB_ENV" | |
| - name: Upload binaries artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: bedm-linux-x86_64 | |
| path: ${{ env.ARCHIVE }} | |
| retention-days: 30 | |
| # Only on a version tag (e.g. v1.0.0): also attach the tarball to a | |
| # GitHub Release so it's downloadable without digging through | |
| # Actions run history. Ordinary pushes/PRs only get the Actions | |
| # artifact above. | |
| - name: Create GitHub Release | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| files: ${{ env.ARCHIVE }} | |
| generate_release_notes: true | |
| all-checks-passed: | |
| name: All checks passed | |
| runs-on: ubuntu-latest | |
| needs: [daemon, greeter, ui, binaries] | |
| if: always() | |
| steps: | |
| - name: Fail if any dependency job failed | |
| run: | | |
| if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then | |
| echo "One or more required jobs failed." | |
| exit 1 | |
| fi |