SERVER-122772 force lockfile generation in lint target (#50574)

GitOrigin-RevId: edd025749b675402ef820608b951452cf58715b9
This commit is contained in:
Daniel Moody 2026-03-27 11:15:06 -05:00 committed by MongoDB Bot
parent 3c2780dec4
commit edfd58e48c
6 changed files with 241 additions and 106 deletions

View File

@ -1,5 +1,5 @@
# Apple cc toolchain needs to be loaded before regular cc toolchain or else Apple will just use regular cc toolchain
bazel_dep(name = "apple_support", version = "1.17.1", repo_name = "build_bazel_apple_support")
bazel_dep(name = "apple_support", version = "1.24.1", repo_name = "build_bazel_apple_support")
bazel_dep(name = "bazel_features", version = "1.37.0")
bazel_features_deps = use_extension("//bazel:bzlmod.bzl", "bazel_features_deps")
@ -110,7 +110,7 @@ single_version_override(
version = "0.0.11",
)
# TODO move over from WORKSPACE
# TODO SERVER-122884: move over from WORKSPACE
# buf does not support s390x
#bazel_dep(name = "aspect_rules_lint", version = "1.1.0")
#single_version_override(
@ -174,6 +174,14 @@ local_path_override(
path = "src/third_party/grpc/dist",
)
# googleapis still requests older grpc-java releases that import repos from
# grpc_repo_deps_ext. Keep grpc-java on a newer module that declares those
# dependencies directly so Bazel's module graph stays consistent with grpc@1.74.
single_version_override(
module_name = "grpc-java",
version = "1.74.0",
)
bazel_dep(name = "boringssl", version = "")
local_path_override(
module_name = "boringssl",
@ -226,7 +234,7 @@ multitool = use_extension("@rules_multitool//multitool:extension.bzl", "multitoo
multitool.hub(lockfile = "//tools/lint:multitool.lock.json")
use_repo(multitool, "multitool")
# TODO port over from WORKSPACE
# TODO SERVER-122885: port over from WORKSPACE
# currently breaks pyright
##node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node", dev_dependency = True)
##node.toolchain(node_version = "18.20.4")
@ -250,7 +258,7 @@ setup_local_config_platform(name = "internal_platforms_do_not_use")
register_execution_platforms("@internal_platforms_do_not_use//host:host")
# TODO port over from WORKSPACE
# TODO SERVER-122886: port over from WORKSPACE
# cannot be moved without changing the path or creating a symlink
# to _main~setup_mongo_toolchains~mongo_toolchain_v4 and _main~setup_mongo_toolchains~mongo_toolchain_v5
##setup_mongo_toolchains_extension = use_extension("//bazel/toolchains:mongo_toolchain.bzl", "setup_mongo_toolchains_extension")

106
MODULE.bazel.lock generated
View File

@ -63,6 +63,7 @@
"https://bcr.bazel.build/modules/curl/8.8.0/source.json": "d7d138b6878cf38891692fee0649ace35357fd549b425614d571786f054374d4",
"https://bcr.bazel.build/modules/cython/3.0.11-1/MODULE.bazel": "868b3f5c956c3657420d2302004c6bb92606bfa47e314bab7f2ba0630c7c966c",
"https://bcr.bazel.build/modules/cython/3.0.11-1/source.json": "da318be900b8ca9c3d1018839d3bebc5a8e1645620d0848fa2c696d4ecf7c296",
"https://bcr.bazel.build/modules/envoy_api/0.0.0-20241214-918efc9/MODULE.bazel": "24e05f6f52f37be63a795192848555a2c8c855e7814dbc1ed419fb04a7005464",
"https://bcr.bazel.build/modules/envoy_api/0.0.0-20250128-4de3c74/MODULE.bazel": "1fe72489212c530086e3ffb0e018b2bfef4663200ca03571570f9f006bef1d75",
"https://bcr.bazel.build/modules/envoy_api/0.0.0-20250128-4de3c74/source.json": "028519164a2e24563f4b43d810fdedc702daed90e71e7042d45ba82ad807b46f",
"https://bcr.bazel.build/modules/gazelle/0.32.0/MODULE.bazel": "b499f58a5d0d3537f3cf5b76d8ada18242f64ec474d8391247438bf04f58c7b8",
@ -76,9 +77,8 @@
"https://bcr.bazel.build/modules/googleapis/0.0.0-20240326-1c8d509c5/MODULE.bazel": "a4b7e46393c1cdcc5a00e6f85524467c48c565256b22b5fae20f84ab4a999a68",
"https://bcr.bazel.build/modules/googleapis/0.0.0-20240819-fe8ba054a/MODULE.bazel": "117b7c7be7327ed5d6c482274533f2dbd78631313f607094d4625c28203cacdf",
"https://bcr.bazel.build/modules/googleapis/0.0.0-20240819-fe8ba054a/source.json": "b31fc7eb283a83f71d2e5bfc3d1c562d2994198fa1278409fbe8caec3afc1d3e",
"https://bcr.bazel.build/modules/grpc-java/1.62.2/MODULE.bazel": "99b8771e8c7cacb130170fed2a10c9e8fed26334a93e73b42d2953250885a158",
"https://bcr.bazel.build/modules/grpc-java/1.66.0/MODULE.bazel": "86ff26209fac846adb89db11f3714b3dc0090fb2fb81575673cc74880cda4e7e",
"https://bcr.bazel.build/modules/grpc-java/1.66.0/source.json": "f841b339ff8516c86c3a5272cd053194dd0cb2fdd63157123835e1157a28328d",
"https://bcr.bazel.build/modules/grpc-java/1.74.0/MODULE.bazel": "6e6b8214d1b77244263e13cf62b71467db8284369ad60567cf6ffcc0650dc621",
"https://bcr.bazel.build/modules/grpc-java/1.74.0/source.json": "340f0cec843668dcdb6b7d1460c8f93937dd915e866c26d20ef1f30ffb4c3125",
"https://bcr.bazel.build/modules/grpc-proto/0.0.0-20240627-ec30f58/MODULE.bazel": "88de79051e668a04726e9ea94a481ec6f1692086735fd6f488ab908b3b909238",
"https://bcr.bazel.build/modules/grpc-proto/0.0.0-20240627-ec30f58/source.json": "5035d379c61042930244ab59e750106d893ec440add92ec0df6a0098ca7f131d",
"https://bcr.bazel.build/modules/highwayhash/0.0.0-20240305-5ad3bf8/MODULE.bazel": "5c7f29d5bd70feff14b0f65b39584957e18e4a8d555e5a29a4c36019afbb44b9",
@ -154,7 +154,6 @@
"https://bcr.bazel.build/modules/rules_go/0.50.1/MODULE.bazel": "b91a308dc5782bb0a8021ad4330c81fea5bda77f96b9e4c117b9b9c8f6665ee0",
"https://bcr.bazel.build/modules/rules_go/0.50.1/source.json": "205765fd30216c70321f84c9a967267684bdc74350af3f3c46c857d9f80a4fa2",
"https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74",
"https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86",
"https://bcr.bazel.build/modules/rules_java/5.5.0/MODULE.bazel": "486ad1aa15cdc881af632b4b1448b0136c76025a1fe1ad1b65c5899376b83a50",
"https://bcr.bazel.build/modules/rules_java/6.0.0/MODULE.bazel": "8a43b7df601a7ec1af61d79345c17b31ea1fedc6711fd4abfd013ea612978e39",
"https://bcr.bazel.build/modules/rules_java/6.3.0/MODULE.bazel": "a97c7678c19f236a956ad260d59c86e10a463badb7eb2eda787490f4c969b963",
@ -250,7 +249,7 @@
"moduleExtensions": {
"//bazel:bzlmod.bzl%bazel_features_deps": {
"general": {
"bzlTransitiveDigest": "haX1p8/zzNyOuJ0HhUV2PcV9W2RcHiQiqq8XfYZU1vY=",
"bzlTransitiveDigest": "Yzq+2OTgVNEOXdAUgB6rEQOTzZf4IceAvW4FzohMemU=",
"usagesDigest": "c91kQ0HotoA1cfwWU4KaqCoQU7lT6pGi6XPGIiMGkb0=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
@ -1051,7 +1050,7 @@
"@@googleapis~//:extensions.bzl%switched_rules": {
"general": {
"bzlTransitiveDigest": "vG6fuTzXD8MMvHWZEQud0MMH7eoC4GXY0va7VrFFh04=",
"usagesDigest": "u1DsNDlYHEnzg4MhPeq0ShO+E1bcBtzwrzDydMGgLzo=",
"usagesDigest": "Emclv08sJIve9RFQTs73czcVTPFxfyGr7Wsx9xV8CQ4=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
@ -1216,99 +1215,6 @@
"recordedRepoMappingEntries": []
}
},
"@@grpc-java~//:repositories.bzl%grpc_java_repositories_extension": {
"general": {
"bzlTransitiveDigest": "Dc2mbLV9wlm7nTboicPbl9flI9g9QgQfLfOQCDn6gS8=",
"usagesDigest": "z2C4wlnLSU87Dnn2au3GB1QDnjDst7hcQQh9lzp0FxY=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
"generatedRepoSpecs": {
"com_github_cncf_xds": {
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
"ruleClassName": "http_archive",
"attributes": {
"strip_prefix": "xds-e9ce68804cb4e64cab5a52e3c8baf840d4ff87b7",
"sha256": "0d33b83f8c6368954e72e7785539f0d272a8aba2f6e2e336ed15fd1514bc9899",
"urls": [
"https://github.com/cncf/xds/archive/e9ce68804cb4e64cab5a52e3c8baf840d4ff87b7.tar.gz"
]
}
},
"io_grpc_grpc_proto": {
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
"ruleClassName": "http_archive",
"attributes": {
"sha256": "729ac127a003836d539ed9da72a21e094aac4c4609e0481d6fc9e28a844e11af",
"strip_prefix": "grpc-proto-4f245d272a28a680606c0739753506880cf33b5f",
"urls": [
"https://github.com/grpc/grpc-proto/archive/4f245d272a28a680606c0739753506880cf33b5f.zip"
]
}
},
"envoy_api": {
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
"ruleClassName": "http_archive",
"attributes": {
"sha256": "c4c9c43903e413924b0cb08e9747f3c3a0727ad221a3c446a326db32def18c60",
"strip_prefix": "data-plane-api-1611a7304794e13efe2d26f8480a2d2473a528c5",
"urls": [
"https://storage.googleapis.com/grpc-bazel-mirror/github.com/envoyproxy/data-plane-api/archive/1611a7304794e13efe2d26f8480a2d2473a528c5.tar.gz",
"https://github.com/envoyproxy/data-plane-api/archive/1611a7304794e13efe2d26f8480a2d2473a528c5.tar.gz"
],
"patch_args": [
"-p1"
],
"patches": [
"@@grpc-java~//:buildscripts/data-plane-api-no-envoy.patch"
]
}
}
},
"recordedRepoMappingEntries": [
[
"grpc-java~",
"bazel_tools",
"bazel_tools"
]
]
}
},
"@@grpc~//bazel:grpc_deps.bzl%grpc_repo_deps_ext": {
"general": {
"bzlTransitiveDigest": "ZIK+FkSQ9VFWe+YyxCnihr4xa6trXZn5uM53Vcd3xHo=",
"usagesDigest": "Rubge6xIUTMu6MchlEGlyJK87+WabtqyVWDLoHnR2No=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
"generatedRepoSpecs": {
"google_cloud_cpp": {
"bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl",
"ruleClassName": "http_archive",
"attributes": {
"sha256": "81ea28cf9e5bb032d356b0187409f30b1035f8ea5b530675ea248c8a6c0070aa",
"strip_prefix": "google-cloud-cpp-2.35.0",
"urls": [
"https://storage.googleapis.com/grpc-bazel-mirror/github.com/googleapis/google-cloud-cpp/archive/refs/tags/v2.35.0.tar.gz",
"https://github.com/googleapis/google-cloud-cpp/archive/refs/tags/v2.35.0.tar.gz"
]
}
}
},
"recordedRepoMappingEntries": [
[
"grpc~",
"bazel_tools",
"bazel_tools"
],
[
"grpc~",
"com_github_grpc_grpc",
"grpc~"
]
]
}
},
"@@pybind11_bazel~//:internal_configure.bzl%internal_configure_extension": {
"general": {
"bzlTransitiveDigest": "Jsid+Er+k0JcuPNjK83nTid7q9/fJQeD+ntFHFVkKQg=",
@ -2096,7 +2002,7 @@
},
"@@rules_rust~//crate_universe/private:internal_extensions.bzl%cu_nr": {
"general": {
"bzlTransitiveDigest": "xIb7ARV0rvK0PRriYWndeoE0A+lAPnQfRAAanLyGojY=",
"bzlTransitiveDigest": "oj71N3YUzUnTad1+abu2Or4iicrpqSz//uW1Wt7TDss=",
"usagesDigest": "t9RsYFKNxo9gStVX1j5ojkf0uAuLQJGxkv4ItCvnQ+4=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},

View File

@ -1,4 +1,4 @@
load("@rules_python//python:defs.bzl", "py_library")
load("@rules_python//python:defs.bzl", "py_library", "py_test")
package(default_visibility = ["//visibility:public"])
@ -40,3 +40,14 @@ py_library(
],
visibility = ["//visibility:public"],
)
py_test(
name = "lint_test",
srcs = [
"lint_test.py",
],
visibility = ["//visibility:public"],
deps = [
":wrapper_hook",
],
)

View File

@ -1,4 +1,5 @@
import argparse
import difflib
import os
import pathlib
import platform
@ -14,6 +15,66 @@ sys.path.append(str(REPO_ROOT))
LARGE_FILE_THRESHOLD = 10 * 1024 * 1024 # 10MiB
def _read_optional_bytes(path: pathlib.Path) -> bytes | None:
try:
return path.read_bytes()
except FileNotFoundError:
return None
def _restore_optional_bytes(path: pathlib.Path, data: bytes | None) -> None:
if data is None:
path.unlink(missing_ok=True)
return
path.write_bytes(data)
def _display_path(path: pathlib.Path) -> str:
try:
return str(path.relative_to(REPO_ROOT))
except ValueError:
return str(path)
def _print_unified_diff(path: pathlib.Path, before: bytes | None, after: bytes | None) -> None:
display_path = _display_path(path).lstrip("/\\")
before_lines = (before or b"").decode("utf-8", errors="replace").splitlines(keepends=True)
after_lines = (after or b"").decode("utf-8", errors="replace").splitlines(keepends=True)
diff = difflib.unified_diff(
before_lines,
after_lines,
fromfile=f"a/{display_path}",
tofile=f"b/{display_path}",
)
print("".join(diff), end="")
def _lockfile_has_git_diff(path: pathlib.Path) -> bool:
display_path = _display_path(path)
result = subprocess.run(
["git", "status", "--porcelain", "--", display_path],
capture_output=True,
text=True,
check=True,
cwd=REPO_ROOT,
)
return bool(result.stdout.strip())
def _print_git_diff_against_head(path: pathlib.Path) -> None:
display_path = _display_path(path)
result = subprocess.run(
["git", "diff", "--no-ext-diff", "HEAD", "--", display_path],
capture_output=True,
text=True,
check=True,
cwd=REPO_ROOT,
)
if result.stdout:
print(result.stdout, end="")
def _get_buildozer() -> Optional[str]:
"""Get the path to buildozer, installing it if necessary."""
from buildscripts.install_bazel import install_bazel
@ -180,6 +241,66 @@ class LintRunner:
if not self.keep_going:
raise LinterFail("Linter failed")
def refresh_module_lockfile(
self,
*,
fix: bool,
dry_run: bool,
lockfile_path: pathlib.Path | None = None,
) -> None:
lockfile_path = lockfile_path or REPO_ROOT / "MODULE.bazel.lock"
lockfile_display = _display_path(lockfile_path)
original_contents = _read_optional_bytes(lockfile_path)
print(f"Refreshing {lockfile_display}...")
result = subprocess.run(
[self.bazel_bin, "mod", "deps", "--lockfile_mode=refresh"],
check=False,
stdout=sys.stdout,
stderr=sys.stderr,
)
refreshed_contents = _read_optional_bytes(lockfile_path)
changed = refreshed_contents != original_contents
if result.returncode != 0:
if changed:
_restore_optional_bytes(lockfile_path, original_contents)
self.fail = True
if not self.keep_going:
raise LinterFail(f"Failed to refresh {lockfile_display}")
return
if dry_run:
if not changed:
print(f"{lockfile_display} is up to date.")
return
print(
f"{lockfile_display} would be updated by `bazel mod deps --lockfile_mode=refresh`:"
)
_print_unified_diff(lockfile_path, original_contents, refreshed_contents)
_restore_optional_bytes(lockfile_path, original_contents)
return
if fix:
if changed:
print(f"Updated {lockfile_display} via `bazel mod deps --lockfile_mode=refresh`.")
else:
print(f"{lockfile_display} is up to date.")
return
if not _lockfile_has_git_diff(lockfile_path):
print(f"{lockfile_display} is up to date.")
return
print(f"{lockfile_display} has diffs after `bazel mod deps --lockfile_mode=refresh`.")
_print_git_diff_against_head(lockfile_path)
print("Run the following to attempt to fix the issue automatically:")
print("\tbazel run lint --fix")
self.fail = True
if not self.keep_going:
raise LinterFail(f"{lockfile_display} has diffs after refresh")
def simple_file_size_check(self, files_to_lint: list[str]):
for file in files_to_lint:
if not os.path.isfile(file):
@ -400,6 +521,7 @@ def run_rules_lint(bazel_bin: str, args: list[str]):
keep_going = parsed_args.keep_going
lr = LintRunner(keep_going, bazel_bin)
lr.refresh_module_lockfile(fix=parsed_args.fix, dry_run=parsed_args.dry_run)
files_with_targets = list_files_with_targets(bazel_bin)
lr.list_files_without_targets(files_with_targets, "C++", "cpp", ["src/mongo"])

View File

@ -0,0 +1,88 @@
"""Unit tests for bazel.wrapper_hook.lint."""
import contextlib
import io
import pathlib
import subprocess
import tempfile
import unittest
from unittest import mock
from bazel.wrapper_hook import lint
class RefreshModuleLockfileTest(unittest.TestCase):
def test_check_mode_fails_when_lockfile_has_git_diff_after_refresh(self):
with tempfile.TemporaryDirectory() as temp_dir:
lockfile = pathlib.Path(temp_dir) / "MODULE.bazel.lock"
lockfile.write_text("before\n", encoding="utf-8")
def fake_run(args, **kwargs):
self.assertEqual(args, ["bazel", "mod", "deps", "--lockfile_mode=refresh"])
lockfile.write_text("after\n", encoding="utf-8")
return subprocess.CompletedProcess(args, 0)
runner = lint.LintRunner(keep_going=False, bazel_bin="bazel")
with (
mock.patch.object(lint.subprocess, "run", side_effect=fake_run),
mock.patch.object(lint, "_lockfile_has_git_diff", return_value=True),
mock.patch.object(lint, "_print_git_diff_against_head"),
):
with contextlib.redirect_stdout(io.StringIO()):
with self.assertRaises(lint.LinterFail):
runner.refresh_module_lockfile(
fix=False,
dry_run=False,
lockfile_path=lockfile,
)
self.assertTrue(runner.fail)
self.assertEqual(lockfile.read_text(encoding="utf-8"), "after\n")
def test_fix_mode_updates_lockfile_and_passes(self):
with tempfile.TemporaryDirectory() as temp_dir:
lockfile = pathlib.Path(temp_dir) / "MODULE.bazel.lock"
lockfile.write_text("before\n", encoding="utf-8")
def fake_run(args, **kwargs):
self.assertEqual(args, ["bazel", "mod", "deps", "--lockfile_mode=refresh"])
lockfile.write_text("after\n", encoding="utf-8")
return subprocess.CompletedProcess(args, 0)
runner = lint.LintRunner(keep_going=False, bazel_bin="bazel")
with mock.patch.object(lint.subprocess, "run", side_effect=fake_run):
with contextlib.redirect_stdout(io.StringIO()):
runner.refresh_module_lockfile(
fix=True,
dry_run=False,
lockfile_path=lockfile,
)
self.assertFalse(runner.fail)
self.assertEqual(lockfile.read_text(encoding="utf-8"), "after\n")
def test_fix_dry_run_restores_lockfile_and_passes(self):
with tempfile.TemporaryDirectory() as temp_dir:
lockfile = pathlib.Path(temp_dir) / "MODULE.bazel.lock"
lockfile.write_text("before\n", encoding="utf-8")
def fake_run(args, **kwargs):
self.assertEqual(args, ["bazel", "mod", "deps", "--lockfile_mode=refresh"])
lockfile.write_text("after\n", encoding="utf-8")
return subprocess.CompletedProcess(args, 0)
runner = lint.LintRunner(keep_going=False, bazel_bin="bazel")
with mock.patch.object(lint.subprocess, "run", side_effect=fake_run):
with contextlib.redirect_stdout(io.StringIO()):
runner.refresh_module_lockfile(
fix=True,
dry_run=True,
lockfile_path=lockfile,
)
self.assertFalse(runner.fail)
self.assertEqual(lockfile.read_text(encoding="utf-8"), "before\n")
if __name__ == "__main__":
unittest.main()

View File

@ -831,7 +831,7 @@ tasks:
- func: "bazel run"
vars:
target: >-
--lockfile_mode=error lint //... --lint-yaml=${project} --large-files
lint //... --lint-yaml=${project} --large-files
# Check that the mutational fuzzer can parse all JS filess.
- name: lint_fuzzer_sanity_all