mirror of
https://github.com/qodo-ai/pr-agent.git
synced 2025-07-04 21:00:40 +08:00
Merge remote-tracking branch 'origin/main' into tr/readme_update
This commit is contained in:
2
.github/workflows/review.yaml
vendored
2
.github/workflows/review.yaml
vendored
@ -8,7 +8,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: PR Agent action step
|
- name: PR Agent action step
|
||||||
id: pragent
|
id: pragent
|
||||||
uses: Codium-ai/pr-agent@feature/github_action
|
uses: Codium-ai/pr-agent@main
|
||||||
env:
|
env:
|
||||||
OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
|
OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
|
||||||
OPENAI_ORG: ${{ secrets.OPENAI_ORG }}
|
OPENAI_ORG: ${{ secrets.OPENAI_ORG }}
|
||||||
|
1
Dockerfile.github_action_dockerhub
Normal file
1
Dockerfile.github_action_dockerhub
Normal file
@ -0,0 +1 @@
|
|||||||
|
FROM codiumai/pr-agent:github_action
|
24
README.md
24
README.md
@ -35,9 +35,10 @@ Example results:
|
|||||||
<div align="left">
|
<div align="left">
|
||||||
|
|
||||||
- [Live demo](#live-demo)
|
- [Live demo](#live-demo)
|
||||||
- [Quickstart](#Quickstart)
|
- [Overview](#overview)
|
||||||
|
- [Quickstart](#quickstart)
|
||||||
- [Usage and tools](#usage-and-tools)
|
- [Usage and tools](#usage-and-tools)
|
||||||
- [Configuration](#Configuration)
|
- [Configuration](#configuration)
|
||||||
- [How it works](#how-it-works)
|
- [How it works](#how-it-works)
|
||||||
- [Roadmap](#roadmap)
|
- [Roadmap](#roadmap)
|
||||||
- [Similar projects](#similar-projects)
|
- [Similar projects](#similar-projects)
|
||||||
@ -52,6 +53,25 @@ Experience GPT-4 powered PR review on your public GitHub repository with our hos
|
|||||||
To set up your own pr-agent, see the [Quickstart](#Quickstart) section
|
To set up your own pr-agent, see the [Quickstart](#Quickstart) section
|
||||||
|
|
||||||
---
|
---
|
||||||
|
## Overview
|
||||||
|
`pr-agent` offers extensive pull request functionalities across various git providers:
|
||||||
|
| | | Github | Gitlab | Bitbucket |
|
||||||
|
|-------|---------------------------------------------|--------|--------|-----------|
|
||||||
|
| TOOLS | Review | ✓ | ✓ | ✓ |
|
||||||
|
| | ⮑ Inline review | ✓ | ✓ | |
|
||||||
|
| | Ask | ✓ | ✓ | |
|
||||||
|
| | Auto-Description | ✓ | | |
|
||||||
|
| | Improve Code | ✓ | | |
|
||||||
|
| | | | | |
|
||||||
|
| USAGE | CLI | ✓ | ✓ | ✓ |
|
||||||
|
| | Tagging bot | ✓ | ✓ | |
|
||||||
|
| | Actions | ✓ | | |
|
||||||
|
| | | | | |
|
||||||
|
| CORE | PR compression | ✓ | ✓ | ✓ |
|
||||||
|
| | Repo language prioritization | ✓ | ✓ | ✓ |
|
||||||
|
| | Adaptive and token-aware<br />file patch fitting | ✓ | ✓ | ✓ |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Quickstart
|
## Quickstart
|
||||||
|
|
||||||
|
@ -2,4 +2,4 @@ name: 'PR Agent'
|
|||||||
description: 'Summarize, review and suggest improvements for pull requests'
|
description: 'Summarize, review and suggest improvements for pull requests'
|
||||||
runs:
|
runs:
|
||||||
using: 'docker'
|
using: 'docker'
|
||||||
image: 'Dockerfile.github_action'
|
image: 'Dockerfile.github_action_dockerhub'
|
||||||
|
@ -10,29 +10,48 @@ from pr_agent.tools.pr_reviewer import PRReviewer
|
|||||||
|
|
||||||
|
|
||||||
def run():
|
def run():
|
||||||
parser = argparse.ArgumentParser(description='AI based pull request analyzer')
|
parser = argparse.ArgumentParser(description='AI based pull request analyzer', usage="""\
|
||||||
|
Usage: cli.py --pr-url <URL on supported git hosting service> <command> [<args>].
|
||||||
|
|
||||||
|
Supported commands:
|
||||||
|
review / review_pr - Add a review that includes a summary of the PR and specific suggestions for improvement.
|
||||||
|
ask / ask_question [question] - Ask a question about the PR.
|
||||||
|
describe / describe_pr - Modify the PR title and description based on the PR's contents.
|
||||||
|
improve / improve_code - Suggest improvements to the code in the PR as pull request comments ready to commit.
|
||||||
|
""")
|
||||||
parser.add_argument('--pr_url', type=str, help='The URL of the PR to review', required=True)
|
parser.add_argument('--pr_url', type=str, help='The URL of the PR to review', required=True)
|
||||||
parser.add_argument('--question', type=str, help='Optional question to ask', required=False)
|
parser.add_argument('command', type=str, help='The', choices=['review', 'review_pr',
|
||||||
parser.add_argument('--pr_description', action='store_true', required=False)
|
'ask', 'ask_question',
|
||||||
parser.add_argument('--pr_code_suggestions', action='store_true', required=False)
|
'describe', 'describe_pr',
|
||||||
|
'improve', 'improve_code'], default='review')
|
||||||
|
parser.add_argument('rest', nargs=argparse.REMAINDER, default=[])
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
logging.basicConfig(level=os.environ.get("LOGLEVEL", "INFO"))
|
logging.basicConfig(level=os.environ.get("LOGLEVEL", "INFO"))
|
||||||
if args.question:
|
command = args.command.lower()
|
||||||
print(f"Question: {args.question} about PR {args.pr_url}")
|
if command in ['ask', 'ask_question']:
|
||||||
reviewer = PRQuestions(args.pr_url, args.question)
|
question = ' '.join(args.rest).strip()
|
||||||
|
if len(question) == 0:
|
||||||
|
print("Please specify a question")
|
||||||
|
parser.print_help()
|
||||||
|
return
|
||||||
|
print(f"Question: {question} about PR {args.pr_url}")
|
||||||
|
reviewer = PRQuestions(args.pr_url, question)
|
||||||
asyncio.run(reviewer.answer())
|
asyncio.run(reviewer.answer())
|
||||||
elif args.pr_description:
|
elif command in ['describe', 'describe_pr']:
|
||||||
print(f"PR description: {args.pr_url}")
|
print(f"PR description: {args.pr_url}")
|
||||||
reviewer = PRDescription(args.pr_url)
|
reviewer = PRDescription(args.pr_url)
|
||||||
asyncio.run(reviewer.describe())
|
asyncio.run(reviewer.describe())
|
||||||
elif args.pr_code_suggestions:
|
elif command in ['improve', 'improve_code']:
|
||||||
print(f"PR code suggestions: {args.pr_url}")
|
print(f"PR code suggestions: {args.pr_url}")
|
||||||
reviewer = PRCodeSuggestions(args.pr_url)
|
reviewer = PRCodeSuggestions(args.pr_url)
|
||||||
asyncio.run(reviewer.suggest())
|
asyncio.run(reviewer.suggest())
|
||||||
else:
|
elif command in ['review', 'review_pr']:
|
||||||
print(f"Reviewing PR: {args.pr_url}")
|
print(f"Reviewing PR: {args.pr_url}")
|
||||||
reviewer = PRReviewer(args.pr_url, cli_mode=True)
|
reviewer = PRReviewer(args.pr_url, cli_mode=True)
|
||||||
asyncio.run(reviewer.review())
|
asyncio.run(reviewer.review())
|
||||||
|
else:
|
||||||
|
print(f"Unknown command: {command}")
|
||||||
|
parser.print_help()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
from pr_agent.config_loader import settings
|
from pr_agent.config_loader import settings
|
||||||
|
from pr_agent.tools.pr_code_suggestions import PRCodeSuggestions
|
||||||
|
from pr_agent.tools.pr_description import PRDescription
|
||||||
from pr_agent.tools.pr_questions import PRQuestions
|
from pr_agent.tools.pr_questions import PRQuestions
|
||||||
from pr_agent.tools.pr_reviewer import PRReviewer
|
from pr_agent.tools.pr_reviewer import PRReviewer
|
||||||
|
|
||||||
@ -16,10 +19,11 @@ async def run_action():
|
|||||||
if not GITHUB_EVENT_PATH:
|
if not GITHUB_EVENT_PATH:
|
||||||
print("GITHUB_EVENT_PATH not set")
|
print("GITHUB_EVENT_PATH not set")
|
||||||
return
|
return
|
||||||
|
try:
|
||||||
event_payload = json.load(open(GITHUB_EVENT_PATH, 'r'))
|
event_payload = json.load(open(GITHUB_EVENT_PATH, 'r'))
|
||||||
RUNNER_DEBUG = os.environ.get('RUNNER_DEBUG', None)
|
except json.decoder.JSONDecodeError as e:
|
||||||
if not RUNNER_DEBUG:
|
print(f"Failed to parse JSON: {e}")
|
||||||
print("RUNNER_DEBUG not set")
|
return
|
||||||
OPENAI_KEY = os.environ.get('OPENAI_KEY', None)
|
OPENAI_KEY = os.environ.get('OPENAI_KEY', None)
|
||||||
if not OPENAI_KEY:
|
if not OPENAI_KEY:
|
||||||
print("OPENAI_KEY not set")
|
print("OPENAI_KEY not set")
|
||||||
@ -48,10 +52,21 @@ async def run_action():
|
|||||||
if comment_body:
|
if comment_body:
|
||||||
pr_url = event_payload.get("issue", {}).get("pull_request", {}).get("url", None)
|
pr_url = event_payload.get("issue", {}).get("pull_request", {}).get("url", None)
|
||||||
if pr_url:
|
if pr_url:
|
||||||
if comment_body.strip().lower() == "review":
|
body = comment_body.strip().lower()
|
||||||
|
if any(cmd in body for cmd in ["/review", "/review_pr"]):
|
||||||
await PRReviewer(pr_url).review()
|
await PRReviewer(pr_url).review()
|
||||||
elif comment_body.lstrip().lower().startswith("answer"):
|
elif any(cmd in body for cmd in ["/describe", "/describe_pr"]):
|
||||||
await PRQuestions(pr_url, comment_body).answer()
|
await PRDescription(pr_url).describe()
|
||||||
|
elif any(cmd in body for cmd in ["/improve", "/improve_code"]):
|
||||||
|
await PRCodeSuggestions(pr_url).suggest()
|
||||||
|
elif any(cmd in body for cmd in ["/ask", "/ask_question"]):
|
||||||
|
pattern = r'(/ask|/ask_question)\s*(.*)'
|
||||||
|
matches = re.findall(pattern, comment_body, re.IGNORECASE)
|
||||||
|
if matches:
|
||||||
|
question = matches[0][1]
|
||||||
|
await PRQuestions(pr_url, question).answer()
|
||||||
|
else:
|
||||||
|
print(f"Unknown command: {body}")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -96,15 +96,18 @@ class PRReviewer:
|
|||||||
|
|
||||||
if not self.cli_mode:
|
if not self.cli_mode:
|
||||||
markdown_text += "\n### How to use\n"
|
markdown_text += "\n### How to use\n"
|
||||||
|
commands_text = "> /review - Ask for a new review after your update the PR\n" \
|
||||||
|
"> /describe - Modify the PR title and description based " \
|
||||||
|
"on the PR's contents.\n" \
|
||||||
|
"> /improve - Suggest improvements to the code in the PR as pull " \
|
||||||
|
"request comments ready to commit.\n" \
|
||||||
|
"> /ask <QUESTION> - Ask a question about the PR.\n"
|
||||||
if user and '[bot]' not in user:
|
if user and '[bot]' not in user:
|
||||||
markdown_text += f"> Tag me in a comment '@{user}' to ask for a new review after you update the PR.\n"
|
markdown_text += f"> Tag me in a comment '@{user}' and add one of the following commands:\n" + \
|
||||||
markdown_text += "> You can also tag me and ask any question, " \
|
commands_text
|
||||||
f"for example '@{user} is the PR ready for merge?'"
|
|
||||||
else:
|
else:
|
||||||
markdown_text += "> Add a comment that says 'review' to ask for a new review " \
|
markdown_text += "> Add a comment to to invoke PR-Agent, use one of the following commands:\n" + \
|
||||||
"after you update the PR.\n"
|
commands_text
|
||||||
markdown_text += "> You can also add a comment that says 'answer QUESTION', " \
|
|
||||||
"for example 'answer is the PR ready for merge?'"
|
|
||||||
|
|
||||||
if settings.config.verbosity_level >= 2:
|
if settings.config.verbosity_level >= 2:
|
||||||
logging.info(f"Markdown response:\n{markdown_text}")
|
logging.info(f"Markdown response:\n{markdown_text}")
|
||||||
|
Reference in New Issue
Block a user