Skip to content

fix(bazel): honor --no-keep_going when querying Bzlmod repositories - #508

Merged
tinder-maxwellelliott merged 2 commits into
Tinder:masterfrom
q4rk:fix/bzlmod-honor-no-keep-going
Sep 23, 2026
Merged

tinder-maxwellelliott merged 2 commits into
Tinder:masterfrom
q4rk:fix/bzlmod-honor-no-keep-going

Conversation

@q4rk

@q4rk q4rk commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Summary

When generate-hashes (or serve) runs with --no-keep_going (the default when --keep_going is not passed), query_all_targets_with() previously swallowed failures in bazel mod subcommands and silently returned an empty Vec::new() of //external:* synthetic Bzlmod repository targets while still exiting 0.

When startingHashes and finalHashes are generated across two revisions and one invocation experiences a transient bazel mod failure (or lockfile/extension evaluation error) while the other succeeds, one hash file contains all //external:<repo> targets and the other contains zero //external:<repo> targets. Because transform_external_input() rewrites every external rule input (@repo//pkg:target -> //external:repo) and DigestBuilder::digest_rule() only hashes dependencies present in the target map (if let Some(dependency_digest) = self.target(input, visited)?), every workspace rule that depends on any external repository receives a different rule digest between startingHashes and finalHashes, causing mass false-positive target invalidation across the workspace.

This PR updates src/bazel.rs to honor !self.keep_going (--no-keep_going) during Bzlmod detection and repository hashing, while preserving the existing partial-graph warning and fallback behavior when --keep_going (-k) is enabled.

Root cause

In src/bazel.rs:

  1. is_bzlmod_enabled() ran bazel mod graph and collapsed any non-zero exit status or execution error into false (unwrap_or(false)). If MODULE.bazel was present in the workspace and bazel mod graph failed transiently, query_all_targets_with() and dependency_fingerprint() silently treated the workspace as a legacy non-Bzlmod workspace (Vec::new() Bzlmod repos / mode:legacy fingerprint).
  2. query_all_targets_with() called self.query_bzlmod_repos(&mut transform).unwrap_or_else(...), logging [Warn] failed to hash Bzlmod repositories and returning Vec::new() even when self.keep_going was false.
  3. query_bzlmod_repos() called self.module_graph_json().unwrap_or_default(), silently dropping inter-module dependency edges (//external:<dep>) on //external:<repo> targets if bazel mod graph --output=json failed.

What changed

  • check_bzlmod_enabled(&self) -> Result<bool>:
    • Checks whether MODULE.bazel exists in self.workspace (and Bzlmod is not explicitly disabled via --noenable_bzlmod or --enable_bzlmod=false|0|no).
    • When MODULE.bazel is present and !self.keep_going, a failure in bazel mod graph now returns an Err with the exit status and stderr output instead of silently returning false.
  • query_all_targets_with(&self, ...) & query_bzlmod_repos(&self, ...):
    • When !self.keep_going, errors from check_bzlmod_enabled(), self.version(), self.query_bzlmod_repos(), and self.module_graph_json() are propagated as Err so the CLI exits non-zero (allowing callers/CI to detect the failure or retry).
    • When self.keep_going is true (--keep_going / -k), the existing [Warn] diagnostic and Vec::new() / empty-edge fallback behavior is preserved.

Verification

Check Result
bazel test //src:rust_tests //src:cli_tests Passed (111 unit tests + CLI tests, including bazel::tests::check_bzlmod_and_query_bzlmod_repos_honor_keep_going)
bazel test //:rust_clippy_check //:rust_format_check Passed
bazel test //tools/... --enable_bzlmod=true --enable_workspace=false Passed
make coverage Passed (src/bazel.rs: 91.68%, overall main-source line coverage: 94.38% >= 90.00% threshold)

Previously, `query_all_targets_with()` silently fell back to an empty
Bzlmod repository list (`Vec::new()`) whenever `bazel mod graph`,
`bazel mod dump_repo_mapping`, `bazel mod show_repo`, or
`bazel mod graph --output=json` failed, even when `--no-keep_going`
(`!self.keep_going`) was in effect.

In Bzlmod workspaces, silently dropping all `//external:*` synthetic
repository targets on one revision causes every rule with an external
dependency (`@repo//...` -> `//external:repo`) to have a mismatched
Rule digest between `startingHashes` and `finalHashes`, resulting in
mass false-positive target invalidation across the workspace.

With this change:
- If `MODULE.bazel` is present (and Bzlmod is not explicitly disabled via
  `--noenable_bzlmod` / `--enable_bzlmod=false`) and `bazel mod graph`
  fails while `!self.keep_going`, `check_bzlmod_enabled()` returns an
  `Err` with the stderr output instead of silently treating the workspace
  as legacy.
- When `!self.keep_going`, errors from `version()`,
  `query_bzlmod_repos()`, and `module_graph_json()` are propagated
  instead of swallowed via `.unwrap_or_else(|_| Vec::new())`.
- When `--keep_going` (`self.keep_going == true`) is enabled, the
  existing warning and partial-graph fallback behavior is preserved.
Comment thread src/bazel.rs Outdated

@tinder-maxwellelliott tinder-maxwellelliott left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@tinder-maxwellelliott
tinder-maxwellelliott merged commit 90b07f4 into Tinder:master Sep 23, 2026
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants