Introduce git-import-orig-rpm tool
authorMarkus Lehtonen <markus.lehtonen@linux.intel.com>
Thu, 12 Jan 2012 13:39:55 +0000 (15:39 +0200)
committerMarkus Lehtonen <markus.lehtonen@linux.intel.com>
Fri, 14 Nov 2014 12:22:11 +0000 (14:22 +0200)
Initial version of the git-import-orig-rpm: version of git-import-orig
intended for maintaining rpm packages.

Signed-off-by: Markus Lehtonen <markus.lehtonen@linux.intel.com>
gbp/config.py
gbp/scripts/import_orig_rpm.py [new file with mode: 0755]
tests/01_test_help.py

index a211e8b..62b8dd2 100644 (file)
@@ -608,6 +608,8 @@ class GbpOptionParserRpm(GbpOptionParser):
             'rpmbuild-srpmdir'          : 'SRPMS',
             'rpmbuild-buildrootdir'     : 'BUILDROOT',
             'patch-export'              : 'False',
+            'merge'                     : 'False',
+            'pristine-tarball-name'     : 'auto',
                     })
 
     help = dict(GbpOptionParser.help)
@@ -637,6 +639,9 @@ class GbpOptionParserRpm(GbpOptionParser):
             'patch-export':
                 "Create patches between upstream and export-treeish, default "
                 "is '%(patch-export)s'",
+            'pristine-tarball-name':
+                "Filename to record to pristine-tar, set to 'auto' to not "
+                "mangle the file name, default is '%(pristine-tarball-name)s'",
                  })
 
 # vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·:
diff --git a/gbp/scripts/import_orig_rpm.py b/gbp/scripts/import_orig_rpm.py
new file mode 100755 (executable)
index 0000000..0a99a08
--- /dev/null
@@ -0,0 +1,315 @@
+# vim: set fileencoding=utf-8 :
+#
+# (C) 2006, 2007, 2009, 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 a new upstream version into a git repository"""
+
+import ConfigParser
+import os
+import sys
+import tempfile
+import gbp.command_wrappers as gbpc
+import string
+from gbp.pkg import parse_archive_filename
+from gbp.rpm import (RpmUpstreamSource, SpecFile, NoSpecError, parse_spec,
+                     guess_spec, guess_spec_repo)
+from gbp.rpm.policy import RpmPkgPolicy
+from gbp.rpm.git import (GitRepositoryError, RpmGitRepository)
+from gbp.config import GbpOptionParserRpm, GbpOptionGroup, no_upstream_branch_msg
+from gbp.errors import GbpError
+import gbp.log
+from gbp.scripts.common.import_orig import (cleanup_tmp_tree, ask_package_name,
+                                            ask_package_version,
+                                            prepare_sources)
+
+
+def upstream_import_commit_msg(options, version):
+    return options.import_msg % dict(version=version)
+
+
+def detect_name_and_version(repo, source, options):
+    # Guess defaults for the package name and version from the
+    # original tarball.
+    (guessed_package, guessed_version) = source.guess_version() or ('', '')
+
+    # Try to find the source package name
+    try:
+        spec = parse_spec(guess_spec(os.path.join(repo.path, options.packaging_dir),
+                                     True,
+                                     os.path.basename(repo.path) + '.spec'))
+        sourcepackage = spec.name
+    except NoSpecError:
+        try:
+            # Check the spec file from the repository, in case
+            # we're not on the packaging-branch (but upstream, for
+            # example).
+            specfile = guess_spec_repo(repo, options.packaging_branch, options.packaging_dir)
+            spec = SpecFile(specfile)
+            sourcepackage = spec.name
+        except NoSpecError:
+            if options.interactive:
+                sourcepackage = ask_package_name(guessed_package,
+                                                 RpmPkgPolicy.is_valid_packagename,
+                                                 RpmPkgPolicy.packagename_msg)
+            else:
+                if guessed_package:
+                    sourcepackage = guessed_package
+                else:
+                    raise GbpError, "Couldn't determine upstream package name. Use --interactive."
+
+    # Try to find the version.
+    if options.version:
+        version = options.version
+    else:
+        if options.interactive:
+            version = ask_package_version(guessed_version,
+                                          RpmPkgPolicy.is_valid_upstreamversion,
+                                          RpmPkgPolicy.upstreamversion_msg)
+        else:
+            if guessed_version:
+                version = guessed_version
+            else:
+                raise GbpError, "Couldn't determine upstream version. Use '-u<version>' or --interactive."
+
+    return (sourcepackage, version)
+
+
+def find_source(options, args):
+    """Find the tarball to import
+    @return: upstream source filename or None if nothing to import
+    @rtype: string
+    @raise GbpError: raised on all detected errors
+    """
+    if len(args) > 1: # source specified
+        raise GbpError, "More than one archive specified. Try --help."
+    elif len(args) == 0:
+        raise GbpError, "No archive to import specified. Try --help."
+    else:
+        return RpmUpstreamSource(args[0])
+
+
+def pristine_tarball_name(source, pkg_name, pkg_version, pristine_name):
+    old_filename = os.path.basename(source.path)
+    base_name, _fmt, _comp = parse_archive_filename(old_filename)
+    if pristine_name != 'auto':
+        ext = string.replace(old_filename, base_name, '', 1)
+        return pristine_name % {'name': pkg_name,
+                                'version': pkg_version,
+                                'upstreamversion': pkg_version,
+                                'filename_base': base_name,
+                                'filename_ext': ext}
+    # Need to repack and mangle filename if the archive is not
+    # pristine-tar-compatible -> we decide to create gz compressed tarball
+    elif not source.is_tarball():
+        return "%s.tar.gz" % base_name
+    return old_filename
+
+
+def set_bare_repo_options(options):
+    """Modify options for import into a bare repository"""
+    if options.pristine_tar or options.merge:
+        gbp.log.info("Bare repository: setting %s%s options"
+                      % (["", " '--no-pristine-tar'"][options.pristine_tar],
+                         ["", " '--no-merge'"][options.merge]))
+        options.pristine_tar = False
+        options.merge = False
+
+
+def parse_args(argv):
+    try:
+        parser = GbpOptionParserRpm(command=os.path.basename(argv[0]),
+                    prefix='',
+                    usage='%prog [options] /path/to/upstream-version.tar.gz')
+    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")
+    cmd_group = GbpOptionGroup(parser, "external command options", "how and when to invoke external commands and hooks")
+
+    for group in [import_group, branch_group, tag_group, cmd_group ]:
+        parser.add_option_group(group)
+
+    branch_group.add_option("-u", "--upstream-version", dest="version",
+                      help="Upstream Version")
+    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_option("--upstream-vcs-tag", dest="vcs_tag",
+                            help="Upstream VCS tag add to the merge commit")
+    branch_group.add_boolean_config_file_option(option_name="merge", dest="merge")
+    branch_group.add_config_file_option(option_name="packaging-dir", dest="packaging_dir")
+
+    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="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_boolean_config_file_option(option_name="filter-pristine-tar",
+                      dest="filter_pristine_tar")
+    import_group.add_config_file_option(option_name="pristine-tarball-name",
+                      dest="pristine_tarball_name")
+    import_group.add_config_file_option(option_name="import-msg",
+                      dest="import_msg")
+    cmd_group.add_config_file_option(option_name="postimport", dest="postimport")
+
+    parser.add_boolean_config_file_option(option_name="interactive",
+                                          dest='interactive')
+    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')
+
+    (options, args) = parser.parse_args(argv[1:])
+    gbp.log.setup(options.color, options.verbose)
+
+    return options, args
+
+
+def main(argv):
+    ret = 0
+
+    (options, args) = parse_args(argv)
+    if not options:
+        return 1
+
+    tmpdir = tempfile.mkdtemp(dir='../')
+    try:
+        source = find_source(options, args)
+        try:
+            repo = RpmGitRepository('.')
+        except GitRepositoryError:
+            raise GbpError, "%s is not a git repository" % (os.path.abspath('.'))
+
+        # an empty repo has now branches:
+        initial_branch = repo.get_branch()
+        is_empty = False if initial_branch else True
+
+        if not repo.has_branch(options.upstream_branch) and not is_empty:
+            gbp.log.err(no_upstream_branch_msg % options.upstream_branch)
+            raise GbpError
+
+        (sourcepackage, version) = detect_name_and_version(repo, source, options)
+
+        (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
+
+        if repo.bare:
+            set_bare_repo_options(options)
+
+        # Prepare sources for importing
+        if options.pristine_tar:
+            prepare_pristine = pristine_tarball_name(source, sourcepackage,
+                                                  version,
+                                                  options.pristine_tarball_name)
+        else:
+            prepare_pristine = None
+        unpacked_orig, pristine_orig = \
+                prepare_sources(source, sourcepackage, version,
+                                prepare_pristine, options.filters,
+                                options.filter_pristine_tar,
+                                None, tmpdir)
+
+        # Don't mess up our repo with git metadata from an upstream tarball
+        if os.path.isdir(os.path.join(unpacked_orig, '.git/')):
+            raise GbpError("The orig tarball contains .git metadata - "
+                           "giving up.")
+        try:
+            filter_msg = ["", " (filtering out %s)"
+                              % options.filters][len(options.filters) > 0]
+            gbp.log.info("Importing '%s' to branch '%s'%s..." % (source.path,
+                                                                 options.upstream_branch,
+                                                                 filter_msg))
+            gbp.log.info("Source package is %s" % sourcepackage)
+            gbp.log.info("Upstream version is %s" % version)
+
+            msg = upstream_import_commit_msg(options, version)
+
+            if options.vcs_tag:
+                parents = [repo.rev_parse("%s^{}" % options.vcs_tag)]
+            else:
+                parents = None
+
+            commit = repo.commit_dir(unpacked_orig,
+                                     msg=msg,
+                                     branch=options.upstream_branch,
+                                     other_parents=parents,
+                                     create_missing_branch=True,
+                                     )
+            if options.pristine_tar and pristine_orig:
+                gbp.log.info("Pristine-tar: commiting %s" % pristine_orig)
+                repo.pristine_tar.commit(pristine_orig, options.upstream_branch)
+
+            tag = repo.version_to_tag(options.upstream_tag, version)
+            repo.create_tag(name=tag,
+                            msg="Upstream version %s" % version,
+                            commit=commit,
+                            sign=options.sign_tags,
+                            keyid=options.keyid)
+            if options.merge:
+                gbp.log.info("Merging to '%s'" % options.packaging_branch)
+                if repo.has_branch(options.packaging_branch):
+                    repo.set_branch(options.packaging_branch)
+                    try:
+                        repo.merge(tag)
+                    except GitRepositoryError:
+                        raise GbpError, """Merge failed, please resolve."""
+                else:
+                    repo.create_branch(options.packaging_branch, rev=options.upstream_branch)
+                    if repo.get_branch() == options.packaging_branch:
+                        repo.force_head(options.packaging_branch, hard=True)
+                if options.postimport:
+                    info = { 'upstreamversion': version }
+                    env = { 'GBP_BRANCH': options.packaging_branch }
+                    gbpc.Command(options.postimport % info, extra_env=env,
+                                 shell=True)()
+            # Update working copy and index if we've possibly updated the
+            # checked out branch
+            current_branch = repo.get_branch()
+            if (current_branch == options.upstream_branch or
+                current_branch == repo.pristine_tar_branch):
+                repo.force_head(current_branch, hard=True)
+        except (GitRepositoryError, gbpc.CommandExecFailed):
+            raise GbpError, "Import of %s failed" % source.path
+    except GbpError, err:
+        if len(err.__str__()):
+            gbp.log.err(err)
+        ret = 1
+
+    if tmpdir:
+        cleanup_tmp_tree(tmpdir)
+
+    if not ret:
+        gbp.log.info("Successfully imported version %s of %s" % (version, source.path))
+    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 d1e96f2..6a4810b 100644 (file)
@@ -29,7 +29,8 @@ class TestHelp(unittest.TestCase):
     def testHelpRpm(self):
         for script in ['import_srpm',
                        'pq_rpm',
-                       'buildpackage_rpm']:
+                       'buildpackage_rpm',
+                       'import_orig_rpm']:
             module = 'gbp.scripts.%s' % script
             m = __import__(module, globals(), locals(), ['main'], -1)
             self.assertRaises(SystemExit,