ultralytics-actions 0.0.25__py3-none-any.whl → 0.0.30__py3-none-any.whl
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.
- actions/__init__.py +1 -1
- actions/first_interaction.py +2 -2
- actions/summarize_pr.py +117 -15
- actions/utils/__init__.py +2 -0
- actions/utils/github_utils.py +21 -0
- {ultralytics_actions-0.0.25.dist-info → ultralytics_actions-0.0.30.dist-info}/METADATA +1 -1
- ultralytics_actions-0.0.30.dist-info/RECORD +15 -0
- ultralytics_actions-0.0.25.dist-info/RECORD +0 -15
- {ultralytics_actions-0.0.25.dist-info → ultralytics_actions-0.0.30.dist-info}/LICENSE +0 -0
- {ultralytics_actions-0.0.25.dist-info → ultralytics_actions-0.0.30.dist-info}/WHEEL +0 -0
- {ultralytics_actions-0.0.25.dist-info → ultralytics_actions-0.0.30.dist-info}/entry_points.txt +0 -0
- {ultralytics_actions-0.0.25.dist-info → ultralytics_actions-0.0.30.dist-info}/top_level.txt +0 -0
actions/__init__.py
CHANGED
actions/first_interaction.py
CHANGED
@@ -335,7 +335,7 @@ INSTRUCTIONS:
|
|
335
335
|
- Adapt the example {issue_type} response below as appropriate, keeping all badges, links and references provided
|
336
336
|
- For bug reports, specifically request a minimum reproducible example (MRE) if not provided
|
337
337
|
- INCLUDE ALL LINKS AND INSTRUCTIONS IN THE EXAMPLE BELOW, customized as appropriate
|
338
|
-
-
|
338
|
+
- Mention to the user that this is an automated response and that an Ultralytics engineer will also assist soon
|
339
339
|
- Do not add a sign-off or valediction like "best regards" at the end of your response
|
340
340
|
- Do not add spaces between bullet points or numbered lists
|
341
341
|
- Only link to files or URLs in the example below, do not add external links
|
@@ -359,7 +359,7 @@ YOUR {issue_type.upper()} RESPONSE:
|
|
359
359
|
messages = [
|
360
360
|
{
|
361
361
|
"role": "system",
|
362
|
-
"content": f"You are a helpful assistant responding to GitHub {issue_type}s for
|
362
|
+
"content": f"You are a helpful assistant responding to GitHub {issue_type}s for {org_name}.",
|
363
363
|
},
|
364
364
|
{"role": "user", "content": prompt},
|
365
365
|
]
|
actions/summarize_pr.py
CHANGED
@@ -10,15 +10,72 @@ 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
|
|
16
|
-
#
|
17
|
+
# Constants
|
17
18
|
SUMMARY_START = (
|
18
19
|
"## 🛠️ PR Summary\n\n<sub>Made with ❤️ by [Ultralytics Actions](https://github.com/ultralytics/actions)<sub>\n\n"
|
19
20
|
)
|
20
21
|
|
21
22
|
|
23
|
+
def generate_merge_message(pr_author, contributors, pr_summary=None):
|
24
|
+
"""Generates a thank-you message for merged PR contributors."""
|
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"Keep the message concise yet relevant to the specific contributions in this PR. "
|
42
|
+
f"We want the contributors to feel their effort is appreciated 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 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):
|
57
|
+
"""Generates a personalized issue comment using based on the PR context."""
|
58
|
+
messages = [
|
59
|
+
{
|
60
|
+
"role": "system",
|
61
|
+
"content": "You are an Ultralytics AI assistant. Generate friendly GitHub issue comments. No @ mentions or direct addressing.",
|
62
|
+
},
|
63
|
+
{
|
64
|
+
"role": "user",
|
65
|
+
"content": f"Write a GitHub issue comment announcing a potential fix has been merged in linked PR {pr_url}\n\n"
|
66
|
+
f"Context from PR:\n{pr_summary}\n\n"
|
67
|
+
f"Include:\n"
|
68
|
+
f"1. An explanation of key changes from the PR that may resolve this issue\n"
|
69
|
+
f"2. Options for testing if PR changes have resolved this issue:\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 the PR changes resolve the issue\n"
|
73
|
+
f"4. Thank 🙏 for reporting the issue and welcome any further feedback if the issue persists\n\n",
|
74
|
+
},
|
75
|
+
]
|
76
|
+
return get_completion(messages)
|
77
|
+
|
78
|
+
|
22
79
|
def generate_pr_summary(repo_name, diff_text):
|
23
80
|
"""Generates a concise, professional summary of a PR using OpenAI's API for Ultralytics repositories."""
|
24
81
|
if not diff_text:
|
@@ -71,9 +128,8 @@ def update_pr_description(repo_name, pr_number, new_summary, max_retries=2):
|
|
71
128
|
return update_response.status_code
|
72
129
|
|
73
130
|
|
74
|
-
def label_fixed_issues(pr_number):
|
75
|
-
"""Labels issues
|
76
|
-
# GraphQL query to get closing issues
|
131
|
+
def label_fixed_issues(pr_number, pr_summary):
|
132
|
+
"""Labels issues closed by this PR when merged, notifies users, and returns PR contributors."""
|
77
133
|
query = """
|
78
134
|
query($owner: String!, $repo: String!, $pr_number: Int!) {
|
79
135
|
repository(owner: $owner, name: $repo) {
|
@@ -83,6 +139,15 @@ query($owner: String!, $repo: String!, $pr_number: Int!) {
|
|
83
139
|
number
|
84
140
|
}
|
85
141
|
}
|
142
|
+
url
|
143
|
+
body
|
144
|
+
author { login, __typename }
|
145
|
+
reviews(first: 50) {
|
146
|
+
nodes { author { login, __typename } }
|
147
|
+
}
|
148
|
+
comments(first: 50) {
|
149
|
+
nodes { author { login, __typename } }
|
150
|
+
}
|
86
151
|
}
|
87
152
|
}
|
88
153
|
}
|
@@ -94,29 +159,59 @@ query($owner: String!, $repo: String!, $pr_number: Int!) {
|
|
94
159
|
response = requests.post(graphql_url, json={"query": query, "variables": variables}, headers=GITHUB_HEADERS)
|
95
160
|
if response.status_code != 200:
|
96
161
|
print(f"Failed to fetch linked issues. Status code: {response.status_code}")
|
97
|
-
return
|
162
|
+
return [], None
|
98
163
|
|
99
164
|
try:
|
100
|
-
|
101
|
-
|
165
|
+
data = response.json()["data"]["repository"]["pullRequest"]
|
166
|
+
comments = data["reviews"]["nodes"] + data["comments"]["nodes"] # merge lists
|
167
|
+
author = data["author"]["login"]
|
168
|
+
|
169
|
+
# Get unique contributors from reviews and comments
|
170
|
+
contributors = {x["author"]["login"] for x in comments if x["author"]["__typename"] != "Bot"}
|
171
|
+
contributors.discard(author) # Remove author from contributors list
|
172
|
+
|
173
|
+
# Generate personalized comment
|
174
|
+
comment = generate_issue_comment(pr_url=data["url"], pr_summary=pr_summary)
|
175
|
+
|
176
|
+
# Update linked issues
|
177
|
+
for issue in data["closingIssuesReferences"]["nodes"]:
|
102
178
|
issue_number = issue["number"]
|
179
|
+
# Add fixed label
|
103
180
|
label_url = f"{GITHUB_API_URL}/repos/{GITHUB_REPOSITORY}/issues/{issue_number}/labels"
|
104
181
|
label_response = requests.post(label_url, json={"labels": ["fixed"]}, headers=GITHUB_HEADERS)
|
105
|
-
|
106
|
-
|
182
|
+
|
183
|
+
# Add comment
|
184
|
+
comment_url = f"{GITHUB_API_URL}/repos/{GITHUB_REPOSITORY}/issues/{issue_number}/comments"
|
185
|
+
comment_response = requests.post(comment_url, json={"body": comment}, headers=GITHUB_HEADERS)
|
186
|
+
|
187
|
+
if label_response.status_code == 200 and comment_response.status_code == 201:
|
188
|
+
print(f"Added 'fixed' label and comment to issue #{issue_number}")
|
107
189
|
else:
|
108
|
-
print(
|
190
|
+
print(
|
191
|
+
f"Failed to update issue #{issue_number}. Label status: {label_response.status_code}, "
|
192
|
+
f"Comment status: {comment_response.status_code}"
|
193
|
+
)
|
194
|
+
|
195
|
+
return contributors, author
|
109
196
|
except KeyError as e:
|
110
197
|
print(f"Error parsing GraphQL response: {e}")
|
111
|
-
return
|
198
|
+
return [], None
|
199
|
+
|
200
|
+
|
201
|
+
def remove_todos_on_merge(pr_number):
|
202
|
+
"""Removes specified labels from PR."""
|
203
|
+
for label in ["TODO"]: # Can be extended with more labels in the future
|
204
|
+
requests.delete(
|
205
|
+
f"{GITHUB_API_URL}/repos/{GITHUB_REPOSITORY}/issues/{pr_number}/labels/{label}", headers=GITHUB_HEADERS
|
206
|
+
)
|
112
207
|
|
113
208
|
|
114
209
|
def main():
|
115
|
-
"""Summarize a pull request and update its description with
|
210
|
+
"""Summarize a pull request and update its description with a summary."""
|
116
211
|
pr_number = PR["number"]
|
117
212
|
|
118
213
|
print(f"Retrieving diff for PR {pr_number}")
|
119
|
-
diff = get_pr_diff(
|
214
|
+
diff = get_pr_diff(pr_number)
|
120
215
|
|
121
216
|
# Generate PR summary
|
122
217
|
print("Generating PR summary...")
|
@@ -130,10 +225,17 @@ def main():
|
|
130
225
|
else:
|
131
226
|
print(f"Failed to update PR description. Status code: {status_code}")
|
132
227
|
|
133
|
-
# Update linked issues
|
228
|
+
# Update linked issues and post thank you message if merged
|
134
229
|
if PR.get("merged"):
|
135
230
|
print("PR is merged, labeling fixed issues...")
|
136
|
-
label_fixed_issues(
|
231
|
+
contributors, author = label_fixed_issues(pr_number, summary)
|
232
|
+
print("Removing TODO label from PR...")
|
233
|
+
remove_todos_on_merge(pr_number)
|
234
|
+
username = get_github_username() # get GITHUB_TOKEN username
|
235
|
+
if author and author != username:
|
236
|
+
print("Posting PR author thank you message...")
|
237
|
+
contributors.discard(username)
|
238
|
+
post_merge_message(pr_number, author, contributors, summary)
|
137
239
|
|
138
240
|
|
139
241
|
if __name__ == "__main__":
|
actions/utils/__init__.py
CHANGED
@@ -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
|
)
|
actions/utils/github_utils.py
CHANGED
@@ -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}"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: ultralytics-actions
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.30
|
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>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
actions/__init__.py,sha256=vI2djkUXlHUwTefYfOmHPdRKls05ZS0all3rQSdIKXw,749
|
2
|
+
actions/first_interaction.py,sha256=ehHkFwWr-14LgesGmO4lOthu8z626FgvV9e6fiyzN8Q,17648
|
3
|
+
actions/summarize_pr.py,sha256=vkMVfy8ukoWMf_BrS1Ykh34GHFDkREaw9hNCiUUIClE,10529
|
4
|
+
actions/summarize_release.py,sha256=l8NBdTAXLysfNKl1Kf_1tyuBRmeEBLyzTDXS6s5_eQg,8350
|
5
|
+
actions/update_markdown_code_blocks.py,sha256=WBNcMD_KKsZS-qSPBn6O1G0ggQ_VrT-jTQffbg7xH_M,6369
|
6
|
+
actions/utils/__init__.py,sha256=e3vKraD3_YpFiVUn3B3KR0diqG1ZrXMV9eXQYikObxo,1025
|
7
|
+
actions/utils/common_utils.py,sha256=XT8GG0SWBtlZLruA0nKrY4AJpkitvPbM8zndE8etuDo,3548
|
8
|
+
actions/utils/github_utils.py,sha256=QBNBx_qb3cbMwYmAQqEmUmGj33jir8Hjc6dr8Jk3vws,6286
|
9
|
+
actions/utils/openai_utils.py,sha256=O0sYtLFTPqC4inw9_NWcVNlhElcp0iJ1FvAX3L3arKo,1834
|
10
|
+
ultralytics_actions-0.0.30.dist-info/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
11
|
+
ultralytics_actions-0.0.30.dist-info/METADATA,sha256=TZDWraFY1tpuTQMnkkF2FHmTM8u5JiP-TLE6oHtvDJc,10591
|
12
|
+
ultralytics_actions-0.0.30.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
13
|
+
ultralytics_actions-0.0.30.dist-info/entry_points.txt,sha256=GowvOFplj0C7JmsjbKcbpgLpdf2r921pcaOQkAHWZRA,378
|
14
|
+
ultralytics_actions-0.0.30.dist-info/top_level.txt,sha256=5apM5x80QlJcGbACn1v3fkmIuL1-XQCKcItJre7w7Tw,8
|
15
|
+
ultralytics_actions-0.0.30.dist-info/RECORD,,
|
@@ -1,15 +0,0 @@
|
|
1
|
-
actions/__init__.py,sha256=t0B8aBSaP2FlwHHA-wL74q6apk3Kgkh3m6RddLrLwaw,749
|
2
|
-
actions/first_interaction.py,sha256=cLXo5hmhOgTfk7F9LbGJeRdg6vvrKUe-0taCa_TkpAg,17683
|
3
|
-
actions/summarize_pr.py,sha256=R-GqxWF24CYRb-jPNF2tckXUV9g5BG_e3tKZwHlhtNQ,5737
|
4
|
-
actions/summarize_release.py,sha256=l8NBdTAXLysfNKl1Kf_1tyuBRmeEBLyzTDXS6s5_eQg,8350
|
5
|
-
actions/update_markdown_code_blocks.py,sha256=WBNcMD_KKsZS-qSPBn6O1G0ggQ_VrT-jTQffbg7xH_M,6369
|
6
|
-
actions/utils/__init__.py,sha256=0vRjFc7i2WOlphuxdUxQo5BuNipgwGw2Bs-fdUBDeUw,973
|
7
|
-
actions/utils/common_utils.py,sha256=XT8GG0SWBtlZLruA0nKrY4AJpkitvPbM8zndE8etuDo,3548
|
8
|
-
actions/utils/github_utils.py,sha256=O9hTo8amqT890YIixMvALNur7R8SGRMJ8xXJT3nwpTc,5659
|
9
|
-
actions/utils/openai_utils.py,sha256=O0sYtLFTPqC4inw9_NWcVNlhElcp0iJ1FvAX3L3arKo,1834
|
10
|
-
ultralytics_actions-0.0.25.dist-info/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
11
|
-
ultralytics_actions-0.0.25.dist-info/METADATA,sha256=lp9CnsStoZ6FQdgpEpCpxcLCZbna4D-cKCC5mOAAVys,10591
|
12
|
-
ultralytics_actions-0.0.25.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
13
|
-
ultralytics_actions-0.0.25.dist-info/entry_points.txt,sha256=GowvOFplj0C7JmsjbKcbpgLpdf2r921pcaOQkAHWZRA,378
|
14
|
-
ultralytics_actions-0.0.25.dist-info/top_level.txt,sha256=5apM5x80QlJcGbACn1v3fkmIuL1-XQCKcItJre7w7Tw,8
|
15
|
-
ultralytics_actions-0.0.25.dist-info/RECORD,,
|
File without changes
|
File without changes
|
{ultralytics_actions-0.0.25.dist-info → ultralytics_actions-0.0.30.dist-info}/entry_points.txt
RENAMED
File without changes
|
File without changes
|