-
Notifications
You must be signed in to change notification settings - Fork 467
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add generate PR description workflow #3042
Closed
Closed
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
479b3c4
Added PR workflow
htahir1 e110bda
Added PR workflow
htahir1 cee2803
Added PR workflow
htahir1 f8b044f
Added PR workflow
htahir1 051a1f0
Added PR workflow
htahir1 d603821
Added PR workflow
htahir1 8dcc99b
Added PR workflow
htahir1 350094a
Added PR workflow
htahir1 74c3468
Added PR workflow
htahir1 fd8ac23
Added PR workflow
htahir1 155f389
Added PR workflow
htahir1 a816e26
Added PR workflow
htahir1 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
name: Auto PR Description | ||
|
||
on: | ||
pull_request: | ||
types: [opened] | ||
|
||
concurrency: | ||
group: ${{ github.workflow }}-${{ github.event.pull_request.number }} | ||
cancel-in-progress: true | ||
|
||
jobs: | ||
auto-describe: | ||
runs-on: ubuntu-latest | ||
if: github.event.pull_request.draft == false | ||
permissions: | ||
contents: read | ||
pull-requests: write | ||
issues: write | ||
steps: | ||
- name: Checkout code | ||
uses: actions/[email protected] | ||
|
||
- name: Set up Python | ||
uses: actions/[email protected] | ||
with: | ||
python-version: '3.11' | ||
|
||
- name: Install dependencies | ||
run: | | ||
curl -LsSf https://astral.sh/uv/install.sh | sh | ||
source $HOME/.cargo/env | ||
uv pip install --system requests openai | ||
htahir1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
- name: Check for previous successful run | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This workflow would run only when the PR is |
||
id: check_comment | ||
run: | | ||
PR_NUMBER="${{ github.event.pull_request.number }}" | ||
COMMENT=$(gh api -X GET "/repos/${{ github.repository }}/issues/${PR_NUMBER}/comments" | jq '.[] | select(.body | contains("Auto PR description generated successfully")) | .id') | ||
if [ -n "$COMMENT" ]; then | ||
echo "Workflow has already run successfully for this PR." | ||
echo "skip=true" >> $GITHUB_OUTPUT | ||
else | ||
echo "skip=false" >> $GITHUB_OUTPUT | ||
fi | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
||
- name: Generate PR description | ||
if: steps.check_comment.outputs.skip == 'false' | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | ||
run: python scripts/generate_pr_description.py | ||
|
||
- name: Add success comment | ||
if: steps.check_comment.outputs.skip == 'false' | ||
run: | | ||
PR_NUMBER="${{ github.event.pull_request.number }}" | ||
gh issue comment ${PR_NUMBER} --body "Auto PR description generated successfully" | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
||
- name: Check for errors | ||
if: failure() | ||
run: | | ||
echo "The PR description generation failed. Please check the logs for more information." | ||
htahir1 marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import json | ||
import os | ||
import requests | ||
import openai | ||
|
||
MAX_CHARS = 400000 # Maximum characters for changes summary | ||
|
||
def truncate_changes(changes_summary): | ||
"""Truncates the changes summary to fit within MAX_CHARS.""" | ||
total_chars = 0 | ||
truncated_summary = [] | ||
for change in changes_summary: | ||
change_chars = len(change) | ||
if total_chars + change_chars > MAX_CHARS: | ||
remaining_chars = MAX_CHARS - total_chars | ||
if remaining_chars > 50: # Ensure we're not adding just a few characters | ||
truncated_change = change[:remaining_chars] | ||
truncated_summary.append(truncated_change + "...") | ||
break | ||
total_chars += change_chars | ||
truncated_summary.append(change) | ||
return truncated_summary | ||
|
||
def generate_pr_description(): | ||
# GitHub API setup | ||
htahir1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
token = os.environ['GITHUB_TOKEN'] | ||
repo = os.environ['GITHUB_REPOSITORY'] | ||
|
||
# Get PR number from the event payload | ||
with open(os.environ['GITHUB_EVENT_PATH']) as f: | ||
event = json.load(f) | ||
pr_number = event['pull_request']['number'] | ||
|
||
headers = {'Authorization': f'token {token}'} | ||
api_url = f'https://api.github.com/repos/{repo}/pulls/{pr_number}' | ||
|
||
# Get current PR description | ||
pr_info = requests.get(api_url, headers=headers).json() | ||
current_description = pr_info['body'] or '' | ||
htahir1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
# Check if description matches the default template | ||
default_template_indicator = "I implemented/fixed _ to achieve _." | ||
|
||
if default_template_indicator in current_description: | ||
# Get PR files | ||
files_url = f'{api_url}/files' | ||
files = requests.get(files_url, headers=headers).json() | ||
|
||
# Process files | ||
changes_summary = [] | ||
for file in files: | ||
filename = file['filename'] | ||
status = file['status'] | ||
|
||
if status == 'removed': | ||
changes_summary.append(f"Removed file: {filename}") | ||
elif status == 'added' or status == 'modified': | ||
if file['binary']: | ||
changes_summary.append(f"Modified binary file: {filename}") | ||
else: | ||
patch = file.get('patch', '') | ||
if patch: | ||
changes_summary.append(f"Modified {filename}:") | ||
changes_summary.append(patch) | ||
elif status == 'renamed': | ||
changes_summary.append(f"Renamed file from {file['previous_filename']} to {filename}") | ||
|
||
# Truncate changes summary if it's too long | ||
truncated_changes = truncate_changes(changes_summary) | ||
changes_text = "\n".join(truncated_changes) | ||
|
||
# Generate description using OpenAI | ||
openai.api_key = os.environ['OPENAI_API_KEY'] | ||
response = openai.OpenAI().chat.completions.create( | ||
model="gpt-4o-mini", | ||
messages=[ | ||
{"role": "system", "content": "You are a helpful assistant that generates concise pull request descriptions based on changes to files."}, | ||
{"role": "user", "content": f"Generate a brief, informative pull request description based on these changes:\n\n{changes_text}"} | ||
], | ||
max_tokens=1000 | ||
) | ||
htahir1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
generated_description = response.choices[0].message.content.strip() | ||
|
||
# Fetch the latest PR description right before updating | ||
pr_info = requests.get(api_url, headers=headers).json() | ||
latest_description = pr_info['body'] or '' | ||
|
||
# Only replace the default template indicator with the generated description | ||
if default_template_indicator in latest_description: | ||
updated_description = latest_description.replace(default_template_indicator, generated_description) | ||
|
||
# Update PR description | ||
data = {'body': updated_description} | ||
requests.patch(api_url, json=data, headers=headers) | ||
print(f"Updated PR description with generated content") | ||
return True | ||
else: | ||
print("Default template indicator no longer present. No changes made.") | ||
return False | ||
else: | ||
print("PR already has a non-default description. No action taken.") | ||
return False | ||
|
||
if __name__ == "__main__": | ||
generate_pr_description() |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I open the PR as a draft and then later change it to "Ready for Review", this code will not run again.