forked from pytest-dev/execnet
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsvn-sync-repo.py
More file actions
139 lines (117 loc) · 3.79 KB
/
svn-sync-repo.py
File metadata and controls
139 lines (117 loc) · 3.79 KB
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
#!/usr/bin/env python
"""
small utility for hot-syncing a svn repository through ssh.
uses execnet.
"""
import os
import pathlib
import subprocess
import sys
import execnet
def usage():
arg0 = sys.argv[0]
print(arg0, "[user@]remote-host:/repo/location localrepo [ssh-config-file]")
def main(args):
remote = args[0]
localrepo = pathlib.Path(args[1])
if not localrepo.is_dir():
raise SystemExit(f"localrepo {localrepo} does not exist")
if len(args) == 3:
configfile = args[2]
else:
configfile = None
remote_host, path = remote.split(":", 1)
print("ssh-connecting to", remote_host)
gw = getgateway(remote_host, configfile)
local_rev = get_svn_youngest(localrepo)
# local protocol
# 1. client sends rev/repo -> server
# 2. server checks for newer revisions and sends dumps
# 3. client receives dumps, updates local repo
# 4. client goes back to step 1
c = gw.remote_exec(
"""
import os
import subprocess
import time
remote_rev, repopath = channel.receive()
while True:
rev = subprocess.run(
["svnlook", "youngest", repopath],
check=True,
capture_output=True,
text=True,
).stdout
rev = int(rev)
if rev > remote_rev:
revrange = (remote_rev+1, rev)
dumpchannel = channel.gateway.newchannel()
channel.send(revrange)
channel.send(dumpchannel)
f = os.popen(
"svnadmin dump -q --incremental -r %s:%s %s"
% (revrange[0], revrange[1], repopath), 'r')
try:
maxcount = dumpchannel.receive()
count = maxcount
while 1:
s = f.read(8192)
if not s:
raise EOFError
dumpchannel.send(s)
count = count - 1
if count <= 0:
ack = dumpchannel.receive()
count = maxcount
except EOFError:
dumpchannel.close()
remote_rev = rev
else:
# using svn-hook instead would be nice here
time.sleep(30)
"""
)
c.send((local_rev, path))
print("checking revisions from %d in %s" % (local_rev, remote))
while 1:
revstart, revend = c.receive()
dumpchannel = c.receive()
print("receiving revisions", revstart, "-", revend, "replaying...")
svn_load(localrepo, dumpchannel)
print("current revision", revend)
def svn_load(repo, dumpchannel, maxcount=100):
# every maxcount we will send an ACK to the other
# side in order to synchronise and avoid our side
# growing buffers (execnet does not control
# RAM usage or receive queue sizes)
dumpchannel.send(maxcount)
f = os.popen(f"svnadmin load -q {repo}", "w")
count = maxcount
for x in dumpchannel:
sys.stdout.write(".")
sys.stdout.flush()
f.write(x)
count = count - 1
if count <= 0:
dumpchannel.send(maxcount)
count = maxcount
print()
f.close()
def get_svn_youngest(repo):
rev = subprocess.run(
["svnlook", "youngest", repo],
check=True,
capture_output=True,
text=True,
).stdout
return int(rev)
def getgateway(host, configfile=None):
xspec = "ssh=%s" % host
if configfile is not None:
xspec += "//ssh_config=%s" % configfile
return execnet.makegateway(xspec)
if __name__ == "__main__":
if len(sys.argv) < 3:
usage()
raise SystemExit(1)
main(sys.argv[1:])