-
Notifications
You must be signed in to change notification settings - Fork 1
242 lines (204 loc) · 8.97 KB
/
Copy pathcherry-pick-commit.yml
File metadata and controls
242 lines (204 loc) · 8.97 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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
name: Cherry-pick Commits to Branches
on:
workflow_dispatch:
inputs:
repository:
required: true
description: 'Target repository (e.g., alaudadevops/repository)'
type: string
commits:
required: true
description: 'Comma-separated list of commit hashes to cherry-pick (in order)'
type: string
target_branches:
required: true
description: 'Comma-separated list of target branches (e.g., release-1.0,release-1.1,main)'
type: string
pr_title_prefix:
required: false
description: 'Prefix for PR titles (optional)'
type: string
default: '[Cherry-pick]'
jobs:
prepare:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Prepare branch matrix
id: set-matrix
run: |
# Convert comma-separated branches to JSON array
branches="${{ inputs.target_branches }}"
# Remove spaces and convert to JSON array
branches_json=$(echo "$branches" | sed 's/[[:space:]]//g' | sed 's/,/","/g' | sed 's/^/["/' | sed 's/$/"]/')
echo "matrix={\"branch\":$branches_json}" >> $GITHUB_OUTPUT
echo "Generated matrix: {\"branch\":$branches_json}"
cherry-pick:
needs: prepare
runs-on: ubuntu-latest
strategy:
matrix: ${{ fromJson(needs.prepare.outputs.matrix) }}
fail-fast: false # Continue with other branches even if one fails
steps:
- name: Generate branch name and prepare commits
id: branch-info
run: |
# Parse commits and create a unique branch name
commits="${{ inputs.commits }}"
# Remove spaces and split by comma
commits_clean=$(echo "$commits" | sed 's/[[:space:]]//g')
# Get first and last commit for branch naming
first_commit=$(echo "$commits_clean" | cut -d',' -f1)
last_commit=$(echo "$commits_clean" | rev | cut -d',' -f1 | rev)
first_short=${first_commit:0:8}
if [ "$first_commit" = "$last_commit" ]; then
branch_name="cherry-pick-${first_short}-to-${{ matrix.branch }}"
else
last_short=${last_commit:0:8}
branch_name="cherry-pick-${first_short}-${last_short}-to-${{ matrix.branch }}"
fi
echo "branch_name=$branch_name" >> $GITHUB_OUTPUT
echo "commits_clean=$commits_clean" >> $GITHUB_OUTPUT
echo "first_commit=$first_commit" >> $GITHUB_OUTPUT
echo "last_commit=$last_commit" >> $GITHUB_OUTPUT
- name: Checkout target repository
uses: actions/checkout@v4
with:
repository: ${{ inputs.repository }}
ref: ${{ matrix.branch }}
token: ${{ secrets.TOKEN }}
fetch-depth: 0 # Fetch full history for cherry-pick
- name: Configure Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Create new branch
run: |
git checkout -b ${{ steps.branch-info.outputs.branch_name }}
- name: Cherry-pick commits
id: cherry-pick
run: |
commits="${{ steps.branch-info.outputs.commits_clean }}"
echo "Attempting to cherry-pick commits: $commits to branch ${{ matrix.branch }}"
# Convert comma-separated commits to array
IFS=',' read -ra COMMIT_ARRAY <<< "$commits"
success=true
failed_commit=""
# Cherry-pick each commit in order
for commit in "${COMMIT_ARRAY[@]}"; do
echo "Cherry-picking commit: $commit"
if ! git cherry-pick "$commit"; then
echo "Cherry-pick failed for commit $commit - conflicts detected"
success=false
failed_commit="$commit"
# Get conflict information
echo "Conflicted files:"
git status --porcelain | grep "^UU\|^AA\|^DD" || true
# Abort the cherry-pick
git cherry-pick --abort
break
else
echo "Successfully cherry-picked commit: $commit"
fi
done
if [ "$success" = true ]; then
echo "All cherry-picks successful"
echo "success=true" >> $GITHUB_OUTPUT
else
echo "Cherry-pick failed at commit: $failed_commit"
echo "success=false" >> $GITHUB_OUTPUT
echo "failed_commit=$failed_commit" >> $GITHUB_OUTPUT
exit 1
fi
- name: Push new branch
if: steps.cherry-pick.outputs.success == 'true'
run: |
git push origin ${{ steps.branch-info.outputs.branch_name }}
- name: Get commit details
if: steps.cherry-pick.outputs.success == 'true'
id: commit-details
run: |
commits="${{ steps.branch-info.outputs.commits_clean }}"
IFS=',' read -ra COMMIT_ARRAY <<< "$commits"
# For PR title, use the first commit's message
first_commit="${{ steps.branch-info.outputs.first_commit }}"
commit_message=$(git log --format=%B -n 1 "$first_commit")
commit_author=$(git log --format="%an <%ae>" -n 1 "$first_commit")
# Escape newlines and quotes for GitHub output
commit_message_escaped=$(echo "$commit_message" | sed ':a;N;$!ba;s/\n/\\n/g' | sed 's/"/\\"/g')
echo "commit_message=$commit_message_escaped" >> $GITHUB_OUTPUT
echo "commit_author=$commit_author" >> $GITHUB_OUTPUT
# Generate detailed commit list for PR body
commit_details=""
for commit in "${COMMIT_ARRAY[@]}"; do
msg=$(git log --format="%h - %s" -n 1 "$commit")
author=$(git log --format="%an" -n 1 "$commit")
commit_details="${commit_details}- \`${commit}\`: ${msg} (by ${author})\n"
done
# Escape for GitHub output
commit_details_escaped=$(echo -e "$commit_details" | sed ':a;N;$!ba;s/\n/\\n/g' | sed 's/"/\\"/g')
echo "commit_details=$commit_details_escaped" >> $GITHUB_OUTPUT
- name: Create Pull Request
if: steps.cherry-pick.outputs.success == 'true'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.TOKEN }}
script: |
const [owner, repo] = '${{ inputs.repository }}'.split('/');
const commits = '${{ steps.branch-info.outputs.commits_clean }}'.split(',');
const commitCount = commits.length;
let prTitle;
if (commitCount === 1) {
prTitle = `${{ inputs.pr_title_prefix }} ${{ steps.commit-details.outputs.commit_message }}`.split('\\n')[0];
} else {
prTitle = `${{ inputs.pr_title_prefix }} ${commitCount} commits to ${{ matrix.branch }}`;
}
const prBody =
`## Cherry-pick Summary
This PR cherry-picks ${commitCount} commit${commitCount > 1 ? 's' : ''} to branch \`${{ matrix.branch }}\`.
**Target branch:** ${{ matrix.branch }}
**Commits cherry-picked:** ${commitCount}
**Commits:** ${commits.join(', ')}
---
*This PR was automatically created by the cherry-pick workflow.*`;
console.log(prBody);
try {
const response = await github.rest.pulls.create({
owner: owner,
repo: repo,
title: prTitle,
head: '${{ steps.branch-info.outputs.branch_name }}',
base: '${{ matrix.branch }}',
body: prBody
});
console.log(`Created PR #${response.data.number}: ${response.data.html_url}`);
} catch (error) {
console.error('Failed to create PR:', error);
throw error;
}
- name: Report failure
if: failure()
uses: actions/github-script@v7
with:
github-token: ${{ secrets.TOKEN }}
script: |
console.log(`❌ Cherry-pick failed for branch ${{ matrix.branch }}`);
console.log(`Commits: ${{ inputs.commits }}`);
console.log(`Repository: ${{ inputs.repository }}`);
// You could optionally create an issue or send a notification here
// For now, we'll just log the failure
summary:
needs: [prepare, cherry-pick]
runs-on: ubuntu-latest
if: always() # Run even if some cherry-picks failed
steps:
- name: Summary
run: |
echo "## Cherry-pick Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Repository:** ${{ inputs.repository }}" >> $GITHUB_STEP_SUMMARY
echo "**Commits:** ${{ inputs.commits }}" >> $GITHUB_STEP_SUMMARY
echo "**Target branches:** ${{ inputs.target_branches }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Check the individual job results above for detailed status of each branch." >> $GITHUB_STEP_SUMMARY