Introduce git-import-srpm tool
authorMarkus Lehtonen <markus.lehtonen@linux.intel.com>
Thu, 12 Jan 2012 13:37:49 +0000 (15:37 +0200)
committerMarkus Lehtonen <markus.lehtonen@linux.intel.com>
Tue, 8 May 2012 08:39:40 +0000 (11:39 +0300)
Initial version of the git-import-srpm: a tool for importing source
rpms.

Unit tests: created a separate method in TestHelp for RPM-specific
commands.

bin/git-import-srpm [new file with mode: 0755]
gbp/scripts/import_srpm.py [new file with mode: 0755]
setup.py
tests/01_test_help.py

diff --git a/bin/git-import-srpm b/bin/git-import-srpm
new file mode 100755 (executable)
index 0000000..721741b
--- /dev/null
@@ -0,0 +1,5 @@
+#! /usr/bin/python -u
+import sys
+from gbp.scripts.import_srpm import main
+
+sys.exit(main(sys.argv))
diff --git a/gbp/scripts/import_srpm.py b/gbp/scripts/import_srpm.py
new file mode 100755 (executable)
index 0000000..0775463
--- /dev/null
@@ -0,0 +1,340 @@
+# vim: set fileencoding=utf-8 :
+#
+# (C) 2006,2007,2011 Guido Guenther <agx@sigxcpu.org>
+# (C) 2012 Intel Corporation <markus.lehtonen@linux.intel.com>
+#    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 an RPM source package into a git repository"""
+
+import ConfigParser
+import sys
+import re
+import os
+import tempfile
+import glob
+import pipes
+import time
+import shutil
+import errno
+from email.Utils import parseaddr
+import gbp.command_wrappers as gbpc
+from gbp.pkg import UpstreamSource
+from gbp.rpm import (parse_srpm, SrcRpmFile, SpecFile, guess_spec, NoSpecError)
+from gbp.rpm.git import (RpmGitRepository, GitRepositoryError)
+from gbp.git import rfc822_date_to_git
+from gbp.config import GbpOptionParserRpm, GbpOptionGroup, no_upstream_branch_msg
+from gbp.errors import GbpError
+import gbp.log
+
+no_packaging_branch_msg = """
+Repository does not have branch '%s' for packaging/distribution sources. If there is none see
+file:///usr/share/doc/git-buildpackage/manual-html/gbp.import.html#GBP.IMPORT.CONVERT
+on howto create it otherwise use --packaging-branch to specify it.
+"""
+
+
+class SkipImport(Exception):
+    pass
+
+
+def download_source(pkg, dirs):
+    if re.match(r'[a-z]{1,5}://', pkg):
+        mode='wget'
+    else:
+        mode='yumdownloader'
+
+    dirs['download'] = os.path.abspath(tempfile.mkdtemp())
+    gbp.log.info("Downloading '%s' using '%s'..." % (pkg, mode))
+    if mode == 'yumdownloader':
+        gbpc.RunAtCommand('yumdownloader',
+                          ['--source', '--destdir=', '.', pkg],
+                          shell=False)(dir=dirs['download'])
+    else:
+        gbpc.RunAtCommand('wget',
+                          [pkg],
+                          shell=False)(dir=dirs['download'])
+    srpm = glob.glob(os.path.join(dirs['download'], '*.src.rpm'))[0]
+    return srpm
+
+
+def move_tag_stamp(repo, format, version):
+    "Move tag out of the way appending the current timestamp"
+    old = repo.version_to_tag(format, version)
+    timestamped = "%s~%s" % (version, int(time.time()))
+    new = repo.version_to_tag(format, timestamped)
+    repo.move_tag(old, new)
+
+
+def set_bare_repo_options(options):
+    """Modify options for import into a bare repository"""
+    if options.pristine_tar:
+        gbp.log.info("Bare repository: setting %s option"
+                      % (["", " '--no-pristine-tar'"][options.pristine_tar], ))
+        options.pristine_tar = False
+
+
+def parse_args(argv):
+    try:
+        parser = GbpOptionParserRpm(command=os.path.basename(argv[0]), prefix='',
+                                    usage='%prog [options] /path/to/package.src.rpm')
+    except ConfigParser.ParsingError, err:
+        gbp.log.err(err)
+        return None, None
+
+    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", type='tristate')
+    parser.add_option("--download", action="store_true", dest="download", default=False,
+                      help="download source package")
+    branch_group.add_config_file_option(option_name="packaging-branch",
+                      dest="packaging_branch")
+    branch_group.add_config_file_option(option_name="upstream-branch",
+                      dest="upstream_branch")
+    branch_group.add_boolean_config_file_option(option_name="create-missing-branches",
+                      dest="create_missing_branches")
+    branch_group.add_option("--orphan-packaging", action="store_true",
+                      dest="orphan_packaging", default=False,
+                      help="The packaging branch doesn't base on upstream")
+    branch_group.add_option("--native", action="store_true",
+                      dest="native", default=False,
+                      help="This is a dist native package, no separate 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="packaging-tag",
+                      dest="packaging_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("--allow-same-version", action="store_true",
+                      dest="allow_same_version", default=False,
+                      help="allow to import already imported version")
+    import_group.add_config_file_option(option_name="packaging-dir",
+                      dest="packaging_dir")
+    (options, args) = parser.parse_args(argv[1:])
+    gbp.log.setup(options.color, options.verbose)
+    return options, args
+
+
+def main(argv):
+    dirs = dict(top=os.path.abspath(os.curdir))
+    needs_repo = False
+    ret = 0
+    skipped = False
+    parents = None
+
+    options, args = parse_args(argv)
+
+    try:
+        if len(args) != 1:
+            gbp.log.err("Need to give exactly one package to import. Try --help.")
+            raise GbpError
+        else:
+            pkg = args[0]
+            if options.download:
+                srpm = download_source(pkg, dirs=dirs)
+            else:
+                srpm = pkg
+
+            src = parse_srpm(srpm)
+            if options.verbose:
+                src.debugprint()
+
+            try:
+                repo = RpmGitRepository('.')
+                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 = RpmGitRepository.create(src.pkg)
+                os.chdir(repo.path)
+
+            if repo.bare:
+                set_bare_repo_options(options)
+
+            dirs['pkgextract'] = os.path.abspath(tempfile.mkdtemp(dir='..'))
+            dirs['srctarball'] = os.path.abspath(tempfile.mkdtemp(dir='..'))
+            dirs['srcunpack'] = os.path.abspath(tempfile.mkdtemp(dir='..'))
+            gbp.log.info("Extracting src rpm...")
+            src.unpack(dirs['pkgextract'],dirs['srctarball'])
+            if src.orig_file:
+                orig_tarball = os.path.join(dirs['srctarball'], src.orig_file)
+                upstream = UpstreamSource(orig_tarball)
+                upstream.unpack(dirs['srcunpack'], options.filters)
+            else:
+                upstream = None
+
+            format = [(options.upstream_tag, "Upstream"), (options.packaging_tag, "Distribution")][options.native]
+            tag = repo.version_to_tag(format[0], src.upstream_version)
+
+            if repo.find_version(options.packaging_tag, src.version):
+                 gbp.log.warn("Version %s already imported." % src.version)
+                 if options.allow_same_version:
+                    gbp.log.info("Moving tag of version '%s' since import forced" % src.version)
+                    move_tag_stamp(repo, options.packaging_tag, src.version)
+                 else:
+                    raise SkipImport
+
+            if is_empty:
+                options.create_missing_branches = True
+
+            # Import upstream sources
+            if upstream:
+                upstream_commit = repo.find_version(format[0], src.upstream_version)
+                if not upstream_commit:
+                    gbp.log.info("Tag %s not found, importing %s tarball" % (tag, format[1]))
+
+                    branch = [options.upstream_branch,
+                              options.packaging_branch][options.native]
+                    if not repo.has_branch(branch):
+                        if options.create_missing_branches:
+                            gbp.log.info("Will create missing branch '%s'" % branch)
+                        else:
+                            gbp.log.err(no_upstream_branch_msg % branch +
+                                "\nAlso check the --create-missing-branches option.")
+                            raise GbpError
+
+                    msg = "%s version %s" % (format[1], src.upstream_version)
+                    upstream_commit = repo.commit_dir(upstream.unpacked,
+                                                      "Imported %s" % msg,
+                                                       branch,create_missing_branch=options.create_missing_branches)
+                    repo.create_tag(name=tag,
+                                    msg=msg,
+                                    commit=upstream_commit,
+                                    sign=options.sign_tags,
+                                    keyid=options.keyid)
+
+                    if not options.native:
+                        if options.pristine_tar:
+                            repo.pristine_tar.commit(orig_tarball, 'refs/heads/%s' % options.upstream_branch)
+                        parents = [ options.upstream_branch ]
+            else:
+                gbp.log.info("No source tarball imported")
+
+            if not options.native or not upstream:
+                # Import packaging files
+                gbp.log.info("Importing packaging files...")
+                branch = options.packaging_branch
+                if not repo.has_branch(branch):
+                    if options.create_missing_branches:
+                        gbp.log.info("Will create missing branch '%s'" % branch)
+                    else:
+                        gbp.log.err(no_packaging_branch_msg % branch +
+                                    "\nAlso check the --create-missing-branches option.")
+                        raise GbpError
+
+                tag = repo.version_to_tag(options.packaging_tag, src.version)
+                msg = "Distribution release %s" % src.version
+
+                if options.orphan_packaging or not upstream:
+                    parents = []
+                    commit = repo.commit_dir(dirs['pkgextract'],
+                                                 "Imported %s" % msg,
+                                                 branch,
+                                                 create_missing_branch=options.create_missing_branches)
+                else:
+                    # Copy packaging files to the unpacked sources dir
+                    try:
+                        pkgsubdir = os.path.join(upstream.unpacked, options.packaging_dir)
+                        os.mkdir(pkgsubdir)
+                    except OSError, (e, emsg):
+                        if e == errno.EEXIST:
+                            pass
+                        else:
+                            raise
+                    for f in glob.glob(dirs['pkgextract']+"/*"):
+                        shutil.copy2(f, pkgsubdir)
+                    commit = repo.commit_dir(upstream.unpacked,
+                                                 "Imported %s" % msg,
+                                                 branch, other_parents=[upstream_commit],
+                                                 create_missing_branch=options.create_missing_branches)
+
+                repo.create_tag(name=tag,
+                                msg=msg,
+                                commit=commit,
+                                sign=options.sign_tags,
+                                keyid=options.keyid)
+
+            if repo.get_branch() == options.packaging_branch:
+                # Update HEAD if we modified the checked out branch
+                repo.force_head(options.packaging_branch, hard=True)
+            # Checkout packaging branch
+            repo.set_branch(options.packaging_branch)
+
+            # Insert autoupdate markers to .spec
+            if not options.native:
+                try:
+                    dummy, specfile = guess_spec(options.packaging_dir)
+                    spec = SpecFile(specfile)
+                    if spec.putautoupdatemarkers() != 0:
+                        gbpc.GitCommand('status')(['--', options.packaging_dir])
+                        gbp.log.warn("Auto-added gbp autoupdate markers to spec file. Verifying the changes manually before git commit is recommended.")
+                except NoSpecError, err:
+                    gbp.log.warn("Unable to find .spec file to add autoupdate markers ('%s')" % err)
+
+    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 [ 'pkgextract', 'srctarball', 'srcunpack', 'download' ]:
+        if dirs.has_key(d):
+            gbpc.RemoveTree(dirs[d])()
+
+    if not ret and not skipped:
+        gbp.log.info("Version '%s' imported under '%s'" % (src.version, 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\:·:
index b9342c2a9de639c6e67946d01cc361259ee2034a..e407b472ba0ad1ac0ae02d0ab39ddd04aa0d73e1 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -54,7 +54,8 @@ setup(name = "gbp",
                   'bin/gbp-pull',
                   'bin/gbp-clone',
                   'bin/gbp-create-remote-repo',
-                  'bin/git-pbuilder'],
+                  'bin/git-pbuilder',
+                  'bin/git-import-srpm'],
       packages = find_packages(),
       data_files = [("/etc/git-buildpackage/", ["gbp.conf"]),],
       setup_requires=['nose>=0.11.1', 'coverage>=2.85'],
index 1974c302cc22fc278e799c5f55c7a28222ccfae7..feccbb3bebbef72a14455429a90a285f67f2a1d8 100644 (file)
@@ -19,4 +19,10 @@ class TestHelp(unittest.TestCase):
             ret = os.system("bin/gbp-%s --help >/dev/null" % prog)
             self.assertEqual(ret, 0)
 
+    """Test help output of RPM-specific commands"""
+    def testHelpRpm(self):
+        for prog in [ "git-import-srpm" ]:
+            ret = os.system("bin/%s --help >/dev/null" % prog)
+            self.assertEqual(ret, 0)
+
 # vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·: