Skip to content

Build

Build #8

Workflow file for this run

name: Build
# Produces the actual downloadable HDM release: the `hdm` (daemon) and
# `hdm-greeter` (greeter, Tauri v2 — the Solid.js UI is bundled into it via
# Tauri's asset embedding, so ui/dist is not shipped separately) release
# binaries, plus the packaging files needed to actually install them
# (systemd unit, PAM service stub, default config), tarballed up.
#
# This is deliberately a SEPARATE workflow from test.yml (fmt/clippy/lint/
# unit tests): test.yml is a fast correctness gate that runs on every push
# and PR; this one does a full `--release` build plus a Tauri build with
# all the GTK/WebKitGTK system dependencies, which takes meaningfully
# longer and isn't something every PR needs to pay for. It runs:
# - on every push to main (so main always has a fresh downloadable build)
# - on version tags (v*.*.*), where it also attaches the tarball to a
# GitHub Release
# - on manual dispatch, for building a one-off from any branch/commit
on:
push:
branches: [main]
tags: ['v*.*.*']
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
jobs:
build:
name: Build release binaries
runs-on: ubuntu-latest
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'
# See test.yml for why this deliberately does NOT use
# `cache: npm` / `cache-dependency-path`: that combination
# hard-fails the whole step if the lockfile path can't be
# resolved, instead of just skipping the cache. Caching is done
# manually below for the same reason.
- name: Cache npm dependencies (ui)
uses: actions/cache@v4
with:
path: ui/node_modules
key: ${{ runner.os }}-npm-ui-${{ hashFiles('ui/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-ui-
- name: Cache npm dependencies (greeter)
uses: actions/cache@v4
with:
path: greeter/node_modules
key: ${{ runner.os }}-npm-greeter-${{ hashFiles('greeter/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-greeter-
- 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. Same
# graceful `npm ci` → `npm install` fallback as test.yml (see the
# comment there for why).
- name: Install dependencies (ui)
working-directory: ui
run: |
if [ -f package-lock.json ]; then
npm ci
else
echo "::warning::ui/package-lock.json is missing — falling back to 'npm install'."
npm install
fi
- name: Install dependencies (greeter — installs @tauri-apps/cli)
working-directory: greeter
run: |
if [ -f package-lock.json ]; then
npm ci
else
echo "::warning::greeter/package-lock.json is missing — falling back to 'npm install'."
npm install
fi
# bundle.active is false in tauri.conf.json (see greeter/tauri.conf.json)
# — deliberately: HDM 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/hdm || { echo "::error::daemon binary missing"; exit 1; }
test -x greeter/target/release/hdm-greeter || { echo "::error::greeter binary missing"; exit 1; }
ls -lh daemon/target/release/hdm greeter/target/release/hdm-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="hdm-${VERSION}-linux-x86_64"
mkdir -p "$OUT"
cp daemon/target/release/hdm "$OUT/hdm"
cp greeter/target/release/hdm-greeter "$OUT/hdm-greeter"
# config/ already contains config/systemd/hdm.service, the
# default config/hdm.hk, and config/pam-hdm — there is no
# separate top-level systemd/ directory in this repo.
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: hdm-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. Plain pushes to main only get the Actions artifact
# above (a "latest main build", not a public release).
- 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