CI #356
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| schedule: | |
| # Nightly fuzz run at 02:15 UTC. Only the `scenario-fuzz` job | |
| # reacts to this trigger — see its `if: github.event_name == | |
| # 'schedule'` guard. | |
| - cron: '15 2 * * *' | |
| env: | |
| CARGO_TERM_COLOR: always | |
| RUSTFLAGS: -Dwarnings | |
| jobs: | |
| changes: | |
| name: Change Scope | |
| runs-on: ubuntu-latest | |
| outputs: | |
| docs: ${{ steps.filter.outputs.docs }} | |
| non_docs: ${{ steps.filter.outputs.non_docs }} | |
| docs_only: ${{ steps.docs_only.outputs.value }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dorny/paths-filter@v3 | |
| id: filter | |
| with: | |
| filters: | | |
| docs: | |
| - 'docs/**' | |
| - 'mkdocs.yml' | |
| - 'scripts/generate-docs.sh' | |
| - '.github/workflows/docs-publish.yml' | |
| non_docs: | |
| - '**' | |
| - '!docs/**' | |
| - '!mkdocs.yml' | |
| - '!scripts/generate-docs.sh' | |
| - '!.github/workflows/docs-publish.yml' | |
| - id: docs_only | |
| shell: bash | |
| run: | | |
| if [ "${{ steps.filter.outputs.docs }}" = "true" ] && [ "${{ steps.filter.outputs.non_docs }}" != "true" ]; then | |
| echo "value=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "value=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| check: | |
| name: Rust Checks (${{ matrix.os }}) | |
| needs: changes | |
| if: github.event_name == 'push' || needs.changes.outputs.non_docs == 'true' | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: clippy, rustfmt | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Install system dependencies | |
| run: | | |
| if [ "$RUNNER_OS" = "Linux" ]; then | |
| sudo apt-get update && sudo apt-get install -y tmux | |
| elif [ "$RUNNER_OS" = "macOS" ]; then | |
| brew install tmux | |
| fi | |
| shell: bash | |
| - name: Install kanban-md | |
| run: | | |
| KMD_VERSION="0.32.2" | |
| if [ "$RUNNER_OS" = "Linux" ]; then | |
| ARCH="linux_amd64" | |
| elif [ "$RUNNER_OS" = "macOS" ]; then | |
| ARCH="darwin_arm64" | |
| fi | |
| curl -sL "https://github.com/antopolskiy/kanban-md/releases/download/v${KMD_VERSION}/kanban-md_${KMD_VERSION}_${ARCH}.tar.gz" | sudo tar xz -C /usr/local/bin kanban-md | |
| shell: bash | |
| - name: Configure git identity and default branch | |
| run: | | |
| git config --global user.email "ci@batty.test" | |
| git config --global user.name "Batty CI" | |
| git config --global init.defaultBranch main | |
| shell: bash | |
| - name: Lint | |
| run: make lint | |
| - name: Build | |
| run: cargo build | |
| - name: Verify parity equivalence | |
| run: cargo run -- verify | |
| - name: Upload verification report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: verification-report-${{ matrix.os }} | |
| path: .batty/reports/verification/latest.md | |
| if-no-files-found: ignore | |
| - name: Test | |
| run: >- | |
| cargo test -- | |
| --skip tmux::tests::create_session | |
| --skip tmux::tests::create_window | |
| --skip tmux::tests::capture_pane | |
| --skip tmux::tests::send_keys | |
| --skip tmux::tests::session_with_short | |
| --skip orchestrator::tests::status_bar | |
| --skip orchestrator::tests::handle_prompt_tier2 | |
| --skip orchestrator::tests::harness_direct_reply | |
| --skip tier2::tests::call_supervisor | |
| --skip work::phase_worktree::tests::prepare_agent_worktrees_creates | |
| --skip team::daemon::tests::startup_cwd_validation_corrects_all_agent_panes | |
| --skip team::daemon::tests::restart_member_corrects_mismatched_cwd_after_respawn | |
| --skip team::daemon::health::tests::check_backend_health | |
| --skip team::daemon::health::tests::uncommitted_diff_lines | |
| --skip team::daemon::health::tests::reconcile_skips_unmerged_branch | |
| --skip team::daemon::health::checks::tests::check_backend_health_emits_event_on_transition | |
| --skip team::daemon::health::checks::tests::check_backend_health_no_event_when_state_unchanged | |
| --skip team::verification::tests::verify_project_updates_parity_and_writes_report | |
| --skip team::verification::tests::verify_project_detects_regressions_from_previous_pass | |
| --skip team::merge::operations::tests::merge_rebase_additive_conflict_keeps_both_sides | |
| --skip team::tact::parser::tests::create_board_tasks_skips_equivalent_generated_specs | |
| --skip team::task_loop::tests::test_run_tests_in_worktree_returns_pass_fail | |
| --skip team::daemon::telegram_bridge::tests::maybe_intervene_triage_backlog_marks_member_working_after_live_delivery | |
| - name: Scenario framework (prescriptive + fuzz smoke) | |
| run: cargo test --test scenarios --features scenario-test | |
| env: | |
| # Default proptest cases; the nightly fuzz job scales this up. | |
| PROPTEST_CASES: '32' | |
| scenario-fuzz: | |
| name: Scenario Framework Fuzz (nightly) | |
| needs: changes | |
| if: github.event_name == 'schedule' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Install system dependencies | |
| run: sudo apt-get update && sudo apt-get install -y tmux | |
| - name: Install kanban-md | |
| run: | | |
| KMD_VERSION="0.32.2" | |
| curl -sL "https://github.com/antopolskiy/kanban-md/releases/download/v${KMD_VERSION}/kanban-md_${KMD_VERSION}_linux_amd64.tar.gz" | sudo tar xz -C /usr/local/bin kanban-md | |
| shell: bash | |
| - name: Run fuzz targets with expanded case budget | |
| run: | | |
| cargo test --test scenarios --features scenario-test --release fuzz_workflow_happy | |
| cargo test --test scenarios --features scenario-test --release fuzz_workflow_with_faults | |
| cargo test --test scenarios --features scenario-test --release fuzz_restart_resilience | |
| env: | |
| PROPTEST_CASES: '2048' | |
| PROPTEST_MAX_SHRINK_ITERS: '8192' | |
| coverage: | |
| name: Code Coverage | |
| needs: changes | |
| if: github.event_name == 'push' || needs.changes.outputs.non_docs == 'true' | |
| runs-on: ubuntu-latest | |
| # Tarpaulin intermittently segfaults on subprocess-heavy tests (child PTYs, | |
| # fake shim channels). Coverage is a reporting metric, not a correctness | |
| # gate — don't block merge on a flaky profiler. The main Rust Checks job | |
| # is the source of truth for test correctness. | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Install system dependencies | |
| run: sudo apt-get update && sudo apt-get install -y tmux | |
| - name: Install kanban-md | |
| run: | | |
| KMD_VERSION="0.32.2" | |
| if [ "$RUNNER_OS" = "Linux" ]; then | |
| ARCH="linux_amd64" | |
| elif [ "$RUNNER_OS" = "macOS" ]; then | |
| ARCH="darwin_arm64" | |
| fi | |
| curl -sL "https://github.com/antopolskiy/kanban-md/releases/download/v${KMD_VERSION}/kanban-md_${KMD_VERSION}_${ARCH}.tar.gz" | sudo tar xz -C /usr/local/bin kanban-md | |
| shell: bash | |
| - name: Configure git identity and default branch | |
| run: | | |
| git config --global user.email "ci@batty.test" | |
| git config --global user.name "Batty CI" | |
| git config --global init.defaultBranch main | |
| shell: bash | |
| - name: Install cargo-tarpaulin | |
| run: cargo install cargo-tarpaulin | |
| - name: Generate coverage | |
| shell: bash | |
| run: | | |
| set +e | |
| cargo tarpaulin --out xml --output-dir coverage/ --skip-clean --include-tests \ | |
| -- \ | |
| --skip tmux::tests::create_session \ | |
| --skip tmux::tests::create_window \ | |
| --skip tmux::tests::capture_pane \ | |
| --skip tmux::tests::send_keys \ | |
| --skip tmux::tests::session_with_short \ | |
| --skip orchestrator::tests::status_bar \ | |
| --skip orchestrator::tests::handle_prompt_tier2 \ | |
| --skip orchestrator::tests::harness_direct_reply \ | |
| --skip tier2::tests::call_supervisor \ | |
| --skip work::phase_worktree::tests::prepare_agent_worktrees_creates \ | |
| --skip team::daemon::tests::startup_cwd_validation_corrects_all_agent_panes \ | |
| --skip team::daemon::tests::restart_member_corrects_mismatched_cwd_after_respawn \ | |
| --skip worktree::tests::branch_fully_merged \ | |
| --skip worktree::tests::reset_worktree_to_base \ | |
| --skip team::daemon::health::checks::tests::check_backend_health_emits_event_on_transition \ | |
| --skip team::daemon::health::checks::tests::check_backend_health_no_event_when_state_unchanged \ | |
| --skip team::daemon::health::tests::uncommitted_diff_lines \ | |
| --skip team::verification::tests::verify_project_updates_parity_and_writes_report \ | |
| --skip team::verification::tests::verify_project_detects_regressions_from_previous_pass \ | |
| --skip team::merge::operations::tests::merge_rebase_additive_conflict_keeps_both_sides \ | |
| --skip team::tact::parser::tests::create_board_tasks_skips_equivalent_generated_specs \ | |
| --skip team::task_loop::tests::test_run_tests_in_worktree_returns_pass_fail \ | |
| --skip team::daemon::health::preflight::tests::startup_preflight_verifies_worktree_operations \ | |
| --skip team::daemon::health::preflight::tests::startup_preflight_accepts_available_agent_binaries \ | |
| --skip team::daemon::telegram_bridge::tests::maybe_intervene_triage_backlog_marks_member_working_after_live_delivery | |
| status=$? | |
| if [ "$status" -ne 0 ]; then | |
| echo "::warning::cargo tarpaulin failed with exit code $status; coverage is non-blocking for this repository" | |
| fi | |
| exit 0 | |
| - name: Upload to Codecov | |
| if: ${{ hashFiles('coverage/cobertura.xml') != '' }} | |
| uses: codecov/codecov-action@v5 | |
| with: | |
| files: coverage/cobertura.xml | |
| fail_ci_if_error: false | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| slug: battysh/batty | |
| docs: | |
| name: Docs Quality | |
| needs: changes | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| - name: Install docs dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| python -m pip install "mkdocs>=1.6,<2" mkdocs-material mdformat mdformat-gfm | |
| - name: Refresh generated references | |
| run: ./scripts/generate-docs.sh | |
| - name: Markdown format check | |
| shell: bash | |
| run: | | |
| mapfile -t doc_files < <(printf '%s\n' docs/*.md docs/reference/*.md | sort) | |
| mdformat --check "${doc_files[@]}" | |
| - name: Markdown lint check | |
| run: npx --yes markdownlint-cli2 docs/*.md docs/reference/*.md | |
| - name: Internal link audit | |
| run: mkdocs build --strict | |
| - name: Verify generated docs are committed | |
| run: git diff --exit-code -- docs/reference/cli.md docs/reference/config.md |