4.11.4 Publishing Rulesets

recommended

Publishing a ruleset is the handoff from "this tag is ready" to "downstream users can depend on this module by name and version." The work is not just uploading a tarball. A published ruleset needs a stable release artifact, registry metadata, integrity data, smoke tests, and user-facing installation instructions that match the artifact users will actually fetch.1

An accepted tag becomes verified artifacts and registry metadata
CI first accepts the exact tag. The release job then turns that tested source into verifiable downloads and immutable registry data.
CI tag gate
Test the exact source
publishing prerequisite
v1.2.3

Pinned commit passes tests, examples, docs, and compatibility lanes

Release artifacts
Artifacts carry the bytes
stable downloads
source.tar.gz

Archive users fetch

binaries / docs

Optional versioned companions

Verify artifacts
Prove what users will fetch
after packaging, before BCR
archive layout

Expected strip prefix and files

integrity

Hashes match the published bytes

consumer test

A downstream module fetches and uses the archive

Registry metadata
BCR records the module
reviewed metadata
source.json

URL, strip prefix, integrity

MODULE.bazel

Name, version, deps

presubmit.yml

Downstream-style validation

Downstream user
Users depend by name
verified fetch
bazel_dep()

Module version selected

fetch + verify

Integrity checked before use

pre-publish gate
CI passes before artifacts are published

The release job proves the exact tag-and-artifact path, not just the development branch.

BCR handoff
Registry data is reviewed and immutable

Treat the BCR PR as finalization, not a scratchpad for discovering the dependency shape.

Publish the tag, attach verifiable artifacts, submit reviewed BCR metadata, and let users fetch with bazel_dep().

The boundary matters. 4.11.3 Module Metadata & Version Semantics decides the module name, version semantics, and published dependency metadata such as bazel_dep() constraints and dev_dependency boundaries. 4.11.5 Ruleset CI/CD Patterns decides whether a commit or tag is safe to release. Publishing starts after that gate: it turns the accepted tag into artifacts and BCR data.

The Release Artifact Is the Contract

Most users do not consume your working tree. They consume a module version whose registry entry points at an archive. In Bazel's registry model, each module version has a MODULE.bazel file and a source.json file. For the common archive-backed case, source.json tells Bazel which URL to download, what Subresource Integrity checksum to verify, and which prefix to strip after extraction.2

That makes the release archive a public byte contract. If the archive contains stale files, missing generated metadata, a different directory prefix, or checksums that do not match the registry entry, users see a dependency-resolution problem before they ever reach your rule APIs. If the archive is patched in BCR, the patch and overlay files need their own integrity data as well.2

For GitHub-hosted rulesets, prefer a versioned archive uploaded to the release page over an on-demand source archive URL. The migration guide calls out that GitHub does not guarantee the checksum of generated source archives, while BCR needs a stable URL whose bytes will keep matching the recorded hash.3

A robust ruleset release pattern treats the tag as the source of truth. A tag-triggered workflow runs tests, prepares a release archive with git archive, produces release notes, publishes GitHub release artifacts, and then relies on Publish to BCR to mirror the module version into the Bazel Central Registry.4 The important property is not GitHub specifically. It is that the artifact is derived from the tagged sources by automation, not assembled manually on a maintainer laptop.

{
  "url": "https://github.com/org/rules_foo/releases/download/v1.2.3/rules_foo-v1.2.3.tar.gz",
  "integrity": "sha256-...",
  "strip_prefix": "rules_foo-v1.2.3"
}

This is the shape to design around: a versioned source archive plus registry metadata that lets Bazel fetch and verify it deterministically.

Keep Source Checkouts and Releases Both Usable

Ruleset authors often need two modes to work:

  • A released module, where users download prebuilt helper tools or release-ready generated metadata.
  • A source checkout or git_override, where contributors and early adopters build or generate those same pieces from source.

One proven approach stamps a version file into the release archive. When the ruleset sees a real release version, it registers toolchains that download released binaries. When it sees the source-archive sentinel version, it registers toolchains that build the tools from source instead.4 That same release path patches the BCR MODULE.bazel so Go toolchain and library dependencies become dev_dependency entries for release consumers, avoiding unnecessary dependency exposure through MVS.4

This pattern builds directly on 4.11.2 Toolchainization: the ruleset should expose a toolchain contract, not leak its implementation language to every downstream workspace.

Integrity Gets Harder with Platform Binaries

If a ruleset ships helper binaries, one archive is no longer the whole story. You also need integrity data for each platform-specific binary that a repository rule may download later. Go makes this comparatively easy because cross-compilation can produce the release matrix from one environment, so those binary hashes can be checked into the source tree to keep every commit releasable.5

Rust makes the same release model harder. When a project cannot reliably build every platform binary from one machine, the tag-triggered workflow builds binaries on the relevant CI runners, collects the artifacts, and injects the resulting hashes into the release tarball after git archive has stamped the version.5 The produced ruleset still presents normal Bazel toolchains to users: Bazel selects the toolchains it needs, and the repository rules download only the binaries for the resolved execution or target platforms.5

The rule-author lesson is simple: do not design publishing around the language you happen to use today. Design around the artifacts your users need, the platforms those artifacts support, and the integrity data required for each download.

BCR Publication Is a Reviewable Registry Change

BCR is an index registry backed by the bazelbuild/bazel-central-registry GitHub repository. In addition to the normal registry files, BCR requires a presubmit.yml for each module version so its CI can validate that the module is usable and interoperates with other modules.2

For manual BCR work, the contribution flow is: clone the registry, run the add_module tool with the module name, version, source archive URL, patch/overlay choices, module file input, and test setup, then run the generated tests locally before opening the PR.6 A practical lesson from a large BCR migration at Canva is to avoid starting inside the registry: first make the dependency work with a non-registry override in a real Bazel project, and only then turn that working shape into a BCR entry.6

That advice exists because registry entries are meant to be immutable. Bazel caches registry files in memory, so mutating already-consumed registry data can produce confusing warm-CI behavior. BCR publication should therefore be the finalization step, not the scratchpad.6 If you are still discovering patches, overlays, or missing dependencies, stay in overrides until the shape settles. Registry structure details belong back in 3.1.8 Registry Structure & Custom Registries. For publishing, the key habit is to treat the submitted version as permanent.

Automate the BCR Handoff

Publish to BCR exists to remove the repetitive parts of that registry PR. Used after publishing GitHub release artifacts, it lets pushing a tag drive the release artifact, release notes, and BCR contribution path in one maintained flow.4 The official rules guidance also points new rulesets at bazel-contrib/rules-template, which includes API documentation generation and CI/CD wiring for distribution.1

The maintained reusable workflow makes the handoff boundary concrete: it accepts an already-created tag and release inputs, renders the repository's .bcr templates, pushes the generated entry to a registry fork, and opens the registry pull request.7 Its templates_ref input can retry corrected registry templates without changing the tagged release bytes. That is a recovery path for publication metadata, not permission to replace an immutable artifact.

Automation does not remove the need for good release inputs. It makes the inputs more important:

  • The tag must already be the version you intend to publish.
  • The release archive must have the same structure users get from the registry.
  • source.json must point at a stable artifact and include correct integrity data.
  • presubmit.yml should exercise the small downstream-style targets that prove the module works.
  • The release notes should include the copy-paste bazel_dep(name = "...", version = "...") snippet users need in MODULE.bazel.1

The mini-ruleset keeps these pieces inspectable without publishing anything: its release-plan script prints the tag-to-BCR checklist, while the BCR templates show the source.json and presubmit.yml handoff.

This is why release jobs often rerun the same test suite even when the tagged commit was green on main: the publishing job is proving the exact tag-and-artifact path, not just the commit's development workflow.4

Documentation Can Be a Release Artifact Too

Publishing is not limited to source archives and helper binaries. BCR can now surface Starlark API documentation when a ruleset publishes a docs archive and adds docs_url to source.template.json for Publish to BCR.8 A typical workflow queries starlark_doc_extract targets, builds them, packs the resulting binaryproto docs into a .docs.tar.gz, and lets normal BCR publication carry the docs URL.8

That changes the release contract for documented rulesets: docs generated from public .bzl APIs are no longer just a GitHub Pages or checked-in Markdown concern. They can travel with the module version, next to the registry entry users already visit. 4.5.4 Stardoc — API Documentation explains how to produce useful API content. Publishing makes that generated content available with the release.

Add Provenance When the Artifact Matters

Integrity hashes prove bytes did not change between the registry entry and the download. They do not, by themselves, prove that the bytes were produced from the reviewed source and workflow. Optional SLSA-style attestations can be added to ruleset release artifacts, and BCR presubmit can verify those attestations for modules that publish them.9

This is an advanced publishing concern, but it is still part of the same mental model: once your ruleset downloads tools and runs them inside downstream builds, the release artifact becomes part of the user's supply chain. A mature publishing pipeline should make the artifact reproducible enough to trust, traceable enough to audit, and boring enough that releases do not depend on one maintainer remembering a manual checklist.

key takeaway

Publish from a tag, publish artifacts that Bazel can verify, and publish registry metadata that reflects the artifact users will actually fetch. The happy path is a tag-triggered workflow: test the tag, prepare the archive, attach any platform binaries or docs, generate correct integrity data, and hand the module version to BCR automation.

Do not use BCR as the place where a dependency shape is invented. Prove it with overrides and release-style tests first. Then submit the immutable registry version.

Check your understanding · 4 questions

1.Which source.json shape gives a BCR entry verifiable source input for a ruleset release?

Select one answer

2.Before opening a BCR publication PR, what should maintainers do first?

Select one answer

3.Which files or data are part of a typical BCR publication contract?

Select all that apply

4.True or false: publishing workflow habits.

Choose True or False for each sentence

A release archive should be derived from the tagged sources by automation.
BCR is a good place to experiment with dependency patches before they work locally.
Platform-specific helper binaries need integrity data for each downloadable artifact.
Checksums prove that an artifact was produced by a trusted workflow.
0 of 4 answered

Footnotes

  1. Deploying Rules — rules-template recommendation, ruleset repository conventions, release snippet, and CI/CD guidance 1 2 3

  2. Bazel registries — index registry structure, source.json, integrity fields, BCR, and presubmit.yml 1 2 3

  3. Bzlmod Migration Guide — BCR publishing guidance on stable, versioned source archives and GitHub-generated archive checksum risks

  4. Releasing Bazel rulesets that publish tools — tag-triggered release workflow, git archive, integrity files, Publish to BCR, and BCR patching of dev dependencies 1 2 3 4 5

  5. Publishing Bazel rules that depend on tools: take 2 — per-platform binary release workflow, hash injection into the release tarball, and toolchain-selected downloads 1 2 3

  6. A Product-First Approach to Growing BCR - Jordan Mele, Canva — BCR contribution flow, local testing, override-first iteration, and registry immutability lesson 1 2 3

  7. Publish to BCR repository map — the supported reusable workflow defines the tag, release, template, fork, token, draft-PR, and templates_ref handoff.

  8. Bazel Starlark Docs on the Registry — publishing generated Stardoc data through BCR docs_url 1 2

  9. Securing Bazel's Module Registry — provenance attestations for BCR module release artifacts and presubmit verification