Skip to content

Commit 92a4df7

Browse files
committed
ci: Add Gemini conflict resolution to cherry-pick
This change integrates the Gemini API into the cherry-pick workflow to automatically resolve merge conflicts. When a cherry-pick fails, the gemini-cli action is invoked to attempt to resolve any conflicts. This reduces manual intervention for routine cherry-picks. Bug: 488071229
1 parent 02a03fd commit 92a4df7

File tree

1 file changed

+75
-22
lines changed

1 file changed

+75
-22
lines changed

.github/workflows/label-cherry-pick.yaml

Lines changed: 75 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ on:
66
- labeled
77
- closed
88

9+
permissions:
10+
contents: read
11+
912
jobs:
1013
prepare_branch_list:
1114
runs-on: ubuntu-latest
@@ -35,10 +38,10 @@ jobs:
3538
branches=("main" "26.eap" "26.android" "25.lts.1+" "24.lts.1+" "23.lts.1+" "22.lts.1+" "21.lts.1+" "20.lts.1+" "19.lts.1+" "rc_11" "COBALT_9")
3639
filtered_branches=()
3740
for branch in "${branches[@]}"; do
38-
if [[ $branch == $BASE_REF ]]; then
41+
if [[ $branch == ${BASE_REF} ]]; then
3942
continue
4043
fi
41-
if [[ ${labels[@]} =~ "cp-$branch" ]]; then
44+
if [[ "$labels" =~ "cp-$branch" ]]; then
4245
filtered_branches+=("$branch")
4346
fi
4447
done
@@ -48,18 +51,14 @@ jobs:
4851
cherry_pick:
4952
runs-on: ubuntu-latest
5053
permissions:
54+
contents: write
55+
pull-requests: write
5156
issues: write
5257
needs: prepare_branch_list
5358
if: needs.prepare_branch_list.outputs.target_branch != '[]'
5459
strategy:
5560
matrix:
5661
target_branch: ${{ fromJson(needs.prepare_branch_list.outputs.target_branch) }}
57-
env:
58-
ACCESS_TOKEN: ${{ secrets.CHERRY_PICK_TOKEN }}
59-
REPOSITORY: ${{ github.repository }}
60-
GITHUB_REF: ${{ github.ref }}
61-
MERGE_COMMIT_SHA: ${{ github.event.pull_request.merge_commit_sha }}
62-
CHERRY_PICK_BRANCH: cherry-pick-${{ matrix.target_branch }}-${{ github.event.pull_request.number }}
6362
steps:
6463
- name: Checkout repository
6564
uses: actions/checkout@v4
@@ -78,33 +77,71 @@ jobs:
7877
- name: Cherry pick merge commit
7978
id: cherry-pick
8079
continue-on-error: true
80+
env:
81+
GEMINI_API_KEY: ${{ secrets.GEMINI_ACTIONS_API_KEY }}
82+
MERGE_COMMIT_SHA: ${{ github.event.pull_request.merge_commit_sha }}
8183
run: |
8284
set -x
83-
git fetch origin ${{ matrix.target_branch }}
84-
8585
set +e
8686
# Select the first parent as the mainline tree. This is necessary if
8787
# the commit has multiple parents as the cherry-pick will fail otherwise.
8888
git cherry-pick -x --mainline=1 ${MERGE_COMMIT_SHA}
8989
RES=$?
9090
set -e
91+
conflicts=""
9192
if [ ${RES} -ne 0 ]; then
92-
# If the cherry pick failed due to a merge conflict we can
93-
# add the conflicting file and create the commit anyway.
94-
git add .
95-
git cherry-pick --continue
93+
conflicts=$(git diff --name-only --diff-filter=U)
94+
if [ -n "${conflicts}" ]; then
95+
# Stage and commit the changes (with conflicts) before Gemini touches them.
96+
git add .
97+
git commit -C ${MERGE_COMMIT_SHA} --no-verify
98+
fi
9699
fi
100+
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
101+
echo "conflicts<<$EOF" >> $GITHUB_OUTPUT
102+
echo "$conflicts" >> $GITHUB_OUTPUT
103+
echo "$EOF" >> $GITHUB_OUTPUT
97104
exit ${RES}
98105
106+
- name: Resolve Merge Conflicts with Gemini CLI
107+
if: steps.cherry-pick.outputs.conflicts != ''
108+
uses: google-github-actions/run-gemini-cli@v0
109+
with:
110+
gemini_api_key: ${{ secrets.GEMINI_ACTIONS_API_KEY }}
111+
prompt: |
112+
You are an expert software engineer dealing with a failed cherry pick.
113+
The most recent commit has the conflicts committed.
114+
Once the conflicts are resolved you must leave the files unstaged.
115+
Analyze what it's trying to achieve and fix the conflicts.
116+
Leave the files with the fixed conflicts unstaged.
117+
Files with conflicts:
118+
${{ steps.cherry-pick.outputs.conflicts }}
119+
120+
- name: Commit Resolution
121+
id: commit-resolution
122+
continue-on-error: true
123+
env:
124+
CONFLICTS: ${{ steps.cherry-pick.outputs.conflicts }}
125+
run: |
126+
set -x
127+
if [ -n "${CONFLICTS}" ]; then
128+
git add -u
129+
git commit -m "Conflicts resolved by Gemini"
130+
# Only output commit sha if commit succeeded.
131+
if [[ $? == 0 ]]; then
132+
echo "gemini_commit_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
133+
fi
134+
fi
135+
99136
- name: Create Pull Request
100137
id: create-pr
101138
continue-on-error: true
102139
uses: peter-evans/create-pull-request@2b011faafdcbc9ceb11414d64d0573f37c774b04 # v4.2.3
103140
with:
104-
token: ${{ secrets.CHERRY_PICK_TOKEN }}
141+
token: ${{ secrets.GITHUB_TOKEN }}
105142
draft: ${{ steps.cherry-pick.outcome == 'failure' }}
106143
base: ${{ matrix.target_branch }}
107-
branch: ${{ env.CHERRY_PICK_BRANCH }}
144+
branch: cherry-pick-${{ matrix.target_branch }}-${{ github.event.pull_request.number }}
108145
committer: GitHub Release Automation <github@google.com>
109146
reviewers: |
110147
${{ github.event.pull_request.user.login }}
@@ -117,23 +154,39 @@ jobs:
117154
118155
- name: Comment on failure
119156
uses: actions/github-script@v8
157+
env:
158+
REPOSITORY: ${{ github.repository }}
159+
NEW_PR_NUMBER: ${{ steps.create-pr.outputs.pull-request-number }}
160+
CHERRY_PICK_OUTCOME: ${{ steps.cherry-pick.outcome }}
161+
GEMINI_COMMIT_SHA: ${{ steps.commit-resolution.outputs.gemini_commit_sha }}
162+
CHERRY_PICK_BRANCH: cherry-pick-${{ matrix.target_branch }}-${{ github.event.pull_request.number }}
120163
with:
121-
github-token: ${{ secrets.CHERRY_PICK_TOKEN }}
164+
github-token: ${{ secrets.GITHUB_TOKEN }}
122165
script: |
123-
if ('${{ steps.create-pr.outputs.pull-request-number }}' == '') {
166+
const { NEW_PR_NUMBER, CHERRY_PICK_OUTCOME, REPOSITORY, CHERRY_PICK_BRANCH, GEMINI_COMMIT_SHA } = process.env;
167+
const logFooter = `Check the log at ${context.serverUrl}/${REPOSITORY}/actions/runs/${context.runId} for details.`;
168+
if (NEW_PR_NUMBER == '') {
124169
// Comment on the originating PR if creating a cherry pick PR failed.
125170
github.rest.issues.createComment({
126171
issue_number: context.payload.number,
127172
owner: context.repo.owner,
128173
repo: context.repo.repo,
129-
body: '> [!CAUTION]\n> Creating the cherry pick PR failed! Check the log at ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} for details.'
174+
body: `> [!CAUTION]\n> Creating the cherry pick PR failed!\n\n${logFooter}`
130175
});
131-
} else if ('${{ steps.cherry-pick.outcome }}' == 'failure') {
176+
} else if (CHERRY_PICK_OUTCOME == 'failure') {
132177
// Comment on the new PR if the cherry pick failed.
178+
const cautionHeader = '> [!CAUTION]\n> There were merge conflicts while cherry picking!';
179+
let geminiNotice;
180+
if (GEMINI_COMMIT_SHA) {
181+
geminiNotice = `\n\nGemini has attempted to fix the conflicts in [this commit](${context.serverUrl}/${REPOSITORY}/pull/${NEW_PR_NUMBER}/commits/${GEMINI_COMMIT_SHA}). Please review carefully before merging.`;
182+
} else {
183+
geminiNotice = '';
184+
}
185+
const checkoutInstructions = `To checkout the cherry-pick branch, run:\n\`\`\`\ngit fetch origin ${CHERRY_PICK_BRANCH}; git checkout FETCH_HEAD\n\`\`\``;
133186
github.rest.issues.createComment({
134-
issue_number: '${{ steps.create-pr.outputs.pull-request-number }}',
187+
issue_number: parseInt(NEW_PR_NUMBER),
135188
owner: context.repo.owner,
136189
repo: context.repo.repo,
137-
body: '![MERGE CONFLICT CAT](https://services.google.com/fh/files/misc/merge_conflict_cat.png)\n> [!CAUTION]\n> There were merge conflicts while cherry picking! Check out [${{ env.CHERRY_PICK_BRANCH }}](${{ github.repository }}/tree/${{ env.CHERRY_PICK_BRANCH }}) and fix the conflicts before proceeding. Check the log at ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} for details.'
190+
body: `![MERGE CONFLICT CAT](https://services.google.com/fh/files/misc/merge_conflict_cat.png)\n${cautionHeader}${geminiNotice}\n\n${checkoutInstructions}\n\n${logFooter}`
138191
});
139192
}

0 commit comments

Comments
 (0)