RPM package generation with Gradle

Do you use CentOS or any other rpm based Linux distribution to host Java applications or services?
In this article you will see how Gradle can be used for that purpose, in particular:

  • what are the naming convention that can be used with RPM
  • which tools can help with rpm generation with Gradle

The Desired Flow

Java to RPM flow

The goal is to use yum installer to download, install or update java application or service. Yum uses rpm version comparison to detect updated versions of packages. In order to be able to achieve this goal, it is important to understand how rpm compares versions.

Rpm Version Comparison

There are many articles on the internet, hence I will stay DRY and provide some links that can be useful:

Another useful thing to do could be to try it yourself. One option is to install rpmdevtools (might require enabling EPEL repository in CentOS): yum install rpmdevtools and run rpmdev-vercmp function. There is also a python implementation of the rpm version compare function. Clone git repository and have fun comparing strings C-like!

Choosing RPM Package Name

According to Red Hat’s guidelines:

Name The base name of the package, which should match the SPEC file name.
Version The upstream version number of the software.
Release The number of times this version of the software was released. Normally, set the initial value to 1%{?dist}, and increment it with each new release of the package. Reset to 1 when a new Version of the software is built.
  • Name can be the name of the software (application or service) that is being released.
  • Version could be the current version of the software. This can be taken form gradle version or git tag or actually any place where gradle can read from.
  • Release explains how many times a package with that version has been built. It can serve the same purpose as SNAPSHOT in maven if there is a need to release the same version of the application and install it many times with yum. This could hold true for development integration environment where subsequent pull requests are being merged to a development branch.

Gradle Implementation

Gradle has 2 plugins that can support rpm generation:

Netflix’s ospackage

The ospackage plugin allows rpm generation with Gradle. It has special values to define version, release, package name and other RPM related metadata. It comes with a predefined buildRpm task. See documentation for more details.

Palantir’s git-version

Git-version allows to use git information to add to the package name. According to current documentation the defaults are as follows:

  • packageName – Default to project.name
  • version – Version field, defaults to project.version

One example of version and release naming can be the follwoing algorithm:

  1. Mark next release on a development branch with a release candidate git tag: rc@1.0.0 for example.
  2. Use this tag as the Version. Git-version allows searching tags only with certain prefixes if needed, which enables ignoring any other tags.
  3. Use commit distance from the tag as the Release. Since Release cannot be 0 (due to the rpm comparison function) add one to it. Additionally git hash can be appended to have complete view of where the code came from.

Example configuration

Below is the implementation of the algorithm described above. Notice highlighted values from ospackage plugin – there can be many more.

ospackage {
    def details = versionDetails(prefix: 'rc@')
    version details.lastTag
    release details.commitDistance + 1 + '.' + details.gitHash
    arch X86_64
    os LINUX

    into '/opt/gradle-rpm-example'
    from(jar.outputs.files) {
        into ''
    }
}

Now it is time to run gradle buildRpm. Examine build/distributions folder for rpms. Some examples of generated packages with git tag rc@1.0.0 and gradle project of name gradle-rpm-example:

gradle-rpm-example-1.0.0-1.1502f6cdf2.x86_64.rpm
gradle-rpm-example-1.0.0-2.ce3bc954a7.x86_64.rpm

Installation with yum

Assuming that these rpms have been stored in a yum repository (for example nexus), and this repository has been added to yum, given the fact that this example follows rpm comparison rules, subsequent rpms will be visible by yum as updates of the application or service. To list available yum repositories run yum repolist. Example output might be:

repo id | repo name | status
base/7/x86_64 CentOS-7 - Base 9591
extras/7/x86_64 CentOS-7 - Extras 435
nexusrepo Nexus Repository 4
updates/7/x86_64 CentOS-7 - Updates 2404

It is worth to remember that yum uses local cache, hence it is worth invalidating it if the updates are not visible.

yum clean expire-cache
yum makecache

To list available packages run

yum --disablerepo="*" --enablerepo="nexusrepo" list available

This command can generate output similar to the one below:

Available Packages
gradle-rpm-example.x86_64 1.0.0-1.1502f6cdf2 nexusrepo

To install the package run

yum install --nogpgcheck gradle-rpm-example

The –nogpgcheck option shouldn’t be used in real life if the author of the package is not known. The reason it is used here is that the sample package is not signed.

If the second package is now uploaded to yum repo, after refreshing yum cache and running yum info gradle-rpm-example it is clear that there is an update available:

Installed Packages
Name : gradle-rpm-example
Arch : x86_64
Version : 1.0.0
Release : 1.1502f6cdf2
Size : 0.0
Repo : installed
From repo : nexusrepo

Available Packages
Name : gradle-rpm-example
Arch : x86_64
Version : 1.0.0
Release : 2.ce3bc954a7
Size : 2.5 k
Repo : nexusrepo

Running yum update –nogpgcheck gradle-rpm-example will update the package and new version will be installed.

Please note that in this example, rpm release numbers can increase by more than one, given there can be more than one commit since the package has last been built. RPM does not impose strict rules on release generation, and unless next release is greater than the previous, yum will handle updates without issues.

Sample Project on GitHub

The example can be downloaded from GitHub repository.

Summary

This post illustrated how to build rpm package with Gradle as well as naming convention that could be used to allow yum updates of subsequent rpm releases.

What are your experiences with Java and RPM? It would be great to see different approaches to rpm generation and installation of software implemented in Java. If you can share your thoughts, please write in comments or contact me directly.

One thought on “RPM package generation with Gradle

  1. This looks like a great plugin but in my environment only the from/into stuff is working. None of the other directives like requires/directory/preInstall/postInstall etc have any effect whatsoever on the generated RPM. Annoyingly, the scriptlet directive complain if I provide an invalid filename, but they do absolutely nothing with a valid filename!

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s