Documentation Index Fetch the complete documentation index at: https://mintlify.com/terrapkg/packages/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Terra uses Anda as its build system, which provides automation for building RPM packages from the monorepo. The build system includes:
anda.hcl - Package configuration files
andax/ - Automation scripts written in Rhai
CI/CD integration - GitHub Actions workflows
Mock - Isolated build environments
Build Process Flow
Anda Command Line Interface
Building Packages
Build Single Package
Build with Mock
Build for Specific Architecture
Build All Changed
# Build a specific package
cd anda/apps/discord
anda build
Testing Builds
# Install built package locally
sudo dnf install ./anda-build/RPMS/x86_64/discord- * .rpm
# Test in clean container
podman run -it -v $( pwd ) :/work:z fedora:rawhide
dnf install /work/anda-build/RPMS/x86_64/discord- * .rpm
Andax Scripts
The andax/ directory contains Rhai scripts that extend Anda’s functionality.
spec.rhai
Utility functions for parsing RPM spec files:
fn get_version () {
return `(?m)^Version: \s *(.+)$` . find ( this . f , 1 );
}
fn get_release () {
let r = `(?m)^Release: \s *(.+)$` . find ( this . f , 1 );
r = sub ( `(?m)(% \? ?dist|% \{\? ?dist \} ) \s *$` , "" , r );
r . replace ( "%autorelease" , "1" );
return r ;
}
fn get_global ( macro ) {
return `(?m)^%global \s + ${ macro } \s +(.+)$` . find ( this . f , 1 );
}
fn get_define ( macro ) {
return `(?m)^%define \s + ${ macro } \s +(.+)$` . find ( this . f , 1 );
}
Usage:
import "spec" as rpm ;
let spec_file = read_file ( "package.spec" );
let version = rpm :: get_version ( spec_file );
let release = rpm :: get_release ( spec_file );
nvidia.rhai
Functions for fetching NVIDIA driver and CUDA component versions:
fn nvidia_component_list () {
let url = "https://developer.download.nvidia.com/compute/cuda/redist/" ;
let matches = find_all ( "redistrib_[ \\ d.]+.json" , get ( url ));
let series = ` ${ url }${ matches [ matches . len - 1 ][ 0 ] } ` ;
return get ( series ). json ();
}
fn nvidia_component_version ( component ) {
let components = nvidia_component_list ();
return components [ component ][ "version" ];
}
fn nvidia_driver_version () {
let driver = get ( "https://gfwsl.geforce.com/services_toolkit/services/com/nvidia/services/AjaxDriverService.php?func=DriverManualLookup&osID=12&languageCode=1033&numberOfResults=1&beta=0" )
. json (). IDS [ 0 ]. downloadInfo . DisplayVersion ;
return ( driver );
}
fn nvidia_legacy_version () {
let driver = get ( "https://gfwsl.geforce.com/services_toolkit/services/com/nvidia/services/AjaxDriverService.php?func=DriverManualLookup&osID=12&languageCode=1033&numberOfResults=1&beta=0&release=580" )
. json (). IDS [ 0 ]. downloadInfo . DisplayVersion ;
return ( driver );
}
Usage:
import "nvidia" as nv ;
let driver_ver = nv :: nvidia_driver_version ();
let cuda_ver = nv :: nvidia_component_version ( "cuda_cudart" );
print ( `Latest NVIDIA driver: ${ driver_ver } ` );
Helper functions for version tracking across distributions:
// Get latest version from AlmaLinux repos
fn alma ( pkg , repo , branch ) {
let vr = alma_vr ( pkg , repo , branch );
return ( vr [ 1 ]);
}
// Get latest version from Fedora Bodhi
fn bodhi ( pkg , branch ) {
let url = `https://bodhi.fedoraproject.org/updates/?search= ${ pkg } &status=stable&releases= ${ branch } &rows_per_page=1&page=1` ;
for entry in get ( url ). json (). updates [ 0 ]. title . split ( ' ' ) {
let matches = find_all ( ` ${ pkg } -([ \\ d.]+)-( \\ d+) \\ .[ \\ w \\ d]+$` , entry );
if matches . len () != 0 {
return matches [ 0 ][ 1 ];
}
}
}
// Get latest version from Terra repos
fn madoguchi ( pkg , branch ) {
return madoguchi_json ( pkg , branch ). ver ;
}
// Get latest release from Codeberg
fn codeberg ( repo ) {
return get ( `https://codeberg.org/api/v1/repos/ ${ repo } /releases/latest` ). json (). tag_name ;
}
Usage:
import "bump_extras" as bump ;
// Check Fedora version
let fedora_ver = bump :: bodhi ( "firefox" , "F41" );
// Check Codeberg release
let release = bump :: codeberg ( "owner/repo" );
get_proj_label.rhai
Extract labels from anda.hcl files:
import "anda::cfg" as cfg ;
print ( cfg :: load_file ( labels . project ). project . pkg . labels . to_json ());
Usage in CI:
# Get subrepo label for a package
anda run andax/get_proj_label.rhai --project anda/apps/discord/anda.hcl
CI Scripts
update_commit_message.rhai
Generates commit messages for automated updates:
update_commit_message.rhai
import "anda::cfg" as cfg ;
let cmd = `git status | sed -nE '/^ \t modified:/{s@^ \t modified: \s +@@;s@[^/]+$@@;p}' | sort -u` ;
let filelist = sh ( cmd , #{ "stdout" : "piped" }). ctx . stdout . split ( ' \n ' );
let modified_list = "" ;
for file in filelist {
if file . is_empty () { continue ; }
let spec = cfg :: load_file ( ` ${ file } /anda.hcl` ). project . pkg . rpm . spec ;
spec . pop ( 5 ); // remove `.spec` suffix
modified_list += ` ${ spec } ` ;
}
print ( modified_list [.. modified_list . len () - 1 ]);
Installs extra repositories during mock builds:
import "anda::cfg" as cfg ;
fn install ( labels ) {
if labels . script_path == () {
print ( "fatal: labels.script_path is empty" );
terminate ();
}
let releasever = sh ( "rpm -E '%fedora'" , #{ "stdout" : "piped" }). ctx . stdout ;
releasever . trim ();
let basearch = sh ( "rpm -E '%_arch'" , #{ "stdout" : "piped" }). ctx . stdout ;
basearch . trim ();
let hcl = cfg :: load_file ( sub ( `(.+/)[^.]+ \\ .rhai` , "${1}anda.hcl" , labels . script_path ));
for repo in hcl . project . pkg . rpm . extra_repos {
repo = sub ( ` \\ $releasever` , releasever , repo );
repo = sub ( ` \\ $basearch` , basearch , repo );
let filename = sub ( ` \\ W` , "_" , repo );
let file = open_file ( `/etc/yum.repos.d/ ${ filename } .repo` );
file . write ( `
[filename]
name= ${ filename }
baseurl= ${ repo }
enabled=1
gpgcheck=0
` );
}
}
Mock Build Environment
Mock provides isolated build environments that prevent dependency pollution.
When to Use Mock
Enable mock builds (labels.mock = 1) when:
Package requires extra_repos
Build has complex dependency chains
Need guaranteed clean environment
Package is security-sensitive
Mock Configuration
Mock environments are configured per Fedora version:
# Default mock config
/etc/mock/fedora-rawhide-x86_64.cfg
# With Terra repos
/etc/mock/terra-rawhide-x86_64.cfg
Mock Build Process
Build Artifacts
Directory Structure
package-directory/
├── anda.hcl
├── package.spec
├── patch1.patch
├── source1.conf
└── anda-build/
├── BUILD/ # Extracted source
├── BUILDROOT/ # %install destination
├── RPMS/
│ └── x86_64/
│ └── package-1.0-1.fc42.x86_64.rpm
├── SOURCES/ # Source files
├── SPECS/ # Spec file
└── SRPMS/ # Source RPM
└── package-1.0-1.fc42.src.rpm
Build Logs
# View build log
less anda-build/build.log
# Check for errors
grep -i error anda-build/build.log
# View mock logs
less /var/lib/mock/fedora-rawhide-x86_64/result/build.log
CI/CD Integration
GitHub Actions Workflow
Terra uses GitHub Actions for automated builds:
name : Build Packages
on :
push :
branches : [ main ]
pull_request :
jobs :
build :
runs-on : ubuntu-latest
container : ghcr.io/terrapkg/builder:latest
steps :
- uses : actions/checkout@v4
- name : Build changed packages
run : |
anda build --changed
- name : Upload artifacts
uses : actions/upload-artifact@v4
with :
name : rpms
path : "**/anda-build/RPMS/**/*.rpm"
Build Matrix
Builds run across multiple Fedora versions:
strategy :
matrix :
fedora : [ 40 , 41 , 42 , rawhide ]
arch : [ x86_64 , aarch64 ]
Troubleshooting
Build fails with missing dependencies
Solution 1: Add to BuildRequires in spec fileBuildRequires: missing-package-devel
Solution 2: Add extra repository to anda.hclrpm {
spec = "package.spec"
extra_repos = [ "https://repo.example.com/fedora/ \\ $releasever/ \\ $basearch" ]
}
Mock build fails but direct build works
Check that all dependencies are properly declared: # List actual dependencies
rpm -qpR package.rpm
# Compare with spec file
grep "^Requires:" package.spec
Missing BuildRequires often cause mock-only failures.
Build succeeds but package doesn't install
Check file conflicts: # List package files
rpm -qpl package.rpm
# Check for conflicts with installed packages
rpm -qf /conflicting/file
May need Conflicts: or Obsoletes: in spec.
For large packages: Parallelize builds: %build
%cmake_build -j%{_smp_build_ncpus}
Advanced Topics
Custom Build Scripts
Create custom Rhai scripts for specialized workflows:
// custom_update.rhai
import "nvidia" as nv ;
import "spec" as rpm ;
// Get latest NVIDIA version
let latest = nv :: nvidia_driver_version ();
// Update spec file
let spec = read_file ( "nvidia.spec" );
spec = spec . replace ( rpm :: get_version ( spec ), latest );
write_file ( "nvidia.spec" , spec );
print ( `Updated to ${ latest } ` );
Multi-Stage Builds
Some packages require building dependencies first:
#!/bin/bash
# Build dependency chain
cd anda/lib/library-a
anda build
sudo dnf install ./anda-build/RPMS/x86_64/library-a- * .rpm
cd ../library-b
anda build
sudo dnf install ./anda-build/RPMS/x86_64/library-b- * .rpm
cd ../../apps/application
anda build
Cross-Architecture Builds
# Build for ARM on x86_64
anda build --arch aarch64
# Requires qemu-user-static
sudo dnf install qemu-user-static
Build System Architecture
Component Diagram
Caching
# Enable ccache for C/C++ builds
export PATH = "/usr/lib64/ccache: $PATH "
# Enable sccache for Rust builds
export RUSTC_WRAPPER = sccache
Parallel Builds
# In spec file %build section
%cmake_build -j%{_smp_build_ncpus}
# Or for make
make -j%{_smp_build_ncpus}
Mock Optimizations
# Keep mock root between builds
anda build --mock --no-clean
# Use tmpfs for faster builds
mock --enable-plugin=tmpfs