Skip to content

BCR: automate publishing SafeDI to the Bazel Central Registry#287

Open
dfed wants to merge 3 commits intomainfrom
claude/bcr-automation
Open

BCR: automate publishing SafeDI to the Bazel Central Registry#287
dfed wants to merge 3 commits intomainfrom
claude/bcr-automation

Conversation

@dfed
Copy link
Copy Markdown
Owner

@dfed dfed commented Apr 23, 2026

Summary

Ties SafeDI's release flow into the Bazel Central Registry via bazel-contrib/publish-to-bcr. Every GitHub release (beta or stable) now files a PR to BCR adding a new version entry.

What's in it

.bcr/ templates

  • metadata.template.json — module metadata (homepage, maintainer, repo).
  • source.template.json — points at a release-asset source tarball (releases/download/{TAG}/{REPO}-{VERSION}.tar.gz). BCR flags GitHub's auto-generated archive/... URLs as unstable, so we upload a reproducible tarball ourselves.
  • presubmit.yml — two validation stages:
    • verify_safedi_build — builds the four SafeDI targets (@safedi//Sources/{SafeDI,SafeDICore,SafeDIMacros,SafeDITool}).
    • bcr_test_module — points BCR at Examples/ExampleBazelIntegration for a downstream smoke test. The example's local_path_override is ignored when not root, so BCR resolves safedi from the registry and proves the consumer path works.
  • config.yml — intentionally empty.

.github/workflows/publish-to-bcr.yml

  • Triggers on release.published (every release, including betas) plus a workflow_dispatch retry hatch.
  • Delegates to bazel-contrib/publish-to-bcr/.github/workflows/publish.yaml@v1.2.0.
  • registry_fork: dfed/bazel-central-registry, auth via BCR_PUBLISH_PAT classic PAT.
  • attest: false because our release is produced by the custom publish.yml, not bazel-contrib's release_ruleset — there are no upstream source-artifact attestations to chain.

Source tarball + version stamping

  • .github/workflows/publish.yml — produces SafeDI-<version>.tar.gz via git archive --prefix=SafeDI-<version>/ from the just-created tag and uploads it as a release asset. Stable URL for BCR's releases/download/... reference.
  • Scripts/update-version.sh — now also stamps MODULE.bazel's top-level module(version = "…"). Scoped to the module(…) block via sed's address-range so bazel_dep version lines elsewhere stay put.
  • .github/workflows/publish.yml — adds MODULE.bazel to the commit step and shows its diff.
  • .github/workflows/ci.ymlupdate-version-check job verifies the stamping end-to-end via the matching awk-range idiom.

Release flow end-to-end

  1. Operator runs the existing Publish workflow with a version.
  2. Build jobs produce SafeDITool binaries → assemble-and-publish bundles + checksums + stamps Package.swift / Plugins/Shared.swift / MODULE.bazel + commits + tags + creates the GitHub release with the source tarball + tool binaries attached.
  3. release.published event fires publish-to-bcr.yml → reusable workflow reads .bcr/, generates the entry, pushes to dfed/bazel-central-registry, opens a PR upstream.
  4. BCR presubmit validates + maintainers merge → downstream Bazel users can bazel_dep(name = "safedi", version = "X").

Not in it (blocked on first BCR merge)

  • README / Manual section for Bazel consumption — writing "install via bazel_dep(name = "safedi", ...)" is premature until the BCR entry actually exists. Add in a follow-up PR once the first submission lands.

Test plan

  • ./Scripts/update-version.sh 99.99.99-test abc123… stamps all three files correctly, verified locally.
  • update-version-check CI step catches the MODULE.bazel stamp via awk '/^module\(/,/^\)/' ... | grep -q ....
  • git archive --prefix=SafeDI-test/ -o /tmp/test.tar.gz HEAD produces the right layout (verified locally).
  • First publish-to-bcr run on next release.
  • BCR presubmit passes (validated by BCR maintainers when reviewing our first submission PR).

🤖 Generated with Claude Code

@dfed
Copy link
Copy Markdown
Owner Author

dfed commented Apr 23, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2812886239

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/publish-to-bcr.yml Outdated
with:
tag_name: ${{ github.event.release.tag_name || inputs.tag_name }}
registry_fork: dfed/bazel-central-registry
attest: true
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Disable attestations for this release pipeline

Set attest to false here unless the release job is migrated to bazel-contrib’s release_ruleset workflow. This repository’s release is produced by .github/workflows/publish.yml (custom gh release create flow), and publish-to-bcr documents that source archive attestations from other release flows are rejected by BCR; keeping attest: true will cause the generated BCR submission to fail validation instead of publishing successfully.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — fixed in f0739aa. Our release workflow is the custom .github/workflows/publish.yml, not bazel-contrib/.github's release_ruleset.yaml, so there are no upstream source-artifact attestations for publish-to-bcr to chain. Set attest: false and dropped the id-token: write / attestations: write permissions that only existed for that path. If we ever migrate the release job, flipping back to true is a one-line change.

dfed added a commit that referenced this pull request Apr 23, 2026
Addresses codex P1 on PR #287. The publish-to-bcr reusable workflow
only produces meaningful attestations when the release itself is
produced by bazel-contrib/.github's `release_ruleset.yaml`, because
it reads source-artifact attestations from the release to chain them
onto the generated BCR entry. SafeDI's release is produced by our
custom `.github/workflows/publish.yml` — there are no upstream
attestations to extend, so `attest: true` would either fail to find
them or produce meaningless ones.

Drop the `id-token: write` / `attestations: write` permissions that
only existed for the attestation path.

If we ever migrate the release job to `release_ruleset.yaml` (tracked
as a separate concern), flip this back to `true`.
@dfed
Copy link
Copy Markdown
Owner Author

dfed commented Apr 23, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f0739aa75c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .bcr/source.template.json Outdated
{
"integrity": "",
"strip_prefix": "{REPO}-{VERSION}",
"url": "https://github.com/{OWNER}/{REPO}/archive/refs/tags/{TAG}.tar.gz"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Switch source template to a stable release asset URL

BCR presubmit treats GitHub archive/... URLs as unstable and fails validation for them, so publishing with this template will produce PRs that block unless a maintainer manually adds @bazel-io skip_check unstable_url each time. Since this workflow is meant to automate BCR submissions on every release, using archive/refs/tags/{TAG}.tar.gz here effectively breaks the automation path; use a stable /releases/download/... source archive URL instead.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 1302650.bcr/source.template.json now points at releases/download/{TAG}/{REPO}-{VERSION}.tar.gz, and .github/workflows/publish.yml produces that tarball via git archive --prefix=SafeDI-<version>/ and uploads it as a release asset alongside the existing SafeDITool binaries. Verified locally that the tarball layout matches the strip_prefix in the template.

dfed added a commit that referenced this pull request Apr 24, 2026
Addresses codex P1 on PR #287. BCR presubmit flags GitHub's auto-
generated `archive/refs/tags/{TAG}.tar.gz` URLs as unstable — the
archive contents are considered subject to change (GitHub's tarball
format has been tweaked historically), so submissions using them
fail validation unless a maintainer adds `@bazel-io skip_check
unstable_url` to the BCR PR. For a fully-automated BCR pipeline that
defeats the point.

Switch to uploading a reproducible source tarball as a release
asset. `git archive --prefix=SafeDI-<version>/` from the just-
created tag produces the same bytes on every invocation, and the
release-asset URL
`releases/download/<tag>/SafeDI-<version>.tar.gz` is stable by
BCR's definition. Update `.bcr/source.template.json` to reference
that URL instead.

The `strip_prefix` (`{REPO}-{VERSION}`) already matches
`--prefix=SafeDI-<version>/` in the `git archive` call.
@dfed
Copy link
Copy Markdown
Owner Author

dfed commented Apr 24, 2026

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Can't wait for the next one!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 24, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (2842816) to head (1302650).

Additional details and impacted files

Impacted file tree graph

@@                          Coverage Diff                          @@
##           claude/bazel-example-project-d6f783      #287   +/-   ##
=====================================================================
  Coverage                               100.00%   100.00%           
=====================================================================
  Files                                       41        41           
  Lines                                     7043      7043           
=====================================================================
  Hits                                      7043      7043           
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@dfed dfed force-pushed the claude/bazel-example-project-d6f783 branch from 7547902 to 5136cd9 Compare May 2, 2026 05:23
Base automatically changed from claude/bazel-example-project-d6f783 to main May 2, 2026 14:53
dfed and others added 3 commits May 2, 2026 07:54
Ties SafeDI's release flow into the Bazel Central Registry via
bazel-contrib/publish-to-bcr. Every GitHub release (including
betas) now files a PR to BCR adding a new version entry.

## What's in it

### `.bcr/` — Publish-to-BCR templates

- `metadata.template.json` — module-level metadata (homepage,
  maintainer, repository). Copied to `modules/safedi/metadata.json` on
  first submission, updated per-version thereafter.
- `source.template.json` — points at GitHub's auto-generated source
  tarball (`archive/refs/tags/{TAG}.tar.gz`) with `strip_prefix =
  "{REPO}-{VERSION}"`. No need to upload a separate source artifact —
  GitHub's tag tarball is reproducible and suffices.
- `presubmit.yml` — BCR validation config. Two stages:
  - `verify_safedi_build` — builds the four SafeDI targets against
    the archive (`@safedi//Sources/SafeDI`, `//SafeDICore`,
    `//SafeDIMacros`, `//SafeDITool`).
  - `bcr_test_module` — points at `Examples/ExampleBazelIntegration`
    for a downstream smoke test. BCR `cd`s into the example's own
    MODULE.bazel workspace, resolves SafeDI via the registry (not the
    local_path_override that only applies when the example is root),
    and builds the two `swift_library` targets. Catches regressions
    in the consumer path that our own build can't.
- `config.yml` — intentionally empty (all config lives in the other
  three files).

### `.github/workflows/publish-to-bcr.yml`

- Triggers on `release.published` — every GitHub release fires the
  workflow, beta or stable. A `workflow_dispatch` escape hatch lets
  us replay a publish after fixing a template bug without cutting a
  new release.
- Delegates to `bazel-contrib/publish-to-bcr/.github/workflows/publish.yaml@v1.2.0`
  (the canonical reusable workflow).
- `registry_fork: dfed/bazel-central-registry` — push branches there
  before filing the PR upstream.
- Authed via the `BCR_PUBLISH_PAT` secret (classic PAT; fine-grained
  PATs can't open PRs against public repos per GitHub's roadmap).

### Version stamping

`Scripts/update-version.sh` now also stamps `MODULE.bazel`'s top-level
`module(version = "…")` — scoped to the `module(…)` block so
`bazel_dep(…, version = "X")` lines elsewhere in the file stay put.
`.github/workflows/publish.yml`'s commit step adds `MODULE.bazel` to
`git add`, and the `Show changes` step prints its diff alongside
Package.swift / Plugins/Shared.swift.

`.github/workflows/ci.yml`'s `update-version-check` job gains a
fourth verification: `awk '/^module\(/,/^\)/' MODULE.bazel | grep -q
'version = "99.99.99-test"'`. Same awk range idiom we use in the
sed — scopes the match so a future `bazel_dep` version can't
false-positive.

## Release flow end-to-end

1. Operator runs the existing `Publish` workflow with a version.
2. `build-*` jobs produce the SafeDITool binaries; `assemble-and-
   publish` bundles them into an artifact bundle, computes its
   checksum, stamps Package.swift + Plugins/Shared.swift +
   MODULE.bazel via `update-version.sh`, commits + tags + creates
   the GitHub release.
3. GitHub fires `release.published`; `publish-to-bcr.yml` runs; the
   reusable workflow clones the tagged source archive, reads
   `.bcr/`, generates the BCR entry, pushes to
   `dfed/bazel-central-registry`, and opens a PR against
   `bazelbuild/bazel-central-registry`.
4. BCR maintainers + presubmit validate the entry; once merged,
   downstream consumers can `bazel_dep(name = "safedi",
   version = "X")`.

## Not in it (future)

- README / Manual section pointing Bazel users at the BCR module
  (blocked on BCR accepting the first submission).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses codex P1 on PR #287. The publish-to-bcr reusable workflow
only produces meaningful attestations when the release itself is
produced by bazel-contrib/.github's `release_ruleset.yaml`, because
it reads source-artifact attestations from the release to chain them
onto the generated BCR entry. SafeDI's release is produced by our
custom `.github/workflows/publish.yml` — there are no upstream
attestations to extend, so `attest: true` would either fail to find
them or produce meaningless ones.

Drop the `id-token: write` / `attestations: write` permissions that
only existed for the attestation path.

If we ever migrate the release job to `release_ruleset.yaml` (tracked
as a separate concern), flip this back to `true`.
Addresses codex P1 on PR #287. BCR presubmit flags GitHub's auto-
generated `archive/refs/tags/{TAG}.tar.gz` URLs as unstable — the
archive contents are considered subject to change (GitHub's tarball
format has been tweaked historically), so submissions using them
fail validation unless a maintainer adds `@bazel-io skip_check
unstable_url` to the BCR PR. For a fully-automated BCR pipeline that
defeats the point.

Switch to uploading a reproducible source tarball as a release
asset. `git archive --prefix=SafeDI-<version>/` from the just-
created tag produces the same bytes on every invocation, and the
release-asset URL
`releases/download/<tag>/SafeDI-<version>.tar.gz` is stable by
BCR's definition. Update `.bcr/source.template.json` to reference
that URL instead.

The `strip_prefix` (`{REPO}-{VERSION}`) already matches
`--prefix=SafeDI-<version>/` in the `git archive` call.
@dfed dfed force-pushed the claude/bcr-automation branch from 1302650 to 394ea86 Compare May 2, 2026 14:55
@dfed dfed marked this pull request as ready for review May 2, 2026 14:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant