ultralytics-actions 0.0.55__py3-none-any.whl → 0.0.57__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/summarize_pr.py +16 -5
- actions/update_markdown_code_blocks.py +77 -32
- {ultralytics_actions-0.0.55.dist-info → ultralytics_actions-0.0.57.dist-info}/METADATA +3 -2
- ultralytics_actions-0.0.57.dist-info/RECORD +15 -0
- {ultralytics_actions-0.0.55.dist-info → ultralytics_actions-0.0.57.dist-info}/WHEEL +1 -1
- ultralytics_actions-0.0.55.dist-info/RECORD +0 -15
- {ultralytics_actions-0.0.55.dist-info → ultralytics_actions-0.0.57.dist-info}/entry_points.txt +0 -0
- {ultralytics_actions-0.0.55.dist-info → ultralytics_actions-0.0.57.dist-info/licenses}/LICENSE +0 -0
- {ultralytics_actions-0.0.55.dist-info → ultralytics_actions-0.0.57.dist-info}/top_level.txt +0 -0
actions/__init__.py
CHANGED
actions/summarize_pr.py
CHANGED
@@ -44,8 +44,13 @@ def post_merge_message(pr_number, pr_url, repository, summary, pr_credit, header
|
|
44
44
|
return response.status_code == 201
|
45
45
|
|
46
46
|
|
47
|
-
def generate_issue_comment(pr_url, pr_summary, pr_credit):
|
48
|
-
"""Generates
|
47
|
+
def generate_issue_comment(pr_url, pr_summary, pr_credit, pr_title=""):
|
48
|
+
"""Generates personalized issue comment based on PR context."""
|
49
|
+
# Extract repo info from PR URL (format: api.github.com/repos/owner/repo/pulls/number)
|
50
|
+
repo_parts = pr_url.split("/repos/")[1].split("/pulls/")[0] if "/repos/" in pr_url else ""
|
51
|
+
owner_repo = repo_parts.split("/")
|
52
|
+
repo_name = owner_repo[-1] if len(owner_repo) > 1 else "package"
|
53
|
+
|
49
54
|
messages = [
|
50
55
|
{
|
51
56
|
"role": "system",
|
@@ -54,13 +59,15 @@ def generate_issue_comment(pr_url, pr_summary, pr_credit):
|
|
54
59
|
{
|
55
60
|
"role": "user",
|
56
61
|
"content": f"Write a GitHub issue comment announcing a potential fix for this issue is now merged in linked PR {pr_url} by {pr_credit}\n\n"
|
62
|
+
f"PR Title: {pr_title}\n\n"
|
57
63
|
f"Context from PR:\n{pr_summary}\n\n"
|
58
64
|
f"Include:\n"
|
59
65
|
f"1. An explanation of key changes from the PR that may resolve this issue\n"
|
60
66
|
f"2. Credit to the PR author and contributors\n"
|
61
67
|
f"3. Options for testing if PR changes have resolved this issue:\n"
|
62
|
-
f" -
|
63
|
-
f" -
|
68
|
+
f" - If the PR mentions a specific version number (like v8.0.0 or 3.1.0), include: pip install -U {repo_name}>=VERSION\n"
|
69
|
+
f" - Also suggest: pip install git+https://github.com/{repo_parts}.git@main\n"
|
70
|
+
f" - If appropriate, mention they can also wait for the next official PyPI release\n"
|
64
71
|
f"4. Request feedback on whether the PR changes resolve the issue\n"
|
65
72
|
f"5. Thank 🙏 for reporting the issue and welcome any further feedback if the issue persists\n\n",
|
66
73
|
},
|
@@ -127,6 +134,7 @@ query($owner: String!, $repo: String!, $pr_number: Int!) {
|
|
127
134
|
pullRequest(number: $pr_number) {
|
128
135
|
closingIssuesReferences(first: 50) { nodes { number } }
|
129
136
|
url
|
137
|
+
title
|
130
138
|
body
|
131
139
|
author { login, __typename }
|
132
140
|
reviews(first: 50) { nodes { author { login, __typename } } }
|
@@ -150,6 +158,7 @@ query($owner: String!, $repo: String!, $pr_number: Int!) {
|
|
150
158
|
comments = data["reviews"]["nodes"] + data["comments"]["nodes"]
|
151
159
|
token_username = action.get_username() # get GITHUB_TOKEN username
|
152
160
|
author = data["author"]["login"] if data["author"]["__typename"] != "Bot" else None
|
161
|
+
pr_title = data.get("title", "")
|
153
162
|
|
154
163
|
# Get unique contributors from reviews and comments
|
155
164
|
contributors = {x["author"]["login"] for x in comments if x["author"]["__typename"] != "Bot"}
|
@@ -173,7 +182,9 @@ query($owner: String!, $repo: String!, $pr_number: Int!) {
|
|
173
182
|
pr_credit += (" with contributions from " if pr_credit else "") + ", ".join(f"@{c}" for c in contributors)
|
174
183
|
|
175
184
|
# Generate personalized comment
|
176
|
-
comment = generate_issue_comment(
|
185
|
+
comment = generate_issue_comment(
|
186
|
+
pr_url=data["url"], pr_summary=pr_summary, pr_credit=pr_credit, pr_title=pr_title
|
187
|
+
)
|
177
188
|
|
178
189
|
# Update linked issues
|
179
190
|
for issue in data["closingIssuesReferences"]["nodes"]:
|
@@ -8,10 +8,16 @@ from pathlib import Path
|
|
8
8
|
|
9
9
|
|
10
10
|
def extract_code_blocks(markdown_content):
|
11
|
-
"""Extracts Python code blocks from markdown content using regex pattern matching."""
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
"""Extracts Python and Bash code blocks from markdown content using regex pattern matching."""
|
12
|
+
# Python code blocks
|
13
|
+
py_pattern = r"^( *)```(?:python|py|\{[ ]*\.py[ ]*\.annotate[ ]*\})\n(.*?)\n\1```"
|
14
|
+
py_code_blocks = re.compile(py_pattern, re.DOTALL | re.MULTILINE).findall(markdown_content)
|
15
|
+
|
16
|
+
# Bash code blocks
|
17
|
+
bash_pattern = r"^( *)```(?:bash|sh|shell)\n(.*?)\n\1```"
|
18
|
+
bash_code_blocks = re.compile(bash_pattern, re.DOTALL | re.MULTILINE).findall(markdown_content)
|
19
|
+
|
20
|
+
return {"python": py_code_blocks, "bash": bash_code_blocks}
|
15
21
|
|
16
22
|
|
17
23
|
def remove_indentation(code_block, num_spaces):
|
@@ -89,31 +95,62 @@ def format_code_with_ruff(temp_dir):
|
|
89
95
|
print(f"ERROR running docformatter ❌ {e}")
|
90
96
|
|
91
97
|
|
92
|
-
def
|
93
|
-
"""
|
94
|
-
|
95
|
-
|
96
|
-
|
98
|
+
def format_bash_with_prettier(temp_dir):
|
99
|
+
"""Formats bash script files in the specified directory using prettier."""
|
100
|
+
try:
|
101
|
+
# Run prettier with explicit config path
|
102
|
+
result = subprocess.run(
|
103
|
+
"npx prettier --write --plugin=$(npm root -g)/prettier-plugin-sh/lib/index.cjs ./**/*.sh",
|
104
|
+
shell=True, # must use shell=True to expand internal $(cmd)
|
105
|
+
capture_output=True,
|
106
|
+
text=True,
|
107
|
+
)
|
108
|
+
if result.returncode != 0:
|
109
|
+
print(f"ERROR running prettier-plugin-sh ❌ {result.stderr}")
|
110
|
+
else:
|
111
|
+
print("Completed bash formatting ✅")
|
112
|
+
except Exception as e:
|
113
|
+
print(f"ERROR running prettier-plugin-sh ❌ {e}")
|
114
|
+
|
115
|
+
|
116
|
+
def generate_temp_filename(file_path, index, code_type):
|
117
|
+
"""Creates unique temp filename with full path info for debugging."""
|
118
|
+
stem = file_path.stem
|
119
|
+
code_letter = code_type[0] # 'p' for python, 'b' for bash
|
120
|
+
path_part = str(file_path.parent).replace("/", "_").replace("\\", "_").replace(" ", "-")
|
121
|
+
hash_val = hashlib.md5(f"{file_path}_{index}".encode()).hexdigest()[:6]
|
122
|
+
ext = ".py" if code_type == "python" else ".sh"
|
123
|
+
filename = f"{stem}_{path_part}_{code_letter}{index}_{hash_val}{ext}"
|
124
|
+
return re.sub(r"[^\w\-.]", "_", filename)
|
97
125
|
|
98
126
|
|
99
|
-
def process_markdown_file(file_path, temp_dir, verbose=False):
|
100
|
-
"""Processes a markdown file, extracting
|
127
|
+
def process_markdown_file(file_path, temp_dir, process_python=True, process_bash=True, verbose=False):
|
128
|
+
"""Processes a markdown file, extracting code blocks for formatting and updating the original file."""
|
101
129
|
try:
|
102
130
|
markdown_content = Path(file_path).read_text()
|
103
|
-
|
131
|
+
code_blocks_by_type = extract_code_blocks(markdown_content)
|
104
132
|
temp_files = []
|
105
133
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
134
|
+
# Process all code block types based on flags
|
135
|
+
code_types = []
|
136
|
+
if process_python:
|
137
|
+
code_types.append(("python", 0))
|
138
|
+
if process_bash:
|
139
|
+
code_types.append(("bash", 1000))
|
140
|
+
|
141
|
+
for code_type, offset in code_types:
|
142
|
+
for i, (num_spaces, code_block) in enumerate(code_blocks_by_type[code_type]):
|
143
|
+
if verbose:
|
144
|
+
print(f"Extracting {code_type} code block {i} from {file_path}")
|
145
|
+
|
146
|
+
num_spaces = len(num_spaces)
|
147
|
+
code_without_indentation = remove_indentation(code_block, num_spaces)
|
148
|
+
temp_file_path = temp_dir / generate_temp_filename(file_path, i + offset, code_type)
|
111
149
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
temp_files.append((num_spaces, code_block, temp_file_path))
|
150
|
+
with open(temp_file_path, "w") as temp_file:
|
151
|
+
temp_file.write(code_without_indentation)
|
152
|
+
|
153
|
+
temp_files.append((num_spaces, code_block, temp_file_path, code_type))
|
117
154
|
|
118
155
|
return markdown_content, temp_files
|
119
156
|
|
@@ -123,15 +160,18 @@ def process_markdown_file(file_path, temp_dir, verbose=False):
|
|
123
160
|
|
124
161
|
|
125
162
|
def update_markdown_file(file_path, markdown_content, temp_files):
|
126
|
-
"""Updates a markdown file with formatted
|
127
|
-
for num_spaces, original_code_block, temp_file_path in temp_files:
|
163
|
+
"""Updates a markdown file with formatted code blocks."""
|
164
|
+
for num_spaces, original_code_block, temp_file_path, code_type in temp_files:
|
128
165
|
try:
|
129
166
|
with open(temp_file_path) as temp_file:
|
130
167
|
formatted_code = temp_file.read().rstrip("\n") # Strip trailing newlines
|
131
168
|
formatted_code_with_indentation = add_indentation(formatted_code, num_spaces)
|
132
169
|
|
133
|
-
#
|
134
|
-
|
170
|
+
# Define the language tags for each code type
|
171
|
+
lang_tags = {"python": ["python", "py", "{ .py .annotate }"], "bash": ["bash", "sh", "shell"]}
|
172
|
+
|
173
|
+
# Replace the code blocks with the formatted version
|
174
|
+
for lang in lang_tags[code_type]:
|
135
175
|
markdown_content = markdown_content.replace(
|
136
176
|
f"{' ' * num_spaces}```{lang}\n{original_code_block}\n{' ' * num_spaces}```",
|
137
177
|
f"{' ' * num_spaces}```{lang}\n{formatted_code_with_indentation}\n{' ' * num_spaces}```",
|
@@ -146,8 +186,8 @@ def update_markdown_file(file_path, markdown_content, temp_files):
|
|
146
186
|
print(f"Error writing file {file_path}: {e}")
|
147
187
|
|
148
188
|
|
149
|
-
def main(root_dir=Path.cwd(), verbose=False):
|
150
|
-
"""Processes markdown files, extracts and formats
|
189
|
+
def main(root_dir=Path.cwd(), process_python=True, process_bash=True, verbose=False):
|
190
|
+
"""Processes markdown files, extracts and formats code blocks, and updates the original files."""
|
151
191
|
root_path = Path(root_dir)
|
152
192
|
markdown_files = list(root_path.rglob("*.md"))
|
153
193
|
temp_dir = Path("temp_code_blocks")
|
@@ -158,12 +198,17 @@ def main(root_dir=Path.cwd(), verbose=False):
|
|
158
198
|
for markdown_file in markdown_files:
|
159
199
|
if verbose:
|
160
200
|
print(f"Processing {markdown_file}")
|
161
|
-
markdown_content, temp_files = process_markdown_file(
|
201
|
+
markdown_content, temp_files = process_markdown_file(
|
202
|
+
markdown_file, temp_dir, process_python, process_bash, verbose
|
203
|
+
)
|
162
204
|
if markdown_content and temp_files:
|
163
205
|
all_temp_files.append((markdown_file, markdown_content, temp_files))
|
164
206
|
|
165
|
-
# Format
|
166
|
-
|
207
|
+
# Format code blocks based on flags
|
208
|
+
if process_python:
|
209
|
+
format_code_with_ruff(temp_dir) # Format Python files
|
210
|
+
if process_bash:
|
211
|
+
format_bash_with_prettier(temp_dir) # Format Bash files
|
167
212
|
|
168
213
|
# Update markdown files with formatted code blocks
|
169
214
|
for markdown_file, markdown_content, temp_files in all_temp_files:
|
@@ -174,4 +219,4 @@ def main(root_dir=Path.cwd(), verbose=False):
|
|
174
219
|
|
175
220
|
|
176
221
|
if __name__ == "__main__":
|
177
|
-
main()
|
222
|
+
main(process_python=True, process_bash=False)
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: ultralytics-actions
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.57
|
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>
|
@@ -33,6 +33,7 @@ Requires-Dist: ruff>=0.9.1
|
|
33
33
|
Requires-Dist: docformatter>=1.7.5
|
34
34
|
Provides-Extra: dev
|
35
35
|
Requires-Dist: pytest; extra == "dev"
|
36
|
+
Dynamic: license-file
|
36
37
|
|
37
38
|
<a href="https://www.ultralytics.com/" target="_blank"><img src="https://raw.githubusercontent.com/ultralytics/assets/main/logo/Ultralytics_Logotype_Original.svg" width="320" alt="Ultralytics logo"></a>
|
38
39
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
actions/__init__.py,sha256=Ahj81FIHSd_RoAqBPeasm8WDsgbMFESfsHki6Qr_IGs,742
|
2
|
+
actions/first_interaction.py,sha256=1_WvQHCi5RWaSfyi49ClF2Zk_3CKGjFnZqz6FlxPRAc,17868
|
3
|
+
actions/summarize_pr.py,sha256=BKttOq-MGaanVaChLU5B1ewKUA8K6S05Cy3FQtyRmxU,11681
|
4
|
+
actions/summarize_release.py,sha256=tov6qsYGC68lfobvkwVyoWZBGtJ598G0m097n4Ydzvo,8472
|
5
|
+
actions/update_markdown_code_blocks.py,sha256=YCNJQO48Off5NQwIzdpE9_BLafEDv3-rkEtkREhEItU,8588
|
6
|
+
actions/utils/__init__.py,sha256=WStdEAYROVnF0nubEOmrFLrejkRiMXIefA5O1ckfcFs,476
|
7
|
+
actions/utils/common_utils.py,sha256=PZkK9Wc3od34J9VSw4ICWHhQQC033o3DCrCy0VIyZE4,6024
|
8
|
+
actions/utils/github_utils.py,sha256=-F--JgxtXE0fSPMFEzakz7iZilp-vonzLiyXfg0b17Y,7117
|
9
|
+
actions/utils/openai_utils.py,sha256=qQbmrJpOUANxSMf7inDSgPIwgf0JHD1fWZuab-y2W6g,2942
|
10
|
+
ultralytics_actions-0.0.57.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
11
|
+
ultralytics_actions-0.0.57.dist-info/METADATA,sha256=g6TwX1wlWsyosq1JztCpqp_GGZmGzR9L6G5HQTn5fEA,10583
|
12
|
+
ultralytics_actions-0.0.57.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
|
13
|
+
ultralytics_actions-0.0.57.dist-info/entry_points.txt,sha256=GowvOFplj0C7JmsjbKcbpgLpdf2r921pcaOQkAHWZRA,378
|
14
|
+
ultralytics_actions-0.0.57.dist-info/top_level.txt,sha256=5apM5x80QlJcGbACn1v3fkmIuL1-XQCKcItJre7w7Tw,8
|
15
|
+
ultralytics_actions-0.0.57.dist-info/RECORD,,
|
@@ -1,15 +0,0 @@
|
|
1
|
-
actions/__init__.py,sha256=DJB5iF9yBtEKiORNXrvFFuRZKrAKH68c_98Wez8VoHs,742
|
2
|
-
actions/first_interaction.py,sha256=1_WvQHCi5RWaSfyi49ClF2Zk_3CKGjFnZqz6FlxPRAc,17868
|
3
|
-
actions/summarize_pr.py,sha256=sPKl6gN7HeGb9o9-QxVTnK2WZJW4iUytK6MApthn6HQ,11086
|
4
|
-
actions/summarize_release.py,sha256=tov6qsYGC68lfobvkwVyoWZBGtJ598G0m097n4Ydzvo,8472
|
5
|
-
actions/update_markdown_code_blocks.py,sha256=tUChNBIZN-B_unGMG9yQk-dohi7bit02Yl3xc4UtycQ,6610
|
6
|
-
actions/utils/__init__.py,sha256=WStdEAYROVnF0nubEOmrFLrejkRiMXIefA5O1ckfcFs,476
|
7
|
-
actions/utils/common_utils.py,sha256=PZkK9Wc3od34J9VSw4ICWHhQQC033o3DCrCy0VIyZE4,6024
|
8
|
-
actions/utils/github_utils.py,sha256=-F--JgxtXE0fSPMFEzakz7iZilp-vonzLiyXfg0b17Y,7117
|
9
|
-
actions/utils/openai_utils.py,sha256=qQbmrJpOUANxSMf7inDSgPIwgf0JHD1fWZuab-y2W6g,2942
|
10
|
-
ultralytics_actions-0.0.55.dist-info/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
11
|
-
ultralytics_actions-0.0.55.dist-info/METADATA,sha256=9NPBdeMdAU7SaSY9VfYQY8KWIDI-6A0RS9vUL8rh82I,10561
|
12
|
-
ultralytics_actions-0.0.55.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
|
13
|
-
ultralytics_actions-0.0.55.dist-info/entry_points.txt,sha256=GowvOFplj0C7JmsjbKcbpgLpdf2r921pcaOQkAHWZRA,378
|
14
|
-
ultralytics_actions-0.0.55.dist-info/top_level.txt,sha256=5apM5x80QlJcGbACn1v3fkmIuL1-XQCKcItJre7w7Tw,8
|
15
|
-
ultralytics_actions-0.0.55.dist-info/RECORD,,
|
{ultralytics_actions-0.0.55.dist-info → ultralytics_actions-0.0.57.dist-info}/entry_points.txt
RENAMED
File without changes
|
{ultralytics_actions-0.0.55.dist-info → ultralytics_actions-0.0.57.dist-info/licenses}/LICENSE
RENAMED
File without changes
|
File without changes
|