From e2f996d263942fea2ec9b72a8c241ad0d2050ea9 Mon Sep 17 00:00:00 2001 From: David Spencer <1526975+DecisionNerd@users.noreply.github.com> Date: Fri, 7 Aug 2026 11:04:28 -0600 Subject: [PATCH] build: add Bazelisk and drift checks to pre-push Require bazelisk in local preflight and pre-push-fast; run cargo-bazel-drift-check early. Add make bazel-test and surface docs/development/bazel.md from AGENTS/CONTRIBUTING/onboarding. Closes #447 Co-authored-by: Cursor --- AGENTS.md | 7 +++++++ CONTRIBUTING.md | 5 ++++- Makefile | 9 ++++++++- docs/development/bazel.md | 6 +++++- docs/development/contributing.md | 18 ++++++++++++++++-- docs/development/testing.md | 16 ++++++++++------ scripts/pre_push_validation.py | 5 +++-- 7 files changed, 53 insertions(+), 13 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 8bb0b6314..df9d5daa7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -38,9 +38,16 @@ Use targeted checks while iterating; run gates appropriate to the changed surfac cargo fmt --all -- --check cargo clippy --workspace -- -D warnings cargo test --workspace +make pre-push-fast # includes bazelisk + Cargo/Bazel drift make pre-push +make bazel-test # optional: authoritative //:ci_rust_tests locally ``` +CI Rust compile/test authority is Bazel (`//:ci_rust_tests`); see +`docs/development/bazel.md`. `make pre-push-fast` requires `bazelisk` on `PATH` +and runs `cargo-bazel-drift-check.py`. Full `//:ci_rust_tests` is optional +locally via `make bazel-test` (heavy). + Run formatting after the final edit. Review intentional snapshot changes before accepting them. Keep native builds isolated with `CARGO_TARGET_DIR`; run at most two heavy builds concurrently and monitor disk. ## PR gate diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c7569c5e8..873ac6c3c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,13 +22,16 @@ Agent-oriented workflow and architecture rules are in [AGENTS.md](AGENTS.md). ### Validation (summary) Before pushing, run the Rust gates appropriate to the changed surface, then the -Python/workspace mirror: +Python/workspace mirror. Install [Bazelisk](https://github.com/bazelbuild/bazelisk) +(`bazelisk` on `PATH`); see [docs/development/bazel.md](docs/development/bazel.md). ```bash cargo fmt --all -- --check cargo clippy --workspace -- -D warnings cargo test --workspace +make pre-push-fast # bazelisk + Cargo/Bazel drift, then format/lint/… make pre-push +make bazel-test # optional local //:ci_rust_tests ``` ## Getting help diff --git a/Makefile b/Makefile index 6412d5690..1cd00492b 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: help lint format type-check security workflow-lint license-check third-party-notices third-party-notices-check cargo-deny-licenses test pre-push pre-push-clean pre-push-preflight pre-push-fast clean test-tck docstring-coverage test-network benchmark test-perf test-perf-xs test-perf-slow test-perf-large coverage coverage-rust coverage-python coverage-node coverage-quick coverage-report coverage-diff coverage-strict check-coverage check-coverage-rust check-coverage-python check-coverage-node check-patch-coverage test-durations test-analytics docs-serve docs-build docs-clean cargo-build bench-traversal bench-fixed-hop-limit bench-fixed-hop-livejournal native-consumers release-load-matrix-check release-load-matrix bulk-construction-conformance-check bulk-construction-conformance cargo-test cargo-check cargo-clippy cargo-fmt cargo-fmt-check clean-builds clean-builds-all pnpm-install pnpm-build pnpm-test-bdd install build release-version-check package-license-verify publish-dry-run publish-dry-run-npm publish-dry-run-docs publish-dry-run-python publish-dry-run-cargo record-release-artifacts clean-env-verify-check clean-env-verify-preflight clean-env-verify +.PHONY: help lint format type-check security workflow-lint license-check third-party-notices third-party-notices-check cargo-deny-licenses test pre-push pre-push-clean pre-push-preflight pre-push-fast bazel-test clean test-tck docstring-coverage test-network benchmark test-perf test-perf-xs test-perf-slow test-perf-large coverage coverage-rust coverage-python coverage-node coverage-quick coverage-report coverage-diff coverage-strict check-coverage check-coverage-rust check-coverage-python check-coverage-node check-patch-coverage test-durations test-analytics docs-serve docs-build docs-clean cargo-build bench-traversal bench-fixed-hop-limit bench-fixed-hop-livejournal native-consumers release-load-matrix-check release-load-matrix bulk-construction-conformance-check bulk-construction-conformance cargo-test cargo-check cargo-clippy cargo-fmt cargo-fmt-check clean-builds clean-builds-all pnpm-install pnpm-build pnpm-test-bdd install build release-version-check package-license-verify publish-dry-run publish-dry-run-npm publish-dry-run-docs publish-dry-run-python publish-dry-run-cargo record-release-artifacts clean-env-verify-check clean-env-verify-preflight clean-env-verify help: ## Show this help message @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' @@ -196,7 +196,14 @@ check-patch-coverage: ## Validate patch coverage for changed files (90% thresho echo "✅ Patch coverage meets 90% threshold"; \ fi +bazel-test: ## Run authoritative Bazel Rust suite (//:ci_rust_tests); see docs/development/bazel.md + @command -v bazelisk >/dev/null || (echo "bazelisk is required on PATH; see docs/development/bazel.md"; exit 1) + bazelisk test //:ci_rust_tests + pre-push-fast: ## Run fast checks only — format, lint, type, security, docstrings (no coverage, ~30s) + @echo "━━━ Bazelisk preflight + Cargo/Bazel drift ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + @command -v bazelisk >/dev/null || (echo "bazelisk is required on PATH; see docs/development/bazel.md"; exit 1) + @python3 scripts/ci/cargo-bazel-drift-check.py @echo "━━━ Public API BDD policy ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" @python3 scripts/ci/api-bdd-policy.py --check-issues @python3 scripts/ci/test-api-bdd-policy.py diff --git a/docs/development/bazel.md b/docs/development/bazel.md index a89164cf0..152f26cd6 100644 --- a/docs/development/bazel.md +++ b/docs/development/bazel.md @@ -53,9 +53,13 @@ bazelisk version # must report 9.2.0 from .bazelversion ## Everyday local commands +`make pre-push-fast` requires `bazelisk` and runs the Cargo/Bazel drift check. +`make bazel-test` wraps the authoritative suite below. + ```bash # Authoritative CI-equivalent Rust test graph -bazelisk test //:ci_rust_tests +make bazel-test +# equivalent: bazelisk test //:ci_rust_tests # Libraries, CLI, resources, release bins bazelisk build //:first_party_libs //:cli_bins //:resource_inputs //:release_bins diff --git a/docs/development/contributing.md b/docs/development/contributing.md index db41683f3..632cd8e9d 100644 --- a/docs/development/contributing.md +++ b/docs/development/contributing.md @@ -19,13 +19,19 @@ suite → open a focused PR against `main`. For release operators, start at ## Development Setup -**Prerequisites:** Python 3.10+, Rust stable, [uv](https://github.com/astral-sh/uv), maturin +**Prerequisites:** Python 3.10+, Rust stable (pinned by `rust-toolchain.toml`), +[uv](https://github.com/astral-sh/uv), maturin, pnpm for the Node binding, and +[Bazelisk](https://github.com/bazelbuild/bazelisk) (`bazelisk` on `PATH`). +CI Rust compile/test authority is Bazel — start with [bazel.md](bazel.md). ```bash # Install Rust curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh rustup update stable +# Install Bazelisk (example: macOS Homebrew) +brew install bazelisk + git clone https://github.com/CurateLabs/graphforge.git cd graphforge @@ -38,6 +44,9 @@ maturin develop --release -m crates/graphforge-bindings-py/Cargo.toml # Verify cargo test --workspace python -c "import graphforge; print(graphforge.__version__)" +make pre-push-fast # requires bazelisk; runs Cargo/Bazel drift +# Optional heavy local Bazel suite (mirrors CI authority): +# make bazel-test ``` See [Installation](../guide/installation.md) for the published-package path. @@ -54,10 +63,15 @@ See [Installation](../guide/installation.md) for the published-package path. cargo fmt --all -- --check cargo clippy --workspace -- -D warnings cargo test --workspace +make pre-push-fast # bazelisk + drift, then format/lint/security/… make pre-push +make bazel-test # optional: bazelisk test //:ci_rust_tests ``` -`make pre-push` mirrors the CI gate for the changed surface. +`make pre-push` mirrors the CI gate for the changed surface. Fast path requires +`bazelisk` and fails closed on Cargo/Bazel feature drift +(`scripts/ci/cargo-bazel-drift-check.py`). Full local Bazel suite: +`make bazel-test` (see [bazel.md](bazel.md)). ### Running Tests diff --git a/docs/development/testing.md b/docs/development/testing.md index 0f3e0be88..241a86553 100644 --- a/docs/development/testing.md +++ b/docs/development/testing.md @@ -212,12 +212,16 @@ pytest tests/ -n auto ## Resumable full validation `make pre-push` is the full local gate. It begins with a prerequisite and disk -preflight, then records content-addressed evidence for policy checks, Rust -tests and coverage, the instrumented native Python and Node builds consumed by -acceptance, wrapper coverage, API BDD acceptance, and -coverage thresholds. It never skips a gate: a compatible passed stage is reused -only when its exact inputs, command contract, toolchain, dependency evidence, -and required native artifact identity still match. +preflight (including `bazelisk` on `PATH`), then records content-addressed +evidence for policy checks, Rust tests and coverage, the instrumented native +Python and Node builds consumed by acceptance, wrapper coverage, API BDD +acceptance, and coverage thresholds. `make pre-push-fast` (also invoked from the +policy-static stage) runs bazelisk presence + `cargo-bazel-drift-check.py` +before format/lint/security. Optional authoritative local Bazel suite: +`make bazel-test` → `bazelisk test //:ci_rust_tests` (see [bazel.md](bazel.md)). +It never skips a gate: a compatible passed stage is reused only when its exact +inputs, command contract, toolchain, dependency evidence, and required native +artifact identity still match. The human-readable stage lines and machine-readable summary report elapsed time, evidence hit or miss, identity digest, invalidation reason, and disk budget. diff --git a/scripts/pre_push_validation.py b/scripts/pre_push_validation.py index 2ec0b6665..7b8da3c37 100644 --- a/scripts/pre_push_validation.py +++ b/scripts/pre_push_validation.py @@ -281,14 +281,15 @@ def collect_artifacts(self, stage: Stage) -> list[dict[str, str]]: def run_preflight(self, environment: Mapping[str, str]) -> None: missing = [ name - for name in ("cargo", "rustc", "rustup", "uv", "node", "pnpm") + for name in ("cargo", "rustc", "rustup", "uv", "node", "pnpm", "bazelisk") if not shutil.which(name) ] if missing: raise ValidationError( "missing prerequisite(s): " + ", ".join(missing) - + ". Install the pinned toolchain described in docs/development/contributing.md." + + ". Install the pinned toolchain described in docs/development/contributing.md " + "(Bazelisk: docs/development/bazel.md)." ) heavy_stages = tuple(stage for stage in self.cache_stages if stage.heavy) warm_cache = bool(heavy_stages) and all(