Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 85 additions & 64 deletions .github/workflows/claude-easy-fixes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ jobs:
- name: "Checkout"
uses: actions/checkout@v4
with:
ref: "2.1.x"
fetch-depth: 0

- name: "Install PHP"
Expand Down Expand Up @@ -55,102 +56,122 @@ jobs:
ISSUE_NUMBER=$(echo "$ISSUE" | jq -r '.number')
ISSUE_TITLE=$(echo "$ISSUE" | jq -r '.title')
ISSUE_URL=$(echo "$ISSUE" | jq -r '.url')
ISSUE_BODY=$(echo "$ISSUE" | jq -r '.body')

echo "issue_number=$ISSUE_NUMBER" >> "$GITHUB_OUTPUT"
echo "issue_title=$ISSUE_TITLE" >> "$GITHUB_OUTPUT"
echo "issue_url=$ISSUE_URL" >> "$GITHUB_OUTPUT"

EOF_MARKER=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
echo "issue_body<<$EOF_MARKER" >> "$GITHUB_OUTPUT"
echo "$ISSUE_BODY" >> "$GITHUB_OUTPUT"
echo "$EOF_MARKER" >> "$GITHUB_OUTPUT"

# Also fetch the first comment (issue body is the first comment)
FIRST_COMMENT_BODY=$(gh issue view "$ISSUE_NUMBER" \
--repo phpstan/phpstan \
--json body \
--jq '.body')

EOF_MARKER=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
echo "first_comment<<$EOF_MARKER" >> "$GITHUB_OUTPUT"
echo "$FIRST_COMMENT_BODY" >> "$GITHUB_OUTPUT"
echo "$EOF_MARKER" >> "$GITHUB_OUTPUT"

echo "### Selected issue: #$ISSUE_NUMBER - $ISSUE_TITLE" >> "$GITHUB_STEP_SUMMARY"
echo "$ISSUE_URL" >> "$GITHUB_STEP_SUMMARY"

- name: "Run Claude Code"
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
prompt: |
You are working on phpstan/phpstan-src, the source code of PHPStan - a PHP static analysis tool.
- name: "Install Claude Code"
run: npm install -g @anthropic-ai/claude-code

- name: "Build prompt"
env:
ISSUE_NUMBER: ${{ steps.pick-issue.outputs.issue_number }}
ISSUE_TITLE: ${{ steps.pick-issue.outputs.issue_title }}
ISSUE_URL: ${{ steps.pick-issue.outputs.issue_url }}
ISSUE_BODY: ${{ steps.pick-issue.outputs.first_comment }}
run: |
python3 << 'PYEOF'
import os

Your task is to fix the following GitHub issue from the phpstan/phpstan repository:
Issue #${{ steps.pick-issue.outputs.issue_number }}: ${{ steps.pick-issue.outputs.issue_title }}
URL: ${{ steps.pick-issue.outputs.issue_url }}
n = os.environ["ISSUE_NUMBER"]
title = os.environ["ISSUE_TITLE"]
url = os.environ["ISSUE_URL"]
body = os.environ["ISSUE_BODY"]

Issue body:
${{ steps.pick-issue.outputs.first_comment }}
prompt = f"""You are working on phpstan/phpstan-src, the source code of PHPStan - a PHP static analysis tool.

## Step 1: Retrieve playground code
Your task is to fix the following GitHub issue from the phpstan/phpstan repository:
Issue #{n}: {title}
URL: {url}

From the issue body above, find any referenced PHPStan playground links (URLs matching https://phpstan.org/r/<UUID>).
Issue body:
{body}

For each playground link, extract the UUID and fetch the code and analysis results using the API:
```
curl https://api.phpstan.org/sample?id=<UUID>
```
## Step 1: Retrieve playground code

The API returns JSON with:
- `code`: The PHP code that was analyzed
- `level`: The PHPStan rule level
- `config.strictRules`: Whether strict rules are enabled
- `config.bleedingEdge`: Whether bleeding edge is enabled
- `config.treatPhpDocTypesAsCertain`: PHPDoc type certainty setting
- `versionedErrors`: Array of `{phpVersion, errors: [{line, message, identifier}]}`
From the issue body above, find any referenced PHPStan playground links (URLs matching https://phpstan.org/r/<UUID>).

## Step 2: Write a regression test
For each playground link, extract the UUID and fetch the code and analysis results using the API:
curl https://api.phpstan.org/sample?id=<UUID>

Based on the issue and the playground code:
The API returns JSON with:
- code: The PHP code that was analyzed
- level: The PHPStan rule level
- config.strictRules: Whether strict rules are enabled
- config.bleedingEdge: Whether bleeding edge is enabled
- config.treatPhpDocTypesAsCertain: PHPDoc type certainty setting
- versionedErrors: Array of {{phpVersion, errors: [{{line, message, identifier}}]}}

- 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.
## Step 2: Write a regression test

- 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.
Based on the issue and the playground code:

The regression test **should fail** without the fix - verify this by running it before implementing the fix.
- 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-{n}.php. Look at existing files in that directory for examples.

Run individual test files with: `php vendor/bin/phpunit <test-file>`
Run all tests with: `make tests`
Run PHPStan with: `make phpstan`
- 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.

## Step 3: Fix the bug
The regression test should fail without the fix - verify this by running it before implementing the fix.

Implement the fix in the source code under `src/`. Common areas to look:
- `src/Analyser/NodeScopeResolver.php` - AST traversal and scope management
- `src/Analyser/MutatingScope.php` - Type tracking
- `src/Analyser/TypeSpecifier.php` - Type narrowing from conditions
- `src/Type/` - Type system implementations
- `src/Rules/` - Rule implementations
- `src/Reflection/` - Reflection layer
Run individual test files with: php vendor/bin/phpunit <test-file>
Run all tests with: make tests
Run PHPStan with: make phpstan

Read CLAUDE.md for important guidelines about the codebase architecture and common patterns.
## Step 3: Fix the bug

## Step 4: Verify the fix
Implement the fix in the source code under src/. Common areas to look:
- src/Analyser/NodeScopeResolver.php - AST traversal and scope management
- src/Analyser/MutatingScope.php - Type tracking
- src/Analyser/TypeSpecifier.php - Type narrowing from conditions
- src/Type/ - Type system implementations
- src/Rules/ - Rule implementations
- src/Reflection/ - Reflection layer

1. Run the regression test to confirm it passes now
2. Run the full test suite: `make tests`
3. Run PHPStan self-analysis: `make phpstan`
4. Fix any failures that come up
Read CLAUDE.md for important guidelines about the codebase architecture and common patterns.

## Step 5: Create a branch and PR
## Step 4: Verify the fix

1. Run the regression test to confirm it passes now
2. Run the full test suite: make tests
3. Run PHPStan self-analysis: make phpstan
4. Fix any failures that come up

## Step 5: Create a branch and PR

1. Create a branch named fix/bug-{n}
2. Commit all changes with message: "Fix #{n}"
3. Push the branch
4. Create a PR targeting the 2.1.x branch with:
- Title: "Fix #{n}: {title}"
- Body referencing the issue: "Fixes phpstan/phpstan#{n}"
- Include a description of the fix and what was wrong
"""

with open("/tmp/claude-prompt.txt", "w") as f:
f.write(prompt)
PYEOF

- name: "Run Claude Code"
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

1. Create a branch named `fix/bug-${{ steps.pick-issue.outputs.issue_number }}`
2. Commit all changes with message: "Fix #${{ steps.pick-issue.outputs.issue_number }}"
3. Push the branch
4. Create a PR targeting the `2.1.x` branch with:
- Title: "Fix #${{ steps.pick-issue.outputs.issue_number }}: ${{ steps.pick-issue.outputs.issue_title }}"
- Body referencing the issue: "Fixes phpstan/phpstan#${{ steps.pick-issue.outputs.issue_number }}"
- Include a description of the fix and what was wrong
claude_args: "--model claude-opus-4-6 --max-turns 50"
claude -p \
--model claude-opus-4-6 \
--max-turns 50 \
"$(cat /tmp/claude-prompt.txt)"
Loading