feat: update bazel central registry publish workflow (#23465)

Includes pre-built protoc integrity hashes in the release artifact, in a new file `bazel/private/prebuilt_tool_integrity.bzl`

tested on my fork of protobuf: https://github.com/alexeagle/protobuf/releases/tag/v0.1000.10

Next steps:
- create toolchain definitions that fetch the prebuilt protoc from github releases
- change logic in Bazel setup to use the prebuilt toolchain when desired
- create a token from @protobuf-team-bot account and add to GHA secrets so the pull request to BCR opens

Closes #23465

COPYBARA_INTEGRATE_REVIEW=https://github.com/protocolbuffers/protobuf/pull/23465 from protocolbuffers:alexeagle/bazel-protoc-prebuilt 05314db026
PiperOrigin-RevId: 817227717
This commit is contained in:
Alex Eagle 2025-10-09 09:49:23 -07:00 committed by Copybara-Service
parent cbedc741fe
commit 7051cc12d9
6 changed files with 147 additions and 1 deletions

View file

@ -1,5 +1,5 @@
{
"integrity": "**leave this alone**",
"strip_prefix": "{REPO}-{VERSION}",
"url": "https://github.com/{OWNER}/{REPO}/releases/download/{TAG}/{REPO}-{VERSION}.zip"
"url": "https://github.com/{OWNER}/{REPO}/releases/download/{TAG}/{REPO}-{VERSION}.tar.gz"
}

4
.gitattributes vendored Normal file
View file

@ -0,0 +1,4 @@
# Configuration for 'git archive'
# see https://git-scm.com/docs/git-archive/2.40.0#ATTRIBUTES
# Don't include compatibility folder in the distribution artifact, just to reduce size
compatibility/ export-ignore

36
.github/workflows/publish_to_bcr.yaml vendored Normal file
View file

@ -0,0 +1,36 @@
# Publish new releases to Bazel Central Registry.
name: Publish to BCR
on:
# Run the publish workflow after a successful release
# Will be triggered from the release.yaml workflow
workflow_call:
inputs:
tag_name:
required: true
type: string
secrets:
# This token should be owned by https://github.com/protobuf-team-bot
BCR_PUBLISH_TOKEN:
required: true
# In case of problems, let release engineers retry by manually dispatching
# the workflow from the GitHub UI
workflow_dispatch:
inputs:
tag_name:
description: git tag being released
required: true
type: string
jobs:
publish:
uses: bazel-contrib/publish-to-bcr/.github/workflows/publish.yaml@v1.0.0
with:
tag_name: ${{ inputs.tag_name }}
# GitHub repository which is a fork of the upstream where the Pull Request will be opened.
registry_fork: protocolbuffers/bazel-central-registry
permissions:
attestations: write
contents: write
id-token: write
secrets:
# Necessary to push to the BCR fork, and to open a pull request against a registry
publish_token: ${{ secrets.BCR_PUBLISH_TOKEN }}

View file

@ -0,0 +1,33 @@
# Prepare a release specifically for Bazel users, including a pre-built protoc.
name: Bazel Release
on:
# Can be triggered from the GitHub Actions ui, using the "Run workflow" button on
# https://github.com/protocolbuffers/protobuf/actions/workflows/release_bazel_module.yaml
# TODO: consider automating the trigger from a new release being created
workflow_dispatch:
inputs:
tag_name:
description: git tag that has the protoc release artifact
required: true
type: string
permissions:
id-token: write
attestations: write
contents: write
jobs:
release:
uses: bazel-contrib/.github/.github/workflows/release_ruleset.yaml@v7.2.3
with:
release_files: protobuf-*.tar.gz
prerelease: false
tag_name: ${{ inputs.tag_name }}
# The release was already created by Google-internal mechanism,
# so there aren't any useful assertions to make here.
bazel_test_command: 'true'
publish:
needs: release
uses: ./.github/workflows/publish_to_bcr.yaml
with:
tag_name: ${{ inputs.tag_name }}
secrets:
BCR_PUBLISH_TOKEN: ${{ secrets.BCR_PUBLISH_TOKEN }}

53
.github/workflows/release_prep.sh vendored Executable file
View file

@ -0,0 +1,53 @@
#!/usr/bin/env bash
# NB: this file must be named release_prep.sh because the attestation generation doesn't trust user control.
# see https://github.com/bazel-contrib/.github/blob/v7.2.3/.github/workflows/release_ruleset.yaml#L33-L45
set -o errexit -o nounset -o pipefail
# Argument provided by reusable workflow caller, see
# https://github.com/bazel-contrib/.github/blob/v7.2.3/.github/workflows/release_ruleset.yaml#L104
TAG=$1
PREFIX="protobuf-${TAG:1}"
ARCHIVE="$PREFIX.tar.gz"
ARCHIVE_TMP=$(mktemp)
INTEGRITY_FILE=${PREFIX}/bazel/private/prebuilt_tool_integrity.bzl
# NB: configuration for 'git archive' is in /.gitattributes
git archive --format=tar --prefix=${PREFIX}/ ${TAG} > $ARCHIVE_TMP
############
# Patch up the archive to have integrity hashes for built binaries that we downloaded in the GHA workflow.
# Now that we've run `git archive` we are free to pollute the working directory.
# Delete the placeholder file
tar --file $ARCHIVE_TMP --delete $INTEGRITY_FILE
# Use jq to translate GitHub Releases json into a Starlark object
filter_releases=$(cat <<'EOF'
# Read the file assets already present on the release
reduce .assets[] as $a (
# Start with an empty dictionary, and for each asset, add
{}; . + {
# The format required in starlark, i.e. "release-name": "deadbeef123"
($a.name): ($a.digest | sub("^sha256:"; ""))
}
)
EOF
)
mkdir -p ${PREFIX}/bazel/private
cat >${INTEGRITY_FILE} <<EOF
"Generated during release by release_prep.sh"
RELEASED_BINARY_INTEGRITY = $(
curl -s https://api.github.com/repos/protocolbuffers/protobuf/releases/tags/${TAG} \
| jq -f <(echo "$filter_releases")
)
EOF
# Append that generated file back into the archive
tar --file $ARCHIVE_TMP --append ${INTEGRITY_FILE}
# END patch up the archive
############
gzip < $ARCHIVE_TMP > $ARCHIVE
SHA=$(shasum -a 256 $ARCHIVE | awk '{print $1}')

View file

@ -0,0 +1,20 @@
"""Release binary integrity hashes.
This file contents are entirely replaced during release publishing, by .github/workflows/release_prep.sh
so that the integrity of the prebuilt tools is included in the release artifact.
The checked in content is only here to allow load() statements in the sources to resolve.
"""
# Create a mapping for every tool name to the hash of /dev/null
NULLSHA = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
RELEASED_BINARY_INTEGRITY = {
"-".join([
"protoc",
os,
arch,
]): NULLSHA
for [os, arch] in {
"linux": ["aarch_64", "x86_64"],
}
}