diff --git a/.github/workflows/openhands-resolver.yml b/.github/workflows/openhands-resolver.yml index a2b82232eaaf..4692413d3fd9 100644 --- a/.github/workflows/openhands-resolver.yml +++ b/.github/workflows/openhands-resolver.yml @@ -11,6 +11,11 @@ on: 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 @@ -135,6 +140,9 @@ jobs: 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: @@ -175,7 +183,8 @@ jobs: --issue-number ${{ env.ISSUE_NUMBER }} \ --issue-type ${{ env.ISSUE_TYPE }} \ --max-iterations ${{ env.MAX_ITERATIONS }} \ - --comment-id ${{ env.COMMENT_ID }} + --comment-id ${{ env.COMMENT_ID }} \ + --target-branch ${{ env.TARGET_BRANCH }} - name: Check resolution result id: check_result diff --git a/openhands/resolver/resolve_issue.py b/openhands/resolver/resolve_issue.py index b371d8160b20..b6cbb9f03c05 100644 --- a/openhands/resolver/resolve_issue.py +++ b/openhands/resolver/resolve_issue.py @@ -315,6 +315,7 @@ async def resolve_issue( repo_instruction: str | None, issue_number: int, comment_id: int | None, + target_branch: str | None = None, reset_logger: bool = False, ) -> None: """Resolve a single github issue. @@ -333,6 +334,7 @@ async def resolve_issue( repo_instruction: Repository instruction to use. issue_number: Issue number to resolve. comment_id: Optional ID of a specific comment to focus on. + target_branch: Optional target branch to create PR against (for PRs). reset_logger: Whether to reset the logger for multiprocessing. """ issue_handler = issue_handler_factory(issue_type, owner, repo, token) @@ -422,15 +424,32 @@ async def resolve_issue( try: # checkout to pr branch if needed if issue_type == 'pr': + branch_to_use = target_branch if target_branch else issue.head_branch logger.info( - f'Checking out to PR branch {issue.head_branch} for issue {issue.number}' + f'Checking out to PR branch {target_branch} for issue {issue.number}' ) + if not branch_to_use: + raise ValueError('Branch name cannot be None') + + # Fetch the branch first to ensure it exists locally + fetch_cmd = ['git', 'fetch', 'origin', branch_to_use] + subprocess.check_output( + fetch_cmd, + cwd=repo_dir, + ) + + # Checkout the branch + checkout_cmd = ['git', 'checkout', branch_to_use] subprocess.check_output( - ['git', 'checkout', f'{issue.head_branch}'], + checkout_cmd, cwd=repo_dir, ) + # Update issue's base_branch if using custom target branch + if target_branch: + issue.base_branch = target_branch + base_commit = ( subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=repo_dir) .decode('utf-8') @@ -553,6 +572,12 @@ def int_or_none(value): choices=['issue', 'pr'], help='Type of issue to resolve, either open issue or pr comments.', ) + parser.add_argument( + '--target-branch', + type=str, + default=None, + help="Target branch to pull and create PR against (for PRs). If not specified, uses the PR's base branch.", + ) my_args = parser.parse_args() @@ -613,6 +638,7 @@ def int_or_none(value): repo_instruction=repo_instruction, issue_number=my_args.issue_number, comment_id=my_args.comment_id, + target_branch=my_args.target_branch, ) )