On mono-project.com, we ship packages for Debian 8, Debian 9, Raspbian 8, Raspbian 9, Ubuntu 14.04, Ubuntu 16.04, Ubuntu 18.04, RHEL/CentOS 6, and RHEL/CentOS 7. Because this is Linux packaging we’re talking about, making one or two repositories to serve every need just isn’t feasible – incompatible versions of libgif, libjpeg, libtiff, OpenSSL, GNUTLS, etc, mean we really do need to build once per target distribution.
For the most part, this level of “LTS-only” coverage has served us reasonably well – the Ubuntu 18.04 packages work in 18.10, the RHEL 7 packages work in Fedora 28, and so on.
I was not at all keen on adding non-LTS Fedora 29 to our build matrix, due to the time and effort required to bootstrap a new distribution into our package release system. And, as if in answer to my pain, the beta release of Red Hat Enterprise 8 landed.
Cramming a square RPM into a round Ubuntu
Our packaging infrastructure relies upon a homogenous pool of Ubuntu 16.04 machines (x64 on Azure, ARM64 and PPC64el on-site at Microsoft), using
pbuilder to target Debian-like distributions (building i386 on the x64 VMs, and various ARM flavours on the ARM64 servers); and
mock to target RPM-like distributions. So in theory, all I needed to do was drop a new RHEL 8 beta mock config file into place, and get on with building packages.
Just one problem – between RHEL 7 (based on Fedora 19) and RHEL 8 (based on Fedora 28), the Red Hat folks had changed package manager, dropping Yum in favour of DNF. And
mock works by using the host distribution’s package manager to perform operations inside the build root – i.e. yum.deb from Ubuntu.
It’s not possible to install RHEL 8 beta with Yum. It just doesn’t work. It’s also not possible to update
mock to $ latest and use a bootstrap chroot, because reasons. The only options: either set up Fedora VMs to do our RHEL 8 builds (since they have DNF), or package DNF for Ubuntu 16.04.
For my sins, I opted for the latter. It turns out DNF has a lot of dependencies, only some of which are backportable from post-16.04 Ubuntu. The dependency tree looked something like:
- Update mock and put it in a PPA
- Backport RPM 4.14+ and put it in a PPA
- Backport python3-distro and put it in a PPA
- Package dnf and put it in a PPA
- Package libdnf and put it in a PPA
- Backport util-linux 2.29+ and put it in a PPA
- Update libsolv and put it in a PPA
- Package librepo and put it in a PPA
- Backport python3-xattr and put it in a PPA
- Backport gpgme1.0 and put it in a PPA
- Backport libgpg-error and put it in a PPA
- Package modulemd and put it in a PPA
- Backport gobject-introspection 1.54+ and put it in a PPA
- Backport meson 0.47.0+ and put it in a PPA
- Backport googletest and put it in a PPA
- Package libcomps and put it in a PPA
- Package libdnf and put it in a PPA
- Package dnf-plugins-core and put it in a PPA
- Hit all the above with sticks until it actually works
- Communicate to community stakeholders about all this, in case they want it
This ended up in two PPAs – the end-user usable one here, and the “you need these to build the other PPA, but probably don’t want them overwriting your system packages” one here. Once I convinced everything to build, it didn’t actually work – a problem I eventually tracked down and proposed a fix for here.
All told it took a bit less than two weeks to do all the above. The end result is, on our Ubuntu 16.04 infrastructure, we now install a version of
mock capable of bootstrapping DNF-requiring RPM distributions, like RHEL 8.
RHEL isn’t CentOS
We make various assumptions about package availability, which are true for CentOS, but not RHEL (8). The (lack of) availability of the EPEL repository for RHEL 8 was a major hurdle – in the end I just grabbed the relevant packages from EPEL 7, shoved them in a web server, and got away with it. The second is structural – for a bunch of the libraries we build against, the packages are available in the public RHEL 8 repo, but the corresponding -devel packages are in a (paid, subscription required) repository called “CodeReady Linux Builder” – and using this repo isn’t
mock-friendly. In the end, I just grabbed the three packages I needed via
curl, and transferred them to the same place as the EPEL 7 packages I grabbed.
Finally, I was able to begin the bootstrapping process.
RHEL isn’t Fedora
After re-bootstrapping all the packages from the CentOS 7 repo into our “””CentOS 8″”” repo (we make lots of naming assumptions in our control flow, so the world would break if we didn’t call it CentOS), I tried installing on Fedora 29, and… Nope. Dependency errors. Turns out there are important differences between the two distributions. The main one is that any package with a Python dependency is incompatible, as the two handle Python paths very differently. Thankfully, the diff here was pretty small.
The final, final end result: we now do every RPM build on CentOS 6, CentOS 7, and RHEL 8. And the RHEL 8 repo works on Fedora 29
The only errata: MonoDevelop’s version control addin is built without support for ssh+git:// repositories, because RHEL 8 does not offer a libssh2-devel. Other than that, hooray!