From 48fc588c3eb545e3ca17776ecf26dbdf5d7d118e Mon Sep 17 00:00:00 2001 From: Matthew Grossman Date: Tue, 27 Oct 2026 16:23:25 -0700 Subject: [PATCH] ci(release): verify dispatched snap and wheel inputs match the release A dispatched snap upload checks that its artifacts were built for the release tag. A wheel publish first confirms the tag points to the given commit and that each wheel is attached to that release at the given version. artifact-run-id is declared on workflow_call so callers do not rely on an undeclared input. Signed-off-by: Matthew Grossman --- .github/workflows/snap-publish.yml | 10 ++++++++ .github/workflows/wheel-publish.yml | 37 ++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/.github/workflows/snap-publish.yml b/.github/workflows/snap-publish.yml index b15616fb1..a7ba1a5ad 100644 --- a/.github/workflows/snap-publish.yml +++ b/.github/workflows/snap-publish.yml @@ -17,6 +17,11 @@ on: required: false type: string default: "" + artifact-run-id: + description: Run whose snap-linux-* artifacts are uploaded; defaults to the current run + required: false + type: string + default: "" secrets: SNAPCRAFT_STORE_CREDENTIALS: required: true @@ -119,6 +124,11 @@ jobs: snap_file="${snaps[0]}" snap_name="${snap_file%.snap}" snap_name="${snap_name%%_*}" + # Artifacts can come from another run, so confirm they were built for this tag. + if [[ -n "${INPUTS_RELEASE_TAG}" && "${snap_file}" != "${snap_name}_${INPUTS_RELEASE_TAG#v}_"* ]]; then + echo "${snap_file} was not built for ${INPUTS_RELEASE_TAG}" >&2 + exit 1 + fi component_args=() for comp in "${snap_name}"+*.comp; do component_file="${comp##*/}" diff --git a/.github/workflows/wheel-publish.yml b/.github/workflows/wheel-publish.yml index fa2ad83d0..451c6ed46 100644 --- a/.github/workflows/wheel-publish.yml +++ b/.github/workflows/wheel-publish.yml @@ -42,11 +42,46 @@ on: required: true type: string -permissions: {} +permissions: + contents: read jobs: + # Dispatch inputs are free-form, so confirm they describe a published release. + verify: + name: Verify Release Inputs + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Check tag, commit and wheels + shell: bash + env: + GH_TOKEN: ${{ github.token }} + COMMIT_SHA: ${{ inputs.source-sha }} + RELEASE_VERSION: ${{ inputs.python-version }} + RELEASE_TAG: ${{ inputs.release-tag }} + WHEEL_FILENAMES: ${{ inputs.wheel-filenames }} + run: | + set -euo pipefail + tag_sha=$(gh api "repos/${GITHUB_REPOSITORY}/commits/${RELEASE_TAG}" --jq .sha) + if [[ "${tag_sha}" != "${COMMIT_SHA}" ]]; then + echo "${RELEASE_TAG} points to ${tag_sha}, not ${COMMIT_SHA}" >&2 + exit 1 + fi + assets=$(gh release view "${RELEASE_TAG}" -R "${GITHUB_REPOSITORY}" --json assets --jq '.assets[].name') + for wheel in ${WHEEL_FILENAMES//,/ }; do + if ! grep -qxF "${wheel}" <<<"${assets}"; then + echo "${wheel} is not attached to the ${RELEASE_TAG} release" >&2 + exit 1 + fi + if [[ "${wheel}" != *"-${RELEASE_VERSION}-"* ]]; then + echo "${wheel} is not version ${RELEASE_VERSION}" >&2 + exit 1 + fi + done + trigger: name: Trigger Wheel Publish + needs: verify runs-on: [self-hosted, nv] timeout-minutes: 10 steps: