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
4 changes: 4 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
build --java_language_version=21
build --java_runtime_version=21
build --tool_java_language_version=21
build --tool_java_runtime_version=21
1 change: 1 addition & 0 deletions .bazelversion
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
8.4.1
14 changes: 14 additions & 0 deletions .github/workflows/ci.bazelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Debug where options came from
build --announce_rc

# Don't rely on test logs being easily accessible from the test runner,
# though it makes the log noisier.
test --test_output=errors

# This directory is configured in GitHub actions to be persisted between runs.
build --disk_cache=$HOME/.cache/bazel
build --repository_cache=$HOME/.cache/bazel-repo
build --repo_contents_cache=

# Allows tests to run bazelisk-in-bazel, since this is the cache folder used
test --test_env=XDG_CACHE_HOME
34 changes: 34 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: CI

# Controls when the action will run.
on:
push:
branches: [master]
pull_request:
workflow_dispatch:

concurrency:
# Cancel previous actions from the same PR: https://stackoverflow.com/a/72408109
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
test:
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
- uses: bazel-contrib/setup-bazel@0.15.0
with:
# Avoid downloading Bazel every time.
bazelisk-cache: true
# Store build cache per workflow.
disk-cache: true
# Share repository cache between workflows.
repository-cache: true
- name: bazel build
run: >-
bazelisk
--bazelrc=.github/workflows/ci.bazelrc
--bazelrc=.bazelrc
build
...
36 changes: 36 additions & 0 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
@@ -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:
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@v0.2.3
with:
draft: false
tag_name: ${{ inputs.tag_name }}
# GitHub repository which is a fork of the upstream where the Pull Request will be opened.
registry_fork: stackb/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.publish_token || secrets.BCR_PUBLISH_TOKEN }}
36 changes: 36 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Cut a release whenever a new tag is pushed to the repo.
name: Release
on:
# Can be triggered from the tag.yaml workflow
workflow_call:
inputs:
tag_name:
required: true
type: string
secrets:
publish_token:
required: true
# Or, developers can manually push a tag from their clone
push:
tags:
- "v*.*.*"
permissions:
id-token: write
attestations: write
contents: write
jobs:
release:
uses: bazel-contrib/.github/.github/workflows/release_ruleset.yaml@v7.2.3
# uses: ./.github/workflows/release_ruleset.yaml # copied-from: bazel-contrib/.github/.github/workflows/release_ruleset.yaml@v7.2.3
with:
prerelease: false
release_files: ui.js-*.tar.gz
tag_name: ${{ inputs.tag_name || github.ref_name }}
secrets: inherit
publish:
needs: release
uses: ./.github/workflows/publish.yaml
with:
tag_name: ${{ inputs.tag_name || github.ref_name }}
secrets:
publish_token: ${{ secrets.publish_token || secrets.BCR_PUBLISH_TOKEN }}
28 changes: 28 additions & 0 deletions .github/workflows/release_prep.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

#!/usr/bin/env bash

set -o errexit -o nounset -o pipefail

# Set by GH actions, see
# https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables
readonly TAG=$1
# The prefix is chosen to match what GitHub generates for source archives.
# This guarantees that users can easily switch from a released artifact to a source archive
# with minimal differences in their code (e.g. strip_prefix remains the same)
readonly PREFIX="ui.js-${TAG}"
readonly ARCHIVE="${PREFIX}.tar.gz"

# NB: configuration for 'git archive' is in /.gitattributes
git archive --format=tar --prefix=${PREFIX}/ ${TAG} | gzip > $ARCHIVE
SHA=$(shasum -a 256 $ARCHIVE | awk '{print $1}')

# The stdout of this program will be used as the top of the release notes for this release.
cat << EOF
## Using bzlmod with Bazel 6 or later:

Add to your \`MODULE.bazel\` file:

\`\`\`starlark
bazel_dep(name = "stackb_ui_js", version = "${TAG}")
\`\`\`
EOF
208 changes: 208 additions & 0 deletions .github/workflows/release_ruleset.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
# Reusable workflow that can be referenced by repositories in their `.github/workflows/release.yaml`.
# See example usage in https://github.com/bazel-contrib/rules-template/blob/main/.github/workflows/release.yaml
#
# This workflow calls `.github/workflows/release_prep.sh` as the command to prepare the release.
# Release notes are expected to be outputted to stdout from the release prep command.
#
# This workflow uses https://github.com/bazel-contrib/setup-bazel to prepare the cache folders.
# Caching may be disabled by setting `mount_bazel_caches` to false.
#
# The workflow requires the following permissions to be set on the invoking job:
#
# permissions:
# id-token: write # Needed to attest provenance
# attestations: write # Needed to attest provenance
# contents: write # Needed to upload release files

permissions: {}

on:
# Make this workflow reusable, see
# https://github.blog/2022-02-10-using-reusable-workflows-github-actions
workflow_call:
inputs:
release_files:
required: true
description: |
Newline-delimited globs of paths to assets to upload for release.
relative to the module repository. The paths should include any files
such as a release archive created by the release_prep script`.

See https://github.com/softprops/action-gh-release#inputs.
type: string
# TODO: there's a security design problem here:
# Users of a workflow_dispatch trigger could fill in something via the GH Web UI
# that would cause the release to use an arbitrary script.
# That change wouldn't be reflected in the sources in the repo, and therefore
# would not be verifiable by the attestation.
# For now, we force this path to be hard-coded.
#
# release_prep_command:
# default: .github/workflows/release_prep.sh
# description: |
# Command to run to prepare the release and generate release notes.
# Release notes are expected to be outputted to stdout.
# type: string
bazel_test_command:
default: "bazel build //..."
description: |
Bazel test command that may be overridden to set custom flags and targets.
The --disk_cache=~/.cache/bazel-disk-cache --repository_cache=~/.cache/bazel-repository-cache flags are
automatically appended to the command.
type: string
mount_bazel_caches:
default: true
description: |
Whether to enable caching in the bazel-contrib/setup-bazel action.
type: boolean
prerelease:
default: true
description: Indicator of whether or not this is a prerelease.
type: boolean
draft:
default: false
description: |
Whether the release should be created as a draft or published immediately.
type: boolean
tag_name:
description: |
The tag which is being released.
By default, https://github.com/softprops/action-gh-release will use `github.ref_name`.
type: string

jobs:
build:
outputs:
release-files-artifact-id: ${{ steps.upload-release-files.outputs.artifact-id }}
release-notes-artifact-id: ${{ steps.upload-release-notes.outputs.artifact-id }}
runs-on: self-hosted
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ inputs.tag_name }}

- uses: bazel-contrib/setup-bazel@0.15.0
with:
disk-cache: ${{ inputs.mount_bazel_caches }}
repository-cache: ${{ inputs.mount_bazel_caches }}

- name: Test
run: ${{ inputs.bazel_test_command }} --disk_cache=~/.cache/bazel-disk-cache --repository_cache=~/.cache/bazel-repository-cache

# Fetch built artifacts (if any) from earlier jobs, which the release script may want to read.
# Extract into ${GITHUB_WORKSPACE}/artifacts/*
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1

- name: Build release artifacts and prepare release notes
run: |
if [ ! -f ".github/workflows/release_prep.sh" ]; then
echo "ERROR: create a .github/workflows/release_prep.sh script"
exit 1
fi
.github/workflows/release_prep.sh ${{ inputs.tag_name || github.ref_name }} > release_notes.txt

- uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 #v4.6.0
id: upload-release-files
with:
name: release_files
path: ${{ inputs.release_files }}

- uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 #v4.6.0
id: upload-release-notes
with:
name: release_notes
path: release_notes.txt

attest:
needs: build
outputs:
attestations-artifact-id: ${{ steps.upload-attestations.outputs.artifact-id }}
permissions:
id-token: write
attestations: write
runs-on: ubuntu-latest
steps:
# actions/download-artifact@v4 does not yet support downloading via the immutable artifact-id,
# but the Javascript library does. See: https://github.com/actions/download-artifact/issues/349
- run: npm install @actions/artifact@2.1.9
- name: download-release-files
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
env:
ARTIFACT_ID: ${{ needs.build.outputs.release-files-artifact-id }}
with:
script: |
const {default: artifactClient} = require('@actions/artifact')
const { ARTIFACT_ID } = process.env
await artifactClient.downloadArtifact(ARTIFACT_ID, { path: 'release_files/'})

# https://github.com/actions/attest-build-provenance
- name: Attest release files
id: attest_release
uses: actions/attest-build-provenance@v2
with:
subject-path: release_files/**/*

# The Bazel Central Registry requires an attestation per release archive, but the
# actions/attest-build-provenance action only produces a single attestation for a
# list of subjects. Copy the combined attestations into individually named
# .intoto.jsonl files.
- name: Write release archive attestations into intoto.jsonl
id: write_release_archive_attestation
run: |
# https://bazel.build/rules/lib/repo/http#http_archive
RELEASE_ARCHIVE_REGEX="(\.zip|\.jar|\.war|\.aar|\.tar|\.tar\.gz|\.tgz|\.tar\.xz|\.txz|\.tar\.xzt|\.tzst|\.tar\.bz2|\.ar|\.deb)$"

ATTESTATIONS_DIR=$(mktemp --directory)
for filename in $(find release_files/ -type f -printf "%f\n"); do
if [[ "${filename}" =~ $RELEASE_ARCHIVE_REGEX ]]; then
ATTESTATION_FILE="$(basename "${filename}").intoto.jsonl"
echo "Writing attestation to ${ATTESTATION_FILE}"
cat ${{ steps.attest_release.outputs.bundle-path }} | jq --compact-output > "${ATTESTATIONS_DIR}/${ATTESTATION_FILE}"
fi
done
echo "release_archive_attestations_dir=${ATTESTATIONS_DIR}" >> $GITHUB_OUTPUT
- uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 #v4.6.0
id: upload-attestations
with:
name: attestations
path: ${{ steps.write_release_archive_attestation.outputs.release_archive_attestations_dir }}/*

release:
needs: [build, attest]
permissions:
contents: write
runs-on: ubuntu-latest
steps:
# actions/download-artifact@v4 does not yet support downloading via the immutable artifact-id,
# but the Javascript library does. See: https://github.com/actions/download-artifact/issues/349
- run: npm install @actions/artifact@2.1.9
- name: download-artifacts
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
env:
RELEASE_FILES_ARTIFACT_ID: ${{ needs.build.outputs.release-files-artifact-id }}
RELEASE_NOTES_ARTIFACT_ID: ${{ needs.build.outputs.release-notes-artifact-id }}
ATTESTATIONS_ARTIFACT_ID: ${{ needs.attest.outputs.attestations-artifact-id }}
with:
script: |
const {default: artifactClient} = require('@actions/artifact')
const { RELEASE_FILES_ARTIFACT_ID, RELEASE_NOTES_ARTIFACT_ID, ATTESTATIONS_ARTIFACT_ID } = process.env
await Promise.all([
artifactClient.downloadArtifact(RELEASE_FILES_ARTIFACT_ID, { path: 'release_files/'}),
artifactClient.downloadArtifact(RELEASE_NOTES_ARTIFACT_ID, { path: 'release_notes/'}),
artifactClient.downloadArtifact(ATTESTATIONS_ARTIFACT_ID, { path: 'attestations/'})
])

- name: Release
uses: softprops/action-gh-release@v2
with:
prerelease: ${{ inputs.prerelease }}
draft: ${{ inputs.draft }}
# Use GH feature to populate the changelog automatically
generate_release_notes: true
body_path: release_notes/release_notes.txt
fail_on_unmatched_files: true
tag_name: ${{ inputs.tag_name }}
files: |
release_files/**/*
attestations/*
Empty file added BUILD.bazel
Empty file.
Loading