Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .bazelrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# Use a symlink prefix to avoid clobbering the workspace root.
build --symlink_prefix=.bazel/
# Disable Aspect Telemetry tracking.
common --repo_env=DO_NOT_TRACK=1
common --repo_env=DO_NOT_TRACK=1
# Windows: rules_js's js_binary (markdownlint) needs a runfiles tree, and
# Windows builds only a runfiles manifest by default.
common --enable_platform_specific_config
build:windows --enable_runfiles
57 changes: 0 additions & 57 deletions .cursor/skills/versioning/SKILL.md

This file was deleted.

152 changes: 126 additions & 26 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
name: CI

# Every public rule is exercised by //... (tests/, unit tests, lint) on all six
# supported platforms. Runs for pull requests and release tags, not every push.
on:
push:
branches: [main]
pull_request:
push:
tags: ["v*"]
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
Expand All @@ -12,48 +15,132 @@ concurrency:
permissions:
contents: read

env:
BAZELISK_VERSION: 1.29.0

jobs:
ci:
name: Bazel
runs-on: ubuntu-latest
timeout-minutes: 45
env:
BAZELISK_VERSION: 1.29.0
BAZELISK_SHA256: 5a408715e932c0250d28bd84555f12edbf70117de42f9181691c736eacc4a992
# Portable caches only. Do not persist output_user_root (~/.cache/bazel):
# it includes sandbox, execroot, and a live Bazel server.
BAZEL_DISK_CACHE: /home/runner/.cache/bazel-disk
BAZEL_REPO_CACHE: /home/runner/.cache/bazel-repo
bazel:
name: Bazel (${{ matrix.platform }})
runs-on: ${{ matrix.runner }}
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
include:
- platform: linux-amd64
runner: ubuntu-24.04
bazelisk: bazelisk-linux-amd64
bazelisk_sha256: 5a408715e932c0250d28bd84555f12edbf70117de42f9181691c736eacc4a992
bazel: bazel
shell: bash
cache_root: ~/.cache/bazel-ci
- platform: linux-arm64
runner: ubuntu-24.04-arm
bazelisk: bazelisk-linux-arm64
bazelisk_sha256: e20e8b0f4f240091b7a55bf17b9398bd4f40ee70ae0208dff95dd4c445fb4010
bazel: bazel
shell: bash
cache_root: ~/.cache/bazel-ci
- platform: darwin-amd64
runner: macos-15-intel
bazelisk: bazelisk-darwin-amd64
bazelisk_sha256: 16c3d7aa15323a9fb69f56c7ec5733ed18bedb786680d0ba13bb12a3c8083007
bazel: bazel
shell: bash
cache_root: ~/.cache/bazel-ci
- platform: darwin-arm64
runner: macos-15
bazelisk: bazelisk-darwin-arm64
bazelisk_sha256: cee851f726789227d5561004e9904a52be45c3efb56f8b38b6993d6adbaa0409
bazel: bazel
shell: bash
cache_root: ~/.cache/bazel-ci
- platform: windows-amd64
runner: windows-2025
bazelisk: bazelisk-windows-amd64.exe
bazelisk_sha256: 092a8738d5b41aae7a85c42cc961b1034e3389aba43ffc20c0fabda7b43e095b
bazel: bazel.exe
# PowerShell, not Git Bash, so Bazel does not inherit Git's Unix tools.
shell: pwsh
cache_root: C:/bazel-ci
- platform: windows-arm64
runner: windows-11-arm
bazelisk: bazelisk-windows-arm64.exe
bazelisk_sha256: 8bc42bd5d7857f18a21440b906469bb6c7cf91a7c72364d4b1e5ec56a76fe94f
bazel: bazel.exe
shell: pwsh
cache_root: C:/bazel-ci
defaults:
run:
shell: ${{ matrix.shell }}
steps:
# Keep LF so buildifier, markdownlint, and `git diff` see the committed bytes.
- name: Disable CRLF conversion
shell: bash
run: git config --global core.autocrlf false

- name: Check out repository
uses: actions/checkout@v7.0.1

- name: Install Bazelisk
shell: bash
env:
BAZELISK: ${{ matrix.bazelisk }}
BAZELISK_SHA256: ${{ matrix.bazelisk_sha256 }}
BAZEL_EXE: ${{ matrix.bazel }}
run: |
set -euo pipefail
curl -fsSL -o /tmp/bazelisk \
"https://github.com/bazelbuild/bazelisk/releases/download/v${BAZELISK_VERSION}/bazelisk-linux-amd64"
echo "${BAZELISK_SHA256} /tmp/bazelisk" | sha256sum -c
sudo install -m 0755 /tmp/bazelisk /usr/local/bin/bazel
dir="${RUNNER_TEMP}/bazelisk"
mkdir -p "${dir}"
curl -fsSL -o "${dir}/${BAZEL_EXE}" \
"https://github.com/bazelbuild/bazelisk/releases/download/v${BAZELISK_VERSION}/${BAZELISK}"
# macOS's BSD sha256sum cannot read a checklist from stdin, so
# compute the hash and compare it here. Hash stdin: with a Windows
# path argument, Git Bash's shasum prefixes the hash with `\`.
if command -v shasum >/dev/null; then
actual="$(shasum -a 256 < "${dir}/${BAZEL_EXE}" | awk '{print $1}')"
else
actual="$(sha256sum < "${dir}/${BAZEL_EXE}" | awk '{print $1}')"
fi
if [[ "${actual}" != "${BAZELISK_SHA256}" ]]; then
echo "bazelisk sha256 mismatch: got ${actual}, want ${BAZELISK_SHA256}" >&2
exit 1
fi
chmod +x "${dir}/${BAZEL_EXE}"
echo "${dir}" >> "${GITHUB_PATH}"

- name: Cache Bazel
uses: actions/cache@v6.1.0
with:
path: |
${{ env.BAZEL_DISK_CACHE }}
${{ env.BAZEL_REPO_CACHE }}
${{ matrix.cache_root }}
~/.cache/bazelisk
key: bazel-${{ runner.os }}-cas-${{ hashFiles('.bazelversion', 'MODULE.bazel.lock', 'markdown/pnpm-lock.yaml', 'python/uv.lock', 'go/go.sum') }}
~/Library/Caches/bazelisk
~/AppData/Local/bazelisk
key: bazel-${{ matrix.platform }}-${{ hashFiles('.bazelversion', 'MODULE.bazel.lock', 'markdown/pnpm-lock.yaml', 'go/go.sum') }}
restore-keys: |
bazel-${{ runner.os }}-cas-
bazel-${{ matrix.platform }}-

- name: Configure Bazel caches
# Portable caches only. Do not persist output_user_root: it includes the
# sandbox, execroot, and a live Bazel server.
- name: Configure Bazel
shell: bash
env:
CACHE_ROOT: ${{ matrix.cache_root }}
run: |
set -euo pipefail
mkdir -p "${BAZEL_DISK_CACHE}" "${BAZEL_REPO_CACHE}"
root="${CACHE_ROOT/#\~/${HOME}}"
mkdir -p "${root}/disk" "${root}/repo"
{
echo "common --disk_cache=${BAZEL_DISK_CACHE}"
echo "common --repository_cache=${BAZEL_REPO_CACHE}"
if [[ "${RUNNER_OS}" == Windows ]]; then
# Short output root: Windows MAX_PATH.
echo "startup --output_user_root=C:/b"
# No host bash: bazel_utils runs its scripts with busybox-w32, so
# anything that still reaches for Bazel's shell fails loudly.
echo "common --shell_executable=C:/no-host-bash/bash.exe"
fi
echo "common --disk_cache=${root}/disk"
echo "common --repository_cache=${root}/repo"
} >> "${HOME}/.bazelrc"

- name: Build
Expand All @@ -62,7 +149,20 @@ jobs:
- name: Test
run: bazel test //... --keep_going --test_output=errors

# Stop the server so repository/disk caches are idle before actions/cache tars them.
# `bazel run` targets: each must run and leave the checkout unchanged.
- name: Run buildifier_format
run: bazel run //:format

- name: Run buf_format
run: bazel run //tests/buf:format

- name: Run ruff_format
run: bazel run //tests/python:format

- name: Check formatters left no changes
run: git diff --exit-code

# Stop the server so repository/disk caches are idle before actions/cache saves them.
- name: Stop Bazel
if: always()
run: bazel shutdown
10 changes: 10 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ test_suite(
],
)

test_suite(
name = "go_tests",
tests = ["@bazel_utils_go//:go_list_patterns_test"],
)

test_suite(
name = "protoc_tests",
tests = ["@bazel_utils_protoc//plugins:catalog_test"],
)

test_suite(
name = "buf_tests",
tests = [
Expand Down
98 changes: 97 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,101 @@ patch; breaking Starlark API changes bump the minor.

## [Unreleased]

## [0.3.0] - 2026-10-09

### Removed

- Cursor rules (`.cursor/`). Maintainer notes (versioning, the 2-day
release quarantine, pin locations, Windows/ash rules, testing) are in
`CLAUDE.md`.
- **Breaking:** `bazel_utils_python` `pip_audit_test`; use `uv_audit_test`.
The module no longer ships pip-audit, its `uv.lock`, `pyproject.toml`, the
`bazel_utils_pypi` hub, or a Python toolchain, so no Python interpreter is
downloaded and Windows needs no bash for it.

### Added

- `bazel_utils_python`: `uv_audit_test` runs `uv audit --frozen` on the
consumer `uv.lock` (all groups and extras, OSV) with the pinned prebuilt
uv. `flags` are `uv audit` flags (`--ignore`, `--no-dev`, `--no-group`, …)
and the binary override is `uv`. `uv audit` is a uv preview feature; the
wrapper opts in with `--preview-features audit-command`.
- `bazel_utils_protoc`: `protoc-gen-protovalidate-buffa` catalog version
v0.10.2 (optional and oneof strings get the plain-string format and length
rules, `IGNORE_IF_ZERO_VALUE` covers field CEL and predefined rules, custom
rule paths keep the extension's package and messages) for linux, darwin,
and windows on amd64 and arm64.
- `protoc-gen-buffa`, `protoc-gen-buffa-packaging`, `protoc-gen-connect-rust`:
windows-arm64 entries that use the x64 build (no aarch64 asset upstream;
Windows 11 on ARM runs it under emulation).
- CI runs on all six platforms (linux, darwin, windows on amd64 and arm64)
for pull requests and release tags. Each job builds and tests `//...` and
runs the `*_format` targets, which must leave the checkout unchanged.
Windows jobs set a missing `--shell_executable`, so any use of a host bash
fails.
- `//tests` fixtures for every public rule: `buf_deps`, `buf_module`,
`buf_generate` with all nine prebuilt plugins, `buf_plugin`,
`buf_lint_test`, `buf_format`, `golangci_test`, `govulncheck_test`,
`ruff_test`, `ruff_format`, `uv_audit_test`, `cargo_audit_test`
(buildifier and markdownlint run on the repo itself). Unit tests for
`go_list_patterns` and for the plugin catalogs (`catalog_test`: every
version lists all six platforms, and one sha256 names exactly one URL).

### Fixed

- `protoc-gen-grpc-gateway` v2.31.0 windows-arm64 pinned protoc-gen-openapiv2's
sha256. Bazel's repository cache is keyed by sha256, so when openapiv2 was
fetched first, Windows arm64 builds silently got the openapiv2 binary under
the grpc-gateway name. It now pins its own hash from the release's
`checksums.txt`.
- `golangci_test`, `govulncheck_test`: run in the `go.mod` directory, with
`dirs` relative to it. A `go.mod` below the repo root (`//go:go.mod`) failed
with "directory prefix … does not contain main module" unless a root
`go.work` listed it.
- Windows: no host bash. `bazel_utils_core` downloads busybox-w32
FRP-6075 (amd64, arm64). `*_test` and `*_format` rules (buildifier, buf
lint/format, golangci-lint, govulncheck, ruff, uv audit, cargo-audit,
markdownlint) return a `.bat` launcher that runs the wrapper with
`busybox sh` (Bazel on Windows does not run a `.bash` file), and
`buf_module` / `buf_generate` run their scripts with it instead of
`run_shell`. Wrapper scripts are ash-compatible (`;` PATH on Windows,
workspace search stops at `C:/`). `bazel_utils_core` now depends on
`bazel_lib` (batch runfiles lookup). Go wrappers point `%LocalAppData%` and
`%USERPROFILE%` at the test tmpdir when Bazel's Windows test env lacks them
(Go and golangci-lint keep their caches there). `markdownlint_test` needs
`build:windows --enable_runfiles`: rules_js's `js_binary` needs a runfiles
tree. Wrappers find the workspace from the runfiles manifest entry for
`MODULE.bazel`, which points into the checkout: a runfiles tree on Windows
holds a copy, and its directory is not the checkout.
- Windows: prebuilt binaries (buf, protoc plugins, ruff, golangci-lint,
cargo-audit) have a `.exe` output instead of `.bin`, so `bazel run` works
and `buf_generate` puts `.exe` plugins on PATH. `buf_plugin` keeps the
wrapped binary's extension.
- `protoc.plugin` no longer needs host Python. Zip releases (Windows builds of
protoc-gen-go, protoc-gen-connect-go, protoc-gen-protovalidate-buffa) are
downloaded by the repository rule and extracted at build time with the
hermetic bsdtar from `tar.bzl` 0.10.9, only for the platform in use.
Bazel 9.2's own zip reader still crashes on protobuf-go's zip comment.

### Changed

- Raised every language module and the aggregator to `0.3.0` (lockstep;
minor because `pip_audit_test` was removed).
- `protoc-gen-protovalidate-buffa` pin from v0.10.1 to v0.10.2.
- `bazel_utils_go`: rules_go from 0.63.0 to 0.64.1.
- `bazel_utils_python`: ruff from 0.16.9 to 0.16.10.
- `bazel_utils_python`: uv 0.12.23 is fetched directly from its GitHub
release (`python/uv.MODULE.bazel`, sha256s from the release's
`sha256.sum`), like ruff, instead of through aspect_rules_py's `uv_bin`
(default 0.11.6). The module no longer depends on aspect_rules_py, whose
1.12.1 host repository fails on Windows (`Unsupported platform windows`)
and broke analysis of the whole build there.
- `bazel_utils_markdown`: aspect_rules_js from 3.4.1 to 3.5.1, rules_nodejs
from 6.7.5 to 6.7.6, Node from 24.18.0 to 24.21.0, and pnpm from 11.20.0
to 12.10.1. Both are newer than the rules' catalogs, so Node's sha256s
(`node_repositories`) come from nodejs.org `SHASUMS256.txt` and pnpm's
`pnpm_version_integrity` from npm. `package.json` `packageManager` from pnpm 12.6.0 to 12.10.1.

## [0.2.14] - 2026-10-07

### Added
Expand Down Expand Up @@ -223,7 +318,8 @@ Initial tagged release. Language modules for Bazel workspaces:
Pin modules with `git_override` at tag `v0.1.0`. `bazel_utils_core` is a
transitive dependency, not a consumer API.

[unreleased]: https://github.com/sonalect/bazel_utils/compare/v0.2.13...HEAD
[unreleased]: https://github.com/sonalect/bazel_utils/compare/v0.3.0...HEAD
[0.3.0]: https://github.com/sonalect/bazel_utils/compare/v0.2.14...v0.3.0
[0.2.14]: https://github.com/sonalect/bazel_utils/compare/v0.2.13...v0.2.14
[0.2.13]: https://github.com/sonalect/bazel_utils/compare/v0.2.12...v0.2.13
[0.2.12]: https://github.com/sonalect/bazel_utils/compare/v0.2.11...v0.2.12
Expand Down
Loading
Loading