Skip to content

Commit 22113b8

Browse files
committed
Switch to reusable workflow from pr-resolver
1 parent 6649410 commit 22113b8

1 file changed

Lines changed: 3 additions & 313 deletions

File tree

.github/workflows/pr-automation.yml

Lines changed: 3 additions & 313 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ on:
77
types: [opened]
88

99
jobs:
10-
# First job runs on GitHub-hosted runner to set pending status immediately
11-
setup:
12-
# Run on PR comments with commands OR PR creation with commands in body
10+
pr-automation:
1311
if: |
1412
(github.event_name == 'issue_comment' &&
1513
github.event.issue.pull_request &&
@@ -20,317 +18,9 @@ jobs:
2018
(contains(github.event.pull_request.body, '[action]') ||
2119
contains(github.event.pull_request.body, '[fix]') ||
2220
contains(github.event.pull_request.body, '[debug]')))
23-
24-
runs-on: ubuntu-latest
25-
26-
permissions:
27-
statuses: write
28-
pull-requests: read
29-
30-
outputs:
31-
branch: ${{ steps.pr.outputs.branch }}
32-
sha: ${{ steps.pr.outputs.sha }}
33-
pr_number: ${{ steps.pr.outputs.pr_number }}
34-
command: ${{ steps.pr.outputs.command }}
35-
instructions: ${{ steps.pr.outputs.instructions }}
36-
37-
steps:
38-
- name: Get PR details and set pending status
39-
id: pr
40-
uses: actions/github-script@v7
41-
with:
42-
script: |
43-
let prData, body, prNumber;
44-
45-
if (context.eventName === 'pull_request') {
46-
// PR creation event - data is directly available
47-
prData = context.payload.pull_request;
48-
body = prData.body || '';
49-
prNumber = prData.number;
50-
} else {
51-
// Comment event - need to fetch PR data
52-
const pr = await github.rest.pulls.get({
53-
owner: context.repo.owner,
54-
repo: context.repo.repo,
55-
pull_number: context.issue.number
56-
});
57-
prData = pr.data;
58-
body = context.payload.comment.body;
59-
prNumber = context.issue.number;
60-
}
61-
62-
const sha = prData.head.sha;
63-
core.setOutput('branch', prData.head.ref);
64-
core.setOutput('sha', sha);
65-
core.setOutput('pr_number', prNumber);
66-
67-
// Extract command - for PR body, search anywhere; for comments, must start with command
68-
let command = 'unknown';
69-
let instructions = '';
70-
71-
if (context.eventName === 'pull_request') {
72-
// For PR body, find command anywhere in text
73-
if (body.includes('[action]')) {
74-
command = 'action';
75-
const match = body.match(/\[action\](.*?)(?=\[(?:action|fix|debug)\]|$)/s);
76-
instructions = match ? match[1].trim() : '';
77-
} else if (body.includes('[fix]')) {
78-
command = 'fix';
79-
const match = body.match(/\[fix\](.*?)(?=\[(?:action|fix|debug)\]|$)/s);
80-
instructions = match ? match[1].trim() : '';
81-
} else if (body.includes('[debug]')) {
82-
command = 'debug';
83-
}
84-
} else {
85-
// For comments, must start with command
86-
if (body.startsWith('[action]')) {
87-
command = 'action';
88-
instructions = body.slice('[action]'.length).trim();
89-
} else if (body.startsWith('[fix]')) {
90-
command = 'fix';
91-
instructions = body.slice('[fix]'.length).trim();
92-
} else if (body.startsWith('[debug]')) {
93-
command = 'debug';
94-
}
95-
}
96-
97-
core.setOutput('command', command);
98-
core.setOutput('instructions', instructions);
99-
100-
// Set pending status immediately
101-
await github.rest.repos.createCommitStatus({
102-
owner: context.repo.owner,
103-
repo: context.repo.repo,
104-
sha: sha,
105-
state: 'pending',
106-
context: `PR Automation / ${command}`,
107-
description: 'Waiting for runner...',
108-
target_url: `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`
109-
});
110-
111-
execute:
112-
needs: setup
113-
runs-on: ${{ vars.RUNNER_TYPE || 'ubuntu-latest' }}
114-
21+
uses: GiggleLiu/pr-resolver/.github/workflows/pr-automation.yml@main
22+
secrets: inherit
11523
permissions:
11624
contents: write
11725
pull-requests: write
11826
statuses: write
119-
120-
steps:
121-
- name: Update status to running
122-
uses: actions/github-script@v7
123-
with:
124-
script: |
125-
await github.rest.repos.createCommitStatus({
126-
owner: context.repo.owner,
127-
repo: context.repo.repo,
128-
sha: '${{ needs.setup.outputs.sha }}',
129-
state: 'pending',
130-
context: 'PR Automation / ${{ needs.setup.outputs.command }}',
131-
description: 'Running...',
132-
target_url: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
133-
});
134-
135-
- name: Checkout PR branch
136-
uses: actions/checkout@v4
137-
with:
138-
ref: ${{ needs.setup.outputs.branch }}
139-
fetch-depth: 0
140-
141-
- name: Setup Node.js (GitHub-hosted only)
142-
if: ${{ vars.RUNNER_TYPE != 'self-hosted' }}
143-
uses: actions/setup-node@v4
144-
with:
145-
node-version: '20'
146-
147-
- name: Install Claude Code CLI (GitHub-hosted only)
148-
if: ${{ vars.RUNNER_TYPE != 'self-hosted' }}
149-
run: npm install -g @anthropic-ai/claude-code
150-
151-
- name: Install superpowers plugin (GitHub-hosted only)
152-
if: ${{ vars.RUNNER_TYPE != 'self-hosted' }}
153-
run: claude plugin install anthropics/claude-code-superpowers
154-
155-
- name: Find plan file
156-
id: plan
157-
if: needs.setup.outputs.command == 'action'
158-
run: |
159-
for f in PLAN.md plan.md .claude/plan.md docs/plan.md; do
160-
[ -f "$f" ] && echo "file=$f" >> $GITHUB_OUTPUT && exit 0
161-
done
162-
latest=$(ls -t docs/plans/*.md 2>/dev/null | head -1)
163-
[ -n "$latest" ] && echo "file=$latest" >> $GITHUB_OUTPUT && exit 0
164-
echo "No plan file found" && exit 1
165-
166-
- name: Execute plan
167-
if: needs.setup.outputs.command == 'action'
168-
env:
169-
ANTHROPIC_API_KEY_SECRET: ${{ secrets.ANTHROPIC_API_KEY }}
170-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
171-
run: |
172-
set -eo pipefail
173-
# Use secret if set, otherwise use runner's env (or OAuth from keychain)
174-
[ -n "$ANTHROPIC_API_KEY_SECRET" ] && export ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY_SECRET"
175-
176-
# For GitHub-hosted runners, API key is required
177-
# For self-hosted runners, OAuth from keychain is also supported
178-
if [ -z "$ANTHROPIC_API_KEY" ] && [ "${{ vars.RUNNER_TYPE }}" != "self-hosted" ]; then
179-
echo "Error: ANTHROPIC_API_KEY not set (required for GitHub-hosted runners)" | tee claude-output.txt
180-
exit 1
181-
fi
182-
183-
INSTRUCTIONS='${{ needs.setup.outputs.instructions }}'
184-
claude --dangerously-skip-permissions --max-turns 100 -p "
185-
Execute the plan in '${{ steps.plan.outputs.file }}'.
186-
${INSTRUCTIONS:+
187-
## Additional Instructions
188-
$INSTRUCTIONS
189-
}
190-
## Process
191-
1. Read the plan file
192-
2. Use /subagent-driven-development to execute tasks
193-
3. Push: git push origin ${{ needs.setup.outputs.branch }}
194-
4. Post summary: gh pr comment ${{ needs.setup.outputs.pr_number }} --repo ${{ github.repository }} --body 'SUMMARY'
195-
196-
## Rules
197-
- Tests should be strong enough to catch regressions.
198-
- Do not modify tests to make them pass.
199-
- Test failure must be reported.
200-
" 2>&1 | tee claude-output.txt
201-
202-
# Check for authentication errors in output
203-
if grep -qiE "authenticat(e|ion)|unauthorized|forbidden|invalid.*key|api.*key.*invalid|API Error: 40[13]" claude-output.txt; then
204-
echo "Error: Authentication failure detected"
205-
exit 1
206-
fi
207-
208-
# Check for max turns exhaustion
209-
if grep -q "Reached max turns" claude-output.txt; then
210-
echo "Error: Claude exhausted max turns without completing"
211-
exit 1
212-
fi
213-
214-
- name: Fix issues
215-
if: needs.setup.outputs.command == 'fix'
216-
env:
217-
ANTHROPIC_API_KEY_SECRET: ${{ secrets.ANTHROPIC_API_KEY }}
218-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
219-
run: |
220-
set -eo pipefail
221-
# Use secret if set, otherwise use runner's env (or OAuth from keychain)
222-
[ -n "$ANTHROPIC_API_KEY_SECRET" ] && export ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY_SECRET"
223-
224-
# For GitHub-hosted runners, API key is required
225-
# For self-hosted runners, OAuth from keychain is also supported
226-
if [ -z "$ANTHROPIC_API_KEY" ] && [ "${{ vars.RUNNER_TYPE }}" != "self-hosted" ]; then
227-
echo "Error: ANTHROPIC_API_KEY not set (required for GitHub-hosted runners)" | tee claude-output.txt
228-
exit 1
229-
fi
230-
231-
# Gather all feedback sources
232-
INLINE=$(gh api repos/${{ github.repository }}/pulls/${{ needs.setup.outputs.pr_number }}/comments --jq '.[].body' 2>/dev/null || echo "")
233-
REVIEWS=$(gh api repos/${{ github.repository }}/pulls/${{ needs.setup.outputs.pr_number }}/reviews --jq '.[] | select(.body != "") | .body' 2>/dev/null || echo "")
234-
PR_COMMENTS=$(gh api repos/${{ github.repository }}/issues/${{ needs.setup.outputs.pr_number }}/comments --jq '.[].body' 2>/dev/null || echo "")
235-
236-
# Gather CI status
237-
CI_STATUS=$(gh pr checks ${{ needs.setup.outputs.pr_number }} --repo ${{ github.repository }} 2>/dev/null || echo "")
238-
FAILED_RUNS=$(gh run list --branch ${{ needs.setup.outputs.branch }} --status failure --limit 3 --json databaseId,name --jq '.[] | "\(.databaseId) \(.name)"' 2>/dev/null || echo "")
239-
240-
# Get failed run logs if any
241-
CI_LOGS=""
242-
for run_id in $(echo "$FAILED_RUNS" | cut -d' ' -f1); do
243-
[ -n "$run_id" ] && CI_LOGS="$CI_LOGS
244-
--- Run $run_id ---
245-
$(gh run view $run_id --log-failed 2>/dev/null | tail -100 || echo 'Could not fetch logs')"
246-
done
247-
248-
INSTRUCTIONS='${{ needs.setup.outputs.instructions }}'
249-
claude --dangerously-skip-permissions --max-turns 100 -p "
250-
Fix all issues with this PR: review comments AND CI failures.
251-
${INSTRUCTIONS:+
252-
## Additional Instructions
253-
$INSTRUCTIONS
254-
}
255-
=== Inline Code Comments ===
256-
$INLINE
257-
258-
=== PR Reviews ===
259-
$REVIEWS
260-
261-
=== PR Conversation ===
262-
$PR_COMMENTS
263-
264-
=== CI Status ===
265-
$CI_STATUS
266-
267-
=== Failed CI Logs (last 100 lines each) ===
268-
$CI_LOGS
269-
270-
## Process
271-
1. Use /systematic-debugging if CI failed
272-
2. Fix review comments and CI issues
273-
3. Run tests to verify: make test, cargo test, npm test, etc.
274-
4. Commit: git commit -am 'Fix review feedback and CI issues'
275-
5. Push: git push origin ${{ needs.setup.outputs.branch }}
276-
6. Post summary: gh pr comment ${{ needs.setup.outputs.pr_number }} --repo ${{ github.repository }} --body 'SUMMARY'
277-
278-
## Rules
279-
- All CI failures must be fixed.
280-
- All change requests must be either addressed or explained.
281-
" 2>&1 | tee claude-output.txt
282-
283-
# Check for authentication errors in output
284-
if grep -qiE "authenticat(e|ion)|unauthorized|forbidden|invalid.*key|api.*key.*invalid|API Error: 40[13]" claude-output.txt; then
285-
echo "Error: Authentication failure detected"
286-
exit 1
287-
fi
288-
289-
# Check for max turns exhaustion
290-
if grep -q "Reached max turns" claude-output.txt; then
291-
echo "Error: Claude exhausted max turns without completing"
292-
exit 1
293-
fi
294-
295-
- name: Debug test
296-
if: needs.setup.outputs.command == 'debug'
297-
run: |
298-
echo "Debug test passed - workflow is working" | tee claude-output.txt
299-
300-
- name: Upload logs
301-
if: always()
302-
uses: actions/upload-artifact@v4
303-
with:
304-
name: claude-output
305-
path: claude-output.txt
306-
retention-days: 7
307-
308-
- name: Set success status
309-
if: success()
310-
uses: actions/github-script@v7
311-
with:
312-
script: |
313-
await github.rest.repos.createCommitStatus({
314-
owner: context.repo.owner,
315-
repo: context.repo.repo,
316-
sha: '${{ needs.setup.outputs.sha }}',
317-
state: 'success',
318-
context: 'PR Automation / ${{ needs.setup.outputs.command }}',
319-
description: 'Completed successfully',
320-
target_url: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
321-
});
322-
323-
- name: Set failure status
324-
if: failure()
325-
uses: actions/github-script@v7
326-
with:
327-
script: |
328-
await github.rest.repos.createCommitStatus({
329-
owner: context.repo.owner,
330-
repo: context.repo.repo,
331-
sha: '${{ needs.setup.outputs.sha }}',
332-
state: 'failure',
333-
context: 'PR Automation / ${{ needs.setup.outputs.command }}',
334-
description: 'Failed - check logs',
335-
target_url: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
336-
});

0 commit comments

Comments
 (0)