name: CI permissions: contents: read on: push: branches: ["master"] pull_request: branches: ["master"] workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: frontend: name: frontend runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: node-version: 20 cache: npm cache-dependency-path: frontend/package-lock.json - name: Install frontend dependencies working-directory: frontend run: npm ci - name: Frontend license check working-directory: frontend run: npm run license:check - name: Frontend format check working-directory: frontend run: npm run format:check - name: Frontend lint working-directory: frontend run: npm run lint - name: Frontend type check working-directory: frontend run: npm run typecheck - name: Frontend tests working-directory: frontend run: npm test - name: Frontend build working-directory: frontend run: npm run build rustfmt: name: rustfmt runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: stable components: rustfmt cache: true - name: Format check run: cargo fmt --all -- --check clippy-stable: name: clippy-stable runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: stable components: clippy cache: true - name: Lint (Clippy) run: cargo clippy --all-targets --all-features -- -D warnings cargo-deny: name: cargo-deny runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: stable cache: true - uses: taiki-e/install-action@cargo-deny - name: License & advisory checks run: cargo deny check advisories licenses bans sources unused-deps: name: unused-deps runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: bnjbvr/cargo-machete@v0.9.2 third-party-licenses: name: third-party-licenses runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: stable cache: true - uses: taiki-e/install-action@v2 with: tool: cargo-about@0.7.1 - name: Prime cargo registry cache run: cargo fetch --locked - name: Regenerate license attribution run: cargo about generate --offline about.hbs | tr -d '\r' > /tmp/THIRDPARTY-LICENSES.html - name: Diff against committed file run: diff -u --strip-trailing-cr THIRDPARTY-LICENSES.html /tmp/THIRDPARTY-LICENSES.html docs-fresh: name: docs-fresh runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: stable cache: true - name: Regenerate rule reference run: cargo run --features docgen --bin nyx-docgen - name: Verify docs/rules.md is fresh run: | if ! git diff --exit-code docs/rules.md; then echo "::error::docs/rules.md is stale. Run 'cargo run --features docgen --bin nyx-docgen' and commit the result." exit 1 fi rustdoc: name: rustdoc runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: stable cache: true - name: Check rustdoc links env: RUSTDOCFLAGS: "-D warnings" run: cargo doc --workspace --no-deps --all-features rust-beta-build: name: rust-beta-build runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: beta cache: true - name: Beta compile compatibility check run: cargo check --all-features --tests msrv: name: msrv runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: "1.88" cache: true - name: Compile check at MSRV run: cargo check --all-features --tests rust-stable-test-linux-without-docker: name: rust-stable-test / linux-without-docker runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: stable cache: true - uses: taiki-e/install-action@nextest - name: Rust tests (stable, no docker) run: cargo nextest run --no-fail-fast --all-features rust-stable-test-linux-with-docker: name: rust-stable-test / linux-with-docker runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: stable cache: true - uses: taiki-e/install-action@nextest - name: Pull language images for sandbox tests run: | docker pull python:3-slim docker pull node:20-slim docker pull eclipse-temurin:21-jre-jammy docker pull php:8-cli - name: Smoke-test interpreter availability run: | docker run --rm python:3-slim python3 --version docker run --rm node:20-slim node --version docker run --rm eclipse-temurin:21-jre-jammy java -version docker run --rm php:8-cli php --version - name: Rust tests with docker (sandbox escape gate) run: cargo nextest run --no-fail-fast --all-features --test dynamic_sandbox_escape --test dynamic_parity escape-positive-control: name: escape-positive-control runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: stable cache: true - uses: taiki-e/install-action@nextest - name: Pull python image run: docker pull python:3-slim - name: Escape positive control (gate wiring check) run: | cargo nextest run --no-fail-fast --all-features --test dynamic_sandbox_escape \ -- --include-ignored positive_control_cap_sys_admin cross-platform-smoke: name: cross-platform-smoke strategy: fail-fast: false matrix: os: [macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v6 - uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: stable cache: true - uses: taiki-e/install-action@nextest - name: Build run: cargo build --release --all-features - name: Smoke tests run: cargo nextest run --no-fail-fast --all-features --test integration_tests --test pattern_tests --test cli_validation_tests rust-beta-test: name: rust-beta-test runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: beta cache: true - uses: taiki-e/install-action@nextest - name: Rust tests (beta) run: cargo nextest run --no-fail-fast --all-features cargo-package: name: cargo-package runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: node-version: 20 cache: npm cache-dependency-path: frontend/package-lock.json - uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: stable cache: true - name: Build frontend working-directory: frontend run: | npm ci npm run build - name: Verify dist embedded in package run: | for f in src/server/assets/dist/index.html src/server/assets/dist/app.js src/server/assets/dist/style.css src/server/assets/favicon.svg default-nyx.conf build.rs; do if ! cargo package --list --allow-dirty | grep -qx "$f"; then echo "::error::missing from cargo package: $f" exit 1 fi done - name: cargo package (verify build) run: cargo package --allow-dirty benchmark-gate: name: benchmark-gate runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: stable cache: true cache-key: benchmark-gate-release - uses: taiki-e/install-action@nextest - name: Build benchmark + perf test binaries run: cargo nextest run --release --all-features --test benchmark_test --test perf_tests --no-run - name: Accuracy regression gate (P/R/F1) run: cargo nextest run --no-fail-fast --release --all-features --test benchmark_test --run-ignored only --no-capture benchmark_evaluation - name: Performance regression gate env: NYX_CI_BENCH: "1" run: cargo nextest run --no-fail-fast --release --all-features --test perf_tests --no-capture - name: Upload benchmark results if: always() uses: actions/upload-artifact@v7 with: name: benchmark-results path: tests/benchmark/results/latest.json if-no-files-found: warn corpus-marker-audit: name: corpus-marker-audit runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions/setup-python@v6 with: python-version: "3.12" - name: Marker collision audit (ยง16.3) run: python3 scripts/corpus_dashboard.py # Exits non-zero if any oracle marker from one cap appears in another # cap's payload bytes. This catches cross-cap oracle collisions that # would cause false-positive confirmed verdicts. - uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: stable cache: true - uses: taiki-e/install-action@nextest - name: Corpus unit tests (no_marker_collisions, all_payloads_have_fixture_paths) run: cargo nextest run --no-fail-fast --lib -p nyx-scanner dynamic::corpus env: RUST_LOG: error - name: Corpus dashboard sync check (Python/Rust payload table parity) run: python3 scripts/check_corpus_sync.py