Until now, packaging Go applications for Red Hat Enterprise Linux (RHEL) was very different from packaging them for Fedora. Fedora has plenty of tools that make the process as easy and quick as possible. And now, with the release of RHEL 10 beta, the packaging differences between Fedora, CentOS Stream and RHEL are smaller than ever before. We can also work from a Fedora machine without complex setups.
Let's explore how similar the process is now and how you can package a sample Go application faster than ever in RHEL 10.
Packaging Go applications in Red Hat Enterprise Linux 10
For the sake of this article, I made a sample application that basically prints a little of information about the system. You can find the code in this repository. But the code is as simple as it gets:
package main
import (
"fmt"
"os"
"runtime"
)
func getRelease() error {
content, err := os.ReadFile("/etc/redhat-release")
if err != nil {
if os.IsNotExist(err) {
return fmt.Errorf("file not found")
}
return fmt.Errorf("failed to read file: %w", err)
}
fmt.Printf("OS: %s", string(content))
return nil
}
func main() {
getRelease()
fmt.Printf("Arch: %s\n", runtime.GOARCH)
fmt.Printf("Go version: %s\n", runtime.Version())
}
The idea behind the workflow for packaging a Go project has not changed, and you can see how I discussed this a few years ago. However, the process became cumbersome as the applications became more complex. The vast majority of the steps can now be leveraged to more modern, up-to-date tools. The majority of RPM packagers operate on Fedora desktops, and so do we. If you don't have a Fedora setup at hand or don't want to mess with your setup directly, we'll use a container.
$ podman pull registry.fedoraproject.org/fedora-toolbox:41
Trying to pull registry.fedoraproject.org/fedora-toolbox:41...
Getting image source signatures
Copying blob cabdb1918cf1 skipped: already exists
Copying config 8a11b6229e done |
Writing manifest to image destination
8a11b6229efe85b59df4b7ae2fd7c27dc1ac9b553ddcdecf932e0256c7c2e710
$ podman run -it registry.fedoraproject.org/fedora-toolbox:41 /bin/bash
[root@18af08a1e3ce /]#
If you have Toolbx installed, the process couldn't be easier:
$ toolbox create
Image required to create Toolbx container.
Download registry.fedoraproject.org/fedora-toolbox:41 (375.4MB)? [y/N]: y
Created container: fedora-toolbox-41
Enter with: toolbox enter
$ toolbox enter
[fedora-toolbox-41]$
Regardless of your choice, you should now have a Fedora 41 environment, where we can install all the necessary components.
[fedora-toolbox-41]$ sudo dnf install golang go-rpm-macros go2rpm fedpkg centpkg "@rpm-development-tools" -y
Before, we had to create the specfile
file manually and adjust several steps to meet the needs of the application we were packaging. However, we can now use go2rpm
to generate the specfile
file automatically. Usually, there aren't many changes required.
If you look at the Go sample repository, you'll see I created a release. This is intentional to represent a real upstream project and to make life easier for go2rpm
, but you can always specify a commit or a tag in case you want a specific release:
[fedora-toolbox-41]$ go2rpm github.com/alexsaezm/rhd_blog2024
# OR
[fedora-toolbox-41]$ go2rpm github.com/alexsaezm/rhd_blog2024 --commit 3da750
Both commands will create a specfile
in the current path. After this, we can use spectool
and centpkg
to build our package. This tool requires us to work with a Git repository. If you aren't already working on a Git repository, create one before you tackle any of the following steps:
[fedora-toolbox-41]$ go2rpm github.com/alexsaezm/rhd_blog2024
[fedora-toolbox-41]$ spectool -g golang-github-alexsaezm-rhd-blog2024.spec
[fedora-toolbox-41]$ centpkg --release c10s mockbuild
This will create a folder that starts with results_
, inside which you will find the resulting RPM packages. These packages can now be installed on your CentOS Stream 10 or RHEL 10 machine.
Furthermore, we can build it for Fedora without any modifications:
[fedora-toolbox-41]$ fedpkg --release rawhide mockbuild
This also simplifies the process of creating an RPM during a CI pipeline. For example, we could add the previous steps to a Containerfile to complete the process with a small tweak. Instead of using mockbuild
which uses a container to perform the build, we can now use local
:
FROM registry.fedoraproject.org/fedora-minimal:latest
RUN dnf install -y \
golang \
go-rpm-macros \
centpkg \
"@rpm-development-tools" && \
dnf clean all
WORKDIR /build
COPY . .
RUN centpkg --release c10s local
Final thoughts
This is, of course, a demo. The next step in a normal packaging workflow would be to put the RPM on a repository so that all machines with it configured would have the update.
Personally, I am very excited about the packaging for RHEL 10 and Go. Some people used to complain that it was challenging to make a Go package work in Fedora and RHEL at the same time. Now that gap is even smaller than ever before.