Skip to content

Commit fa873f9

Browse files
authored
Add Claude Easy Fixes workflow for automated bug fixing (#4875)
1 parent 8c1d275 commit fa873f9

1 file changed

Lines changed: 156 additions & 0 deletions

File tree

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
name: "Claude Easy Fixes"
2+
3+
on:
4+
workflow_dispatch:
5+
6+
permissions:
7+
contents: write
8+
pull-requests: write
9+
issues: write
10+
11+
jobs:
12+
easy-fix:
13+
name: "Pick and fix an easy issue"
14+
runs-on: blacksmith-4vcpu-ubuntu-2404
15+
timeout-minutes: 60
16+
17+
steps:
18+
- name: "Checkout"
19+
uses: actions/checkout@v4
20+
with:
21+
fetch-depth: 0
22+
23+
- name: "Install PHP"
24+
uses: "shivammathur/setup-php@v2"
25+
with:
26+
coverage: "none"
27+
php-version: "8.4"
28+
ini-file: development
29+
extensions: mbstring
30+
31+
- uses: "ramsey/composer-install@v3"
32+
33+
- name: "Pick a random Easy fix issue"
34+
id: pick-issue
35+
env:
36+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
37+
run: |
38+
ISSUE_JSON=$(gh issue list \
39+
--repo phpstan/phpstan \
40+
--milestone "Easy fixes" \
41+
--state open \
42+
--json number,title,body,url \
43+
--limit 100 \
44+
)
45+
46+
TOTAL=$(echo "$ISSUE_JSON" | jq 'length')
47+
if [ "$TOTAL" -eq 0 ]; then
48+
echo "No issues found in Easy fixes milestone"
49+
exit 1
50+
fi
51+
52+
RANDOM_INDEX=$(( RANDOM % TOTAL ))
53+
ISSUE=$(echo "$ISSUE_JSON" | jq -c ".[$RANDOM_INDEX]")
54+
55+
ISSUE_NUMBER=$(echo "$ISSUE" | jq -r '.number')
56+
ISSUE_TITLE=$(echo "$ISSUE" | jq -r '.title')
57+
ISSUE_URL=$(echo "$ISSUE" | jq -r '.url')
58+
ISSUE_BODY=$(echo "$ISSUE" | jq -r '.body')
59+
60+
echo "issue_number=$ISSUE_NUMBER" >> "$GITHUB_OUTPUT"
61+
echo "issue_title=$ISSUE_TITLE" >> "$GITHUB_OUTPUT"
62+
echo "issue_url=$ISSUE_URL" >> "$GITHUB_OUTPUT"
63+
64+
EOF_MARKER=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
65+
echo "issue_body<<$EOF_MARKER" >> "$GITHUB_OUTPUT"
66+
echo "$ISSUE_BODY" >> "$GITHUB_OUTPUT"
67+
echo "$EOF_MARKER" >> "$GITHUB_OUTPUT"
68+
69+
# Also fetch the first comment (issue body is the first comment)
70+
FIRST_COMMENT_BODY=$(gh issue view "$ISSUE_NUMBER" \
71+
--repo phpstan/phpstan \
72+
--json body \
73+
--jq '.body')
74+
75+
echo "first_comment<<$EOF_MARKER" >> "$GITHUB_OUTPUT"
76+
echo "$FIRST_COMMENT_BODY" >> "$GITHUB_OUTPUT"
77+
echo "$EOF_MARKER" >> "$GITHUB_OUTPUT"
78+
79+
echo "### Selected issue: #$ISSUE_NUMBER - $ISSUE_TITLE" >> "$GITHUB_STEP_SUMMARY"
80+
echo "$ISSUE_URL" >> "$GITHUB_STEP_SUMMARY"
81+
82+
- name: "Run Claude Code"
83+
uses: anthropics/claude-code-action@v1
84+
with:
85+
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
86+
github_token: ${{ secrets.GITHUB_TOKEN }}
87+
prompt: |
88+
You are working on phpstan/phpstan-src, the source code of PHPStan - a PHP static analysis tool.
89+
90+
Your task is to fix the following GitHub issue from the phpstan/phpstan repository:
91+
Issue #${{ steps.pick-issue.outputs.issue_number }}: ${{ steps.pick-issue.outputs.issue_title }}
92+
URL: ${{ steps.pick-issue.outputs.issue_url }}
93+
94+
Issue body:
95+
${{ steps.pick-issue.outputs.first_comment }}
96+
97+
## Step 1: Retrieve playground code
98+
99+
From the issue body above, find any referenced PHPStan playground links (URLs matching https://phpstan.org/r/<UUID>).
100+
101+
For each playground link, extract the UUID and fetch the code and analysis results using the API:
102+
```
103+
curl https://api.phpstan.org/sample?id=<UUID>
104+
```
105+
106+
The API returns JSON with:
107+
- `code`: The PHP code that was analyzed
108+
- `level`: The PHPStan rule level
109+
- `config.strictRules`: Whether strict rules are enabled
110+
- `config.bleedingEdge`: Whether bleeding edge is enabled
111+
- `config.treatPhpDocTypesAsCertain`: PHPDoc type certainty setting
112+
- `versionedErrors`: Array of `{phpVersion, errors: [{line, message, identifier}]}`
113+
114+
## Step 2: Write a regression test
115+
116+
Based on the issue and the playground code:
117+
118+
- If the problem is **only about type inference** (wrong type reported, missing type narrowing, etc.), add a test file in `tests/PHPStan/Analyser/nsrt/` using `assertType()` and `assertNativeType()` calls. Name it `bug-<issue_number>.php`. Look at existing files in that directory for examples.
119+
120+
- If the problem is about a **rule false positive/negative** (wrong error reported or missing error), add a rule-specific test. Find the relevant rule test case in `tests/PHPStan/Rules/` and add a test data file. Look at existing bug test files in those directories for the pattern.
121+
122+
The regression test **should fail** without the fix - verify this by running it before implementing the fix.
123+
124+
Run individual test files with: `php vendor/bin/phpunit <test-file>`
125+
Run all tests with: `make tests`
126+
Run PHPStan with: `make phpstan`
127+
128+
## Step 3: Fix the bug
129+
130+
Implement the fix in the source code under `src/`. Common areas to look:
131+
- `src/Analyser/NodeScopeResolver.php` - AST traversal and scope management
132+
- `src/Analyser/MutatingScope.php` - Type tracking
133+
- `src/Analyser/TypeSpecifier.php` - Type narrowing from conditions
134+
- `src/Type/` - Type system implementations
135+
- `src/Rules/` - Rule implementations
136+
- `src/Reflection/` - Reflection layer
137+
138+
Read CLAUDE.md for important guidelines about the codebase architecture and common patterns.
139+
140+
## Step 4: Verify the fix
141+
142+
1. Run the regression test to confirm it passes now
143+
2. Run the full test suite: `make tests`
144+
3. Run PHPStan self-analysis: `make phpstan`
145+
4. Fix any failures that come up
146+
147+
## Step 5: Create a branch and PR
148+
149+
1. Create a branch named `fix/bug-${{ steps.pick-issue.outputs.issue_number }}`
150+
2. Commit all changes with message: "Fix #${{ steps.pick-issue.outputs.issue_number }}"
151+
3. Push the branch
152+
4. Create a PR targeting the `2.1.x` branch with:
153+
- Title: "Fix #${{ steps.pick-issue.outputs.issue_number }}: ${{ steps.pick-issue.outputs.issue_title }}"
154+
- Body referencing the issue: "Fixes phpstan/phpstan#${{ steps.pick-issue.outputs.issue_number }}"
155+
- Include a description of the fix and what was wrong
156+
claude_args: "--model claude-opus-4-6 --max-turns 50"

0 commit comments

Comments
 (0)