Monorepo for VJftw's Bazel rule sets. Each rule set lives in its own top-level directory as an independent bzlmod module, publishable to the Bazel Central Registry (BCR) on its own version/release cadence. See each module's own README (linked below) for its own quickstart.
- Modules
- Consuming a module from here directly (before/without BCR)
- Layout
- Releasing
- Publishing to BCR
- Adding a new rule set
terraform/-rules_terraform: hermetic, per-target Terraform toolchains (see its own README).plantuml/-rules_plantuml: hermetic PlantUML diagram rendering (see its own README).k8s/-rules_k8s: hermetic Kustomize/Helm/kubectl toolchains, with rules to render and compose Kubernetes manifests (see its own README).opa/-rules_opa: hermetic Open Policy Agent toolchain, with rules to build bundles, evaluate policies, and run policy tests (see its own README).hugo/-rules_hugo: hermetic Hugo static site generator toolchain, with a rule to render a site and a runnable binary for local preview (see its own README).nushell/-rules_nushell: hermetic Nushell toolchain, with rules to run Nushell scripts as binaries and tests, and share modules between them (see its own README).
Every released version is also published as a self-hosted Bazel module
registry, right in this repo under
registry/ - so a module here is consumable via a plain bazel_dep as soon
as it's released, with no BCR submission/acceptance needed at all (BCR
publishing, below, is a separate, optional step for wider discoverability).
In your own project's .bazelrc:
common --registry=https://raw.githubusercontent.com/VJftw/bazel-rules/main/registry/
common --registry=https://bcr.bazel.build
--registry is repeatable and order-sensitive (first match wins), but
listing any --registry at all stops Bazel from consulting BCR
by default - hence the second line, so modules that aren't in this repo
still resolve normally.
Then in your MODULE.bazel:
bazel_dep(name = "rules_terraform", version = "0.1.0")registry/ only changes through pull requests - each one that changes a
module adds that module's new version to it (see Releasing below), and
release.yml publishes the version's release archive as soon as it merges.
bazel-rules/
<module>/ # a bzlmod module: MODULE.bazel names + versions it
MODULE.bazel
...
examples/ # (optional) a separate bzlmod module consuming
# <module> via bazel_dep + local_path_override,
# used both as a worked example and as this
# module's own test suite
.bcr/
config.yml # moduleRoots - which top-level dirs are BCR modules
<module>/ # BCR release templates for that module, see below
metadata.template.json
presubmit.yml
source.template.json
registry/ # self-hosted module registry, see above - updated
# in pull requests by .github/scripts/registry.nu
bazel_registry.json
modules/
rules_<module>/
metadata.json
<version>/
MODULE.bazel
source.json
.github/
MODULE.bazel # Bazel workspace for the scripts below (run with
# `cd .github && bazel run //scripts:<name>`)
scripts/
registry.nu # adds a version to registry/, checks pull
# requests do, and builds release archives -
# see Releasing below
workflows/
ci.yml # bazel test on every module + its examples/
registry.yml # on pull requests: `registry check`
release.yml # on every merge to main: releases each module
# version in registry/ that isn't released yet
Each module is versioned and released independently: version X.Y.Z of
<module> is the GitHub Release tagged <module>-vX.Y.Z (e.g.
terraform-v0.1.0), with that module's release archive,
rules_<module>-<module>-vX.Y.Z.tar, attached. There's no separate release
step - a pull request that changes a module also adds its new version to
registry/, and merging it releases that version:
-
Give your pull request a Conventional Commit title - with squash merges, it's the commit message that lands on
main, and it decides every changed module's new version:Title Version bump feat(opa)!: ...(any type with!)major - minor while the module's still 0.xfeat(opa): ...minor fix(opa): ..., or any other typepatch - the module's released contents still changed It applies to every module the pull request changes - split it up if they need different bumps.
-
Stage your changes (
git add), then run:$ cd .github && bazel run //scripts:registry -- bumpFor every module whose released contents changed, that sets
module(version = ...)in itsMODULE.bazelto the version the title implies, writesregistry/modules/rules_<module>/<version>/(itsMODULE.bazelandsource.json) and adds the version tometadata.json(created from.bcr/<module>/metadata.template.jsonthe first time a module is released), and stages all of it - commit it alongside your change. It reads the title from the open pull request (viagh), or the branch's only commit if there's no pull request yet - otherwise, pass--title '...'. Re-run it whenever you change a module again or retitle the pull request; it replaces what it wrote before.CI's
version-bumpsjob (registry check, in.github/workflows/registry.yml- which also re-runs when the title is edited) fails the pull request unless the title is a Conventional Commit, and every module whose released contents changed has exactly the version it implies and an up-to-dateregistry/entry. Changes to only a module'sexamples/, its*.mddocs or itsMODULE.bazel.lockdon't need a new version. -
Once it merges,
.github/workflows/release.ymlreleases every module version inregistry/that doesn't have a GitHub Release yet: it rebuilds the module's release archive, checks it matches theregistry/entry's integrity hash, and creates the tag and GitHub Release with it attached.
This never pushes to main, so it works with branch protection - every
change to registry/ is reviewed in a pull request like any other.
The registry entry's integrity hash can be computed before the archive is
published because the archive is built reproducibly - from what's staged in
git (not the working tree), with sorted entries and fixed timestamps,
ownership and permissions, as an uncompressed .tar (a compressor can produce
different bytes for the same input on different machines) - so the same
change always gives byte-for-byte the same archive, wherever it's built.
Between a merge and release.yml finishing, the new version is in registry/
but its archive isn't downloadable yet - usually well under a minute. If
release.yml fails part-way, re-run it (it also has a manual
workflow_dispatch trigger) - it only ever releases what isn't released yet.
Independently of the above, once bazel-contrib/publish-to-bcr
is installed on this repo (a one-time GitHub App install), the same GitHub
Release triggers a pull request against the Bazel Central Registry using the
.bcr/<module>/*.template.json/presubmit.yml templates here.
A new top-level directory with its own MODULE.bazel, plus a matching entry
in .bcr/config.yml's moduleRoots and its own .bcr/<new-module>/
template directory (registry/ needs no new wiring - registry.nu/
release.yml already handle any module directory generically; its first
pull request adds its first version, as for any other change).