Component Definition Files (*.comp.toml)
May 22, 2026 · View on GitHub
Component definitions tell azldev where to find a spec and how to customize it. Schema: azldev.schema.json (or azldev config generate-schema for latest).
Structure
Every component lives under [components.<name>]. A bare entry inherits defaults from the distro config:
[components.curl]
Key fields
| Field | Purpose | Example |
|---|---|---|
spec | Where to find the spec | { type = "upstream" }, { type = "local", path = "mypackage.spec" } |
spec.upstream-name | Upstream package name (if different) | "redhat-rpm-config" |
spec.upstream-distro | Pin to a specific distro/version | { name = "fedora", version = "rawhide" } |
overlays | List of spec/source modifications | See Overlays section |
release.calculation | Release tag handling ("auto", "autorelease", "static", or "manual") | "manual" |
render.skip-file-filter | Keep all source files during render (edge case) | true |
build.defines | RPM macro overrides | { rhel = "11" } |
build.with | Enable build conditionals | ["feature_x"] |
build.without | Disable build conditionals | ["plugin_rhsm"] |
Spec Source Types
# Upstream (default) — inherits distro's Fedora version
[components.curl]
# Upstream — pinned version
[components.curl]
spec = { type = "upstream", upstream-distro = { name = "fedora", version = "rawhide" } }
# Upstream — different package name
[components.azurelinux-rpm-config]
spec = { type = "upstream", upstream-name = "redhat-rpm-config" }
# Local spec (Azure Linux-originating package)
[components.azurelinux-release]
spec = { type = "local", path = "azurelinux-release.spec" }
Overlays
Overlays modify upstream specs/sources without forking. Every overlay MUST have a description field explaining why the change is needed.
TOML syntax
Use array-of-tables (multi-line) for overlays — one [[...overlays]] block per overlay:
# Targeted type (preferred) — more robust to upstream changes
[[components.curl.overlays]]
description = "Add missing build dependency for Azure Linux"
type = "spec-add-tag"
tag = "BuildRequires"
value = "golang >= 1.21"
# Regex type (last resort) — brittle if upstream changes the line
[[components.rpm.overlays]]
description = "Customize RPM vendor"
type = "spec-search-replace"
regex = "RPM_VENDOR=redhat"
replacement = "RPM_VENDOR=azurelinux"
Overlay types
| Type | Does | Key fields |
|---|---|---|
spec-add-tag | Add a new tag; fails if already exists | tag, value |
spec-insert-tag | Insert tag after the last tag of the same family (e.g., Source9999 after last Source*); falls back to after last tag of any kind | tag, value |
spec-set-tag | Set tag value; replaces entire value if exists, adds if not | tag, value |
spec-update-tag | Replace value of existing tag; fails if tag doesn't exist | tag, value |
spec-remove-tag | Remove a tag; fails if tag doesn't exist | tag, optionally value to match |
spec-prepend-lines | Insert at start of section body | section, lines |
spec-append-lines | Insert at end of section body | section, lines |
spec-search-replace | Regex replace in spec (last resort) | regex, replacement |
patch-add | Copy a patch file into sources and register it in the spec (PatchN or %patchlist) | source, optionally file |
patch-remove | Remove patch files and their spec references matching a glob | file |
file-add | Add a file to sources root | file, source |
file-remove | Remove a file from sources | file |
file-rename | Rename a source file | file, replacement |
file-prepend-lines | Prepend lines to a file | file, lines |
file-search-replace | Regex replace in source files | file, regex, replacement |
Optional fields that apply to multiple types: section (target spec section), package (target sub-package).
Up-to-date details can be found from the azldev schema command, or by inspecting azldev.schema.json.
Choosing the right overlay type (avoiding regex)
spec-search-replace is fragile — it breaks when upstream changes the matched text. Before reaching for regex, check if a targeted type can do the job:
| Task | Use this | NOT this |
|---|---|---|
Add a BuildRequires or Requires | spec-add-tag | regex to insert a line |
Add a Source tag alongside existing ones | spec-insert-tag (e.g., tag = "Source9999") | regex to find the last Source line |
Change Version, Release, or Summary | spec-set-tag | regex s/old/new/ |
| Remove a specific dependency | spec-remove-tag with tag + value | regex to delete the line |
Add commands at end of %install | spec-append-lines with section = "%install" | regex to find and insert after a line |
Add entries to %files | spec-append-lines with section = "%files" | regex to append after existing entries |
Add env/export at start of %build | spec-prepend-lines with section = "%build" | regex to insert before existing content |
| Add a patch | patch-add (auto-registers PatchN or %patchlist) | manual spec-add-tag for PatchN + file-add |
| Remove a patch | patch-remove with glob (e.g., file = "CVE-*.patch") | regex to delete PatchN line + file-remove |
Target a sub-package's %files | spec-append-lines with section = "%files", package = "devel" | regex scoped to a section |
When regex IS appropriate: modifying arbitrary text mid-section (e.g., changing a configure flag, replacing a variable value, removing a conditional block). Even then, always scope with section and package to limit the blast radius.
Overlay pitfalls
- Do NOT use inline array syntax for overlays. Write each overlay as a separate
[[components.<name>.overlays]]block (array-of-tables), not asoverlays = [{ ... }, { ... }]. The inline form is valid TOML but harder to read and review. Some older components in the repo use the inline style — don't copy it. - No
$schemain TOML.$is invalid at the start of a bare TOML key. - Scope regex overlays with
sectionandpackage. When usingspec-search-replace, always setsection(e.g."%files","%install") andpackage(e.g."foo"for a%files foosection) to limit where the regex matches if possible. Thepackagevalue is the short sub-package suffix as it appears after the section tag in the spec (e.g.%files foo→package = "foo", notpackage = "mypkg-foo"). Unscoped regex overlays risk matching unintended lines elsewhere in the spec, especially after upstream updates. If the overlay targets a specific sub-package's%filessection, both fields should be set. - No multi-line regex.
spec-search-replacedoesn't support(?s)/DOTALL. Use multiple single-line replacements. - No backreferences in
spec-search-replace.${1}or$1inreplacementis literal text, not a capture group backreference. Repeat the matched text in the replacement instead. linesmust be an array of strings. Uselines = ["single line"]for single-element lists, or a multi-line array for multiple elements (not a bare string likelines = "...").lines = [ "line 1", "line 2", ]file-addplaces files at the sources root, alongside the tarball and other Source files — NOT inside the extracted source tree. To install the added file, also add aspec-add-tagfor the correspondingSourcetag and aspec-append-linesin%installto install it. To modify files inside the extracted tree, usefile-search-replaceor add asedcommand in%prepviaspec-append-lines.- Use TOML literal strings for regex.
regex = 'RPM_VENDOR=redhat'avoids double-escaping backslashes. - Prefer multi-line format for TOML arrays. When a list field (
lines,with,without, etc.) has 2+ elements, split it across multiple lines with a trailing comma for readability:lines = [ "# Comment explaining the change", "rm -f broken_test", ] - Prefer targeted types over regex.
spec-add-tag,spec-set-tag,spec-prepend-lines, etc. are more robust to upstream changes. Usespec-search-replaceas a last resort. spec-prepend-linesandspec-append-linesoperate within a section body.spec-prepend-linesinserts right after the section header (start of body);spec-append-linesinserts at the end of the section body (before the next section). Neither inserts outside the section boundary. For example, to add install commands at the end of%install, usespec-append-lineswithsection = "%install"— do NOT usespec-prepend-lineswithsection = "%files"(that would put the lines inside%files).- Don't rename
Name:. Changing the specName:tag causes cascading breakage (%{name}in Source0,%setup, paths,%files). file-search-replacesupports globs. Usefile = "**/*"to replace across all source files.
File Organization
- Inline (in
components.toml): for simple imports with no customization ([components.jq]) - Dedicated (
<name>/<name>.comp.toml): when overlays, build config, or local spec are needed - Rule of thumb: if it's more than
[components.<name>], give it a dedicated file components.tomlhasincludes = ["**/*.comp.toml"]— dedicated files are picked up automatically
Build Configuration
# Override RPM macros
[components.wget2.build]
defines = { rhel = "11" }
# Enable build conditionals
[components.mypackage.build]
with = ["feature_x"]
# Disable build conditionals
[components.dnf5.build]
without = ["plugin_rhsm"]
Release Configuration
By default (release.calculation = "auto"), azldev auto-calculates the Release tag during rendering. There are four modes:
auto (default)
Auto-detects whether the spec uses %autorelease or a static release value and handles it accordingly. Works for most packages.
autorelease
Forces azldev to treat the spec as using %autorelease, preserving the %autorelease macro in the rendered spec. Use this when auto-detection gets it wrong — typically when the spec wraps %autorelease in a conditional:
%if %{defined autorelease}
Release: %autorelease
%else
Release: 1
%endif
In this pattern, auto-detection may see the conditional and misidentify the release mode, expanding %autorelease to a hardcoded integer. Setting calculation = "autorelease" forces correct behavior:
[components.gvisor-tap-vsock]
# Upstream spec uses conditional %autorelease — auto-detection misidentifies it
[components.gvisor-tap-vsock.release]
calculation = "autorelease"
When to use autorelease: When render produces a hardcoded integer where %autorelease should be, or when the spec has conditional %autorelease logic that confuses auto-detection.
static
Forces azldev to treat the spec as using a static (hardcoded) release value, even if auto-detection thinks it uses %autorelease. Uses a regex to match release patterns like 5.%{dist} and bumps the integer component automatically during rendering. The inverse of autorelease — use when auto-detection incorrectly identifies a static release as %autorelease.
[components.mypackage.release]
calculation = "static"
When to use static: When auto-detection misidentifies a static release as %autorelease and produces incorrect rendering (e.g., inserts %autorelease where a hardcoded integer release should be).
manual
Some upstream specs use non-standard Release tag values (e.g., %{baserelease}%{?dist}, %{pkg_release}) that the auto-calculator can't parse. These fail with:
non-standard Release tag value ... does not start with an integer
Fix: set release.calculation = "manual" and add a spec-set-tag overlay to set a concrete Release value:
[components.gcc]
release = { calculation = "manual" }
[[components.gcc.overlays]]
description = "Set explicit Release tag — upstream uses %{gcc_release} macro which azldev cannot auto-calculate"
type = "spec-set-tag"
tag = "Release"
value = "1%{?dist}"
When to use manual: Only when render fails with the "non-standard Release tag" error. Don't preemptively set it — most packages work fine with auto.
When using manual: You take ownership of bumping the Release value when needed (e.g., rebuild without a version change). The auto-calculator will not touch it.
Pitfall — modifying a component with manual release: Unlike
auto/static/autoreleasemodes,manualrelease components do NOT get their Release bumped by the commit-render-amend cycle. When you change a manual-release component (add/remove build flags, modify overlays, etc.), you must also increment the release counter in the same change — typically anazl_releasedefine in[components.<name>.build.defines]or thevaluein aspec-set-tagoverlay forRelease.
Render Configuration
The render section controls spec rendering behavior. Currently has one field:
[components.mypackage.render]
skip-file-filter = true
skip-file-filter disables post-render file filtering. During rendering, azldev normally prunes source/patch files not referenced by the rendered spec. If a spec uses unexpandable macros in Source or Patch tags (e.g., Source0: %{name}-%{version}%{?prerelease}.tar.gz), the filter can't resolve the filename and may incorrectly remove needed files. Setting skip-file-filter = true keeps all files. The tool auto-detects unexpandable macros and handles them correctly in the vast majority of cases - this is an edge case escape hatch that should almost never be needed in practice.
Adding Description Comments
When adding or modifying fields in a .comp.toml file, check the schema (external/schemas/azldev.schema.json) for a description on that field. If the schema field does not have a description key (or equivalent, i.e. skip_reason for check skip), add a TOML comment above the field explaining what it does and why it's set to that value. This helps future readers (human and agent) understand fields that aren't self-documenting via the schema.
BUT... don't add pointless noise - if the change is self-explanatory and requires no additional context, a comment may not be necessary. Use your judgement, but when in doubt, add a comment.
DO:
# Pin to Fedora rawhide since we need feature 'x' that is not in stable releases yet.
# Re-align to a stable release once the feature is backported or a new release is available.
spec = { type = "upstream", upstream-distro = { name = "fedora", version = "rawhide" } }
Offers important context about why the component is pinned to rawhide, which is not obvious from the TOML field alone. Future readers will understand the rationale and know to check for backports or new releases to remove the rawhide pin.
DO NOT:
# Need new stuff.
spec = { type = "upstream", upstream-distro = { name = "fedora", version = "rawhide" } }
This offers no real information beyond what the TOML field already says, and doesn't explain why the new stuff is needed, or what that new stuff is. It's just noise.
References
When making changes based on external information (e.g. a bug report, an upstream commit, a changelog entry, etc.), include a link to the relevant source in a comment (prefer full URL for ease of use). This provides valuable context for future readers to understand the reasoning behind the change and investigate further if needed. It also makes it easier to determine if the change is still relevant or if there have been updates since.
# ...
# Fixed upstream in my-package-1.2.3 (rawhide): https://src.fedoraproject.org/rpms/my-package/c/abcdef123456 (rhbz#1234567)
[[components.mypackage.overlays]]
description = "Fix build failure due to missing dependency that was added in my-package-1.2.3"
...
Validation
Verify overlays apply cleanly with azldev comp prep-sources before committing. See skills skill-build-component and skill-fix-overlay for step-by-step workflows.