#!/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 # 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]