Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,8 @@ The TOML config files in `defaultconfigs/` are loaded via `internal/projectconfi
**IMPORTANT**: Code generation runs automatically with build/test commands. `mage generate` (runs `go generate` for each package in parallel) is a prerequisite for building and runs automatically with `mage build` and `mage unit`. `mage docs` rebuilds the binary and updates the JSON schema (`schemas/azldev.schema.json`) and CLI docs (`docs/user/reference/cli/`). Run `mage docs` explicitly after changing config structs or Cobra command descriptions so that checked-in generated files stay current (checked by PR gates).

**CRITICAL**: Run `mage scenarioUpdate` when test expectations change (updates snapshots).
Schema changes affect both `TestSnapshots` and `TestSnapshotsContainer` baselines.
If Docker is unavailable, the update can refresh only local snapshots before failing;
check both baselines against the generated schema and report the incomplete container run.

Follow conventional commit format for all changes AND PR titles. A github action will fail the PR if the title does not adhere to the format.
3 changes: 3 additions & 0 deletions .github/instructions/go.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ description: "Instructions for working on the azldev Go codebase. IMPORTANT: Alw

## Component Fingerprinting — `fingerprint:"-"` Tags

New component fields must survive `ComponentConfig.WithAbsolutePaths` without aliasing
mutable data. Its automatic clone deliberately shares only config-file back-references.

Structs in `internal/projectconfig/` are hashed by `hashstructure.Hash()` to detect component changes. Fields **included by default** (safe: false positive > false negative).

When adding a new field to a fingerprinted struct, ask: **"Does changing this field change the build output?"**
Expand Down
155 changes: 155 additions & 0 deletions docs/user/how-to/vendor-go-application.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# Vendor a Go application

Use `go-vendor` to bundle a Go application's module dependencies instead of
building against separately packaged `golang(...)` libraries. This is a targeted
conversion of the selected upstream spec, not a `go2rpm` wrapper or a newly
generated replacement spec. The same setting regenerates sources for supported
specs that already use go-vendor-tools.

## Enable and prepare

Add this to the component definition:

```toml
[components.example.go-vendor]
enabled = true
```

For custom vendor settings, use a separate file beside the component definition:

```toml
# example.comp.toml
[components.example.go-vendor]
enabled = true
config-file = "go-vendor-tools.toml"
```

```toml
# go-vendor-tools.toml
[archive]
use_module_proxy = true

[licensing]
detector = "askalono"
```

This file replaces, rather than merges with, upstream settings. Carry forward
any required upstream license exceptions. Omit `config-file` to reuse those
settings unchanged. Paths are relative to the declaring component configuration,
including when inherited from defaults; no adjacent file is loaded implicitly.
File-only edits affect the component fingerprint. Inline `tool-config` values
are no longer accepted.

Then prepare the complete build inputs:

```sh
azldev component prepare-sources -p example -o prepared-example --without-git
```

The project/build distro must have a working mock configuration and provide
`go-vendor-tools`, `askalono-cli`, `golang`, `go-rpm-macros`, `rpm-build`, and
`rpmdevtools`, `rpmautospec`, `git`, and `python3-click`. These are installed in a
fresh, network-enabled generation chroot;
neither `go2rpm` nor the Go vendor tools are required on the host. Configure
`prep-packages` for additional tools required by upstream prep scripts.

The pipeline fetches the selected upstream sources, applies overlays, and copies
them into isolated staging. It processes automatic release/changelog metadata
there and runs the existing `%prep` section with `rpmbuild -bp --nodeps`.
This retains upstream patches and edits to module files. It then calls
`go_vendor_archive create` on the prepared module tree, extracts the result for
license detection, and runs `go_vendor_license` without interactive prompts.
The archive tool runs from staged dist-git, so hooks can find preserved patches
and sidecars through `GVT_CWD`.
Unrecognized licenses fail preparation; add reviewed, hash-qualified exceptions
to the file selected by `config-file` rather than suppressing detection.

The output includes the converted spec, all its original sources and patches,
`<component>-vendor.tar.bz2`, and `azldev-go-vendor.toml`. The archive is recorded
with SHA-512 in `sources` and excluded from synthetic git commits with `.gitignore`.
The upstream release/changelog is retained; ordinary azldev release handling
still applies afterwards.

## What is converted

The converter removes standalone `BuildRequires: golang(import/path)` tags,
including version constraints, and bare `%go_generate_buildrequires` invocations.
It changes a bare `%gocheck` to `%gotest ./...`, adds vendor extraction at the end
of `%prep`, and enables module/vendor mode for subsequent build scripts.
It adds the Go compiler/macros and vendor tooling build requirements, dynamic
license backend requirements, license installation/file-list handling, and a
license check in `%build` so disabling `%check` does not bypass license validation.
The main `License:` tag is updated to the detected cumulative SPDX expression.

Unrelated runtime dependencies, C-library build dependencies, custom commands,
patches, metadata, and ordinary subpackages are retained. Go vendoring does not
eliminate dependencies on shared libraries used by cgo.

The initial supported scope is application specs with one unconditional
main-package `%prep`, `%build`, `%install`, and `%files` section. The prep section
must finish in the module root, or use `module-dir` for a relative subdirectory.
Custom build/install commands must already address that same module. Conditional
commands within those sections are retained.

Automatic Go development subpackages (`%gopkg`, `%goinstall`, Go file-list macros),
flagged `%gocheck` commands, compound/rich Go BuildRequires,
explicit conflicting module-mode settings, and ambiguous/conditional
target sections are rejected. Use narrowly scoped overlays to adapt these cases;
azldev does not silently drop subpackages or rewrite arbitrary shell scripts.

## Regenerate an existing vendor archive

For an already-vendored spec, azldev recognizes a single
`tar -xf %{S:N}` (or `%{SOURCEN}`) prep command and the configuration source
referenced by `%go_vendor_license_* -c %{S:M}` commands. The Source numbers need
not be 1 and 2. The configuration must name a plain sidecar file.

The original Source numbers, license/file-list macros, module-mode settings, and
custom packaging are retained. The vendor extraction moves to the end of `%prep`,
after module-file patches and edits, because the replacement archive already
contains their results. Directory changes after the original extraction and
secondary-source extraction with `%setup -a/-b` or `%autosetup -a/-b` are rejected
as ambiguous; adapt these layouts with an overlay. The temporary prep spec
omits the vendor Source declaration and its extraction command, so generating
the replacement does not consume the old vendor payload. RPM expands the
upstream archive filename and pins the vendor Source tag to that expanded name,
so subsequent release/history changes cannot desynchronize the spec and payload.
The regenerated archive replaces that file and its
`sources` checksum rather than adding a second vendor archive.

When `config-file` is omitted, the upstream go-vendor-tools configuration is used,
including its reviewed license exceptions. An explicit file replaces that configuration
even when empty; missing files or invalid TOML fail without falling back.
The detected licenses are verified against the existing
main-package `License:` expression; mismatches fail instead of rewriting it.
Upstream `general.go_mod_dir` is honored by generation and licensing, preserving
the module subdirectory within the archive. A conflicting `module-dir` fails;
use a separate `config-file` to replace upstream settings when changing the layout.
Neither the regenerated archive nor its configuration sidecar may also be
declared in `source-files`, even with `replace-upstream = true`.
The source-fetch phase currently still downloads the upstream vendor archive
alongside the other upstream sources, before regenerating its contents.

## Review and upgrade

Run `azldev component diff-sources -p example` to inspect the conversion, or
`azldev component render -p example` to refresh checked-in rendered output.
Rendering and source diffs also generate the archive: they need network access and
can be substantially slower for enabled components.
Rendered output retains the `sources` checksums but excludes payloads listed in
that manifest; archives remain preparation/build artifacts rather than checked-in files.

Review the cumulative license expression and the retained build/install/check
commands before building. Automatic detection is not approval of a dependency's
license. The converter sets `GO111MODULE=on`, `-mod=vendor`, `GOPROXY=off`,
`GOSUMDB=off`, and `GOTOOLCHAIN=local` for build scripts; custom commands must not
override these settings to download dependencies.

The same TOML applies after an upstream version update: no archive version,
import path, Source number, or spec line number is embedded in the declaration.
Generation reruns against the newly selected and patched inputs. Review changed
vendor hashes and licenses; an upstream packaging change may require updating
an overlay or resolving a newly unsupported construct.

See [Go vendoring configuration](../reference/config/components.md#go-vendoring)
for module layout, dependency overrides, and license exceptions.
4 changes: 2 additions & 2 deletions docs/user/reference/cli/azldev_component_prepare-sources.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions docs/user/reference/cli/azldev_component_render.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

72 changes: 72 additions & 0 deletions docs/user/reference/config/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ A component definition tells azldev where to find the spec file, how to customiz
| Build config | `build` | [BuildConfig](#build-configuration) | No | Build-time options (macros, conditionals, check config) |
| Render config | `render` | [RenderConfig](#render-configuration) | No | Options controlling spec rendering behavior |
| Source files | `source-files` | array of [SourceFileReference](#source-file-references) | No | Additional source files to download for this component |
| Go vendoring | `go-vendor` | [GoVendorConfig](#go-vendoring) | No | Convert a supported Go application spec in place and generate bundled dependencies |
| Package overrides | `packages` | map of string → [PackageConfig](package-groups.md#package-config) | No | Exact per-package configuration overrides; highest priority in the resolution order |
| Tests | `tests` | [ComponentTests](#component-tests) | No | Test references that apply to this component (see [Tests and Test Groups](tests.md)) |

Expand Down Expand Up @@ -165,6 +166,77 @@ skip-file-filter = true

> **Note:** This should only be used for specs with macros that `spectool` cannot resolve. For most components, the default filtering behavior is correct and keeps the rendered output clean.

## Go Vendoring

Go vendoring is opt-in and preserves the upstream spec rather than replacing it
with a generated spec. See [Vendor a Go application](../../how-to/vendor-go-application.md)
for the workflow and supported spec patterns.

```toml
[components.example.go-vendor]
enabled = true
```

| TOML Key | Type | Default | Description |
|----------|------|---------|-------------|
| `enabled` | boolean | `false` | Enable conversion and vendor archive generation. An explicit `false` overrides inherited enablement. |
| `module-dir` | string | `""` | Relative directory containing `go.mod`, resolved from the working directory at the end of the upstream `%prep` section. Empty uses that directory itself. No absolute paths, traversal, or RPM macros. |
| `prep-packages` | array of string | `[]` | Additional RPM packages needed by upstream `%prep` commands or the selected license detector. Installed only in the isolated generation chroot. |
| `config-file` | string | `""` | Path to a separate [go-vendor-tools TOML file](https://fedora.gitlab.io/sigs/go/go-vendor-tools/config/), relative to the configuration file that declares it. Its contents replace upstream tool settings; they are not merged. When no file is configured, reuse an already-vendored spec's upstream settings or use tool defaults for a new conversion. |

Custom vendor settings must live in a separate file, typically
`go-vendor-tools.toml` beside the component definition. Its contents participate
in the component fingerprint; absolute checkout paths do not. Inherited paths
retain their original declaration directory. Editing only this file makes the
lock fingerprint stale and requires the normal update/render workflow.

```toml
# example.comp.toml
[components.example.go-vendor]
enabled = true
config-file = "go-vendor-tools.toml"
```

```toml
# go-vendor-tools.toml
[archive.dependency_overrides]
"example.com/dependency" = "v1.2.3"
```

The configured file must exist and contain valid TOML. An explicitly selected
empty file is an empty replacement, not a request to fall back to upstream.
In an explicitly selected file, use `module-dir`, not `general.go_mod_dir`, for
the module location. Reused upstream `general.go_mod_dir` settings remain honored;
an inconsistent component `module-dir` is rejected.
Configured command hooks execute in the generation chroot; treat this file as
trusted build code. The old inline `tool-config` string/table is no longer supported.

For previously unbundled specs, preparation emits `<component>-vendor.tar.bz2` and `azldev-go-vendor.toml`,
allocating unused `Source` numbers automatically. These names are reserved for
generated output; do not also declare them in `source-files` or supply them through
overlays. The archive's SHA-512 digest is recorded in the prepared `sources` file.
Generation runs afresh on every preparation; there is no filename-based cache.
The recorded digest identifies that output, not a separately pinned vendor lock.

For supported specs already using go-vendor-tools, preparation instead regenerates
the existing vendor source, retaining its Source number, RPM-expanded filename,
and configuration sidecar. The Source tag is pinned to the generated filename
before release/history updates, and extraction moves after prep's module edits
to avoid applying patches twice. Ambiguous secondary-source setup and
post-extraction directory changes require a prep overlay.
It updates that archive's `sources` entry in place and
verifies the existing `License:` expression without rewriting it.
These generated archive/config filenames must not also appear in `source-files`,
including entries with `replace-upstream = true`.
When `config-file` is set, its contents are staged under the configuration filename
referenced by the prepared spec, retaining the existing Source number.

All archive/spec/loose-file overlays run before conversion. Source preparation,
builds, rendering (including `--check-only`), and source diffs use the same
conversion. Enabled components require downloads even with `--skip-sources`,
because preparation and license detection need the effective upstream sources.
`--skip-overlays` also skips conversion, leaving the upstream packaging unchanged.

## Build Configuration

The `[components.<name>.build]` section controls build-time options for a component.
Expand Down
15 changes: 15 additions & 0 deletions internal/app/azldev/agentskill/agentskill_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,21 @@ func TestComponentTomlSkillDocumentsRPMDevBumpspecPreview(t *testing.T) {
assert.Contains(t, doc, "no bumpspec runtime discovery occurs")
}

func TestComponentTomlSkillDocumentsGoVendoring(t *testing.T) {
for _, withoutLockfile := range []bool{false, true} {
doc, err := agentskill.NewCatalog(withoutLockfile).SkillDocument("azldev-comp-toml", testParams())
require.NoError(t, err)
assert.Contains(t, doc, "go-vendor.enabled")
assert.Contains(t, doc, "does\nnot run `go2rpm`")
assert.Contains(t, doc, "Rendering and source diffs also generate")
assert.Contains(t, doc, "reviewed license exceptions")
assert.Contains(t, doc, `config-file = "go-vendor-tools.toml"`)
assert.Contains(t, doc, "Inline `tool-config` is not supported.")
assert.Contains(t, doc, "Upstream `general.go_mod_dir` is honored")
assert.Contains(t, doc, "Source filename is pinned before")
}
}

func TestImageSkillDocumentsSKUGroupGuidance(t *testing.T) {
doc, err := agentskill.SkillDocument("azldev-image", testParams())
require.NoError(t, err)
Expand Down
Loading
Loading