Skip to content

Commit a487f11

Browse files
committed
ci: auto-resolve merge conflicts in beta sync using opencode
When merging PRs into the beta branch, the sync script now attempts to automatically resolve merge conflicts using opencode before failing. This reduces manual intervention needed for beta releases when multiple PRs have overlapping changes.
1 parent 637059a commit a487f11

File tree

2 files changed

+66
-13
lines changed

2 files changed

+66
-13
lines changed

.github/workflows/beta.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ jobs:
2727
opencode-app-id: ${{ vars.OPENCODE_APP_ID }}
2828
opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }}
2929

30+
- name: Install OpenCode
31+
run: bun i -g opencode-ai
32+
3033
- name: Sync beta branch
3134
env:
3235
GH_TOKEN: ${{ steps.setup-git-committer.outputs.token }}
36+
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
3337
run: bun script/beta.ts

script/beta.ts

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,52 @@ Please resolve this issue to include this PR in the next beta release.`
3030
}
3131
}
3232

33+
async function conflicts() {
34+
const out = await $`git diff --name-only --diff-filter=U`.text().catch(() => "")
35+
return out
36+
.split("\n")
37+
.map((x) => x.trim())
38+
.filter(Boolean)
39+
}
40+
41+
async function cleanup() {
42+
try {
43+
await $`git merge --abort`
44+
} catch {}
45+
try {
46+
await $`git checkout -- .`
47+
} catch {}
48+
try {
49+
await $`git clean -fd`
50+
} catch {}
51+
}
52+
53+
async function fix(pr: PR, files: string[]) {
54+
console.log(` Trying to auto-resolve ${files.length} conflict(s) with opencode...`)
55+
const prompt = [
56+
`Resolve the current git merge conflicts while merging PR #${pr.number} into the beta branch.`,
57+
`Only touch these files: ${files.join(", ")}.`,
58+
"Keep the merge in progress, do not abort the merge, and do not create a commit.",
59+
"When done, leave the working tree with no unmerged files.",
60+
].join("\n")
61+
62+
try {
63+
await $`opencode run ${prompt}`
64+
} catch (err) {
65+
console.log(` opencode failed: ${err}`)
66+
return false
67+
}
68+
69+
const left = await conflicts()
70+
if (left.length > 0) {
71+
console.log(` Conflicts remain: ${left.join(", ")}`)
72+
return false
73+
}
74+
75+
console.log(" Conflicts resolved with opencode")
76+
return true
77+
}
78+
3379
async function main() {
3480
console.log("Fetching open PRs with beta label...")
3581

@@ -69,19 +115,22 @@ async function main() {
69115
try {
70116
await $`git merge --no-commit --no-ff pr/${pr.number}`
71117
} catch {
72-
console.log(" Failed to merge (conflicts)")
73-
try {
74-
await $`git merge --abort`
75-
} catch {}
76-
try {
77-
await $`git checkout -- .`
78-
} catch {}
79-
try {
80-
await $`git clean -fd`
81-
} catch {}
82-
failed.push({ number: pr.number, title: pr.title, reason: "Merge conflicts" })
83-
await commentOnPR(pr.number, "Merge conflicts with dev branch")
84-
continue
118+
const files = await conflicts()
119+
if (files.length > 0) {
120+
console.log(" Failed to merge (conflicts)")
121+
if (!(await fix(pr, files))) {
122+
await cleanup()
123+
failed.push({ number: pr.number, title: pr.title, reason: "Merge conflicts" })
124+
await commentOnPR(pr.number, "Merge conflicts with dev branch")
125+
continue
126+
}
127+
} else {
128+
console.log(" Failed to merge")
129+
await cleanup()
130+
failed.push({ number: pr.number, title: pr.title, reason: "Merge failed" })
131+
await commentOnPR(pr.number, "Merge failed")
132+
continue
133+
}
85134
}
86135

87136
try {

0 commit comments

Comments
 (0)