-
Notifications
You must be signed in to change notification settings - Fork 6k
173 lines (142 loc) · 6.41 KB
/
cherry-pick.yml
File metadata and controls
173 lines (142 loc) · 6.41 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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
name: Cherry Pick
on:
pull_request_target:
branches: [develop]
types: [closed, labeled]
permissions:
contents: write
pull-requests: write
issues: write
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: false
jobs:
cherry-pick:
if: >
github.event.pull_request.merged == true &&
(
github.event.action == 'labeled' ||
contains(join(github.event.pull_request.labels.*.name, ' '), 'cherry-pick')
)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Cherry Pick
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_BODY: ${{ github.event.pull_request.body }}
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
MERGE_COMMIT_SHA: ${{ github.event.pull_request.merge_commit_sha }}
run: |
# Function to post comment
post_comment() {
gh pr comment "$PR_NUMBER" --body "$1"
}
# Configure git for the original author
echo "Fetching author info for $PR_AUTHOR..."
AUTHOR_INFO=$(gh api "/users/$PR_AUTHOR" --jq '{email: .email, name: .name}')
AUTHOR_EMAIL=$(echo "$AUTHOR_INFO" | jq -r '.email')
AUTHOR_NAME=$(echo "$AUTHOR_INFO" | jq -r '.name')
if [ "$AUTHOR_EMAIL" = "null" ] || [ -z "$AUTHOR_EMAIL" ]; then
AUTHOR_EMAIL="${PR_AUTHOR}@users.noreply.github.com"
echo "Author email not found, using default: $AUTHOR_EMAIL"
fi
if [ "$AUTHOR_NAME" = "null" ] || [ -z "$AUTHOR_NAME" ]; then
AUTHOR_NAME="${PR_AUTHOR}"
echo "Author name not found, using username: $AUTHOR_NAME"
fi
git config user.name "$AUTHOR_NAME"
git config user.email "$AUTHOR_EMAIL"
# Capture current SHA to return to later
ORIGINAL_HEAD_SHA=$(git rev-parse HEAD)
# Get labels
LABELS=$(gh pr view "$PR_NUMBER" --json labels --jq '.labels[].name')
if [ -z "$LABELS" ]; then
echo "No labels found."
exit 0
fi
# Loop through labels
while read -r label; do
if [[ "$label" == cherry-pick:* ]]; then
TARGET_BRANCH=$(echo "${label#cherry-pick:}" | xargs)
if [ -z "$TARGET_BRANCH" ]; then
echo "Empty target branch for label '$label', skipping."
continue
fi
echo "Processing cherry-pick to $TARGET_BRANCH"
# Check if target branch exists on remote
if ! git ls-remote --exit-code --heads origin "$TARGET_BRANCH"; then
echo "Target branch $TARGET_BRANCH does not exist."
post_comment "❌ Cherry-pick failed: Target branch \`$TARGET_BRANCH\` does not exist."
continue
fi
# Create a new branch for the cherry-pick
NEW_BRANCH="cherry-pick/$PR_NUMBER/$TARGET_BRANCH"
# Clean up local branch if it exists (from previous run)
if git show-ref --verify --quiet "refs/heads/$NEW_BRANCH"; then
git branch -D "$NEW_BRANCH"
fi
# Fetch the target branch and checkout a new branch from it
git fetch origin "$TARGET_BRANCH"
git checkout -b "$NEW_BRANCH" "origin/$TARGET_BRANCH"
# Cherry pick
# Try standard cherry-pick first (for squash merges or single commits)
if git cherry-pick "$MERGE_COMMIT_SHA"; then
echo "Cherry-pick successful."
else
echo "Standard cherry-pick failed, trying with -m 1 (for merge commits)..."
git cherry-pick --abort
if git cherry-pick -m 1 "$MERGE_COMMIT_SHA"; then
echo "Cherry-pick with -m 1 successful."
else
echo "Cherry-pick failed."
git cherry-pick --abort
post_comment "❌ Cherry-pick failed: Conflicts detected when cherry-picking to \`$TARGET_BRANCH\`. Please resolve manually."
# Cleanup
git checkout "$ORIGINAL_HEAD_SHA"
git branch -D "$NEW_BRANCH"
continue
fi
fi
# Push
# Check if remote branch already exists, if so, force push? Or maybe fail?
# Force push is better to update the PR if we are re-running.
git push origin "$NEW_BRANCH" --force
# Create PR
NEW_TITLE="[$TARGET_BRANCH] $PR_TITLE"
NEW_BODY="$PR_BODY
Cherry-pick of #$PR_NUMBER
Close and reopen this PR to trigger CI checks."
# Check if PR already exists
EXISTING_PR=$(gh pr list --base "$TARGET_BRANCH" --head "$NEW_BRANCH" --json url --jq '.[0].url')
if [ -n "$EXISTING_PR" ]; then
echo "PR already exists: $EXISTING_PR"
post_comment "ℹ️ Cherry-pick PR already exists: $EXISTING_PR"
else
# Create PR using gh CLI, ignoring errors because of "Resource not accessible" false positives
gh pr create --base "$TARGET_BRANCH" --head "$NEW_BRANCH" --title "$NEW_TITLE" --body "$NEW_BODY" || true
# Wait a bit for eventual consistency
sleep 2
# Search for the created PR
CREATED_PR_URL=$(gh pr list --head "$NEW_BRANCH" --state all --json url --jq '.[0].url')
if [ -n "$CREATED_PR_URL" ]; then
echo "Created PR: $CREATED_PR_URL"
post_comment "✅ Cherry-pick successful! Created PR: $CREATED_PR_URL"
# Request review
gh pr review-request "$CREATED_PR_URL" --reviewer "$PR_AUTHOR" || true
else
echo "Failed to create PR."
post_comment "❌ Cherry-pick failed: Could not create PR to \`$TARGET_BRANCH\`."
continue
fi
fi
# Cleanup for next loop
git checkout "$ORIGINAL_HEAD_SHA"
git branch -D "$NEW_BRANCH"
fi
done <<< "$LABELS"