summaryrefslogtreecommitdiffstats
path: root/rpmci/lame_vcs_abstraction.py
blob: a96f23d087897dcdd02c03e1c0ee7998951e15f6 (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
#!/usr/bin/python
# lame_vcs_abstraction.py:
#
# Licensed under the new-BSD license (http://www.opensource.org/licenses/bsd-license.php)
# Copyright (C) 2010 Red Hat, Inc.
# Written by Colin Walters <walters@verbum.org>

# Feel free to replace the bits here with something better...

import os
import sys
import re
import urlparse
import getopt
import subprocess
import shutil
import hashlib
import logging

from . import async_subprocess

class Vcs(object):
    def __init__(self, parsedurl, directory=None):
        self._parsed_url = parsedurl
        # Deliberately drop params/query
        self._nonfragment_url_string = urlparse.urlunparse((parsedurl.scheme,
                                                            parsedurl.netloc,
                                                            parsedurl.path,
                                                            '', '', ''))
        self._branch = self._parsed_url.fragment

        self._dir = directory
        
    def get_url(self):
        return self._parsed_url
        
    def get_base_url_string(self):
        return self._nonfragment_url_string

    def checkout_async(self, destdir):
        """Retrieve a new copy of the source tree, saving as destdir"""
        raise Exception("not implemented")

    def set_directory(self, dirpath):
        """Set the checkout directory."""
        self._dir = dirpath

    def get_directory(self):
        return self._dir
        
    def update_async(self):
        """Update directory from the latest upstream"""
        raise Exception("not implemented")

    def export_archive(self, prefix, target_filename):
        """Export a tarball with minimal (or no) version control data."""
        raise Exception("not implemented")
        
    def get_scheme(self):
        return self._parsed_url.scheme
        
    def get_id(self):
        raise Exception("not implemented")
        
    def get_abbreviated_id(self):
        raise Exception("not implemented")
        
    def _vcs_exec_sync_log_error(self, args):
        logging.info("Synchronously executing: %r in cwd %r" % (args, self._dir))
        return subprocess.check_output(args, stderr=subprocess.STDOUT, close_fds=True, cwd=self._dir)

    def _vcs_exec_async(self, args, logfile_path, on_exited):
        logging.info("Asynchronously executing: %r in cwd %r" % (args, self._dir))
        return async_subprocess.spawn_async_output_to_file(args, logfile_path, on_exited, cwd=self._dir)
        
    @classmethod
    def new_from_spec(cls, spec):
        """See http://maven.apache.org/scm/scm-url-format.html ; we use this format,
        but without the "scm:" prefix."""
        # Hack for backwards compatibility
        if spec.startswith('git://'):
            (vcstype, url) = ('git', spec)
        else:
            (vcstype, url) = spec.split(':', 1)
        orig = urlparse.urlsplit(url)
        # We want to support fragments, even if the URL type isn't recognized.  So change the
        # scheme to http temporarily.
        temp = urlparse.urlunsplit(('http', orig.netloc, orig.path, orig.query, orig.fragment))
        new = urlparse.urlsplit(temp)
        combined = urlparse.SplitResult(orig.scheme, new.netloc, new.path, new.query, new.fragment)
        if vcstype == 'git':
            return GitVcs(combined)
        
class GitVcs(Vcs):
    vcstype = "git"

    def checkout_async(self, destdir, logfile, on_exited):
        assert self._dir is None
        args = ['git', 'clone']
        if self._branch:
            args.extend(['-b', self._branch])
        args.extend([self._nonfragment_url_string, destdir])
        def _wrap_on_exited(process, condition):
            if condition == 0:
                self._dir = destdir
            on_exited(process, condition)
        return self._vcs_exec_async(args, logfile, _wrap_on_exited)
        
    def update_async(self, logfile, on_exited):
        assert self._dir is not None
        return self._vcs_exec_async(['git', 'pull', '-r'], logfile, on_exited)

    def export_archive(self, prefix, target_filename, logfile):
        if not prefix.endswith('/'):
            prefix += '/'
        args = ['git', 'archive', '--format=tar', '--prefix=%s' % (prefix,), 'HEAD']
        logging.info("Synchronously executing: %r" % (args, ))
        log_f = open(logfile, 'w')
        gitproc = subprocess.Popen(args, cwd=src_directory, stdout=subprocess.PIPE, stderr=log_f)
        if target_filename.endswith('.bz2'):
            zipbin = 'bzip2'
        elif target_filename.endswith('.gz'):
            zipbin = 'gzip'
        else:
            raise ValueError("Unknown compression for filename %r" % (target_filename,))
        args = [zipbin, '-c']
        logging.info("Synchronously executing: %r" % (args, ))
        f = open(target_filename, 'w')
        zipproc = subprocess.Popen(args, cwd=src_directory, stdout=f, stdin=gitproc.stdout, stderr=log_f)
        zipproc.wait()
        
    def get_commit_as_patch(self, commitid, destfile):
        output = self._vcs_exec_sync_log_error(['git', 'format-patch', '--stdout', commitid + '^..' + commitid])
        f = open(destfile, 'w')
        f.write(output)
        f.close()   
        
    def get_id(self):
        output = self._vcs_exec_sync_log_error(['git', 'show', '--format=%H'])
        return output.split('\n')[0]
        
    def get_abbreviated_id(self):
        full_id = self.get_id()
        return full_id[0:8]
        
    def get_commit_summary_as_filename(self, commitid):
        output = self._vcs_exec_sync_log_error(['git', 'show', '--format=%f', commitid])
        return output.split('\n')[0]