Skip to content

Commit cd45e38

Browse files
stevsmitSteven Smith
andauthored
Updates cp workflow (quay#1504)
Co-authored-by: Steven Smith <[email protected]>
1 parent d876b94 commit cd45e38

File tree

1 file changed

+171
-15
lines changed

1 file changed

+171
-15
lines changed

.github/workflows/cherry-pick.yml

Lines changed: 171 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,189 @@ on:
44
issue_comment:
55
types: [created]
66

7+
permissions:
8+
contents: write
9+
pull-requests: write
10+
711
jobs:
812
cherry-pick:
913
runs-on: ubuntu-latest
1014
if: |
15+
github.event.issue.pull_request != null &&
1116
startsWith(github.event.comment.body, '/cherry-pick')
1217
steps:
1318
- name: Check out repository
14-
uses: actions/checkout@v2
19+
uses: actions/checkout@v4
20+
with:
21+
fetch-depth: 0
22+
token: ${{ secrets.GITHUB_TOKEN }}
1523

1624
- name: Set up Git
1725
run: |
18-
git config --global user.name "${{ github.actor }}"
19-
git config --global user.email "${{ github.actor }}@users.noreply.github.com"
26+
git config --global user.name "github-actions[bot]"
27+
git config --global user.email "github-actions[bot]@users.noreply.github.com"
28+
29+
- name: Get PR information
30+
id: pr_info
31+
uses: actions/github-script@v7
32+
with:
33+
github-token: ${{ secrets.GITHUB_TOKEN }}
34+
script: |
35+
const issueNumber = context.payload.issue.number;
36+
const { data: pr } = await github.rest.pulls.get({
37+
owner: context.repo.owner,
38+
repo: context.repo.repo,
39+
pull_number: issueNumber,
40+
});
41+
42+
core.setOutput('pr_number', issueNumber.toString());
43+
core.setOutput('base_branch', pr.base.ref);
44+
core.setOutput('head_branch', pr.head.ref);
45+
core.setOutput('head_sha', pr.head.sha);
46+
core.setOutput('title', pr.title || '');
47+
core.setOutput('body', pr.body || '');
48+
2049
- name: Extract target branch
2150
id: extract
22-
run: echo "::set-output name=branch::$(echo "${{ github.event.comment.body }}" | cut -d' ' -f2)"
51+
run: |
52+
COMMENT_BODY="${{ github.event.comment.body }}"
53+
TARGET_BRANCH=$(echo "$COMMENT_BODY" | sed -n 's|.*/cherry-pick[[:space:]]*\([^[:space:]]*\).*|\1|p')
54+
55+
if [ -z "$TARGET_BRANCH" ]; then
56+
echo "❌ Error: No target branch specified. Usage: /cherry-pick <branch-name>"
57+
exit 1
58+
fi
59+
60+
echo "branch=$TARGET_BRANCH" >> $GITHUB_OUTPUT
61+
echo "Target branch: $TARGET_BRANCH"
62+
63+
- name: Validate target branch exists
64+
id: validate_branch
65+
run: |
66+
TARGET_BRANCH="${{ steps.extract.outputs.branch }}"
67+
68+
# Remove any remote prefix if present
69+
BRANCH_NAME=$(echo "$TARGET_BRANCH" | sed 's|^[^/]*/||')
70+
71+
# Check if branch exists in origin
72+
if git ls-remote --heads origin "$BRANCH_NAME" | grep -q "$BRANCH_NAME"; then
73+
echo "✅ Found branch: origin/$BRANCH_NAME"
74+
echo "remote=origin" >> $GITHUB_OUTPUT
75+
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
76+
# Check if downstream remote exists and has the branch
77+
elif git remote | grep -q "^downstream$"; then
78+
if git ls-remote --heads downstream "$BRANCH_NAME" | grep -q "$BRANCH_NAME"; then
79+
echo "✅ Found branch: downstream/$BRANCH_NAME"
80+
echo "remote=downstream" >> $GITHUB_OUTPUT
81+
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
82+
else
83+
echo "❌ Error: Branch '$BRANCH_NAME' does not exist in downstream remote"
84+
exit 1
85+
fi
86+
else
87+
echo "❌ Error: Branch '$BRANCH_NAME' does not exist in origin"
88+
exit 1
89+
fi
2390
24-
- name: Cherry-pick the PR
25-
env:
26-
GH_PAT: ${{ secrets.GH_PAT }}
91+
- name: Create cherry-pick branch
92+
id: create_branch
2793
run: |
28-
TARGET_BRANCH=${{ steps.extract.outputs.branch }}
29-
git fetch origin ${{ github.event.pull_request.head.ref }}
30-
git checkout $TARGET_BRANCH
31-
git cherry-pick ${{ github.event.pull_request.head.sha }} || exit 0
32-
- name: Push changes
33-
env:
34-
GH_PAT: ${{ secrets.GH_PAT }}
94+
TARGET_BRANCH="${{ steps.validate_branch.outputs.branch_name }}"
95+
REMOTE="${{ steps.validate_branch.outputs.remote }}"
96+
PR_NUMBER="${{ steps.pr_info.outputs.pr_number }}"
97+
98+
# Create branch name: cherry-pick-<pr-number>-to-<target-branch>
99+
# Replace any slashes with dashes for branch name
100+
SAFE_BRANCH_NAME=$(echo "$TARGET_BRANCH" | tr '/' '-')
101+
CHERRY_PICK_BRANCH="cherry-pick-${PR_NUMBER}-to-${SAFE_BRANCH_NAME}"
102+
echo "branch_name=$CHERRY_PICK_BRANCH" >> $GITHUB_OUTPUT
103+
104+
# Fetch and checkout target branch
105+
git fetch "$REMOTE" "$TARGET_BRANCH"
106+
git checkout -b "$CHERRY_PICK_BRANCH" "${REMOTE}/${TARGET_BRANCH}"
107+
108+
echo "✅ Created branch: $CHERRY_PICK_BRANCH from ${REMOTE}/${TARGET_BRANCH}"
109+
110+
- name: Cherry-pick commits
111+
id: cherry_pick
35112
run: |
36-
git push https://${GH_PAT}@github.com/${{ github.repository }} $TARGET_BRANCH
113+
PR_NUMBER="${{ steps.pr_info.outputs.pr_number }}"
114+
BASE_BRANCH="${{ steps.pr_info.outputs.base_branch }}"
115+
116+
# Fetch the PR branch
117+
git fetch origin "pull/${PR_NUMBER}/head:pr-${PR_NUMBER}"
118+
119+
# Get all commits from the PR (compare base branch to PR branch)
120+
COMMITS=$(git log --oneline --reverse origin/${BASE_BRANCH}..pr-${PR_NUMBER} | awk '{print $1}')
121+
122+
if [ -z "$COMMITS" ]; then
123+
echo "❌ Error: No commits found to cherry-pick"
124+
exit 1
125+
fi
126+
127+
echo "Found commits to cherry-pick:"
128+
echo "$COMMITS"
129+
130+
# Cherry-pick each commit
131+
CHERRY_PICKED_COMMITS=""
132+
for COMMIT in $COMMITS; do
133+
echo "Cherry-picking commit: $COMMIT"
134+
if git cherry-pick "$COMMIT"; then
135+
CHERRY_PICKED_COMMITS="$CHERRY_PICKED_COMMITS $COMMIT"
136+
echo "✅ Successfully cherry-picked $COMMIT"
137+
else
138+
echo "❌ Error: Failed to cherry-pick commit $COMMIT"
139+
git cherry-pick --abort
140+
exit 1
141+
fi
142+
done
143+
144+
echo "✅ Successfully cherry-picked all commits"
145+
146+
- name: Push cherry-pick branch
147+
run: |
148+
CHERRY_PICK_BRANCH="${{ steps.create_branch.outputs.branch_name }}"
149+
git push origin "$CHERRY_PICK_BRANCH"
150+
151+
- name: Create pull request
152+
uses: actions/github-script@v7
153+
with:
154+
script: |
155+
const targetBranch = '${{ steps.validate_branch.outputs.branch_name }}';
156+
const cherryPickBranch = '${{ steps.create_branch.outputs.branch_name }}';
157+
const prNumber = parseInt('${{ steps.pr_info.outputs.pr_number }}');
158+
159+
// Get original PR details
160+
const { data: originalPR } = await github.rest.pulls.get({
161+
owner: context.repo.owner,
162+
repo: context.repo.repo,
163+
pull_number: prNumber,
164+
});
165+
166+
const title = `Cherry-pick #${prNumber} to ${targetBranch}: ${originalPR.title}`;
167+
const body = `This PR cherry-picks #${prNumber} to \`${targetBranch}\`.\n\n` +
168+
`**Original PR:** #${prNumber}\n` +
169+
`**Target branch:** \`${targetBranch}\`\n\n` +
170+
`---\n` +
171+
`_This PR was created automatically by the cherry-pick workflow._`;
172+
173+
const { data: pr } = await github.rest.pulls.create({
174+
owner: context.repo.owner,
175+
repo: context.repo.repo,
176+
title: title,
177+
body: body,
178+
head: cherryPickBranch,
179+
base: targetBranch,
180+
});
181+
182+
// Add a comment to the original PR
183+
await github.rest.issues.createComment({
184+
owner: context.repo.owner,
185+
repo: context.repo.repo,
186+
issue_number: prNumber,
187+
body: `✅ Cherry-pick PR created: #${pr.number}\n\n` +
188+
`**Target branch:** \`${targetBranch}\`\n` +
189+
`**Cherry-pick PR:** #${pr.number}`,
190+
});
191+
192+
console.log(`Created PR #${pr.number}`);

0 commit comments

Comments
 (0)