ultralytics-actions 0.0.27__tar.gz → 0.0.28__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.
- {ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/PKG-INFO +1 -1
- {ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/actions/__init__.py +1 -1
- {ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/actions/summarize_pr.py +73 -17
- {ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/actions/utils/__init__.py +2 -0
- {ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/actions/utils/github_utils.py +21 -0
- {ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/ultralytics_actions.egg-info/PKG-INFO +1 -1
- {ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/LICENSE +0 -0
- {ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/README.md +0 -0
- {ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/actions/first_interaction.py +0 -0
- {ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/actions/summarize_release.py +0 -0
- {ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/actions/update_markdown_code_blocks.py +0 -0
- {ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/actions/utils/common_utils.py +0 -0
- {ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/actions/utils/openai_utils.py +0 -0
- {ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/pyproject.toml +0 -0
- {ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/setup.cfg +0 -0
- {ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/ultralytics_actions.egg-info/SOURCES.txt +0 -0
- {ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/ultralytics_actions.egg-info/dependency_links.txt +0 -0
- {ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/ultralytics_actions.egg-info/entry_points.txt +0 -0
- {ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/ultralytics_actions.egg-info/requires.txt +0 -0
- {ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/ultralytics_actions.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: ultralytics-actions
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.28
|
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>
|
@@ -10,6 +10,7 @@ from .utils import (
|
|
10
10
|
GITHUB_REPOSITORY,
|
11
11
|
PR,
|
12
12
|
get_completion,
|
13
|
+
get_github_username,
|
13
14
|
get_pr_diff,
|
14
15
|
)
|
15
16
|
|
@@ -19,23 +20,57 @@ SUMMARY_START = (
|
|
19
20
|
)
|
20
21
|
|
21
22
|
|
22
|
-
def
|
23
|
+
def generate_merge_message(pr_author, contributors, pr_summary=None):
|
24
|
+
"""Generates an AI thank you message for merged PRs using OpenAI."""
|
25
|
+
contributors_str = ", ".join(f"@{c}" for c in contributors if c != pr_author)
|
26
|
+
mention_str = f"@{pr_author}"
|
27
|
+
if contributors_str:
|
28
|
+
mention_str += f" and {contributors_str}"
|
29
|
+
|
30
|
+
messages = [
|
31
|
+
{
|
32
|
+
"role": "system",
|
33
|
+
"content": "You are an Ultralytics AI assistant. Generate meaningful, inspiring messages to GitHub users.",
|
34
|
+
},
|
35
|
+
{
|
36
|
+
"role": "user",
|
37
|
+
"content": f"Write a friendly thank you for a merged PR by these GitHub contributors: {mention_str}. "
|
38
|
+
f"Context from PR:\n{pr_summary}\n\n"
|
39
|
+
f"Start with the exciting message that this PR is now merged, and weave in an inspiring quote "
|
40
|
+
f"from a famous figure in science, philosophy or stoicism. "
|
41
|
+
f"Make the message relevant to the specific contributions in this PR. "
|
42
|
+
f"We want them to feel their hard work is acknowledged and will make a difference in the world.",
|
43
|
+
},
|
44
|
+
]
|
45
|
+
return get_completion(messages)
|
46
|
+
|
47
|
+
|
48
|
+
def post_merge_message(pr_number, pr_author, contributors, summary):
|
49
|
+
"""Posts AI-generated thank you message on PR after merge."""
|
50
|
+
message = generate_merge_message(pr_author, contributors, summary)
|
51
|
+
comment_url = f"{GITHUB_API_URL}/repos/{GITHUB_REPOSITORY}/issues/{pr_number}/comments"
|
52
|
+
response = requests.post(comment_url, json={"body": message}, headers=GITHUB_HEADERS)
|
53
|
+
return response.status_code == 201
|
54
|
+
|
55
|
+
|
56
|
+
def generate_issue_comment(pr_url, pr_summary):
|
23
57
|
"""Generates a personalized issue comment using AI based on the PR context."""
|
24
58
|
messages = [
|
25
59
|
{
|
26
60
|
"role": "system",
|
27
|
-
"content": "You are
|
61
|
+
"content": "You are an Ultralytics AI assistant. Generate friendly GitHub issue comments. No @ mentions or direct addressing.",
|
28
62
|
},
|
29
63
|
{
|
30
64
|
"role": "user",
|
31
|
-
"content": f"Write a comment for a
|
65
|
+
"content": f"Write a comment for a GitHub issue where a potential fix has been merged in PR: {pr_url}\n\n"
|
66
|
+
f"Context from PR:\n{pr_summary}\n\n"
|
32
67
|
f"Include:\n"
|
33
|
-
f"1.
|
34
|
-
f"2.
|
35
|
-
f" - pip install git+https://github.com/ultralytics/ultralytics.git@main #
|
36
|
-
f" - or await next release\n"
|
37
|
-
f"3. Request
|
38
|
-
f"4. Thank 🙏 for reporting the issue and
|
68
|
+
f"1. An explanation of key changes from the PR that may resolve this issue\n"
|
69
|
+
f"2. Testing options:\n"
|
70
|
+
f" - pip install git+https://github.com/ultralytics/ultralytics.git@main # test latest changes\n"
|
71
|
+
f" - or await next official PyPI release\n"
|
72
|
+
f"3. Request feedback on whether these changes resolve the issue\n"
|
73
|
+
f"4. Thank 🙏 for reporting the issue and welcome any further feedback if the issue persists\n\n",
|
39
74
|
},
|
40
75
|
]
|
41
76
|
return get_completion(messages)
|
@@ -93,8 +128,8 @@ def update_pr_description(repo_name, pr_number, new_summary, max_retries=2):
|
|
93
128
|
return update_response.status_code
|
94
129
|
|
95
130
|
|
96
|
-
def label_fixed_issues(pr_number):
|
97
|
-
"""Labels issues closed by this PR when merged
|
131
|
+
def label_fixed_issues(pr_number, pr_summary):
|
132
|
+
"""Labels issues closed by this PR when merged, notifies users, and returns PR contributors."""
|
98
133
|
query = """
|
99
134
|
query($owner: String!, $repo: String!, $pr_number: Int!) {
|
100
135
|
repository(owner: $owner, name: $repo) {
|
@@ -106,6 +141,13 @@ query($owner: String!, $repo: String!, $pr_number: Int!) {
|
|
106
141
|
}
|
107
142
|
url
|
108
143
|
body
|
144
|
+
author { login }
|
145
|
+
reviews(first: 50) {
|
146
|
+
nodes { author { login } }
|
147
|
+
}
|
148
|
+
comments(first: 50) {
|
149
|
+
nodes { author { login } }
|
150
|
+
}
|
109
151
|
}
|
110
152
|
}
|
111
153
|
}
|
@@ -117,22 +159,29 @@ query($owner: String!, $repo: String!, $pr_number: Int!) {
|
|
117
159
|
response = requests.post(graphql_url, json={"query": query, "variables": variables}, headers=GITHUB_HEADERS)
|
118
160
|
if response.status_code != 200:
|
119
161
|
print(f"Failed to fetch linked issues. Status code: {response.status_code}")
|
120
|
-
return
|
162
|
+
return [], None
|
121
163
|
|
122
164
|
try:
|
123
165
|
data = response.json()["data"]["repository"]["pullRequest"]
|
124
166
|
issues = data["closingIssuesReferences"]["nodes"]
|
167
|
+
author = data["author"]["login"]
|
168
|
+
|
169
|
+
# Get unique contributors from reviews and comments
|
170
|
+
contributors = {review["author"]["login"] for review in data["reviews"]["nodes"]}
|
171
|
+
contributors.update(comment["author"]["login"] for comment in data["comments"]["nodes"])
|
172
|
+
contributors.discard(author) # Remove author from contributors list
|
125
173
|
|
126
174
|
# Generate personalized comment
|
127
|
-
comment = generate_issue_comment(pr_url=data["url"],
|
175
|
+
comment = generate_issue_comment(pr_url=data["url"], pr_summary=pr_summary)
|
128
176
|
|
177
|
+
# Update linked issues
|
129
178
|
for issue in issues:
|
130
179
|
issue_number = issue["number"]
|
131
180
|
# Add fixed label
|
132
181
|
label_url = f"{GITHUB_API_URL}/repos/{GITHUB_REPOSITORY}/issues/{issue_number}/labels"
|
133
182
|
label_response = requests.post(label_url, json={"labels": ["fixed"]}, headers=GITHUB_HEADERS)
|
134
183
|
|
135
|
-
# Add
|
184
|
+
# Add comment
|
136
185
|
comment_url = f"{GITHUB_API_URL}/repos/{GITHUB_REPOSITORY}/issues/{issue_number}/comments"
|
137
186
|
comment_response = requests.post(comment_url, json={"body": comment}, headers=GITHUB_HEADERS)
|
138
187
|
|
@@ -143,9 +192,11 @@ query($owner: String!, $repo: String!, $pr_number: Int!) {
|
|
143
192
|
f"Failed to update issue #{issue_number}. Label status: {label_response.status_code}, "
|
144
193
|
f"Comment status: {comment_response.status_code}"
|
145
194
|
)
|
195
|
+
|
196
|
+
return contributors, author
|
146
197
|
except KeyError as e:
|
147
198
|
print(f"Error parsing GraphQL response: {e}")
|
148
|
-
return
|
199
|
+
return [], None
|
149
200
|
|
150
201
|
|
151
202
|
def remove_todos_on_merge(pr_number):
|
@@ -175,12 +226,17 @@ def main():
|
|
175
226
|
else:
|
176
227
|
print(f"Failed to update PR description. Status code: {status_code}")
|
177
228
|
|
178
|
-
# Update linked issues
|
229
|
+
# Update linked issues and post thank you message if merged
|
179
230
|
if PR.get("merged"):
|
180
231
|
print("PR is merged, labeling fixed issues...")
|
181
|
-
label_fixed_issues(pr_number)
|
232
|
+
contributors, author = label_fixed_issues(pr_number, summary)
|
182
233
|
print("Removing TODO label from PR...")
|
183
234
|
remove_todos_on_merge(pr_number)
|
235
|
+
username = get_github_username() # get GITHUB_TOKEN username
|
236
|
+
if author and author != username:
|
237
|
+
print("Posting PR author thank you message...")
|
238
|
+
contributors.discard(username)
|
239
|
+
post_merge_message(pr_number, author, contributors, summary)
|
184
240
|
|
185
241
|
|
186
242
|
if __name__ == "__main__":
|
@@ -14,6 +14,7 @@ from .github_utils import (
|
|
14
14
|
PR,
|
15
15
|
check_pypi_version,
|
16
16
|
get_github_data,
|
17
|
+
get_github_username,
|
17
18
|
get_pr_diff,
|
18
19
|
graphql_request,
|
19
20
|
ultralytics_actions_info,
|
@@ -38,6 +39,7 @@ __all__ = (
|
|
38
39
|
"OPENAI_API_KEY",
|
39
40
|
"OPENAI_MODEL",
|
40
41
|
"get_completion",
|
42
|
+
"get_github_username",
|
41
43
|
"check_pypi_version",
|
42
44
|
"ultralytics_actions_info",
|
43
45
|
)
|
@@ -24,6 +24,27 @@ PR = EVENT_DATA.get("pull_request", {})
|
|
24
24
|
DISCUSSION = EVENT_DATA.get("discussion", {})
|
25
25
|
|
26
26
|
|
27
|
+
def get_github_username():
|
28
|
+
"""Gets username associated with the GitHub token in GITHUB_HEADERS."""
|
29
|
+
query = """
|
30
|
+
query {
|
31
|
+
viewer {
|
32
|
+
login
|
33
|
+
}
|
34
|
+
}
|
35
|
+
"""
|
36
|
+
response = requests.post("https://api.github.com/graphql", json={"query": query}, headers=GITHUB_HEADERS)
|
37
|
+
if response.status_code != 200:
|
38
|
+
print(f"Failed to fetch authenticated user. Status code: {response.status_code}")
|
39
|
+
return None
|
40
|
+
|
41
|
+
try:
|
42
|
+
return response.json()["data"]["viewer"]["login"]
|
43
|
+
except KeyError as e:
|
44
|
+
print(f"Error parsing authenticated user response: {e}")
|
45
|
+
return None
|
46
|
+
|
47
|
+
|
27
48
|
def get_pr_diff(pr_number: int) -> str:
|
28
49
|
"""Retrieves the diff content for a specified pull request in a GitHub repository."""
|
29
50
|
url = f"{GITHUB_API_URL}/repos/{GITHUB_REPOSITORY}/pulls/{pr_number}"
|
{ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/ultralytics_actions.egg-info/PKG-INFO
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: ultralytics-actions
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.28
|
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
|
{ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/actions/update_markdown_code_blocks.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/ultralytics_actions.egg-info/SOURCES.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|
{ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/ultralytics_actions.egg-info/requires.txt
RENAMED
File without changes
|
{ultralytics_actions-0.0.27 → ultralytics_actions-0.0.28}/ultralytics_actions.egg-info/top_level.txt
RENAMED
File without changes
|