Skip to content

Commit 763762d

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

File tree

1 file changed

+194
-0
lines changed

1 file changed

+194
-0
lines changed
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
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 --model claude-opus-4-6 \
81+
--dangerously-skip-permissions \
82+
-p "$(cat << 'PROMPT_EOF'
83+
You are working on phpstan/phpstan-src, the source code of PHPStan - a PHP static analysis tool.
84+
85+
Your task is to fix the following GitHub issue from the phpstan/phpstan repository:
86+
Issue phpstan/phpstan#${{ inputs.issue-number }}: ${{ steps.issue.outputs.title }}
87+
URL: ${{ steps.issue.outputs.url }}
88+
89+
Issue body is in the file /tmp/issue-body.txt — read it before proceeding.
90+
91+
## Step 1: Write a regression test
92+
93+
Read .claude/skills/regression-test/SKILL.md for detailed guidance on writing regression tests for PHPStan bugs.
94+
95+
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.
96+
97+
Skip Steps 5-6 of the skill (reverting fix and committing) — those are not needed here.
98+
99+
The regression test should fail without the fix — verify this by running it before implementing the fix.
100+
101+
## Step 2: Fix the bug
102+
103+
Implement the fix in the source code under src/. Common areas to look:
104+
- src/Analyser/NodeScopeResolver.php - AST traversal and scope management
105+
- src/Analyser/MutatingScope.php - Type tracking
106+
- src/Analyser/TypeSpecifier.php - Type narrowing from conditions
107+
- src/Type/ - Type system implementations
108+
- src/Rules/ - Rule implementations
109+
- src/Reflection/ - Reflection layer
110+
111+
Read CLAUDE.md for important guidelines about the codebase architecture and common patterns.
112+
113+
## Step 3: Verify the fix
114+
115+
1. Run the regression test to confirm it passes now
116+
2. Run the full test suite: make tests
117+
3. Run PHPStan self-analysis: make phpstan
118+
4. Fix any failures that come up
119+
5. Run make cs-fix to fix any coding standard violations
120+
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.
121+
122+
Do not create a branch, push, or create a PR - this will be handled automatically.
123+
124+
## Step 4: Write a summary
125+
126+
After completing the fix, write two files:
127+
128+
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:
129+
Fix array_key_exists narrowing for template types
130+
131+
- Added handling for TemplateType in TypeSpecifier when processing array_key_exists
132+
- New regression test in tests/PHPStan/Analyser/nsrt/bug-12345.php
133+
- The root cause was that TypeSpecifier did not unwrap template bounds before narrowing
134+
135+
2. /tmp/pr-description.md - A pull request description in this format:
136+
## Summary
137+
Brief description of what the issue was about and what the fix does.
138+
139+
## Changes
140+
- Bullet points of specific code changes made
141+
- Reference file paths where changes were made
142+
143+
## Root cause
144+
Explain why the bug happened and how the fix addresses it.
145+
146+
## Test
147+
Describe the regression test that was added.
148+
149+
Fixes phpstan/phpstan#${{ inputs.issue-number }}
150+
151+
These files are critical - they will be used for the commit message and PR description.
152+
PROMPT_EOF
153+
)"
154+
155+
- name: "Read Claude's summary"
156+
id: claude-summary
157+
env:
158+
ISSUE_NUMBER: ${{ inputs.issue-number }}
159+
run: |
160+
if [ -f /tmp/commit-message.txt ]; then
161+
delimiter="EOF_$(openssl rand -hex 16)"
162+
{
163+
echo "commit_message<<${delimiter}"
164+
cat /tmp/commit-message.txt
165+
echo "${delimiter}"
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+
delimiter="EOF_$(openssl rand -hex 16)"
173+
{
174+
echo "pr_body<<${delimiter}"
175+
cat /tmp/pr-description.md
176+
echo "${delimiter}"
177+
} >> "$GITHUB_OUTPUT"
178+
else
179+
echo "pr_body=Fixes phpstan/phpstan#$ISSUE_NUMBER" >> "$GITHUB_OUTPUT"
180+
fi
181+
182+
- name: "Create Pull Request"
183+
id: create-pr
184+
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
185+
with:
186+
branch-token: ${{ secrets.PHPSTAN_BOT_FORK_TOKEN }}
187+
token: ${{ secrets.PHPSTAN_BOT_PR_TOKEN }}
188+
push-to-fork: phpstan-bot/phpstan-src
189+
branch-suffix: random
190+
delete-branch: true
191+
title: "Fix #${{ inputs.issue-number }}: ${{ steps.issue.outputs.title }}"
192+
body: ${{ steps.claude-summary.outputs.pr_body }}
193+
committer: "phpstan-bot <ondrej+phpstanbot@mirtes.cz>"
194+
commit-message: ${{ steps.claude-summary.outputs.commit_message }}

0 commit comments

Comments
 (0)