44
55# One-click release workflow for cuda-pathfinder.
66#
7- # Provide a version number. The workflow automatically finds the CI run and
8- # creates the git tag, creates a draft GitHub release with the standard
7+ # Provide a version number and commit SHA . The workflow automatically finds
8+ # the CI run and creates the git tag, creates a draft GitHub release with the standard
99# body, builds versioned docs, uploads source archive + wheels to the
1010# release, publishes to TestPyPI, verifies the install, publishes to PyPI,
1111# verifies again, and finally marks the release as published.
1919 description : " Version to release (e.g. 1.3.5)"
2020 required : true
2121 type : string
22+ commit :
23+ description : " Commit SHA to release (must be on default branch)"
24+ required : true
25+ type : string
2226
2327concurrency :
2428 group : release-cuda-pathfinder
7074 echo "version=${version}"
7175 } >> "$GITHUB_OUTPUT"
7276
77+ - name : Validate and resolve commit
78+ id : commit
79+ env :
80+ COMMIT_INPUT : ${{ inputs.commit }}
81+ DEFAULT_BRANCH : ${{ github.event.repository.default_branch }}
82+ run : |
83+ # Require a full SHA to avoid ambiguity and accidental releases.
84+ if [[ ! "${COMMIT_INPUT}" =~ ^[0-9a-fA-F]{40}$ ]]; then
85+ echo "::error::Commit must be a full 40-character SHA, got: ${COMMIT_INPUT}"
86+ exit 1
87+ fi
88+
89+ git fetch --no-tags origin "${DEFAULT_BRANCH}"
90+ commit=$(git rev-parse --verify "${COMMIT_INPUT}^{commit}" 2>/dev/null || true)
91+ if [[ -z "${commit}" ]]; then
92+ echo "::error::Commit not found in repository: ${COMMIT_INPUT}"
93+ exit 1
94+ fi
95+
96+ if ! git merge-base --is-ancestor "${commit}" "origin/${DEFAULT_BRANCH}"; then
97+ echo "::error::Commit ${commit} is not reachable from origin/${DEFAULT_BRANCH}"
98+ exit 1
99+ fi
100+
101+ echo "commit=${commit}" >> "$GITHUB_OUTPUT"
102+ echo "Using release commit: ${commit}"
103+
73104 - name : Check release notes exist
74105 env :
75106 VERSION : ${{ steps.vars.outputs.version }}
@@ -90,11 +121,17 @@ jobs:
90121 - name : Create tag
91122 env :
92123 TAG : ${{ steps.vars.outputs.tag }}
124+ TARGET_COMMIT : ${{ steps.commit.outputs.commit }}
93125 run : |
94126 if git rev-parse "${TAG}" >/dev/null 2>&1; then
95- echo "Tag ${TAG} already exists"
127+ existing_commit=$(git rev-parse "${TAG}^{commit}")
128+ if [[ "${existing_commit}" != "${TARGET_COMMIT}" ]]; then
129+ echo "::error::Tag ${TAG} already exists at ${existing_commit}, expected ${TARGET_COMMIT}"
130+ exit 1
131+ fi
132+ echo "Tag ${TAG} already exists at requested commit ${TARGET_COMMIT}"
96133 else
97- git tag "${TAG}"
134+ git tag "${TAG}" "${TARGET_COMMIT}"
98135 git push origin "${TAG}"
99136 fi
100137
0 commit comments