diff --git a/.github/workflows/openhands-resolver.yml b/.github/workflows/openhands-resolver.yml new file mode 100644 index 0000000000..9243a25df7 --- /dev/null +++ b/.github/workflows/openhands-resolver.yml @@ -0,0 +1,108 @@ +name: Auto-Fix Tagged Issues with OpenHands + +on: + issues: + types: [labeled] + +permissions: + contents: write + pull-requests: write + issues: write + +jobs: + auto-fix: + if: github.event.label.name == 'fix-me' + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Comment on issue with start message + uses: actions/github-script@v6 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `OpenHands started fixing the issue! You can monitor the progress [here](https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}).` + }); + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install openhands-resolver + + - name: Attempt to resolve issue + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + LLM_MODEL: ${{ secrets.LLM_MODEL }} + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }} + run: | + python -m openhands_resolver.resolve_issues \ + --repo ${{ github.repository }} \ + --issue-numbers ${{ github.event.issue.number }} + + - name: Check resolution result + id: check_result + run: | + if grep -q '"success":true' output/output.jsonl; then + echo "RESOLUTION_SUCCESS=true" >> $GITHUB_OUTPUT + else + echo "RESOLUTION_SUCCESS=false" >> $GITHUB_OUTPUT + fi + + - name: Create draft PR or push branch + 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 }} + run: | + if [ "${{ steps.check_result.outputs.RESOLUTION_SUCCESS }}" == "true" ]; then + python -m openhands_resolver.send_pull_request \ + --issue-number ${{ github.event.issue.number }} \ + --pr-type draft | tee pr_result.txt && \ + grep "draft created" pr_result.txt | sed 's/.*\///g' > pr_number.txt + else + python -m openhands_resolver.send_pull_request \ + --issue-number ${{ github.event.issue.number }} \ + --pr-type branch \ + --send-on-failure | tee branch_result.txt && \ + grep "Branch" branch_result.txt | sed 's/.*\///g; s/.expand=1//g' > branch_name.txt + fi + + - name: Comment on issue + uses: actions/github-script@v6 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + const fs = require('fs'); + const issueNumber = context.issue.number; + const success = ${{ steps.check_result.outputs.RESOLUTION_SUCCESS }}; + + if (success) { + const prNumber = fs.readFileSync('pr_number.txt', 'utf8').trim(); + 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 { + const branchName = fs.readFileSync('branch_name.txt', 'utf8').trim(); + 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.` + }); + } diff --git a/.github/workflows/solve-issue.yml b/.github/workflows/solve-issue.yml deleted file mode 100644 index b8dafdd0c9..0000000000 --- a/.github/workflows/solve-issue.yml +++ /dev/null @@ -1,116 +0,0 @@ -# Workflow that uses OpenHands to resolve a GitHub issue. Issue must be labeled 'solve-this' -name: Use OpenHands to Resolve GitHub Issue - -on: - issues: - types: [labeled] - -permissions: - contents: write - pull-requests: write - issues: write - -jobs: - dogfood: - if: github.event.label.name == 'solve-this' - runs-on: ubuntu-latest - container: - image: ghcr.io/all-hands-ai/openhands - volumes: - - /var/run/docker.sock:/var/run/docker.sock - steps: - - name: install git, github cli - run: apt-get install -y git gh - - name: Set up Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v3 - - name: Checkout Repository - uses: actions/checkout@v4 - - name: Write Task File - env: - ISSUE_TITLE: ${{ github.event.issue.title }} - ISSUE_BODY: ${{ github.event.issue.body }} - run: | - echo "TITLE:" > task.txt - echo "${ISSUE_TITLE}" >> task.txt - echo "" >> task.txt - echo "BODY:" >> task.txt - echo "${ISSUE_BODY}" >> task.txt - - name: Set up environment - run: | - curl -sSL https://install.python-poetry.org | python3 - - export PATH="/github/home/.local/bin:$PATH" - poetry install --without evaluation,llama-index - poetry run playwright install --with-deps chromium - - name: Run OpenHands - env: - ISSUE_TITLE: ${{ github.event.issue.title }} - ISSUE_BODY: ${{ github.event.issue.body }} - LLM_API_KEY: ${{ secrets.OPENAI_API_KEY }} - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - run: | - # Append path to launch poetry - export PATH="/github/home/.local/bin:$PATH" - # Append path to correctly import package, note: must set pwd at first - export PYTHONPATH=$(pwd):$PYTHONPATH - WORKSPACE_MOUNT_PATH=$GITHUB_WORKSPACE poetry run python ./openhands/core/main.py -i 50 -f task.txt -d $GITHUB_WORKSPACE - rm task.txt - - name: Setup Git, Create Branch, and Commit Changes - run: | - # Setup Git configuration - git config --global --add safe.directory $PWD - git config --global user.name 'OpenHands' - git config --global user.email 'OpenHands@users.noreply.github.com' - - # Create a unique branch name with a timestamp - BRANCH_NAME="fix/${{ github.event.issue.number }}-$(date +%Y%m%d%H%M%S)" - - # Checkout new branch - git checkout -b $BRANCH_NAME - - # Add all changes to staging, except task.txt - git add --all -- ':!task.txt' - - # Commit the changes, if any - git commit -m "OpenHands: Resolve Issue #${{ github.event.issue.number }}" - if [ $? -ne 0 ]; then - echo "No changes to commit." - exit 0 - fi - - # Push changes - git push --set-upstream origin $BRANCH_NAME - - name: Fetch Default Branch - env: - GH_TOKEN: ${{ github.token }} - run: | - # Fetch the default branch using gh cli - DEFAULT_BRANCH=$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name) - echo "Default branch is $DEFAULT_BRANCH" - echo "DEFAULT_BRANCH=$DEFAULT_BRANCH" >> $GITHUB_ENV - - name: Generate PR - env: - GH_TOKEN: ${{ github.token }} - run: | - # Create PR and capture URL - PR_URL=$(gh pr create \ - --title "OpenHands: Resolve Issue #2" \ - --body "This PR was generated by OpenHands to resolve issue #2" \ - --repo "foragerr/OpenHands" \ - --head "${{ github.head_ref }}" \ - --base "${{ env.DEFAULT_BRANCH }}" \ - | grep -o 'https://github.com/[^ ]*') - - # Extract PR number from URL - PR_NUMBER=$(echo "$PR_URL" | grep -o '[0-9]\+$') - - # Set environment vars - echo "PR_URL=$PR_URL" >> $GITHUB_ENV - echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV - - - name: Post Comment - env: - GH_TOKEN: ${{ github.token }} - run: | - gh issue comment ${{ github.event.issue.number }} \ - -b "OpenHands raised [PR #${{ env.PR_NUMBER }}](${{ env.PR_URL }}) to resolve this issue." diff --git a/.openhands_instructions b/.openhands_instructions new file mode 100644 index 0000000000..015ea101a0 --- /dev/null +++ b/.openhands_instructions @@ -0,0 +1,7 @@ +OpenHands is an automated AI software engineer. It is a repo with a Python backend +(in the `openhands` directory) and typescript frontend (in the `frontend` directory). + +- Setup: To set up the repo, including frontend/backend you can `make build` +- Backend Testing: All tests are in `tests/unit/test_*.py`. To test new code, you + can do `poetry run pytest tests/unit/test_xxx.py` where `xxx` is the appropriate + file for the current functionality. Write all tests with pytest.