#!/usr/bin/python -u # vim: set fileencoding=utf-8 : # # (C) 2006,2007 Guido Guenther # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """Import a Debian source package into a git repository""" import ConfigParser import sys import re import os import tempfile import glob import pipes from email.Utils import parseaddr import gbp.command_wrappers as gbpc from gbp.deb import (debian_version_chars, parse_changelog, unpack_orig, parse_dsc, DscFile, tar_toplevel) from gbp.git import (build_tag, create_repo, GitRepository, GitRepositoryError, rfc822_date_to_git) from gbp.config import GbpOptionParser, GbpOptionGroup from gbp.errors import GbpError import gbp.log class SkipImport(Exception): pass def download_source(pkg, dirs): if re.match(r'[a-z]{1,5}://', pkg): mode='dget' else: mode='apt-get' dirs['download'] = os.path.abspath(tempfile.mkdtemp()) gbp.log.info("Downloading '%s' using '%s'..." % (pkg, mode)) if mode == 'apt-get': gbpc.RunAtCommand('apt-get', ['-qq', '--download-only', 'source', pkg], shell=False)(dir=dirs['download']) else: gbpc.RunAtCommand('dget', ['-q', '--download-only', pkg], shell=False)(dir=dirs['download']) dsc = glob.glob(os.path.join(dirs['download'], '*.dsc'))[0] return dsc def apply_patch(diff, unpack_dir): "Apply patch to a source tree" d = os.path.abspath(unpack_dir) pipe = pipes.Template() pipe.prepend('gunzip -c %s' % diff, '.-') pipe.append('patch -p1 --quiet', '-.') try: ret = pipe.copy('', '') if ret: gbp.log.err("Error import %s: %d" % (diff, ret)) return False except OSError, err: gbp.log.err("Error importing %s: %s" % (diff, err[0])) return False return True def apply_deb_tgz(deb_tgz, unpack_dir): """Apply .debian.tar.gz (V3 source format)""" unpackArchive = gbpc.UnpackTarArchive(deb_tgz, ".")() return True def apply_debian_patch(repo, unpack_dir, src, options, parents): """apply the debian patch and tag appropriately""" version = "%s-%s" % (src.upstream_version, src.debian_version) gitTag = gbpc.GitTag(options.sign_tags, options.keyid) try: os.chdir(unpack_dir) if src.diff and not apply_patch(src.diff, unpack_dir): raise GbpError if src.deb_tgz and not apply_deb_tgz(src.deb_tgz, unpack_dir): raise GbpError if os.path.exists('debian/rules'): os.chmod('debian/rules', 0755) os.chdir(repo.path) dch = parse_changelog(filename=os.path.join(unpack_dir, 'debian/changelog')) date= rfc822_date_to_git(dch['Date']) author, email = parseaddr(dch['Maintainer']) if not (author and email): gbp.log.warn("Failed to parse maintainer") commit = repo.commit_dir(unpack_dir, "Imported Debian patch %s" % version, branch = options.debian_branch, other_parents = parents, author = author, email = email, date = date) gitTag(build_tag(options.debian_tag, version), msg="Debian release %s" % version, commit=commit) except gbpc.CommandExecFailed: gbp.log.err("Failed to import Debian package") raise GbpError finally: os.chdir(repo.path) def print_dsc(dsc): if dsc.native: gbp.log.debug("Debian Native Package") gbp.log.debug("Version:", dsc.upstream_version) gbp.log.debug("Debian tarball:", dsc.tgz) else: gbp.log.debug("Upstream version:", dsc.upstream_version) gbp.log.debug("Debian version:", dsc.debian_version) gbp.log.debug("Upstream tarball:", dsc.tgz) gbp.log.debug("Debian diff:", dsc.diff) if dsc.epoch: gbp.log.debug("Epoch: %s" % dsc.epoch) def main(argv): dirs = {'top': os.path.abspath(os.curdir)} needs_repo = False ret = 0 skipped = False parents = None try: parser = GbpOptionParser(command=os.path.basename(argv[0]), prefix='', usage='%prog [options] /path/to/package.dsc') except ConfigParser.ParsingError, err: gbp.log.err(err) return 1 import_group = GbpOptionGroup(parser, "import options", "pristine-tar and filtering") tag_group = GbpOptionGroup(parser, "tag options", "options related to git tag creation") branch_group = GbpOptionGroup(parser, "version and branch naming options", "version number and branch layout options") for group in [import_group, branch_group, tag_group ]: parser.add_option_group(group) parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, help="verbose command execution") parser.add_config_file_option(option_name="color", dest="color") parser.add_option("--download", action="store_true", dest="download", default=False, help="download source package") branch_group.add_config_file_option(option_name="debian-branch", dest="debian_branch") branch_group.add_config_file_option(option_name="upstream-branch", dest="upstream_branch") tag_group.add_boolean_config_file_option(option_name="sign-tags", dest="sign_tags") tag_group.add_config_file_option(option_name="keyid", dest="keyid") tag_group.add_config_file_option(option_name="debian-tag", dest="debian_tag") tag_group.add_config_file_option(option_name="upstream-tag", dest="upstream_tag") import_group.add_config_file_option(option_name="filter", dest="filters", action="append") import_group.add_boolean_config_file_option(option_name="pristine-tar", dest="pristine_tar") import_group.add_option("--ignore-same-version", action="store_true", dest="ignore_same_version", default=False, help="don't import already imported version") (options, args) = parser.parse_args(argv[1:]) gbp.log.setup(options.color, options.verbose) gitTag = gbpc.GitTag(options.sign_tags, options.keyid) try: if len(args) != 1: parser.print_help() raise GbpError else: pkg = args[0] if options.download: dsc = download_source(pkg, dirs=dirs) else: dsc = pkg src = parse_dsc(dsc) if src.pkgformat not in [ '1.0', '3.0' ]: raise GbpError, "Importing %s source format not yet supported." % src.pkgformat if options.verbose: print_dsc(src) try: repo = GitRepository('.') is_empty = repo.is_empty() (clean, out) = repo.is_clean() if not clean and not is_empty: gbp.log.err("Repository has uncommitted changes, commit these first: ") raise GbpError, out except GitRepositoryError: # no repo found, create one needs_repo = True is_empty = True if needs_repo: gbp.log.info("No git repository found, creating one.") repo = create_repo(src.pkg) os.chdir(repo.path) dirs['tmp'] = os.path.abspath(tempfile.mkdtemp(dir='..')) unpack_dir = unpack_orig(src.tgz, dirs['tmp'], options.filters) unpack_dir = tar_toplevel(unpack_dir) format = [(options.upstream_tag, "Upstream"), (options.debian_tag, "Debian")][src.native] tag = build_tag(format[0], src.upstream_version) msg = "%s version %s" % (format[1], src.upstream_version) if not options.ignore_same_version: if repo.find_version(options.debian_tag, src.version): gbp.log.info("Version %s already imported." % src.version) raise SkipImport if not repo.find_version(format[0], src.upstream_version): gbp.log.info("Tag %s not found, importing %s tarball" % (tag, format[1])) if is_empty: branch = None else: branch = [options.upstream_branch, options.debian_branch][src.native] commit = repo.commit_dir(unpack_dir, "Imported %s" % msg, branch) gitTag(version=tag, msg=msg, commit=commit) if not src.native: if is_empty: gbpc.GitBranch()(options.upstream_branch, commit) if options.pristine_tar: gbpc.PristineTar().commit(src.tgz, 'refs/heads/%s' % options.upstream_branch) parents = [ options.upstream_branch ] if not src.native: if is_empty and not repo.has_branch(options.debian_branch): gbpc.GitBranch()(options.debian_branch, commit) if src.diff or src.deb_tgz: apply_debian_patch(repo, unpack_dir, src, options, parents) else: gbp.log.warn("Didn't find a diff to apply.") if repo.get_branch() == options.debian_branch: # Update HEAD if we modified the checkout out branch repo.force_head(options.debian_branch, hard=True) except KeyboardInterrupt: ret = 1 gbp.log.err("Interrupted. Aborting.") except gbpc.CommandExecFailed: ret = 1 except GitRepositoryError, msg: gbp.log.err("Git command failed: %s" % msg) ret = 1 except GbpError, err: if len(err.__str__()): gbp.log.err(err) ret = 1 except SkipImport: skipped = True finally: os.chdir(dirs['top']) for d in [ 'tmp', 'download' ]: if dirs.has_key(d): gbpc.RemoveTree(dirs[d])() if not ret and not skipped: gbp.log.info("Everything imported under '%s'" % src.pkg) return ret if __name__ == '__main__': sys.exit(main(sys.argv)) # vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·: