Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
executable file 134 lines (101 sloc) 4.16 KB
#!/usr/bin/python3
# Copyright (c) 2018 Tomas Tomecek. All rights reserved.
#
# This work is licensed under the terms of the MIT license.
# For a copy, see <https://opensource.org/licenses/MIT>.
"""
Usage:
$ ./gh-fork $GITHUB_NAMESPACE/$GITHUB_REPO
In order to use this script, you need to have [GitHub API token](https://github.com/settings/tokens) present at:
~/.github.token
Functionality:
1. Fork given repo
a) if the fork exists, skip
2. Clone fork
3. Cd to the repo
4. Set remote upstream
4. Set remote origin
5. Fetch all
"""
import os
import sys
import logging
import subprocess
import github
logger = logging.getLogger("gh-fork")
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler(sys.stderr)
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s %(levelname)-6s %(message)s', '%H:%M:%S')
handler.setFormatter(formatter)
logger.addHandler(handler)
def is_fork_of(user_repo, target_repo):
""" is provided repo fork of gh.com/{parent_repo}/? """
return user_repo.fork and user_repo.parent and \
user_repo.parent.full_name == target_repo.full_name
def fork_gracefully(user, target_repo, user_repo):
""" fork if not forked, return forked repo """
if user_repo:
if is_fork_of(user_repo, target_repo):
logger.info("repo %s is fork of %s", user_repo.full_name, target_repo.full_name)
return user_repo
try:
full_name = target_repo.full_name
except github.GithubException.UnknownObjectException:
logger.error("repository doesn't exist")
sys.exit(1)
logger.info("forking repo %s to %s", full_name, user.login)
return user.create_fork(target_repo)
def clone_repo_and_cd_inside(repo, namespace):
os.makedirs(namespace, exist_ok=True)
os.chdir(namespace)
logger.debug("clone %s", repo.ssh_url)
retcode = subprocess.call(["git", "clone", repo.ssh_url])
# likely the repo is already there, not an issue
logger.debug("clone return code: %s", retcode)
os.chdir(repo.name)
def set_upstream_remote(repo):
logger.debug("set remote upstream to %s", repo.clone_url)
try:
subprocess.check_call(["git", "remote", "add", "upstream", repo.clone_url])
except subprocess.CalledProcessError:
subprocess.check_call(["git", "remote", "set-url", "upstream", repo.clone_url])
try:
subprocess.check_call(["git", "remote", "add", "upstream-w", repo.ssh_url])
except subprocess.CalledProcessError:
subprocess.check_call(["git", "remote", "set-url", "upstream-w", repo.ssh_url])
logger.debug("adding fetch rule to get PRs for upstream")
subprocess.check_call(["git", "config", "--local", "--add", "remote.upstream.fetch", "+refs/pull/*/head:refs/remotes/upstream/pr/*"])
def set_origin_remote(repo):
logger.debug("set remote origin to %s", repo.ssh_url)
subprocess.check_call(["git", "remote", "set-url", "origin", repo.ssh_url])
logger.debug("adding fetch rule to get PRs for origin")
subprocess.check_call(["git", "config", "--local", "--add", "remote.origin.fetch", "+refs/pull/*/head:refs/remotes/origin/pr/*"])
def fetch_all():
logger.debug("fetching everything")
with open("/dev/null", "w") as fd:
subprocess.check_call(["git", "fetch", "--all"], stdout=fd)
def main():
with open(os.path.expanduser("~/.github.token"), "r") as fd:
token = fd.read().strip()
full_repo_name = sys.argv[1].strip()
target_repo_org, target_repo_name = full_repo_name.split("/", 1)
g = github.Github(login_or_token=token)
target_repo = g.get_repo(full_repo_name)
user = g.get_user()
try:
user_repo = user.get_repo(target_repo_name)
except github.UnknownObjectException:
user_repo = None
if user.login == target_repo_org:
if not user_repo:
raise RuntimeError("repo %s not found" % target_repo_name)
clone_repo_and_cd_inside(user_repo, target_repo_org)
else:
user_repo = fork_gracefully(user, target_repo, user_repo)
clone_repo_and_cd_inside(user_repo, target_repo_org)
set_upstream_remote(target_repo)
set_origin_remote(user_repo)
fetch_all()
if __name__ == "__main__":
main()