Skip to content

feat: publish docs with releases #2

feat: publish docs with releases

feat: publish docs with releases #2

# Reusable workflow that can be referenced by repositories in their `.github/workflows/release.yaml`.

Check failure on line 1 in .github/workflows/release_ruleset.yaml

View workflow run for this annotation

GitHub Actions / .github/workflows/release_ruleset.yaml

Invalid workflow file

(Line: 102, Col: 11): A sequence was not expected
# 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 test //..."
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
docs:
default: false
description: |
Indicator of whether or not to generate starlark documentation from bzl_library targets.
When true, an additional release artifact will be produced, which is an archive file of the binary protobuf files
output by the [starlark_doc_extract](https://bazel.build/reference/be/general#starlark_doc_extract) rule.
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: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ inputs.tag_name }}
- uses: bazel-contrib/setup-bazel@0.14.0
with:
disk-cache: ${{ inputs.mount_bazel_caches }}
external-cache: ${{ inputs.mount_bazel_caches }}
repository-cache: ${{ inputs.mount_bazel_caches }}
- name: Generate starlark documentation
id: generate-docs
if: inputs.docs
env:
- USE_BAZEL_VERSION: 7.x
run: |
cd $(mktemp -d)
cat >MODULE.bazel <<EOF
bazel_dep(name = "rules_oci" version = "0.0.0")
local_path_override(
module_name = "rules_oci",
path = "${{github.workspace}}",
)
EOF
BES_JSON=$(mktemp)
bazel query 'kind("starlark_doc_extract rule", @rules_oci//oci/...)' | \
xargs bazel build \
--remote_download_regex='.*doc_extract\.binaryproto' \
--build_event_json_file=$BES_JSON
cat $BES_JSON | jq -r '.namedSetOfFiles | values | .files[] | select(.name | endswith(".doc_extract.binaryproto")) | ((.pathPrefix | join("/")) + "/" + .name)' > $GITHUB_OUTPUT
- 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
- uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 #v4.6.0
id: upload-generated-docs
if: inputs.docs
with:
name: docs
path: ${{ steps.generate-docs.outputs.files }}
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/*