Skip to content

Commit 4f68cc1

Browse files
authored
Make extensions reproducible, add lock files (#1785)
Returns `module_ctx.extension_metadata(reproducible = True)` from module extensions and checks in `MODULE.bazel.lock` files. Adds `--lockfile_mode=error` to the top level `.bazelrc`, and adds `--lockfile_mode=update` where necessary to ensure tests don't break. The following command reveals the substantial part of this change: ```txt $ git diff --stat HEAD^ ':!:**MODULE.bazel.lock' .bazelci/presubmit.yml | 1 + .bazelrc | 6 ++++++ .gitignore | 7 ++----- dt_patches/test_dt_patches/.bazelrc | 5 +++++ dt_patches/test_dt_patches_user_srcjar/.bazelrc | 6 ++++++ dt_patches/test_dt_patches_user_srcjar/extensions.bzl | 4 +++- examples/crossbuild/.bazelrc | 12 ++++++++++++ scala/extensions/config.bzl | 1 + scala/extensions/deps.bzl | 22 ++++++++++++++++++---- scala/extensions/protoc.bzl | 3 +++ scala/private/extensions/dev_deps.bzl | 1 + test/compiler_sources_integrity/.bazelrc | 5 +++++ test/shell/test_bzlmod_macros.sh | 4 +++- test_version.sh | 4 +++- 14 files changed, 69 insertions(+), 12 deletions(-) ``` This change commits the top level `MODULE.bazel.lock` file and the lock files from all nested modules where tests run. It excludes lock files from modules that are consumed by other test modules, but where tests don't actually run, specifically: - `deps/latest` - `dt_patches/compiler_sources` The test module lock files reflect the state after `./test_all.sh` completes successfully. Some test modules explicitly set `--lockfile_mode=update`, so their lock file contents may change during test runs. --- At @jayconrod's suggestion, it seems worth revisiting the `MODULE.bazel.lock` mechanism. The format appears to have stabilized (especially in newer Bazels), and it seems explicitly marking extensions as reproducible helps shrink lock files dramatically. While I've yet to notice (or measure) performance benefits, the stability and security benefits appear worth the effort. The following comments explain different aspects of this change in greater detail. --- The concept of module extension "reproducibility" helps avoid unnecessary `MODULE.bazel.lock` updates. This why most extensions now return `extension_metadata` with `reproducibility = True`: - https://bazel.build/versions/7.6.0/external/extension#specify_reproducibility Before marking `scala_deps` as reproducible, the `MODULE.bazel.lock` file was 30892 lines long. This dramatic effect on our own lock file suggests that this change will potentially help consumers maintain smaller lock files as well. --- Setting `common --lockfile_mode=error` in `.bazelrc` ensures Bazel raises an error when it detects any `MODULE.bazel.lock` files are out of date. However, there are situations where we need to relax this strict setting: - Updating `MODULE.bazel.lock` files after editing `MODULE.bazel` files or module extensions. - Running tests whose modules depend upon nonreproducible extensions. - Running tests that generate their own temporary test modules. - Running the entire test suite with different versions of Bazel. The rest of this commit message describes how this change handles each of these cases. --- Per the new `.bazelrc` comment, we need to set `--lockfile_mode=refresh` when actually intend to update any `MODULE.bazel.lock` files. --- The following test modules depend upon nonreproducible extensions, which require `MODULE.bazel.lock` updates during test runs: 1. `dt_patches/test_dt_patches` 2. `dt_patches/test_dt_patches_user_srcjar` 3. `test/compiler_sources_integrity` Specifically: - Modules 1. and 2. use the `compiler_sources_repo` extension from `dt_patches/compiler_sources`, which instantiates Maven artifact repos without supplying any integrity values. This is the textbook definition of a nonreproducible module extension. - Modules 2. and 3. contain `scala_deps.compiler_srcjar` tags without a `label`, `sha256`, or `integrity` attribute. This should prove so limited a case as to be nonexistent in practice. Even so, the `scala_deps` extension makes sure to indicate that it's _not_ reproducible in that case. Diffing the `dt_patches` test module lock files shows what happens when a `compiler_srcjar` tag lacks those attributes: ```sh diff dt_patches/test_dt_patches{,_user_srcjar}/MODULE.bazel.lock ``` These modules' `.bazelrc` files now set `common --lockfile_mode=update` after importing the top level `.bazelrc`. This prevents Bazel breakages by overriding the inherited `common --lockfile_mode=error` setting. --- Tests that generate their own test modules fail under `--lockfile_mode=error` because Bazel needs to generate `MODULE.bazel.lock` files for such modules. The following test scripts, which previously only copied the top level `.bazelrc` into their generated modules, now use `sed` to set `--lockfile_mode=update`: - `test/shell/test_bzlmod_macros.sh` - `test_version.sh` --- Finally, running tests with different versions of Bazel while `--lockfile_mode=error` is in effect will fail. This is because the lock file version generated by a specific Bazel version isn't compatible with any other Bazel version. - The `.bazelrc` line introducing the `--lockfile_mode` flag has a comment suggesting that users set it to `update` when testing other Bazel versions. - `.bazelci/presubmit.yml` now applies `--lockfile_mode=update` to the `last_green` job. - `examples/crossbuild/.bazelrc` now applies `--lockfile_mode=update`. This ensures that the Bazel Central Registry presubmit jobs based on this module, which run under a matrix of different Bazel versions, will all succeed. Otherwise, the `bazel --nosystem_rc --nohome_rc version` setup command will break before the `build_flags` and `test_flags` attributes apply. (Note this command _doesn't_ use `--noworkspace_rc`.)
1 parent 747b660 commit 4f68cc1

File tree

31 files changed

+4792
-12
lines changed

31 files changed

+4792
-12
lines changed

.bazelci/presubmit.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ tasks:
4141
shell_commands:
4242
# Install xmllint
4343
- sudo apt update && sudo apt install --reinstall libxml2-utils -y
44+
- echo "common --lockfile_mode=update" >>.bazelrc
4445
- "./test_rules_scala.sh"
4546
soft_fail:
4647
- exit_status: "*"

.bazelrc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
# Remove once Bazel 8 becomes the minimum supported version.
22
common --noenable_workspace --incompatible_use_plus_in_repo_names
33

4+
# Ensure that the `MODULE.bazel.lock` files don't change silently.
5+
# - Set to `refresh` to actually update the lockfiles.
6+
# - Set to `update` when testing a Bazel version different from .bazelversion
7+
# (or comment it out, since `update` is the default).
8+
common --lockfile_mode=error
9+
410
# Uncomment to run tests under `WORKSPACE`. Remove once Bazel 9 becomes the
511
# minimum supported version.
612
#common --enable_workspace --noenable_bzlmod

.gitignore

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,9 @@ repository-artifacts.json
2020
# From scripts/*.py
2121
**/__pycache__/
2222

23-
# Until it settles down
24-
**/MODULE.bazel.lock
25-
2623
# Used by some tests, but can also be used for local experimentation.
2724
tmp/
2825

2926
# Not required by tests.
30-
deps/latest/.bazelversion
31-
27+
deps/latest/MODULE.bazel.lock
28+
dt_patches/compiler_sources/MODULE.bazel.lock

MODULE.bazel.lock

Lines changed: 545 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
11
import ../../.bazelrc
2+
3+
# `MODULE.bazel` uses the nonreproducible `compiler_source_repos` module
4+
# extension. As a result, `--repo_env=SCALA_VERSION` values from test cases
5+
# change the `MODULE.bazel.lock` file.
6+
common --lockfile_mode=update

dt_patches/test_dt_patches/MODULE.bazel.lock

Lines changed: 454 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
11
import ../../.bazelrc
2+
3+
# `MODULE.bazel` uses the nonreproducible `compiler_source_repos` module
4+
# extension and contains `scala_deps.compiler_srcjar` tags without `sha256`,
5+
# `integrity`, or `label` attributes. As a result, `--repo_env=SCALA_VERSION`
6+
# values from test cases change the `MODULE.bazel.lock` file.
7+
common --lockfile_mode=update

dt_patches/test_dt_patches_user_srcjar/MODULE.bazel.lock

Lines changed: 578 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dt_patches/test_dt_patches_user_srcjar/extensions.bzl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ def import_compiler_user_srcjar_repos():
1313
url = "https://repo1.maven.org/maven2/org/scala-lang/scala3-compiler_3/3.4.3/scala3-compiler_3-3.4.3-sources.jar",
1414
)
1515

16-
def _compiler_user_srcjar_repos_impl(_ctx):
16+
def _compiler_user_srcjar_repos_impl(module_ctx):
1717
import_compiler_user_srcjar_repos()
18+
return module_ctx.extension_metadata(reproducible = True)
19+
1820

1921
compiler_user_srcjar_repos = module_extension(
2022
implementation = _compiler_user_srcjar_repos_impl,

examples/crossbuild/.bazelrc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,13 @@
11
import ../../.bazelrc
2+
3+
# The Bazel Central Registry presubmit jobs in `.bazelci/presubmit.yml` and
4+
# `.bcr/presubmit.yml` use this module. However, those jobs run `bazel
5+
# --nosystem_rc --nohome_rc version` before the `build_flags` and `test_flags`
6+
# attributes apply. (Note this command _doesn't_ use `--noworkspace_rc`.)
7+
#
8+
# Without the following override, `--lockfile_mode=error` still applies, even if
9+
# `--lockfile_mode=update` appears in `build_flags` and `test_flags`. Since
10+
# these jobs run under a matrix of different Bazel versions, most jobs will
11+
# break without this flag. This is because the lock file version generated by a
12+
# specific Bazel version isn't compatible with any other Bazel version.
13+
common --lockfile_mode=update

0 commit comments

Comments
 (0)