Auto-Fix Tagged Issue with OpenHands #6
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Auto-Fix Tagged Issue with OpenHands | |
on: | |
workflow_call: | |
inputs: | |
max_iterations: | |
required: false | |
type: number | |
default: 50 | |
macro: | |
required: false | |
type: string | |
default: "@openhands-agent" | |
target_branch: | |
required: false | |
type: string | |
default: "main" | |
description: "Target branch to pull and create PR against" | |
secrets: | |
LLM_MODEL: | |
required: true | |
LLM_API_KEY: | |
required: true | |
LLM_BASE_URL: | |
required: false | |
PAT_TOKEN: | |
required: true | |
PAT_USERNAME: | |
required: true | |
issues: | |
types: [labeled] | |
pull_request: | |
types: [labeled] | |
issue_comment: | |
types: [created] | |
pull_request_review_comment: | |
types: [created] | |
pull_request_review: | |
types: [submitted] | |
permissions: | |
contents: write | |
pull-requests: write | |
issues: write | |
jobs: | |
auto-fix: | |
if: | | |
github.event_name == 'workflow_call' || | |
github.event.label.name == 'fix-me' || | |
github.event.label.name == 'fix-me-experimental' || | |
( | |
((github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment') && | |
contains(github.event.comment.body, inputs.macro || '@openhands-agent') && | |
(github.event.comment.author_association == 'OWNER' || github.event.comment.author_association == 'COLLABORATOR' || github.event.comment.author_association == 'MEMBER') | |
) || | |
(github.event_name == 'pull_request_review' && | |
contains(github.event.review.body, inputs.macro || '@openhands-agent') && | |
(github.event.review.author_association == 'OWNER' || github.event.review.author_association == 'COLLABORATOR' || github.event.review.author_association == 'MEMBER') | |
) | |
) | |
runs-on: ubuntu-latest | |
steps: | |
- name: Checkout repository | |
uses: actions/checkout@v4 | |
- name: Set up Python | |
uses: actions/setup-python@v5 | |
with: | |
python-version: "3.12" | |
- name: Get latest versions and create requirements.txt | |
run: | | |
python -m pip index versions openhands-ai > openhands_versions.txt | |
OPENHANDS_VERSION=$(head -n 1 openhands_versions.txt | awk '{print $2}' | tr -d '()') | |
echo "openhands-ai==${OPENHANDS_VERSION}" >> requirements.txt | |
cat requirements.txt | |
- name: Cache pip dependencies | |
if: | | |
!( | |
github.event.label.name == 'fix-me-experimental' || | |
( | |
(github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment') && | |
contains(github.event.comment.body, '@openhands-agent-exp') | |
) || | |
( | |
github.event_name == 'pull_request_review' && | |
contains(github.event.review.body, '@openhands-agent-exp') | |
) | |
) | |
uses: actions/cache@v3 | |
with: | |
path: ${{ env.pythonLocation }}/lib/python3.12/site-packages/* | |
key: ${{ runner.os }}-pip-openhands-resolver-${{ hashFiles('requirements.txt') }} | |
restore-keys: | | |
${{ runner.os }}-pip-openhands-resolver-${{ hashFiles('requirements.txt') }} | |
- name: Check required environment variables | |
env: | |
LLM_MODEL: ${{ secrets.LLM_MODEL }} | |
LLM_API_KEY: ${{ secrets.LLM_API_KEY }} | |
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }} | |
PAT_TOKEN: ${{ secrets.PAT_TOKEN }} | |
PAT_USERNAME: ${{ secrets.PAT_USERNAME }} | |
run: | | |
required_vars=("LLM_MODEL" "LLM_API_KEY" "PAT_TOKEN" "PAT_USERNAME") | |
for var in "${required_vars[@]}"; do | |
if [ -z "${!var}" ]; then | |
echo "Error: Required environment variable $var is not set." | |
exit 1 | |
fi | |
done | |
- name: Set environment variables | |
run: | | |
if [ -n "${{ github.event.review.body }}" ]; then | |
echo "ISSUE_NUMBER=${{ github.event.pull_request.number }}" >> $GITHUB_ENV | |
echo "ISSUE_TYPE=pr" >> $GITHUB_ENV | |
elif [ -n "${{ github.event.issue.pull_request }}" ]; then | |
echo "ISSUE_NUMBER=${{ github.event.issue.number }}" >> $GITHUB_ENV | |
echo "ISSUE_TYPE=pr" >> $GITHUB_ENV | |
elif [ -n "${{ github.event.pull_request.number }}" ]; then | |
echo "ISSUE_NUMBER=${{ github.event.pull_request.number }}" >> $GITHUB_ENV | |
echo "ISSUE_TYPE=pr" >> $GITHUB_ENV | |
else | |
echo "ISSUE_NUMBER=${{ github.event.issue.number }}" >> $GITHUB_ENV | |
echo "ISSUE_TYPE=issue" >> $GITHUB_ENV | |
fi | |
if [ -n "${{ github.event.review.body }}" ]; then | |
echo "COMMENT_ID=${{ github.event.review.id || 'None' }}" >> $GITHUB_ENV | |
else | |
echo "COMMENT_ID=${{ github.event.comment.id || 'None' }}" >> $GITHUB_ENV | |
fi | |
echo "MAX_ITERATIONS=${{ inputs.max_iterations || 50 }}" >> $GITHUB_ENV | |
echo "SANDBOX_ENV_GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV | |
# Set branch variables | |
echo "TARGET_BRANCH=${{ inputs.target_branch }}" >> $GITHUB_ENV | |
- name: Comment on issue with start message | |
uses: actions/github-script@v7 | |
with: | |
github-token: ${{secrets.GITHUB_TOKEN}} | |
script: | | |
const issueType = process.env.ISSUE_TYPE; | |
github.rest.issues.createComment({ | |
issue_number: ${{ env.ISSUE_NUMBER }}, | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
body: `[OpenHands](https://github.com/All-Hands-AI/OpenHands) started fixing the ${issueType}! You can monitor the progress [here](https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}).` | |
}); | |
- name: Install OpenHands | |
run: | | |
if [[ "${{ github.event.label.name }}" == "fix-me-experimental" ]] || | |
([[ "${{ github.event_name }}" == "issue_comment" || "${{ github.event_name }}" == "pull_request_review_comment" ]] && | |
[[ "${{ github.event.comment.body }}" == "@openhands-agent-exp"* ]]) || | |
([[ "${{ github.event_name }}" == "pull_request_review" ]] && | |
[[ "${{ github.event.review.body }}" == "@openhands-agent-exp"* ]]); then | |
python -m pip install --upgrade pip | |
pip install git+https://github.com/all-hands-ai/openhands.git | |
else | |
python -m pip install --upgrade -r requirements.txt | |
fi | |
- name: Attempt to resolve issue | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
GITHUB_USERNAME: ${{ secrets.PAT_USERNAME }} | |
LLM_MODEL: ${{ secrets.LLM_MODEL }} | |
LLM_API_KEY: ${{ secrets.LLM_API_KEY }} | |
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }} | |
PYTHONPATH: "" | |
run: | | |
cd /tmp && python -m openhands.resolver.resolve_issue \ | |
--repo ${{ github.repository }} \ | |
--issue-number ${{ env.ISSUE_NUMBER }} \ | |
--issue-type ${{ env.ISSUE_TYPE }} \ | |
--max-iterations ${{ env.MAX_ITERATIONS }} \ | |
--comment-id ${{ env.COMMENT_ID }} \ | |
- name: Check resolution result | |
id: check_result | |
run: | | |
if cd /tmp && grep -q '"success":true' output/output.jsonl; then | |
echo "RESOLUTION_SUCCESS=true" >> $GITHUB_OUTPUT | |
else | |
echo "RESOLUTION_SUCCESS=false" >> $GITHUB_OUTPUT | |
fi | |
- name: Upload output.jsonl as artifact | |
uses: actions/upload-artifact@v4 | |
if: always() # Upload even if the previous steps fail | |
with: | |
name: resolver-output | |
path: /tmp/output/output.jsonl | |
retention-days: 30 # Keep the artifact for 30 days | |
- name: Create draft PR or push branch | |
if: always() # Create PR or branch even if the previous steps fail | |
env: | |
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }} | |
GITHUB_USERNAME: ${{ secrets.PAT_USERNAME }} | |
LLM_MODEL: ${{ secrets.LLM_MODEL }} | |
LLM_API_KEY: ${{ secrets.LLM_API_KEY }} | |
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }} | |
PYTHONPATH: "" | |
run: | | |
if [ "${{ steps.check_result.outputs.RESOLUTION_SUCCESS }}" == "true" ]; then | |
cd /tmp && python -m openhands.resolver.send_pull_request \ | |
--issue-number ${{ env.ISSUE_NUMBER }} \ | |
--pr-type draft | tee pr_result.txt && \ | |
grep "draft created" pr_result.txt | sed 's/.*\///g' > pr_number.txt | |
else | |
cd /tmp && python -m openhands.resolver.send_pull_request \ | |
--issue-number ${{ env.ISSUE_NUMBER }} \ | |
--pr-type branch \ | |
--send-on-failure | tee branch_result.txt && \ | |
grep "branch created" branch_result.txt | sed 's/.*\///g; s/.expand=1//g' > branch_name.txt | |
fi | |
- name: Comment on issue | |
uses: actions/github-script@v7 | |
if: always() # Comment on issue even if the previous steps fail | |
with: | |
github-token: ${{secrets.GITHUB_TOKEN}} | |
script: | | |
const fs = require('fs'); | |
const issueNumber = ${{ env.ISSUE_NUMBER }}; | |
const success = ${{ steps.check_result.outputs.RESOLUTION_SUCCESS }}; | |
let prNumber = ''; | |
let branchName = ''; | |
let logContent = ''; | |
const noChangesMessage = `No changes to commit for issue #${issueNumber}. Skipping commit.`; | |
try { | |
if (success){ | |
logContent = fs.readFileSync('/tmp/pr_result.txt', 'utf8').trim(); | |
} else { | |
logContent = fs.readFileSync('/tmp/branch_result.txt', 'utf8').trim(); | |
} | |
} catch (error) { | |
console.error('Error reading results file:', error); | |
} | |
try { | |
if (success) { | |
prNumber = fs.readFileSync('/tmp/pr_number.txt', 'utf8').trim(); | |
} else { | |
branchName = fs.readFileSync('/tmp/branch_name.txt', 'utf8').trim(); | |
} | |
} catch (error) { | |
console.error('Error reading file:', error); | |
} | |
if (logContent.includes(noChangesMessage)) { | |
github.rest.issues.createComment({ | |
issue_number: issueNumber, | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
body: `The workflow to fix this issue encountered an error. Openhands failed to create any code changes.` | |
}); | |
} else if (success && prNumber) { | |
github.rest.issues.createComment({ | |
issue_number: issueNumber, | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
body: `A potential fix has been generated and a draft PR #${prNumber} has been created. Please review the changes.` | |
}); | |
} else if (!success && branchName) { | |
github.rest.issues.createComment({ | |
issue_number: issueNumber, | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
body: `An attempt was made to automatically fix this issue, but it was unsuccessful. A branch named '${branchName}' has been created with the attempted changes. You can view the branch [here](https://github.com/${context.repo.owner}/${context.repo.repo}/tree/${branchName}). Manual intervention may be required.` | |
}); | |
} else { | |
github.rest.issues.createComment({ | |
issue_number: issueNumber, | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
body: `The workflow to fix this issue encountered an error. Please check the [workflow logs](https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}) for more information.` | |
}); | |
} |