mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
* Added a push action * Tests * Add tests * Fix capitalization * Update * Fix typo * Fix integration tests * Added poetry.lock * Set lock * Fix action parsing * Update integration test output * Updated prompt * Update integration test * Add github token to default config --------- Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
159 lines
4.9 KiB
Python
159 lines
4.9 KiB
Python
from dataclasses import dataclass
|
|
from opendevin.observation import Observation, AgentErrorObservation
|
|
from opendevin.observation.message import AgentMessageObservation
|
|
from opendevin.observation.run import CmdOutputObservation
|
|
from opendevin.schema import ActionType
|
|
from opendevin import config
|
|
from typing import TYPE_CHECKING
|
|
import requests
|
|
import random
|
|
import string
|
|
|
|
from opendevin.schema.config import ConfigType
|
|
|
|
from .base import ExecutableAction
|
|
|
|
if TYPE_CHECKING:
|
|
from opendevin.controller import AgentController
|
|
|
|
|
|
@dataclass
|
|
class GitHubPushAction(ExecutableAction):
|
|
"""This pushes the current branch to github.
|
|
|
|
To use this, you need to set the GITHUB_TOKEN environment variable.
|
|
The agent will return a message with a URL that you can click to make a pull
|
|
request.
|
|
|
|
Attributes:
|
|
owner: The owner of the source repo
|
|
repo: The name of the source repo
|
|
branch: The branch to push
|
|
action: The action identifier
|
|
"""
|
|
|
|
owner: str
|
|
repo: str
|
|
branch: str
|
|
action: str = ActionType.PUSH
|
|
|
|
async def run(self, controller: 'AgentController') -> Observation:
|
|
github_token = config.get(ConfigType.GITHUB_TOKEN)
|
|
if not github_token:
|
|
return AgentErrorObservation(
|
|
'GITHUB_TOKEN is not set'
|
|
)
|
|
|
|
# Create a random short string to use as a temporary remote
|
|
random_remote = ''.join(
|
|
['opendevin_temp_'] + random.choices(string.ascii_lowercase, k=5)
|
|
)
|
|
|
|
# Set the temporary remote
|
|
new_url = f'https://{github_token}@github.com/{self.owner}/{self.repo}.git'
|
|
command = f'git remote add {random_remote} {new_url}'
|
|
remote_add_result = controller.action_manager.run_command(
|
|
command, background=False
|
|
)
|
|
if (
|
|
not isinstance(remote_add_result, CmdOutputObservation)
|
|
or remote_add_result.exit_code != 0
|
|
):
|
|
return remote_add_result
|
|
|
|
# Push the branch to the temporary remote
|
|
command = f'git push {random_remote} {self.branch}'
|
|
push_result = controller.action_manager.run_command(command, background=False)
|
|
|
|
# Delete the temporary remote
|
|
command = f'git remote remove {random_remote}'
|
|
remote_remove_result = controller.action_manager.run_command(
|
|
command, background=False
|
|
)
|
|
if (
|
|
not isinstance(remote_remove_result, CmdOutputObservation)
|
|
or remote_remove_result.exit_code != 0
|
|
):
|
|
return remote_remove_result
|
|
|
|
return push_result
|
|
|
|
@property
|
|
def message(self) -> str:
|
|
return f'Pushing branch {self.branch} to {self.owner}/{self.repo}'
|
|
|
|
|
|
@dataclass
|
|
class GitHubSendPRAction(ExecutableAction):
|
|
"""An action to send a github PR.
|
|
|
|
To use this, you need to set the GITHUB_TOKEN environment variable.
|
|
|
|
Attributes:
|
|
owner: The owner of the source repo
|
|
repo: The name of the source repo
|
|
title: The title of the PR
|
|
head: The branch to send the PR from
|
|
head_repo: The repo to send the PR from
|
|
base: The branch to send the PR to
|
|
body: The body of the PR
|
|
"""
|
|
|
|
owner: str
|
|
repo: str
|
|
title: str
|
|
head: str
|
|
head_repo: str | None
|
|
base: str
|
|
body: str | None
|
|
action: str = ActionType.SEND_PR
|
|
|
|
async def run(self, controller: 'AgentController') -> Observation:
|
|
github_token = config.get(ConfigType.GITHUB_TOKEN)
|
|
if not github_token:
|
|
return AgentErrorObservation(
|
|
'GITHUB_TOKEN is not set'
|
|
)
|
|
|
|
# API URL to create the pull request
|
|
url = f'https://api.github.com/repos/{self.owner}/{self.repo}/pulls'
|
|
|
|
# Headers to authenticate and request JSON responses
|
|
headers = {
|
|
'Authorization': f'token {github_token}',
|
|
'Accept': 'application/vnd.github.v3+json',
|
|
}
|
|
|
|
# Data for the pull request
|
|
data = {
|
|
'title': self.title,
|
|
'head': self.head,
|
|
'head_repo': self.head_repo,
|
|
'base': self.base,
|
|
'body': self.body,
|
|
}
|
|
data = {k: v for k, v in data.items() if v is not None}
|
|
|
|
# Make the request
|
|
response = requests.post(url, headers=headers, json=data)
|
|
|
|
# Check for errors
|
|
if response.status_code == 201:
|
|
return AgentMessageObservation(
|
|
'Pull request created successfully!\n'
|
|
f'Pull request URL:{response.json()["html_url"]}'
|
|
)
|
|
else:
|
|
return AgentErrorObservation(
|
|
'Failed to create pull request\n'
|
|
f'Status code: {response.status_code}\n'
|
|
f'Response: {response.text}'
|
|
)
|
|
|
|
@property
|
|
def message(self) -> str:
|
|
return (
|
|
f'Sending PR from {self.head_repo}:{self.head} to '
|
|
f'{self.owner}:{self.base}'
|
|
)
|