git-backup.py (5588B) - raw
1 #!/usr/bin/env python3 2 # Copyright (C) 2019 Oscar Benedito 3 # 4 # This program is free software: you can redistribute it and/or modify 5 # it under the terms of the GNU Affero General Public License as 6 # published by the Free Software Foundation, either version 3 of the 7 # License, or (at your option) any later version. 8 # 9 # This program is distributed in the hope that it will be useful, 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 # GNU Affero General Public License for more details. 13 # 14 # You should have received a copy of the GNU Affero General Public License 15 # along with this program. If not, see <https://www.gnu.org/licenses/>. 16 17 import datetime 18 import json 19 import os 20 import sys 21 import subprocess 22 import git 23 import requests 24 25 26 ENV_GITLAB_TOKEN = "GITLAB_TOKEN" 27 ENV_GITHUB_TOKEN = "GITHUB_TOKEN" 28 ENV_CUSTOM_REPOSITORIES_PATH = "CUSTOM_REPOSITORIES_PATH" 29 ENV_TARGET_DIR = "TARGET_DIR" 30 31 32 def backup_gitlab(token): 33 def get_repositories_data_gitlab(url, page): 34 headers = {"PRIVATE-TOKEN": token} 35 response = requests.get(url + "&page=" + str(page), headers=headers, timeout=(10, 60)) 36 response.raise_for_status() 37 return response.json() 38 39 url = "https://gitlab.com/api/v4/projects?per_page=100&membership=true" 40 page = 1 41 repositories = get_repositories_data_gitlab(url, page) 42 43 backup_data["sites"]["gitlab.com"] = [] 44 while len(repositories) != 0: 45 for repository in repositories: 46 clone_dir = "repositories/gitlab.com/" + repository["path_with_namespace"] 47 print("gitlab.com/" + repository["path_with_namespace"]) 48 if os.path.isdir(clone_dir): 49 git.cmd.Git(clone_dir).fetch() 50 else: 51 subprocess.run(["git", "clone", "--mirror", "--", repository["ssh_url_to_repo"], clone_dir], check=True) 52 backup_data["sites"]["gitlab.com"].append( 53 { 54 "name": repository["name"], 55 "description": repository["description"], 56 "path": repository["path_with_namespace"], 57 "clone_url": repository["ssh_url_to_repo"], 58 } 59 ) 60 page += 1 61 repositories = get_repositories_data_gitlab(url, page) 62 63 64 def backup_github(token): 65 def get_repositories_data_github(url, token, page): 66 headers = {"Authorization": "token " + token} 67 response = requests.get(url + "?page=" + str(page), headers=headers, timeout=(10, 60)) 68 response.raise_for_status() 69 return response.json() 70 71 url = "https://api.github.com/user/repos" 72 page = 1 73 repositories = get_repositories_data_github(url, token, page) 74 75 backup_data["sites"]["github.com"] = [] 76 while len(repositories) != 0: 77 for repository in repositories: 78 clone_dir = "repositories/github.com/" + repository["full_name"] 79 print("github.com/" + repository["full_name"]) 80 if os.path.isdir(clone_dir): 81 git.cmd.Git(clone_dir).fetch() 82 else: 83 subprocess.run(["git", "clone", "--mirror", "--", repository["ssh_url"], clone_dir], check=True) 84 backup_data["sites"]["github.com"].append( 85 { 86 "name": repository["name"], 87 "description": repository["description"], 88 "path": repository["full_name"], 89 "clone_url": repository["ssh_url"], 90 } 91 ) 92 page += 1 93 repositories = get_repositories_data_github(url, token, page) 94 95 96 def backup_custom_repositories(repositories): 97 for repository in repositories: 98 clone_dir = "repositories/" + repository["host"] + "/" + repository["path"] 99 print(repository["host"] + "/" + repository["path"]) 100 if os.path.isdir(clone_dir): 101 git.cmd.Git(clone_dir).fetch() 102 else: 103 subprocess.run(["git", "clone", "--mirror", "--", repository["clone_url"], clone_dir], check=True) 104 if repository["host"] not in backup_data["sites"]: 105 backup_data["sites"][repository["host"]] = [] 106 backup_data["sites"][repository["host"]].append( 107 { 108 "name": repository["name"], 109 "description": repository["description"], 110 "path": repository["path"], 111 "clone_url": repository["clone_url"], 112 } 113 ) 114 115 116 def main(): 117 try: 118 os.makedirs(os.environ.get(ENV_TARGET_DIR), exist_ok=True) 119 os.chdir(os.environ.get(ENV_TARGET_DIR)) 120 except Exception as e: 121 raise RuntimeError("Failed to create backup directory") from e 122 123 backup_data["time"] = str(datetime.datetime.now()) 124 backup_data["sites"] = {} 125 126 token = os.environ.get(ENV_GITLAB_TOKEN) 127 if token: 128 backup_gitlab(token) 129 130 token = os.environ.get(ENV_GITHUB_TOKEN) 131 if token: 132 backup_github(token) 133 134 custom_repositories_path = os.environ.get(ENV_CUSTOM_REPOSITORIES_PATH) 135 if custom_repositories_path: 136 try: 137 with open(custom_repositories_path, "r") as f: 138 repositories = json.load(f) 139 backup_custom_repositories(repositories) 140 except FileNotFoundError: 141 raise RuntimeError("Missing configured repository list: " + custom_repositories_path) 142 143 with open("backup_data.json", "w", encoding="utf-8") as output_file: 144 json.dump(backup_data, output_file, ensure_ascii=False) 145 output_file.close() 146 147 148 backup_data = {} 149 main()