Last active
August 29, 2023 03:56
-
-
Save maejok-xx/ba8de8d512d52155275389aa845d3fbe to your computer and use it in GitHub Desktop.
Easily Fork multiple GitHub repositories (and set them private) using GitHub API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Author: https://github.com/maej20 | |
# ---- repolist.txt EXAMPLE ---- | |
# | |
# user1/repo-name | |
# # someoneelse/skipped-repo-name | |
# user524/you-get-the-repo | |
# CoolDude_69420/For_Good-Measure-repo | |
# | |
# -------- END EXAMPLE -------- | |
from colorama import Fore, Style, Back | |
import requests | |
import json | |
import time | |
# NewLine Separated List of repos to fork (format: 0wnerName/the-repo-name) | |
REPO_LIST = 'repolist.txt' | |
# GitHub Developer Personal Access Token (https://github.com/settings/tokens) | |
API_KEY = '<API_KEY>' | |
# GitHub Username you're forking to | |
USERNAME = '<YOUR_GITHUB_USERNAME>' | |
# GitHub Organization you're forking yo (blank if not using an organization) | |
ORGANIZATION = '<YOUR_GITHUB_ORGANIZATION>' | |
# Make new forks private? | |
PRIVATE = 0 | |
# Hide commented repos outputs? (use # to comment a line/repo) | |
HIDE_SKIPPED_REPOS = 0 | |
# Prevent actually sending the payload and just runs the logic? | |
TESTING = 0 | |
with open(REPO_LIST) as f: | |
errors = [] | |
repos = list(filter(None, (line.rstrip() for line in f))) | |
for count, line in enumerate(repos): | |
if line[0] == "#": # skip commented out repos | |
if not HIDE_SKIPPED_REPOS: print(Style.DIM + "{}/{} - skipped - {}".format(count+1, len(repos), line.split("#", 1)[1].strip()) + Style.RESET_ALL) | |
continue | |
repoStr = line.split("/", 1) | |
repo_owner = repoStr[0] | |
repo_name = repoStr[1] | |
print("{}/{} - Forking - {}".format(count+1, len(repos), line)) | |
url = "https://api.github.com/repos/{}/{}/forks".format(repo_owner,repo_name) | |
payload = json.dumps({ | |
"owner": repo_owner, | |
"repo": repo_name, | |
"organization": ORGANIZATION | |
}) | |
headers = { | |
'Accept': 'application/json', | |
'Authorization': 'Bearer {}'.format(API_KEY), | |
'Content-Type': 'application/json', | |
'X-GitHub-Api-Version': '2022-11-28' | |
} | |
if not TESTING: | |
response = requests.request("POST", url, headers=headers, data=payload) | |
if not response.status_code == 200 and not response.status_code == 202: | |
errors.append([line, response.status_code, "FORKING"]) | |
print(Fore.YELLOW + 'Forking Error: {} [{}]'.format(response.status_code, line) + Style.RESET_ALL) | |
if PRIVATE: | |
url = "https://api.github.com/repos/{}/{}".format(ORGANIZATION if ORGANIZATION else USERNAME, repo_name) | |
payload = json.dumps({ | |
"visibility": "private" | |
}) | |
headers = { | |
'Accept': 'application/json', | |
'owner': ORGANIZATION if ORGANIZATION else USERNAME, | |
'new_owner': ORGANIZATION if ORGANIZATION else USERNAME, | |
'repo': repo_name, | |
'Authorization': 'Bearer {}'.format(API_KEY), | |
'Content-Type': 'application/json', | |
'X-GitHub-Api-Version': '2022-11-28' | |
} | |
if not TESTING: | |
response = requests.request("PATCH", url, headers=headers, data=payload) | |
if not response.status_code == 200 and not response.status_code == 202: | |
errors.append([line, response.status_code, "PRIVATING"]) | |
print(Fore.YELLOW + 'Privating Error: {} [{}]'.format(response.status_code, line) + Style.RESET_ALL) | |
print(' ') | |
print(Fore.GREEN + "DONE!" + Style.RESET_ALL) | |
if len(errors): | |
print(' ') | |
print("Completed with errors: ") | |
for count, error in enumerate(errors): | |
print(Fore.YELLOW, end ='') | |
print('{}/{} {} had a {} error while {}'.format(count, len(errors), error[0], error[1], error[2])) | |
print(' ' + Style.RESET_ALL) | |
print(Fore.BLUE + "Note: errors don't mean the repo(s) didn't fork or get their visibility set to private. Verify for yourself." + Style.RESET_ALL) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This was way less painful to write than manually forking a project with 80+ repos.
I hope it helps somebody else.