ultralytics-actions 0.1.2__tar.gz → 0.1.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ultralytics-actions might be problematic. Click here for more details.
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/PKG-INFO +1 -1
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/actions/__init__.py +1 -1
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/actions/first_interaction.py +16 -1
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/actions/review_pr.py +22 -35
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/actions/utils/github_utils.py +17 -3
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/ultralytics_actions.egg-info/PKG-INFO +1 -1
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/LICENSE +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/README.md +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/actions/dispatch_actions.py +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/actions/summarize_pr.py +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/actions/summarize_release.py +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/actions/update_file_headers.py +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/actions/update_markdown_code_blocks.py +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/actions/utils/__init__.py +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/actions/utils/common_utils.py +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/actions/utils/openai_utils.py +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/actions/utils/version_utils.py +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/pyproject.toml +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/setup.cfg +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/tests/test_cli_commands.py +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/tests/test_common_utils.py +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/tests/test_dispatch_actions.py +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/tests/test_file_headers.py +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/tests/test_first_interaction.py +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/tests/test_github_utils.py +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/tests/test_init.py +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/tests/test_openai_utils.py +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/tests/test_summarize_pr.py +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/tests/test_summarize_release.py +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/tests/test_update_markdown_codeblocks.py +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/tests/test_urls.py +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/ultralytics_actions.egg-info/SOURCES.txt +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/ultralytics_actions.egg-info/dependency_links.txt +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/ultralytics_actions.egg-info/entry_points.txt +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/ultralytics_actions.egg-info/requires.txt +0 -0
- {ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/ultralytics_actions.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ultralytics-actions
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Ultralytics Actions for GitHub automation and PR management.
|
|
5
5
|
Author-email: Glenn Jocher <glenn.jocher@ultralytics.com>
|
|
6
6
|
Maintainer-email: Ultralytics <hello@ultralytics.com>
|
|
@@ -5,12 +5,14 @@ from __future__ import annotations
|
|
|
5
5
|
import os
|
|
6
6
|
import time
|
|
7
7
|
|
|
8
|
+
from . import review_pr
|
|
8
9
|
from .utils import Action, filter_labels, get_completion, get_pr_open_response, remove_html_comments
|
|
9
10
|
|
|
10
11
|
SUMMARY_START = (
|
|
11
12
|
"## 🛠️ PR Summary\n\n<sub>Made with ❤️ by [Ultralytics Actions](https://github.com/ultralytics/actions)<sub>\n\n"
|
|
12
13
|
)
|
|
13
14
|
BLOCK_USER = os.getenv("BLOCK_USER", "false").lower() == "true"
|
|
15
|
+
AUTO_PR_REVIEW = os.getenv("REVIEW", "true").lower() == "true"
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
def apply_and_check_labels(event, number, node_id, issue_type, username, labels, label_descriptions):
|
|
@@ -185,13 +187,18 @@ def main(*args, **kwargs):
|
|
|
185
187
|
|
|
186
188
|
# Use unified PR open response for new PRs (summary + labels + first comment in 1 API call)
|
|
187
189
|
if issue_type == "pull request" and action == "opened":
|
|
190
|
+
if event.should_skip_pr_author():
|
|
191
|
+
return
|
|
192
|
+
|
|
188
193
|
print("Processing PR open with unified API call...")
|
|
189
194
|
diff = event.get_pr_diff()
|
|
190
195
|
response = get_pr_open_response(event.repository, diff, title, body, label_descriptions)
|
|
191
196
|
|
|
192
197
|
if summary := response.get("summary"):
|
|
193
198
|
print("Updating PR description with summary...")
|
|
194
|
-
event.update_pr_description(number, SUMMARY_START + summary)
|
|
199
|
+
event.update_pr_description(number, SUMMARY_START + summary + "\n\n" + body)
|
|
200
|
+
else:
|
|
201
|
+
summary = body
|
|
195
202
|
|
|
196
203
|
if relevant_labels := response.get("labels", []):
|
|
197
204
|
apply_and_check_labels(event, number, node_id, issue_type, username, relevant_labels, label_descriptions)
|
|
@@ -200,6 +207,14 @@ def main(*args, **kwargs):
|
|
|
200
207
|
print("Adding first interaction comment...")
|
|
201
208
|
time.sleep(1) # sleep to ensure label added first
|
|
202
209
|
event.add_comment(number, node_id, first_comment, issue_type)
|
|
210
|
+
|
|
211
|
+
# Automatic PR review after first interaction
|
|
212
|
+
if AUTO_PR_REVIEW:
|
|
213
|
+
print("Starting automatic PR review...")
|
|
214
|
+
review_number = review_pr.dismiss_previous_reviews(event)
|
|
215
|
+
review_data = review_pr.generate_pr_review(event.repository, diff, title, summary)
|
|
216
|
+
review_pr.post_review_summary(event, review_data, review_number)
|
|
217
|
+
print("PR review completed")
|
|
203
218
|
return
|
|
204
219
|
|
|
205
220
|
# Handle issues and discussions (NOT PRs)
|
|
@@ -79,34 +79,20 @@ def generate_pr_review(repository: str, diff_text: str, pr_title: str, pr_descri
|
|
|
79
79
|
diff_truncated = len(diff_text) > limit
|
|
80
80
|
lines_changed = sum(len(lines) for lines in diff_files.values())
|
|
81
81
|
|
|
82
|
-
comment_guidance = (
|
|
83
|
-
"Provide up to 1-3 comments only if critical issues exist"
|
|
84
|
-
if lines_changed < 50
|
|
85
|
-
else "Provide up to 3-5 comments only if high-impact issues exist"
|
|
86
|
-
if lines_changed < 200
|
|
87
|
-
else "Provide up to 5-10 comments only for the most critical issues"
|
|
88
|
-
)
|
|
89
|
-
|
|
90
82
|
content = (
|
|
91
83
|
"You are an expert code reviewer for Ultralytics. Provide detailed inline comments on specific code changes.\n\n"
|
|
92
|
-
"Focus on:
|
|
93
|
-
"FORMATTING: Use backticks for
|
|
84
|
+
"Focus on: Bugs, security, performance, best practices, edge cases, error handling\n\n"
|
|
85
|
+
"FORMATTING: Use backticks for code: `x=3`, `file.py`, `function()`\n\n"
|
|
94
86
|
"CRITICAL RULES:\n"
|
|
95
|
-
"1. Quality over quantity
|
|
96
|
-
|
|
97
|
-
"3.
|
|
98
|
-
"4.
|
|
99
|
-
"5.
|
|
100
|
-
"6.
|
|
101
|
-
"
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
"SUMMARY GUIDELINES:\n"
|
|
105
|
-
"- Keep summary brief, clear, and actionable - avoid overly detailed explanations\n"
|
|
106
|
-
"- Highlight only the most important findings\n"
|
|
107
|
-
"- Do NOT include file names or line numbers in the summary - inline comments already show exact locations\n"
|
|
108
|
-
"- Focus on what needs to be fixed, not where\n\n"
|
|
109
|
-
"CODE SUGGESTIONS:\n"
|
|
87
|
+
"1. Quality over quantity - zero comments is fine for clean code, only flag truly important issues\n"
|
|
88
|
+
"2. Combine issues that are directly related to the same problem\n"
|
|
89
|
+
"3. Use 'start_line' and 'line' to highlight multi-line ranges when issues span multiple lines\n"
|
|
90
|
+
"4. Prioritize: CRITICAL bugs/security > HIGH impact > code quality improvements\n"
|
|
91
|
+
"5. Keep comments concise and friendly - avoid jargon\n"
|
|
92
|
+
"6. Skip routine changes: imports, version updates, standard refactoring\n\n"
|
|
93
|
+
"SUMMARY:\n"
|
|
94
|
+
"- Brief and actionable - what needs fixing, not where (locations shown in inline comments)\n\n"
|
|
95
|
+
"SUGGESTIONS:\n"
|
|
110
96
|
"- ONLY provide 'suggestion' field when you have high certainty the code is problematic AND sufficient context for a confident fix\n"
|
|
111
97
|
"- If uncertain about the correct fix, omit 'suggestion' field and explain the concern in 'message' only\n"
|
|
112
98
|
"- Suggestions must be ready-to-merge code with NO comments, placeholders, or explanations\n"
|
|
@@ -118,10 +104,7 @@ def generate_pr_review(repository: str, diff_text: str, pr_title: str, pr_descri
|
|
|
118
104
|
"Return JSON: "
|
|
119
105
|
'{"comments": [{"file": "exact/path", "line": N, "severity": "HIGH", "message": "...", "suggestion": "..."}], "summary": "..."}\n\n'
|
|
120
106
|
"Rules:\n"
|
|
121
|
-
"- Only
|
|
122
|
-
"- Use exact file paths from diff (no ./ prefix)\n"
|
|
123
|
-
"- Line numbers must match NEW file line numbers from @@ hunks\n"
|
|
124
|
-
"- When '- old' then '+ new', new line keeps SAME line number\n"
|
|
107
|
+
"- Only NEW lines (+ in diff), exact paths (no ./), correct line numbers from @@ hunks\n"
|
|
125
108
|
"- Severity: CRITICAL, HIGH, MEDIUM, LOW, SUGGESTION\n"
|
|
126
109
|
f"- Files changed: {len(file_list)} ({', '.join(file_list[:10])}{'...' if len(file_list) > 10 else ''})\n"
|
|
127
110
|
f"- Lines changed: {lines_changed}\n"
|
|
@@ -241,12 +224,12 @@ def post_review_summary(event: Action, review_data: dict, review_number: int) ->
|
|
|
241
224
|
body = (
|
|
242
225
|
f"## {review_title}\n\n"
|
|
243
226
|
"<sub>Made with ❤️ by [Ultralytics Actions](https://github.com/ultralytics/actions)</sub>\n\n"
|
|
244
|
-
f"{review_data.get('summary', 'Review completed')}\n\n"
|
|
227
|
+
f"{review_data.get('summary', 'Review completed')[:1000]}\n\n" # Clip summary length
|
|
245
228
|
)
|
|
246
229
|
|
|
247
230
|
if comments:
|
|
248
231
|
shown = min(len(comments), 10)
|
|
249
|
-
body += f"💬 Posted {shown} inline comment{'s' if shown != 1 else ''}
|
|
232
|
+
body += f"💬 Posted {shown} inline comment{'s' if shown != 1 else ''}\n"
|
|
250
233
|
|
|
251
234
|
if review_data.get("diff_truncated"):
|
|
252
235
|
body += "\n⚠️ **Large PR**: Review focused on critical issues. Some details may not be covered.\n"
|
|
@@ -256,14 +239,15 @@ def post_review_summary(event: Action, review_data: dict, review_number: int) ->
|
|
|
256
239
|
|
|
257
240
|
# Build inline comments for the review
|
|
258
241
|
review_comments = []
|
|
259
|
-
for comment in comments[:10]:
|
|
242
|
+
for comment in comments[:10]: # Limit to 10 comments
|
|
260
243
|
if not (file_path := comment.get("file")) or not (line := comment.get("line", 0)):
|
|
261
244
|
continue
|
|
262
245
|
|
|
263
246
|
severity = comment.get("severity", "SUGGESTION")
|
|
264
|
-
comment_body = f"{EMOJI_MAP.get(severity, '💭')} **{severity}**: {comment.get('message', '')}"
|
|
247
|
+
comment_body = f"{EMOJI_MAP.get(severity, '💭')} **{severity}**: {comment.get('message', '')[:1000]}"
|
|
265
248
|
|
|
266
249
|
if suggestion := comment.get("suggestion"):
|
|
250
|
+
suggestion = suggestion[:1000] # Clip suggestion length
|
|
267
251
|
if "```" not in suggestion:
|
|
268
252
|
# Extract original line indentation and apply to suggestion
|
|
269
253
|
if original_line := review_data.get("diff_files", {}).get(file_path, {}).get(line):
|
|
@@ -307,12 +291,15 @@ def main(*args, **kwargs):
|
|
|
307
291
|
print(f"Skipping: PR state is {event.pr.get('state') if event.pr else 'None'}")
|
|
308
292
|
return
|
|
309
293
|
|
|
294
|
+
# Skip self-authored or bot PRs unless manually review_requested
|
|
295
|
+
if event.event_data.get("action") != "review_requested" and event.should_skip_pr_author():
|
|
296
|
+
return
|
|
297
|
+
|
|
310
298
|
print(f"Starting PR review for #{event.pr['number']}")
|
|
311
299
|
review_number = dismiss_previous_reviews(event)
|
|
312
300
|
|
|
313
301
|
diff = event.get_pr_diff()
|
|
314
|
-
|
|
315
|
-
review = generate_pr_review(event.repository, diff, event.pr.get("title", ""), pr_description)
|
|
302
|
+
review = generate_pr_review(event.repository, diff, event.pr.get("title", ""), event.pr.get("body", ""))
|
|
316
303
|
|
|
317
304
|
post_review_summary(event, review, review_number)
|
|
318
305
|
print("PR review completed")
|
|
@@ -188,6 +188,20 @@ class Action:
|
|
|
188
188
|
"""Checks if a user is a member of the organization."""
|
|
189
189
|
return self.get(f"{GITHUB_API_URL}/orgs/{self.repository.split('/')[0]}/members/{username}").status_code == 204
|
|
190
190
|
|
|
191
|
+
def should_skip_pr_author(self) -> bool:
|
|
192
|
+
"""Checks if PR should be skipped based on author (self-authored or bot PRs)."""
|
|
193
|
+
if not self.pr:
|
|
194
|
+
return False
|
|
195
|
+
if pr_author := self.pr.get("user", {}).get("login"):
|
|
196
|
+
if pr_author == self.get_username():
|
|
197
|
+
print(f"Skipping: PR author ({pr_author}) is the same as bot")
|
|
198
|
+
return True
|
|
199
|
+
# Check both user.type and [bot] suffix for robust bot detection
|
|
200
|
+
if self.pr.get("user", {}).get("type") == "Bot" or pr_author.endswith("[bot]"):
|
|
201
|
+
print(f"Skipping: PR author ({pr_author}) is a bot")
|
|
202
|
+
return True
|
|
203
|
+
return False
|
|
204
|
+
|
|
191
205
|
def is_fork_pr(self) -> bool:
|
|
192
206
|
"""Checks if PR is from a fork (different repo than base)."""
|
|
193
207
|
if not self.pr:
|
|
@@ -386,7 +400,7 @@ Thank you 🙏
|
|
|
386
400
|
try:
|
|
387
401
|
data = response.json()["data"]["repository"]["pullRequest"]
|
|
388
402
|
comments = data["reviews"]["nodes"] + data["comments"]["nodes"]
|
|
389
|
-
|
|
403
|
+
username = self.get_username()
|
|
390
404
|
author = data["author"]["login"] if data["author"]["__typename"] != "Bot" else None
|
|
391
405
|
|
|
392
406
|
contributors = {x["author"]["login"] for x in comments if x["author"]["__typename"] != "Bot"}
|
|
@@ -399,10 +413,10 @@ Thank you 🙏
|
|
|
399
413
|
contributors.add(login)
|
|
400
414
|
|
|
401
415
|
contributors.discard(author)
|
|
402
|
-
contributors.discard(
|
|
416
|
+
contributors.discard(username)
|
|
403
417
|
|
|
404
418
|
pr_credit = ""
|
|
405
|
-
if author and author !=
|
|
419
|
+
if author and author != username:
|
|
406
420
|
pr_credit += f"@{author}"
|
|
407
421
|
if contributors:
|
|
408
422
|
pr_credit += (" with contributions from " if pr_credit else "") + ", ".join(
|
{ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/ultralytics_actions.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ultralytics-actions
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Ultralytics Actions for GitHub automation and PR management.
|
|
5
5
|
Author-email: Glenn Jocher <glenn.jocher@ultralytics.com>
|
|
6
6
|
Maintainer-email: Ultralytics <hello@ultralytics.com>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/actions/update_markdown_code_blocks.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/tests/test_update_markdown_codeblocks.py
RENAMED
|
File without changes
|
|
File without changes
|
{ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/ultralytics_actions.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/ultralytics_actions.egg-info/requires.txt
RENAMED
|
File without changes
|
{ultralytics_actions-0.1.2 → ultralytics_actions-0.1.3}/ultralytics_actions.egg-info/top_level.txt
RENAMED
|
File without changes
|