Skip to content

Commit 281e60d

Browse files
committed
Proper Claude Fix Issue workflow (for the fork)
1 parent b054374 commit 281e60d

File tree

1 file changed

+193
-0
lines changed

1 file changed

+193
-0
lines changed
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
name: "Claude Fix Issue"
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
issue-number:
7+
description: "Issue number from phpstan/phpstan repository"
8+
required: true
9+
type: string
10+
workflow_call:
11+
inputs:
12+
issue-number:
13+
description: "Issue number from phpstan/phpstan repository"
14+
required: true
15+
type: string
16+
17+
permissions:
18+
contents: read
19+
20+
jobs:
21+
fix:
22+
name: "Fix #${{ inputs.issue-number }}"
23+
runs-on: ubuntu-latest
24+
timeout-minutes: 60
25+
permissions:
26+
contents: read
27+
issues: read
28+
pull-requests: write
29+
30+
steps:
31+
- name: Harden the runner (Audit all outbound calls)
32+
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
33+
with:
34+
egress-policy: audit
35+
36+
- name: "Checkout"
37+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
38+
with:
39+
ref: 2.1.x
40+
repository: phpstan/phpstan-src
41+
fetch-depth: 0
42+
43+
- name: "Install PHP"
44+
uses: "shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1" # v2
45+
with:
46+
coverage: "none"
47+
php-version: "8.4"
48+
ini-file: development
49+
extensions: mbstring
50+
51+
- uses: "ramsey/composer-install@3cf229dc2919194e9e36783941438d17239e8520" # v3
52+
53+
- name: "Install Claude Code"
54+
run: npm install -g @anthropic-ai/claude-code
55+
56+
- name: "Fetch issue details"
57+
id: issue
58+
env:
59+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
60+
ISSUE_NUMBER: ${{ inputs.issue-number }}
61+
run: |
62+
ISSUE_JSON=$(gh issue view "$ISSUE_NUMBER" \
63+
--repo phpstan/phpstan \
64+
--json title,body,url)
65+
66+
TITLE=$(echo "$ISSUE_JSON" | jq -r '.title')
67+
URL=$(echo "$ISSUE_JSON" | jq -r '.url')
68+
echo "title=$TITLE" >> "$GITHUB_OUTPUT"
69+
echo "url=$URL" >> "$GITHUB_OUTPUT"
70+
echo "$ISSUE_JSON" | jq -r '.body' > /tmp/issue-body.txt
71+
72+
- name: "Run Claude Code"
73+
env:
74+
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
75+
GH_TOKEN: ${{ secrets.PHPSTAN_BOT_FORK_TOKEN }}
76+
run: |
77+
git config user.name "phpstan-bot"
78+
git config user.email "ondrej+phpstanbot@mirtes.cz"
79+
80+
claude -p \
81+
--model claude-opus-4-6 \
82+
--dangerously-skip-permissions
83+
"$(cat << 'PROMPT_EOF'
84+
You are working on phpstan/phpstan-src, the source code of PHPStan - a PHP static analysis tool.
85+
86+
Your task is to fix the following GitHub issue from the phpstan/phpstan repository:
87+
Issue phpstan/phpstan#${{ inputs.issue-number }}: ${{ steps.issue.outputs.title }}
88+
URL: ${{ steps.issue.outputs.url }}
89+
90+
Issue body is in the file /tmp/issue-body.txt — read it before proceeding.
91+
92+
## Step 1: Write a regression test
93+
94+
Read .claude/skills/regression-test/SKILL.md for detailed guidance on writing regression tests for PHPStan bugs.
95+
96+
The issue body is already provided above — start from Step 2 of the skill (deciding test type). For Step 1 (gathering context), you only need to fetch the playground samples from any playground links found in the issue body.
97+
98+
Skip Steps 5-6 of the skill (reverting fix and committing) — those are not needed here.
99+
100+
The regression test should fail without the fix — verify this by running it before implementing the fix.
101+
102+
## Step 2: Fix the bug
103+
104+
Implement the fix in the source code under src/. Common areas to look:
105+
- src/Analyser/NodeScopeResolver.php - AST traversal and scope management
106+
- src/Analyser/MutatingScope.php - Type tracking
107+
- src/Analyser/TypeSpecifier.php - Type narrowing from conditions
108+
- src/Type/ - Type system implementations
109+
- src/Rules/ - Rule implementations
110+
- src/Reflection/ - Reflection layer
111+
112+
Read CLAUDE.md for important guidelines about the codebase architecture and common patterns.
113+
114+
## Step 3: Verify the fix
115+
116+
1. Run the regression test to confirm it passes now
117+
2. Run the full test suite: make tests
118+
3. Run PHPStan self-analysis: make phpstan
119+
4. Fix any failures that come up
120+
5. Run make cs-fix to fix any coding standard violations
121+
6. Run make name-collision and fix violations - add different tests in unique namespaces. If the function and class declarations are exactly the same, you can reuse them across files instead of duplicating them.
122+
123+
Do not create a branch, push, or create a PR - this will be handled automatically.
124+
125+
## Step 4: Write a summary
126+
127+
After completing the fix, write two files:
128+
129+
1. /tmp/commit-message.txt - A concise commit message (first line: short summary under 72 chars, then a blank line, then a few bullet points describing key changes). Example:
130+
Fix array_key_exists narrowing for template types
131+
132+
- Added handling for TemplateType in TypeSpecifier when processing array_key_exists
133+
- New regression test in tests/PHPStan/Analyser/nsrt/bug-12345.php
134+
- The root cause was that TypeSpecifier did not unwrap template bounds before narrowing
135+
136+
2. /tmp/pr-description.md - A pull request description in this format:
137+
## Summary
138+
Brief description of what the issue was about and what the fix does.
139+
140+
## Changes
141+
- Bullet points of specific code changes made
142+
- Reference file paths where changes were made
143+
144+
## Root cause
145+
Explain why the bug happened and how the fix addresses it.
146+
147+
## Test
148+
Describe the regression test that was added.
149+
150+
Fixes phpstan/phpstan#${{ inputs.issue-number }}
151+
152+
These files are critical - they will be used for the commit message and PR description.
153+
PROMPT_EOF
154+
)"
155+
156+
- name: "Read Claude's summary"
157+
id: claude-summary
158+
env:
159+
ISSUE_NUMBER: ${{ inputs.issue-number }}
160+
run: |
161+
if [ -f /tmp/commit-message.txt ]; then
162+
{
163+
echo "commit_message<<COMMIT_MSG_EOF"
164+
cat /tmp/commit-message.txt
165+
echo "COMMIT_MSG_EOF"
166+
} >> "$GITHUB_OUTPUT"
167+
else
168+
echo "commit_message=Fix #$ISSUE_NUMBER" >> "$GITHUB_OUTPUT"
169+
fi
170+
171+
if [ -f /tmp/pr-description.md ]; then
172+
{
173+
echo "pr_body<<PR_BODY_EOF"
174+
cat /tmp/pr-description.md
175+
echo "PR_BODY_EOF"
176+
} >> "$GITHUB_OUTPUT"
177+
else
178+
echo "pr_body=Fixes phpstan/phpstan#$ISSUE_NUMBER" >> "$GITHUB_OUTPUT"
179+
fi
180+
181+
- name: "Create Pull Request"
182+
id: create-pr
183+
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
184+
with:
185+
branch-token: ${{ secrets.PHPSTAN_BOT_FORK_TOKEN }}
186+
token: ${{ secrets.PHPSTAN_BOT_PR_TOKEN }}
187+
push-to-fork: phpstan-bot/phpstan-src
188+
branch-suffix: random
189+
delete-branch: true
190+
title: "Fix #${{ inputs.issue-number }}: ${{ steps.issue.outputs.title }}"
191+
body: ${{ steps.claude-summary.outputs.pr_body }}
192+
committer: "phpstan-bot <ondrej+phpstanbot@mirtes.cz>"
193+
commit-message: ${{ steps.claude-summary.outputs.commit_message }}

0 commit comments

Comments
 (0)