logo

qmk_firmware

custom branch of QMK firmware git clone https://anongit.hacktivis.me/git/qmk_firmware.git

develop_pr_list.py (5023B)


  1. """Export the initial list of PRs associated with a `develop` merge to `master`.
  2. """
  3. import os
  4. import re
  5. from pathlib import Path
  6. from subprocess import DEVNULL
  7. from milc import cli
  8. cache_timeout = 7 * 86400
  9. fix_expr = re.compile(r'fix', flags=re.IGNORECASE)
  10. clean1_expr = re.compile(r'\[(develop|keyboard|keymap|core|cli|bug|docs|feature)\]', flags=re.IGNORECASE)
  11. clean2_expr = re.compile(r'^(develop|keyboard|keymap|core|cli|bug|docs|feature):', flags=re.IGNORECASE)
  12. ignored_titles = ["Format code according to conventions"]
  13. def _is_ignored(title):
  14. for ignore in ignored_titles:
  15. if ignore in title:
  16. return True
  17. return False
  18. def _get_pr_info(cache, gh, pr_num):
  19. pull = cache.get(f'pull:{pr_num}')
  20. if pull is None:
  21. print(f'Retrieving info for PR #{pr_num}')
  22. pull = gh.pulls.get(owner='qmk', repo='qmk_firmware', pull_number=pr_num)
  23. cache.set(f'pull:{pr_num}', pull, cache_timeout)
  24. return pull
  25. def _try_open_cache(cli):
  26. # These dependencies are manually handled because people complain. Fun.
  27. try:
  28. from sqlite_cache.sqlite_cache import SqliteCache
  29. except ImportError:
  30. return None
  31. cache_loc = Path(cli.config_file).parent
  32. return SqliteCache(cache_loc)
  33. def _get_github():
  34. try:
  35. from ghapi.all import GhApi
  36. except ImportError:
  37. return None
  38. return GhApi()
  39. @cli.argument('-f', '--from-ref', default='0.11.0', help='Git revision/tag/reference/branch to begin search')
  40. @cli.argument('-b', '--branch', default='upstream/develop', help='Git branch to iterate (default: "upstream/develop")')
  41. @cli.subcommand('Creates the develop PR list.', hidden=False if cli.config.user.developer else True)
  42. def generate_develop_pr_list(cli):
  43. """Retrieves information from GitHub regarding the list of PRs associated
  44. with a merge of `develop` branch into `master`.
  45. Requires environment variable GITHUB_TOKEN to be set.
  46. """
  47. if 'GITHUB_TOKEN' not in os.environ or os.environ['GITHUB_TOKEN'] == '':
  48. cli.log.error('Environment variable "GITHUB_TOKEN" is not set.')
  49. return 1
  50. cache = _try_open_cache(cli)
  51. gh = _get_github()
  52. git_args = ['git', 'rev-list', '--oneline', '--no-merges', '--reverse', f'{cli.args.from_ref}...{cli.args.branch}', '^upstream/master']
  53. commit_list = cli.run(git_args, capture_output=True, stdin=DEVNULL)
  54. if cache is None or gh is None:
  55. cli.log.error('Missing one or more dependent python packages: "ghapi", "python-sqlite-cache"')
  56. return 1
  57. pr_list_bugs = []
  58. pr_list_dependencies = []
  59. pr_list_core = []
  60. pr_list_keyboards = []
  61. pr_list_keyboard_fixes = []
  62. pr_list_cli = []
  63. pr_list_others = []
  64. def _categorise_commit(commit_info):
  65. def fix_or_normal(info, fixes_collection, normal_collection):
  66. if "bug" in info['pr_labels'] or fix_expr.search(info['title']):
  67. fixes_collection.append(info)
  68. else:
  69. normal_collection.append(info)
  70. if _is_ignored(commit_info['title']):
  71. return
  72. elif "dependencies" in commit_info['pr_labels']:
  73. fix_or_normal(commit_info, pr_list_bugs, pr_list_dependencies)
  74. elif "core" in commit_info['pr_labels']:
  75. fix_or_normal(commit_info, pr_list_bugs, pr_list_core)
  76. elif "keyboard" in commit_info['pr_labels'] or "keymap" in commit_info['pr_labels'] or "via" in commit_info['pr_labels']:
  77. fix_or_normal(commit_info, pr_list_keyboard_fixes, pr_list_keyboards)
  78. elif "cli" in commit_info['pr_labels']:
  79. fix_or_normal(commit_info, pr_list_bugs, pr_list_cli)
  80. else:
  81. fix_or_normal(commit_info, pr_list_bugs, pr_list_others)
  82. git_expr = re.compile(r'^(?P<hash>[a-f0-9]+) (?P<title>.*) \(#(?P<pr>[0-9]+)\)$')
  83. for line in commit_list.stdout.split('\n'):
  84. match = git_expr.search(line)
  85. if match:
  86. pr_info = _get_pr_info(cache, gh, match.group("pr"))
  87. commit_info = {'hash': match.group("hash"), 'title': pr_info['title'], 'pr_num': int(match.group("pr")), 'pr_labels': [label.name for label in pr_info.labels.items]}
  88. _categorise_commit(commit_info)
  89. def _dump_commit_list(name, collection):
  90. if len(collection) == 0:
  91. return
  92. print("")
  93. print(f"{name}:")
  94. for commit in sorted(collection, key=lambda x: x['pr_num']):
  95. title = clean1_expr.sub('', clean2_expr.sub('', commit['title'])).strip()
  96. pr_num = commit['pr_num']
  97. print(f'* {title} ([#{pr_num}](https://github.com/qmk/qmk_firmware/pull/{pr_num}))')
  98. _dump_commit_list("Core", pr_list_core)
  99. _dump_commit_list("CLI", pr_list_cli)
  100. _dump_commit_list("Submodule updates", pr_list_dependencies)
  101. _dump_commit_list("Keyboards", pr_list_keyboards)
  102. _dump_commit_list("Keyboard fixes", pr_list_keyboard_fixes)
  103. _dump_commit_list("Others", pr_list_others)
  104. _dump_commit_list("Bugs", pr_list_bugs)