Skip to content

Commit 1375273

Browse files
authored
BCR bazel compatibility test: Add generate_report.py (#2091)
This will be run in the last step for a https://buildkite.com/bazel/bcr-bazel-compatibility-test build without `USE_BAZELISK_MIGRATE` and generate a report in markdown that can be used for filing a GitHub issue report for broken modules. e.g.: <img width="685" alt="image" src="https://github.com/user-attachments/assets/3e888dd0-601c-4b38-bce0-047eb80be9bb">
1 parent 9b86972 commit 1375273

File tree

2 files changed

+128
-0
lines changed

2 files changed

+128
-0
lines changed

buildkite/bazel-central-registry/bcr_compatibility.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import os
2424
import sys
2525
import subprocess
26+
import time
2627

2728
import bazelci
2829
import bcr_presubmit
@@ -47,6 +48,14 @@
4748
# Default to use only 30% of CI resources for each type of machines.
4849
CI_RESOURCE_PERCENTAGE = int(os.environ.get('CI_RESOURCE_PERCENTAGE', 30))
4950

51+
SCRIPT_URL = "https://raw.githubusercontent.com/bazelbuild/continuous-integration/{}/buildkite/bazel-central-registry/generate_report.py?{}".format(
52+
bazelci.GITHUB_BRANCH, int(time.time())
53+
)
54+
55+
56+
def fetch_generate_report_py_command():
57+
return "curl -s {0} -o generate_report.py".format(SCRIPT_URL)
58+
5059

5160
def select_modules_from_env_vars():
5261
"""
@@ -108,6 +117,26 @@ def create_step_for_report_flags_results():
108117
),
109118
]
110119

120+
def create_step_for_generate_report():
121+
parts = [
122+
bazelci.PLATFORMS[bazelci.DEFAULT_PLATFORM]["python"],
123+
"generate_report.py",
124+
"--build_number=%s" % os.getenv("BUILDKITE_BUILD_NUMBER"),
125+
]
126+
return [
127+
{"wait": "~", "continue_on_failure": "true"},
128+
bazelci.create_step(
129+
label="Generate report in markdown",
130+
commands=[
131+
bazelci.fetch_bazelcipy_command(),
132+
bcr_presubmit.fetch_bcr_presubmit_py_command(),
133+
fetch_generate_report_py_command(),
134+
" ".join(parts),
135+
],
136+
platform=bazelci.DEFAULT_PLATFORM,
137+
),
138+
]
139+
111140
def main():
112141
modules = get_target_modules()
113142
pipeline_steps = []
@@ -127,6 +156,8 @@ def main():
127156
pipeline_steps.insert(0, {"block": "Please review generated jobs before proceeding", "blocked_state": "running"})
128157
if bazelci.use_bazelisk_migrate():
129158
pipeline_steps += create_step_for_report_flags_results()
159+
else:
160+
pipeline_steps += create_step_for_generate_report()
130161

131162
bcr_presubmit.upload_jobs_to_pipeline(pipeline_steps)
132163

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
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 generate report for BCR Bazel Compatibility Test pipeline."""
21+
22+
23+
import argparse
24+
import collections
25+
import os
26+
import json
27+
import re
28+
import sys
29+
30+
import bazelci
31+
import bcr_presubmit
32+
33+
BUILDKITE_ORG = os.environ["BUILDKITE_ORGANIZATION_SLUG"]
34+
35+
PIPELINE = os.environ["BUILDKITE_PIPELINE_SLUG"]
36+
37+
MODULE_VERSION_PATTERN = re.compile(r'(?P<module_version>[a-z](?:[a-z0-9._-]*[a-z0-9])?@[^\s]+)')
38+
39+
def extract_module_version(line):
40+
match = MODULE_VERSION_PATTERN.search(line)
41+
if match:
42+
return match.group("module_version")
43+
44+
45+
def get_github_maintainer(module_name):
46+
metadata = json.load(open(bcr_presubmit.get_metadata_json(module_name), "r"))
47+
github_maintainers = []
48+
for maintainer in metadata["maintainers"]:
49+
if "github" in maintainer:
50+
github_maintainers.append(maintainer["github"])
51+
52+
if not github_maintainers:
53+
github_maintainers.append("bazelbuild/bcr-maintainers")
54+
return github_maintainers
55+
56+
57+
def print_report_in_markdown(failed_jobs_per_module, pipeline_url):
58+
bazel_version = os.environ.get("USE_BAZEL_VERSION")
59+
print("\n")
60+
print("## The following modules are broken%s:" % (f" with Bazel@{bazel_version}" if bazel_version else ""))
61+
print("BCR Bazel Compatibility Test: ", pipeline_url)
62+
for module, jobs in failed_jobs_per_module.items():
63+
module_name = module.strip().split("@")[0]
64+
github_maintainers = get_github_maintainer(module_name)
65+
print(f"### {module}")
66+
print("Maintainers: ", ", ".join(f"@{maintainer}" for maintainer in github_maintainers))
67+
for job in jobs:
68+
print(f"- [{job['name']}]({job['web_url']})")
69+
print("\n")
70+
71+
72+
def main(argv=None):
73+
if argv is None:
74+
argv = sys.argv[1:]
75+
76+
parser = argparse.ArgumentParser(description="Script to report BCR Bazel Compatibility Test result.")
77+
parser.add_argument("--build_number", type=str)
78+
79+
args = parser.parse_args(argv)
80+
if not args.build_number:
81+
parser.print_help()
82+
return 2
83+
84+
client = bazelci.BuildkiteClient(org=BUILDKITE_ORG, pipeline=PIPELINE)
85+
build_info = client.get_build_info(args.build_number)
86+
failed_jobs_per_module = collections.defaultdict(list)
87+
for job in build_info["jobs"]:
88+
if job.get("state") == "failed" and "name" in job:
89+
module = extract_module_version(job["name"])
90+
if not module:
91+
continue
92+
failed_jobs_per_module[module].append(job)
93+
94+
print_report_in_markdown(failed_jobs_per_module, build_info["web_url"])
95+
96+
if __name__ == "__main__":
97+
sys.exit(main())

0 commit comments

Comments
 (0)