Jirka Borovec 0c2ebfd6e1
Ruff: use I rule for isort (#1410)
Ruff: use I rule for isort
2024-04-29 15:41:58 -07:00

160 lines
4.9 KiB
Python

import random
import string
from dataclasses import dataclass
from typing import TYPE_CHECKING
import requests
from opendevin import config
from opendevin.observation import AgentErrorObservation, Observation
from opendevin.observation.message import AgentMessageObservation
from opendevin.observation.run import CmdOutputObservation
from opendevin.schema import ActionType
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}'
)