<p>Kevin Harwell has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/7144">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Update JIRA issues during the make release process<br><br>Typically when doing a release there are many JIRA issues that need to be<br>updated with the version number in which the fix is going out. This use to<br>be a separate step in the release process. Meaning the mkrelease script<br>was run, and then another script was manually ran to update the JIRA issues.<br><br>Now, however with this patch the JIRA issues are updated during the make<br>release process. The execution of the second script is no longer needed.<br>It also will create the version being released in JIRA if it does not<br>already exist.<br><br>Change-Id: I1be07a4ff67f5733b6f4cc410d47c4fce7b5a1e3<br>---<br>M digium_git.py<br>M digium_jira.py<br>M jira-release-update.py<br>M mkrelease.py<br>M release_summary.py<br>5 files changed, 150 insertions(+), 71 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/repotools refs/changes/44/7144/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/digium_git.py b/digium_git.py<br>index 4a78b7f..3536522 100644<br>--- a/digium_git.py<br>+++ b/digium_git.py<br>@@ -21,6 +21,38 @@<br> LOGGER = logging.getLogger(__name__)<br> <br> <br>+# The one and only Gerrit/Git server.<br>+GERRIT = 'ssh://gerrit.asterisk.org:29418'<br>+<br>+DEFAULT_PROJECT = 'asterisk'<br>+DEFAULT_LOCAL_ROOT = '/tmp'<br>+<br>+<br>+def get_repo(project=None, local_root=None, remote_url=None,<br>+             show_progress=False):<br>+    """Prepare the repo that the release will be made from<br>+<br>+    Keyword Arguments:<br>+    project - The name of the project (default 'asterisk')<br>+    local_root - The local root directory where the repository will be<br>+        checked out under (default '/tmp')<br>+    remote_url - The remote url for the repository (default GERRIT)<br>+    show_progress - False (default) if a progress bar should not be shown<br>+<br>+    Returns:<br>+    A DigiumGitRepo object<br>+    """<br>+    project = project or DEFAULT_PROJECT<br>+    local_root = local_root or DEFAULT_LOCAL_ROOT<br>+    remote_url = remote_url or GERRIT<br>+<br>+    path = os.path.join(local_root, project)<br>+    repo_url = '{0}/{1}'.format(remote_url, project)<br>+<br>+    LOGGER.debug("Cloning from '{0}' to '{1}'".format(repo_url, path))<br>+    return DigiumGitRepo(path, repo_url=repo_url, show_progress=show_progress)<br>+<br>+<br> class GitProgressBar(RemoteProgress):<br>     """A progress bar that maintains the state of a Git operation<br>     """<br>@@ -373,4 +405,3 @@<br> <br>         self._push_tags()<br>         self._push_branch()<br>-<br>diff --git a/digium_jira.py b/digium_jira.py<br>index 560b7c2..b7d26f8 100644<br>--- a/digium_jira.py<br>+++ b/digium_jira.py<br>@@ -5,11 +5,17 @@<br> Russell Bryant <russell@digium.com><br> """<br> <br>+import logging<br> import os<br> import getpass<br> <br> from jira.client import JIRA<br>+from version_parser import AsteriskVersion<br> <br>+<br>+LOGGER = logging.getLogger(__name__)<br>+<br>+DEFAULT_PROJECT = 'asterisk'<br> <br> def _get_jira_auth():<br>     """Get JIRA credentials"""<br>@@ -42,3 +48,88 @@<br>     jira = JIRA(options=jira_options, basic_auth=(jira_user, jira_password))<br> <br>     return jira<br>+<br>+<br>+class DigiumJira(object):<br>+    """A managed Jira client<br>+<br>+    This class wraps up a Jira client and provides some common operations on it<br>+    that are useful for common project operations.<br>+    """<br>+<br>+    def __init__(self, project=DEFAULT_PROJECT, jira=None):<br>+        """Constructor<br>+<br>+        Keyword Arguments:<br>+        project - The name of the project in Jira<br>+        jira - The Jira client. If not specified one is created.<br>+        """<br>+        self._jira = jira or get_jira_client()<br>+        self._project = project<br>+<br>+<br>+class DigiumJiraVersion(DigiumJira):<br>+<br>+    def __init__(self, version, project=DEFAULT_PROJECT, jira=None):<br>+        """Constructor<br>+<br>+        Keyword Arguments:<br>+        version - The version to be used<br>+        project - The name of the project in Jira<br>+        jira - The Jira client. If not specified one is created.<br>+        """<br>+        super(DigiumJiraVersion, self).__init__(project, jira)<br>+<br>+        version = AsteriskVersion.create_from_string(version)<br>+<br>+        if version.prefix == 'certified':<br>+            # Set the apply version method to empty so it becomes a no op<br>+            self.apply_version = lambda x: None<br>+            return<br>+<br>+        self._version = '{0}.{1}.{2}'.format(<br>+            version.major, version.minor, version.patch)<br>+<br>+        self._jira_versions = {}<br>+<br>+        try:<br>+            LOGGER.info("Creating version {0} in project {1}"<br>+                         .format(self._version, self._project))<br>+            self._jira.create_version(version, self._project)<br>+        except:<br>+            LOGGER.debug("Version {0} already exists in project {1}"<br>+                         .format(self._version, self._project))<br>+<br>+    def apply_version(self, issue):<br>+        """Apply the version to the given issue<br>+<br>+        Keyword Arguments:<br>+        issue - The issue to apply the version to<br>+        """<br>+<br>+        # Get the actual version object for the project. This<br>+        # is what must be passed to JIRA to update the fixVersion<br>+        # field for the issue.<br>+        project = issue.fields.project<br>+        if project.name not in self._jira_versions:<br>+            project_versions = self._jira.project_versions(project)<br>+            try:<br>+                version, = [ver.id for ver in project_versions<br>+                            if ver.name == self._version]<br>+            except Exception:<br>+                LOGGER.error("Could not handle versions for {0}"<br>+                             .format(issue.id))<br>+                return<br>+<br>+            self._jira_versions[project.name] = version<br>+<br>+        # Make sure we don't update the fixVersion more than once for<br>+        # a particular issue<br>+        matches = [match for match in issue.fields.fixVersions<br>+                   if match.name == self._version]<br>+        if len(matches) == 0:<br>+            version_array = [{'id': u'{0}'.format(ver.id)} for ver in<br>+                             issue.fields.fixVersions]<br>+            version_array.append({'id': u'{0}'.format(<br>+                self._jira_versions[project.name])})<br>+            issue.update(fields={'fixVersions': version_array})<br>diff --git a/jira-release-update.py b/jira-release-update.py<br>index 6cd62dc..b980638 100755<br>--- a/jira-release-update.py<br>+++ b/jira-release-update.py<br>@@ -12,8 +12,8 @@<br> from progressbar import ProgressBar<br> from optparse import OptionParser<br> <br>-from digium_git import DigiumGitRepo<br>-from digium_jira import get_jira_client<br>+from digium_git import get_repo<br>+from digium_jira import DigiumJiraVersion<br> <br> # The upstream Gerrit repo<br> GERRIT = 'ssh://gerrit.asterisk.org:29418'<br>@@ -31,11 +31,9 @@<br>     print "Update JIRA for %s-%s ..." % \<br>         (options.project, options.version)<br> <br>-    jira = get_jira_client()<br>-<br>-    path = os.path.join(options.local_root, options.project)<br>-    gerrit_repo = '{0}/{1}'.format(GERRIT, options.project)<br>-    repo = DigiumGitRepo(path, gerrit_repo, show_progress=True)<br>+    djv = DigiumJiraVersion(options.version, options.project)<br>+    repo = get_repo(options.project, options.local_root,<br>+                    options.remote_url, True)<br> <br>     log_messages = repo.get_commits_by_tags(options.start_tag, options.end_tag)<br> <br>@@ -64,33 +62,8 @@<br>                 continue<br> <br>             status = str(issue.fields.status).lower()<br>-            if status != 'closed' and status != 'complete':<br>-                continue<br>-<br>-            # Get the actual version object for the project. This<br>-            # is what must be passed to JIRA to update the fixVersion<br>-            # field for the issue.<br>-            project = issue.fields.project<br>-            if project.name not in jira_versions:<br>-                project_versions = jira.project_versions(project)<br>-                try:<br>-                    version, = [ver.id for ver in project_versions<br>-                                if ver.name == options.version]<br>-                except Exception:<br>-                    print "Could not handle versions for {0}".format(issue_id)<br>-                    continue<br>-                jira_versions[project.name] = version<br>-<br>-            # Make sure we don't update the fixVersion more than once for<br>-            # a particular issue<br>-            matches = [match for match in issue.fields.fixVersions<br>-                       if match.name == options.version]<br>-            if len(matches) == 0:<br>-                version_array = [{'id': u'{0}'.format(ver.id)} for ver in<br>-                                 issue.fields.fixVersions]<br>-                version_array.append({'id': u'{0}'.format(<br>-                                     jira_versions[project.name])})<br>-                issue.update(fields={'fixVersions': version_array})<br>+            if status == 'closed' or status == 'complete':<br>+                djv.apply_version(issue)<br> <br>         pbar.update(i + 1)<br>     pbar.finish()<br>@@ -122,6 +95,8 @@<br>     parser.add_option("-v", "--version", action="store", type="string",<br>         dest="version", default="",<br>         help="Version ID.")<br>+    parser.add_option("-r", "--remote-url", action="store", type="string",<br>+        dest="remote_url", default="", help="The remote url")<br> <br>     (options, args) = parser.parse_args(argv)<br> <br>diff --git a/mkrelease.py b/mkrelease.py<br>index 851dec2..a3f3f18 100755<br>--- a/mkrelease.py<br>+++ b/mkrelease.py<br>@@ -18,7 +18,7 @@<br> from datetime import datetime<br> from optparse import OptionParser<br> <br>-from digium_git import DigiumGitRepo<br>+from digium_git import get_repo<br> from version_parser import AsteriskVersion<br> from release_summary import ReleaseSummary, ReleaseSummaryOptions<br> from alembic_creator import create_db_script<br>@@ -31,10 +31,6 @@<br> logging.getLogger("urllib3").setLevel(logging.WARNING)<br> # Same for the migration stuff<br> logging.getLogger("alembic").setLevel(logging.WARNING)<br>-<br>-<br>-# The one and only Gerrit/Git server.<br>-GERRIT = 'ssh://gerrit.asterisk.org:29418'<br> <br> # The previous tag from this release.<br> previous_tag = ''<br>@@ -114,25 +110,6 @@<br>     i = version.find('/')<br>     ftp_project = ('{0}-{1}'.format(version[:i], options.project) if i > 0<br>                    else options.project)<br>-<br>-<br>-def prepare_repo(options):<br>-    """Prepare the repo that the release will be made from<br>-<br>-    Keyword Arguments:<br>-    options - Parsed command line arguments<br>-<br>-    Returns:<br>-    A DigiumGitRepo object<br>-    """<br>-<br>-    path = os.path.join(options.local_root, options.project)<br>-    repo_url = '{0}/{1}'.format(options.remote_url, options.project)<br>-<br>-    LOGGER.debug("Cloning from '{0}' to '{1}'".format(repo_url, path))<br>-    repo = DigiumGitRepo(path, repo_url=repo_url,<br>-                         show_progress=options.loglevel == logging.DEBUG)<br>-    return repo<br> <br> <br> def prepare_branch(options, repo):<br>@@ -296,7 +273,7 @@<br>                 sum_opts.branch = branch<br> <br>                 summary = ReleaseSummary(<br>-                    sum_opts, debug=options.loglevel == logging.DEBUG)<br>+                    sum_opts, repo=repo, debug=options.loglevel == logging.DEBUG)<br>             else:<br>                 summary = None<br> <br>@@ -375,7 +352,7 @@<br>         file_path = os.path.join(file_dir, file_name)<br> <br>         summary = ReleaseSummary(<br>-            sum_opts, debug=options.loglevel == logging.DEBUG)<br>+            sum_opts, repo=repo, debug=options.loglevel == logging.DEBUG)<br>         summary.to_html(out_file=file_path)<br>         LOGGER.debug("Release summaries created as '{0}'".format(file_path))<br> <br>@@ -663,7 +640,7 @@<br>                       default="/tmp")<br>     parser.add_option("-r", "--remote-url", action="store", type="string",<br>                       dest="remote_url", help="The remote url",<br>-                      default=GERRIT)<br>+                      default="")<br>     parser.add_option("-p", "--project", action="store", type="string",<br>                       dest="project", help="The project to work from",<br>                       default="asterisk")<br>@@ -692,7 +669,8 @@<br>     # The following are all various set up steps that extract options, prepare<br>     # the environment, and calculate what it is we are trying to create.<br>     setup_options(options)<br>-    repo = prepare_repo(options)<br>+    repo = get_repo(options.project, options.local_root, options.remote_url,<br>+                    show_progress=options.loglevel == logging.DEBUG)<br>     prepare_branch(options, repo)<br>     extract_tags(options, repo)<br> <br>diff --git a/release_summary.py b/release_summary.py<br>index 27d2fdd..de475a8 100755<br>--- a/release_summary.py<br>+++ b/release_summary.py<br>@@ -15,7 +15,7 @@<br> from optparse import OptionParser<br> from progressbar import ProgressBar<br> <br>-from digium_jira import get_jira_client<br>+from digium_jira import get_jira_client, DigiumJiraVersion<br> from digium_jira_user import AsteriskUser<br> from digium_git import DigiumGitRepo<br> <br>@@ -30,9 +30,6 @@<br> <br> # URL prefix for security advisories<br> ADVISORY_URL = "http://downloads.asterisk.org/pub/security/"<br>-<br>-# URL for Gerrit/Git repos<br>-GERRIT = "ssh://gerrit.asterisk.org:29418"<br> <br> # Default repo location<br> DEFAULT_REPO_ROOT = "/tmp"<br>@@ -171,13 +168,14 @@<br>         "protect their systems from these issues."<br> <br> <br>-    def __init__(self, options, jira=None, debug=False):<br>+    def __init__(self, options, jira=None, repo=None, debug=False):<br>         """Constructor<br> <br>         Keyword Arguments:<br>         options      - An instance of ReleaseSummaryOptions.<br>         jira         - The JIRA client to use.<br>         debug        - If true, display progress as the summary is built<br>+        repo<br>         """<br> <br>         self.jira = jira<br>@@ -194,9 +192,8 @@<br>         self.misc_commits = []      # Commits not associated with an issue<br>         self.contributors = Contributors()<br> <br>-        path = os.path.join(self.options.local_root, self.options.project)<br>-        gerrit_repo = '{0}/{1}'.format(GERRIT, self.options.project)<br>-        self.repo = DigiumGitRepo(path, gerrit_repo, show_progress=self.debug)<br>+        self.repo = repo or get_repo(options.project, options.local_root,<br>+                                     options.remote_url, self.debug)<br> <br>         # Infer the branch from the version<br>         if not self.options.branch:<br>@@ -254,6 +251,12 @@<br>             pbar.maxval = len(self.raw_log_messages)<br>             pbar.start()<br> <br>+        # Go ahead and update the fixedVersion on the issue while processing<br>+        # the release summary. If the version has already been applied it<br>+        # won't do it again.<br>+        djv = DigiumJiraVersion(self.options.version,<br>+                                self.options.project, self.jira)<br>+<br>         for i, log_message in enumerate(self.raw_log_messages):<br> <br>             if log_message.raw and len(log_message.raw.parents) > 1:<br>@@ -306,6 +309,7 @@<br>                     status = str(issue.fields.status).lower()<br>                     if status == 'closed' or status == 'complete':<br>                         issue_dict = self.closed_issues<br>+                        djv.apply_version(issue)<br>                     else:<br>                         issue_dict = self.open_issues<br> <br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/7144">change 7144</a>. To unsubscribe, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/7144"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: repotools </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>
<div style="display:none"> Gerrit-Change-Id: I1be07a4ff67f5733b6f4cc410d47c4fce7b5a1e3 </div>
<div style="display:none"> Gerrit-Change-Number: 7144 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Kevin Harwell <kharwell@digium.com> </div>