Skip to content

feat(cel): bind request.body_json + on_match.deny (ADR-0030 prereqs) #332

feat(cel): bind request.body_json + on_match.deny (ADR-0030 prereqs)

feat(cel): bind request.body_json + on_match.deny (ADR-0030 prereqs) #332

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
issue_comment:
types: [created]
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
# Detect what files changed to skip unnecessary jobs
changes:
name: Detect Changes
if: github.event_name != 'issue_comment'
runs-on: ubuntu-latest
outputs:
code: ${{ steps.filter.outputs.code }}
plugins: ${{ steps.filter.outputs.plugins }}
ui: ${{ steps.filter.outputs.ui }}
steps:
- uses: actions/checkout@v6
- uses: dorny/paths-filter@v4
id: filter
with:
filters: |
code:
- '**/*.rs'
- '**/Cargo.toml'
- '**/Cargo.lock'
- 'plugins/**'
- '.github/workflows/ci.yml'
plugins:
- 'plugins/**'
- 'crates/barbacane-plugin-sdk/**'
- 'crates/barbacane-plugin-macros/**'
ui:
- 'ui/**'
- '.github/workflows/ci.yml'
# Fast checks first (format, clippy) for quick feedback
fmt:
name: Format
runs-on: ubuntu-latest
needs: [changes]
if: needs.changes.outputs.code == 'true'
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: Check formatting
run: cargo fmt --all -- --check
clippy:
name: Clippy
runs-on: ubuntu-latest
needs: [changes]
if: needs.changes.outputs.code == 'true'
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: Cache cargo registry
uses: actions/cache@v5
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Run Clippy
run: cargo clippy --workspace --lib --bins -- -D warnings
# Security audit via cargo-deny (advisories configured in deny.toml)
audit:
name: Security Audit
runs-on: ubuntu-latest
needs: [changes]
if: needs.changes.outputs.code == 'true'
steps:
- uses: actions/checkout@v6
- name: Install cargo-deny
run: cargo install cargo-deny --locked
- name: Run security audit
run: cargo deny check advisories
# Build workspace and plugins, upload artifacts for test job
build:
name: Build
runs-on: ubuntu-latest
needs: [changes]
if: needs.changes.outputs.code == 'true'
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown
- name: Cache cargo registry
uses: actions/cache@v5
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Cache WASM plugins
uses: actions/cache@v5
id: plugin-cache
with:
path: |
plugins/*/*.wasm
plugins/*/target
key: ${{ runner.os }}-plugins-${{ hashFiles('plugins/**/Cargo.toml', 'plugins/**/Cargo.lock', 'plugins/**/*.rs', 'crates/barbacane-plugin-sdk/**') }}
- name: Build workspace
run: cargo build --workspace --all-targets
- name: Build WASM plugins
if: steps.plugin-cache.outputs.cache-hit != 'true'
run: make plugins
- name: Upload build artifacts
uses: actions/upload-artifact@v7
with:
name: build-artifacts
path: |
target/debug/barbacane
plugins/*/*.wasm
retention-days: 1
# Unit tests (fast, no external dependencies)
unit-tests:
name: Unit Tests
runs-on: ubuntu-latest
needs: [changes, fmt, clippy, build]
if: needs.changes.outputs.code == 'true'
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo registry
uses: actions/cache@v5
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Run unit tests
# Exclude barbacane-test as it contains integration tests requiring gateway binary
run: cargo test --workspace --lib --bins --exclude barbacane-test -- --test-threads=4
# Control plane API tests (require PostgreSQL)
control-plane-tests:
name: Control Plane Tests
runs-on: ubuntu-latest
needs: [changes, fmt, clippy, build]
if: needs.changes.outputs.code == 'true'
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: barbacane
POSTGRES_PASSWORD: barbacane
POSTGRES_DB: barbacane
options: >-
--health-cmd pg_isready
--health-interval 5s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
env:
DATABASE_URL: postgres://barbacane:barbacane@localhost:5432/barbacane
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo registry
uses: actions/cache@v5
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Run control plane tests
run: cargo test -p barbacane-control -- --test-threads=4
# Plugin unit tests (only when plugin or SDK code changes)
plugin-tests:
name: Plugin Tests
runs-on: ubuntu-latest
needs: [changes, fmt]
if: needs.changes.outputs.plugins == 'true'
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo registry
uses: actions/cache@v5
with:
path: |
~/.cargo/registry
~/.cargo/git
key: ${{ runner.os }}-cargo-plugins-${{ hashFiles('plugins/**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-plugins-
- name: Run plugin tests
run: |
failed=0
for p in plugins/*/; do
name=$(basename "$p")
echo "::group::$name"
if ! (cd "$p" && cargo test 2>&1); then
echo "::error::Plugin $name tests failed"
failed=1
fi
echo "::endgroup::"
done
exit $failed
# Integration tests (slower, requires gateway binary and plugins)
integration-tests:
name: Integration Tests
runs-on: ubuntu-latest
needs: [changes, unit-tests]
if: needs.changes.outputs.code == 'true'
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo registry
uses: actions/cache@v5
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Download build artifacts
uses: actions/download-artifact@v8
with:
name: build-artifacts
- name: Make binary executable
run: chmod +x target/debug/barbacane
- name: Run integration tests
# Run gateway integration tests from barbacane-test crate
run: cargo test -p barbacane-test --lib -- --test-threads=2
# Benchmark regression check
# Triggered by: adding the "run-benchmarks" label, or commenting "/bench" on a PR.
benchmarks:
name: Benchmark Check
runs-on: ubuntu-latest
if: >-
(github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'run-benchmarks'))
|| (github.event_name == 'issue_comment' && github.event.issue.pull_request
&& contains(github.event.comment.body, '/bench'))
steps:
- uses: actions/checkout@v6
with:
# For issue_comment events, check out the PR head
ref: ${{ github.event_name == 'issue_comment' && format('refs/pull/{0}/head', github.event.issue.number) || '' }}
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo registry
uses: actions/cache@v5
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Cache benchmark baselines
uses: actions/cache@v5
with:
path: target/criterion
key: ${{ runner.os }}-criterion-${{ github.base_ref }}
restore-keys: |
${{ runner.os }}-criterion-main
- name: Run benchmarks
run: |
cargo bench -p barbacane -p barbacane-compiler -p barbacane-wasm 2>&1 | tee /tmp/bench-output.txt
- name: Parse benchmark results
run: |
# Extract benchmark results into a GitHub Step Summary table.
# Criterion output format (name and stats on separate lines):
# bench_group/bench_name/size
# time: [low est high]
# change: [low% mid% high%] (p = ...)
# Performance has regressed/improved.
echo "## Benchmark Results" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
regressed=$(grep -c "Performance has regressed" /tmp/bench-output.txt || true)
improved=$(grep -c "Performance has improved" /tmp/bench-output.txt || true)
no_change=$(grep -c "No change in performance" /tmp/bench-output.txt || true)
if [ "$regressed" -gt 0 ]; then
echo "> [!WARNING]" >> "$GITHUB_STEP_SUMMARY"
echo "> **$regressed** benchmark(s) regressed, **$improved** improved, **$no_change** unchanged" >> "$GITHUB_STEP_SUMMARY"
else
echo "> **$improved** improved, **$no_change** unchanged, no regressions" >> "$GITHUB_STEP_SUMMARY"
fi
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "| Benchmark | Time (est) | Change | Verdict |" >> "$GITHUB_STEP_SUMMARY"
echo "|-----------|-----------|--------|---------|" >> "$GITHUB_STEP_SUMMARY"
# Parse criterion output into table rows.
# Criterion uses two formats depending on benchmark name length:
# Multi-line: bench_name\n time: [low est high]
# Single-line: bench_name time: [low est high]
# Both followed by change: and verdict lines.
perl -ne '
if (/^[A-Za-z_]/ && /\// && !/time:/ && !/^(?:Benchmarking|Found |Gnuplot)/) {
chomp; $name = $_; $name =~ s/^\s+|\s+$//g;
}
if (/^[A-Za-z_]/ && /\// && /time:/ && !/^Benchmarking/) {
chomp; $name = $_; $name =~ s/\s*time:.*//; $name =~ s/^\s+|\s+$//g;
}
if (/time:/ && /\[/) {
/\[([^\]]+)\]/; @t = split(/\s+/, $1);
$time_est = "$t[2] $t[3]";
}
if (/change:/ && /\[/) {
/\[([^\]]+)\]/; @p = split(/\s+/, $1);
$change = $p[1];
}
if (/Performance has regressed/) {
printf "| \x60%s\x60 | %s | %s | :red_circle: regressed |\n", $name, $time_est, $change;
}
if (/Performance has improved/) {
printf "| \x60%s\x60 | %s | %s | :green_circle: improved |\n", $name, $time_est, $change;
}
if (/No change in performance/) {
printf "| \x60%s\x60 | %s | %s | :white_circle: no change |\n", $name, $time_est, $change;
}
' /tmp/bench-output.txt >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
if [ "$regressed" -gt 0 ]; then
echo "::warning::$regressed benchmark(s) regressed — see job summary for details"
fi
# UI unit tests (build + vitest)
ui-tests:
name: UI Tests
runs-on: ubuntu-latest
needs: [changes]
if: needs.changes.outputs.ui == 'true'
defaults:
run:
working-directory: ui
steps:
- uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
cache: npm
cache-dependency-path: ui/package-lock.json
- name: Install dependencies
run: npm ci
- name: Type check & build
run: npm run build
- name: Run unit tests
run: npm test
# UI E2E tests (Playwright)
ui-e2e:
name: UI E2E Tests
runs-on: ubuntu-latest
needs: [changes, ui-tests]
if: needs.changes.outputs.ui == 'true'
defaults:
run:
working-directory: ui
steps:
- uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
cache: npm
cache-dependency-path: ui/package-lock.json
- name: Install dependencies
run: npm ci
- name: Install Playwright browsers
run: npx playwright install --with-deps chromium
- name: Run E2E tests
run: npx playwright test --project=chromium
- name: Upload test report
uses: actions/upload-artifact@v7
if: failure()
with:
name: playwright-report
path: ui/playwright-report/
retention-days: 7