How to generate a changelog from Jira for your deb/rpm/…

A changelog is a log or record of changes made to a project, such as a website or software project, usually including such records as bug fixes, new features, etc. Some open source projects include a changelog as one of the top level files in their distribution.

If you are running a RHEL distribution (Centos, Fedora, Red Hat…), you can read it via the rpm command:

rpm -q --changelog vim-enhanced.x86_64 | less

For Debian based distributions, you can do it via apt-get:

apt-get changelog vim | less

If you build rpm or deb packages, it can be usefull to generate a changelog, but how to generate it from Jira?

Jira provides a versions tab on the project home page: https://jira.springsource.org/browse/DATASOLR?selectedTab=com.atlassian.jira.plugin.system.project%3Aversions-panel
We want to extract all the tickets under these versions and generate a changelog file that will be attached to the package. And this is pretty simple: Jira offers a JQL interface where you can query the underlying database with JPA-like language (https://confluence.atlassian.com/display/JIRA/Advanced+Searching), and an awesome REST API (https://docs.atlassian.com/jira/REST/latest/).

The format of a changelog file is simple. Start each new entry with a line with a * followed by the date, and optionnaly with your name and your email address. The date should appear in the same format that is output by: date +”%a %b %d %Y”. We will use joda-time as dateformatter http://www.joda.org/joda-time/).The rest of the section is a free text field, but should be organized in some coherent manner.

First, you need some dependencies:

  <dependency>
   <groupId>com.atlassian.jira</groupId>
   <artifactId>jira-rest-java-client-core</artifactId>
   <version>2.0.0-m25</version>
  </dependency>
  <dependency>
   <groupId>org.apache.httpcomponents</groupId>
   <artifactId>httpclient</artifactId>
   <version>4.3</version>
  </dependency>

NB
Atlassian has a custom Maven repo:

  <repository>
   <id>atlassian-public</id>
   <url>https://m2proxy.atlassian.com/repository/public</url>
   <snapshots>
    <enabled>true</enabled>
    <updatePolicy>daily</updatePolicy>
    <checksumPolicy>warn</checksumPolicy>
   </snapshots>
   <releases>
    <enabled>true</enabled>
    <checksumPolicy>warn</checksumPolicy>
   </releases>
  </repository>

Second, you have to launch an ugly piece of code like this one in order to generate a changelog for the project DATASOLR (https://jira.springsource.org/browse/DATASOLR):

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Locale;
import java.util.concurrent.ExecutionException;

import org.apache.http.client.ClientProtocolException;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.DateTimeFormatterBuilder;
import org.junit.Test;

import com.atlassian.jira.rest.client.api.JiraRestClient;
import com.atlassian.jira.rest.client.api.JiraRestClientFactory;
import com.atlassian.jira.rest.client.api.domain.Issue;
import com.atlassian.jira.rest.client.api.domain.SearchResult;
import com.atlassian.jira.rest.client.api.domain.Version;
import com.atlassian.jira.rest.client.auth.AnonymousAuthenticationHandler;
import com.atlassian.jira.rest.client.internal.async.AsynchronousJiraRestClientFactory;

public class Jira {

 @Test
 public void monTest() throws URISyntaxException, InterruptedException,
   ExecutionException, ClientProtocolException, IOException {

  // Output
  File releaseNote = new File("/tmp/changelog");

  if (!releaseNote.exists()) {
   releaseNote.createNewFile();
  } else {
   releaseNote.delete();
   releaseNote.createNewFile();
  }

  BufferedWriter writer = new BufferedWriter(new FileWriter(
    releaseNote.getAbsoluteFile()));

  // Formatter
  DateTimeFormatter fmt = new DateTimeFormatterBuilder()
    .appendDayOfWeekShortText().appendLiteral(' ')
    .appendMonthOfYearShortText().appendLiteral(' ')
    .appendDayOfMonth(2).appendLiteral(' ').appendYear(4, 4)
    .toFormatter().withLocale(Locale.US);

  // Client
  JiraRestClientFactory factory = new AsynchronousJiraRestClientFactory();
  URI jiraServerUri = new URI("https://jira.springsource.org");
  JiraRestClient restClient = factory.create(jiraServerUri,
    new AnonymousAuthenticationHandler());

  SearchResult searchResult = restClient
    .getSearchClient()
    .searchJql(
      "project = DATASOLR and fixVersion is not null ORDER BY fixVersion desc, issuetype asc",
      1000, 0, null).get();

  // Prepare data
  String lastIssueType = null;
  String lastRelease = null;
  for (Issue issue : searchResult.getIssues()) {
   Version version = issue.getFixVersions().iterator().next();
   if (lastRelease == null || !lastRelease.equals(version.getName())) {
    lastRelease = version.getName();
    if (lastRelease == null) {
     writer.append("\n");
    }
    writer.append("* ").append(fmt.print(version.getReleaseDate()))
      .append(" ").append(version.getName()).append("\n");
    lastIssueType = null;
   }
   if (lastIssueType == null
     || !lastIssueType.equals(issue.getIssueType().getName())) {
    lastIssueType = issue.getIssueType().getName();
    writer.append("- ").append(lastIssueType).append("\n");
   }
   writer.append("\t[").append(issue.getKey()).append("] ");
   writer.append(issue.getSummary());

   writer.append("\n");
  }

  restClient.close();
  writer.close();

 }
}

We are using a AnonymousAuthenticationHandler because most of open source projects doesn’t need an authentication on Jira. If you need it, you can switch to a BasicHttpAuthenticationHandler:

JiraRestClient restClient = factory.createWithBasicHttpAuthentication(
    jiraServerUri, "jira", "jira");

For custom authentication schemes (OAuth…), create a specific implementation of the interface AuthenticationHandler.

And finally, you will get a file /tmp/changelog containing something like:

* Thu Oct 17 2013 1.0.1
- Bug
 [DATASOLR-116] MappingSolrConverter doesn't honor Solr wildcard rules on read

* Wed Sep 11 2013 1.0 GA
- Bug
 [DATASOLR-98] MappingSolrConverter does not retain element order when reading property
 [DATASOLR-81] Cannot index Geospatial with solr4

- Improvement
 [DATASOLR-100] Support proximity or sloppy searches
 [DATASOLR-68] Add support for facet.prefix

- New Feature
 [DATASOLR-93] Solr softCommit support

- Task
 [DATASOLR-111] Release 1.0 GA
....

Enjoy!

Sources and other infos:

http://www.tldp.org/HOWTO/RPM-HOWTO/build.html
http://en.wikipedia.org/wiki/Release_notes
http://www.cyberciti.biz/tips/howto-find-out-linux-rpm-package-changelog.html

Leave a comment

About privacy:

This site uses Akismet to reduce spam. Learn how your comment data is processed.