-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.py
executable file
·254 lines (212 loc) · 8.09 KB
/
main.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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
#!/usr/bin/env python3
"""
git-svn-sync: a server that reflects commits made on GitHub repos to
svn repos.
Sai Vineet(svineet) [email protected]
"""
import sys
import json
import os
import subprocess
import http.server
import socketserver
from pprint import pprint
try:
from config import PORT, DIRECTORY_MAP
except ImportError:
print('Please copy config.example.py into config.py and edit as required')
print('Exiting.')
sys.exit(0)
from config import INITIAL_COMMIT_MESSAGE, COMMIT_MESSAGE, UPDATE_COMMIT_MESSAGE
from config import BREAK_LOCKS_EVERYTIME, RELOCK_EVERYTIME
UNLOCK_COMMAND = '''find . -type d \( -path ./.git -o -path ./.svn \) -prune -o -exec svn unlock --force {} \;'''
LOCK_COMMAND = '''find . -type d \( -path ./.git -o -path ./.svn \) -prune -o -exec svn lock --force {} \;'''
def do_command(command, print_output=True, return_=False):
"""
Execute a command on the shell and return output if asked for.
"""
output = subprocess.check_output(command, stderr=subprocess.STDOUT,
shell=True)
if print_output:
print(output.decode('utf-8'))
if return_:
return output.decode('utf-8')
def get_sha(repo_name):
"""
Enters repo_name directory and returns current git sha
"""
initial_dir = os.getcwd()
dir_ = os.path.join(os.getcwd(), DIRECTORY_MAP[repo_name][0])
os.chdir(dir_)
shia = ""
try:
shia = do_command('git rev-parse HEAD', return_=True)
except subprocess.CalledProcessError as error:
print('Error.')
print(error.output.decode('utf-8'))
os.chdir(initial_dir)
return shia
def svn_clone(repo_name):
"""
Enter repo_name directory and clone.
Will sync local repo with git remote.
Also does related things like add ignores and tries setting locks.
"""
name, git_url, svn_url = DIRECTORY_MAP[repo_name]
initial_dir = os.getcwd()
dir_ = os.path.join(os.getcwd(), name)
try:
print('This is the first run, please be patient as cloning and setting up might take time.')
do_command('svn co "'+svn_url+'" "'+name+'"')
print('Cloned from SVN successfully.')
os.chdir(dir_)
print('Setting ignores for svn')
do_command('svn propset svn:ignore .git .')
except subprocess.CalledProcessError as error:
print('Error!')
print(error.output.decode('utf-8'))
os.chdir(dir_)
try:
print('Now trying to gain lock access. May fail.')
do_command(UNLOCK_COMMAND)
print('Unlocking might have worked.')
except subprocess.CalledProcessError as error:
print('Unlocking/locking failed. Please see README#Troubleshooting')
print(error.output.decode('utf-8'))
try:
print('Initializing local repo and setting remotes to GitHub')
do_command('git init')
do_command('git remote add origin '+git_url)
print('Now pulling everything. May take time.')
try:
do_command('git pull origin master -f')
except Exception:
pass
do_command('git reset --hard origin/master')
do_command('svn add --force .')
try:
do_command('''svn st | grep ^! | awk '{print " --force "$2}' | xargs svn rm''')
except Exception:
pass
except subprocess.CalledProcessError as error:
print('Error!')
print(error.output.decode('utf-8'))
os.chdir(initial_dir)
def svn_push(repo_name, commit_message):
"""
Enter a directory with repo_name and then
svn commit with commit message.
It will first restore pristine state using git clean -d -f
then it will remove all staged files from svn
then it will add all files in current dir to svn
then it pushes to remote svn. This is so that
deleted or renamed files are taken care of.
"""
dir_name, git_url, svn_url = DIRECTORY_MAP[repo_name]
initial_dir = os.getcwd()
dir_ = os.path.join(os.getcwd(), dir_name)
print('Commiting with message '+commit_message)
os.chdir(dir_)
try:
if BREAK_LOCKS_EVERYTIME:
print('Breaking all locks as stated in config')
do_command(UNLOCK_COMMAND)
do_command('svn commit -m "'+commit_message+'"')
if RELOCK_EVERYTIME:
print('Now relocking repository.')
do_command(LOCK_COMMAND)
except subprocess.CalledProcessError as error:
print('Error!')
print(error.output.decode('utf-8'))
os.chdir(initial_dir)
def git_pull(repo_name):
"""
Enter repo_name directory and fetch-reset from git repo.
It will also remove all files from svn tracking and then
add them again to svn so that renamed files and deleted
files are taken care of.
"""
initial_dir = os.getcwd()
dir_ = os.path.join(os.getcwd(), DIRECTORY_MAP[repo_name][0])
os.chdir(dir_)
try:
try:
do_command('git pull origin master -f')
except Exception:
pass
do_command('git reset --hard origin/master')
do_command('svn add --force .')
try:
do_command('''svn st | grep ^! | awk '{print " --force "$2}' | xargs svn rm''')
except Exception:
pass
except subprocess.CalledProcessError as error:
print('Error.')
print(error.output.decode('utf-8'))
os.chdir(initial_dir)
class SyncHandler(http.server.SimpleHTTPRequestHandler):
"""
HTTP Server that syncs local svn repos with upstream
GitHub repos.
"""
def do_GET(self):
'''Returns an error because GET is useless for us'''
self.send_response(403)
def do_POST(self):
'''Handles POST requests for all hooks.'''
# read and decode data
length = int(self.headers['Content-Length'])
indata = self.rfile.read(length)
data = json.loads(indata.decode('utf-8'))
# handle GitHub triggers only
if 'GitHub' in self.headers['User-Agent']:
event = self.headers['X-Github-Event']
email, name = data['pusher']['email'], data['pusher']['name']
repo = data['repository']['name']
dir_ = DIRECTORY_MAP[repo][0]
print("{}: {} committed to {}".format(name, email, repo))
if event == 'push':
git_pull(repo)
new_sha = get_sha(repo)
gh_msg = data['head_commit']['message']
commit_message = COMMIT_MESSAGE.format(
gh_msg, new_sha[:7], name, email)
svn_push(repo, commit_message)
self.send_response(200)
if __name__ == '__main__':
if len(sys.argv) >= 2:
cmd = sys.argv[1]
if cmd.lower() == 'clean':
print('Do you want to delete all local repository copies? Y/n')
confirm = input()
if not confirm or confirm[:1].lower() == 'y':
for (repo_name, value) in DIRECTORY_MAP.items():
print('Deleting ', repo_name)
dir_ = os.path.join(os.getcwd(),
DIRECTORY_MAP[repo_name][0])
do_command('rm -rf '+dir_)
print('Done, exiting.')
sys.exit(0)
print('Pulling all repositories')
print('-'*80)
for (repo_name, value) in DIRECTORY_MAP.items():
print('Pulling: '+repo_name)
dir_ = os.path.join(os.getcwd(), DIRECTORY_MAP[repo_name][0])
commit_message = INITIAL_COMMIT_MESSAGE
if not os.path.isdir(dir_):
print('Could not find repo '+repo_name+', now cloning.')
svn_clone(repo_name)
else:
pre_sha = get_sha(repo_name)
git_pull(repo_name)
post_sha = get_sha(repo_name)
commit_message = UPDATE_COMMIT_MESSAGE.format(repo_name, pre_sha, post_sha)
svn_push(repo_name, commit_message)
print('-'*80)
httpd = socketserver.TCPServer(("", PORT), SyncHandler)
print("Serving on {}".format(PORT))
try:
httpd.serve_forever()
except KeyboardInterrupt:
print()
print('Exiting')