├── LICENSE ├── README.md ├── export.py └── output.html /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Max Base 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GitHub Repository Export List 2 | 3 | A tiny script to get list of all repository of a GitHub user and generate HTML output with style. 4 | 5 | ## Features of github-repos-exporter 6 | 7 | - Auto get name from username 8 | - Auto detect number of repositories 9 | - Auto pagination to get list of all repos 10 | - Groupination repos by its main language name 11 | - Groupination non-language repo to other 12 | - Generate **HTML** output page 13 | 14 | ## Using GitHub export repositories 15 | 16 | Set your username at export.py file. then: 17 | 18 | ```bash 19 | $ git clone https://github.com/BaseMax/GitHub-Repository-Export-List/ 20 | $ cd GitHub-Repository-Export-List 21 | $ python export.py > output.html 22 | ``` 23 | 24 | You can watch/see example HTML generated in output.html at [here](https://basemax.github.io/GitHub-Repository-Export-List/output.html). 25 | 26 | ### Rate Limit 27 | 28 | Keep in mind that the GitHub server has limitations. 29 | 30 | While I was writing and testing, I seemed to be blocked and GitHub servers no longer answered, so I had to use the `torsocks` to continue the project. 31 | 32 | ### TODO 33 | 34 | - Get username from `$argv` 35 | - Move functions to a new class 36 | - Remove the example code from lib file (export.py) 37 | - Create examples and tests file 38 | - Publish the package to pip 39 | - Generate JSON output 40 | - Generate csv output 41 | 42 | © Copyright Max Base 43 | -------------------------------------------------------------------------------- /export.py: -------------------------------------------------------------------------------- 1 | # Max Base 2 | # 2021-03-21 3 | # https://github.com/BaseMax/GitHub-Repository-Export-List 4 | 5 | import sys 6 | import time 7 | import json 8 | import math 9 | import requests 10 | 11 | def check_user(username): 12 | url = "https://api.github.com/users/" + username 13 | # Note: if you put a / at the end of url, this will not works! 14 | response = requests.get(url) 15 | json_data = json.loads(response.text) 16 | return json_data 17 | 18 | def get_repos(username, page): 19 | url = "https://api.github.com/users/" + username + "/repos?per_page=100&page=" + str(page) 20 | response = requests.get(url) 21 | json_data = json.loads(response.text) 22 | 23 | items = [] 24 | 25 | for item in json_data: 26 | # {'id': 329726066, 'node_id': 'MDEwOlJlcG9zaXRvcnkzMjk3MjYwNjY=', 'name': 'GameNetSystemPHP', 'full_name': 'BaseMax/GameNetSystemPHP', 'private': False, 'owner': {'login': 'BaseMax', 'id': 2658040, 'node_id': 'MDQ6VXNlcjI2NTgwNDA=', 'avatar_url': 'https://avatars.githubusercontent.com/u/2658040?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/BaseMax', 'html_url': 'https://github.com/BaseMax', 'followers_url': 'https://api.github.com/users/BaseMax/followers', 'following_url': 'https://api.github.com/users/BaseMax/following{/other_user}', 'gists_url': 'https://api.github.com/users/BaseMax/gists{/gist_id}', 'starred_url': 'https://api.github.com/users/BaseMax/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/BaseMax/subscriptions', 'organizations_url': 'https://api.github.com/users/BaseMax/orgs', 'repos_url': 'https://api.github.com/users/BaseMax/repos', 'events_url': 'https://api.github.com/users/BaseMax/events{/privacy}', 'received_events_url': 'https://api.github.com/users/BaseMax/received_events', 'type': 'User', 'site_admin': False}, 'html_url': 'https://github.com/BaseMax/GameNetSystemPHP', 'description': 'A complete system for game nets that can manage the category of console devices and also have control over rent and receiving money. (PHP + Ajax javascript JSON)', 'fork': False, 'url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP', 'forks_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/forks', 'keys_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/keys{/key_id}', 'collaborators_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/collaborators{/collaborator}', 'teams_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/teams', 'hooks_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/hooks', 'issue_events_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/issues/events{/number}', 'events_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/events', 'assignees_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/assignees{/user}', 'branches_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/branches{/branch}', 'tags_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/tags', 'blobs_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/git/blobs{/sha}', 'git_tags_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/git/tags{/sha}', 'git_refs_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/git/refs{/sha}', 'trees_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/git/trees{/sha}', 'statuses_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/statuses/{sha}', 'languages_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/languages', 'stargazers_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/stargazers', 'contributors_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/contributors', 'subscribers_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/subscribers', 'subscription_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/subscription', 'commits_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/commits{/sha}', 'git_commits_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/git/commits{/sha}', 'comments_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/comments{/number}', 'issue_comment_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/issues/comments{/number}', 'contents_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/contents/{+path}', 'compare_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/compare/{base}...{head}', 'merges_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/merges', 'archive_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/{archive_format}{/ref}', 'downloads_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/downloads', 'issues_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/issues{/number}', 'pulls_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/pulls{/number}', 'milestones_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/milestones{/number}', 'notifications_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/notifications{?since,all,participating}', 'labels_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/labels{/name}', 'releases_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/releases{/id}', 'deployments_url': 'https://api.github.com/repos/BaseMax/GameNetSystemPHP/deployments', 'created_at': '2021-01-14T20:25:17Z', 'updated_at': '2021-03-06T11:49:23Z', 'pushed_at': '2021-03-06T11:49:20Z', 'git_url': 'git://github.com/BaseMax/GameNetSystemPHP.git', 'ssh_url': 'git@github.com:BaseMax/GameNetSystemPHP.git', 'clone_url': 'https://github.com/BaseMax/GameNetSystemPHP.git', 'svn_url': 'https://github.com/BaseMax/GameNetSystemPHP', 'homepage': '', 'size': 1953, 'stargazers_count': 0, 'watchers_count': 0, 'language': 'PHP', 'has_issues': True, 'has_projects': True, 'has_downloads': True, 'has_wiki': True, 'has_pages': False, 'forks_count': 0, 'mirror_url': None, 'archived': False, 'disabled': False, 'open_issues_count': 0, 'license': {'key': 'gpl-3.0', 'name': 'GNU General Public License v3.0', 'spdx_id': 'GPL-3.0', 'url': 'https://api.github.com/licenses/gpl-3.0', 'node_id': 'MDc6TGljZW5zZTk='}, 'forks': 0, 'open_issues': 0, 'watchers': 0, 'default_branch': 'main'} 27 | 28 | link = "https://github.com/" + item["full_name"] 29 | description = item["description"] 30 | language = item["language"] 31 | name = item["name"] 32 | 33 | items.append({ 34 | "name": name, 35 | "language": language, 36 | "description": description, 37 | "link": link, 38 | }) 39 | 40 | return items 41 | 42 | def get_all_repos(username, public_repos): 43 | pages = math.ceil(public_repos / 100) 44 | 45 | items = [] 46 | 47 | for page in range(pages): # page starts from 0 48 | res = get_repos(username, page+1) 49 | items.extend(res) 50 | time.sleep(3) 51 | 52 | return items 53 | 54 | def group_by_languages(repos): 55 | languages = {} 56 | for repo in repos: 57 | if repo["language"] == "" or repo["language"] == None: 58 | lang = "other" 59 | else: 60 | lang = str(repo["language"]) 61 | 62 | if lang not in languages: 63 | languages[lang] = [] 64 | 65 | # TODO: remove language field from repo 66 | languages[lang].append(repo) 67 | 68 | return languages 69 | 70 | def generate_html(profile, groups): 71 | if profile["name"] == None: 72 | profile["name"] = profile["login"] 73 | 74 | print("" + profile["name"] + " GitHub") 75 | print("

" + profile["name"] + " GitHub

\n") 76 | 77 | for language, repos in groups.items(): 78 | 79 | print(""+ language +"") # TODO: upper-case first char of language name 80 | print("\n") 89 | 90 | if __name__ == "__main__": 91 | # check args 92 | if len(sys.argv) != 2: 93 | username="basemax" 94 | else: 95 | username = sys.argv[1] 96 | 97 | profile = check_user(username) 98 | if profile == None or profile == "" or profile == {} or profile == []: 99 | print("Error: User not found!") 100 | exit() 101 | elif "message" in profile and profile["message"] == "Not Found": 102 | print("Error: User not found!") 103 | exit() 104 | 105 | try: 106 | public_repos = profile["public_repos"] 107 | except KeyError: 108 | public_repos = 0 109 | 110 | if public_repos: 111 | all_repos = get_all_repos(username, public_repos) 112 | 113 | groups = group_by_languages(all_repos) 114 | 115 | # generate HTML output 116 | generate_html(profile, groups) 117 | else: 118 | print("Error: No public repositories or maybe network problem!") 119 | -------------------------------------------------------------------------------- /output.html: -------------------------------------------------------------------------------- 1 | Max Base GitHub 2 |

Max Base GitHub

3 | 4 | JavaScript 5 | 175 | 176 | PHP 177 | 459 | 460 | Java 461 | 523 | 524 | HTML 525 | 647 | 648 | Assembly 649 | 667 | 668 | other 669 | 739 | 740 | C++ 741 | 827 | 828 | Python 829 | 939 | 940 | C 941 | 1083 | 1084 | Objective-C 1085 | 1091 | 1092 | CSS 1093 | 1143 | 1144 | Go 1145 | 1163 | 1164 | Dart 1165 | 1175 | 1176 | Shell 1177 | 1191 | 1192 | GAP 1193 | 1199 | 1200 | C# 1201 | 1211 | 1212 | R 1213 | 1219 | 1220 | MATLAB 1221 | 1227 | 1228 | QML 1229 | 1235 | 1236 | Smali 1237 | 1243 | 1244 | Ruby 1245 | 1255 | 1256 | TeX 1257 | 1263 | 1264 | --------------------------------------------------------------------------------