name: CI
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
test:
name: Build & Test
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false matrix:
os: [ubuntu-latest, macOS-latest, windows-latest]
toolchain: [stable]
continue-on-error: false
steps:
- uses: actions/checkout@v4
- name: Update tool chain
run: |
rustup update ${{ matrix.toolchain }}
rustup default ${{ matrix.toolchain }}
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: cargo test
timeout-minutes: 10
run: cargo test --release -- --nocapture
- name: cargo doc (no deps, no-run)
run: cargo doc --no-deps
- name: Smoke-test examples
timeout-minutes: 10
shell: bash
run: |
set -euo pipefail
# Pre-build so `cargo run` below starts each binary immediately,
# without compilation eating into the liveness window.
cargo build --release --examples
# Exercise the HTTP client end-to-end against a *local* server, so the
# check is hermetic — no external host and no hostname/DNS dependency
# (client_get's example.com default would otherwise add network flake;
# numeric 127.0.0.1 also avoids the resolver entirely).
cargo run --release --example hello_server &
hs=$!
sleep 5
set +e
cargo run --release --example client_get -- http://127.0.0.1:8080/hello
rc=$?
set -e
kill "$hs" 2>/dev/null || true
wait "$hs" 2>/dev/null || true
if [ "$rc" -ne 0 ]; then
echo "ERROR: client_get against local server failed (exit $rc)"
exit 1
fi
# The server examples block forever in listen_and_serve(). Start
# each one, confirm it is still alive after a few seconds (i.e. it
# bound its port and is serving rather than crashing on startup),
# then terminate it. An early exit means a startup failure.
for ex in echo_server hello_server middleware routing static_files; do
echo "=== smoke-testing $ex ==="
cargo run --release --example "$ex" &
pid=$!
sleep 5
if kill -0 "$pid" 2>/dev/null; then
echo "$ex started OK"
kill "$pid" 2>/dev/null || true
wait "$pid" 2>/dev/null || true
else
echo "ERROR: $ex exited before the smoke-test window elapsed"
exit 1
fi
done
build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Update tool chain
run: |
rustup update
rustup default
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: cargo build
run: cargo build --all-targets
coverage:
name: Coverage
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ ubuntu-latest ]
toolchain: [ nightly ]
steps:
- uses: actions/checkout@v4
- name: Update tool chain
run: |
rustup update ${{ matrix.toolchain }}
rustup default ${{ matrix.toolchain }}
rustup component add llvm-tools-preview --toolchain ${{ matrix.toolchain }}
- name: Install coverage tool
run: cargo +stable install cargo-llvm-cov --locked
- name: Test project and generate coverage report
run: cargo llvm-cov --release --remap-path-prefix --show-missing-lines --branch --lcov --output-path lcov.info
- name: Publish coverage summary to job summary
uses: livewing/lcov-job-summary@v1.1.0
with:
lcov: lcov.info
- name: Fail build if coverage below 80%
uses: bigmeech/gha-simple-coverage@master
with:
lcov-file-path: lcov.info
fail-if-below: 80