-
Notifications
You must be signed in to change notification settings - Fork 10
153 lines (135 loc) · 7.07 KB
/
Copy pathcherry-pick-patch.yml
File metadata and controls
153 lines (135 loc) · 7.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
name: Cherry-pick patch PRs to release branch
on:
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to cherry-pick onto the release branch'
required: true
type: string
pull_request:
types: [closed]
branches: [main]
issues:
types: [labeled]
permissions:
contents: write
pull-requests: write
actions: write
jobs:
cherry-pick:
name: Cherry-pick onto ${{ vars.RELEASE_BRANCH || 'v5.0' }}
runs-on: ubuntu-latest
# Run when a PR with the patch label is merged into main.
# Handles two triggering paths:
# pull_request/closed — PR merged with patch label already present
# issues/labeled — patch label added to an already-merged PR
# (GitHub fires 'issues' not 'pull_request' for label
# changes on closed/merged PRs)
if: >-
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'pull_request' &&
github.event.pull_request.merged == true &&
contains(github.event.pull_request.labels.*.name, 'patch')) ||
(github.event_name == 'issues' &&
github.event.label.name == 'patch' &&
github.event.issue.pull_request.merged_at != '')
env:
RELEASE_BRANCH: ${{ vars.RELEASE_BRANCH || 'v5.0' }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Configure git
run: |
git config user.email "noreply@harperdb.io"
git config user.name "Harperfast"
- name: Cherry-pick onto ${{ env.RELEASE_BRANCH }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.inputs.pr_number || github.event.pull_request.number || github.event.issue.number }}
MERGE_SHA_FROM_EVENT: ${{ github.event.pull_request.merge_commit_sha }}
PR_TITLE_FROM_EVENT: ${{ github.event.pull_request.title || github.event.issue.title }}
run: |
set -euo pipefail
# For workflow_dispatch/issues events, some fields aren't in the payload; fetch them.
if [ -n "$MERGE_SHA_FROM_EVENT" ]; then
MERGE_SHA="$MERGE_SHA_FROM_EVENT"
PR_TITLE="$PR_TITLE_FROM_EVENT"
else
PR_DATA=$(gh api "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER" --jq '{sha: .merge_commit_sha, title: .title, state: .state}')
MERGE_SHA=$(echo "$PR_DATA" | jq -r '.sha')
PR_TITLE=$(echo "$PR_DATA" | jq -r '.title')
STATE=$(echo "$PR_DATA" | jq -r '.state')
if [ "$STATE" != "closed" ] || [ -z "$MERGE_SHA" ] || [ "$MERGE_SHA" = "null" ]; then
echo "PR #$PR_NUMBER is not yet merged — aborting."
exit 1
fi
fi
# Fetch enough history for subject-match checks and ancestor lookups
# (cherry-pick needs MERGE_SHA^ to compute the diff; rebase merges need
# up to COMMIT_COUNT ancestors). Full unshallow fetches both branches.
git fetch --depth=200 origin main "$RELEASE_BRANCH"
# Skip if this commit is already in the release branch.
# Subject matching handles cherry-picks whose patch-id changed due to
# conflict resolution (e.g. package-lock.json merges).
SUBJECT=$(git log -1 --format=%s "$MERGE_SHA")
if git log "origin/$RELEASE_BRANCH" --format=%s | grep -qxF "$SUBJECT"; then
echo "PR #$PR_NUMBER already applied to $RELEASE_BRANCH — skipping."
exit 0
fi
# ── Determine pick strategy ────────────────────────────────────────────
PARENT_COUNT=$(git cat-file -p "$MERGE_SHA" | grep -c "^parent ")
PICK_FLAGS=""
PICK_SHAS="$MERGE_SHA"
if [ "$PARENT_COUNT" -gt 1 ]; then
# True merge commit → cherry-pick combined diff
PICK_FLAGS="-m 1"
else
COMMIT_COUNT=$(gh api "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/commits" --jq '. | length')
if [ "$COMMIT_COUNT" -gt 1 ]; then
LAST_BRANCH_SUBJECT=$(gh api "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/commits" \
--jq '.[-1].commit.message | split("\n")[0]')
# Rebase merge: subject equals last branch commit subject with no "(#N)" suffix.
# Squash merge: either has "(#N)" suffix, or subject differs from last branch commit.
if ! echo "$SUBJECT" | grep -qF "(#$PR_NUMBER)" && [ "$SUBJECT" = "$LAST_BRANCH_SUBJECT" ]; then
# Rebase merge — reconstruct commit list oldest→newest
PICK_SHAS=""
for i in $(seq $((COMMIT_COUNT - 1)) -1 0); do
SHA=$([ "$i" -eq 0 ] && echo "$MERGE_SHA" || git rev-parse "${MERGE_SHA}~${i}")
PICK_SHAS="${PICK_SHAS:+$PICK_SHAS }$SHA"
done
fi
# else: squash — PICK_SHAS stays as $MERGE_SHA
fi
fi
# ── Cherry-pick ────────────────────────────────────────────────────────
git checkout "$RELEASE_BRANCH"
# shellcheck disable=SC2086
if git cherry-pick $PICK_FLAGS $PICK_SHAS; then
git push origin "$RELEASE_BRANCH"
echo "✅ Cherry-picked PR #$PR_NUMBER onto $RELEASE_BRANCH"
gh workflow run integration-tests.yml --ref "$RELEASE_BRANCH" --repo "$GITHUB_REPOSITORY"
gh workflow run unit-test.yml --ref "$RELEASE_BRANCH" --repo "$GITHUB_REPOSITORY"
else
echo "⚠️ Cherry-pick conflict — creating resolution branch"
git cherry-pick --abort 2>/dev/null || true
CONFLICT_BRANCH="cherry-pick/${RELEASE_BRANCH}/pr-${PR_NUMBER}"
git checkout -b "$CONFLICT_BRANCH"
# Re-apply each commit individually so partial successes are preserved.
# Conflict markers are staged and committed for the reviewer to resolve.
for SHA in $PICK_SHAS; do
# shellcheck disable=SC2086
git cherry-pick $PICK_FLAGS "$SHA" || {
git add -A
git cherry-pick --continue --no-edit || git cherry-pick --skip
}
done
git push origin "$CONFLICT_BRANCH"
PR_BODY=$(printf '## Resolve cherry-pick conflicts\n\nPR #%s (_%s_) conflicted when cherry-picking onto `%s`.\n\nConflict markers (`<<<<<<<` / `=======` / `>>>>>>>`) have been committed to this branch. Please check out the branch, resolve all conflicts, push, and merge.\n\n**Source PR:** #%s\n**Commit(s):** `%s`' \
"$PR_NUMBER" "$PR_TITLE" "$RELEASE_BRANCH" "$PR_NUMBER" "$PICK_SHAS")
gh pr create \
--title "cherry-pick: \`${PR_TITLE}\` → \`${RELEASE_BRANCH}\` (conflict)" \
--body "$PR_BODY" \
--base "$RELEASE_BRANCH" \
--head "$CONFLICT_BRANCH"
fi