Skip to content

Commit 70cf49e

Browse files
authored
Merge pull request #289 from tekktrik/dev/use-ci-check
Convert pylint check to CI check
2 parents f7cc418 + 1cc4fd5 commit 70cf49e

File tree

4 files changed

+51
-113
lines changed

4 files changed

+51
-113
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ jobs:
5353
ADABOT_EMAIL: ${{ secrets.ADABOT_EMAIL }}
5454
ADABOT_GITHUB_USER: ${{ secrets.ADABOT_GITHUB_USER }}
5555
ADABOT_GITHUB_ACCESS_TOKEN: ${{ secrets.ADABOT_GITHUB_ACCESS_TOKEN }}
56+
RTD_TOKEN: ${{ secrets.RTD_TOKEN }}
5657
REDIS_PORT: ${{ job.services.redis.ports[6379] }}
5758
run: |
5859
python3 -u -m pytest

adabot/circuitpython_libraries.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def run_library_checks(validators, kw_args, error_depth):
9595
pylint_info = pypi.get("/pypi/pylint/json")
9696
if pylint_info and pylint_info.ok:
9797
latest_pylint = pylint_info.json()["info"]["version"]
98-
logger.info("Latest pylint is: %s", latest_pylint)
98+
# logger.info("Latest pylint is: %s", latest_pylint)
9999

100100
repos = common_funcs.list_repos(
101101
include_repos=tuple(blinka_repos)

adabot/lib/circuitpython_library_validators.py

Lines changed: 48 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,15 @@
88
errors, across the entire CirtuitPython library ecosystem."""
99

1010
import datetime
11-
from io import StringIO
12-
import json
1311
import os
1412
import logging
15-
import pathlib
1613
import re
1714
import time
18-
from tempfile import TemporaryDirectory
1915

2016
from packaging.version import parse as pkg_version_parse
2117

22-
from pylint import lint
23-
from pylint.reporters import JSONReporter
24-
2518
import requests
2619

27-
import sh
28-
from sh.contrib import git
29-
3020
import yaml
3121
import parse
3222

@@ -38,18 +28,6 @@
3828
GH_INTERFACE = pygithub.Github(os.environ["ADABOT_GITHUB_ACCESS_TOKEN"])
3929

4030

41-
class CapturedJsonReporter(JSONReporter):
42-
"""Helper class to stringify PyLint JSON reports."""
43-
44-
def __init__(self):
45-
self._stringio = StringIO()
46-
super().__init__(self._stringio)
47-
48-
def get_result(self):
49-
"""The current value."""
50-
return self._stringio.getvalue()
51-
52-
5331
# Define constants for error strings to make checking against them more robust:
5432
ERROR_README_DOWNLOAD_FAILED = "Failed to download README"
5533
ERROR_README_IMAGE_MISSING_ALT = "README image missing alt text"
@@ -61,6 +39,7 @@ def get_result(self):
6139
"README CI badge needs to be changed to GitHub Actions"
6240
)
6341
ERROR_PYFILE_DOWNLOAD_FAILED = "Failed to download .py code file"
42+
ERROR_TOMLFILE_DOWNLOAD_FAILED = "Failed to download .toml file"
6443
ERROR_PYFILE_MISSING_STRUCT = (
6544
".py file contains reference to import ustruct"
6645
" without reference to import struct. See issue "
@@ -99,9 +78,12 @@ def get_result(self):
9978
ERROR_MISSING_CODE_OF_CONDUCT = "Missing CODE_OF_CONDUCT.md"
10079
ERROR_MISSING_README_RST = "Missing README.rst"
10180
ERROR_MISSING_READTHEDOCS = "Missing readthedocs.yaml"
102-
ERROR_MISSING_PYPROJECT_TOML = "For pypi compatibility, missing pyproject.toml"
81+
ERROR_MISSING_PYPROJECT_TOML = "For PyPI compatibility, missing pyproject.toml"
10382
ERROR_MISSING_PRE_COMMIT_CONFIG = "Missing .pre-commit-config.yaml"
104-
ERROR_MISSING_REQUIREMENTS_TXT = "For pypi compatibility, missing requirements.txt"
83+
ERROR_MISSING_REQUIREMENTS_TXT = "For PyPI compatibility, missing requirements.txt"
84+
ERROR_MISSING_OPTIONAL_REQUIREMENTS_TXT = (
85+
"For PyPI compatibility, missing optional_requirements.txt"
86+
)
10587
ERROR_MISSING_BLINKA = (
10688
"For pypi compatibility, missing Adafruit-Blinka in requirements.txt"
10789
)
@@ -114,7 +96,7 @@ def get_result(self):
11496
ERROR_ONLY_ALLOW_MERGES = "Only allow merges, disallow rebase and squash"
11597
ERROR_RTD_SUBPROJECT_MISSING = "ReadTheDocs missing as a subproject on CircuitPython"
11698
ERROR_RTD_ADABOT_MISSING = "ReadTheDocs project missing adabot as owner"
117-
ERROR_RTD_FAILED_TO_LOAD_BUILD_STATUS = "Failed to load build status"
99+
ERROR_RTD_FAILED_TO_LOAD_BUILD_STATUS = "Failed to load RTD build status"
118100
ERROR_RTD_SUBPROJECT_FAILED = "Failed to list CircuitPython subprojects on ReadTheDocs"
119101
ERROR_RTD_OUTPUT_HAS_WARNINGS = "ReadTheDocs latest build has warnings and/or errors"
120102
ERROR_GITHUB_NO_RELEASE = "Library repository has no releases"
@@ -145,7 +127,7 @@ def get_result(self):
145127
"Missing or incorrect pre-commit version in .pre-commit-config.yaml"
146128
)
147129
ERROR_PYLINT_VERSION = "Missing or incorrect pylint version in .pre-commit-config.yaml"
148-
ERROR_PYLINT_FAILED_LINTING = "Failed PyLint checks"
130+
ERROR_CI_BUILD = "Failed CI build"
149131
ERROR_NEW_REPO_IN_WORK = "New repo(s) currently in work, and unreleased"
150132

151133
# Temp category for GitHub Actions migration.
@@ -308,26 +290,6 @@ def validate_repo_state(self, repo):
308290
errors.append(ERROR_ONLY_ALLOW_MERGES)
309291
return errors
310292

311-
def validate_actions_state(self, repo):
312-
"""Validate if the most recent GitHub Actions run on the default branch
313-
has passed.
314-
Just returns a message stating that the most recent run failed.
315-
"""
316-
317-
if not (
318-
repo["owner"]["login"] == "adafruit"
319-
and repo["name"].startswith("Adafruit_CircuitPython")
320-
):
321-
return []
322-
323-
try:
324-
repo_obj = GH_INTERFACE.get_repo("Adafruit/" + repo["full_name"])
325-
workflow = repo_obj.get_workflow("build.yml")
326-
workflow_runs = workflow.get_runs(branch="main")
327-
return [] if workflow_runs[0].conclusion else [ERROR_GITHUB_FAILING_ACTIONS]
328-
except pygithub.GithubException:
329-
return [ERROR_UNABLE_PULL_REPO_DETAILS]
330-
331293
# pylint: disable=too-many-locals,too-many-return-statements,too-many-branches
332294
def validate_release_state(self, repo):
333295
"""Validate if a repo 1) has a release, and 2) if there have been commits
@@ -577,17 +539,14 @@ def _validate_pre_commit_config_yaml(self, file_info):
577539
return errors
578540

579541
def _validate_pyproject_toml(self, file_info):
580-
"""Check prproject.toml for pypi compatibility"""
542+
"""Check pyproject.toml for pypi compatibility"""
581543
download_url = file_info["download_url"]
582544
contents = requests.get(download_url, timeout=30)
583545
if not contents.ok:
584-
return [ERROR_PYFILE_DOWNLOAD_FAILED]
585-
586-
errors = []
587-
588-
return errors
546+
return [ERROR_TOMLFILE_DOWNLOAD_FAILED]
547+
return []
589548

590-
def _validate_requirements_txt(self, repo, file_info):
549+
def _validate_requirements_txt(self, repo, file_info, check_blinka=True):
591550
"""Check requirements.txt for pypi compatibility"""
592551
download_url = file_info["download_url"]
593552
contents = requests.get(download_url, timeout=30)
@@ -598,7 +557,11 @@ def _validate_requirements_txt(self, repo, file_info):
598557
lines = contents.text.split("\n")
599558
blinka_lines = [l for l in lines if re.match(r"[\s]*Adafruit-Blinka[\s]*", l)]
600559

601-
if not blinka_lines and repo["name"] not in LIBRARIES_DONT_NEED_BLINKA:
560+
if (
561+
not blinka_lines
562+
and repo["name"] not in LIBRARIES_DONT_NEED_BLINKA
563+
and check_blinka
564+
):
602565
errors.append(ERROR_MISSING_BLINKA)
603566
return errors
604567

@@ -733,6 +696,13 @@ def validate_contents(self, repo):
733696
errors.extend(self._validate_requirements_txt(repo, file_info))
734697
else:
735698
errors.append(ERROR_MISSING_REQUIREMENTS_TXT)
699+
if "optional_requirements.txt" in files:
700+
file_info = content_list[files.index("optional_requirements.txt")]
701+
errors.extend(
702+
self._validate_requirements_txt(repo, file_info, check_blinka=False)
703+
)
704+
else:
705+
errors.append(ERROR_MISSING_OPTIONAL_REQUIREMENTS_TXT)
736706

737707
# Check for an examples folder.
738708
dirs = [
@@ -1167,71 +1137,38 @@ def validate_labels(self, repo):
11671137

11681138
return errors
11691139

1170-
def validate_passes_linting(self, repo):
1171-
"""Clones the repo and runs pylint on the Python files"""
1140+
def validate_actions_state(self, repo):
1141+
"""Validate if the most recent GitHub Actions run on the default branch
1142+
has passed.
1143+
Just returns a message stating that the most recent run failed.
1144+
"""
1145+
11721146
if not repo["name"].startswith("Adafruit_CircuitPython"):
11731147
return []
11741148

1175-
ignored_py_files = ["conf.py"]
1176-
1177-
desination_type = TemporaryDirectory
1178-
if self.keep_repos:
1179-
desination_type = pathlib.Path("repos").absolute
1180-
1181-
with desination_type() as tempdir:
1182-
repo_dir = pathlib.Path(tempdir) / repo["name"]
1149+
while True:
11831150
try:
1184-
if not repo_dir.exists():
1185-
git.clone("--depth=1", repo["clone_url"], repo_dir)
1186-
except sh.ErrorReturnCode as err:
1187-
self.output_file_data.append(
1188-
f"Failed to clone repo for linting: {repo['full_name']}\n {err.stderr}"
1189-
)
1190-
return [ERROR_OUTPUT_HANDLER]
1191-
1192-
if self.keep_repos and (repo_dir / ".pylint-ok").exists():
1193-
return []
1194-
1195-
for file in repo_dir.rglob("*.py"):
1196-
if file.name in ignored_py_files or str(file.parent).endswith(
1197-
"examples"
1198-
):
1199-
continue
1200-
1201-
pylint_args = [str(file)]
1202-
if (repo_dir / ".pylintrc").exists():
1203-
pylint_args += [f"--rcfile={str(repo_dir / '.pylintrc')}"]
1204-
1205-
reporter = CapturedJsonReporter()
1151+
lib_repo = GH_INTERFACE.get_repo(repo["full_name"])
12061152

1207-
logging.debug("Running pylint on %s", file)
1153+
if lib_repo.archived:
1154+
return []
12081155

1209-
lint.Run(pylint_args, reporter=reporter, exit=False)
1210-
pylint_stderr = ""
1211-
pylint_stdout = reporter.get_result()
1212-
1213-
if pylint_stderr:
1214-
self.output_file_data.append(
1215-
f"PyLint error ({repo['name']}): '{pylint_stderr}'"
1216-
)
1217-
return [ERROR_OUTPUT_HANDLER]
1156+
arg_dict = {"branch": lib_repo.default_branch}
12181157

12191158
try:
1220-
pylint_result = json.loads(pylint_stdout)
1221-
except json.JSONDecodeError as json_err:
1222-
self.output_file_data.append(
1223-
f"PyLint output JSONDecodeError: {json_err.msg}"
1224-
)
1225-
return [ERROR_OUTPUT_HANDLER]
1226-
1227-
if pylint_result:
1228-
return [ERROR_PYLINT_FAILED_LINTING]
1229-
1230-
if self.keep_repos:
1231-
with open(repo_dir / ".pylint-ok", "w") as pylint_ok:
1232-
pylint_ok.write("".join(pylint_result))
1233-
1234-
return []
1159+
workflow = lib_repo.get_workflow("build.yml")
1160+
workflow_runs = workflow.get_runs(**arg_dict)
1161+
except pygithub.GithubException: # This can probably be tightened later
1162+
# No workflows or runs yet
1163+
return []
1164+
if not workflow_runs[0].conclusion:
1165+
return [ERROR_CI_BUILD]
1166+
return []
1167+
except pygithub.RateLimitExceededException:
1168+
core_rate_limit_reset = GH_INTERFACE.get_rate_limit().core.reset
1169+
sleep_time = core_rate_limit_reset - datetime.datetime.now()
1170+
logging.warning("Rate Limit will reset at: %s", core_rate_limit_reset)
1171+
time.sleep(sleep_time.seconds)
12351172

12361173
def validate_default_branch(self, repo):
12371174
"""Makes sure that the default branch is main"""

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
black==22.3.0
66
packaging==20.3
7-
pylint
7+
pylint==2.11.1
88
pytest
99
pyyaml==5.4.1
1010
redis==2.10.6

0 commit comments

Comments
 (0)