mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-05-24 07:57:29 +08:00
Update backport workflow to use commit SHA input (#14043)
This commit is contained in:
parent
8fecef0686
commit
8edff549e3
130
.github/workflows/backport_release.yaml
vendored
130
.github/workflows/backport_release.yaml
vendored
@ -3,8 +3,8 @@ name: Backport Release
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
branch:
|
commit:
|
||||||
description: 'Source branch containing the backported commits (PR source branch into master)'
|
description: 'Full 40-char SHA of the tip commit of the backport source branch (the PR head commit that passed tests). The branch is resolved from this SHA and must be unique.'
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
@ -39,21 +39,71 @@ jobs:
|
|||||||
git config user.name "fen-release[bot]"
|
git config user.name "fen-release[bot]"
|
||||||
git config user.email "fen-release[bot]@users.noreply.github.com"
|
git config user.email "fen-release[bot]@users.noreply.github.com"
|
||||||
|
|
||||||
- name: Validate source branch exists
|
- name: Resolve source branch from commit SHA
|
||||||
|
id: resolve
|
||||||
env:
|
env:
|
||||||
SOURCE_BRANCH: ${{ inputs.branch }}
|
SOURCE_COMMIT: ${{ inputs.commit }}
|
||||||
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
|
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
if [[ "${SOURCE_BRANCH}" == "${DEFAULT_BRANCH}" ]]; then
|
|
||||||
|
# Require a full 40-char lowercase-hex SHA. Short SHAs are ambiguous
|
||||||
|
# and we will be comparing this value against API responses (PR head
|
||||||
|
# SHA, ref tips) that always return the full form.
|
||||||
|
if [[ ! "${SOURCE_COMMIT}" =~ ^[0-9a-f]{40}$ ]]; then
|
||||||
|
echo "::error::Input commit '${SOURCE_COMMIT}' is not a full 40-char lowercase hex SHA."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fetch all remote branches so we can search for which one(s) point
|
||||||
|
# at this SHA. `actions/checkout` with fetch-depth: 0 fetches full
|
||||||
|
# history of the checked-out ref but does not necessarily populate
|
||||||
|
# every refs/remotes/origin/*, so do it explicitly.
|
||||||
|
git fetch --prune origin '+refs/heads/*:refs/remotes/origin/*'
|
||||||
|
|
||||||
|
# Verify the commit actually exists in this repo's object DB.
|
||||||
|
if ! git cat-file -e "${SOURCE_COMMIT}^{commit}" 2>/dev/null; then
|
||||||
|
echo "::error::Commit ${SOURCE_COMMIT} was not found in the repository."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find every remote branch whose tip == SOURCE_COMMIT. Exactly one
|
||||||
|
# branch must point at it. If zero, the commit isn't anyone's tip
|
||||||
|
# (likely stale, force-pushed past, or never the PR head). If more
|
||||||
|
# than one, the (branch -> SHA) mapping is ambiguous and we refuse
|
||||||
|
# to guess — the operator must give us a unique branch to release.
|
||||||
|
mapfile -t matching_branches < <(
|
||||||
|
git for-each-ref \
|
||||||
|
--format='%(refname:strip=3)' \
|
||||||
|
--points-at="${SOURCE_COMMIT}" \
|
||||||
|
refs/remotes/origin/ \
|
||||||
|
| grep -vx 'HEAD' || true
|
||||||
|
)
|
||||||
|
|
||||||
|
if [[ "${#matching_branches[@]}" -eq 0 ]]; then
|
||||||
|
echo "::error::No branch on origin has ${SOURCE_COMMIT} as its tip."
|
||||||
|
echo "::error::Either the branch was updated after you copied this SHA, or this commit was never the head of a branch."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${#matching_branches[@]}" -gt 1 ]]; then
|
||||||
|
echo "::error::More than one branch on origin has ${SOURCE_COMMIT} as its tip; cannot pick one:"
|
||||||
|
for b in "${matching_branches[@]}"; do
|
||||||
|
echo "::error:: - ${b}"
|
||||||
|
done
|
||||||
|
echo "::error::Refusing to proceed with an ambiguous source branch."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
source_branch="${matching_branches[0]}"
|
||||||
|
|
||||||
|
if [[ "${source_branch}" == "${DEFAULT_BRANCH}" ]]; then
|
||||||
echo "::error::Source branch must not be the default branch ('${DEFAULT_BRANCH}')."
|
echo "::error::Source branch must not be the default branch ('${DEFAULT_BRANCH}')."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
git fetch origin "refs/heads/${SOURCE_BRANCH}:refs/remotes/origin/${SOURCE_BRANCH}"
|
|
||||||
if ! git show-ref --verify --quiet "refs/remotes/origin/${SOURCE_BRANCH}"; then
|
echo "Resolved commit ${SOURCE_COMMIT} to branch '${source_branch}'."
|
||||||
echo "::error::Source branch '${SOURCE_BRANCH}' not found on origin."
|
echo "source_branch=${source_branch}" >> "$GITHUB_OUTPUT"
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Determine latest stable release
|
- name: Determine latest stable release
|
||||||
id: latest
|
id: latest
|
||||||
@ -107,13 +157,18 @@ jobs:
|
|||||||
|
|
||||||
- name: Validate source branch is cut directly from the latest stable release
|
- name: Validate source branch is cut directly from the latest stable release
|
||||||
env:
|
env:
|
||||||
SOURCE_BRANCH: ${{ inputs.branch }}
|
SOURCE_BRANCH: ${{ steps.resolve.outputs.source_branch }}
|
||||||
|
SOURCE_COMMIT: ${{ inputs.commit }}
|
||||||
LATEST_TAG_SHA: ${{ steps.latest.outputs.latest_sha }}
|
LATEST_TAG_SHA: ${{ steps.latest.outputs.latest_sha }}
|
||||||
LATEST_TAG: ${{ steps.latest.outputs.latest_tag }}
|
LATEST_TAG: ${{ steps.latest.outputs.latest_tag }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
source_sha="$(git rev-parse "refs/remotes/origin/${SOURCE_BRANCH}")"
|
# Use the user-provided SHA directly rather than re-resolving the branch
|
||||||
|
# tip — the resolve step already proved the branch tip equals SOURCE_COMMIT,
|
||||||
|
# and pinning to the SHA here makes the rest of the job TOCTOU-safe against
|
||||||
|
# someone pushing to the branch mid-run.
|
||||||
|
source_sha="${SOURCE_COMMIT}"
|
||||||
|
|
||||||
# Walking first-parent from the source tip must reach LATEST_TAG_SHA.
|
# Walking first-parent from the source tip must reach LATEST_TAG_SHA.
|
||||||
# We capture rev-list into a variable and grep against a here-string
|
# We capture rev-list into a variable and grep against a here-string
|
||||||
@ -156,10 +211,11 @@ jobs:
|
|||||||
added_count="$(printf '%s\n' "${all_added}" | grep -c . || true)"
|
added_count="$(printf '%s\n' "${all_added}" | grep -c . || true)"
|
||||||
echo "Source branch is cut directly from ${LATEST_TAG} with ${added_count} commit(s) on top."
|
echo "Source branch is cut directly from ${LATEST_TAG} with ${added_count} commit(s) on top."
|
||||||
|
|
||||||
- name: Validate PR exists, is named correctly, and checks pass
|
- name: Validate PR exists, is open, named correctly, has latest commit, and checks pass
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||||
SOURCE_BRANCH: ${{ inputs.branch }}
|
SOURCE_BRANCH: ${{ steps.resolve.outputs.source_branch }}
|
||||||
|
SOURCE_COMMIT: ${{ inputs.commit }}
|
||||||
NEW_VERSION: ${{ steps.latest.outputs.new_version }}
|
NEW_VERSION: ${{ steps.latest.outputs.new_version }}
|
||||||
REPO: ${{ github.repository }}
|
REPO: ${{ github.repository }}
|
||||||
run: |
|
run: |
|
||||||
@ -167,20 +223,22 @@ jobs:
|
|||||||
|
|
||||||
expected_title="ComfyUI backport release ${NEW_VERSION}"
|
expected_title="ComfyUI backport release ${NEW_VERSION}"
|
||||||
|
|
||||||
# Find open PRs from this branch into master
|
# Find open PRs from this branch into master. The --state open filter
|
||||||
|
# is load-bearing: a closed/merged PR with passing checks must not be
|
||||||
|
# accepted as authorization for a new release.
|
||||||
pr_json="$(
|
pr_json="$(
|
||||||
gh pr list \
|
gh pr list \
|
||||||
--repo "${REPO}" \
|
--repo "${REPO}" \
|
||||||
--state open \
|
--state open \
|
||||||
--head "${SOURCE_BRANCH}" \
|
--head "${SOURCE_BRANCH}" \
|
||||||
--base master \
|
--base master \
|
||||||
--json number,title,headRefOid \
|
--json number,title,headRefOid,state \
|
||||||
--limit 10
|
--limit 10
|
||||||
)"
|
)"
|
||||||
|
|
||||||
pr_count="$(echo "${pr_json}" | jq 'length')"
|
pr_count="$(echo "${pr_json}" | jq 'length')"
|
||||||
if [[ "${pr_count}" -eq 0 ]]; then
|
if [[ "${pr_count}" -eq 0 ]]; then
|
||||||
echo "::error::No open PR found from '${SOURCE_BRANCH}' into 'master'."
|
echo "::error::No open PR found from '${SOURCE_BRANCH}' into 'master'. The PR must exist and be open."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -199,7 +257,19 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Found PR #${pr_number} titled '${expected_title}' (head ${pr_head_sha})."
|
# The PR's current head commit must equal the SHA the operator gave us.
|
||||||
|
# This is what closes the door on releasing stale code: if anyone has
|
||||||
|
# pushed to the branch since the operator validated tests passed, the
|
||||||
|
# PR head will have advanced past SOURCE_COMMIT and we abort. (The
|
||||||
|
# resolve step already proved the branch tip == SOURCE_COMMIT; this
|
||||||
|
# ties that same SHA to the PR that authorizes the release.)
|
||||||
|
if [[ "${pr_head_sha}" != "${SOURCE_COMMIT}" ]]; then
|
||||||
|
echo "::error::PR #${pr_number} head commit is ${pr_head_sha}, but the operator-provided commit is ${SOURCE_COMMIT}."
|
||||||
|
echo "::error::The PR has new commits since this release was authorized. Re-run with the new head SHA after verifying its checks."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Found open PR #${pr_number} titled '${expected_title}' at head ${pr_head_sha} (matches operator-provided commit)."
|
||||||
|
|
||||||
# Verify all check runs on the head commit have completed successfully.
|
# Verify all check runs on the head commit have completed successfully.
|
||||||
# A check is considered passing if conclusion is success, neutral, or skipped.
|
# A check is considered passing if conclusion is success, neutral, or skipped.
|
||||||
@ -241,7 +311,6 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||||
REPO: ${{ github.repository }}
|
REPO: ${{ github.repository }}
|
||||||
SOURCE_BRANCH: ${{ inputs.branch }}
|
|
||||||
RELEASE_BRANCH: ${{ steps.latest.outputs.release_branch }}
|
RELEASE_BRANCH: ${{ steps.latest.outputs.release_branch }}
|
||||||
LATEST_TAG: ${{ steps.latest.outputs.latest_tag }}
|
LATEST_TAG: ${{ steps.latest.outputs.latest_tag }}
|
||||||
LATEST_TAG_SHA: ${{ steps.latest.outputs.latest_sha }}
|
LATEST_TAG_SHA: ${{ steps.latest.outputs.latest_sha }}
|
||||||
@ -277,7 +346,8 @@ jobs:
|
|||||||
|
|
||||||
- name: Fast-forward merge source branch into release branch
|
- name: Fast-forward merge source branch into release branch
|
||||||
env:
|
env:
|
||||||
SOURCE_BRANCH: ${{ inputs.branch }}
|
SOURCE_BRANCH: ${{ steps.resolve.outputs.source_branch }}
|
||||||
|
SOURCE_COMMIT: ${{ inputs.commit }}
|
||||||
RELEASE_BRANCH: ${{ steps.latest.outputs.release_branch }}
|
RELEASE_BRANCH: ${{ steps.latest.outputs.release_branch }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@ -288,12 +358,16 @@ jobs:
|
|||||||
# that the source branch is rooted on the latest stable tag, and the
|
# that the source branch is rooted on the latest stable tag, and the
|
||||||
# release branch tip equals that same tag, this fast-forward should
|
# release branch tip equals that same tag, this fast-forward should
|
||||||
# always succeed for a well-formed backport branch.
|
# always succeed for a well-formed backport branch.
|
||||||
if ! git merge --ff-only "refs/remotes/origin/${SOURCE_BRANCH}"; then
|
#
|
||||||
echo "::error::Cannot fast-forward '${RELEASE_BRANCH}' to '${SOURCE_BRANCH}'. A merge commit would be required. Aborting."
|
# We merge the operator-provided SHA, not the branch ref, so a push to
|
||||||
|
# the branch in the window between resolve and now cannot smuggle new
|
||||||
|
# commits into the release.
|
||||||
|
if ! git merge --ff-only "${SOURCE_COMMIT}"; then
|
||||||
|
echo "::error::Cannot fast-forward '${RELEASE_BRANCH}' to ${SOURCE_COMMIT} (tip of '${SOURCE_BRANCH}'). A merge commit would be required. Aborting."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Fast-forwarded '${RELEASE_BRANCH}' to tip of '${SOURCE_BRANCH}'."
|
echo "Fast-forwarded '${RELEASE_BRANCH}' to ${SOURCE_COMMIT} (tip of '${SOURCE_BRANCH}')."
|
||||||
|
|
||||||
- name: Bump version files
|
- name: Bump version files
|
||||||
env:
|
env:
|
||||||
@ -390,14 +464,20 @@ jobs:
|
|||||||
NEW_VERSION: ${{ steps.latest.outputs.new_version }}
|
NEW_VERSION: ${{ steps.latest.outputs.new_version }}
|
||||||
RELEASE_BRANCH: ${{ steps.latest.outputs.release_branch }}
|
RELEASE_BRANCH: ${{ steps.latest.outputs.release_branch }}
|
||||||
LATEST_TAG: ${{ steps.latest.outputs.latest_tag }}
|
LATEST_TAG: ${{ steps.latest.outputs.latest_tag }}
|
||||||
SOURCE_BRANCH: ${{ inputs.branch }}
|
SOURCE_BRANCH: ${{ steps.resolve.outputs.source_branch }}
|
||||||
|
SOURCE_COMMIT: ${{ inputs.commit }}
|
||||||
run: |
|
run: |
|
||||||
|
# SOURCE_BRANCH is empty if the resolve step never produced an output
|
||||||
|
# (e.g. the workflow failed in or before that step). Show a placeholder
|
||||||
|
# in that case so the summary table still renders cleanly.
|
||||||
|
source_branch_display="${SOURCE_BRANCH:-(unresolved)}"
|
||||||
{
|
{
|
||||||
echo "## Backport release"
|
echo "## Backport release"
|
||||||
echo ""
|
echo ""
|
||||||
echo "| Field | Value |"
|
echo "| Field | Value |"
|
||||||
echo "|---|---|"
|
echo "|---|---|"
|
||||||
echo "| Source branch | \`${SOURCE_BRANCH}\` |"
|
echo "| Source commit | \`${SOURCE_COMMIT}\` |"
|
||||||
|
echo "| Source branch | \`${source_branch_display}\` |"
|
||||||
echo "| Previous stable | \`${LATEST_TAG}\` |"
|
echo "| Previous stable | \`${LATEST_TAG}\` |"
|
||||||
echo "| New version | \`${NEW_VERSION}\` |"
|
echo "| New version | \`${NEW_VERSION}\` |"
|
||||||
echo "| Release branch | \`${RELEASE_BRANCH}\` |"
|
echo "| Release branch | \`${RELEASE_BRANCH}\` |"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user