Skip to content

Commit f9f0579

Browse files
authored
Add bcr_compatibility.py (#2079)
Stack on #2077 - Added `bcr_compatibility.py` for https://buildkite.com/bazel/bcr-bazel-compatibility-test - Added documentation for BCR scripts.
1 parent 2ac682f commit f9f0579

File tree

3 files changed

+179
-1
lines changed

3 files changed

+179
-1
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
## BCR Postsubmit
2+
3+
`bcr_postsubmit.py` is a script used for Bazel Central Registry (BCR) postsubmit operations. It synchronizes the `bazel_registry.json` and the `modules/` directory from the main branch of the Bazel Central Registry to the BCR's public cloud storage bucket.
4+
5+
## BCR Presubmit
6+
7+
`bcr_presubmit.py` is a script used for Bazel Central Registry (BCR) [presubmit operations](https://github.com/bazelbuild/bazel-central-registry/blob/main/docs/README.md#presubmit). This script primarily handles the preparation and execution of tests for new modules or updated versions of modules being added to the Bazel Central Registry.
8+
9+
This script powers the [BCR Presubmit](https://buildkite.com/bazel/bcr-presubmit) pipeline.
10+
11+
## BCR Bazel Compatibility Test
12+
13+
`bcr_compatibility.py` is a script used for testing compatibility between any versions of Bazel and BCR modules, and optionally with given incompatible flags.
14+
15+
A new build can be triggered via the [BCR Bazel Compatibility Test](https://buildkite.com/bazel/bcr-bazel-compatibility-test) pipeline with the following environment variables:
16+
17+
* `MODULE_SELECTIONS`: (Mandatory) A comma-separated list of module patterns to be tested in the format `<module_pattern>@<version_pattern>`. A module is selected if it matches any of the given patterns.
18+
19+
The `<module_pattern>` can include wildcards (*) to match multiple modules (e.g. `rules_*`).
20+
21+
The `<version_pattern>` can be:
22+
23+
- A specific version (e.g. `1.2.3`)
24+
- `latest` to select the latest version
25+
- A comparison operator followed by a version (e.g. `>=1.0.0`, `<2.0.0`)
26+
27+
Examples: `[email protected],rules_java@latest`, `rules_*@latest`, `protobuf@<29.0-rc1`
28+
29+
* `SMOKE_TEST_PERCENTAGE`: (Optional) Specifies a percentage of selected modules to be randomly sampled for smoke testing.
30+
31+
For example, if `MODULE_SELECTIONS=rules_*@latest` and `SMOKE_TEST_PERCENTAGE=10`, then 10% of modules with name starting with `rules_` will be randomly selected.
32+
33+
* `USE_BAZEL_VERSION`: (Optional) Specifies the Bazel version to be used. The script will override Bazel version for all task configs.
34+
35+
* `USE_BAZELISK_MIGRATE`: (Optional) Set this env var to `1` to enable testing incompatible flags with Bazelisk's [`--migrate`](https://github.com/bazelbuild/bazelisk?tab=readme-ov-file#--migrate) feature. A report will be generated for the pipeline if this feature is enabled.
36+
37+
* `INCOMPATIBLE_FLAGS`: (Optional) Specifies the list of incompatible flags to be tested with Bazelisk. By default incompatible flags are fetched by parsing titles of [open Bazel Github issues](https://github.com/bazelbuild/bazel/issues?q=is%3Aopen+is%3Aissue+label%3Aincompatible-change+label%3Amigration-ready) with `incompatible-change` and `migration-ready` labels. Make sure the Bazel version you select support those flags.
38+
39+
* `CI_RESOURCE_PERCENTAGE`: (Optional) Specifies the percentage of CI machine resources to use for running tests. Default is 30%. **ATTENTION**: please do NOT overwhelm CI during busy hours.
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright 2024 The Bazel Authors. All rights reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
# pylint: disable=line-too-long
17+
# pylint: disable=missing-function-docstring
18+
# pylint: disable=unspecified-encoding
19+
# pylint: disable=invalid-name
20+
"""The CI script for BCR Bazel Compatibility Test pipeline."""
21+
22+
23+
import os
24+
import sys
25+
import subprocess
26+
27+
import bazelci
28+
import bcr_presubmit
29+
30+
CI_MACHINE_NUM = {
31+
"bazel": {
32+
"default": 100,
33+
"windows": 30,
34+
"macos_arm64": 45,
35+
"macos": 110,
36+
"arm64": 1,
37+
},
38+
"bazel-testing": {
39+
"default": 30,
40+
"windows": 4,
41+
"macos_arm64": 1,
42+
"macos": 10,
43+
"arm64": 1,
44+
},
45+
}[bazelci.BUILDKITE_ORG]
46+
47+
# Default to use only 30% of CI resources for each type of machines.
48+
CI_RESOURCE_PERCENTAGE = int(os.environ.get('CI_RESOURCE_PERCENTAGE', 30))
49+
50+
51+
def select_modules_from_env_vars():
52+
"""
53+
Parses MODULE_SELECTIONS and SMOKE_TEST_PERCENTAGE environment variables
54+
and returns a list of selected module versions.
55+
"""
56+
MODULE_SELECTIONS = os.environ.get('MODULE_SELECTIONS', '')
57+
SMOKE_TEST_PERCENTAGE = os.environ.get('SMOKE_TEST_PERCENTAGE', None)
58+
59+
if not MODULE_SELECTIONS:
60+
return []
61+
62+
selections = [s.strip() for s in MODULE_SELECTIONS.split(',') if s.strip()]
63+
args = [f"--select={s}" for s in selections]
64+
if SMOKE_TEST_PERCENTAGE:
65+
args += [f"--random-percentage={SMOKE_TEST_PERCENTAGE}"]
66+
output = subprocess.check_output(
67+
["python3", "./tools/module_selector.py"] + args,
68+
)
69+
modules = []
70+
for line in output.decode("utf-8").split():
71+
name, version = line.strip().split("@")
72+
modules.append((name, version))
73+
return modules
74+
75+
76+
def get_target_modules():
77+
"""
78+
If the `MODULE_SELECTIONS` and `SMOKE_TEST_PERCENTAGE(S)` are specified, calculate the target modules from those env vars.
79+
Otherwise, calculate target modules based on changed files from the main branch.
80+
"""
81+
if "MODULE_SELECTIONS" not in os.environ:
82+
raise ValueError("Please set MODULE_SELECTIONS env var to select modules for testing!")
83+
84+
modules = select_modules_from_env_vars()
85+
if modules:
86+
bazelci.print_expanded_group("The following modules are selected:\n\n%s" % "\n".join([f"{name}@{version}" for name, version in modules]))
87+
return sorted(list(set(modules)))
88+
else:
89+
raise ValueError("MODULE_SELECTIONS env var didn't select any modules!")
90+
91+
92+
def create_step_for_report_flags_results():
93+
parts = [
94+
bazelci.PLATFORMS[bazelci.DEFAULT_PLATFORM]["python"],
95+
"aggregate_incompatible_flags_test_result.py",
96+
"--build_number=%s" % os.getenv("BUILDKITE_BUILD_NUMBER"),
97+
]
98+
return [
99+
{"wait": "~", "continue_on_failure": "true"},
100+
bazelci.create_step(
101+
label="Aggregate incompatible flags test result",
102+
commands=[
103+
bazelci.fetch_bazelcipy_command(),
104+
bazelci.fetch_aggregate_incompatible_flags_test_result_command(),
105+
" ".join(parts),
106+
],
107+
platform=bazelci.DEFAULT_PLATFORM,
108+
),
109+
]
110+
111+
def main():
112+
modules = get_target_modules()
113+
pipeline_steps = []
114+
# A function to calculate concurrency number for each BuildKite queue
115+
calc_concurrency = lambda queue : max(1, (CI_RESOURCE_PERCENTAGE * CI_MACHINE_NUM[queue]) // 100)
116+
# Respect USE_BAZEL_VERSION to override bazel version in presubmit.yml files.
117+
bazel_version = os.environ.get("USE_BAZEL_VERSION")
118+
for module_name, module_version in modules:
119+
previous_size = len(pipeline_steps)
120+
121+
configs = bcr_presubmit.get_anonymous_module_task_config(module_name, module_version, bazel_version)
122+
bcr_presubmit.add_presubmit_jobs(module_name, module_version, configs.get("tasks", {}), pipeline_steps, overwrite_bazel_version=bazel_version, calc_concurrency=calc_concurrency)
123+
configs = bcr_presubmit.get_test_module_task_config(module_name, module_version, bazel_version)
124+
bcr_presubmit.add_presubmit_jobs(module_name, module_version, configs.get("tasks", {}), pipeline_steps, is_test_module=True, overwrite_bazel_version=bazel_version, calc_concurrency=calc_concurrency)
125+
126+
if len(pipeline_steps) == previous_size:
127+
bcr_presubmit.error("No pipeline steps generated for %s@%s. Please check the configuration." % (module_name, module_version))
128+
129+
if pipeline_steps:
130+
# Always wait for approval to proceed
131+
pipeline_steps = [{"block": "Please review generated jobs before proceeding", "blocked_state": "running"}] + pipeline_steps
132+
if bazelci.use_bazelisk_migrate():
133+
pipeline_steps += create_step_for_report_flags_results()
134+
135+
bcr_presubmit.upload_jobs_to_pipeline(pipeline_steps)
136+
137+
138+
if __name__ == "__main__":
139+
sys.exit(main())

buildkite/bazel-central-registry/bcr_presubmit.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ def main(argv=None):
499499
error("No pipeline steps generated for %s@%s. Please check the configuration." % (module_name, module_version))
500500

501501
if should_wait_bcr_maintainer_review(modules) and pipeline_steps:
502-
pipeline_steps = [{"block": "Wait on BCR maintainer review", "blocked_state": "running"}] + pipeline_steps
502+
pipeline_steps.insert(0, {"block": "Wait on BCR maintainer review", "blocked_state": "running"})
503503

504504
upload_jobs_to_pipeline(pipeline_steps)
505505
elif args.subparsers_name == "anonymous_module_runner":

0 commit comments

Comments
 (0)