Skip to content

Commit 106ac22

Browse files
authored
Add bazel support (with tests), add ability to cache downloads locally for increased iteration speed. (#19)
* Add bazel support, optional local caching for downloads * lint * Try to fix secrets * Add bazel section to readme
1 parent b92b312 commit 106ac22

File tree

13 files changed

+372
-4
lines changed

13 files changed

+372
-4
lines changed

.bazelrc

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
try-import user.bazelrc
2+
try-import .buildbuddy-auth.rc
3+
4+
test --test_output=errors
5+
6+
# Build Buddy Cache Setup
7+
build:build_buddy --bes_results_url=https://app.buildbuddy.io/invocation/
8+
build:build_buddy --bes_backend=grpcs://remote.buildbuddy.io
9+
build:build_buddy --remote_cache=grpcs://remote.buildbuddy.io
10+
build:build_buddy --remote_timeout=3600
11+
12+
# Additional suggestions from buildbuddy for speed
13+
build:build_buddy --experimental_remote_cache_compression
14+
build:build_buddy --experimental_remote_cache_compression_threshold=100
15+
build:build_buddy --noslim_profile
16+
build:build_buddy --experimental_profile_include_target_label
17+
build:build_buddy --experimental_profile_include_primary_output
18+
build:build_buddy --nolegacy_important_outputs
19+
20+
build:ci --config=build_buddy
21+
build:ci --remote_download_minimal
22+
build:ci --build_metadata=ROLE=CI
23+
build:ci --build_metadata=VISIBILITY=PUBLIC

.bazelversion

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
7.2.0

.github/workflows/bazel.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# This workflow runs the JSON checker on each vendordep JSON file.
2+
3+
name: Bazel CI
4+
5+
# Controls when the action will run. Triggers the workflow on push or pull request
6+
# events but only for the main branch
7+
on:
8+
push:
9+
pull_request:
10+
11+
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
12+
jobs:
13+
# This workflow contains a single job called "run-json-checker"
14+
run-json-checker:
15+
# The type of runner that the job will run on
16+
runs-on: ubuntu-latest
17+
18+
# Steps represent a sequence of tasks that will be executed as part of the job
19+
steps:
20+
- uses: actions/checkout@v4
21+
with:
22+
fetch-depth: 0 # Fetch all history so we can use git diff
23+
24+
- name: Maybe setup BuildBuddy key
25+
env:
26+
API_KEY: ${{ secrets.API_KEY }}
27+
if: ${{ env.API_KEY != '' }}
28+
shell: bash
29+
run: |
30+
echo "API Key detected!"
31+
echo "build:build_buddy --remote_header=x-buildbuddy-api-key=${{ env.API_KEY }}" > .buildbuddy-auth.rc
32+
33+
- name: Run check
34+
run: |
35+
bazel test //... -k --config=ci

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
*.sw?
2+
3+
bazel-*
4+
user.bazelrc

BUILD.bazel

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
load("@rules_python//python:defs.bzl", "py_binary")
2+
load("@rules_python//python:pip.bzl", "compile_pip_requirements")
3+
load("@vendor-json-repo-pip//:requirements.bzl", "requirement")
4+
load("//:test_utils.bzl", "vendordep_check_test")
5+
6+
# bazel run //:requirements.update
7+
compile_pip_requirements(
8+
name = "requirements",
9+
extra_args = ["--allow-unsafe"],
10+
requirements_in = "requirements.txt",
11+
requirements_txt = "requirements_lock.txt",
12+
)
13+
14+
py_binary(
15+
name = "check",
16+
srcs = ["check.py"],
17+
visibility = ["//visibility:public"],
18+
deps = [
19+
requirement("pyelftools"),
20+
requirement("pefile"),
21+
],
22+
)
23+
24+
# Change this for local testing only.
25+
cache_directory = None
26+
27+
[vendordep_check_test(
28+
allowable_errors = 1,
29+
allowable_warnings = None,
30+
cache_directory = cache_directory,
31+
vendor_file = f,
32+
) for f in glob(["2024/*.json"])]
33+
34+
[vendordep_check_test(
35+
allowable_errors = 0,
36+
allowable_warnings = None,
37+
cache_directory = cache_directory,
38+
vendor_file = f,
39+
) for f in glob(["2025/*.json"])]

MODULE.bazel

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
module(
2+
name = "vendor-json-repo",
3+
version = "",
4+
compatibility_level = 1,
5+
)
6+
7+
bazel_dep(name = "rules_python", version = "0.37.0")
8+
9+
python = use_extension("@rules_python//python/extensions:python.bzl", "python")
10+
python.toolchain(
11+
python_version = "3.10",
12+
)
13+
use_repo(python, "python_versions")
14+
15+
register_toolchains(
16+
"@python_versions//:all",
17+
)
18+
19+
pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip")
20+
pip.parse(
21+
hub_name = "vendor-json-repo-pip",
22+
python_version = "3.10",
23+
requirements_lock = "//:requirements_lock.txt",
24+
)
25+
use_repo(pip, "vendor-json-repo-pip")

MODULE.bazel.lock

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

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,11 @@ Currently only one option is supported: `no_debug_suffix`. Normally debug libra
3535

3636
The check.py script requires the `pyelftools` and `pefile` dependencies be installed; use `pip3 install` to install these.
3737

38+
## Bazel Testing
39+
Pyunit tests are automatically auto generated run using the checker tool against all of the vendordep json files in the repository by bazel.
40+
41+
### Prerequisites
42+
- Install [Bazelisk](https://github.com/bazelbuild/bazelisk/releases) and add it to your path. Bazelisk is a wrapper that will download the correct version of bazel specified in the repository. Note: You can alias/rename the binary to `bazel` if you want to keep the familiar `bazel build` vs `bazelisk build` syntax.
43+
44+
### Running the tests
45+
To run the tests, simply run `bazel test //...`. Alternatively, you can run the `checker.py` tool in a standalone mode by running `bazel run //:checker -- <command line arguments from above>`

WORKSPACE.bzlmod

Whitespace-only changes.

check.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import sys
1010
import urllib.request
1111
import uuid
12+
import pathlib
1213
from zipfile import ZipFile, BadZipFile
1314

1415
try:
@@ -38,6 +39,7 @@
3839
got_error = 0
3940
got_warn = 0
4041
message_context = []
42+
cache_directory = None
4143

4244
def msg(t, s):
4345
ctx = ': '.join(message_context) + ': ' if message_context else ''
@@ -64,19 +66,21 @@ def info(s):
6466
local_maven = None
6567
year = "2025"
6668

67-
def parse_args():
69+
def parse_args(argv):
6870
"""Parse command line arguments. Returns list of filenames."""
6971
parser = argparse.ArgumentParser(description='Checks a vendor json file')
7072
parser.add_argument('--verbose', '-v', action='count', help='increase the verbosity of output')
7173
parser.add_argument('--local-maven', help='directory to use for artifacts instead of fetching from mavenUrls')
7274
parser.add_argument('--year', '-y', help='FRC competition season year (used to set known libraries)')
75+
parser.add_argument('--cache_directory', type=pathlib.Path, help='Optional. If present will set up a download cache in this directory to prevent re-downloading artifacts. Should be used for debugging purposes only.')
7376
parser.add_argument('file', nargs='+', help='json file to parse')
74-
args = parser.parse_args()
77+
args = parser.parse_args(argv)
7578

76-
global verbose, local_maven, year
79+
global verbose, local_maven, year, cache_directory
7780
verbose = args.verbose or 0
7881
local_maven = args.local_maven
7982
year = args.year or "2025"
83+
cache_directory = args.cache_directory
8084

8185
return args.file
8286

@@ -208,11 +212,22 @@ def fetch(self, classifier, failok=False):
208212
else:
209213
for baseurl in self.urls:
210214
url = baseurl + self.path + fn
215+
maybe_cached_file = None
216+
if cache_directory:
217+
maybe_cached_file = cache_directory / (self.path + fn)
218+
if maybe_cached_file.exists():
219+
if verbose >= 2:
220+
print(f"Found a cache hit for {maybe_cached_file}")
221+
return fn, maybe_cached_file.read_bytes()
222+
211223
if verbose >= 1:
212224
print('downloading "{0}"'.format(url))
213225
try:
214226
with urlopener.open(url) as f:
215227
result = f.read()
228+
if maybe_cached_file:
229+
maybe_cached_file.parent.mkdir(parents=True, exist_ok=True)
230+
maybe_cached_file.write_bytes(result)
216231
except urllib.error.HTTPError as e:
217232
if not failok:
218233
warn('could not fetch url "{0}": {1}'.format(url, e))
@@ -629,7 +644,7 @@ def check_file(filename):
629644

630645
def main():
631646
had_errors = False
632-
for fn in parse_args():
647+
for fn in parse_args(sys.argv[1:]):
633648
global json_filename, got_error, got_warn
634649
json_filename = fn
635650
got_error = 0

0 commit comments

Comments
 (0)