Build a rpm with docker

In my current company I had to build some rpm’s in order to distribute my scripts to the servers we are selling to the customers. Since I am leaving this company I wanted to document this way of working as it might be useful for me in the future…

I am using jenkins to kick-off the job, basically jenkins is used to checkout the code then launch a script that will:

  • First create a docker image containings the necessary packages for rpmbuild and my sources;
  • Secondly run a container based on this image and start a script to create the rpm;
  • the created rpm is copied on a bind volume so that jenkins can recuperate it.

jenkins sets the variables BUILD_NUMBER and WORKSPACE. I use BUILD_NUMBER as the release part of the rpm version, but if the build is running on branch develop, then I use “snapshot” instead. The first part of the version is always read from a file called version.txt stored at the root of the git repo.

So, assuming the file version.txt contains 7.0.0 :

  • building on branch develop -> the rpm will be called mypackage-7.0.0-snapshot-noarch.rpm
  • building on another branch -> the rpm will be called mypackage-7.0.0-xxx-noarch.rpm, where xxx is BUILD_NUMBER

Here is how the main script looks like. This script is called from jenkins with a parameter that is the git branch.


PACKAGE_TO_BUILD=mypackage

help(){
 echo "$0 -b <branch> where branch is git branch. When branch is develop, snapshot will be added to the version"
 exit 1
}

while getopts b:h ARG
do
   case $ARG in
      b ) BRANCH=${OPTARG};;
      h ) help ;;
      * ) echo "invalid parameter"
          help
          exit;;
   esac
done
VERSION=$(cat version.txt)
if [ $BRANCH == "develop" ] ; then
  BUILD_NUMBER="snapshot"
fi
# directory where the rpm will be stored
if [ -d artifacts ] ; then
 sudo rm -rf artifacts
fi
mkdir artifacts
chmod 777 artifacts

# First build a docker image that contains the sources and necessary packages for rpmbuild
docker build -t ${PACKAGE_TO_BUILD}-build .

# then run the docker image
if [ -z $WORKSPACE ] ; then
  # we are not being called from within jenkins
  WORKSPACE=$(pwd)
fi
docker run --rm=true -v ${WORKSPACE}/artifacts:/artifacts -e BUILD_NUMBER=${BUILD_NUMBER} -e VERSION=$VERSION --user rpmbuild ${PACKAGE_TO_BUILD}-build /home/rpmbuild/build_rpm.bash

The Dockerfile contains something similar to the one below. Since jenkins has done the checkout of the repository, everything needed is in the docker build context (i.e. my sources are readily available to be added in the image, see the COPY instructions)

FROM centos:centos7
MAINTAINER ptim007@yahoo.com
RUN yum -y install rpm-build redhat-rpm-config make gcc git vi tar unzip rpmlint && yum clean all
RUN useradd rpmbuild -u 5002 -g users -p rpmbuild
# here I add everything I need to make the rpm: sources, the build script, etc...all of that was checkout from git by jenkins.
COPY build/build_rpm.bash /home/rpmbuild/build_rpm.bash
COPY build/mypackage.spec /home/rpmbuild/mypackage.spec
COPY version.txt /home/rpmbuild/versions.txt
COPY src /home/rpmbuild/src
USER rpmbuild
ENV HOME /home/rpmbuild
WORKDIR /home/rpmbuild
RUN mkdir -p /home/rpmbuild/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
RUN echo '%_topdir %{getenv:HOME}/rpmbuild' > /home/rpmbuild/.rpmmacros

The build_rpm.bash copied inside the docker image is the script executed in the container (see docker run command). It looks like that

#!/bin/bash

PACKAGE=mypackage

if [ -z $BUILD_NUMBER ] ; then
 echo BUILD_NUMBER is not know, it is normally given by jenkins
 exit 1
fi
RELEASE=$BUILD_NUMBER
if [ -z $VERSION ] ; then
  echo VERSION is not set via parameter neither via ENV, will use version.txt
  VERSION=`cat version.txt`
fi

cd /home/rpmbuild
if [ ! -f ./${MYPACKAGE}.spec ] ; then
 echo Sorry, can not find rpm spec file 
 exit 1
fi
cp ${MYPACKAGE}.spec $HOME/rpmbuild/SPECS
# here I patch the spec file to feed it with the version and the release and the date
sed -i -e "s/##VERSION##/${VERSION}/" -e "s/##RELEASE##/${RELEASE}/" /home/rpmbuild/rpmbuild/SPECS/${PACKNAME}.spec
sed -i -e "s/##DATE##/`date +\"%a %b %d %Y\"`/" /home/rpmbuild/rpmbuild/SPECS/${PACKNAME}.spec

# prepare a tar.gz file with the sources and copy it  to the SOURCES directory
...
tar -zcf ${PACKAGE}-${VERSION}-${RELEASE}.tar.gz ./src
cp ${PACKAGE}-${VERSION}-${RELEASE}.tar.gz $HOME/rpmbuild/SOURCES/


# then execute the rpmbuild command
cd $HOME/rpmbuild
rpmbuild -ba --define "_buildnr ${BUILD_NUMBER}" --define "_myversion $VERSION" ./SPECS/${PACKAGE}.spec
# copy the rpms to the artifact directory, for jenkins.
if [[ -d /artifacts ]] ; then
 cp ./RPMS/noarch/${PACKAGE}*.rpm /artifacts/
fi
 

For reference (and my future usage !), here is how a spec file of one of my rpm looks like:

Name: mypackage
Version: ##VERSION##
Release: ##RELEASE##
Summary: mypackage scripts

Group: Any
License: GPL
URL: www.XXXX.com
Source0: mypackage_%{version}-%{release}.tar.gz
BuildArch: noarch
BuildRoot:      %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)

%description
Installation package for mypackage

%prep
%setup -q -n mypackage_%{version}-%{release}

%install
rm -rf %{buildroot}
install -d $RPM_BUILD_ROOT/opt/mypackage
cp -r src  $RPM_BUILD_ROOT/opt/mypackage

%clean
rm -rf %{buildroot}

%post
echo " "
echo "install done, you can execute the install.sh scripts"

%files
%defattr(754,oracle,oinstall,754)
%dir /opt/mypackage
/opt/mypackage

%attr(750,oracle,oinstall) /opt/mypackage/install.sh
... etc (list the files here)

%changelog
* ##DATE## mypackage - %{version} %{release}
- Automatic build
Written on February 14, 2019