Commits

holger krekel committed b314967

initial state

  • Participants

Comments (0)

Files changed (5)

+codespeak-svnrepo
+# This is ssh client systemwide configuration file.  This file provides 
+# defaults for users, and the values can be changed in per-user configuration
+# files or on the command line.
+
+# Configuration data is parsed as follows:
+#  1. command line options
+#  2. user-specific file
+#  3. system-wide file
+# Any configuration value is only changed the first time it is set.
+# Thus, host-specific definitions should be at the beginning of the
+# configuration file, and defaults at the end.
+
+# Site-wide defaults for various options
+
+
+Host *
+   ForwardX11 yes
+   RSAAuthentication yes
+   Protocol 2,1 
+   ForwardX11Trusted yes
+   Compression yes 
+   ForwardAgent yes
+   TCPKeepAlive yes
+   ServerAliveInterval 60
+   ServerAliveCountMax 3
+   SendEnv EXECNET_DEBUG 
+
+Host code codespeak
+    HostName codespeak.net
+    User hotsync
+    IdentityFile id_dsa
+#!/usr/bin/env python 
+
+"""
+
+small utility for hot-syncing a svn repository through ssh. 
+uses execnet. 
+
+"""
+
+import py, execnet
+import sys, os
+
+def usage():
+    arg0 = sys.argv[0]
+    print "%s [user@]remote-host:/repo/location localrepo [ssh-config-file]" % (arg0,)
+
+
+def main(args):
+    remote = args[0]
+    localrepo = py.path.local(args[1])
+    if not localrepo.check(dir=1):
+        raise SystemExit("localrepo %s does not exist" %(localrepo,))
+    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 py
+        import os
+        remote_rev, repopath = channel.receive()
+        while 1: 
+            rev = py.process.cmdexec('svnlook youngest "%s"' % repopath) 
+            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
+                py.std.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("svnadmin load -q %s" %(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 >>sys.stdout
+    f.close() 
+
+def get_svn_youngest(repo):
+    rev = py.process.cmdexec('svnlook youngest "%s"' % repo) 
+    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:])
+

sync-codespeak.conf

+# init-file for Ubuntu's upstart service: sync svn repository to here
+#
+# This service maintains a process that logs via ssh to a
+# hotsync@codespeak.net account and transfers repo commits
+
+start on stopped rc RUNLEVEL=[2345]
+stop on runlevel [!2345]
+
+respawn
+exec /root/sync-codespeak/sync.sh 
+#!/bin/bash
+
+cd /root/sync-codespeak
+
+if [ ! -e id_dsa ]; then
+    echo you need to have a id_dsa file that provides authentication
+    echo use 'ssh-keygen -t dsa -f id_dsa' and
+    echo put the resulting id_dsa.pub to hotsync@codespeak.net:.ssh/authorized_keys
+    exit 1
+fi
+
+REPO=codespeak-svnrepo
+if [ ! -d $REPO ]; then
+    echo creating repo at $REPO
+    svnadmin create $REPO
+fi
+
+echo starting synchronisation with codespeak, logging to sync.log 
+python svn-sync-repo.py code:/svn $REPO config >>sync.log 2>&1
+