liven 0.0.1

High-velocity embedded database with streaming pipelines, vector search, and real-time subscriptions
Documentation
name: Build & Verify Liven

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

# Ensure only one workflow runs per branch at a time to optimize resources
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  # ── Fast check: runs on every PR and push ──
  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

  # ── Full build: cross-platform, release, install tests, packaging ──
  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

      # ── Use mold linker on Linux for ~5x faster LTO linking ──
      - 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

      # ── Installation tests ──
      - 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

  # ── Packaging: depends on build artifacts ──
  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