autopilot-code 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -3
- package/package.json +1 -1
- package/scripts/run_autopilot.py +293 -149
- package/templates/autopilot.json +0 -1
- package/scripts/run_opencode_issue.sh +0 -690
|
@@ -1,690 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# =============================================================================
|
|
3
|
-
# DEPRECATED: This script is deprecated and will be removed in a future version.
|
|
4
|
-
#
|
|
5
|
-
# The functionality has been replaced by Python IssueRunner module at:
|
|
6
|
-
# scripts/issue_runner/
|
|
7
|
-
#
|
|
8
|
-
# To use the new runner, ensure "useNewRunner" is not set to false in your
|
|
9
|
-
# autopilot.json config (it defaults to true).
|
|
10
|
-
#
|
|
11
|
-
# This script is maintained only for backward compatibility.
|
|
12
|
-
# =============================================================================
|
|
13
|
-
set -euo pipefail
|
|
14
|
-
|
|
15
|
-
REPO_DIR="$1"
|
|
16
|
-
ISSUE_NUMBER="$2"
|
|
17
|
-
|
|
18
|
-
if [[ -z "$REPO_DIR" || -z "$ISSUE_NUMBER" ]]; then
|
|
19
|
-
echo "Usage: $0 <repoDir> <issueNumber>" >&2
|
|
20
|
-
exit 1
|
|
21
|
-
fi
|
|
22
|
-
|
|
23
|
-
cd "$REPO_DIR"
|
|
24
|
-
|
|
25
|
-
# Read config (prefer jq, fallback to python3)
|
|
26
|
-
if command -v jq >/dev/null 2>&1; then
|
|
27
|
-
REPO=$(jq -r '.repo' < .autopilot/autopilot.json)
|
|
28
|
-
AUTO_MERGE=$(jq -r '.autoMerge // true' < .autopilot/autopilot.json)
|
|
29
|
-
MERGE_METHOD=$(jq -r '.mergeMethod // "squash"' < .autopilot/autopilot.json)
|
|
30
|
-
AUTO_RESOLVE_CONFLICTS=$(jq -r '.autoResolveConflicts // true' < .autopilot/autopilot.json)
|
|
31
|
-
CONFLICT_RESOLUTION_MAX_ATTEMPTS=$(jq -r '.conflictResolutionMaxAttempts // 3' < .autopilot/autopilot.json)
|
|
32
|
-
AUTO_FIX_CHECKS=$(jq -r '.autoFixChecks // true' < .autopilot/autopilot.json)
|
|
33
|
-
AUTO_FIX_CHECKS_MAX_ATTEMPTS=$(jq -r '.autoFixChecksMaxAttempts // 3' < .autopilot/autopilot.json)
|
|
34
|
-
ALLOWED_USERS=$(jq -r '.allowedMergeUsers[]' < .autopilot/autopilot.json 2>/dev/null || true)
|
|
35
|
-
AGENT_PATH=$(jq -r '.agentPath // ""' < .autopilot/autopilot.json)
|
|
36
|
-
ENABLE_PLANNING_STEP=$(jq -r '.enablePlanningStep // true' < .autopilot/autopilot.json)
|
|
37
|
-
else
|
|
38
|
-
REPO=$(python3 -c 'import json; print(json.load(open(".autopilot/autopilot.json"))["repo"])')
|
|
39
|
-
AUTO_MERGE=$(python3 -c 'import json; print(json.load(open(".autopilot/autopilot.json")).get("autoMerge", True))')
|
|
40
|
-
MERGE_METHOD=$(python3 -c 'import json; print(json.load(open(".autopilot/autopilot.json")).get("mergeMethod", "squash"))')
|
|
41
|
-
AUTO_RESOLVE_CONFLICTS=$(python3 -c 'import json; print(json.load(open(".autopilot/autopilot.json")).get("autoResolveConflicts", True))')
|
|
42
|
-
CONFLICT_RESOLUTION_MAX_ATTEMPTS=$(python3 -c 'import json; print(json.load(open(".autopilot/autopilot.json")).get("conflictResolutionMaxAttempts", 3))')
|
|
43
|
-
AUTO_FIX_CHECKS=$(python3 -c 'import json; print(json.load(open(".autopilot/autopilot.json")).get("autoFixChecks", True))')
|
|
44
|
-
AUTO_FIX_CHECKS_MAX_ATTEMPTS=$(python3 -c 'import json; print(json.load(open(".autopilot/autopilot.json")).get("autoFixChecksMaxAttempts", 3))')
|
|
45
|
-
ALLOWED_USERS=$(python3 -c 'import json,sys; users=json.load(open(".autopilot/autopilot.json")).get("allowedMergeUsers", []); print("\n".join(users))' 2>/dev/null || true)
|
|
46
|
-
AGENT_PATH=$(python3 -c 'import json; print(json.load(open(".autopilot/autopilot.json")).get("agentPath", ""))' 2>/dev/null || true)
|
|
47
|
-
ENABLE_PLANNING_STEP=$(python3 -c 'import json; print(json.load(open(".autopilot/autopilot.json")).get("enablePlanningStep", True))' 2>/dev/null || echo "true")
|
|
48
|
-
fi
|
|
49
|
-
|
|
50
|
-
# Find opencode binary - check config, PATH, then common locations
|
|
51
|
-
find_opencode() {
|
|
52
|
-
# 1. Config-specified path
|
|
53
|
-
if [[ -n "$AGENT_PATH" ]] && [[ -x "$AGENT_PATH" ]]; then
|
|
54
|
-
echo "$AGENT_PATH"
|
|
55
|
-
return
|
|
56
|
-
fi
|
|
57
|
-
|
|
58
|
-
# 2. Already in PATH
|
|
59
|
-
if command -v opencode >/dev/null 2>&1; then
|
|
60
|
-
command -v opencode
|
|
61
|
-
return
|
|
62
|
-
fi
|
|
63
|
-
|
|
64
|
-
# 3. Common nvm locations
|
|
65
|
-
for node_dir in "$HOME/.nvm/versions/node"/*/bin; do
|
|
66
|
-
if [[ -x "$node_dir/opencode" ]]; then
|
|
67
|
-
echo "$node_dir/opencode"
|
|
68
|
-
return
|
|
69
|
-
fi
|
|
70
|
-
done
|
|
71
|
-
|
|
72
|
-
# 4. Other common locations
|
|
73
|
-
for path in "$HOME/.local/bin/opencode" "/usr/local/bin/opencode" "$HOME/.npm-global/bin/opencode"; do
|
|
74
|
-
if [[ -x "$path" ]]; then
|
|
75
|
-
echo "$path"
|
|
76
|
-
return
|
|
77
|
-
fi
|
|
78
|
-
done
|
|
79
|
-
|
|
80
|
-
# Not found
|
|
81
|
-
return 1
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
OPENCODE_BIN=$(find_opencode) || {
|
|
85
|
-
echo "[run_opencode_issue.sh] ERROR: opencode not found. Set 'agentPath' in autopilot.json or ensure opencode is installed." >&2
|
|
86
|
-
exit 1
|
|
87
|
-
}
|
|
88
|
-
echo "[run_opencode_issue.sh] Using opencode: $OPENCODE_BIN"
|
|
89
|
-
|
|
90
|
-
WORKTREE="/tmp/autopilot-issue-$ISSUE_NUMBER"
|
|
91
|
-
BRANCH="autopilot/issue-$ISSUE_NUMBER"
|
|
92
|
-
|
|
93
|
-
echo "[run_opencode_issue.sh] repo=$REPO issue=$ISSUE_NUMBER worktree=$WORKTREE"
|
|
94
|
-
|
|
95
|
-
# 1. Create or reuse git worktree from main
|
|
96
|
-
# First, prune any stale worktrees (e.g., if directory was deleted but still registered)
|
|
97
|
-
git worktree prune 2>/dev/null || true
|
|
98
|
-
|
|
99
|
-
if [[ -d "$WORKTREE" ]]; then
|
|
100
|
-
echo "[run_opencode_issue.sh] Reusing existing worktree: $WORKTREE"
|
|
101
|
-
else
|
|
102
|
-
# If branch exists already, add worktree from that branch; otherwise create from main.
|
|
103
|
-
if git show-ref --verify --quiet "refs/heads/$BRANCH"; then
|
|
104
|
-
git worktree add "$WORKTREE" "$BRANCH"
|
|
105
|
-
else
|
|
106
|
-
git worktree add "$WORKTREE" -b "$BRANCH" main
|
|
107
|
-
fi
|
|
108
|
-
fi
|
|
109
|
-
|
|
110
|
-
# 1.5. Install dependencies if package.json exists (for Node.js projects)
|
|
111
|
-
if [[ -f "$WORKTREE/package.json" ]]; then
|
|
112
|
-
echo "[run_opencode_issue.sh] Installing npm dependencies..."
|
|
113
|
-
(cd "$WORKTREE" && npm install --silent) || true
|
|
114
|
-
fi
|
|
115
|
-
|
|
116
|
-
# 2. Fetch issue title/body
|
|
117
|
-
ISSUE_JSON=$(gh issue view "$ISSUE_NUMBER" --repo "$REPO" --json title,body)
|
|
118
|
-
if command -v jq >/dev/null 2>&1; then
|
|
119
|
-
ISSUE_TITLE=$(echo "$ISSUE_JSON" | jq -r '.title')
|
|
120
|
-
ISSUE_BODY=$(echo "$ISSUE_JSON" | jq -r '.body')
|
|
121
|
-
else
|
|
122
|
-
ISSUE_TITLE=$(python3 -c 'import json,sys; d=json.load(sys.stdin); print(d["title"])' <<<"$ISSUE_JSON")
|
|
123
|
-
ISSUE_BODY=$(python3 -c 'import json,sys; d=json.load(sys.stdin); print(d.get("body") or "")' <<<"$ISSUE_JSON")
|
|
124
|
-
fi
|
|
125
|
-
|
|
126
|
-
# 3. Build prompt
|
|
127
|
-
PROMPT="Please implement the following GitHub issue.
|
|
128
|
-
|
|
129
|
-
Issue #$ISSUE_NUMBER: $ISSUE_TITLE
|
|
130
|
-
|
|
131
|
-
$ISSUE_BODY
|
|
132
|
-
|
|
133
|
-
Work rules:
|
|
134
|
-
- Make the necessary code changes.
|
|
135
|
-
- Commit with message: \"autopilot: work for issue #$ISSUE_NUMBER\".
|
|
136
|
-
- Push your changes to the remote branch $BRANCH.
|
|
137
|
-
- If the issue is a simple file-addition, just do it directly (no extra refactors)."
|
|
138
|
-
|
|
139
|
-
# 3.5. Planning step - analyze and plan before implementation
|
|
140
|
-
if [[ "$ENABLE_PLANNING_STEP" == "true" ]]; then
|
|
141
|
-
echo "[run_opencode_issue.sh] Running planning/analysis step..."
|
|
142
|
-
|
|
143
|
-
PLANNING_PROMPT="Please analyze the following GitHub issue and provide a detailed implementation plan. Do NOT make any code changes yet - only analyze and plan.
|
|
144
|
-
|
|
145
|
-
Issue #$ISSUE_NUMBER: $ISSUE_TITLE
|
|
146
|
-
|
|
147
|
-
$ISSUE_BODY
|
|
148
|
-
|
|
149
|
-
Your task:
|
|
150
|
-
1. Analyze the issue requirements thoroughly
|
|
151
|
-
2. Explore the codebase to understand the relevant components
|
|
152
|
-
3. Identify the files that need to be modified or created
|
|
153
|
-
4. Outline the step-by-step approach for implementing this change
|
|
154
|
-
5. Note any potential issues, dependencies, or risks
|
|
155
|
-
6. List any assumptions you're making
|
|
156
|
-
|
|
157
|
-
Provide your analysis and plan in a clear, structured format. Focus on WHAT needs to be done and HOW you'll approach it."
|
|
158
|
-
|
|
159
|
-
# Run opencode to generate the plan
|
|
160
|
-
cd "$WORKTREE"
|
|
161
|
-
RAW_OUTPUT=$("$OPENCODE_BIN" run "$PLANNING_PROMPT" 2>&1 || echo "Planning failed")
|
|
162
|
-
|
|
163
|
-
# Filter out noise - keep only the final plan output
|
|
164
|
-
# Remove tool calls, internal logs, and system messages
|
|
165
|
-
PLAN_OUTPUT=$(echo "$RAW_OUTPUT" | grep -vE '^\s*(bash|read|write|edit|grep|glob|question|task|webfetch|google_search|morph-mcp|todowrite|todoread|skill|search)\s' | \
|
|
166
|
-
grep -vE '^\s*(Running|Executing|File:|Offset:|Limit:|parameters:|pattern:|path:|include:)' | \
|
|
167
|
-
grep -vE '^<[a-z_]+>|^<\/[a-z_]+>|^system:|^user:|^assistant:|^model:' | \
|
|
168
|
-
grep -vE '^\s*\$|^\s*\[.*\]|^Using|Called|Invoked' | \
|
|
169
|
-
grep -vE '^\s*$|^(command|description|filePath|oldString|newString|questions|header|options):' | \
|
|
170
|
-
tail -100)
|
|
171
|
-
|
|
172
|
-
# If filtering removed everything, use a fallback
|
|
173
|
-
if [[ -z "$PLAN_OUTPUT" ]]; then
|
|
174
|
-
PLAN_OUTPUT="Unable to extract a clean plan. The raw output contained excessive tool logs."
|
|
175
|
-
fi
|
|
176
|
-
|
|
177
|
-
# Post the plan as a comment on the issue
|
|
178
|
-
PLAN_COMMENT="## 📋 Implementation Plan
|
|
179
|
-
|
|
180
|
-
I've analyzed the issue and codebase. Here's my planned approach:
|
|
181
|
-
|
|
182
|
-
\`\`\`
|
|
183
|
-
$PLAN_OUTPUT
|
|
184
|
-
\`\`\`
|
|
185
|
-
|
|
186
|
-
I will now proceed with implementation based on this plan. If you'd like me to adjust my approach, please add a comment with feedback."
|
|
187
|
-
|
|
188
|
-
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "$PLAN_COMMENT" || true
|
|
189
|
-
echo "[run_opencode_issue.sh] Planning step completed and posted to issue."
|
|
190
|
-
fi
|
|
191
|
-
|
|
192
|
-
# 4. Run opencode inside worktree
|
|
193
|
-
cd "$WORKTREE"
|
|
194
|
-
"$OPENCODE_BIN" run "$PROMPT"
|
|
195
|
-
|
|
196
|
-
# 4.5. Comment that implementation is complete
|
|
197
|
-
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "✅ Implementation complete.
|
|
198
|
-
|
|
199
|
-
I've finished implementing the solution. Now committing changes and creating PR..." || true
|
|
200
|
-
|
|
201
|
-
# 5. Commit any changes OpenCode made
|
|
202
|
-
if [[ -n "$(git status --porcelain)" ]]; then
|
|
203
|
-
git add -A
|
|
204
|
-
git commit -m "autopilot: work for issue #$ISSUE_NUMBER"
|
|
205
|
-
|
|
206
|
-
# Comment with commit info
|
|
207
|
-
COMMIT_SHA=$(git rev-parse --short HEAD)
|
|
208
|
-
CHANGES_COUNT=$(git diff-tree --no-commit-id --name-only -r HEAD | wc -l | xargs)
|
|
209
|
-
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "📝 Changes committed.
|
|
210
|
-
|
|
211
|
-
- Commit: \`$COMMIT_SHA\`
|
|
212
|
-
- Files changed: $CHANGES_COUNT
|
|
213
|
-
- Branch: \`$BRANCH\`
|
|
214
|
-
|
|
215
|
-
Next step: pushing to remote..." || true
|
|
216
|
-
else
|
|
217
|
-
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "ℹ️ No changes were made by the AI agent. This might indicate the issue is already resolved or requires manual intervention." || true
|
|
218
|
-
fi
|
|
219
|
-
|
|
220
|
-
# 6. Ensure branch is pushed (no-op if already up to date)
|
|
221
|
-
git push -u origin "$BRANCH" || true
|
|
222
|
-
|
|
223
|
-
# 6.5. Comment after push
|
|
224
|
-
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "📤 Changes pushed to remote branch \`$BRANCH\`.
|
|
225
|
-
|
|
226
|
-
Now creating pull request..." || true
|
|
227
|
-
|
|
228
|
-
# 7. Create PR if one doesn't already exist
|
|
229
|
-
PR_URL=""
|
|
230
|
-
if gh pr view --repo "$REPO" --head "$BRANCH" --json url --jq .url >/dev/null 2>&1; then
|
|
231
|
-
PR_URL=$(gh pr view --repo "$REPO" --head "$BRANCH" --json url --jq .url)
|
|
232
|
-
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "📋 PR already exists: $PR_URL" || true
|
|
233
|
-
else
|
|
234
|
-
PR_URL=$(gh pr create --repo "$REPO" --title "Autopilot: Issue #$ISSUE_NUMBER" --body "Closes #$ISSUE_NUMBER" --base main --head "$BRANCH")
|
|
235
|
-
|
|
236
|
-
# Add short delay after PR creation to allow GitHub to calculate merge state
|
|
237
|
-
sleep 10
|
|
238
|
-
fi
|
|
239
|
-
|
|
240
|
-
# 8. Comment on issue with PR URL (best-effort)
|
|
241
|
-
if [[ -n "$PR_URL" ]]; then
|
|
242
|
-
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "🎉 PR created: $PR_URL
|
|
243
|
-
|
|
244
|
-
Autopilot has completed the initial implementation. The PR is now open and ready for review." || true
|
|
245
|
-
fi
|
|
246
|
-
|
|
247
|
-
# 8.5. Handle merge conflicts if auto-resolve is enabled
|
|
248
|
-
if [[ "$AUTO_RESOLVE_CONFLICTS" == "true" ]] && [[ -n "$PR_URL" ]]; then
|
|
249
|
-
echo "[run_opencode_issue.sh] Checking for merge conflicts..."
|
|
250
|
-
|
|
251
|
-
# Get PR merge status
|
|
252
|
-
MERGE_STATUS=$(gh pr view --repo "$REPO" --head "$BRANCH" --json mergeable,mergeStateStatus --jq '{mergeable, mergeStateStatus}' 2>/dev/null || echo '{"mergeable":"UNKNOWN"}')
|
|
253
|
-
|
|
254
|
-
if command -v jq >/dev/null 2>&1; then
|
|
255
|
-
IS_CONFLICTING=$(echo "$MERGE_STATUS" | jq -r 'if .mergeable == "CONFLICTING" or .mergeStateStatus == "DIRTY" then "true" else "false" end')
|
|
256
|
-
else
|
|
257
|
-
IS_CONFLICTING=$(python3 -c 'import json,sys; d=json.load(sys.stdin); print("true" if d.get("mergeable") == "CONFLICTING" or d.get("mergeStateStatus") == "DIRTY" else "false")' <<<"$MERGE_STATUS")
|
|
258
|
-
fi
|
|
259
|
-
|
|
260
|
-
if [[ "$IS_CONFLICTING" == "true" ]]; then
|
|
261
|
-
echo "[run_opencode_issue.sh] PR has merge conflicts. Attempting auto-resolution..."
|
|
262
|
-
|
|
263
|
-
RESOLVE_ATTEMPT=0
|
|
264
|
-
MAX_ATTEMPTS=$((CONFLICT_RESOLUTION_MAX_ATTEMPTS + 0))
|
|
265
|
-
|
|
266
|
-
while [[ $RESOLVE_ATTEMPT -lt $MAX_ATTEMPTS ]]; do
|
|
267
|
-
RESOLVE_ATTEMPT=$((RESOLVE_ATTEMPT + 1))
|
|
268
|
-
echo "[run_opencode_issue.sh] Conflict resolution attempt $RESOLVE_ATTEMPT/$MAX_ATTEMPTS"
|
|
269
|
-
|
|
270
|
-
# Fetch latest main
|
|
271
|
-
echo "[run_opencode_issue.sh] Fetching latest main..."
|
|
272
|
-
git fetch origin main || true
|
|
273
|
-
|
|
274
|
-
# Checkout branch and try to rebase
|
|
275
|
-
echo "[run_opencode_issue.sh] Rebasing branch onto main..."
|
|
276
|
-
cd "$WORKTREE"
|
|
277
|
-
if git rebase origin/main; then
|
|
278
|
-
echo "[run_opencode_issue.sh] Rebase successful."
|
|
279
|
-
|
|
280
|
-
# Push rebased changes
|
|
281
|
-
git push -f origin "$BRANCH" || true
|
|
282
|
-
|
|
283
|
-
# Check if conflicts are resolved
|
|
284
|
-
MERGE_STATUS=$(gh pr view --repo "$REPO" --head "$BRANCH" --json mergeable,mergeStateStatus --jq '{mergeable, mergeStateStatus}' 2>/dev/null || echo '{"mergeable":"UNKNOWN"}')
|
|
285
|
-
|
|
286
|
-
if command -v jq >/dev/null 2>&1; then
|
|
287
|
-
IS_CONFLICTING=$(echo "$MERGE_STATUS" | jq -r 'if .mergeable == "CONFLICTING" or .mergeStateStatus == "DIRTY" then "true" else "false" end')
|
|
288
|
-
else
|
|
289
|
-
IS_CONFLICTING=$(python3 -c 'import json,sys; d=json.load(sys.stdin); print("true" if d.get("mergeable") == "CONFLICTING" or d.get("mergeStateStatus") == "DIRTY" else "false")' <<<"$MERGE_STATUS")
|
|
290
|
-
fi
|
|
291
|
-
|
|
292
|
-
if [[ "$IS_CONFLICTING" == "false" ]]; then
|
|
293
|
-
echo "[run_opencode_issue.sh] Conflicts resolved successfully."
|
|
294
|
-
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "✅ Autopilot successfully resolved merge conflicts." || true
|
|
295
|
-
break
|
|
296
|
-
fi
|
|
297
|
-
else
|
|
298
|
-
# Rebase failed - there are conflicts to resolve
|
|
299
|
-
echo "[run_opencode_issue.sh] Rebase encountered conflicts. Attempting resolution with agent..."
|
|
300
|
-
|
|
301
|
-
# Abort the rebase to get back to a clean state
|
|
302
|
-
git rebase --abort 2>/dev/null || true
|
|
303
|
-
|
|
304
|
-
# Try merging main instead (may be easier to resolve)
|
|
305
|
-
if git merge origin/main; then
|
|
306
|
-
echo "[run_opencode_issue.sh] Merge successful."
|
|
307
|
-
git push -f origin "$BRANCH" || true
|
|
308
|
-
|
|
309
|
-
# Check if conflicts are resolved
|
|
310
|
-
MERGE_STATUS=$(gh pr view --repo "$REPO" --head "$BRANCH" --json mergeable,mergeStateStatus --jq '{mergeable, mergeStateStatus}' 2>/dev/null || echo '{"mergeable":"UNKNOWN"}')
|
|
311
|
-
|
|
312
|
-
if command -v jq >/dev/null 2>&1; then
|
|
313
|
-
IS_CONFLICTING=$(echo "$MERGE_STATUS" | jq -r 'if .mergeable == "CONFLICTING" or .mergeStateStatus == "DIRTY" then "true" else "false" end')
|
|
314
|
-
else
|
|
315
|
-
IS_CONFLICTING=$(python3 -c 'import json,sys; d=json.load(sys.stdin); print("true" if d.get("mergeable") == "CONFLICTING" or d.get("mergeStateStatus") == "DIRTY" else "false")' <<<"$MERGE_STATUS")
|
|
316
|
-
fi
|
|
317
|
-
|
|
318
|
-
if [[ "$IS_CONFLICTING" == "false" ]]; then
|
|
319
|
-
echo "[run_opencode_issue.sh] Conflicts resolved successfully."
|
|
320
|
-
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "✅ Autopilot successfully resolved merge conflicts." || true
|
|
321
|
-
break
|
|
322
|
-
fi
|
|
323
|
-
else
|
|
324
|
-
# Merge also has conflicts - abort and try to resolve with agent
|
|
325
|
-
git merge --abort 2>/dev/null || true
|
|
326
|
-
|
|
327
|
-
echo "[run_opencode_issue.sh] Attempting to resolve conflicts with opencode agent..."
|
|
328
|
-
|
|
329
|
-
# Use opencode to resolve conflicts
|
|
330
|
-
CONFLICT_PROMPT="This PR has merge conflicts. Please resolve them by examining the conflicted files and making the necessary edits to resolve the conflicts. Follow these steps:
|
|
331
|
-
1. Run 'git status' to see all conflicted files
|
|
332
|
-
2. For each conflicted file, examine the conflict markers (<<<<<<<, =======, >>>>>>>)
|
|
333
|
-
3. Resolve the conflicts by choosing the appropriate code
|
|
334
|
-
4. Stage the resolved files with 'git add <file>'
|
|
335
|
-
5. Commit the resolved files if needed
|
|
336
|
-
6. The goal is to make the branch mergeable with main
|
|
337
|
-
|
|
338
|
-
After resolving all conflicts, report the files that were resolved."
|
|
339
|
-
|
|
340
|
-
if "$OPENCODE_BIN" run "$CONFLICT_PROMPT"; then
|
|
341
|
-
# Check if there are still conflicts
|
|
342
|
-
if [[ -z "$(git diff --name-only --diff-filter=U)" ]]; then
|
|
343
|
-
echo "[run_opencode_issue.sh] Conflicts resolved by agent."
|
|
344
|
-
|
|
345
|
-
# Complete the merge or rebase
|
|
346
|
-
git add -A
|
|
347
|
-
|
|
348
|
-
# If we were in the middle of a merge, complete it
|
|
349
|
-
if [[ -f ".git/MERGE_HEAD" ]]; then
|
|
350
|
-
git commit --no-edit || true
|
|
351
|
-
fi
|
|
352
|
-
|
|
353
|
-
# Push resolved changes
|
|
354
|
-
git push -f origin "$BRANCH" || true
|
|
355
|
-
|
|
356
|
-
# Check if conflicts are resolved
|
|
357
|
-
MERGE_STATUS=$(gh pr view --repo "$REPO" --head "$BRANCH" --json mergeable,mergeStateStatus --jq '{mergeable, mergeStateStatus}' 2>/dev/null || echo '{"mergeable":"UNKNOWN"}')
|
|
358
|
-
|
|
359
|
-
if command -v jq >/dev/null 2>&1; then
|
|
360
|
-
IS_CONFLICTING=$(echo "$MERGE_STATUS" | jq -r 'if .mergeable == "CONFLICTING" or .mergeStateStatus == "DIRTY" then "true" else "false" end')
|
|
361
|
-
else
|
|
362
|
-
IS_CONFLICTING=$(python3 -c 'import json,sys; d=json.load(sys.stdin); print("true" if d.get("mergeable") == "CONFLICTING" or d.get("mergeStateStatus") == "DIRTY" else "false")' <<<"$MERGE_STATUS")
|
|
363
|
-
fi
|
|
364
|
-
|
|
365
|
-
if [[ "$IS_CONFLICTING" == "false" ]]; then
|
|
366
|
-
echo "[run_opencode_issue.sh] Conflicts resolved successfully."
|
|
367
|
-
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "✅ Autopilot successfully resolved merge conflicts." || true
|
|
368
|
-
break
|
|
369
|
-
fi
|
|
370
|
-
fi
|
|
371
|
-
fi
|
|
372
|
-
fi
|
|
373
|
-
fi
|
|
374
|
-
|
|
375
|
-
# If we're here, resolution failed and we should try again
|
|
376
|
-
echo "[run_opencode_issue.sh] Conflict resolution attempt $RESOLVE_ATTEMPT failed."
|
|
377
|
-
|
|
378
|
-
if [[ $RESOLVE_ATTEMPT -ge $MAX_ATTEMPTS ]]; then
|
|
379
|
-
echo "[run_opencode_issue.sh] Failed to resolve conflicts after $MAX_ATTEMPTS attempts."
|
|
380
|
-
|
|
381
|
-
# Mark issue as blocked
|
|
382
|
-
if command -v jq >/dev/null 2>&1; then
|
|
383
|
-
LABEL_BLOCKED=$(jq -r '.issueLabels.blocked' < .autopilot/autopilot.json)
|
|
384
|
-
else
|
|
385
|
-
LABEL_BLOCKED=$(python3 -c 'import json; print(json.load(open(".autopilot/autopilot.json"))["issueLabels"]["blocked"])')
|
|
386
|
-
fi
|
|
387
|
-
|
|
388
|
-
BLOCKED_MSG="❌ Autopilot failed to resolve merge conflicts after $MAX_ATTEMPTS attempts.\n\nThe PR will remain open but autopilot has stopped attempting to resolve conflicts.\n\nTo resolve manually:\n1. Checkout the branch: \`git checkout $BRANCH\`\n2. Fetch and rebase main: \`git fetch origin main && git rebase origin/main\`\n3. Resolve conflicts as needed\n4. Push resolved changes: \`git push -f origin $BRANCH\`\n\nOnce resolved, autopilot can attempt to merge the PR."
|
|
389
|
-
|
|
390
|
-
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "$BLOCKED_MSG" || true
|
|
391
|
-
gh issue edit "$ISSUE_NUMBER" --repo "$REPO" --add-label "$LABEL_BLOCKED" || true
|
|
392
|
-
|
|
393
|
-
exit 1
|
|
394
|
-
fi
|
|
395
|
-
|
|
396
|
-
# Wait a bit before next attempt
|
|
397
|
-
sleep 5
|
|
398
|
-
done
|
|
399
|
-
fi
|
|
400
|
-
fi
|
|
401
|
-
|
|
402
|
-
# 9. Auto-merge if enabled and user is allowed
|
|
403
|
-
if [[ "$AUTO_MERGE" == "true" ]] && [[ -n "$PR_URL" ]]; then
|
|
404
|
-
# Get authenticated GitHub user
|
|
405
|
-
AUTH_USER=$(gh api user --jq .login)
|
|
406
|
-
|
|
407
|
-
# Check if user is in allowed list
|
|
408
|
-
USER_ALLOWED=false
|
|
409
|
-
if [[ -n "$ALLOWED_USERS" ]]; then
|
|
410
|
-
while IFS= read -r user; do
|
|
411
|
-
if [[ "$AUTH_USER" == "$user" ]]; then
|
|
412
|
-
USER_ALLOWED=true
|
|
413
|
-
break
|
|
414
|
-
fi
|
|
415
|
-
done <<< "$ALLOWED_USERS"
|
|
416
|
-
fi
|
|
417
|
-
|
|
418
|
-
if [[ "$USER_ALLOWED" == "true" ]]; then
|
|
419
|
-
echo "[run_opencode_issue.sh] Auto-merge enabled. Waiting for PR checks to pass..."
|
|
420
|
-
|
|
421
|
-
# Comment that we're waiting for checks
|
|
422
|
-
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "⏳ Waiting for PR checks to complete...
|
|
423
|
-
|
|
424
|
-
I'm monitoring the CI checks and will auto-merge once they pass. This may take a few minutes." || true
|
|
425
|
-
|
|
426
|
-
# Initialize auto-fix attempt counter
|
|
427
|
-
FIX_ATTEMPT=0
|
|
428
|
-
|
|
429
|
-
# Poll PR checks until they pass or fail
|
|
430
|
-
MAX_POLL_ATTEMPTS=60
|
|
431
|
-
POLL_INTERVAL=30
|
|
432
|
-
POLL_ATTEMPT=0
|
|
433
|
-
|
|
434
|
-
while [[ $POLL_ATTEMPT -lt $MAX_POLL_ATTEMPTS ]]; do
|
|
435
|
-
POLL_ATTEMPT=$((POLL_ATTEMPT + 1))
|
|
436
|
-
|
|
437
|
-
# Get full PR status for debugging and decision making
|
|
438
|
-
PR_STATUS_JSON=$(gh pr view --repo "$REPO" --head "$BRANCH" --json mergeable,mergeStateStatus,statusCheckRollup 2>/dev/null || echo '{"mergeable":"UNKNOWN","mergeStateStatus":"UNKNOWN","statusCheckRollup":null}')
|
|
439
|
-
|
|
440
|
-
# Log raw API response for debugging
|
|
441
|
-
echo "[run_opencode_issue.sh] Debug: Raw PR status = $PR_STATUS_JSON"
|
|
442
|
-
|
|
443
|
-
# Parse mergeStateStatus
|
|
444
|
-
if command -v jq >/dev/null 2>&1; then
|
|
445
|
-
MERGE_STATE_STATUS=$(echo "$PR_STATUS_JSON" | jq -r '.mergeStateStatus')
|
|
446
|
-
else
|
|
447
|
-
MERGE_STATE_STATUS=$(python3 -c 'import json,sys; print(json.load(sys.stdin).get("mergeStateStatus", "UNKNOWN"))' <<<"$PR_STATUS_JSON")
|
|
448
|
-
fi
|
|
449
|
-
|
|
450
|
-
# Determine check status based on both statusCheckRollup and mergeStateStatus
|
|
451
|
-
# If mergeStateStatus is CLEAN or HAS_HOOKS and statusCheckRollup is null/empty → PASSED
|
|
452
|
-
# If mergeStateStatus is UNKNOWN → wait and retry
|
|
453
|
-
if [[ "$MERGE_STATE_STATUS" == "UNKNOWN" ]]; then
|
|
454
|
-
# Check if there are any CI checks configured
|
|
455
|
-
if command -v jq >/dev/null 2>&1; then
|
|
456
|
-
SCR_IS_NULL=$(echo "$PR_STATUS_JSON" | jq -r '.statusCheckRollup == null')
|
|
457
|
-
SCR_LENGTH=$(echo "$PR_STATUS_JSON" | jq -r '.statusCheckRollup | length // 0')
|
|
458
|
-
else
|
|
459
|
-
SCR_VALUE=$(python3 -c 'import json,sys; data=json.load(sys.stdin); print(data.get("statusCheckRollup", "null"))' <<<"$PR_STATUS_JSON")
|
|
460
|
-
SCR_IS_NULL=$([[ "$SCR_VALUE" == "null" ]] && echo "true" || echo "false")
|
|
461
|
-
SCR_LENGTH=$(python3 -c 'import json,sys; data=json.load(sys.stdin); scr=data.get("statusCheckRollup"); print(len(scr) if scr else 0)' <<<"$PR_STATUS_JSON")
|
|
462
|
-
fi
|
|
463
|
-
|
|
464
|
-
# If no checks configured, wait at most 20 seconds for them to appear
|
|
465
|
-
if [[ "$SCR_IS_NULL" == "true" ]] || [[ "$SCR_LENGTH" == "0" ]]; then
|
|
466
|
-
if [[ $POLL_ATTEMPT -lt 1 ]]; then
|
|
467
|
-
CHECK_STATUS="PENDING"
|
|
468
|
-
echo "[run_opencode_issue.sh] No CI checks configured, waiting up to 20 seconds for checks to appear..."
|
|
469
|
-
sleep 20
|
|
470
|
-
continue
|
|
471
|
-
else
|
|
472
|
-
CHECK_STATUS="PASSED"
|
|
473
|
-
echo "[run_opencode_issue.sh] No checks found after waiting 20 seconds, continuing to merge..."
|
|
474
|
-
fi
|
|
475
|
-
else
|
|
476
|
-
CHECK_STATUS="PENDING"
|
|
477
|
-
echo "[run_opencode_issue.sh] mergeStateStatus is UNKNOWN, waiting for GitHub to calculate..."
|
|
478
|
-
fi
|
|
479
|
-
elif [[ "$MERGE_STATE_STATUS" == "CLEAN" || "$MERGE_STATE_STATUS" == "HAS_HOOKS" ]]; then
|
|
480
|
-
# Check statusCheckRollup - if null/empty, no checks configured, so PASSED
|
|
481
|
-
if command -v jq >/dev/null 2>&1; then
|
|
482
|
-
# Debug: log the statusCheckRollup value
|
|
483
|
-
SCR_VALUE=$(echo "$PR_STATUS_JSON" | jq -r '.statusCheckRollup')
|
|
484
|
-
echo "[run_opencode_issue.sh] Debug: statusCheckRollup value = $SCR_VALUE"
|
|
485
|
-
|
|
486
|
-
# Check if statusCheckRollup is null or empty array
|
|
487
|
-
SCR_IS_NULL=$(echo "$PR_STATUS_JSON" | jq -r '.statusCheckRollup == null')
|
|
488
|
-
SCR_LENGTH=$(echo "$PR_STATUS_JSON" | jq -r '.statusCheckRollup | length // 0')
|
|
489
|
-
echo "[run_opencode_issue.sh] Debug: statusCheckRollup is_null=$SCR_IS_NULL, length=$SCR_LENGTH"
|
|
490
|
-
|
|
491
|
-
if [[ "$SCR_IS_NULL" == "true" ]] || [[ "$SCR_LENGTH" == "0" ]]; then
|
|
492
|
-
CHECK_STATUS="PASSED"
|
|
493
|
-
echo "[run_opencode_issue.sh] No CI checks configured (statusCheckRollup is null/empty)"
|
|
494
|
-
else
|
|
495
|
-
# Has checks - determine status
|
|
496
|
-
CHECK_STATUS=$(echo "$PR_STATUS_JSON" | jq -r '.statusCheckRollup | map(.conclusion) | if any(. == "FAILURE") then "FAILED" elif any(. == "PENDING") or any(. == "QUEUED") then "PENDING" else "PASSED" end')
|
|
497
|
-
echo "[run_opencode_issue.sh] CI checks found, status = $CHECK_STATUS"
|
|
498
|
-
fi
|
|
499
|
-
else
|
|
500
|
-
# Python fallback
|
|
501
|
-
SCR_VALUE=$(python3 -c 'import json,sys; data=json.load(sys.stdin); print(data.get("statusCheckRollup", "null"))' <<<"$PR_STATUS_JSON")
|
|
502
|
-
echo "[run_opencode_issue.sh] Debug: statusCheckRollup value = $SCR_VALUE"
|
|
503
|
-
|
|
504
|
-
CHECK_STATUS=$(python3 -c 'import json,sys; data=json.load(sys.stdin); scr=data.get("statusCheckRollup"); print("PASSED" if not scr or len(scr)==0 else "FAILED" if any(c.get("conclusion")=="FAILURE" for c in scr) else "PENDING" if any(c.get("conclusion") in ["PENDING","QUEUED"] for c in scr) else "PASSED")' <<<"$PR_STATUS_JSON")
|
|
505
|
-
|
|
506
|
-
if [[ "$SCR_VALUE" == "null" ]] || [[ "$SCR_VALUE" == "[]" ]]; then
|
|
507
|
-
echo "[run_opencode_issue.sh] No CI checks configured (statusCheckRollup is null/empty)"
|
|
508
|
-
fi
|
|
509
|
-
fi
|
|
510
|
-
elif [[ "$MERGE_STATE_STATUS" == "BLOCKED" ]]; then
|
|
511
|
-
# If mergeStateStatus is BLOCKED, check if it's due to failed checks
|
|
512
|
-
echo "[run_opencode_issue.sh] mergeStateStatus is BLOCKED, checking if due to failed checks"
|
|
513
|
-
if command -v jq >/dev/null 2>&1; then
|
|
514
|
-
# Debug: log the statusCheckRollup value
|
|
515
|
-
SCR_VALUE=$(echo "$PR_STATUS_JSON" | jq -r '.statusCheckRollup')
|
|
516
|
-
echo "[run_opencode_issue.sh] Debug: statusCheckRollup value = $SCR_VALUE"
|
|
517
|
-
|
|
518
|
-
# Check if statusCheckRollup is null or empty array
|
|
519
|
-
SCR_IS_NULL=$(echo "$PR_STATUS_JSON" | jq -r '.statusCheckRollup == null')
|
|
520
|
-
SCR_LENGTH=$(echo "$PR_STATUS_JSON" | jq -r '.statusCheckRollup | length // 0')
|
|
521
|
-
echo "[run_opencode_issue.sh] Debug: statusCheckRollup is_null=$SCR_IS_NULL, length=$SCR_LENGTH"
|
|
522
|
-
|
|
523
|
-
if [[ "$SCR_IS_NULL" == "true" ]] || [[ "$SCR_LENGTH" == "0" ]]; then
|
|
524
|
-
CHECK_STATUS="FAILED"
|
|
525
|
-
echo "[run_opencode_issue.sh] No CI checks but mergeStateStatus is BLOCKED - other blocking issue"
|
|
526
|
-
else
|
|
527
|
-
# Has checks - determine status
|
|
528
|
-
CHECK_STATUS=$(echo "$PR_STATUS_JSON" | jq -r '.statusCheckRollup | map(.conclusion) | if any(. == "FAILURE") then "FAILED" elif any(. == "PENDING") or any(. == "QUEUED") then "PENDING" else "PASSED" end')
|
|
529
|
-
echo "[run_opencode_issue.sh] CI checks found, status = $CHECK_STATUS"
|
|
530
|
-
fi
|
|
531
|
-
else
|
|
532
|
-
# Python fallback
|
|
533
|
-
SCR_VALUE=$(python3 -c 'import json,sys; data=json.load(sys.stdin); print(data.get("statusCheckRollup", "null"))' <<<"$PR_STATUS_JSON")
|
|
534
|
-
echo "[run_opencode_issue.sh] Debug: statusCheckRollup value = $SCR_VALUE"
|
|
535
|
-
|
|
536
|
-
CHECK_STATUS=$(python3 -c 'import json,sys; data=json.load(sys.stdin); scr=data.get("statusCheckRollup"); print("PASSED" if not scr or len(scr)==0 else "FAILED" if any(c.get("conclusion")=="FAILURE" for c in scr) else "PENDING" if any(c.get("conclusion") in ["PENDING","QUEUED"] for c in scr) else "PASSED")' <<<"$PR_STATUS_JSON")
|
|
537
|
-
|
|
538
|
-
if [[ "$SCR_VALUE" == "null" ]] || [[ "$SCR_VALUE" == "[]" ]]; then
|
|
539
|
-
echo "[run_opencode_issue.sh] No CI checks but mergeStateStatus is BLOCKED - other blocking issue"
|
|
540
|
-
fi
|
|
541
|
-
fi
|
|
542
|
-
else
|
|
543
|
-
# Other states (DRAFT, DIRTY, BEHIND) - mark as FAILED to exit
|
|
544
|
-
CHECK_STATUS="FAILED"
|
|
545
|
-
fi
|
|
546
|
-
|
|
547
|
-
echo "[run_opencode_issue.sh] Poll attempt $POLL_ATTEMPT/$MAX_POLL_ATTEMPTS: Check status = $CHECK_STATUS, mergeStateStatus = $MERGE_STATE_STATUS"
|
|
548
|
-
|
|
549
|
-
if [[ "$CHECK_STATUS" == "PASSED" ]]; then
|
|
550
|
-
echo "[run_opencode_issue.sh] All checks passed. Proceeding with merge..."
|
|
551
|
-
break
|
|
552
|
-
elif [[ "$CHECK_STATUS" == "FAILED" ]]; then
|
|
553
|
-
echo "[run_opencode_issue.sh] PR checks failed."
|
|
554
|
-
|
|
555
|
-
# Check if auto-fix is enabled
|
|
556
|
-
if [[ "$AUTO_FIX_CHECKS" == "true" ]] && [[ $FIX_ATTEMPT -lt $AUTO_FIX_CHECKS_MAX_ATTEMPTS ]]; then
|
|
557
|
-
FIX_ATTEMPT=$((FIX_ATTEMPT + 1))
|
|
558
|
-
echo "[run_opencode_issue.sh] Auto-fix enabled. Attempting to fix failing checks ($FIX_ATTEMPT/$AUTO_FIX_CHECKS_MAX_ATTEMPTS)..."
|
|
559
|
-
|
|
560
|
-
# Fetch failed check details and logs
|
|
561
|
-
CHECK_RUNS_JSON=$(gh api "repos/$REPO/commits/$(git rev-parse HEAD)/check-runs" --jq '.check_runs[] | select(.conclusion == "FAILURE") | {name: .name, conclusion: .conclusion, details_url: .details_url, output: {title: .output.title, summary: .output.summary, text: .output.text}}' 2>/dev/null || echo "[]")
|
|
562
|
-
|
|
563
|
-
# Build failure context
|
|
564
|
-
FAILURE_CONTEXT="The following CI checks failed:\n"
|
|
565
|
-
|
|
566
|
-
# Add failure details to context
|
|
567
|
-
if [[ -n "$CHECK_RUNS_JSON" ]]; then
|
|
568
|
-
FAILURE_CONTEXT+="$CHECK_RUNS_JSON\n"
|
|
569
|
-
fi
|
|
570
|
-
|
|
571
|
-
# Generate repair prompt
|
|
572
|
-
REPAIR_PROMPT="The PR checks have failed. Please analyze the CI failures and fix the issues.\n\n$FAILURE_CONTEXT\n\nWork rules:\n- Examine the failed checks and identify the root cause\n- Make the necessary code changes to fix the failures\n- Commit with message: \"autopilot: fix CI check failures (attempt $FIX_ATTEMPT/$AUTO_FIX_CHECKS_MAX_ATTEMPTS)\"\n- Push your changes to the branch $BRANCH\n- Focus only on fixing the CI failures, do not make unrelated changes"
|
|
573
|
-
|
|
574
|
-
echo "[run_opencode_issue.sh] Running opencode agent to fix CI failures..."
|
|
575
|
-
|
|
576
|
-
# Run opencode to fix the issue
|
|
577
|
-
cd "$WORKTREE"
|
|
578
|
-
if "$OPENCODE_BIN" run "$REPAIR_PROMPT"; then
|
|
579
|
-
# Commit any changes
|
|
580
|
-
if [[ -n "$(git status --porcelain)" ]]; then
|
|
581
|
-
git add -A
|
|
582
|
-
git commit -m "autopilot: fix CI check failures (attempt $FIX_ATTEMPT/$AUTO_FIX_CHECKS_MAX_ATTEMPTS)"
|
|
583
|
-
echo "[run_opencode_issue.sh] Committed fixes for CI failures."
|
|
584
|
-
|
|
585
|
-
# Push changes
|
|
586
|
-
git push origin "$BRANCH"
|
|
587
|
-
echo "[run_opencode_issue.sh] Pushed fixes for CI failures."
|
|
588
|
-
|
|
589
|
-
# Reset poll counter to re-check CI status
|
|
590
|
-
POLL_ATTEMPT=0
|
|
591
|
-
|
|
592
|
-
# Wait a moment for checks to start
|
|
593
|
-
sleep 10
|
|
594
|
-
|
|
595
|
-
# Continue polling
|
|
596
|
-
continue
|
|
597
|
-
else
|
|
598
|
-
echo "[run_opencode_issue.sh] No changes made by opencode agent."
|
|
599
|
-
fi
|
|
600
|
-
else
|
|
601
|
-
echo "[run_opencode_issue.sh] Opencode agent failed to fix CI issues."
|
|
602
|
-
fi
|
|
603
|
-
|
|
604
|
-
# If we're here, auto-fix failed or made no changes
|
|
605
|
-
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "⚠️ Autopilot attempted to fix CI failures (attempt $FIX_ATTEMPT/$AUTO_FIX_CHECKS_MAX_ATTEMPTS) but was unable to resolve all issues." || true
|
|
606
|
-
fi
|
|
607
|
-
|
|
608
|
-
# If auto-fix is disabled or max attempts reached, fail
|
|
609
|
-
if [[ "$FIX_ATTEMPT" -ge "$AUTO_FIX_CHECKS_MAX_ATTEMPTS" ]]; then
|
|
610
|
-
FAILED_MSG="❌ Autopilot cannot auto-merge PR: checks failed after $AUTO_FIX_CHECKS_MAX_ATTEMPTS auto-fix attempts.\n\nPR will remain open for review."
|
|
611
|
-
else
|
|
612
|
-
FAILED_MSG="❌ Autopilot cannot auto-merge PR: checks failed (auto-fix disabled).\n\nPR will remain open for review."
|
|
613
|
-
fi
|
|
614
|
-
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "$FAILED_MSG" || true
|
|
615
|
-
exit 1
|
|
616
|
-
fi
|
|
617
|
-
|
|
618
|
-
# Wait before next poll
|
|
619
|
-
if [[ $POLL_ATTEMPT -lt $MAX_POLL_ATTEMPTS ]]; then
|
|
620
|
-
echo "[run_opencode_issue.sh] Waiting ${POLL_INTERVAL}s before next check..."
|
|
621
|
-
sleep "$POLL_INTERVAL"
|
|
622
|
-
fi
|
|
623
|
-
done
|
|
624
|
-
|
|
625
|
-
# After polling loop: merge if checks passed
|
|
626
|
-
if [[ "$CHECK_STATUS" == "PASSED" ]]; then
|
|
627
|
-
echo "[run_opencode_issue.sh] Merging PR using method: $MERGE_METHOD"
|
|
628
|
-
|
|
629
|
-
# Build merge command based on method
|
|
630
|
-
MERGE_CMD="gh pr merge $PR_URL --delete-branch"
|
|
631
|
-
case "$MERGE_METHOD" in
|
|
632
|
-
squash)
|
|
633
|
-
MERGE_CMD="$MERGE_CMD --squash"
|
|
634
|
-
;;
|
|
635
|
-
merge)
|
|
636
|
-
MERGE_CMD="$MERGE_CMD --merge"
|
|
637
|
-
;;
|
|
638
|
-
rebase)
|
|
639
|
-
MERGE_CMD="$MERGE_CMD --rebase"
|
|
640
|
-
;;
|
|
641
|
-
*)
|
|
642
|
-
echo "[run_opencode_issue.sh] Unknown merge method '$MERGE_METHOD', defaulting to squash"
|
|
643
|
-
MERGE_CMD="$MERGE_CMD --squash"
|
|
644
|
-
;;
|
|
645
|
-
esac
|
|
646
|
-
|
|
647
|
-
# Attempt merge
|
|
648
|
-
if eval "$MERGE_CMD"; then
|
|
649
|
-
echo "[run_opencode_issue.sh] PR merged successfully!"
|
|
650
|
-
|
|
651
|
-
# Update issue with success message and mark as done
|
|
652
|
-
SUCCESS_MSG="✅ Autopilot successfully merged PR #${PR_URL##*/} using $MERGE_METHOD method.\n\nThe fix has been merged to the main branch."
|
|
653
|
-
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "$SUCCESS_MSG" || true
|
|
654
|
-
|
|
655
|
-
# Get done label and mark issue as done
|
|
656
|
-
if command -v jq >/dev/null 2>&1; then
|
|
657
|
-
LABEL_DONE=$(jq -r '.issueLabels.done' < .autopilot/autopilot.json)
|
|
658
|
-
LABEL_IN_PROGRESS=$(jq -r '.issueLabels.inProgress' < .autopilot/autopilot.json)
|
|
659
|
-
else
|
|
660
|
-
LABEL_DONE=$(python3 -c 'import json; print(json.load(open(".autopilot/autopilot.json"))["issueLabels"]["done"])')
|
|
661
|
-
LABEL_IN_PROGRESS=$(python3 -c 'import json; print(json.load(open(".autopilot/autopilot.json"))["issueLabels"]["inProgress"])')
|
|
662
|
-
fi
|
|
663
|
-
|
|
664
|
-
gh issue edit "$ISSUE_NUMBER" --repo "$REPO" --add-label "$LABEL_DONE" --remove-label "$LABEL_IN_PROGRESS" || true
|
|
665
|
-
gh issue close "$ISSUE_NUMBER" --repo "$REPO" || true
|
|
666
|
-
else
|
|
667
|
-
echo "[run_opencode_issue.sh] Failed to merge PR"
|
|
668
|
-
MERGE_FAIL_MSG="❌ Autopilot failed to merge PR automatically.\n\nThe PR is ready (checks passed) but the merge operation failed. You may need to merge manually."
|
|
669
|
-
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "$MERGE_FAIL_MSG" || true
|
|
670
|
-
fi
|
|
671
|
-
elif [[ $POLL_ATTEMPT -ge $MAX_POLL_ATTEMPTS ]]; then
|
|
672
|
-
echo "[run_opencode_issue.sh] Timed out waiting for checks to pass"
|
|
673
|
-
TIMEOUT_MSG="⏱️ Autopilot timed out waiting for PR checks to complete.\n\nThe PR will remain open for manual review."
|
|
674
|
-
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "$TIMEOUT_MSG" || true
|
|
675
|
-
fi
|
|
676
|
-
else
|
|
677
|
-
BLOCKED_MSG="❌ Autopilot cannot auto-merge PR: authenticated user '$AUTH_USER' is not in the allowedMergeUsers list."
|
|
678
|
-
gh issue comment "$ISSUE_NUMBER" --repo "$REPO" --body "$BLOCKED_MSG" || true
|
|
679
|
-
|
|
680
|
-
# Mark issue as blocked
|
|
681
|
-
if command -v jq >/dev/null 2>&1; then
|
|
682
|
-
LABEL_BLOCKED=$(jq -r '.issueLabels.blocked' < .autopilot/autopilot.json)
|
|
683
|
-
else
|
|
684
|
-
LABEL_BLOCKED=$(python3 -c 'import json; print(json.load(open(".autopilot/autopilot.json"))["issueLabels"]["blocked"])')
|
|
685
|
-
fi
|
|
686
|
-
gh issue edit "$ISSUE_NUMBER" --repo "$REPO" --add-label "$LABEL_BLOCKED" || true
|
|
687
|
-
fi
|
|
688
|
-
fi
|
|
689
|
-
|
|
690
|
-
echo "[run_opencode_issue.sh] Done: $PR_URL"
|