name: CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
build-web:
name: Build Web UI
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- name: Install dependencies
run: |
cd web
npm ci
- name: Type check
run: |
cd web
npm run type-check
- name: Lint
run: |
cd web
npm run lint
- name: Build static export
run: |
cd web
npm run build
- name: Upload web build artifact
uses: actions/upload-artifact@v4
with:
name: web-ui-build
path: web/out/
if-no-files-found: error
check:
name: Check
runs-on: ubuntu-latest
needs: [build-web]
steps:
- uses: actions/checkout@v4
- name: Free disk space
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf /usr/local/lib/android
sudo rm -rf /opt/ghc
sudo rm -rf /opt/hostedtoolcache/CodeQL
sudo docker image prune --all --force
df -h
- name: Download web build
uses: actions/download-artifact@v4
with:
name: web-ui-build
path: web/out/
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo index
uses: actions/cache@v4
with:
path: ~/.cargo/git
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo build
uses: actions/cache@v4
with:
path: target
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
- name: Check formatting
run: cargo fmt --all -- --check
- name: Run clippy
run: cargo clippy --all-targets --all-features -- -D clippy::correctness -D clippy::suspicious -D clippy::perf -W clippy::style -W clippy::complexity
- name: Check documentation
run: cargo doc --no-deps --all-features
test:
name: Test Suite (${{ matrix.os }}, ${{ matrix.rust }})
runs-on: ${{ matrix.os }}
needs: [build-web]
continue-on-error: ${{ matrix.rust == 'beta' }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
rust: stable
- os: ubuntu-latest
rust: beta
- os: macos-latest
rust: stable
- os: windows-latest
rust: stable
steps:
- uses: actions/checkout@v4
- name: Free disk space (Ubuntu only)
if: matrix.os == 'ubuntu-latest'
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf /usr/local/lib/android
sudo rm -rf /opt/ghc
sudo rm -rf /opt/hostedtoolcache/CodeQL
sudo docker image prune --all --force
df -h
- name: Download web build
uses: actions/download-artifact@v4
with:
name: web-ui-build
path: web/out/
- name: Verify web build artifact
shell: bash
run: |
echo "Checking web/out directory..."
if [ ! -d "web/out" ]; then
echo "WARNING: web/out directory does not exist"
ls -la web/ || true
else
echo "Directory exists. Contents:"
ls -la web/out/ || true
echo "File count:"
find web/out -type f 2>/dev/null | wc -l || echo "0"
fi
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-${{ matrix.rust }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo index
uses: actions/cache@v4
with:
path: ~/.cargo/git
key: ${{ runner.os }}-${{ matrix.rust }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo build
uses: actions/cache@v4
with:
path: target
key: ${{ runner.os }}-${{ matrix.rust }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
- name: Run unit tests (lib, default features)
run: cargo test --lib --verbose
- name: Run unit tests (bins, default features)
run: cargo test --bins --verbose
- name: Clean build artifacts before all-features
run: cargo clean --release
- name: Run unit tests (lib, all features)
run: cargo test --lib --all-features --verbose
- name: Run unit tests (bins, all features)
run: cargo test --bins --all-features --verbose
- name: Clean build artifacts before TUI
run: cargo clean --release
- name: Run unit tests (lib, TUI feature)
run: cargo test --lib --features tui --verbose
- name: Run unit tests (bins, TUI feature)
run: cargo test --bins --features tui --verbose
- name: Run doc tests
run: cargo test --doc --all-features
build-tui:
name: Build TUI (${{ matrix.target }})
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
artifact_name: periplon-tui
asset_name: periplon-tui-linux-x86_64
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
artifact_name: periplon-tui
asset_name: periplon-tui-linux-aarch64
- os: macos-13
target: x86_64-apple-darwin
artifact_name: periplon-tui
asset_name: periplon-tui-macos-x86_64
- os: macos-latest
target: aarch64-apple-darwin
artifact_name: periplon-tui
asset_name: periplon-tui-macos-aarch64
- os: windows-latest
target: x86_64-pc-windows-msvc
artifact_name: periplon-tui.exe
asset_name: periplon-tui-windows-x86_64.exe
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install cross-compilation tools (Linux ARM64)
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo index
uses: actions/cache@v4
with:
path: ~/.cargo/git
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo build
uses: actions/cache@v4
with:
path: target
key: ${{ runner.os }}-${{ matrix.target }}-cargo-build-${{ hashFiles('**/Cargo.lock') }}
- name: Build TUI binary
run: cargo build --release --bin periplon-tui --features tui --target ${{ matrix.target }}
- name: Strip binary (Unix)
if: matrix.os != 'windows-latest'
run: |
if [ "${{ matrix.target }}" = "aarch64-unknown-linux-gnu" ]; then
aarch64-linux-gnu-strip target/${{ matrix.target }}/release/${{ matrix.artifact_name }}
else
strip target/${{ matrix.target }}/release/${{ matrix.artifact_name }}
fi
- name: Calculate checksum (Unix)
if: matrix.os != 'windows-latest'
run: |
cd target/${{ matrix.target }}/release
shasum -a 256 ${{ matrix.artifact_name }} > ${{ matrix.asset_name }}.sha256
- name: Calculate checksum (Windows)
if: matrix.os == 'windows-latest'
run: |
cd target/${{ matrix.target }}/release
certutil -hashfile ${{ matrix.artifact_name }} SHA256 > ${{ matrix.asset_name }}.sha256
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.asset_name }}
path: |
target/${{ matrix.target }}/release/${{ matrix.artifact_name }}
target/${{ matrix.target }}/release/${{ matrix.asset_name }}.sha256
if-no-files-found: error
build-executor:
name: Build Executor (${{ matrix.target }})
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
artifact_name: periplon-executor
asset_name: periplon-executor-linux-x86_64
- os: macos-13
target: x86_64-apple-darwin
artifact_name: periplon-executor
asset_name: periplon-executor-macos-x86_64
- os: macos-latest
target: aarch64-apple-darwin
artifact_name: periplon-executor
asset_name: periplon-executor-macos-aarch64
- os: windows-latest
target: x86_64-pc-windows-msvc
artifact_name: periplon-executor.exe
asset_name: periplon-executor-windows-x86_64.exe
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
- name: Build executor binary
run: cargo build --release --bin periplon-executor --target ${{ matrix.target }}
- name: Strip binary (Unix)
if: matrix.os != 'windows-latest'
run: strip target/${{ matrix.target }}/release/${{ matrix.artifact_name }}
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.asset_name }}
path: target/${{ matrix.target }}/release/${{ matrix.artifact_name }}
if-no-files-found: error
audit:
name: Security Audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install cargo-audit
run: cargo install cargo-audit
- name: Run security audit
run: cargo audit
coverage:
name: Code Coverage
runs-on: ubuntu-latest
continue-on-error: true needs: [build-web]
steps:
- uses: actions/checkout@v4
- name: Free disk space
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf /usr/local/lib/android
sudo rm -rf /opt/ghc
sudo rm -rf /opt/hostedtoolcache/CodeQL
sudo docker image prune --all --force
df -h
- name: Download web build
uses: actions/download-artifact@v4
with:
name: web-ui-build
path: web/out/
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo-tarpaulin
id: cache-tarpaulin
uses: actions/cache@v4
with:
path: ~/.cargo/bin/cargo-tarpaulin
key: ${{ runner.os }}-cargo-tarpaulin-0.31.0
- name: Install tarpaulin
if: steps.cache-tarpaulin.outputs.cache-hit != 'true'
run: |
# Use precompiled binary for faster installation
TARPAULIN_VERSION="0.31.0"
wget https://github.com/xd009642/tarpaulin/releases/download/${TARPAULIN_VERSION}/cargo-tarpaulin-x86_64-unknown-linux-musl.tar.gz
tar -xzf cargo-tarpaulin-x86_64-unknown-linux-musl.tar.gz -C ~/.cargo/bin/
chmod +x ~/.cargo/bin/cargo-tarpaulin
- name: Generate coverage (unit tests only)
run: |
# Run tarpaulin on unit tests only (lib and bins, excluding integration tests)
# Using --lib and --bins flags to exclude integration tests in tests/
cargo tarpaulin \
--features cli,server \
--workspace \
--lib \
--bins \
--timeout 600 \
--out xml \
|| echo "Coverage generation failed but continuing..."
- name: Upload coverage to Codecov
if: success() || failure()
uses: codecov/codecov-action@v4
with:
files: ./cobertura.xml
fail_ci_if_error: false