Introducing a Go Package to Fedora
Inhaltsverzeichnis
How to Introduce a Go Package to Fedora
If you maintain an open-source Go project and want it available via dnf install on Fedora, the path is well-defined but not always obvious. The Fedora packaging ecosystem has decades of conventions, and Go projects bring their own particularities — vendored dependencies, static linking, and a toolchain that moves fast.
This article documents the full journey from upstream Go project to Fedora stable repository, based on my experience introducing complyctl and complytime-providers to Fedora. My goal is to save you the hours I spent reading scattered documentation and figuring out the right path.
Before You Start
Prerequisites
You need two things before proposing a package to Fedora:
-
A Fedora Account (FAS): Create one at accounts.fedoraproject.org . This is your identity across all Fedora infrastructure.
-
Membership in the packager group: Follow the Joining the Package Maintainers guide. This involves signing the FPCA (Fedora Project Contributor Agreement) and being sponsored by an existing packager.
If you are not yet a Fedora packager, you can still prepare your spec
and SRPM. Ask a sponsor to review your work — the sponsorship process
often starts by demonstrating that you can produce a well-formed package.
Install the Tools
On a Fedora machine:
sudo dnf install fedora-packager fedora-review go-vendor-tools \
rpm-build rpmlint rpmdevtools
sudo usermod -a -G mock $USER
newgrp mock
The key tools:
| Tool | Purpose |
|---|---|
fedora-review |
Automated review checklist + mock build |
go-vendor-tools |
Vendored license detection and archive generation |
rpmbuild |
Build SRPMs and binary RPMs locally |
rpmlint |
Lint RPMs for common packaging errors |
mock |
Build in a clean chroot (simulates koji) |
spectool |
Download sources declared in the spec |
rpmdev-setuptree |
Create the ~/rpmbuild/ directory tree |
Understanding Go in Fedora
The Vendoring Question
In most languages, Fedora packaging guidelines recommend consuming dependencies from the Fedora ecosystem. Python packages use Fedora’s python3-* RPMs. Ruby packages use Fedora’s rubygem-* RPMs. The idea is that security updates to a shared library benefit all packages that depend on it.
Go is different. The Fedora Go SIG
(Special Interest Group) explicitly allows — and effectively expects — vendoring. This means your Go module’s vendor/ directory is shipped as part of the source, and dependencies are not packaged separately as individual Fedora RPMs.
This is a pragmatic decision. The Go ecosystem has thousands of small
modules, many with rapid release cycles. Packaging each one individually
would be unsustainable, and Go's static linking means shared libraries
don't provide the same security benefit as in C/Python/Ruby.
The go-vendor-tools Ecosystem
Since vendored dependencies are bundled inside your package, Fedora needs a way to:
- Track which licenses are bundled — the RPM
License:field must include ALL vendored licenses - Verify license consistency — the build must fail if the license config is stale
- Generate bundled
Provides:— sodnfknows what’s inside
This is handled by go-vendor-tools
, a Fedora-specific toolset that integrates with the RPM build macros. You will create a go-vendor-tools.toml configuration file that declares the license detector and any manual overrides for licenses that can’t be auto-detected.
Key Fedora Go Macros
The Fedora Go packaging macros replace raw go build commands with standardized, hardened equivalents:
| Macro | Replaces | What it does |
|---|---|---|
%gometa -f |
Manual URL/Source setup | Resolves forge metadata (GitHub URL, source tarball) |
%gosource |
Raw GitHub archive URL | Generates the correct source URL from forge metadata |
%goprep -A |
tar xf + directory setup |
Prepares the build tree without inline vendor |
%gobuild |
go build |
Builds with PIE, RELRO, FORTIFY, debuginfo generation |
%gocheck2 |
go test |
Runs tests with proper flags and vendor mode |
%autorelease |
Release: 1%{?dist} |
Automatic release numbering |
The %gobuild macro is particularly important — it applies Fedora’s hardening flags (position-independent executables, full RELRO, FORTIFY_SOURCE) and generates proper DWARF debuginfo. Without it, your package would lack debuginfo packages and miss security hardening.
The RPM Spec File
Anatomy of a Go Spec
Here is the structure of a well-formed Fedora Go spec file, annotated with the purpose of each section:
# SPDX-License-Identifier: Apache-2.0
# Toggle for running tests (disable with --without check)
%bcond check 1
# Application-specific constants
%global app_dir myapp
# Forge metadata — goipath + Version are used by %gometa
%global goipath github.com/org/project
Version: 1.0.0
%gometa -f
Name: myproject
Release: %autorelease
Summary: One-line description of your project
# SPDX expression covering ALL vendored licenses
License: Apache-2.0 AND BSD-3-Clause AND MIT
URL: %{gourl}
Source0: %{gosource}
Source1: %{archivename}-vendor.tar.bz2
Source2: go-vendor-tools.toml
BuildRequires: go-vendor-tools
ExclusiveArch: %{go_arches}
%description
Longer description of what your project does.
%prep
%goprep -A
%setup -q -T -D -a1 %{forgesetupargs}
%autopatch -p1
%generate_buildrequires
%go_vendor_license_buildrequires -c %{S:2}
%build
export GO_LDFLAGS="-X main.version=%{version}"
%gobuild -o %{gobuilddir}/bin/myapp %{goipath}/cmd/myapp
%install
%go_vendor_license_install -c %{S:2}
install -d -m 0755 %{buildroot}%{_bindir}
install -p -m 0755 %{gobuilddir}/bin/myapp %{buildroot}%{_bindir}/myapp
%check
%go_vendor_license_check -c %{S:2}
%if %{with check}
%gocheck2
%endif
%files -f %{go_vendor_license_filelist}
%doc README.md
%{_bindir}/myapp
%changelog
* Wed Sep 02 2026 Your Name <[email protected]> - 1.0.0-1
- Initial Fedora packaging
The Version: line must appear BEFORE %gometa -f because the forge
macros need it to construct the source URL. This is a common gotcha
that produces confusing errors if you place Version after %gometa.
Creating go-vendor-tools.toml
The license configuration file tells go-vendor-tools how to detect vendored licenses:
# First, check which licenses askalono can auto-detect:
go_vendor_license report all
# Look for any "Unknown" entries — those need manual overrides
Create go-vendor-tools.toml:
[archive]
[licensing]
detector = "askalono"
# Add overrides for licenses askalono can't auto-detect.
# Common case: go.yaml.in/yaml/v3 has a dual-license file
# that askalono can't parse.
[[licensing.licenses]]
path = "vendor/go.yaml.in/yaml/v3/LICENSE"
sha256sum = "d18f6323..."
expression = "MIT AND (MIT AND Apache-2.0)"
Generate the sha256sum:
sha256sum vendor/go.yaml.in/yaml/v3/LICENSE
Verify the final expression:
go_vendor_license --config go-vendor-tools.toml report expression
This expression goes into the spec’s License: field.
If your vendored dependencies change between releases (e.g., a module
migrates from gopkg.in/yaml.v3 to go.yaml.in/yaml/v3), the license
override path in go-vendor-tools.toml must be updated. The
%go_vendor_license_check macro in %check catches this at build time,
but it's easy to miss during development.
Fedora Version Compatibility
If your go.mod requires a Go version newer than what a supported Fedora release ships, you may need a compatibility workaround. For example, Fedora 43 shipped only Go 1.25 for some time while my project was already updated to require Go 1.26 in Upstream. This is already solved by the time I am writing, but good to keep the reference:
%prep
%goprep -A
%setup -q -T -D -a1 %{forgesetupargs}
%autopatch -p1
# TODO(2027-01): remove F43 workaround — Fedora 43 EOL: 2026-12-09
%if 0%{?fedora} == 43
sed -i 's/^go [0-9].*/go 1.25/' go.mod
sed -i '/^## explicit; go /s/go [0-9]\..*/go 1.25/' vendor/modules.txt
%endif
Always add a TODO comment with the EOL date so the workaround gets cleaned up.
Building and Testing Locally
Generate the SRPM
With a tagged upstream release and a complete spec, generate the SRPM:
# 1. Prepare the rpmbuild tree
rpmdev-setuptree
# 2. Download the source tarball (to the current directory)
spectool -g myproject.spec
# 3. Generate the vendor archive
go_vendor_archive create --config go-vendor-tools.toml myproject.spec
# 4. Copy everything to rpmbuild SOURCES
cp myproject-*.tar.gz ~/rpmbuild/SOURCES/
cp myproject-*-vendor.tar.bz2 ~/rpmbuild/SOURCES/
cp go-vendor-tools.toml ~/rpmbuild/SOURCES/
# 5. Build the SRPM
rpmbuild -bs myproject.spec
spectool downloads to the current directory (without the -R flag),
and go_vendor_archive also expects the source tarball in the current
directory. Run both from the same working directory to avoid
"file not found" errors.
Full Binary Build
rpmbuild --rebuild ~/rpmbuild/SRPMS/myproject-*.src.rpm
This performs the complete build cycle: %prep, %build, %check, %install, and RPM generation. If it succeeds, your spec is sound.
Verify with rpmlint
rpmlint ~/rpmbuild/SRPMS/myproject-*.src.rpm \
~/rpmbuild/RPMS/x86_64/myproject-*.rpm
Common false positives for Go packages:
spelling-errorfor tool names (e.g.,complyctl,gRPC) — these are real names, not typosinvalid-url Source1— the vendor archive is a local file generated bygo_vendor_archive, not a URLunused-direct-shlib-dependency libresolv.so.2— Go’s net package links libresolv via cgo for DNS resolution; this is a known Go packaging quirk and is accepted by Fedora reviewers
Test in Mock
mock -r fedora-rawhide-x86_64 rebuild ~/rpmbuild/SRPMS/myproject-*.src.rpm
Mock builds in a clean chroot, simulating the koji build environment. This catches issues that local builds miss (e.g., undeclared build dependencies). Ensure your user is in the mock group (newgrp mock if you just added yourself).
The Fedora Review Process
Filing the Review Request
Upload your spec and SRPM to a publicly accessible location. Fedora provides fedorapeople.org for packagers:
scp myproject.spec ~/rpmbuild/SRPMS/myproject-1.0.0-1.fc44.src.rpm \
fedorapeople.org:public_html/
Then file a bug at bugzilla.redhat.com :
| Field | Value |
|---|---|
| Product | Fedora |
| Component | Package Review |
| Version | rawhide |
| Summary | Review Request: myproject - Short description |
In the description:
Spec URL: https://yourname.fedorapeople.org/myproject.spec
SRPM URL: https://yourname.fedorapeople.org/myproject-1.0.0-1.fc44.src.rpm
Description: What your project does.
Fedora Account System Username: yourfasid
What the Reviewer Does
The reviewer runs:
fedora-review -b <BZ_NUMBER>
This tool automatically:
- Downloads the spec and SRPM from the URLs in the bug
- Builds the package in mock
- Runs rpmlint on all produced RPMs
- Generates a review checklist with MUST/SHOULD/EXTRA items
- Verifies source checksums against upstream
The output is a structured checklist where each item is marked:
[x]= automated pass[ ]= manual review needed[!]= fail[-]= not applicable
Helping Your Reviewer
The review process requires at least one person who is NOT the package author to review and approve. To make their job easier, here are some optionals:
- Run
fedora-reviewyourself first and identify any issues before filing the BZ - Post a proactive self-review as a BZ comment, documenting the rationale for each manual check item. But make it clear this does not replace the neutral review
The review checklist can look intimidating with many [ ] items. Most
of them are trivially passable — the reviewer reads the spec, verifies
each item, and marks it [x]. A good proactive self-review with clear
explanations can reduce a multi-hour review to under 30 minutes.
After Approval
Once the reviewer sets the fedora-review+ flag:
- Request the dist-git repo: The reviewer (or you) runs
fedpkg request-repo myproject <BZ_NUMBER> - Pagure repo is created at
src.fedoraproject.org/rpms/myproject - Initial import: Push your spec, sources, and patches to the repo
- Build in koji:
fedpkg build - Submit Bodhi update:
fedpkg update
Coordinating Multiple Packages
If your project depends on another package that is also new to Fedora (or needs a major version update), you face a coordination challenge. This is common in Go projects where a CLI tool depends on separately-packaged plugins.
Build vs Runtime Dependencies
The key insight for Go packages is that BuildRequires and Requires have different implications:
- BuildRequires must be in the Fedora buildroot (koji) at build time
- Requires must be in the Fedora repos at install time
Since Go dependencies are vendored, your inter-package dependency is likely a runtime-only Requires, not a BuildRequires. This means your package can be built and reviewed independently, even if the dependency isn’t yet updated in Fedora.
Multi-Build Bodhi Updates
Bodhi supports grouping multiple package builds into a single update. This means two packages can arrive in updates-testing and then stable as an atomic unit:
# After both packages are built in koji:
bodhi updates new --type enhancement \
--notes "Coordinated update: myproject + myplugins" \
myproject-1.0.0-1.fc44 myplugins-1.0.0-1.fc44
This ensures users never see a broken intermediate state where one package is updated but the other isn’t.
Automating Future Updates with Packit
Once the initial package is in Fedora, future updates can be automated with Packit
. Add a .packit.yaml to your upstream repo:
downstream_package_name: myproject
upstream_tag_template: v{version}
jobs:
- job: propose_downstream
trigger: release
dist_git_branches:
- fedora-rawhide
- fedora-stable
- job: koji_build
trigger: commit
dist_git_branches:
- fedora-rawhide
- fedora-stable
- job: bodhi_update
trigger: commit
dist_git_branches:
- fedora-stable
When you create a new GitHub release, Packit will automatically:
- Propose a PR to dist-git with the updated spec
- After merge, trigger a koji build
- Submit a Bodhi update for branched Fedora releases
This reduces the maintenance burden to reviewing and merging the automated PRs.
Key References
Here is a curated list of the most useful documentation. The Fedora docs are extensive and sometimes circular — these are the pages I actually needed:
| Topic | URL |
|---|---|
| New Package Process | docs.fedoraproject.org/…/New_Package_Process_for_Existing_Contributors |
| Package Review Process | docs.fedoraproject.org/…/Package_Review_Process |
| Review Guidelines | docs.fedoraproject.org/…/ReviewGuidelines |
| Go Packaging Guidelines | docs.fedoraproject.org/…/Golang |
| go-vendor-tools docs | fedora.gitlab.io/sigs/go/go-vendor-tools |
| Fedora Go SIG | fedoraproject.org/wiki/SIGs/Go |
| Package Maintenance Guide | docs.fedoraproject.org/…/Package_Maintenance_Guide |
| Packit | packit.dev |
| Bodhi | bodhi.fedoraproject.org |
Conclusion
Introducing a Go package to Fedora is a structured process with clear steps. The first time is the hardest — understanding the vendoring model, the Go-specific macros, the review process, and the toolchain takes time. But once you’ve done it, subsequent packages follow the same pattern, and tools like Packit automate the ongoing maintenance.
The Fedora Go packaging ecosystem is well-designed. The go-vendor-tools + %gobuild + %gocheck2 combination handles the hard parts: license compliance, hardening flags, and test execution. Your job is to write a clean spec, verify it locally, and help your reviewer understand the package.
I hope this guide saves you some of the time I spent navigating the documentation the first time around. The Fedora community is welcoming and the infrastructure is solid — getting your Go project into Fedora is worth the effort.