Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
b5c957a
feat: extend package manifest for required input dependencies
teresaromero Apr 13, 2026
d9f3026
feat: add registry client package download with TLS
teresaromero Apr 13, 2026
aa4c8f9
feat: optional PGP verification for downloaded package zips
teresaromero Apr 13, 2026
84efe17
feat: bundle required input packages at build time
teresaromero Apr 13, 2026
9d71a69
feat: wire required input resolver into build and EPR-aware commands
teresaromero Apr 13, 2026
18a010c
test: add manual package fixtures for composable required inputs
teresaromero Apr 13, 2026
1f81dbe
docs: composable integrations, dependencies, and local registry
teresaromero Apr 13, 2026
0dd8203
fix(requiredinputs): satisfy golangci-lint
teresaromero Apr 13, 2026
f788b32
test(archetype): remove unnecessary resolver mock
teresaromero Apr 14, 2026
f3745fe
refactor(requiredinputs): extract processDataStreamManifest sub-function
teresaromero Apr 14, 2026
ce1685e
refactor(requiredinputs): extract shared collectAndCopyPolicyTemplate…
teresaromero Apr 14, 2026
06e7072
Merge branch 'main' of github.com:elastic/elastic-package into feat/3…
teresaromero Apr 14, 2026
575a756
Merge branch 'main' of github.com:elastic/elastic-package into feat/3…
teresaromero Apr 14, 2026
4a6e784
Add composable CI test packages and two-phase zip build
teresaromero Apr 14, 2026
34f2f17
Fix composable CI package manifests for spec and Fleet
teresaromero Apr 14, 2026
a36572e
Skip composable integration in test-build-zip
teresaromero Apr 14, 2026
dc05353
fix(registry): propagate HTTP client errors and harden package download
teresaromero Apr 15, 2026
b6ff9cb
build: resolve EPR base URL from profile like other commands
teresaromero Apr 15, 2026
90d857b
refactor(requiredinputs): split merge/resolve helpers and extend tests
teresaromero Apr 15, 2026
206316b
ci: install yq for build-install-zip-file integration targets
teresaromero Apr 15, 2026
e7b6f0a
scripts: isolate stack env to USE_SHELLINIT branch
teresaromero Apr 15, 2026
d88455e
fix(tests): update error handling for package verification
teresaromero Apr 15, 2026
f05fcbc
feat(ci): add composable integration tests and update build scripts
teresaromero Apr 15, 2026
16dc21d
refactor(tests): simplify cleanup logic in package download tests
teresaromero Apr 15, 2026
56443be
revert(tests): remove composable integration targets from build scripts
teresaromero Apr 15, 2026
bdcc821
refactor(requiredinputs): migrate from gopkg.in/yaml.v3 to goccy/go-yaml
teresaromero Apr 15, 2026
f6af43c
chore(requiredinputs): remove unnecessary blank line in streamdefs.go
teresaromero Apr 15, 2026
4a7129e
refactor(verification): remove package signature verification logic
teresaromero Apr 15, 2026
490cb0d
move test fixture to manual_packages
teresaromero Apr 16, 2026
58e8841
docs(dependency_management): update package registry configuration de…
teresaromero Apr 16, 2026
74c3cc0
Merge branch 'main' of github.com:elastic/elastic-package into feat/3…
teresaromero Apr 16, 2026
bd6d201
Merge branch 'main' of github.com:elastic/elastic-package into feat/3…
teresaromero Apr 16, 2026
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
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,8 @@ Built packages are served up by the Elastic Package Registry running locally (se

Built packages can also be published to the global package registry service.

When the package declares required input packages ("requires.input" in manifest.yml), the build downloads those input packages from the configured package registry (see "package_registry.base_url" in ~/.elastic-package/config.yml). The build then incorporates their policy and data stream templates, merges variable definitions into the integration manifest, bundles data stream field definitions, and resolves package: references on inputs and streams to the effective input types expected by Fleet. For details on using a local or custom registry during development, see the [HOWTO guide](./docs/howto/local_package_registry.md).

For details on how to enable dependency management, see the [HOWTO guide](https://github.com/elastic/elastic-package/blob/main/docs/howto/dependency_management.md).

### `elastic-package changelog`
Expand Down Expand Up @@ -821,6 +823,10 @@ There are available some environment variables that could be used to change some
- `ELASTIC_PACKAGE_SIGNER_PRIVATE_KEYFILE`: Path to the private key file to sign packages.
- `ELASTIC_PACKAGE_SIGNER_PASSPHRASE`: Passphrase to use the private key file.

- Related to verifying packages downloaded from the Package Registry (EPR):
- `ELASTIC_PACKAGE_VERIFY_PACKAGE_SIGNATURE`: If set to `true` (or `1`), `elastic-package` verifies detached OpenPGP signatures for package zips fetched from the registry (for example when resolving required input packages). The registry must serve a signature at the same path as the zip with a `.sig` suffix (for example `/epr/apache/apache-1.0.0.zip.sig`). Leave this unset or `false` for local or unsigned registries.
- `ELASTIC_PACKAGE_VERIFIER_PUBLIC_KEYFILE`: Path to an armored **public** key matching the key that signed packages on that registry. Required when `ELASTIC_PACKAGE_VERIFY_PACKAGE_SIGNATURE` is enabled; the file must exist before the download runs.

- Related to tests:
- `ELASTIC_PACKAGE_SERVERLESS_PIPELINE_TEST_DISABLE_COMPARE_RESULTS`: If set to `true`, the results from pipeline tests are not compared to avoid errors from GeoIP.
- `ELASTIC_PACKAGE_DISABLE_ELASTIC_AGENT_WOLFI`: If set to `true`, the Elastic Agent image used for running agents will be using the Ubuntu docker images
Expand Down
22 changes: 22 additions & 0 deletions cmd/benchmark.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import (
"github.com/elastic/elastic-package/internal/install"
"github.com/elastic/elastic-package/internal/logger"
"github.com/elastic/elastic-package/internal/packages"
"github.com/elastic/elastic-package/internal/registry"
"github.com/elastic/elastic-package/internal/requiredinputs"
"github.com/elastic/elastic-package/internal/signal"
"github.com/elastic/elastic-package/internal/stack"
"github.com/elastic/elastic-package/internal/testrunner"
Expand Down Expand Up @@ -331,6 +333,15 @@ func rallyCommandAction(cmd *cobra.Command, args []string) error {
return fmt.Errorf("can't create Kibana client: %w", err)
}

appConfig, err := install.Configuration()
if err != nil {
return fmt.Errorf("can't load configuration: %w", err)
}

baseURL := stack.PackageRegistryBaseURL(profile, appConfig)
eprClient := registry.NewClient(baseURL, stack.RegistryClientOptions(baseURL, profile)...)
requiredInputsResolver := requiredinputs.NewRequiredInputsResolver(eprClient)

withOpts := []rally.OptionFunc{
rally.WithVariant(variant),
rally.WithBenchmarkName(benchName),
Expand All @@ -344,6 +355,7 @@ func rallyCommandAction(cmd *cobra.Command, args []string) error {
rally.WithRallyPackageFromRegistry(packageName, packageVersion),
rally.WithRallyCorpusAtPath(corpusAtPath),
rally.WithRepositoryRoot(repositoryRoot),
rally.WithRequiredInputsResolver(requiredInputsResolver),
}

esMetricsClient, err := initializeESMetricsClient(ctx)
Expand Down Expand Up @@ -506,6 +518,15 @@ func streamCommandAction(cmd *cobra.Command, args []string) error {
return fmt.Errorf("can't create Kibana client: %w", err)
}

appConfig, err := install.Configuration()
if err != nil {
return fmt.Errorf("can't load configuration: %w", err)
}

baseURL := stack.PackageRegistryBaseURL(profile, appConfig)
eprClient := registry.NewClient(baseURL, stack.RegistryClientOptions(baseURL, profile)...)
requiredInputsResolver := requiredinputs.NewRequiredInputsResolver(eprClient)

withOpts := []stream.OptionFunc{
stream.WithVariant(variant),
stream.WithBenchmarkName(benchName),
Expand All @@ -519,6 +540,7 @@ func streamCommandAction(cmd *cobra.Command, args []string) error {
stream.WithKibanaClient(kc),
stream.WithProfile(profile),
stream.WithRepositoryRoot(repositoryRoot),
stream.WithRequiredInputsResolver(requiredInputsResolver),
}

runner := stream.NewStreamBenchmark(stream.NewOptions(withOpts...))
Expand Down
32 changes: 24 additions & 8 deletions cmd/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import (
"github.com/elastic/elastic-package/internal/install"
"github.com/elastic/elastic-package/internal/logger"
"github.com/elastic/elastic-package/internal/packages"
"github.com/elastic/elastic-package/internal/profile"
"github.com/elastic/elastic-package/internal/registry"
"github.com/elastic/elastic-package/internal/requiredinputs"
"github.com/elastic/elastic-package/internal/stack"
)

const buildLongDescription = `Use this command to build a package.
Expand All @@ -26,6 +30,8 @@ Built packages are served up by the Elastic Package Registry running locally (se

Built packages can also be published to the global package registry service.

When the package declares required input packages ("requires.input" in manifest.yml), the build downloads those input packages from the configured package registry (see "package_registry.base_url" in ~/.elastic-package/config.yml). The build then incorporates their policy and data stream templates, merges variable definitions into the integration manifest, bundles data stream field definitions, and resolves package: references on inputs and streams to the effective input types expected by Fleet. For details on using a local or custom registry during development, see the [HOWTO guide](./docs/howto/local_package_registry.md).

For details on how to enable dependency management, see the [HOWTO guide](https://github.com/elastic/elastic-package/blob/main/docs/howto/dependency_management.md).`

func setupBuildCommand() *cobraext.Command {
Expand Down Expand Up @@ -84,15 +90,25 @@ func buildCommandAction(cmd *cobra.Command, args []string) error {
return fmt.Errorf("can't load configuration: %w", err)
}

baseURL := appConfig.PackageRegistryBaseURL()
Comment thread
teresaromero marked this conversation as resolved.
Outdated
prof, err := profile.LoadProfile(appConfig.CurrentProfile())
if err != nil {
return fmt.Errorf("could not load profile: %w", err)
}
eprClient := registry.NewClient(baseURL, stack.RegistryClientOptions(baseURL, prof)...)

requiredInputsResolver := requiredinputs.NewRequiredInputsResolver(eprClient)

target, err := builder.BuildPackage(builder.BuildOptions{
PackageRoot: packageRoot,
BuildDir: buildDir,
CreateZip: createZip,
SignPackage: signPackage,
SkipValidation: skipValidation,
RepositoryRoot: repositoryRoot,
UpdateReadmes: true,
SchemaURLs: appConfig.SchemaURLs(),
PackageRoot: packageRoot,
BuildDir: buildDir,
CreateZip: createZip,
SignPackage: signPackage,
SkipValidation: skipValidation,
RepositoryRoot: repositoryRoot,
UpdateReadmes: true,
SchemaURLs: appConfig.SchemaURLs(),
RequiredInputsResolver: requiredInputsResolver,
})
if err != nil {
return fmt.Errorf("building package failed: %w", err)
Expand Down
20 changes: 14 additions & 6 deletions cmd/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import (
"github.com/elastic/elastic-package/internal/kibana"
"github.com/elastic/elastic-package/internal/packages"
"github.com/elastic/elastic-package/internal/packages/installer"
"github.com/elastic/elastic-package/internal/registry"
"github.com/elastic/elastic-package/internal/requiredinputs"
"github.com/elastic/elastic-package/internal/stack"
)

Expand Down Expand Up @@ -96,13 +98,19 @@ func installCommandAction(cmd *cobra.Command, _ []string) error {
return fmt.Errorf("can't load configuration: %w", err)
}

baseURL := stack.PackageRegistryBaseURL(profile, appConfig)
eprClient := registry.NewClient(baseURL, stack.RegistryClientOptions(baseURL, profile)...)

requiredInputsResolver := requiredinputs.NewRequiredInputsResolver(eprClient)

installer, err := installer.NewForPackage(installer.Options{
Kibana: kibanaClient,
PackageRoot: packageRoot,
SkipValidation: skipValidation,
ZipPath: zipPathFile,
RepositoryRoot: repositoryRoot,
SchemaURLs: appConfig.SchemaURLs(),
Kibana: kibanaClient,
PackageRoot: packageRoot,
SkipValidation: skipValidation,
ZipPath: zipPathFile,
RepositoryRoot: repositoryRoot,
SchemaURLs: appConfig.SchemaURLs(),
RequiredInputsResolver: requiredInputsResolver,
})
if err != nil {
return fmt.Errorf("package installation failed: %w", err)
Expand Down
33 changes: 23 additions & 10 deletions cmd/testrunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
"github.com/elastic/elastic-package/internal/install"
"github.com/elastic/elastic-package/internal/logger"
"github.com/elastic/elastic-package/internal/packages"
"github.com/elastic/elastic-package/internal/registry"
"github.com/elastic/elastic-package/internal/requiredinputs"
"github.com/elastic/elastic-package/internal/signal"
"github.com/elastic/elastic-package/internal/stack"
"github.com/elastic/elastic-package/internal/testrunner"
Expand Down Expand Up @@ -760,6 +762,12 @@ func testRunnerScriptCommandAction(cmd *cobra.Command, args []string) error {

opts.Package = manifest.Name

profile, err := cobraext.GetProfileFlag(cmd)
if err != nil {
return err
}
opts.Profile = profile

var results []testrunner.TestResult
err = script.Run(&results, cmd.OutOrStderr(), opts)
if err != nil {
Expand Down Expand Up @@ -870,19 +878,24 @@ func testRunnerPolicyCommandAction(cmd *cobra.Command, args []string) error {
return fmt.Errorf("can't load configuration: %w", err)
}

baseURL := stack.PackageRegistryBaseURL(profile, appConfig)
eprClient := registry.NewClient(baseURL, stack.RegistryClientOptions(baseURL, profile)...)
requiredInputsResolver := requiredinputs.NewRequiredInputsResolver(eprClient)

logger.Info(version.Version())
logger.Infof("elastic-stack: %s", stackVersion.Version())
runner := policy.NewPolicyTestRunner(policy.PolicyTestRunnerOptions{
PackageRoot: packageRoot,
KibanaClient: kibanaClient,
DataStreams: dataStreams,
FailOnMissingTests: failOnMissing,
GenerateTestResult: generateTestResult,
GlobalTestConfig: globalTestConfig.Policy,
WithCoverage: testCoverage,
CoverageType: testCoverageFormat,
RepositoryRoot: repositoryRoot,
SchemaURLs: appConfig.SchemaURLs(),
PackageRoot: packageRoot,
KibanaClient: kibanaClient,
DataStreams: dataStreams,
FailOnMissingTests: failOnMissing,
GenerateTestResult: generateTestResult,
GlobalTestConfig: globalTestConfig.Policy,
WithCoverage: testCoverage,
CoverageType: testCoverageFormat,
RepositoryRoot: repositoryRoot,
SchemaURLs: appConfig.SchemaURLs(),
RequiredInputsResolver: requiredInputsResolver,
})

results, err := testrunner.RunSuite(ctx, runner)
Expand Down
65 changes: 62 additions & 3 deletions docs/howto/dependency_management.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,22 @@ which field definition was correct, maintenance and typo correction process was
The described situation brought us to a point in time when a simple dependency management was a requirement to maintain
all used fields, especially ones imported from external sources.

Elastic Packages support two kinds of build-time dependency:

- **Field dependencies** — import field definitions from external schemas (e.g. ECS) using
`_dev/build/build.yml`. Resolved from Git references and cached locally.
- **Package dependencies** — composable (integration) packages can depend on input and content packages
declared under `requires` in `manifest.yml`. **Input package** dependencies are resolved
at build time by downloading from the package registry. **Content package** dependencies are
resolved at runtime by Fleet.

Both are described in the sections below.

## Principles of operation

Currently Elastic Packages support build-time dependencies that can be used as external field sources. They use a flat
dependency model represented with an additional build manifest, stored in an optional YAML file - `_dev/build/build.yml`:
Currently Elastic Packages support build-time field dependencies that can be used as external
field sources. They use a flat dependency model represented with an additional build manifest,
stored in an optional YAML file - `_dev/build/build.yml`:

```yaml
dependencies:
Expand Down Expand Up @@ -83,4 +95,51 @@ and use a following field definition:
```yaml
- name: event.category
external: ecs
```
```

## Composable packages and the package registry

Composable (integration) packages can also depend on input or content packages by declaring them under
`requires` in `manifest.yml`. Depending on the package type, dependencies are resolved
differently: **input package** dependencies are fetched at build time; **content package**
dependencies are resolved at runtime by Fleet.

```yaml
requires:
input:
- package: sql_input
version: "0.2.0"
```

This type of dependency is resolved at **build time** by downloading the required input package
from the **package registry**. During `elastic-package build`, elastic-package fetches those
packages and updates the built integration: it bundles agent templates (policy and data stream),
merges variable definitions from the input packages into the composable manifest, adds data
stream field definitions where configured, and rewrites `package:` references on inputs and
streams to the concrete input types Fleet needs. Fleet still merges policy-specific values at
policy creation time.

Unlike field-level dependencies (which are resolved from Git references and cached locally),
package dependencies are fetched from the configured package registry URL
(`package_registry.base_url` in `~/.elastic-package/config.yml`, defaulting to
`https://epr.elastic.co`).

For details on using a local or custom registry when the required input packages are still
under development, see [HOWTO: Use a local or custom package registry](./local_package_registry.md).

### Linked files (`*.link`) and `template_path`

Some repositories share agent templates using **link files** (files ending in `.link` that
point at shared content). During `elastic-package build`, linked content is copied into the
build output under the **target** path (the link filename without the `.link` suffix).

Composable bundling (`requires.input`) runs **after** linked files are materialized in the
build directory. In `manifest.yml`, always set `template_path` / `template_paths` to those
**materialized** names (for example `owned.hbs`), **not** the stub name (`owned.hbs.link`).
Fleet and the builder resolve templates by the names declared in the manifest; the `.link`
file exists only in the source tree.

A small manual fixture that combines `requires.input` with a linked policy input template
lives under `test/manual_packages/required_inputs/with_linked_template_path/`. Automated
coverage is in `TestBundleInputPackageTemplates_PreservesLinkedTemplateTargetPath` in
`internal/requiredinputs/requiredinputs_test.go`.
Loading