Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 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
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
2 changes: 2 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
28 changes: 28 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,18 @@ 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, err := registry.NewClient(baseURL, stack.RegistryClientOptions(baseURL, profile)...)
if err != nil {
return fmt.Errorf("failed to create package registry client: %w", err)
}
requiredInputsResolver := requiredinputs.NewRequiredInputsResolver(eprClient)

withOpts := []rally.OptionFunc{
rally.WithVariant(variant),
rally.WithBenchmarkName(benchName),
Expand All @@ -344,6 +358,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 +521,18 @@ 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, err := registry.NewClient(baseURL, stack.RegistryClientOptions(baseURL, profile)...)
if err != nil {
return fmt.Errorf("failed to create package registry client: %w", err)
}
requiredInputsResolver := requiredinputs.NewRequiredInputsResolver(eprClient)

withOpts := []stream.OptionFunc{
stream.WithVariant(variant),
stream.WithBenchmarkName(benchName),
Expand All @@ -519,6 +546,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
35 changes: 27 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,28 @@ func buildCommandAction(cmd *cobra.Command, args []string) error {
return fmt.Errorf("can't load configuration: %w", err)
}

prof, err := profile.LoadProfile(appConfig.CurrentProfile())
if err != nil {
return fmt.Errorf("could not load profile: %w", err)
}
baseURL := stack.PackageRegistryBaseURL(prof, appConfig)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jsoriano with the use of profile at the build command, we are coupling the stack with build. the command itself does not have the flag to select profile.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need it to support the local package registry, right?

What it is true is that it should remain working even if there is no stack started, this should be optional.

eprClient, err := registry.NewClient(baseURL, stack.RegistryClientOptions(baseURL, prof)...)
if err != nil {
return fmt.Errorf("failed to create package registry client: %w", err)
}

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
23 changes: 17 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,22 @@ func installCommandAction(cmd *cobra.Command, _ []string) error {
return fmt.Errorf("can't load configuration: %w", err)
}

baseURL := stack.PackageRegistryBaseURL(profile, appConfig)
eprClient, err := registry.NewClient(baseURL, stack.RegistryClientOptions(baseURL, profile)...)
if err != nil {
return fmt.Errorf("failed to create package registry client: %w", err)
}

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
5 changes: 4 additions & 1 deletion cmd/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,10 @@ func statusCommandAction(cmd *cobra.Command, args []string) error {

// Create registry client with configured URL
// Currently, this command does not use profile, so we take the URL from the application configuration
registryClient := registry.NewClient(appConfig.PackageRegistryBaseURL())
registryClient, err := registry.NewClient(appConfig.PackageRegistryBaseURL())
if err != nil {
return fmt.Errorf("failed to create package registry client: %w", err)
}

options := registry.SearchOptions{
All: showAll,
Expand Down
36 changes: 26 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,27 @@ func testRunnerPolicyCommandAction(cmd *cobra.Command, args []string) error {
return fmt.Errorf("can't load configuration: %w", err)
}

baseURL := stack.PackageRegistryBaseURL(profile, appConfig)
eprClient, err := registry.NewClient(baseURL, stack.RegistryClientOptions(baseURL, profile)...)
if err != nil {
return fmt.Errorf("failed to create package registry client: %w", err)
}
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