#!/usr/bin/env python3 import argparse import configparser import os import re import sys from typing import Any import requests # Scans zulip repository for issues that don't have any `area` labels. # GitHub API token is required as GitHub limits unauthenticated # requests to 60/hour. There is a good chance that this limit is # bypassed in consecutive attempts. # The API token can be generated here # https://github.com/settings/tokens/new?description=Zulip%20Issue%20Label%20Checker # # Copy conf.ini-template to conf.ini and populate with your API token. # # usage: python check-issue-labels # Pass --force as an argument to run without a token. def get_config() -> configparser.ConfigParser: config = configparser.ConfigParser() config.read(os.path.join(os.path.dirname(os.path.abspath(__file__)), "conf.ini")) return config def area_labeled(issue: dict[str, Any]) -> bool: for label in issue["labels"]: label_name = str(label["name"]) if "area:" in label_name: return True return False def is_issue(item: dict[str, Any]) -> bool: return "issues" in item["html_url"] def get_next_page_url(link_header: str) -> str | None: matches = re.findall(r"\<(\S+)\>; rel=\"next\"", link_header) try: return matches[0] except IndexError: return None def check_issue_labels() -> None: parser = argparse.ArgumentParser() parser.add_argument("--force", action="store_true") args = parser.parse_args() if not args.force: config = get_config() try: token = config.get("github", "api_token") except configparser.Error: print( "Error fetching GitHub API token. Copy conf.ini-template to conf.ini and populate with " "your API token. If you want to continue without using a token use --force." ) sys.exit(1) next_page_url: str | None = "https://api.github.com/repos/zulip/zulip/issues" unlabeled_issue_urls: list[str] = [] while next_page_url: try: if args.force: response = requests.get(next_page_url) else: response = requests.get(next_page_url, headers={"Authorization": f"token {token}"}) if response.status_code == 401: sys.exit("Error. Please check the token.") if response.status_code == 403: sys.exit( "403 Error. This is generally caused when API limit is exceeded. You use an API " "token to overcome this limit." ) except requests.exceptions.RequestException as e: print(e) sys.exit(1) next_page_url = get_next_page_url(response.headers["Link"]) unlabeled_issue_urls.extend( item["html_url"] for item in response.json() if is_issue(item) and not area_labeled(item) ) if len(unlabeled_issue_urls): print("The following issues don't have any area labels associated with it") print("\n".join(unlabeled_issue_urls)) else: print("No GitHub issues found with missing area labels.") if __name__ == "__main__": check_issue_labels()