rpm-ch: make it possible to commit/tag the changes
authorMarkus Lehtonen <markus.lehtonen@linux.intel.com>
Thu, 6 Feb 2014 09:35:42 +0000 (11:35 +0200)
committerMarkus Lehtonen <markus.lehtonen@linux.intel.com>
Fri, 14 Nov 2014 12:47:19 +0000 (14:47 +0200)
Implement '--tag' command line option (and other related options for
signing) for creating and tagging a release. These correspond the
tagging options in git-buildpackage-rpm.

The git-buildpackage-rpm tool does not commit anything to git. However,
in rpm-ch the '--tag' option causes the changelog modifications (and,
all other staged changes) to be committed to git before creating the
tag. This makes it possible to create a release and document the
packaging/release tag name in the rpm changelog.

Signed-off-by: Markus Lehtonen <markus.lehtonen@linux.intel.com>
gbp/scripts/rpm_ch.py

index 7f66f62..6206e88 100755 (executable)
@@ -30,10 +30,13 @@ import gbp.command_wrappers as gbpc
 import gbp.log
 from gbp.config import GbpOptionParserRpm, GbpOptionGroup
 from gbp.errors import GbpError
+from gbp.git.modifier import GitModifier
 from gbp.rpm import guess_spec, NoSpecError, SpecFile
 from gbp.rpm.changelog import Changelog, ChangelogParser, ChangelogError
 from gbp.rpm.git import GitRepositoryError, RpmGitRepository
 from gbp.rpm.policy import RpmPkgPolicy
+from gbp.scripts.buildpackage_rpm import packaging_tag_name
+from gbp.scripts.buildpackage_rpm import create_packaging_tag
 
 
 ChangelogEntryFormatter = RpmPkgPolicy.ChangelogEntryFormatter
@@ -118,14 +121,27 @@ def determine_editor(options):
         return 'vi'
 
 
-def check_branch(repo, options):
-    """Check the current git branch"""
+def check_repo_state(repo, options):
+    """Check that the repository is in good state"""
+    # Check branch
     branch = repo.get_branch()
     if options.packaging_branch != branch and not options.ignore_branch:
         gbp.log.err("You are not on branch '%s' but on '%s'" %
                     (options.packaging_branch, branch))
         raise GbpError("Use --ignore-branch to ignore or "
                        "--packaging-branch to set the branch name.")
+    # Check unstaged changes
+    if options.tag:
+        unstaged = []
+        status = repo.status()
+        for group, files in status.iteritems():
+            if group != '??' and group[1] != ' ':
+                unstaged.extend(files)
+        if unstaged:
+            gbp.log.error("Unstaged changes in:\n    %s" %
+                          '\n    '.join(unstaged))
+            raise GbpError("Please commit or stage your changes before using "
+                           "the --tag option")
 
 
 def parse_spec_file(repo, options):
@@ -275,8 +291,17 @@ def update_changelog(changelog, entries, repo, spec, options):
     name, email = get_author(repo, options.git_author)
     rev_str_fields = dict(spec.version,
                 version=RpmPkgPolicy.compose_full_version(spec.version),
-                vendor=options.vendor,
-                tagname=repo.describe('HEAD', longfmt=True, always=True))
+                vendor=options.vendor)
+    if options.tag:
+        # Get fake information for the to-be-created git commit
+        commit_info = {'author': GitModifier(date=now),
+                       'committer': GitModifier(date=now)}
+        tag = packaging_tag_name(repo, spec, commit_info, options)
+    else:
+        commit_info = {'author': None, 'committer': None}
+        tag = repo.describe('HEAD', longfmt=True, always=True)
+    rev_str_fields['tagname'] = tag
+
     try:
         revision = options.changelog_revision % rev_str_fields
     except KeyError as err:
@@ -296,6 +321,13 @@ def update_changelog(changelog, entries, repo, spec, options):
     # Add new entries to the topmost section
     for entry in entries:
         top_section.append_entry(entry)
+    return (tag, commit_info['author'], commit_info['committer'])
+
+def commit_changelog(repo, changelog, author, committer, edit):
+    """Commit changelog and create a packaging/release tag"""
+    repo.add_files(changelog.path)
+    repo.commit_staged("Update changelog", author_info=author,
+                        committer_info=committer, edit=edit)
 
 
 def parse_args(argv):
@@ -313,9 +345,12 @@ def parse_args(argv):
                     "how to format the changelog entries")
     naming_grp = GbpOptionGroup(parser, "naming",
                     "branch names, tag formats, directory and file naming")
+    commit_grp = GbpOptionGroup(parser, "commit",
+                    "automatic committing and tagging")
     parser.add_option_group(range_grp)
     parser.add_option_group(format_grp)
     parser.add_option_group(naming_grp)
+    parser.add_option_group(commit_grp)
 
     # Non-grouped options
     parser.add_option("-v", "--verbose", action="store_true", dest="verbose",
@@ -333,9 +368,8 @@ def parse_args(argv):
     parser.add_config_file_option(option_name="customizations",
                     dest="customization_file",
                     help="Load Python code from CUSTOMIZATION_FILE. At the "
-                    "moment, the only useful thing the code can do is define a "
-                    "custom ChangelogEntryFormatter class.")
-
+                         "moment, the only useful thing the code can do is "
+                         "define a custom ChangelogEntryFormatter class.")
     # Naming group options
     naming_grp.add_config_file_option(option_name="packaging-branch",
                     dest="packaging_branch")
@@ -370,6 +404,15 @@ def parse_args(argv):
                     dest="spawn_editor")
     format_grp.add_config_file_option(option_name="editor-cmd",
                     dest="editor_cmd")
+    # Commit/tag group options
+    commit_grp.add_option("--tag", action="store_true",
+                    help="commit the changes and create a packaging/release"
+                         "tag")
+    commit_grp.add_option("--retag", action="store_true",
+                    help="Overwrite packaging tag if it already exists")
+    commit_grp.add_boolean_config_file_option(option_name="sign-tags",
+                    dest="sign_tags")
+    commit_grp.add_config_file_option(option_name="keyid", dest="keyid")
 
     options, args = parser.parse_args(argv[1:])
     if not options.changelog_revision:
@@ -390,7 +433,7 @@ def main(argv):
         editor_cmd = determine_editor(options)
 
         repo = RpmGitRepository('.')
-        check_branch(repo, options)
+        check_repo_state(repo, options)
 
         # Find and parse spec file
         spec = parse_spec_file(repo, options)
@@ -411,14 +454,21 @@ def main(argv):
         # Do the actual update
         entries = entries_from_commits(ch_file.changelog, repo, commits,
                                        options)
-        update_changelog(ch_file.changelog, entries, repo, spec, options)
-
+        tag, author, committer = update_changelog(ch_file.changelog, entries,
+                                                  repo, spec, options)
         # Write to file
         ch_file.write()
 
         if editor_cmd:
             gbpc.Command(editor_cmd, [ch_file.path])()
 
+        if options.tag:
+            if options.retag and repo.has_tag(tag):
+                repo.delete_tag(tag)
+            edit = True if editor_cmd else False
+            commit_changelog(repo, ch_file, author, committer, edit)
+            create_packaging_tag(repo, tag, 'HEAD', spec.version, options)
+
     except (GbpError, GitRepositoryError, ChangelogError, NoSpecError) as err:
         if len(err.__str__()):
             gbp.log.err(err)