github.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. #!/usr/bin/env python
  2. # -*- encoding: utf-8 -*-
  3. """
  4. @Contact : liuyuqi.gov@msn.cn
  5. @Time : 2023/06/04 13:43:46
  6. @License : Copyright © 2017-2022 liuyuqi. All Rights Reserved.
  7. @Desc : github get a user all open source repo
  8. """
  9. import os
  10. import json
  11. import csv, subprocess
  12. from repo_sync.models import Repo
  13. from .base_platform import BasePlatform
  14. from repo_sync.utils.logger import logger
  15. class GithubIE(BasePlatform):
  16. """github util"""
  17. _host = 'https://api.github.com'
  18. def __init__(self, username: str, token: str, host: str = None, params: dict = None) -> None:
  19. """init"""
  20. super().__init__(username=username, token=token)
  21. if self.token:
  22. self.sess.headers.update({'Accept': 'application/vnd.github.v3+json'})
  23. self.repo_private = True if params.get('github_private', "true").lower() == 'true' else False
  24. def create_repo(self, repo_name: str):
  25. """create a repo"""
  26. if not self._repo_exists(repo_name=repo_name):
  27. url = f'{self._host}/user/repos'
  28. payload = {
  29. 'name': repo_name,
  30. 'private': self.repo_private,
  31. 'has_issues': True,
  32. 'has_projects': False,
  33. 'has_wiki': False,
  34. }
  35. r = self.sess.post(url, data=json.dumps(payload))
  36. if r.status_code != 201:
  37. logger.error(f'create repo {repo_name} failed, status code {r.status_code}')
  38. return
  39. logger.info(f'create repo {repo_name} success')
  40. logger.info(f'https://github.com/{self.username}/{repo_name}')
  41. def delete(self, repo_name: str):
  42. """delete a repo, maybe request a confirm by input"""
  43. url = f'{self._host}/repos/{self.username}/{repo_name}'
  44. response = self.sess.delete(url)
  45. if response.status_code == 204:
  46. logger.info(f'Repository: {repo_name} deleted from github successfully!')
  47. else:
  48. logger.error(f'Failed to delete repository: {repo_name} from github. Error {response.status_code}: {response.text}')
  49. logger.warning(f'delete repo: {repo_name} from github success')
  50. def _repo_exists(self, repo_name: str):
  51. """check if a repo exists"""
  52. url = f'{self._host}/repos/{self.username}/{repo_name}'
  53. try:
  54. response = self.sess.get(url)
  55. if response.status_code == 200:
  56. logger.info(f'repo: {repo_name} is existed.')
  57. return True
  58. except Exception as e:
  59. return False
  60. def pull(self, local_repo_path: str):
  61. """pull a repo"""
  62. if local_repo_path[-1] == os.path.sep:
  63. local_repo_path = local_repo_path[:-1]
  64. repo_name = local_repo_path.split(os.path.sep)[-1]
  65. logger.info(f'pull repo: {self.username}/{repo_name} from github')
  66. os.chdir(local_repo_path)
  67. os.system('git remote remove origin_github')
  68. os.system(
  69. f'git remote add origin_github https://{self.username}:{self.token}@github.com/{self.username}/{repo_name}.git'
  70. )
  71. result = subprocess.run(
  72. ['git', 'symbolic-ref', '--short', 'HEAD'], capture_output=True, text=True, encoding='utf-8'
  73. )
  74. current_branch = result.stdout.strip()
  75. os.system(f'git pull origin_github {current_branch}')
  76. os.system('git remote remove origin_github')
  77. os.chdir('..')
  78. logger.info(f'pull from github success')
  79. def push(self, local_repo_path: str):
  80. """push a local repo to remote"""
  81. if local_repo_path[-1] == os.path.sep:
  82. local_repo_path = local_repo_path[:-1]
  83. repo_name = local_repo_path.split(os.path.sep)[-1]
  84. logger.info(f'push repo: {self.username}/{repo_name} to github')
  85. self.create_repo(repo_name)
  86. os.chdir(local_repo_path)
  87. os.system('git remote remove origin_github')
  88. os.system(
  89. f'git remote add origin_github https://{self.username}:{self.token}@github.com/{self.username}/{repo_name}.git'
  90. )
  91. result = subprocess.run(
  92. ['git', 'symbolic-ref', '--short', 'HEAD'], capture_output=True, text=True, encoding='utf-8'
  93. )
  94. current_branch = result.stdout.strip()
  95. os.system(f'git pull origin_github {current_branch}')
  96. os.system(f'git push -u origin_github {current_branch}')
  97. os.system('git remote remove origin_github')
  98. os.chdir('..')
  99. logger.info(f'push to github success')
  100. def get_repo_list(self) -> list:
  101. """get all repo list of a user"""
  102. if os.path.exists(self.repo_list_path):
  103. logger.info(f'repo is exist, please read from {self.repo_list_path} file')
  104. with open(self.repo_list_path, 'r', newline='') as csvfile:
  105. reader = csv.reader(csvfile)
  106. for row in reader:
  107. repo = Repo()
  108. repo.__dict__ = row
  109. self.repos.append(repo)
  110. else:
  111. page_num = 1
  112. url = '{}/users/{}/repos'.format(self._host, self.username)
  113. while True:
  114. r = self.sess.get(url, params={'type': 'all', 'page': page_num})
  115. if r.status_code != 200:
  116. logger.error(f'request url {url} failed, status code {r.status_code}')
  117. return
  118. repo_list = r.json()
  119. for repo in repo_list:
  120. repo_obj = Repo()
  121. repo_obj.name = repo.get('name')
  122. repo_obj.url = repo.get('html_url')
  123. repo_obj.description = repo.get('description')
  124. repo_obj.language = repo.get('language')
  125. repo_obj.star = repo.get('stargazers_count')
  126. repo_obj.fork = repo.get('forks_count')
  127. repo_obj.watch = repo.get('watchers_count')
  128. self.repos.append(repo_obj)
  129. self.repos.sort(key=lambda x: x.star, reverse=True)
  130. links = r.headers.get('Link')
  131. if not links or 'rel="next"' not in links:
  132. break
  133. next_url = None
  134. for link in links.split(','):
  135. if 'rel="next"' in link:
  136. next_url = link.split(';')[0].strip('<>')
  137. break
  138. if not next_url:
  139. break
  140. page_num += 1
  141. self.save_csv()
  142. return self.repos
  143. def _clone_all_repo(self):
  144. """clone all repo"""
  145. for repo in self.repos:
  146. os.system(f'git clone {repo.url}')
  147. def clone_user_repos(self):
  148. """clone all repo of a user"""
  149. if os.path.exists(self.repo_list_path):
  150. with open(self.repo_list_path, 'r', newline='') as csvfile:
  151. reader = csv.reader(csvfile)
  152. for row in reader:
  153. if row[0] == 'name':
  154. continue
  155. repo = Repo()
  156. repo.__dict__ = row
  157. self.repos.append(repo)
  158. else:
  159. self.get_repo_list()
  160. self._clone_all_repo()
  161. @classmethod
  162. def suitable(cls, extractor: str) -> bool:
  163. """check if this extractor is suitable for this platform"""
  164. return extractor == 'github'