From 841040e781f9ad3ceca12bd3069804fe48a1ab0b Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 8 Jun 2026 07:41:38 +0200 Subject: [PATCH] Attach release artifacts to draft via workflow_dispatch Draft releases do not fire the `release: created` event, so the release workflow never ran and the source zip and SLSA provenance were not attached to the draft. Trigger the workflow explicitly from release.py via workflow_dispatch, passing the tag to attach to and the ref to build from, and resolve the tag/ref in the workflow for both event types. --- .github/workflows/release.yml | 35 +++++++++++++++++++++++++++++------ support/release.py | 23 +++++++++++++++++++---- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 868c1e8c..13a5ee1d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,17 +1,33 @@ -# Builds the release source package in CI when a draft release is created -# (typically via support/release.py), uploads the zip to that release, and +# Builds the release source package in CI for a draft release (typically +# created via support/release.py), uploads the zip to that release, and # attaches a SLSA v1.0 provenance attestation generated by the OpenSSF # slsa-github-generator. The maintainer reviews the draft (which by then has # both the zip and *.intoto.jsonl attached) and clicks Publish to finalize. # # This makes the provenance attest to the actual build that produced the # artifact, rather than just attesting to a hash observed after the fact. +# +# GitHub does not fire `release: created` for draft releases, so release.py +# triggers this workflow explicitly via workflow_dispatch, passing the tag to +# attach to and the ref to build from. The `release: created` trigger is kept +# for non-draft releases created directly through the GitHub UI. name: release on: release: types: [created] + workflow_dispatch: + inputs: + tag_name: + description: "Release tag to attach the artifacts to (e.g. 12.2.0)" + required: true + type: string + ref: + description: "Git ref to build the source package from" + required: false + default: release + type: string permissions: read-all @@ -24,11 +40,18 @@ jobs: outputs: hashes: ${{ steps.hash.outputs.hashes }} package: ${{ steps.build.outputs.package }} + tag: ${{ steps.vars.outputs.tag }} steps: + - name: Resolve tag and ref for both event types + id: vars + run: | + echo "tag=${{ github.event.release.tag_name || inputs.tag_name }}" >> "$GITHUB_OUTPUT" + echo "ref=${{ github.event.release.target_commitish || inputs.ref }}" >> "$GITHUB_OUTPUT" + - name: Checkout the release ref uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: - ref: ${{ github.event.release.target_commitish }} + ref: ${{ steps.vars.outputs.ref }} persist-credentials: false - name: Build source zip via CPack @@ -51,7 +74,7 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | - gh release upload "${{ github.event.release.tag_name }}" \ + gh release upload "${{ steps.vars.outputs.tag }}" \ "${{ steps.build.outputs.package }}" \ --repo "${{ github.repository }}" --clobber @@ -64,6 +87,6 @@ jobs: uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0 with: base64-subjects: ${{ needs.build.outputs.hashes }} - provenance-name: "fmt-${{ github.event.release.tag_name }}.intoto.jsonl" + provenance-name: "fmt-${{ needs.build.outputs.tag }}.intoto.jsonl" upload-assets: true - upload-tag-name: ${{ github.event.release.tag_name }} + upload-tag-name: ${{ needs.build.outputs.tag }} diff --git a/support/release.py b/support/release.py index 58206755..b3585b84 100755 --- a/support/release.py +++ b/support/release.py @@ -193,10 +193,10 @@ if __name__ == '__main__': run('cmake', '.') run('make', 'doc') - # Create a draft release on GitHub. The release workflow triggers on - # `release: created`, builds the source zip from `target_commitish`, and - # attaches the zip plus *.intoto.jsonl provenance to this draft. After - # reviewing the draft, the maintainer clicks Publish to finalize. + # Create a draft release on GitHub, then trigger the release workflow to + # build the source zip from the `release` branch and attach the zip plus + # *.intoto.jsonl provenance to this draft. After reviewing the draft, the + # maintainer clicks Publish to finalize. fmt_repo.push('origin', 'release') auth_headers = {'Authorization': 'token ' + os.getenv('FMT_TOKEN')} req = urllib.request.Request( @@ -210,5 +210,20 @@ if __name__ == '__main__': raise Exception(f'Failed to create a release ' + '{response.status} {response.reason}') + # Draft releases do not fire `release: created`, so explicitly dispatch the + # release workflow. It runs from the default branch (`branch`) but builds + # and uploads artifacts from the `release` branch for tag `version`. + dispatch_req = urllib.request.Request( + 'https://api.github.com/repos/fmtlib/fmt/actions/workflows/' + 'release.yml/dispatches', + data=json.dumps({'ref': branch, + 'inputs': {'tag_name': version, + 'ref': 'release'}}).encode('utf-8'), + headers=auth_headers, method='POST') + with urllib.request.urlopen(dispatch_req) as response: + if response.status != 204: + raise Exception('Failed to dispatch the release workflow ' + + f'{response.status} {response.reason}') + short_version = '.'.join(version.split('.')[:-1]) check_call(['./mkdocs', 'deploy', short_version], env=doc_env)