diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 0000000..1c997ea --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,121 @@ +# Reusable workflow that can be referenced by repositories in their `.github/workflows/publish.yaml`. +# See example usage in TODO for running automatically after a release or triggering manually. + +on: + # Make this workflow reusable, see + # https://github.blog/2022-02-10-using-reusable-workflows-github-actions + workflow_call: + inputs: + tag_name: + description: 'TODO' + required: true + type: string + registry: + description: 'TODO' + required: false + default: 'fweikert/bazel-central-registry' + type: string + registry-fork: + description: 'TODO' + required: true + type: string + repository: + description: 'TODO' + required: false + default: ${{ github.repository }} + type: string + secrets: + publish_token: + required: true +jobs: + publish: + runs-on: ubuntu-latest + steps: + - name: Checkout this repo + uses: actions/checkout@v4.2.2 + with: + ref: ${{ github.event.inputs.tag_name }} + path: this + + - name: Checkout BCR + uses: actions/checkout@v4.2.2 + with: + repository: ${{ github.event.inputs.registry }} + token: ${{ secrets.GITHUB_TOKEN }} + path: bazel-central-registry + + # Get version from the tag, stripping any v-prefix + - name: Write release version + env: + TAG: ${{ github.event.inputs.tag_name }} + run: | + VERSION=${TAG#v} + echo Version: $VERSION + echo "VERSION=$VERSION" >> $GITHUB_ENV + + - name: Create attestations template + working-directory: this/.bcr + run: | + if [ ! -f "attestations.template.json" ]; then + RELEASE_ARCHIVE_URL=$(jq --raw-output '.url' <<< source.template.json) + cat < attestations.template.json + { + "types": ["https://slsa.dev/provenance/v1"], + "attestations": { + "source.json": { + "url": "https://github.com/{OWNER}/{REPO}/releases/download/{TAG}/source.json.intoto.jsonl", + "integrity": "" + }, + "MODULE.bazel": { + "url": "https://github.com/{OWNER}/{REPO}/releases/download/{TAG}/MODULE.bazel.intoto.jsonl", + "integrity": "" + }, + "$(basename $RELEASE_ARCHIVE_URL).intoto.jsonl": { + "url": "${RELEASE_ARCHIVE_URL}.intoto.jsonl", + "integrity": "" + } + } + } + EOF + fi + + - name: Create entry + id: create-entry + uses: bazel-contrib/publish-to-bcr@0e74ba6f869f21ad7d5dcf5786b2119fa3e5a17e + with: + attest: true + attestations-dest: attestations + tag: ${{ github.event.inputs.tag_name }} + module-version: ${{ env.VERSION }} + local-registry: bazel-central-registry + templates-dir: this/.bcr + + - name: Upload attestations to release + uses: softprops/action-gh-release@v1 + with: + files: attestations/* + tag_name: ${{ inputs.tag_name }} + + - name: Create Pull Request + id: create-pull-request + uses: peter-evans/create-pull-request@v7 + with: + token: ${{ secrets.publish_token }} + path: bazel-central-registry + commit-message: Publish ${{ steps.create-entry.outputs.module-name }}@${{ inputs.tag_name }} + base: main + branch: ${{ steps.create-entry.outputs.module-name }}-${{ inputs.tag_name }} + push-to-fork: ${{ inputs.registry-fork }} + title: Publish ${{ steps.create-entry.outputs.module-name }}@${{ inputs.tag_name }} + body: | + Release: https://github.com/publish-to-bcr-dev/bazel-lib/releases/tag/${{ inputs.tag_name }} + + _Automated by [Publish to BCR](https://github.com/bazel-contrib/publish-to-bcr)_ + maintainer-can-modify: true + + - uses: peter-evans/enable-pull-request-automerge@v3 + with: + token: ${{ secrets.publish_token }} + path: bazel-central-registry + pull-request-number: ${{ steps.create-pull-request.outputs.pull-request-number }} + repository: ${{ inputs.registry }} \ No newline at end of file diff --git a/.github/workflows/release_ruleset.yaml b/.github/workflows/release_ruleset.yaml index 5eacd2f..cdfad23 100644 --- a/.github/workflows/release_ruleset.yaml +++ b/.github/workflows/release_ruleset.yaml @@ -13,17 +13,26 @@ on: # 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. - See https://github.com/softprops/action-gh-release#inputs - type: string 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. + + The script will be provided with the following env vars: + + ARTIFACTS_DIR: Path to a pre-created directory where all artifacts to be + uploaded to the GitHub release must be placed. + + RELEASE_NOTES: Path to a pre-created file where release notes content should + be written. + + The script must output a JSON blob with a to the release archive. The path can + be absolute or relative to the ARTIFACTS_DIR. For example: + + { + "release_archive": "my-ruleset-vX.Y.Z.tar.gz" + } + type: string bazel_test_command: default: "bazel test //..." @@ -55,23 +64,59 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ inputs.tag_name }} + path: this - - uses: bazel-contrib/setup-bazel@0.8.0 + - uses: bazel-contrib/setup-bazel@0.14.0 with: + module-root: this disk-cache: ${{ inputs.mount_bazel_caches }} external-cache: ${{ inputs.mount_bazel_caches }} repository-cache: ${{ inputs.mount_bazel_caches }} - name: Test + working-directory: this run: ${{ inputs.bazel_test_command }} --disk_cache=~/.cache/bazel-disk-cache --repository_cache=~/.cache/bazel-repository-cache - - name: Build release artifacts and prepare release notes + - name: Release preparation + id: release_prep + working-directory: this run: | if [ ! -f "${{ inputs.release_prep_command }}" ]; then echo "ERROR: create a ${{ inputs.release_prep_command }} release prep script or configure a different release prep command with the release_prep_command attribute" exit 1 fi - ${{ inputs.release_prep_command }} ${{ inputs.tag_name || github.ref_name }} > release_notes.txt + + export ARTIFACTS_DIR=$(mktemp --directory) + export RELEASE_NOTES=$(mktemp) + OUTPUT=$(${{ inputs.release_prep_command }} ${{ inputs.tag_name || github.ref_name }} | jq --compact-output .) + + echo "${OUTPUT}" + + # Make the release archive path absolute + RELEASE_ARCHIVE=$(jq -r '.release_archive' <<< "${OUTPUT}") + if [[ "${RELEASE_ARCHIVE:0:1}" != '/' ]]; then + RELEASE_ARCHIVE="${ARTIFACTS_DIR}/${RELEASE_ARCHIVE}" + fi + + echo "${RELEASE_ARCHIVE}" + + echo "artifacts_dir=$ARTIFACTS_DIR" >> $GITHUB_OUTPUT + echo "release_notes=$RELEASE_NOTES" >> $GITHUB_OUTPUT + echo "release_archive=$RELEASE_ARCHIVE" >> $GITHUB_OUTPUT + + - name: Attest release archive provenance + id: attest_release_archive + uses: actions/attest-build-provenance@v2 + with: + subject-path: ${{ steps.release_prep.outputs.release_archive }} + + - name: Write release archive attestation into intoto.jsonl + id: write_release_archive_attestation + run: | + ATTESTATION_FILE="$(basename "${{ steps.release_prep.outputs.release_archive }}").intoto.jsonl" + echo "Writing attestation to ${ATTESTATION_FILE}" + cat ${{ steps.attest_release_archive.outputs.bundle-path }} | jq -c > $ATTESTATION_FILE + echo "release_archive_attestation=$ATTESTATION_FILE" >> $GITHUB_OUTPUT - name: Release uses: softprops/action-gh-release@v1 @@ -79,7 +124,9 @@ jobs: prerelease: ${{ inputs.prerelease }} # Use GH feature to populate the changelog automatically generate_release_notes: true - body_path: release_notes.txt + body_path: ${{ steps.release_prep.outputs.release_notes }} fail_on_unmatched_files: true - files: ${{ inputs.release_files }} - tag_name: ${{ inputs.tag_name }} + files: | + ${{ steps.release_prep.outputs.artifacts_dir }}/* + ${{ steps.write_release_archive_attestation.outputs.release_archive_attestation }} + tag_name: ${{ inputs.tag_name }} \ No newline at end of file