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

FieldPurposeExample
specWhere to find the spec{ type = "upstream" }, { type = "local", path = "mypackage.spec" }
spec.upstream-nameUpstream package name (if different)"redhat-rpm-config"
spec.upstream-distroPin to a specific distro/version{ name = "fedora", version = "rawhide" }
overlaysList of spec/source modificationsSee Overlays section
release.calculationRelease tag handling ("auto", "autorelease", "static", or "manual")"manual"
render.skip-file-filterKeep all source files during render (edge case)true
build.definesRPM macro overrides{ rhel = "11" }
build.withEnable build conditionals["feature_x"]
build.withoutDisable 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

TypeDoesKey fields
spec-add-tagAdd a new tag; fails if already existstag, value
spec-insert-tagInsert tag after the last tag of the same family (e.g., Source9999 after last Source*); falls back to after last tag of any kindtag, value
spec-set-tagSet tag value; replaces entire value if exists, adds if nottag, value
spec-update-tagReplace value of existing tag; fails if tag doesn't existtag, value
spec-remove-tagRemove a tag; fails if tag doesn't existtag, optionally value to match
spec-prepend-linesInsert at start of section bodysection, lines
spec-append-linesInsert at end of section bodysection, lines
spec-search-replaceRegex replace in spec (last resort)regex, replacement
patch-addCopy a patch file into sources and register it in the spec (PatchN or %patchlist)source, optionally file
patch-removeRemove patch files and their spec references matching a globfile
file-addAdd a file to sources rootfile, source
file-removeRemove a file from sourcesfile
file-renameRename a source filefile, replacement
file-prepend-linesPrepend lines to a filefile, lines
file-search-replaceRegex replace in source filesfile, 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:

TaskUse thisNOT this
Add a BuildRequires or Requiresspec-add-tagregex to insert a line
Add a Source tag alongside existing onesspec-insert-tag (e.g., tag = "Source9999")regex to find the last Source line
Change Version, Release, or Summaryspec-set-tagregex s/old/new/
Remove a specific dependencyspec-remove-tag with tag + valueregex to delete the line
Add commands at end of %installspec-append-lines with section = "%install"regex to find and insert after a line
Add entries to %filesspec-append-lines with section = "%files"regex to append after existing entries
Add env/export at start of %buildspec-prepend-lines with section = "%build"regex to insert before existing content
Add a patchpatch-add (auto-registers PatchN or %patchlist)manual spec-add-tag for PatchN + file-add
Remove a patchpatch-remove with glob (e.g., file = "CVE-*.patch")regex to delete PatchN line + file-remove
Target a sub-package's %filesspec-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 as overlays = [{ ... }, { ... }]. 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 $schema in TOML. $ is invalid at the start of a bare TOML key.
  • Scope regex overlays with section and package. When using spec-search-replace, always set section (e.g. "%files", "%install") and package (e.g. "foo" for a %files foo section) to limit where the regex matches if possible. The package value is the short sub-package suffix as it appears after the section tag in the spec (e.g. %files foopackage = "foo", not package = "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 %files section, both fields should be set.
  • No multi-line regex. spec-search-replace doesn't support (?s)/DOTALL. Use multiple single-line replacements.
  • No backreferences in spec-search-replace. ${1} or $1 in replacement is literal text, not a capture group backreference. Repeat the matched text in the replacement instead.
  • lines must be an array of strings. Use lines = ["single line"] for single-element lists, or a multi-line array for multiple elements (not a bare string like lines = "...").
    lines = [
        "line 1",
        "line 2",
    ]
    
  • file-add places 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 a spec-add-tag for the corresponding Source tag and a spec-append-lines in %install to install it. To modify files inside the extracted tree, use file-search-replace or add a sed command in %prep via spec-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. Use spec-search-replace as a last resort.
  • spec-prepend-lines and spec-append-lines operate within a section body. spec-prepend-lines inserts right after the section header (start of body); spec-append-lines inserts 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, use spec-append-lines with section = "%install" — do NOT use spec-prepend-lines with section = "%files" (that would put the lines inside %files).
  • Don't rename Name:. Changing the spec Name: tag causes cascading breakage (%{name} in Source0, %setup, paths, %files).
  • file-search-replace supports globs. Use file = "**/*" 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.toml has includes = ["**/*.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/autorelease modes, manual release 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 an azl_release define in [components.<name>.build.defines] or the value in a spec-set-tag overlay for Release.

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.