repos.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. # SPDX-License-Identifier: AGPL-3.0-or-later
  2. #
  3. # Copyright (C) 2023 Ferass El Hafidi <vitali64pmemail@protonmail.com>
  4. # Copyright (C) 2023 Leo Gavilieau <xmoo@vern.cc>
  5. from markupsafe import escape
  6. from pycmarkgfm import gfm_to_html as markdown
  7. from base64 import b64decode # For some reason GitLab encodes file contents
  8. from api.base import api_call
  9. # Repositories
  10. def get_repo_primary_branch(instance, repo):
  11. recv = api_call(\
  12. 'https://%s/api/v4/projects/%s' % (instance, repo))
  13. if "default_branch" in recv:
  14. return recv["default_branch"]
  15. return "<none>"
  16. def get_repo_description(instance, repo):
  17. recv = api_call("https://%s/api/v4/projects/%s" % \
  18. (instance, repo))
  19. if "description" in recv:
  20. if recv['description'] != None and recv['description'] != "":
  21. return recv['description']
  22. return "<none>" # If no conditions match
  23. def get_repo_idle(instance, repo):
  24. recv = api_call("https://%s/api/v4/projects/%s" % \
  25. (instance, repo))
  26. if "last_activity_at" in recv:
  27. # TODO: I will include a separate patch that prettifies the date soon.
  28. return recv["last_activity_at"]
  29. else:
  30. return "<none>"
  31. def get_repo_cloneurls(instance, repo):
  32. recv = api_call("https://%s/api/v4/projects/%s" % \
  33. (instance, repo))
  34. a = "<none>" # Basic error handling
  35. b = "<none>"
  36. if "http_url_to_repo" in recv and recv["http_url_to_repo"] != "":
  37. a = recv["http_url_to_repo"]
  38. if "ssh_url_to_repo" in recv and recv["ssh_url_to_repo"] != "":
  39. b = recv["ssh_url_to_repo"]
  40. return a,b
  41. def get_repo_brancheslist(instance, repo, page = None):
  42. branches_list = api_call(\
  43. 'https://%s/api/v4/projects/%s/repository/branches?page=%s&per_page=%s' \
  44. % (instance, repo, page if page is not None else 1, \
  45. 100 if page is not None else 8))
  46. if "message" in branches_list:
  47. return ""
  48. # Convert to HTML
  49. branches_list_html = ""
  50. for branch in branches_list:
  51. branches_list_html += "<tr><td><a href=\"/%s/%s/-/tree/%s\">%s</a></td>" % \
  52. (instance, repo.replace("%2F", "/"), branch['name'], branch['name']) + \
  53. "<td>%s</td>" % branch['commit']['title'] + \
  54. "<td>%s</td>" % branch['commit']['author_name'] + \
  55. "<td>%s</td></tr>" % branch['commit']['authored_date']
  56. return branches_list_html
  57. def get_repo_tagslist(instance, repo, page = None):
  58. tags_list = api_call(\
  59. 'https://%s/api/v4/projects/%s/repository/tags?page=%s&per_page=%s' \
  60. % (instance, repo, page if page is not None else 1, \
  61. 100 if page is not None else 8))
  62. if "message" in tags_list:
  63. return ""
  64. # Convert to HTML
  65. tags_list_html = ""
  66. for tag in tags_list:
  67. tags_list_html += "<tr><td><a href=\"/%s/%s/-/tree/%s\">%s</a></td>" % \
  68. (instance, repo.replace("%2F", "/"), tag['name'], tag['name']) + \
  69. "<td>%s</td>" % tag['commit']['title'] + \
  70. "<td>%s</td>" % tag['commit']['author_name'] + \
  71. "<td>%s</td></tr>" % tag['commit']['authored_date']
  72. return tags_list_html
  73. def get_repo_commits(instance, repo, page = 1, branch = None, commit = None):
  74. if commit is None:
  75. if branch:
  76. commits_list = api_call(\
  77. 'https://%s/api/v4/projects/%s/repository/commits?page=%s&per_page=%s&ref_name=%s' \
  78. % (instance, repo, page, 100, branch))
  79. else:
  80. commits_list = api_call(\
  81. 'https://%s/api/v4/projects/%s/repository/commits?page=%s&per_page=%s' \
  82. % (instance, repo, page, 100))
  83. if "message" in commits_list:
  84. return "<p class=\"error\">Could not retrieve commits: %s</p>" \
  85. % (commits_list["message"])
  86. # Convert to HTML
  87. commits_list_html = ""
  88. for commit in commits_list:
  89. commits_list_html += \
  90. "<tr><td><a href=\"/%s/%s/-/commit/%s\"><pre>%s</pre></a></td>" % \
  91. (instance, repo.replace("%2F", "/"), commit['id'], commit['short_id']) + \
  92. "<td>%s</td>" % commit['title'] + \
  93. "<td>%s</td>" % commit['author_name'] + \
  94. "<td>%s</td></tr>" % commit['authored_date']
  95. commits_list_html += "<tr><td><a href=\"?page=%s\">Next →</a></td></tr>" \
  96. % (page + 1)
  97. return commits_list_html
  98. else:
  99. commit_info = api_call(\
  100. 'https://%s/api/v4/projects/%s/repository/commits/%s' \
  101. % (instance, repo, commit))
  102. try: commit_info['id']
  103. except KeyError:
  104. return "<p style=\"background-color: orangered; padding: 10px\">" \
  105. "an error occured: commit not found</p>"
  106. commit_diff = api_call(\
  107. 'https://%s/api/v4/projects/%s/repository/commits/%s/diff' \
  108. % (instance, repo, commit))
  109. commit_diff_html = ""
  110. for diff in commit_diff:
  111. if diff['new_file'] == False:
  112. commit_diff_html += "--- /dev/null\n"
  113. else:
  114. commit_diff_html += "--- a/%s\n" % diff['old_path']
  115. commit_diff_html += "+++ b/%s\n" % diff['new_path']
  116. commit_diff_html += diff['diff']
  117. return commit_info['author_name'], commit_info['committer_name'], \
  118. commit_info['id'], commit_info['parent_ids'][0], commit_info['message'], \
  119. commit_diff_html
  120. def get_repo_readme(instance, repo):
  121. recv = api_call(\
  122. 'https://%s/api/v4/projects/%s' % (instance, repo))
  123. readme_decoded = "" # No readme
  124. if not "readme_url" in recv:
  125. return ""
  126. if recv['readme_url']:
  127. readme = api_call(\
  128. 'https://%s/api/v4/projects/%s/repository/files/%s?ref=%s' \
  129. % (instance, repo, recv['readme_url'].split('/')[-1], \
  130. get_repo_primary_branch(instance, repo)))['content']
  131. readme_decoded = b64decode(readme).decode('utf-8')#.replace(\
  132. # "https://%s/" % instance, "/%s/" % instance)
  133. if recv['readme_url'].split('/')[-1].split('.')[-1] == 'md':
  134. readme_decoded = markdown(readme_decoded)
  135. else: readme_decoded = "<pre>" + readme_decoded + "</pre>"
  136. return readme_decoded
  137. def get_repo_avatar(instance, repo):
  138. recv = api_call(\
  139. 'https://%s/api/v4/projects/%s' % (instance, repo))
  140. if recv['avatar_url'] is None:
  141. return ""
  142. return recv['avatar_url']
  143. def get_repo_tree(instance, repo, path, branch = None):
  144. recv = api_call(\
  145. 'https://%s/api/v4/projects/%s' % (instance, repo))
  146. if branch is None: branch = recv['default_branch']
  147. tree = api_call(\
  148. 'https://%s/api/v4/projects/%s/repository/tree?path=%s&per_page=100&ref=%s' % \
  149. (instance, repo, path, branch))
  150. if "message" in tree:
  151. return "<p class=\"error\">Could not retrieve tree list: %s</p>" \
  152. % (tree["message"])
  153. tree_html = ""
  154. for file in tree:
  155. tree_html += "<tr><td>%s</td>" % file['mode'] + \
  156. "<td><a href=\"/%s/%s/-/%s/%s/%s\">%s</a></td>" % (instance, \
  157. recv['path_with_namespace'], file['type'], branch, \
  158. escape(file['path']), file['name']) + \
  159. "<td>%s</td>" % file['type']
  160. return tree_html
  161. def get_repo_blob(instance, repo, blob, branch = None):
  162. blob_decoded = "" # blob
  163. lines = ""
  164. line = 1
  165. if branch is None: branch = get_repo_primary_branch(instance, repo)
  166. blob_file = api_call(\
  167. 'https://%s/api/v4/projects/%s/repository/files/%s?ref=%s' \
  168. % (instance, repo, blob.replace('/', '%2F'), # TODO: Replace replace() \
  169. branch))['content']
  170. blob_decoded = b64decode(blob_file).decode('utf-8')
  171. while line <= blob_decoded.count("\n"):
  172. lines += "%d\n" % line
  173. line += 1
  174. return blob_decoded, lines
  175. def get_repo_issues(instance, repo, state = None, page = None):
  176. if state is None: state = "all"
  177. if page is None: page = 1
  178. issues_list = api_call(\
  179. 'https://%s/api/v4/projects/%s/issues?state=%s&page=%s' \
  180. % (instance, repo, state, page))
  181. issues_list_html = ""
  182. for issue in issues_list:
  183. issues_list_html += "<tr><td>#<a href=\"/%s/%s/-/issues/%s\">%s</a></td>" \
  184. % (instance, repo.replace('%2F', '/'), issue['iid'], issue['iid']) + \
  185. "<td><a href=\"/%s/%s/-/issues/%s\">%s</a></td>" \
  186. % (instance, repo.replace('%2F', '/'), issue['iid'], issue['title']) + \
  187. "<td>%s</td>" % issue['author']['name'] + \
  188. "<td>%s</td>" % issue['state'] + \
  189. "<td>%s</td></tr>" % issue['updated_at']
  190. return issues_list_html
  191. def get_repo_issue(instance, repo, iid):
  192. issue = api_call(\
  193. 'https://%s/api/v4/projects/%s/issues/%s' \
  194. % (instance, repo, iid))
  195. return issue['title'], issue['state'], issue['updated_at'], issue['author']['name'], \
  196. markdown(issue['description']).replace('<p>', '').replace('</p>', '')
  197. def get_repo_issueparticipants(instance, repo, iid):
  198. recv = api_call(\
  199. 'https://%s/api/v4/projects/%s/issues/%s/participants' \
  200. % (instance, repo, iid))
  201. issue_participants = []
  202. for participant in recv:
  203. issue_participants.append(participant['name'])
  204. return issue_participants