Skip to content

Build

Build #4

Workflow file for this run

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