Skip to main content
This guide outlines the standards and best practices for packaging software in Terra. Following these guidelines ensures consistency, maintainability, and quality across all packages.

General Principles

Follow Fedora Standards

Terra packages should follow Fedora Packaging Guidelines unless Terra-specific exceptions apply.
When in doubt, check how Fedora packages similar software and follow their conventions.

Quality Over Quantity

  • Package software that provides real value to users
  • Ensure packages are well-tested before submission
  • Maintain packages you contribute - don’t abandon them
  • Upstream bug fixes when possible rather than patching locally

Respect Licensing

  • Only package software with compatible licenses (MIT, GPL, Apache, BSD, etc.)
  • Always include license files in packages
  • Document license information accurately in spec files
  • For Rust packages, include dependency licenses

Spec File Standards

Header Section

Every spec file must have complete metadata:
Name:           package-name
Version:        1.2.3
Release:        1%?dist
Summary:        Brief one-line description of the package

License:        MIT
URL:            https://upstream-project-url.com
Source0:        https://example.com/package-name-%{version}.tar.gz

BuildRequires:  dependency1
BuildRequires:  dependency2
Requires:       runtime-dependency
The %?dist macro is required in Release to ensure proper version sorting across Fedora releases.

Version and Release

  • Version: Upstream version number (1.2.3, not v1.2.3)
  • Release: Start at 1, increment for packaging changes
  • Use %?dist macro for distribution tag
Version:        2.5.0
Release:        1%?dist
For pre-releases:
Version:        2.5.0
Release:        0.1.beta2%?dist

Source Handling

URL:            https://github.com/user/project
Source0:        %{url}/archive/refs/tags/v%{version}.tar.gz

Dependencies

BuildRequires

List all build-time dependencies:
BuildRequires:  gcc
BuildRequires:  make
BuildRequires:  pkgconfig(libfoo) >= 1.2
BuildRequires:  cargo-rpm-macros >= 24
Use pkgconfig(name) for library dependencies when possible - it’s more portable.

Requires

Runtime dependencies are often auto-detected, but specify when needed:
Requires:       some-runtime-dependency
Requires:       python3-requests >= 2.20
For library packages:
Requires:       %{name}-libs%{?_isa} = %{version}-%{release}

Build Sections

%prep - Prepare Sources

%prep
%autosetup -n %{name}-%{version} -p1
For Rust packages:
%prep
%autosetup -n %{crate}-%{version}
%cargo_prep_online

%build - Compile

%build
%configure
%make_build

%install - Install Files

%install
%make_install

# Or for manual installation
install -Dm755 binary %{buildroot}%{_bindir}/binary
install -Dm644 config.conf %{buildroot}%{_sysconfdir}/package/config.conf
For Rust packages:
%install
install -Dm 755 target/rpm/%{name} -t %{buildroot}%{_bindir}

File Lists

Be specific about what files are included:
%files
%license LICENSE
%doc README.md CHANGELOG.md
%{_bindir}/package-name
%{_datadir}/package-name/
%config(noreplace) %{_sysconfdir}/package/config.conf
Never use overly broad wildcards like %{_bindir}/* - be explicit about files.

Common File Locations

  • %{_bindir} - /usr/bin - User executables
  • %{_sbindir} - /usr/sbin - System executables
  • %{_libdir} - /usr/lib64 (or /usr/lib) - Libraries
  • %{_datadir} - /usr/share - Architecture-independent data
  • %{_sysconfdir} - /etc - Configuration files
  • %{_mandir} - /usr/share/man - Man pages
  • %{_docdir} - /usr/share/doc - Documentation

Shell Completions

For tools with shell completions:
%build
mkdir -p completions
target/rpm/%{name} completion bash > completions/%{name}
target/rpm/%{name} completion fish > completions/%{name}.fish
target/rpm/%{name} completion zsh > completions/_%{name}

%install
install -Dpm 0644 completions/%{name} -t %{buildroot}%{bash_completions_dir}
install -Dpm 0644 completions/%{name}.fish -t %{buildroot}%{fish_completions_dir}
install -Dpm 0644 completions/_%{name} -t %{buildroot}%{zsh_completions_dir}

%files
%pkg_completion -Bfz %{name}

Scriptlets

Use scriptlets for system integration:
%post
%systemd_post package.service

%preun
%systemd_preun package.service

%postun
%systemd_postun_with_restart package.service

Changelog

Maintain an accurate changelog:
%changelog
* Thu Nov 20 2025 Your Name <[email protected]> - 1.2.3-1
- Update to version 1.2.3
- Add shell completions
- Fix build on aarch64

* Wed Oct 15 2025 Your Name <[email protected]> - 1.2.2-2
- Add missing runtime dependency

* Mon Oct 01 2025 Your Name <[email protected]> - 1.2.2-1
- Initial package
Use rpmdev-bumpspec to automatically add changelog entries with proper formatting.

Terra-Specific Macros

anda-srpm-macros

For Rust packages, use Terra’s macros:
BuildRequires:  anda-srpm-macros
BuildRequires:  cargo-rpm-macros >= 24
These provide:
  • %{crates_source} - Automatically construct crates.io URLs
  • Cargo build macros with Terra optimizations

Vendor Definition

Packages built in Terra CI include:
# Set automatically in CI builds
%define vendor Terra

Language-Specific Guidelines

Rust Packages

1

Use standard macros

BuildRequires:  cargo-rpm-macros >= 24
BuildRequires:  mold
BuildRequires:  anda-srpm-macros
2

Prepare with cargo

%prep
%autosetup -n %{crate}-%{version}
%cargo_prep_online
3

Handle licenses

%build
%cargo_build
%cargo_license_summary_online
%{cargo_license_online} > LICENSE.dependencies

%files
%license LICENSE.dependencies

Python Packages

BuildRequires:  python3-devel
BuildRequires:  python3-setuptools

%prep
%autosetup -n %{name}-%{version}

%build
%py3_build

%install
%py3_install

%files
%{python3_sitelib}/%{name}/
%{python3_sitelib}/%{name}-%{version}-py%{python3_version}.egg-info/

Go Packages

BuildRequires:  golang >= 1.20

%prep
%autosetup -n %{name}-%{version}

%build
export GOFLAGS="-mod=vendor"
go build -o %{name} .

%install
install -Dm755 %{name} %{buildroot}%{_bindir}/%{name}

Packaging Best Practices

Directory Organization

Organize packages by category:
anda/
├── tools/          # CLI tools and utilities
├── apps/           # Desktop applications
├── system/         # System packages and drivers
├── themes/         # Visual themes
├── fonts/          # Font packages
├── games/          # Gaming software
└── terra/          # Terra infrastructure

Naming Conventions

anda/tools/package-name/
├── anda.hcl
├── package-name.spec
└── update.rhai

Patch Management

Keep patches organized and documented:
Patch0:         fix-build-flags.patch
Patch1:         use-system-libraries.patch

%prep
%autosetup -n %{name}-%{version} -p1
Patch naming:
  • Descriptive names explaining what the patch does
  • Numbered sequentially (0001-fix.patch, 0002-feature.patch)
  • Include upstream PR/issue numbers when applicable

Security Considerations

Never package software with known critical vulnerabilities. Check upstream security advisories.
  • Keep packages updated with security fixes
  • Don’t disable security features (PIE, RELRO, stack protector)
  • Validate checksums for sources
  • Review code before packaging unknown software

Performance Optimization

Use mold Linker (Rust/C++)

BuildRequires:  mold

%build
%cargo_build
# mold is automatically used via macros

Enable Compiler Optimizations

%build
export CFLAGS="%{optflags}"
export CXXFLAGS="%{optflags}"
%configure
%make_build

Parallel Builds

%build
%make_build -j%{?_smp_mflags}

Testing Requirements

Before submitting packages:
1

Build successfully

anda build anda/category/package/pkg -c terra-frawhide-x86_64
2

Install and run

sudo dnf install anda-build/rpm/rpms/x86_64/package-*.rpm
package --version
# Test main functionality
3

Check rpmlint

rpmlint anda-build/rpm/rpms/x86_64/package-*.rpm
Address warnings and errors (some can be waived if justified).
4

Verify file ownership

rpm -qlp anda-build/rpm/rpms/x86_64/package-*.rpm
Ensure all files have proper locations and permissions.

Common Issues and Solutions

Ensure source URLs are correct and accessible:
spectool -g package.spec
Check that % and % macros are expanded correctly.
For some packages, disable debuginfo:
%global debug_package %{nil}
Only do this when necessary (e.g., pre-compiled binaries).
Update Cargo.lock or use %cargo_prep_online to fetch dependencies:
%prep
%autosetup -n %{crate}-%{version}
%cargo_prep_online
Add architecture-specific conditionals:
%ifarch x86_64
BuildRequires: some-x86-only-dep
%endif

%ifarch aarch64
BuildRequires: some-arm-only-dep
%endif

Review Checklist

Before submitting your package, verify:
  • Spec file has complete metadata (Name, Version, License, URL, Summary)
  • All sources have checksums or use trusted URLs
  • BuildRequires lists all build dependencies
  • License files are included in %files
  • Files are installed to standard locations
  • Package builds successfully on x86_64 and aarch64
  • Package installs and runs correctly
  • Changelog entry is present and formatted correctly
  • No unnecessary files are packaged
  • Update script (update.rhai) works if present

Additional Resources

Fedora Packaging Guidelines

Official Fedora packaging standards

RPM Packaging Guide

Comprehensive RPM packaging tutorial

Testing Guide

How to test your packages

Cargo RPM Macros

Rust packaging tools

Build docs developers (and LLMs) love