-
Notifications
You must be signed in to change notification settings - Fork 73
/
github_clone_setup.py
executable file
·163 lines (127 loc) · 5.48 KB
/
github_clone_setup.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#!/usr/bin/env python
"""
github_clone_setup.py
---------------------
Simple script to use on clones of GitHub repositories. Sets fetch refs for pull
requests and, if `git config --global github.token` returns a valid API token,
sets an 'upstream' remote if the repository is a fork.
Note - I *was* going to use ConfigParser to interact with .git/config instead
of shelling out like a bad person. Then I found that ConfigParser barfs on any
lines with leading space, like .git/config. Oh well. I can't fix *every* upstream
bug.
The canonical version of this script lives at:
https://github.com/jantman/misc-scripts/blob/master/github_clone_setup.py
Copyright 2014 Jason Antman <[email protected]> <http://www.jasonantman.com>
Free for any use provided that patches are submitted back to me.
Requirements
============
* Python 2.7+ (uses subprocess.check_output)
* github3.py>=0.8.2 (if using GitHub integration; tested with 0.8.2)
Changelog
=========
2014-04-27 jantman (Jason Antman) <[email protected]>
- initial version
"""
import sys
import os.path
import subprocess
import optparse
import re
from github3 import login, GitHub
def get_api_token():
""" get GH api token """
apikey = subprocess.check_output(['git', 'config', '--global', 'github.token']).strip()
if len(apikey) != 40:
raise SystemExit("ERROR: invalid github api token from `git config --global github.token`: '%s'" % apikey)
return apikey
def get_config_value(confpath, key):
""" gets a git config value using `git config` """
output = subprocess.check_output(['git', 'config', '--file=%s' % confpath, '--get-all', '%s' % key])
return output
def add_config_value(confpath, key, value):
""" adds a git config value using `git config` """
cmd = ['git', 'config', '--file=%s' % confpath, '--add', '%s' % key, value]
output = subprocess.check_output(cmd)
print(" ".join(cmd))
return output
def get_remotes(confpath, gitdir):
""" list all remotes for a repo """
remotes_str = subprocess.check_output(['git', '--work-tree=%s' % gitdir, '--git-dir=%s' % (os.path.join(gitdir, '.git')), 'remote'])
remotes = remotes_str.splitlines()
return remotes
def set_pull_fetches(confpath, gitdir, remotes):
""" set fetch refs for pulls if not already there """
for rmt in remotes:
fetches_str = get_config_value(confpath, 'remote.%s.fetch' % rmt)
fetches = fetches_str.splitlines()
pull_fetch = '+refs/pull/*/head:refs/pull/%s/*' % rmt
if pull_fetch not in fetches:
add_config_value(confpath, 'remote.%s.fetch' % rmt, pull_fetch)
return True
def get_owner_reponame_from_url(url):
"""
parse a github repo URL into (owner, reponame)
patterns:
[email protected]:jantman/misc-scripts.git
https://github.com/jantman/misc-scripts.git
http://github.com/jantman/misc-scripts.git
git://github.com/jantman/misc-scripts.git
"""
m = re.match(r'^.+[/:]([^/]+)/([^/\.]+)(\.git)?$', url)
if not m:
raise SystemExit("ERROR: unable to parse URL '%s'" % url)
if len(m.groups()) < 3:
raise SystemExit("ERROR: unable to parse URL '%s'" % url)
return (m.group(1), m.group(2))
def setup_upstream(confpath, gitdir):
""" use GH API to find parent/upstream, and set remote for it """
# see if upstream is set
try:
upstream = get_config_value(confpath, 'remote.upstream.url')
return True
except subprocess.CalledProcessError:
pass
origin_url = get_config_value(confpath, 'remote.origin.url')
(owner, reponame) = get_owner_reponame_from_url(origin_url)
apikey = get_api_token()
gh = login(token=apikey)
repo = gh.repository(owner, reponame)
if repo.fork:
upstream_url = repo.parent.ssh_url
cmd = ['git', '--work-tree=%s' % gitdir, '--git-dir=%s' % (os.path.join(gitdir, '.git')), 'remote', 'add', 'upstream', upstream_url]
subprocess.check_call(cmd)
print(" ".join(cmd))
return True
def is_github_repo(confpath, gitdir):
""" return true if this repo origin is on GitHub, False otherwise """
origin_url = get_config_value(confpath, 'remote.origin.url')
if 'github.com' in origin_url:
return True
return False
def main(gitdir):
""" main entry point """
gitdir = os.path.abspath(os.path.expanduser(gitdir))
if not os.path.exists(gitdir):
raise SystemExit("ERROR: path does not exist: %s" % gitdir)
confpath = os.path.join(gitdir, '.git', 'config')
if not os.path.exists(confpath):
raise SystemExit("ERROR: does not appear to be a valid git repo - path does not exist: %s" % confpath)
if not is_github_repo(confpath, gitdir):
raise SystemExit("%s is not a clone of a github repo" % gitdir, 0)
remotes = get_remotes(confpath, gitdir)
if 'upstream' not in remotes:
setup_upstream(confpath, gitdir)
remotes = get_remotes(confpath, gitdir)
if 'upstream' not in remotes:
raise SystemExit("Error: upstream not successfully added to remotes")
set_pull_fetches(confpath, gitdir, remotes)
def parse_args(argv):
""" parse arguments with OptionParser """
parser = optparse.OptionParser(usage='github_clone_setup.py -d <path to clone>')
parser.add_option('-d', '--dir', dest='gitdir', action='store', type='string',
help='path to the local clone of the repository')
options, args = parser.parse_args(argv)
return (options, args)
if __name__ == "__main__":
opts, args = parse_args(sys.argv)
main(opts.gitdir)