Skip to content

Commit 9c5cca3

Browse files
authored
feat: initial overlay support (#381)
* Extends TOML config file format with support for defining overlays, semantic patches for RPM spec files / sources. * Documentation for the overlay definition format has been added to `docs/reference/overlays.md`.
1 parent 773f3f6 commit 9c5cca3

19 files changed

Lines changed: 3482 additions & 11 deletions

docs/reference/overlays.md

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
# Overlays
2+
3+
Overlays are semantic patches that modify RPM spec files and other source files during component processing. They allow you to make targeted changes to upstream specs without maintaining full forks.
4+
5+
Overlays are defined within a component's configuration in your TOML config file and are applied in the order they appear. Each overlay specifies a type and the parameters needed to perform its modification.
6+
7+
> **Note:** Overlays are applied in sequence and modifications are non-atomic. If an overlay fails mid-way, previously applied changes remain. Work on copies if atomicity is required.
8+
9+
## Overlay Types
10+
11+
### Spec Overlays
12+
13+
These overlays modify `.spec` files using the structured spec parser, allowing precise targeting of tags and sections.
14+
15+
| Type | Description | Required Fields |
16+
|------|-------------|-----------------|
17+
| `spec-add-tag` | Adds a tag to the spec; **fails if the tag already exists** | `tag`, `value` |
18+
| `spec-set-tag` | Sets a tag value; replaces if exists, adds if not | `tag`, `value` |
19+
| `spec-update-tag` | Updates an existing tag; **fails if the tag doesn't exist** | `tag`, `value` |
20+
| `spec-remove-tag` | Removes a tag from the spec; **fails if the tag doesn't exist** | `tag` |
21+
| `spec-prepend-lines` | Prepends lines to the start of a section; **fails if section doesn't exist** | `lines` |
22+
| `spec-append-lines` | Appends lines to the end of a section; **fails if section doesn't exist** | `lines` |
23+
| `spec-search-replace` | Regex-based search and replace on spec content | `regex` |
24+
25+
### File Overlays
26+
27+
These overlays modify non-spec source files directly. They cannot be used on `.spec` files. These
28+
overlays are typically only used to modify loose files next to specs when standard patching mechanisms
29+
can't easily be used.
30+
31+
| Type | Description | Required Fields |
32+
|------|-------------|-----------------|
33+
| `file-prepend-lines` | Prepends lines to a file | `file`, `lines` |
34+
| `file-search-replace` | Regex-based search and replace on a file | `file`, `regex` |
35+
| `file-add` | Copies a new file from a source location | `file`, `source` |
36+
37+
## Field Reference
38+
39+
| Field | TOML Key | Description | Used By |
40+
|-------|----------|-------------|---------|
41+
| Type | `type` | **Required.** The overlay type to apply | All overlays |
42+
| Description | `description` | Human-readable explanation documenting the need for the change; helps identify overlays in error messages | All (optional) |
43+
| Tag | `tag` | The spec tag name (e.g., `BuildRequires`, `Requires`, `Version`) | `spec-add-tag`, `spec-set-tag`, `spec-update-tag`, `spec-remove-tag` |
44+
| Value | `value` | The tag value to set, or value to match for removal | `spec-add-tag`, `spec-set-tag`, `spec-update-tag`, `spec-remove-tag` (optional for matching) |
45+
| Section | `section` | The spec section to target (e.g., `%build`, `%install`, `%files`, `%description`) | `spec-prepend-lines`, `spec-append-lines`, `spec-search-replace` (optional) |
46+
| Package | `package` | The sub-package name for multi-package specs; omit to target the main package | All spec overlays (optional) |
47+
| Regex | `regex` | Regular expression pattern to match | `spec-search-replace`, `file-search-replace` |
48+
| Replacement | `replacement` | Literal replacement text; omit or leave empty to delete matched text | `spec-search-replace`, `file-search-replace` (optional) |
49+
| Lines | `lines` | Array of text lines to insert | `spec-prepend-lines`, `spec-append-lines`, `file-prepend-lines` |
50+
| File | `file` | The name of the non-spec file to modify or add | `file-prepend-lines`, `file-search-replace`, `file-add` |
51+
| Source | `source` | Path to source file for `file-add`; relative paths are relative to the config file | `file-add` |
52+
53+
## Examples
54+
55+
### Adding a Build Dependency
56+
57+
```toml
58+
[[components.mypackage.overlays]]
59+
type = "spec-add-tag"
60+
description = "Add missing build dependency"
61+
tag = "BuildRequires"
62+
value = "some-devel-package"
63+
```
64+
65+
### Setting a Version
66+
67+
Use `spec-set-tag` when you want to set a value regardless of whether the tag exists:
68+
69+
```toml
70+
[[components.mypackage.overlays]]
71+
type = "spec-set-tag"
72+
tag = "Version"
73+
value = "2.0.0"
74+
```
75+
76+
### Removing a Dependency
77+
78+
Remove a specific tag by matching both the tag name and value:
79+
80+
```toml
81+
[[components.mypackage.overlays]]
82+
type = "spec-remove-tag"
83+
description = "Remove problematic dependency"
84+
tag = "BuildRequires"
85+
value = "unwanted-package"
86+
```
87+
88+
### Appending Lines to a Section
89+
90+
```toml
91+
[[components.mypackage.overlays]]
92+
type = "spec-append-lines"
93+
section = "%install"
94+
lines = [
95+
"mkdir -p %{buildroot}%{_datadir}/mypackage",
96+
"install -m 644 extra.conf %{buildroot}%{_datadir}/mypackage/"
97+
]
98+
```
99+
100+
### Prepending Lines to a Section
101+
102+
```toml
103+
[[components.mypackage.overlays]]
104+
type = "spec-prepend-lines"
105+
section = "%build"
106+
lines = ["export CFLAGS=\"$CFLAGS -DEXTRA_FLAG\""]
107+
```
108+
109+
### Search and Replace in Spec
110+
111+
> **Tip:** The regex must match at least once or an error is raised. This prevents silent no-ops from typos or upstream changes.
112+
113+
```toml
114+
[[components.mypackage.overlays]]
115+
type = "spec-search-replace"
116+
description = "Remove unwanted configure flag"
117+
section = "%build"
118+
regex = "--enable-deprecated-feature\\s*"
119+
replacement = ""
120+
```
121+
122+
### Targeting a Sub-Package
123+
124+
For multi-package specs, use the `package` field to target a specific sub-package:
125+
126+
```toml
127+
[[components.mypackage.overlays]]
128+
type = "spec-append-lines"
129+
section = "%files"
130+
package = "devel"
131+
lines = ["%{_includedir}/mypackage/*.h"]
132+
```
133+
134+
```toml
135+
[[components.mypackage.overlays]]
136+
type = "spec-set-tag"
137+
package = "libs"
138+
tag = "Summary"
139+
value = "Shared libraries for mypackage"
140+
```
141+
142+
### Prepending Lines to a Non-Spec File
143+
144+
```toml
145+
[[components.mypackage.overlays]]
146+
type = "file-prepend-lines"
147+
file = "Makefile"
148+
lines = ["# Modified by azldev overlay", "EXTRA_FLAGS := -O2"]
149+
```
150+
151+
### Search and Replace in a File
152+
153+
```toml
154+
[[components.mypackage.overlays]]
155+
type = "file-search-replace"
156+
file = "configure.ac"
157+
regex = "AC_INIT\\(\\[mypackage\\],\\s*\\[\\d+\\.\\d+\\]"
158+
replacement = "AC_INIT([mypackage], [2.0]"
159+
description = "Update version in configure.ac"
160+
```
161+
162+
### Adding a New File
163+
164+
The `source` path is relative to the config file that defines the overlay:
165+
166+
```toml
167+
[[components.mypackage.overlays]]
168+
type = "file-add"
169+
file = "extra-config.conf"
170+
source = "files/mypackage/extra-config.conf"
171+
description = "Add custom configuration file"
172+
```
173+
174+
## Validation
175+
176+
Overlay configurations are validated when the config file is loaded. Validation checks:
177+
178+
- Required fields are present for each overlay type
179+
- Regex patterns compile successfully
180+
- Error messages include the `description` field (if provided) to help identify which overlay failed
181+
182+
> **Tip:** Always provide a `description` for overlays to make debugging easier when validation or application fails.
183+
184+
## Related Resources
185+
186+
- [JSON Schema](../../schemas/azldev.schema.json) — Use with editors that support JSON Schema for TOML to get validation and auto-completion
187+
- Component configuration is defined in the `[[components.<name>.overlays]]` array within your project's TOML config files

internal/app/azldev/cmds/component/build.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ func BuildComponent(
171171
return nil
172172
}, &err)
173173

174-
sourcePreparer, err := sources.NewPreparer(sourceManager, env.FS())
174+
sourcePreparer, err := sources.NewPreparer(sourceManager, env.FS(), env, env)
175175
if err != nil {
176176
return ComponentBuildResults{},
177177
fmt.Errorf("failed to create source preparer for component %q:\n%w", component.GetName(), err)

internal/app/azldev/cmds/component/preparesources.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ func PrepareComponentSources(env *azldev.Env, options *PrepareSourcesOptions) er
8484
return fmt.Errorf("failed to create source manager:\n%w", err)
8585
}
8686

87-
preparer, err := sources.NewPreparer(sourceManager, env.FS())
87+
preparer, err := sources.NewPreparer(sourceManager, env.FS(), env, env)
8888
if err != nil {
8989
return fmt.Errorf("failed to create source preparer:\n%w", err)
9090
}

internal/app/azldev/core/componentbuilder/componentbuilder_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func setupBuilder(t *testing.T) *componentBuilderTestParams {
4949

5050
sourceManager := sourceproviders_test.NewNoOpMockSourceManager(ctrl)
5151

52-
preparer, err := sources.NewPreparer(sourceManager, testEnv.Env.FS())
52+
preparer, err := sources.NewPreparer(sourceManager, testEnv.Env.FS(), testEnv.Env, testEnv.Env)
5353

5454
require.NoError(t, err)
5555

0 commit comments

Comments
 (0)