name: Build & Verify Liven
on:
push:
branches: [main]
tags-ignore:
- "**"
pull_request:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
check:
name: Lint & Core Tests
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout repository
uses: actions/checkout@v7
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: "24"
cache: "npm"
cache-dependency-path: "ui/package-lock.json"
- name: Install UI dependencies & Build Frontend
working-directory: ui
shell: bash
run: |
npm ci --legacy-peer-deps
npm run build
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt
- name: Cache Cargo dependencies
uses: Swatinem/rust-cache@v2
- name: Verify code formatting
run: cargo fmt --all -- --check
- name: Lint with clippy (lib + tests, no benches)
run: cargo clippy --lib --tests -- -D warnings
- name: Test embedded core (no default features)
run: cargo test --no-default-features --lib --tests
- name: Test full suite (lib + tests, no benches)
run: cargo test --lib --tests
- name: Audit dependencies
run: |
cargo install cargo-audit --locked 2>&1 || true
cargo audit
build:
name: Build (${{ matrix.target }})
if: github.event_name == 'push'
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
native: true
platform: linux
arch: amd64
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
native: false
platform: linux
arch: arm64
- os: macos-latest
target: aarch64-apple-darwin
native: true
platform: macos
arch: arm64
- os: macos-latest
target: x86_64-apple-darwin
native: false
platform: macos
arch: amd64
- os: windows-latest
target: x86_64-pc-windows-msvc
native: true
platform: windows
arch: amd64
- os: windows-latest
target: aarch64-pc-windows-msvc
native: false
platform: windows
arch: arm64
steps:
- name: Checkout repository
uses: actions/checkout@v7
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: "24"
cache: "npm"
cache-dependency-path: "ui/package-lock.json"
- name: Install UI dependencies & Build Frontend
working-directory: ui
shell: bash
run: |
npm ci --legacy-peer-deps
npm run build
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
components: clippy
targets: ${{ matrix.target }}
- name: Cache Cargo dependencies
uses: Swatinem/rust-cache@v2
with:
key: ${{ matrix.target }}
- name: Install mold linker (Linux amd64)
if: matrix.target == 'x86_64-unknown-linux-gnu'
run: |
sudo apt-get update -qq
sudo apt-get install -y -qq mold
echo "CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=clang" >> $GITHUB_ENV
echo "RUSTFLAGS=-C link-arg=-fuse-ld=mold" >> $GITHUB_ENV
- name: Install aarch64 cross toolchain (Linux arm64)
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: |
sudo apt-get update -qq
sudo apt-get install -y -qq gcc-aarch64-linux-gnu
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV
- name: Build release binary
run: cargo build --release --target ${{ matrix.target }}
- name: Stage runtime config alongside binary
shell: bash
run: cp liven.toml target/${{ matrix.target }}/release/liven.toml
- name: Run Installation Tests (Linux amd64)
if: matrix.target == 'x86_64-unknown-linux-gnu'
timeout-minutes: 10
run: |
echo "==== Linux Installation Tests ===="
chmod +x setup.sh
# setup.sh expects the binary at target/release/, not the
# target-triple-scoped path cargo writes to with --target.
mkdir -p target/release
cp target/${{ matrix.target }}/release/liven target/release/liven
# 1. Production install
sudo ./setup.sh --env production
grep -q 'environment = "production"' /etc/liven/liven.toml
test -f /etc/liven/certs/ca.crt
test -f /etc/liven/certs/ca.key
test -f /etc/liven/certs/server.crt
test -f /etc/liven/certs/server.key
[ "$(stat -c '%a' /etc/liven/certs/server.key)" = "600" ]
[ "$(stat -c '%a' /etc/liven/certs/ca.key)" = "600" ]
test -f /etc/systemd/system/liven.service
# 2. Container fallback (Debian)
docker run --rm -v ${{ github.workspace }}:/workspace debian:bookworm-slim sh -c "
cp /workspace/setup.sh /setup.sh &&
chmod +x /setup.sh &&
mkdir -p /target/release &&
cp /workspace/target/${{ matrix.target }}/release/liven /target/release/liven &&
chmod +x /target/release/liven &&
./setup.sh --env development &&
test -f /usr/local/bin/liven &&
test -f /usr/local/bin/liven-entrypoint &&
! test -f /etc/systemd/system/liven.service &&
grep -q 'environment = \"development\"' /etc/liven/liven.toml
"
# 3. Live-process boot
echo "Booting mTLS protected production LIVEN in background..."
sudo /usr/local/bin/liven start --config /etc/liven/liven.toml &
DB_PID=$!
sleep 3
sudo ss -tlnp | grep -q 43121
# --max-time bounds the WHOLE request (not just the TCP connect phase).
# Without it, a TCP connection that's accepted but never completes the
# TLS handshake leaves curl blocked indefinitely waiting on the socket.
curl -v --connect-timeout 3 --max-time 8 http://127.0.0.1:43121/api/streams \
&& (echo "FAIL: Cleartext succeeded in production!" && exit 1) \
|| echo "PASS: Cleartext rejected by mTLS shield"
sudo kill -15 $DB_PID || true
- name: Run Installation Tests (macOS arm64)
if: matrix.target == 'aarch64-apple-darwin'
timeout-minutes: 10
run: |
echo "==== macOS Installation Tests ===="
chmod +x setup.sh
# setup.sh expects the binary at target/release/, not the
# target-triple-scoped path cargo writes to with --target.
mkdir -p target/release
cp target/${{ matrix.target }}/release/liven target/release/liven
sudo ./setup.sh --env development
grep -q 'environment = "development"' /etc/liven/liven.toml
test -f /Library/LaunchDaemons/com.liven.liven.plist
test -f /usr/local/bin/liven
echo "Booting cleartext development LIVEN in background..."
sudo /usr/local/bin/liven start --config /etc/liven/liven.toml &
DB_PID=$!
sleep 3
if curl -v --connect-timeout 3 --max-time 8 http://127.0.0.1:43120/api/streams; then
echo "PASS: Development API connection succeeded"
else
echo "FAIL: Development API connection failed" && exit 1
fi
sudo kill -15 $DB_PID || true
- name: Run Installation Tests (Windows amd64)
if: matrix.target == 'x86_64-pc-windows-msvc'
shell: bash
timeout-minutes: 10
run: |
echo "==== Windows Installation Tests ===="
INSTALL_OUTPUT=$(./setup.sh)
echo "$INSTALL_OUTPUT" | grep -q "Windows Deployment Instructions"
echo "PASS: Windows instructions successfully returned"
- name: Upload compiled executable (Unix)
if: matrix.platform != 'windows'
uses: actions/upload-artifact@v7
with:
name: liven-${{ matrix.target }}
path: |
target/${{ matrix.target }}/release/liven
target/${{ matrix.target }}/release/liven.toml
if-no-files-found: error
- name: Upload compiled executable (Windows)
if: matrix.platform == 'windows'
uses: actions/upload-artifact@v7
with:
name: liven-${{ matrix.target }}
path: |
target/${{ matrix.target }}/release/liven.exe
target/${{ matrix.target }}/release/liven.toml
if-no-files-found: error