-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathgit.py
141 lines (104 loc) · 4.57 KB
/
git.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
from os.path import join, exists, dirname
from os import getcwd, mkdir, environ
from logging import getLogger
from util import locked_file, is_fresh, touch, run_cmd
from requests_oauthlib import OAuth2Session
from requests import get
github_client_id = r'e62e0d541bb6d0125b62'
github_client_secret = r'1f488407e92a59beb897814e9240b5a06a2020e3'
jlogger = getLogger('jekit')
class PrivateRepoException (Exception): pass
class MissingRepoException (Exception): pass
class MissingRefException (Exception): pass
def prepare_git_checkout(account, repo, ref, token):
'''
'''
repo_href = 'https://github.com/%s/%s.git' % (account, repo)
repo_path = join(getcwd(), 'repos/%s-%s' % (account, repo))
repo_refs = 'https://api.github.com/repos/%s/%s/branches' % (account, repo)
repo_sha = 'https://api.github.com/repos/%s/%s/commits/%s' % (account, repo, ref)
checkout_path = join(getcwd(), 'checkouts/%s-%s-%s' % (account, repo, ref))
checkout_lock = checkout_path + '.git-lock'
if exists(checkout_path) and is_fresh(checkout_path):
return checkout_path
ref_check = OAuth2Session(github_client_id, token=token).get(repo_refs)
if ref_check.status_code == 401:
# Github wants authentication.
raise PrivateRepoException()
elif ref_check.status_code == 404:
# This repository might not exist at all?
raise MissingRepoException()
branches = dict([(b['name'], b['commit']['sha']) for b in ref_check.json()])
ref_sha = branches.get(ref, None)
if ref_sha is None:
# The ref is not a branch, but it may be a sha.
sha_check = OAuth2Session(github_client_id, token=token).get(repo_sha)
if sha_check.status_code == 200:
# The ref must be a sha hash.
ref_sha = sha_check.json()['sha']
else:
# The repository exists, but the branch does not?
raise MissingRefException()
if token:
jlogger.debug('Adding Github credentials to environment')
environ.update(dict(GIT_ASKPASS=join(dirname(__file__), 'askpass.py')))
environ.update(dict(GIT_USERNAME=token['access_token'], GIT_PASSWORD=''))
else:
jlogger.debug('Clearing Github credentials from environment')
environ.update(dict(GIT_ASKPASS='', GIT_USERNAME='', GIT_PASSWORD=''))
with locked_file(checkout_lock):
if not exists(repo_path):
git_clone(repo_href, repo_path)
else:
git_fetch(repo_path, ref, ref_sha)
git_checkout(repo_path, checkout_path, ref)
# Make sure these are gone before we return.
environ.update(dict(GIT_ASKPASS='', GIT_USERNAME='', GIT_PASSWORD=''))
return checkout_path
def git_clone(href, path):
''' Clone a git repository from its remote address to a local path.
'''
jlogger.info('Cloning to ' + path)
run_cmd(('git', 'clone', '--mirror', href, path))
def get_ref_sha(repo_path, ref):
''' Get the current SHA for a ref in the given repo path.
'''
return run_cmd(('git', 'show', '--pretty=%H', '--summary', ref), repo_path).strip()
def git_fetch(repo_path, ref, sha):
''' Run `git fetch` inside a local git repository.
'''
jlogger.info('Fetching in ' + repo_path)
try:
found_sha = get_ref_sha(repo_path, ref)
except RuntimeError:
#
# Account for a missing ref by performing a complete fetch.
#
jlogger.debug('Complete fetch in '+repo_path)
run_cmd(('git', 'fetch'), repo_path)
found_sha = get_ref_sha(repo_path, ref)
if sha == found_sha:
jlogger.debug('Skipping fetch in '+repo_path)
else:
run_cmd(('git', 'fetch'), repo_path)
touch(repo_path)
def git_checkout(repo_path, checkout_path, ref):
''' Check out a git repository to a given reference and path.
This function is assumed to be run in a lock.
'''
jlogger.info('Checking out to ' + checkout_path)
if not exists(checkout_path):
mkdir(checkout_path)
hash_file = checkout_path + '.commit-hash'
commit_hash = get_ref_sha(repo_path, ref)
do_checkout = True
if exists(hash_file):
previous_hash = open(hash_file).read().strip()
if previous_hash == commit_hash:
jlogger.debug('Skipping checkout to '+checkout_path)
do_checkout = False
if do_checkout:
run_cmd(('git', '--work-tree='+checkout_path, 'checkout', ref, '--', '.'), repo_path)
touch(checkout_path)
with open(hash_file, 'w') as file:
print >> file, commit_hash