ultralytics-actions 0.0.58__tar.gz → 0.0.60__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.58/ultralytics_actions.egg-info → ultralytics_actions-0.0.60}/PKG-INFO +1 -1
- {ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60}/actions/__init__.py +1 -1
- {ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60}/actions/utils/common_utils.py +50 -5
- {ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60/ultralytics_actions.egg-info}/PKG-INFO +1 -1
- {ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60}/LICENSE +0 -0
- {ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60}/README.md +0 -0
- {ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60}/actions/first_interaction.py +0 -0
- {ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60}/actions/summarize_pr.py +0 -0
- {ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60}/actions/summarize_release.py +0 -0
- {ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60}/actions/update_markdown_code_blocks.py +0 -0
- {ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60}/actions/utils/__init__.py +0 -0
- {ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60}/actions/utils/github_utils.py +0 -0
- {ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60}/actions/utils/openai_utils.py +0 -0
- {ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60}/pyproject.toml +0 -0
- {ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60}/setup.cfg +0 -0
- {ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60}/tests/test_urls.py +0 -0
- {ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60}/ultralytics_actions.egg-info/SOURCES.txt +0 -0
- {ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60}/ultralytics_actions.egg-info/dependency_links.txt +0 -0
- {ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60}/ultralytics_actions.egg-info/entry_points.txt +0 -0
- {ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60}/ultralytics_actions.egg-info/requires.txt +0 -0
- {ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60}/ultralytics_actions.egg-info/top_level.txt +0 -0
{ultralytics_actions-0.0.58/ultralytics_actions.egg-info → ultralytics_actions-0.0.60}/PKG-INFO
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ultralytics-actions
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.60
|
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>
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
2
2
|
|
3
|
+
import os
|
3
4
|
import re
|
4
5
|
import time
|
5
6
|
from concurrent.futures import ThreadPoolExecutor
|
@@ -24,6 +25,7 @@ REQUESTS_HEADERS = {
|
|
24
25
|
}
|
25
26
|
BAD_HTTP_CODES = frozenset(
|
26
27
|
{
|
28
|
+
# 204, # No content
|
27
29
|
# 403, # Forbidden - client lacks permission to access the resource (commented as works in browser typically)
|
28
30
|
404, # Not Found - requested resource doesn't exist
|
29
31
|
405, # Method Not Allowed - HTTP method not supported for this endpoint
|
@@ -32,6 +34,7 @@ BAD_HTTP_CODES = frozenset(
|
|
32
34
|
502, # Bad Gateway - upstream server sent invalid response
|
33
35
|
503, # Service Unavailable - server temporarily unable to handle request
|
34
36
|
504, # Gateway Timeout - upstream server didn't respond in time
|
37
|
+
525, # Cloudfare handshake error
|
35
38
|
}
|
36
39
|
)
|
37
40
|
URL_IGNORE_LIST = { # use a set (not frozenset) to update with possible private GitHub repos
|
@@ -80,6 +83,18 @@ def clean_url(url):
|
|
80
83
|
return url
|
81
84
|
|
82
85
|
|
86
|
+
def brave_search(query, api_key, count=5):
|
87
|
+
"""Search for alternative URLs using Brave Search API."""
|
88
|
+
headers = {"X-Subscription-Token": api_key, "Accept": "application/json"}
|
89
|
+
if len(query) > 400:
|
90
|
+
print(f"WARNING ⚠️ Brave search query length {len(query)} exceed limit of 400 characters, truncating.")
|
91
|
+
url = f"https://api.search.brave.com/res/v1/web/search?q={parse.quote(query.strip()[:400])}&count={count}"
|
92
|
+
response = requests.get(url, headers=headers)
|
93
|
+
data = response.json() if response.status_code == 200 else {}
|
94
|
+
results = data.get("web", {}).get("results", []) if data else []
|
95
|
+
return [result.get("url") for result in results if result.get("url")]
|
96
|
+
|
97
|
+
|
83
98
|
def is_url(url, session=None, check=True, max_attempts=3, timeout=2):
|
84
99
|
"""Check if string is URL and optionally verify it exists, with fallback for GitHub repos."""
|
85
100
|
try:
|
@@ -103,7 +118,8 @@ def is_url(url, session=None, check=True, max_attempts=3, timeout=2):
|
|
103
118
|
try:
|
104
119
|
# Try HEAD first, then GET if needed
|
105
120
|
for method in (requester.head, requester.get):
|
106
|
-
|
121
|
+
status_code = method(url, stream=method == requester.get, **kwargs).status_code
|
122
|
+
if status_code not in BAD_HTTP_CODES:
|
107
123
|
return True
|
108
124
|
|
109
125
|
# If GitHub and check fails (repo might be private), add the base GitHub URL to ignore list
|
@@ -126,23 +142,51 @@ def is_url(url, session=None, check=True, max_attempts=3, timeout=2):
|
|
126
142
|
return False
|
127
143
|
|
128
144
|
|
129
|
-
def check_links_in_string(text, verbose=True, return_bad=False):
|
145
|
+
def check_links_in_string(text, verbose=True, return_bad=False, replace=False):
|
130
146
|
"""Process a given text, find unique URLs within it, and check for any 404 errors."""
|
131
147
|
all_urls = []
|
132
148
|
for md_text, md_url, plain_url in URL_PATTERN.findall(text):
|
133
149
|
url = md_url or plain_url
|
134
150
|
if url and parse.urlparse(url).scheme:
|
135
|
-
all_urls.append(url)
|
151
|
+
all_urls.append((md_text, url, md_url != ""))
|
152
|
+
|
153
|
+
urls = [(t, clean_url(u), is_md) for t, u, is_md in all_urls] # clean URLs
|
136
154
|
|
137
|
-
urls = set(map(clean_url, all_urls)) # remove extra characters and make unique
|
138
155
|
with requests.Session() as session, ThreadPoolExecutor(max_workers=16) as executor:
|
139
156
|
session.headers.update(REQUESTS_HEADERS)
|
140
|
-
|
157
|
+
valid_results = list(executor.map(lambda x: is_url(x[1], session), urls))
|
158
|
+
bad_urls = [url for (_, url, _), valid in zip(urls, valid_results) if not valid]
|
159
|
+
|
160
|
+
if replace and bad_urls:
|
161
|
+
if brave_api_key := os.getenv("BRAVE_API_KEY"):
|
162
|
+
replacements = {}
|
163
|
+
modified_text = text
|
164
|
+
|
165
|
+
for (title, url, is_md), valid in zip(urls, valid_results):
|
166
|
+
if not valid:
|
167
|
+
alternative_urls = brave_search(f"{title[:200]} {url[:200]}", brave_api_key, count=3)
|
168
|
+
if alternative_urls:
|
169
|
+
# Try each alternative URL until we find one that works
|
170
|
+
for alt_url in alternative_urls:
|
171
|
+
if is_url(alt_url, session):
|
172
|
+
break
|
173
|
+
replacements[url] = alt_url
|
174
|
+
modified_text = modified_text.replace(url, alt_url)
|
175
|
+
|
176
|
+
if verbose and replacements:
|
177
|
+
print(
|
178
|
+
f"WARNING ⚠️ replaced {len(replacements)} broken links:\n"
|
179
|
+
+ "\n".join(f" {k}: {v}" for k, v in replacements.items())
|
180
|
+
)
|
181
|
+
if replacements:
|
182
|
+
return (True, [], modified_text) if return_bad else modified_text
|
141
183
|
|
142
184
|
passing = not bad_urls
|
143
185
|
if verbose and not passing:
|
144
186
|
print(f"WARNING ⚠️ errors found in URLs {bad_urls}")
|
145
187
|
|
188
|
+
if replace:
|
189
|
+
return (passing, bad_urls, text) if return_bad else text
|
146
190
|
return (passing, bad_urls) if return_bad else passing
|
147
191
|
|
148
192
|
|
@@ -152,3 +196,4 @@ if __name__ == "__main__":
|
|
152
196
|
|
153
197
|
print(f"is_url(): {is_url(url)}")
|
154
198
|
print(f"check_links_in_string(): {check_links_in_string(string)}")
|
199
|
+
print(f"check_links_in_string() with replace: {check_links_in_string(string, replace=True)}")
|
{ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60/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.0.
|
3
|
+
Version: 0.0.60
|
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
|
{ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60}/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
|
{ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60}/ultralytics_actions.egg-info/SOURCES.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|
{ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60}/ultralytics_actions.egg-info/requires.txt
RENAMED
File without changes
|
{ultralytics_actions-0.0.58 → ultralytics_actions-0.0.60}/ultralytics_actions.egg-info/top_level.txt
RENAMED
File without changes
|