Skip to content

Commit b025932

Browse files
committed
Merge branch 'master' of github.com:adafruit/adabot
2 parents e08d205 + 5cb9ec6 commit b025932

15 files changed

+392
-282
lines changed

.github/workflows/bundle_cron.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ jobs:
4949
- name: Run adabot.circuitpython_bundle
5050
env:
5151
ADABOT_EMAIL: ${{ secrets.ADABOT_EMAIL }}
52+
ADABOT_GITHUB_USER: ${{ secrets.ADABOT_GITHUB_USER }}
5253
ADABOT_GITHUB_ACCESS_TOKEN: ${{ secrets.ADABOT_GITHUB_ACCESS_TOKEN }}
5354
REDIS_PORT: ${{ job.services.redis.ports[6379] }}
5455
run: |

.github/workflows/reports_cron.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ jobs:
2323
# be limited (they run on all forks' default branches).
2424
if: startswith(github.repository, 'adafruit/')
2525
env:
26+
ADABOT_GITHUB_USER: ${{ secrets.ADABOT_GITHUB_USER }}
2627
ADABOT_GITHUB_ACCESS_TOKEN: ${{ secrets.ADABOT_GITHUB_ACCESS_TOKEN }}
2728
steps:
2829
- name: Dump GitHub context

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ Applying Patches To All CircuitPython Libraries
115115
================================================
116116
To apply a patch to all CircuitPython libraries (only guaranteed for files shared
117117
among all libraries, such as those included in the cookiecutter (e.g. README.rst,
118-
.travis.yml, etc), do the following:
118+
etc), do the following:
119119

120120
1. Apply your update(s) to any library as normal, using ``git commit``. It is recommended to
121121
give a short, detailed description of the patch. This description will be used by the next

adabot/arduino_libraries.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def list_repos():
4949
"""
5050
repos = []
5151
result = github.get("/search/repositories",
52-
params={"q":"Arduino in:name in:description in:readme fork:true user:adafruit archived:false AND NOT PCB in:name AND NOT CircuitPython in:name",
52+
params={"q":"Arduino in:name in:description in:readme fork:true user:adafruit archived:false OR Library in:name in:description in:readme fork:true user:adafruit archived:false AND NOT PCB in:name AND NOT CircuitPython in:name",
5353
"per_page": 100,
5454
"sort": "updated",
5555
"order": "asc"})
@@ -160,11 +160,11 @@ def validate_release_state(repo):
160160

161161
return
162162

163-
def validate_travis(repo):
164-
"""Validate if a repo has .travis.yml.
163+
def validate_actions(repo):
164+
"""Validate if a repo has workflows/githubci.yml
165165
"""
166-
repo_has_travis = requests.get("https://raw.githubusercontent.com/adafruit/" + repo["name"] + "/master/.travis.yml")
167-
return repo_has_travis.ok
166+
repo_has_actions = requests.get("https://raw.githubusercontent.com/adafruit/" + repo["name"] + "/master/.github/workflows/githubci.yml")
167+
return repo_has_actions.ok
168168

169169
def validate_actions(repo):
170170
"""Validate if a repo has actions githubci.yml
@@ -187,7 +187,7 @@ def run_arduino_lib_checks():
187187
failed_lib_prop = [[" Repo", "Release Tag", "library.properties Version"], [" ----", "-----------", "--------------------------"]]
188188
needs_release_list = [[" Repo", "Latest Release", "Commits Behind"], [" ----", "--------------", "--------------"]]
189189
needs_registration_list = [[" Repo"], [" ----"]]
190-
missing_ci_list = [[" Repo"], [" ----"]]
190+
missing_actions_list = [[" Repo"], [" ----"]]
191191
missing_library_properties_list = [[" Repo"], [" ----"]]
192192

193193
for repo in repo_list:
@@ -223,10 +223,10 @@ def run_arduino_lib_checks():
223223
if needs_release:
224224
needs_release_list.append([" " + str(repo["name"]), needs_release[0], needs_release[1]])
225225

226-
missing_ci = not validate_travis(repo) and not validate_actions(repo)
227-
entry['needs_ci'] = missing_ci
228-
if missing_ci:
229-
missing_ci_list.append([" " + str(repo["name"])])
226+
missing_actions = not validate_actions(repo)
227+
entry['needs_actions'] = missing_actions
228+
if missing_actions:
229+
missing_actions_list.append([" " + str(repo["name"])])
230230

231231
all_libraries.append(entry)
232232

@@ -242,8 +242,8 @@ def run_arduino_lib_checks():
242242
if len(needs_release_list) > 2:
243243
print_list_output("Libraries have commits since last release: ({})", needs_release_list);
244244

245-
if len(missing_ci_list) > 2:
246-
print_list_output("Libraries that is not configured with Travis or Actions: ({})", missing_ci_list)
245+
if len(missing_actions_list) > 2:
246+
print_list_output("Libraries that is not configured with Actions: ({})", missing_actions_list)
247247

248248
if len(missing_library_properties_list) > 2:
249249
print_list_output("Libraries that is missing library.properties file: ({})", missing_library_properties_list)

adabot/circuitpython_libraries.py

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import requests
3333

3434
from adabot import github_requests as github
35-
from adabot import travis_requests as travis
3635
from adabot import pypi_requests as pypi
3736
from adabot.lib import circuitpython_library_validators as cirpy_lib_vals
3837
from adabot.lib import common_funcs
@@ -68,12 +67,6 @@
6867
default=5,
6968
metavar="n"
7069
)
71-
cmd_line_parser.add_argument(
72-
"-t", "--travis-github-token",
73-
help="Prompt for the GitHub user's password in order to make a GitHub token to use on Travis.",
74-
dest="gh_token",
75-
action="store_true"
76-
)
7770
cmd_line_parser.add_argument(
7871
"-v", "--validator",
7972
help="Run validators with 'all', or only the validator(s) supplied in a string.",
@@ -103,7 +96,8 @@
10396
if vals[0].startswith("validate")
10497
]
10598

106-
pr_sort_re = re.compile("(?<=\(Open\s)(.+)(?=\sdays)")
99+
pr_sort_re = re.compile(r"(?<=\(Open\s)(.+)(?=\sdays)")
100+
close_pr_sort_re = re.compile(r"(?<=\(Days\sopen:\s)(.+)(?=\))")
107101

108102
def run_library_checks(validators, bundle_submodules, latest_pylint, kw_args):
109103
"""runs the various library checking functions"""
@@ -112,7 +106,8 @@ def run_library_checks(validators, bundle_submodules, latest_pylint, kw_args):
112106
latest_pylint = pylint_info.json()["info"]["version"]
113107
output_handler("Latest pylint is: {}".format(latest_pylint))
114108

115-
repos = common_funcs.list_repos(include_repos=('Adafruit_Blinka',))
109+
repos = common_funcs.list_repos(include_repos=('Adafruit_Blinka',
110+
'CircuitPython_Community_Bundle'))
116111
output_handler("Found {} repos to check.".format(len(repos)))
117112
bundle_submodules = common_funcs.get_bundle_submodules()
118113
output_handler("Found {} submodules in the bundle.".format(len(bundle_submodules)))
@@ -165,7 +160,8 @@ def run_library_checks(validators, bundle_submodules, latest_pylint, kw_args):
165160
insights = blinka_insights
166161
elif repo["name"] == "circuitpython":
167162
insights = core_insights
168-
errors = validator.gather_insights(repo, insights, since)
163+
closed_metric = bool(insights == lib_insights)
164+
errors = validator.gather_insights(repo, insights, since, show_closed_metric=closed_metric)
169165
if errors:
170166
print("insights error")
171167
for error in errors:
@@ -182,11 +178,11 @@ def run_library_checks(validators, bundle_submodules, latest_pylint, kw_args):
182178
updated_libs[repo["name"]] = repo["html_url"]
183179

184180
output_handler()
185-
output_handler("State of CircuitPython + Libraries")
181+
output_handler("State of CircuitPython + Libraries + Blinka")
186182

187183
output_handler("Overall")
188-
print_pr_overview(lib_insights, core_insights)
189-
print_issue_overview(lib_insights, core_insights)
184+
print_pr_overview(lib_insights, core_insights, blinka_insights)
185+
print_issue_overview(lib_insights, core_insights, blinka_insights)
190186

191187
output_handler()
192188
output_handler("Core")
@@ -208,20 +204,38 @@ def run_library_checks(validators, bundle_submodules, latest_pylint, kw_args):
208204
core_insights["milestones"][milestone]))
209205
output_handler(" * {} issues not assigned a milestone".format(len(core_insights["open_issues"]) - ms_count))
210206
output_handler()
211-
print_circuitpython_download_stats()
207+
208+
## temporarily disabling core download stats:
209+
# - GitHub API has been broken, due to the number of release artifacts
210+
# - Release asset delivery is being moved to AWS CloudFront/S3
211+
#print_circuitpython_download_stats()
212+
output_handler(
213+
"* Core download stats available at https://circuitpython.org/stats"
214+
)
212215

213216
output_handler()
214217
output_handler("Libraries")
215218
print_pr_overview(lib_insights)
216-
output_handler("* {} open pull requests".format(len(lib_insights["open_prs"])))
217-
sorted_prs = sorted(lib_insights["open_prs"],
218-
key=lambda days: int(pr_sort_re.search(days).group(1)),
219+
output_handler(" * Merged pull requests:")
220+
sorted_prs = sorted(lib_insights["merged_prs"],
221+
key=lambda days: int(close_pr_sort_re.search(days).group(1)),
219222
reverse=True)
220223
for pr in sorted_prs:
221-
output_handler(" * {}".format(pr))
224+
output_handler(" * {}".format(pr))
222225
print_issue_overview(lib_insights)
223-
output_handler("* {} open issues".format(len(lib_insights["open_issues"])))
224-
output_handler(" * https://circuitpython.org/contributing")
226+
output_handler("* https://circuitpython.org/contributing")
227+
output_handler(" * {} open issues".format(len(lib_insights["open_issues"])))
228+
open_pr_days = [
229+
int(pr_sort_re.search(pr).group(1)) for pr in lib_insights["open_prs"]
230+
if pr_sort_re.search(pr) is not None
231+
]
232+
output_handler(
233+
" * {0} open pull requests (Oldest: {1}, Newest: {2})".format(
234+
len(lib_insights["open_prs"]),
235+
max(open_pr_days),
236+
max((min(open_pr_days), 1)) # ensure the minumum is '1'
237+
)
238+
)
225239
output_handler("Library updates in the last seven days:")
226240
if len(new_libs) != 0:
227241
output_handler("**New Libraries**")
@@ -280,9 +294,25 @@ def output_handler(message="", quiet=False):
280294

281295
def print_circuitpython_download_stats():
282296
"""Gather and report analytics on the main CircuitPython repository."""
283-
response = github.get("/repos/adafruit/circuitpython/releases")
297+
298+
# TODO: with the move of release assets to AWS CloudFront/S3, update
299+
# this to use AWS CloudWatch metrics to gather download stats.
300+
# AWS' Python SDK `boto3` has CloudWatch interfaces which should
301+
# enable this. https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudwatch.html
302+
303+
try:
304+
response = github.get("/repos/adafruit/circuitpython/releases")
305+
except (ValueError, RuntimeError):
306+
output_handler(
307+
"Core CircuitPython GitHub download statistics request failed."
308+
)
309+
return
310+
284311
if not response.ok:
285-
output_handler("Core CircuitPython GitHub analytics request failed.")
312+
output_handler(
313+
"Core CircuitPython GitHub download statistics request failed."
314+
)
315+
return
286316
releases = response.json()
287317

288318
found_unstable = False
@@ -416,7 +446,7 @@ def print_circuitpython_download_stats():
416446
output_handler()
417447

418448
def print_pr_overview(*insights):
419-
merged_prs = sum([x["merged_prs"] for x in insights])
449+
merged_prs = sum([len(x["merged_prs"]) for x in insights])
420450
authors = set().union(*[x["pr_merged_authors"] for x in insights])
421451
reviewers = set().union(*[x["pr_reviewers"] for x in insights])
422452

@@ -467,10 +497,6 @@ def print_issue_overview(*insights):
467497
error_depth = cmd_line_args.error_depth
468498
startup_message.append(" - Depth for listing libraries with errors: {}".format(error_depth))
469499

470-
github_token = cmd_line_args.gh_token
471-
validator_kwarg_list["github_token"] = github_token
472-
startup_message.append(" - Prompts for the GitHub Token are {}.".format(("enabled" if github_token else "disabled")))
473-
474500
if cmd_line_args.validator != "all":
475501
validators = []
476502
for func in cmd_line_args.validator.split(","):

adabot/circuitpython_library_patches.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,11 @@ def check_patches(repo, patches, flags, use_apply, dry_run):
173173
run_apply = False
174174
if (b"error" not in Err.stderr or
175175
b"patch does not apply" in Err.stderr):
176+
parse_err = Err.stderr.decode()
177+
parse_err = parse_err[parse_err.rfind(":")+1:-1]
178+
print(
179+
" . Skipping {}:{}".format(repo["name"], parse_err)
180+
)
176181
skipped += 1
177182
else:
178183
failed += 1

adabot/github_requests.py

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
import time
3636
import traceback
3737

38+
from base64 import b64encode
39+
3840
TIMEOUT = 60
3941

4042
def _fix_url(url):
@@ -52,11 +54,12 @@ def _fix_kwargs(kwargs):
5254
else:
5355
kwargs["headers"] = {"Accept": "application/vnd.github.hellcat-preview+json"}
5456
if "ADABOT_GITHUB_ACCESS_TOKEN" in os.environ and "auth" not in kwargs:
57+
user = os.environ.get("ADABOT_GITHUB_USER", "")
5558
access_token = os.environ["ADABOT_GITHUB_ACCESS_TOKEN"]
56-
if "params" in kwargs:
57-
kwargs["params"]["access_token"] = access_token
58-
else:
59-
kwargs["params"] = {"access_token": access_token}
59+
basic_encoded = b64encode(str(user + ":" + access_token).encode()).decode()
60+
auth_header = "Basic {}".format(basic_encoded)
61+
62+
kwargs["headers"]["Authorization"] = auth_header
6063

6164
return kwargs
6265

@@ -79,15 +82,11 @@ def get(url, **kwargs):
7982
print("GitHub API Rate Limit reached. Pausing until Rate Limit reset.")
8083
while datetime.datetime.now() < rate_limit_reset:
8184
print("Rate Limit will reset at: {}".format(rate_limit_reset))
82-
if "TRAVIS" in os.environ:
83-
# only pause for 5 minutes so that Travis doesn't timeout
84-
# due to idle console output.
85-
time.sleep(300)
86-
else:
87-
reset_diff = rate_limit_reset - datetime.datetime.now()
88-
89-
print("Sleeping {} seconds".format(reset_diff.seconds))
90-
time.sleep(reset_diff.seconds + 1)
85+
reset_diff = rate_limit_reset - datetime.datetime.now()
86+
87+
print("Sleeping {} seconds".format(reset_diff.seconds))
88+
time.sleep(reset_diff.seconds + 1)
89+
9190
if remaining % 100 == 0:
9291
print(remaining, "requests remaining this hour")
9392
return response

adabot/lib/blinka_funcs.py

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,38 +20,17 @@
2020
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2121
# THE SOFTWARE.
2222

23-
import os
24-
import pathlib
25-
import re
26-
27-
import sh
28-
from sh.contrib import git
29-
23+
from adabot import github_requests as github
3024

3125
def board_count():
3226
""" Retrieve the number of boards currently supported by Adafruit_Blinka,
33-
via Adafruit_Python_PlatformDetect.
27+
via the count of files in circuitpython-org/_blinka.
3428
"""
35-
platdetec_boards_re = re.compile(r'^[A-Z]\w+\s+\=\s[\'\"]\w+[\'\"]',
36-
re.MULTILINE)
3729
board_count = 0
38-
working_dir = os.getcwd()
39-
blinka_dir = pathlib.Path(working_dir, '.blinka')
40-
repo_url = 'https://github.com/adafruit/Adafruit_Python_PlatformDetect.git'
41-
42-
if not blinka_dir.exists():
43-
try:
44-
git.clone(repo_url, blinka_dir.resolve(), '--depth', '1')
45-
except sh.ErrorReturnCode_128:
46-
print("Failed to clone Adafruit_Blinka. Board count not determined.")
47-
board_count = "Error"
48-
49-
src_board_path = blinka_dir / 'adafruit_platformdetect/board.py'
50-
if src_board_path.exists():
51-
board_content = ""
52-
with open(src_board_path, 'r') as board_py:
53-
board_content = board_py.read()
54-
content_re = platdetec_boards_re.findall(board_content)
55-
board_count = len(content_re)
30+
cirpy_org_url = '/repos/adafruit/circuitpython-org/contents/_blinka'
31+
response = github.get(cirpy_org_url)
32+
if response.ok:
33+
response_json = response.json()
34+
board_count = len(response_json)
5635

5736
return board_count

0 commit comments

Comments
 (0)