aboutsummaryrefslogtreecommitdiff
blob: 2a57651a7b80ce9de20e808984c0af5706e17a89 (plain)
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
# vim: set sw=4 sts=4 et :
# Copyright: 2008 Gentoo Foundation
# Author(s): Nirbheek Chauhan <nirbheek.chauhan@gmail.com>
# License: GPL-2
#
# Immortal lh!
#

import os, subprocess, shutil
import os.path as osp
from .. import config

# init == sync (example: rsync)
ONLY_SYNC = 1
# init and sync are different (eg: git)
INIT_SYNC = 2
# sync == rmtree && init (eg: bzr-export, git with destdir not a repo)
RMTREE_INIT = 3


class Syncer(object):
    """
    Sync stuff
    """

    def __init__(self, scheme='git', uri=config.JOBTAGE_URI, rev=None, destdir=config.JOBTAGE_DIR):
        """
        Default is to sync the jobtage tree from the default location.

        @param destdir: Destination directory; sync *contents* into destdir
        @scheme destdir: string

        @param uri: Uri where to sync from
        @scheme uri: string

        @param rev: SCM Revision to export. Default is latest
                    this variable is only taken into account in
                    the following cases: bzr-export, git-export
        @scheme rev: string

        @param scheme: The URI scheme
        @scheme scheme: string
        """
        self.destdir = destdir
        self.scheme = scheme
        self.command = Command(scheme, uri, rev, destdir)

    def _is_repo(self):
        """
        Broad classification of repos into three types based on their behaviour
         1. Same commands for init & sync (eg: rsync)
            - return ONLY_SYNC
         2. Different init & sync commands (eg: git)
            - Detect if self.destdir is a repo
              * Return INIT_SYNC if it is, RMTREE_INIT if not (rmtree && init)
         3. No concept of sync (eg: bzr-export)
            - Delete self.destdir before 'sync'
              * return RMTREE_INIT (rmtree && init)
        """
        if self.scheme in ['rsync', 'rsync-nc']:
            return ONLY_SYNC
        elif self.scheme in ['git', 'bzr']:
            is_repo_cmd = None
            if self.scheme == 'git':
                is_repo_cmd = 'git rev-parse'
            elif self.scheme == 'bzr':
                is_repo_cmd = 'bzr info'
            else:
                raise Exception('Unknown scm: "%s"' % self.scheme)
            result = subprocess.Popen('cd "%s"; %s' % (self.destdir, is_repo_cmd), shell=True)
            returncode = result.wait()
            if returncode != 0:
                return RMTREE_INIT
            return INIT_SYNC
        elif self.scheme in ['git-export', 'bzr-export']:
            return RMTREE_INIT

    def sync(self):
        """
        Sync self.uri contents to self.destdir 
        """
        if osp.exists(self.destdir):
            if not osp.isdir(self.destdir):
                raise Exception('"%s" exists and is not a directory' % self.destdir)
            result = self._is_repo()
            if result == ONLY_SYNC:
                self.command.run('init')
            elif result == INIT_SYNC:
                self.command.run('sync')
            elif result == RMTREE_INIT:
                shutil.rmtree(self.destdir)
                self.command.run('init')
            else:
                raise Exception('Erm. I did not expect this. DIE DIE DIE.')
        else:
            if not osp.exists(osp.dirname(self.destdir)):
                # Create parents
                os.makedirs(osp.dirname(self.destdir))
            self.command.run('init')

class Command(object):
    """Command to use for each uri scheme and action"""

    def __init__(self, scheme, uri, rev, destdir):
        if not uri.endswith('/'):
            uri += '/'
        if not rev:
            if scheme == 'bzr-export':
                rev = 'last:1'
            elif scheme == 'git-export':
                rev = 'HEAD'
        self.scheme = scheme
        self.uri = uri
        self.rev = rev
        self.destdir = destdir

    def _get_args(self, action='init'):
        if self.scheme == 'bzr': # Sync to latest revision
            if action == 'init':
                return 'bzr branch "%s" "%s"' % (self.uri, self.destdir)
            elif action == 'sync':
                return 'bzr pull --overwrite "%s" -d "%s"' % (self.uri, self.destdir)
        elif self.scheme == 'bzr-export':
            return 'bzr export -r"%s" "%s" "%s"' % (self.rev, self.destdir, self.uri)
        elif self.scheme == 'git': # Sync to latest HEAD
            cmd = 'export GIT_DIR="%s" URI="%s";' % (self.destdir, self.uri)
            if action == 'init':
                return cmd+'git clone --bare "$URI" "$GIT_DIR";git config remote.origin.url "$URI"'
            if action == 'sync':
                return cmd+'git config remote.origin.url "$URI";git fetch -f -u origin master:master'
        elif self.scheme == 'git-export': # export self.rev
            return 'git archive --prefix="%s/" --remote="%s" "%s" | tar x -C "%s"' % \
                    (osp.basename(self.destdir), self.uri, self.rev, osp.dirname(self.destdir))
        elif self.scheme == 'rsync': # rsync, delete
            return 'rsync -a --delete-after "%s" "%s"' % (self.uri, self.destdir)
        elif self.scheme == 'rsync-nc': # rsync, no-clobber
            return 'rsync -a "%s" "%s"' % (self.uri, self.destdir)
        else:
            raise Exception("Unknown scheme: %s" % self.scheme)

    def run(self, action):
        """Run a sync command"""
        args = self._get_args(action=action)
        subprocess.check_call(args, shell=True)

if __name__ == "__main__":
    syncer = Syncer(destdir='/tmp/jobtage')
    syncer.sync()