diff --git a/.github/workflows/ruview-swarm-ci.yml b/.github/workflows/ruview-swarm-ci.yml new file mode 100644 index 00000000..c103f7f9 --- /dev/null +++ b/.github/workflows/ruview-swarm-ci.yml @@ -0,0 +1,143 @@ +name: ruview-swarm CI guard + +# Dedicated guard for the ADR-148 drone swarm crate (`v2/crates/ruview-swarm`). +# The main ci.yml runs `cargo test --workspace --no-default-features`, which +# only exercises ruview-swarm's DEFAULT feature set. This guard additionally: +# - tests every feature combination (train / ruflo+itar / full) +# - fails on ANY clippy warning in the crate's own code (--no-deps) +# - asserts the ITAR + publish guards stay in place (USML Cat VIII(h)(12)) +# - builds the GPU training binary under the `train` feature +# +# Path-scoped so it only runs when the crate or this workflow changes. + +on: + push: + branches: [ main, 'feat/*' ] + paths: + - 'v2/crates/ruview-swarm/**' + - '.github/workflows/ruview-swarm-ci.yml' + pull_request: + paths: + - 'v2/crates/ruview-swarm/**' + - '.github/workflows/ruview-swarm-ci.yml' + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + +jobs: + # ── Feature-matrix tests ───────────────────────────────────────────────── + tests: + name: tests (${{ matrix.features.label }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + features: + - { label: 'default', flags: '--no-default-features' } + - { label: 'train', flags: '--features train' } + - { label: 'ruflo+itar', flags: '--features ruflo,itar-unrestricted' } + - { label: 'full+train', flags: '--features full,train' } + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - name: Cache cargo + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + v2/target + key: ${{ runner.os }}-ruview-swarm-${{ hashFiles('v2/Cargo.lock') }} + restore-keys: ${{ runner.os }}-ruview-swarm- + - name: cargo test -p ruview-swarm ${{ matrix.features.flags }} + working-directory: v2 + run: cargo test -p ruview-swarm ${{ matrix.features.flags }} --lib + + # ── Clippy: zero warnings in the crate's own code ──────────────────────── + clippy: + name: clippy (-D warnings, --no-deps) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + components: clippy + - name: Cache cargo + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + v2/target + key: ${{ runner.os }}-ruview-swarm-clippy-${{ hashFiles('v2/Cargo.lock') }} + restore-keys: ${{ runner.os }}-ruview-swarm-clippy- + # --no-deps confines linting to ruview-swarm's own source, so pre-existing + # warnings in dependency crates don't gate this PR. + - name: clippy (default) + working-directory: v2 + run: cargo clippy -p ruview-swarm --no-default-features --no-deps -- -D warnings + - name: clippy (full,train) + working-directory: v2 + run: cargo clippy -p ruview-swarm --features full,train --no-deps -- -D warnings + + # ── Build the GPU training binary (train feature) ──────────────────────── + train-bin: + name: build train_marl bin + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - name: Cache cargo + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + v2/target + key: ${{ runner.os }}-ruview-swarm-bin-${{ hashFiles('v2/Cargo.lock') }} + restore-keys: ${{ runner.os }}-ruview-swarm-bin- + - name: cargo build --bin train_marl --features train + working-directory: v2 + run: cargo build -p ruview-swarm --features train --bin train_marl + - name: train_marl is excluded from the default build + working-directory: v2 + run: | + # The training binary requires the `train` feature; a default `--bins` + # build must NOT produce it (keeps default/CI builds light + Candle-free). + # Remove any prior artifact first so this checks what the DEFAULT build + # produces, not a leftover from the train-feature build above. + rm -f target/debug/train_marl + cargo build -p ruview-swarm --no-default-features --bins + if [ -f target/debug/train_marl ]; then + echo "ERROR: train_marl built without the 'train' feature" >&2 + exit 1 + fi + echo "OK: train_marl correctly gated behind the 'train' feature" + + # ── ITAR + publish guards ──────────────────────────────────────────────── + export-control-guard: + name: ITAR / publish guard + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: publish = false is present (no accidental crates.io publish) + run: | + CARGO=v2/crates/ruview-swarm/Cargo.toml + if ! grep -qE '^\s*publish\s*=\s*false' "$CARGO"; then + echo "ERROR: ruview-swarm Cargo.toml must keep 'publish = false' until" >&2 + echo " PR merge + dependency publish + ITAR export sign-off." >&2 + exit 1 + fi + echo "OK: publish = false present" + - name: default feature set does NOT enable itar-unrestricted + run: | + CARGO=v2/crates/ruview-swarm/Cargo.toml + # USML Cat VIII(h)(12): swarming coordination must be opt-in, never default. + DEFAULT_LINE=$(grep -E '^\s*default\s*=' "$CARGO" || true) + echo "default = $DEFAULT_LINE" + if echo "$DEFAULT_LINE" | grep -q 'itar-unrestricted'; then + echo "ERROR: 'itar-unrestricted' must NOT be in the default feature set" >&2 + exit 1 + fi + echo "OK: ITAR-gated coordination features are opt-in, not default"