56
56
ERROR_MISSING_EXAMPLE_FOLDER = "Missing examples folder"
57
57
ERROR_EXAMPLE_MISSING_SENSORNAME = "Example file(s) missing sensor/library name"
58
58
ERROR_MISSING_EXAMPLE_SIMPLETEST = "Missing simpletest example."
59
+ ERROR_MISSING_STANDARD_LABELS = "Missing one or more standard issue labels (bug, documentation, enhancement, good first issue)."
59
60
ERROR_MISSING_LIBRARIANS = "CircuitPythonLibrarians team missing or does not have write access"
60
61
ERROR_MISSING_LICENSE = "Missing license."
61
62
ERROR_MISSING_LINT = "Missing lint config"
133
134
"Adafruit_CircuitPython_miniQR" ,
134
135
]
135
136
137
+ STD_REPO_LABELS = ("bug" , "documentation" , "enhancement" , "good first issue" )
138
+
136
139
# Cache CircuitPython's subprojects on ReadTheDocs so its not fetched every repo check.
137
140
rtd_subprojects = None
138
141
@@ -439,14 +442,24 @@ def validate_contents(self, repo):
439
442
return []
440
443
441
444
content_list = github .get ("/repos/" + repo ["full_name" ] + "/contents/" )
442
- # Empty repos return an object with a "message" that the repo is empty.
443
- if not content_list .ok or "message" in content_list .json ():
444
- if not self .validate_contents_quiet :
445
- return [ERROR_UNABLE_PULL_REPO_CONTENTS ]
446
- return []
445
+ empty_repo = False
446
+ if not content_list .ok :
447
+ # Empty repos return:
448
+ # - a 404 status code
449
+ # - a "message" that the repo is empty.
450
+ if "message" in content_list .json ():
451
+ if "empty" in content_list .json ()["message" ]:
452
+ empty_repo = True
453
+ if not empty_repo :
454
+ if not self .validate_contents_quiet :
455
+ return [ERROR_UNABLE_PULL_REPO_CONTENTS ]
456
+ return []
447
457
448
458
content_list = content_list .json ()
449
- files = [x ["name" ] for x in content_list ]
459
+ files = []
460
+ # an empty repo will return a 'message'
461
+ if not empty_repo :
462
+ files = [x ["name" ] for x in content_list ]
450
463
451
464
# ignore new/in-work repos, which should have less than 8 files:
452
465
# ___.py or folder, CoC, .travis.yml, .readthedocs.yml, docs/,
@@ -834,10 +847,17 @@ def gather_insights(self, repo, insights, since):
834
847
835
848
for issue in issues :
836
849
created = datetime .datetime .strptime (issue ["created_at" ], "%Y-%m-%dT%H:%M:%SZ" )
850
+ days_open = datetime .datetime .today () - created
851
+ if days_open .days < 0 : # opened earlier today
852
+ days_open += datetime .timedelta (days = (days_open .days * - 1 ))
837
853
if "pull_request" in issue :
838
- insights ["open_prs" ].append (issue ["pull_request" ]["html_url" ])
854
+ pr_link = "{0} (Open {1} days)" .format (issue ["pull_request" ]["html_url" ],
855
+ days_open .days )
856
+ insights ["open_prs" ].append (pr_link )
839
857
else :
840
- insights ["open_issues" ].append (issue ["html_url" ])
858
+ issue_link = "{0} (Open {1} days)" .format (issue ["html_url" ],
859
+ days_open .days )
860
+ insights ["open_issues" ].append (issue_link )
841
861
842
862
# get milestones for core repo
843
863
if repo ["name" ] == "circuitpython" :
@@ -864,3 +884,22 @@ def validate_in_pypi(self, repo):
864
884
if not common_funcs .repo_is_on_pypi (repo ):
865
885
return [ERROR_NOT_ON_PYPI ]
866
886
return []
887
+
888
+ def validate_labels (self , repo ):
889
+ """ensures the repo has the standard labels available"""
890
+ response = github .get ("/repos/" + repo ["full_name" ] + "/labels" )
891
+ if not response .ok :
892
+ # replace 'output_handler' with ERROR_OUTPUT_HANDLER
893
+ self .output_file_data .append ("Labels request failed: {}" .format (repo ["full_name" ]))
894
+ return [ERROR_OUTPUT_HANDLER ]
895
+
896
+ repo_labels = [label ["name" ] for label in response .json ()]
897
+ has_all_labels = True
898
+ for label in STD_REPO_LABELS :
899
+ if not label in repo_labels :
900
+ has_all_labels = False
901
+
902
+ if not has_all_labels :
903
+ return [ERROR_MISSING_STANDARD_LABELS ]
904
+ else :
905
+ return []
0 commit comments