| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- 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}'
- )
|