<p>George Joseph <strong>merged</strong> this change.</p><p><a href="https://gerrit.asterisk.org/7144">View Change</a></p><div style="white-space:pre-wrap">Approvals:
Joshua Colp: Looks good to me, but someone else must approve; Verified
George Joseph: Looks good to me, approved; Approved for Submit
</div><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;">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: merged </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>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Joshua Colp <jcolp@digium.com> </div>