name: Build & Verify Liven
on:
push:
branches: [main]
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: Full Build (${{ matrix.os }})
if: github.event_name == 'push'
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
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
- name: Cache Cargo dependencies
uses: Swatinem/rust-cache@v2
- name: Install mold linker (Linux)
if: matrix.os == 'ubuntu-latest'
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: Build release binary
run: cargo build --release
- name: Stage runtime config alongside binary
shell: bash
run: cp liven.toml target/release/liven.toml
- name: Run Installation Tests (Linux)
if: matrix.os == 'ubuntu-latest'
timeout-minutes: 10
run: |
echo "==== Linux Installation Tests ===="
chmod +x install.sh
# 1. Production install
sudo ./install.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/install.sh /install.sh &&
chmod +x /install.sh &&
mkdir -p /target/release &&
cp /workspace/target/release/liven /target/release/liven &&
chmod +x /target/release/liven &&
./install.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)
if: matrix.os == 'macos-latest'
timeout-minutes: 10
run: |
echo "==== macOS Installation Tests ===="
chmod +x install.sh
sudo ./install.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)
if: matrix.os == 'windows-latest'
shell: bash
timeout-minutes: 10
run: |
echo "==== Windows Installation Tests ===="
INSTALL_OUTPUT=$(./install.sh)
echo "$INSTALL_OUTPUT" | grep -q "Windows Deployment Instructions"
echo "PASS: Windows instructions successfully returned"
- name: Upload compiled executable (Unix)
if: matrix.os != 'windows-latest'
uses: actions/upload-artifact@v7
with:
name: liven-${{ matrix.os }}
path: |
target/release/liven
target/release/liven.toml
if-no-files-found: error
- name: Upload compiled executable (Windows)
if: matrix.os == 'windows-latest'
uses: actions/upload-artifact@v7
with:
name: liven-${{ matrix.os }}
path: |
target/release/liven.exe
target/release/liven.toml
if-no-files-found: error
package:
name: Package Distribution (${{ matrix.os }})
if: github.event_name == 'push'
needs: build
runs-on: ${{ matrix.os }}
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- name: Checkout repository
uses: actions/checkout@v7
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
- name: Cache Cargo dependencies
uses: Swatinem/rust-cache@v2
- name: Download compiled executable
uses: actions/download-artifact@v8
with:
name: liven-${{ matrix.os }}
path: target/release
- name: Install cargo-binstall (pre-built binary installer)
run: |
curl -fsSL https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
- name: Install packaging tools via binstall (skip compilation)
run: |
cargo binstall cargo-deb --no-confirm --locked 2>&1 || echo "Warning: cargo-deb install failed"
cargo binstall cargo-generate-rpm --no-confirm --locked 2>&1 || echo "Warning: cargo-generate-rpm install failed"
cargo binstall cargo-wix --no-confirm --locked 2>&1 || echo "Warning: cargo-wix install failed"
- name: Package Linux (.deb, .rpm, .tar.gz)
if: matrix.os == 'ubuntu-latest'
run: |
chmod +x target/release/liven
cargo deb --no-build || echo "Warning: cargo deb failed, skipping .deb"
cargo generate-rpm --no-build || echo "Warning: cargo generate-rpm failed, skipping .rpm"
tar -czvf liven-linux-amd64.tar.gz -C target/release liven liven.toml
mkdir -p dist
mv target/debian/*.deb dist/ || true
mv target/generate-rpm/*.rpm dist/ || true
mv liven-linux-amd64.tar.gz dist/
- name: Package Windows (.msi, .zip)
if: matrix.os == 'windows-latest'
shell: bash
run: |
echo "==== Packaging Windows Distribution Assets ===="
cargo wix --no-build || echo "Warning: cargo wix failed, skipping .msi"
powershell -Command "Compress-Archive -Path target/release/liven.exe, liven.toml -DestinationPath liven-windows-amd64.zip"
mkdir -p dist
mv target/wix/*.msi dist/ || true
mv liven-windows-amd64.zip dist/
- name: Package macOS (.tar.gz)
if: matrix.os == 'macos-latest'
run: |
echo "==== Packaging macOS Distribution Assets ===="
mkdir -p dist
chmod +x target/release/liven
tar -czvf liven-macos-x64.tar.gz -C target/release liven liven.toml
mv liven-macos-x64.tar.gz dist/
- name: Upload Packaged Installers
uses: actions/upload-artifact@v7
with:
name: liven-installers-${{ matrix.os }}
path: dist/*
if-no-files-found: ignore