Skip to content

Commit

Permalink
Service to annotate commits with bazel build information (#761)
Browse files Browse the repository at this point in the history
  • Loading branch information
LittleChimera authored Aug 22, 2024
1 parent 66de608 commit 1f4df9a
Show file tree
Hide file tree
Showing 14 changed files with 346 additions and 22 deletions.
10 changes: 5 additions & 5 deletions .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ build --@rules_rust//:extra_rustc_flags=-Cdebug-assertions=on
build --@rules_rust//:extra_rustc_flag=-Dbindings_with_variant_name
build --strip=never

# build:dfinity --remote_cache=bazel-remote.idx.dfinity.network
build:dfinity --remote_cache=bazel-remote.idx.dfinity.network
# build --remote_cache=grpc://localhost:9092
build --remote_instance_name=default
build --google_default_credentials=false
Expand All @@ -46,14 +46,14 @@ build --remote_timeout=30s # Default is 60s.
build:ci --remote_timeout=5m # Default is 60s.
build:ci --remote_upload_local_results=true

# build:dfinity --experimental_remote_downloader=bazel-remote.idx.dfinity.network --experimental_remote_downloader_local_fallback
build:dfinity --experimental_remote_downloader=bazel-remote.idx.dfinity.network --experimental_remote_downloader_local_fallback
build:local --experimental_remote_downloader=

# Does not produce valid JSON. See https://github.com/bazelbuild/bazel/issues/14209
build --execution_log_json_file=bazel-build-log.json

# build:dfinity --bes_results_url=https://dash.idx.dfinity.network/invocation/
# build:dfinity --bes_backend=bes.idx.dfinity.network
build:dfinity --bes_results_url=https://dash.idx.dfinity.network/invocation/
build:dfinity --bes_backend=bes.idx.dfinity.network
build --bes_timeout=30s # Default is no timeout.
build:ci --bes_timeout=180s # Default is no timeout.
build:ci --bes_upload_mode=fully_async
Expand All @@ -72,7 +72,7 @@ build:fmt --aspects=@rules_rust//rust:defs.bzl%rustfmt_aspect
build:fmt --output_groups=+rustfmt_checks
build --@rules_rust//:rustfmt.toml=//:rustfmt.toml

test --test_output=errors
test --test_output=streamed
test --test_env=RUST_BACKTRACE=full

test:precommit --build_tests_only
Expand Down
6 changes: 3 additions & 3 deletions WORKSPACE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ http_archive(
)

http_file(
name = "bazelisk_darwin",
sha256 = "9a4b169038a63ebf60a9b4f367b449ab9b484c4ec7d1ef9f6b7a4196dfd50f33",
url = "https://github.com/bazelbuild/bazelisk/releases/download/v1.20.0/bazelisk-darwin",
name = "target_determinator",
sha256 = "65000bba3a5eb1713d93b1e08e33b6fbe5787535664bbc1ba2f4166b0d26d0a1",
url = "https://github.com/bazel-contrib/target-determinator/releases/download/v0.27.0/target-determinator.linux.amd64",
)

http_file(
Expand Down
36 changes: 35 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ slackblocks = "^1.0.10"
aiohttp = "^3.10.3"
quart = "^0.19.6"
pytest-asyncio = "^0.23.8"
pytest-xdist = "^3.6.1"

[tool.poetry.group.dev.dependencies]
black = "^24"
Expand Down
49 changes: 47 additions & 2 deletions release-controller/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ deps = [

dev_deps = [
requirement("httpretty"),
requirement("pytest-xdist"),
]

env = {
Expand All @@ -43,29 +44,73 @@ py_binary(
deps = deps,
)

py_binary(
name = "commit_annotator",
main = "commit_annotator.py",
srcs = glob(
["*.py"],
exclude = [
"test_*.py",
"pytest.py",
],
),
data = [":bazelisk", ":target_determinator"],
env = env,
deps = deps,
)

native_binary(
name = "bazelisk",
src = select({
"@platforms//os:linux": "@bazelisk_linux//file",
"@platforms//os:macos": "@bazelisk_darwin//file",
}),
out = "bazelisk",
)

native_binary(
name = "target_determinator",
src = select({
"@platforms//os:linux": "@target_determinator//file",
}),
out = "target-determinator",
)

long_tests = [
"test_commit_annotator.py",
]

py_test(
name = "pytest",
srcs = ["pytest.py"],
data = glob(["*.py"]) + [":bazelisk"],
data = glob(["*.py"], exclude = long_tests) + [":bazelisk", ":target_determinator"],
env = env,
tags = ["no-sandbox"],
deps = deps + dev_deps,
env_inherit = ["HOME"],
)

py_test(
name = "pytest_enormous",
srcs = ["pytest.py"],
main = "pytest.py",
data = long_tests + glob(["*.py"], exclude = ["test*.py"]) + [":bazelisk", ":target_determinator"],
env = env,
tags = ["no-sandbox"],
deps = deps + dev_deps,
env_inherit = ["HOME"],
size = "enormous",
)

py_oci_image(
name = "oci_image",
base = "@bazel_image_6_5_0",
binary = ":release_controller",
)

py_oci_image(
name = "oci_image_commit_annotator",
base = "@bazel_image_6_5_0",
binary = ":commit_annotator",
)

exports_files(["README.md"])
116 changes: 116 additions & 0 deletions release-controller/commit_annotator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import logging
import os
import subprocess
import sys
import re
from git_repo import GitRepo
from datetime import datetime
from tenacity import retry, stop_after_attempt
from util import bazel_binary

sys.path.append(os.path.join(os.path.dirname(__file__)))

GUESTOS_CHANGED_NOTES_NAMESPACE = "guestos-changed"
GUESTOS_TARGETS_NOTES_NAMESPACE = "guestos-targets"
GUESTOS_BAZEL_TARGETS = "//ic-os/guestos/envs/prod:update-img.tar.zst union //ic-os/setupos/envs/prod:disk-img.tar.zst"
CUTOFF_COMMIT = "8646665552677436c8a889ce970857e531fee49b"


def release_branch_date(branch: str) -> datetime:
branch_search = re.search(r"rc--(\d{4}-\d{2}-\d{2})", branch, re.IGNORECASE)
if branch_search:
branch_date = branch_search.group(1)
else:
raise Exception(f"branch '{branch}' does not match RC branch format")
return datetime.strptime(branch_date, "%Y-%m-%d")


# target-determinator sometimes fails on first few tries
@retry(stop=stop_after_attempt(10))
def target_determinator(ic_repo: GitRepo, object: str) -> bool:
ic_repo.checkout(object)
target_determinator_binary = "target-determinator"
target_determinator_binary_local = os.path.abspath(os.curdir) + "/release-controller/target-determinator"
if os.path.exists(target_determinator_binary_local):
target_determinator_binary = target_determinator_binary_local

p = subprocess.run(
[
target_determinator_binary,
"-before-query-error-behavior=fatal",
f"-bazel={bazel_binary()}",
"--targets",
GUESTOS_BAZEL_TARGETS,
ic_repo.parent(object),
],
cwd=ic_repo.dir,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
output = p.stdout.decode().strip()
logging.info(f"stdout of target determinator for {object}: '{output}'")
logging.info(f"stderr of target determinator for {object}: '{p.stderr.decode().strip()}'")
return output != ""


def annotate_object(ic_repo: GitRepo, object: str):
logging.info("annotating {}".format(object))
ic_repo.checkout(object)
ic_repo.add_note(
GUESTOS_TARGETS_NOTES_NAMESPACE,
object=object,
content="\n".join(
[
l
for l in subprocess.check_output(["bazel", "query", f"deps({GUESTOS_BAZEL_TARGETS})"], cwd=ic_repo.dir)
.decode()
.splitlines()
if not l.startswith("@")
]
),
)
ic_repo.add_note(
namespace=GUESTOS_CHANGED_NOTES_NAMESPACE,
object=object,
content=str(target_determinator(ic_repo=ic_repo, object=object)),
)


def annotate_branch(ic_repo: GitRepo, branch: str):
commits = []
current_commit = branch
ic_repo.checkout(branch)
while True:
if current_commit == CUTOFF_COMMIT:
break
if ic_repo.get_note(namespace=GUESTOS_CHANGED_NOTES_NAMESPACE, object=current_commit):
break

logging.info("will annotate {}".format(current_commit))
commits.append(current_commit)
current_commit = ic_repo.parent(current_commit)

# reverse to annotate oldest objects first so that loop can be easily restarted if it breaks
for c in reversed(commits):
annotate_object(ic_repo=ic_repo, object=c)


def main():
ic_repo = GitRepo(
f"https://oauth2:{os.environ['GITHUB_TOKEN']}@github.com/{os.environ['GITHUB_ORG']}/ic.git",
main_branch="master",
)
while True:
ic_repo.fetch()
for b in ic_repo.branch_list("rc--*"):
if (datetime.now() - release_branch_date(b)).days > 20:
logging.info("skipping branch {}".format(b))
continue
logging.info("annotating branch {}".format(b))
annotate_branch(ic_repo, branch=b)


if __name__ == "__main__":
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
main()
Loading

0 comments on commit 1f4df9a

Please sign in to comment.