The little things give you away... A collection of various small helper stuff
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

110 lines
4.1 KiB

  1. #!/usr/bin/env python3
  2. import collections
  3. import html
  4. import logging
  5. import re
  6. import requests
  7. import shlex
  8. import sys
  9. import time
  10. GIT_URLS_OPTION = '--git-urls'
  11. GITGUD_COMPLETE_ITEMS_OPTION = '--gitgud-complete-items'
  12. NAME_OPTION = '--name'
  13. MODES = (GIT_URLS_OPTION, GITGUD_COMPLETE_ITEMS_OPTION, NAME_OPTION)
  14. mode = None
  15. users = sys.argv[1:]
  16. if users and users[0] in MODES:
  17. mode = users[0]
  18. users = users[1:]
  19. assert users and (mode is None or mode in MODES) and not users[0].startswith('--'), f'Usage: github-list-repos [{" | ".join(MODES)}] USER [USER...]'
  20. def get(url):
  21. while True:
  22. logging.info(f'Fetching {url}')
  23. r = requests.get(url, headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; rv:60.0) Gecko/20100101 Firefox/60.0', 'Accept': 'text/html'})
  24. if r.status_code == 429:
  25. logging.warning(f'Got 429, sleeping and retrying')
  26. time.sleep(5)
  27. else:
  28. break
  29. return r
  30. def p(repoName):
  31. if mode is None:
  32. print(f'https://github.com/{repoName}')
  33. elif mode == GIT_URLS_OPTION:
  34. print(f'https://github.com/{repoName}.git')
  35. print(f'https://github.com/{repoName}.wiki.git')
  36. elif mode == GITGUD_COMPLETE_ITEMS_OPTION:
  37. print(f'web:complete:{repoName}')
  38. for user in users:
  39. r = get(f'https://github.com/{user}')
  40. if '<div id="org-repositories"' in r.text:
  41. # Organisation, archived repositories don't appear on /orgs/ + pagination, so need to also iterate over all the 'type' parameters
  42. if mode == NAME_OPTION:
  43. musername = re.search(r'<meta property="profile:username" content="([^"]*)" />', r.text)
  44. if not musername:
  45. print('Error: could not find profile:username meta tag', file = sys.stderr)
  46. sys.exit(1)
  47. mfullname = re.search(r'<h1\s(?:[^>]*\s)?class="(?:[^"]*\s)?h2(?:\s[^"]*)?"(?:\s[^>]*)?>(.*?)</h1>', r.text, flags = re.DOTALL)
  48. if not mfullname:
  49. print('Error: could not find name h1', file = sys.stderr)
  50. sys.exit(1)
  51. print(html.unescape(musername.group(1).strip().replace('\n', ' ').replace('\r', ' ')))
  52. print(html.unescape(mfullname.group(1).strip().replace('\n', ' ').replace('\r', ' ')))
  53. sys.exit(0)
  54. types = collections.deque()
  55. types.append('')
  56. seen = set()
  57. def maybe_p(repoName):
  58. if repoName not in seen:
  59. p(repoName)
  60. seen.add(repoName)
  61. while types:
  62. type_ = types.popleft()
  63. j = '&' if type_ else '?'
  64. r = get(f'https://github.com/orgs/{user}/repositories{type_}')
  65. if not type_:
  66. types.extend(x.split('"')[1] for x in re.findall(r'href="\?type=[^"]*', r.text))
  67. page = 1
  68. while True:
  69. for m in re.finditer(r'<a itemprop="name codeRepository"\s(?:[^>]*\s)?data-hovercard-url="/([^/>"]+/[^/>"]+)/hovercard"', r.text):
  70. maybe_p(m.group(1))
  71. for m in re.finditer(r'<a data-testid="listitem-title-link"\s(?:[^>]*\s)?href="/([^/>"]+/[^/>"]+)"', r.text):
  72. maybe_p(m.group(1))
  73. if '<a class="next_page"' not in r.text and '<a rel="next"' not in r.text:
  74. # End of pagination
  75. break
  76. page += 1
  77. r = get(f'https://github.com/orgs/{user}/repositories{type_}{j}page={page}')
  78. else:
  79. # User, ?tab=repositories + cursor pagination
  80. if mode == NAME_OPTION:
  81. musername = re.search(r'<span\s(?:[^>]*\s)?class="(?:[^"]*\s)?vcard-username(?:\s[^"]*)?"(?:\s[^>]*)?>(.*?)</span>', r.text, flags = re.DOTALL)
  82. if not musername:
  83. print('Error: could not find vcard-username span', file = sys.stderr)
  84. sys.exit(1)
  85. if (m := re.search(r'<span\s(?:[^>]*\s)?class="(?:[^"]*\s)?vcard-fullname(?:\s[^"]*)?"(?:\s[^>]*)?>(.*?)</span>', r.text, flags = re.DOTALL)):
  86. fullname = html.unescape(m.group(1).strip())
  87. else:
  88. fullname = ''
  89. print(html.unescape(musername.group(1).strip()).replace('\n', ' ').replace('\r', ' '))
  90. print(fullname.replace('\n', ' ').replace('\r', ' '))
  91. sys.exit(0)
  92. r = get(f'https://github.com/{user}?tab=repositories')
  93. while True:
  94. for m in re.finditer(r'<a href="/([^/>"]+/[^/>"]+)" itemprop="name codeRepository"(\s[^>]*)?>', r.text):
  95. p(m.group(1))
  96. if not (m := re.search(r'<a\s(?=(?:[^>]*\s)?class="next_page"(?:\s[^>]*)?>)(?:[^>]*\s)?href="/[^/?"]+\?page=([^&]+)&amp;tab=repositories"(?:\s[^>]*)?>', r.text)):
  97. # End of pagination
  98. break
  99. r = get(f'https://github.com/{user}?page={m.group(1)}&tab=repositories')