mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-05-18 13:55:17 +02:00
Merge branch 'feature/toollib/git' into feature/import_repo
This commit is contained in:
commit
426eb5e61f
10 changed files with 357 additions and 41 deletions
65
metagpt/tools/libs/git.py
Normal file
65
metagpt/tools/libs/git.py
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from metagpt.tools.tool_registry import register_tool
|
||||
from metagpt.utils.git_repository import GitRepository
|
||||
|
||||
|
||||
@register_tool(tags=["git"])
|
||||
async def git_clone(url: str, output_dir: str | Path = None) -> Path:
|
||||
"""
|
||||
Clones a Git repository from the given URL.
|
||||
|
||||
Args:
|
||||
url (str): The URL of the Git repository to clone.
|
||||
output_dir (str or Path, optional): The directory where the repository will be cloned.
|
||||
If not provided, the repository will be cloned into the current working directory.
|
||||
|
||||
Returns:
|
||||
Path: The path to the cloned repository.
|
||||
|
||||
Raises:
|
||||
ValueError: If the specified Git root is invalid.
|
||||
|
||||
Example:
|
||||
>>> # git clone to /TO/PATH
|
||||
>>> url = 'https://github.com/geekan/MetaGPT.git'
|
||||
>>> output_dir = "/TO/PATH"
|
||||
>>> repo_dir = await git_clone(url=url, output_dir=output_dir)
|
||||
>>> print(repo_dir)
|
||||
/TO/PATH/MetaGPT
|
||||
|
||||
>>> # git clone to default directory.
|
||||
>>> url = 'https://github.com/geekan/MetaGPT.git'
|
||||
>>> repo_dir = await git_clone(url)
|
||||
>>> print(repo_dir)
|
||||
/WORK_SPACE/downloads/MetaGPT
|
||||
"""
|
||||
repo = await GitRepository.clone_from(url, output_dir)
|
||||
return repo.workdir
|
||||
|
||||
|
||||
async def git_checkout(repo_dir: str | Path, commit_id: str):
|
||||
"""
|
||||
Checks out a specific commit in a Git repository.
|
||||
|
||||
Args:
|
||||
repo_dir (str or Path): The directory containing the Git repository.
|
||||
commit_id (str): The ID of the commit to check out.
|
||||
|
||||
Raises:
|
||||
ValueError: If the specified Git root is invalid.
|
||||
|
||||
Example:
|
||||
>>> repo_dir = '/TO/GIT/REPO'
|
||||
>>> commit_id = 'main'
|
||||
>>> await git_checkout(repo_dir=repo_dir, commit_id=commit_id)
|
||||
git checkout main
|
||||
"""
|
||||
repo = GitRepository(local_path=repo_dir, auto_init=False)
|
||||
if not repo.is_valid:
|
||||
ValueError(f"Invalid git root: {repo_dir}")
|
||||
await repo.checkout(commit_id)
|
||||
63
metagpt/tools/libs/shell.py
Normal file
63
metagpt/tools/libs/shell.py
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Tuple, Union
|
||||
|
||||
from metagpt.tools.tool_registry import register_tool
|
||||
|
||||
|
||||
@register_tool(tags=["shell"])
|
||||
async def shell_execute(
|
||||
command: Union[List[str], str], cwd: str | Path = None, env: Dict = None, timeout: int = 600
|
||||
) -> Tuple[str, str, int]:
|
||||
"""
|
||||
Execute a command asynchronously and return its standard output and standard error.
|
||||
|
||||
Args:
|
||||
command (Union[List[str], str]): The command to execute and its arguments. It can be provided either as a list
|
||||
of strings or as a single string.
|
||||
cwd (str | Path, optional): The current working directory for the command. Defaults to None.
|
||||
env (Dict, optional): Environment variables to set for the command. Defaults to None.
|
||||
timeout (int, optional): Timeout for the command execution in seconds. Defaults to 600.
|
||||
|
||||
Returns:
|
||||
Tuple[str, str, int]: A tuple containing the string type standard output and string type standard error of the executed command and int type return code.
|
||||
|
||||
Raises:
|
||||
ValueError: If the command times out, this error is raised. The error message contains both standard output and
|
||||
standard error of the timed-out process.
|
||||
|
||||
Example:
|
||||
>>> # command is a list
|
||||
>>> stdout, stderr, returncode = await shell_execute(command=["ls", "-l"], cwd="/home/user", env={"PATH": "/usr/bin"})
|
||||
>>> print(stdout)
|
||||
total 8
|
||||
-rw-r--r-- 1 user user 0 Mar 22 10:00 file1.txt
|
||||
-rw-r--r-- 1 user user 0 Mar 22 10:00 file2.txt
|
||||
...
|
||||
|
||||
>>> # command is a string of shell script
|
||||
>>> stdout, stderr, returncode = await shell_execute(command="ls -l", cwd="/home/user", env={"PATH": "/usr/bin"})
|
||||
>>> print(stdout)
|
||||
total 8
|
||||
-rw-r--r-- 1 user user 0 Mar 22 10:00 file1.txt
|
||||
-rw-r--r-- 1 user user 0 Mar 22 10:00 file2.txt
|
||||
...
|
||||
|
||||
References:
|
||||
This function uses `subprocess.Popen` for executing shell commands asynchronously.
|
||||
"""
|
||||
cwd = str(cwd) if cwd else None
|
||||
shell = True if isinstance(command, str) else False
|
||||
process = subprocess.Popen(command, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, shell=shell)
|
||||
try:
|
||||
# Wait for the process to complete, with a timeout
|
||||
stdout, stderr = process.communicate(timeout=timeout)
|
||||
return stdout.decode("utf-8"), stderr.decode("utf-8"), process.returncode
|
||||
except subprocess.TimeoutExpired:
|
||||
process.kill() # Kill the process if it times out
|
||||
stdout, stderr = process.communicate()
|
||||
raise ValueError(f"{stdout.decode('utf-8')}\n{stderr.decode('utf-8')}")
|
||||
Loading…
Add table
Add a link
Reference in a new issue