Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
04bb272
publish remote components
AustinAbro321 Aug 13, 2026
76381a2
improve e2e test
AustinAbro321 Aug 13, 2026
0c5dabe
eliminate bad test
AustinAbro321 Aug 13, 2026
d13c5cd
add remotes
AustinAbro321 Aug 13, 2026
1fae4ab
map
AustinAbro321 Aug 13, 2026
438919a
move resource map
AustinAbro321 Aug 13, 2026
3a69429
fix flavors
AustinAbro321 Aug 13, 2026
4512409
sign package
AustinAbro321 Aug 13, 2026
0ff6fc2
publish
AustinAbro321 Aug 13, 2026
e764654
confirm flag
AustinAbro321 Aug 13, 2026
1fa1842
component publish ux
AustinAbro321 Aug 13, 2026
aab425d
publish archives
AustinAbro321 Aug 13, 2026
ac0be8a
component config import
AustinAbro321 Aug 13, 2026
2255386
publish componennt
AustinAbro321 Aug 13, 2026
36a71f3
publishing
AustinAbro321 Aug 13, 2026
547d62a
kind
AustinAbro321 Aug 13, 2026
62b6084
figure out architectures
AustinAbro321 Aug 13, 2026
91bf00f
annotation
AustinAbro321 Aug 13, 2026
d411216
update
AustinAbro321 Aug 13, 2026
27e8707
simlify test
AustinAbro321 Aug 13, 2026
40ed863
Merge branch 'main' into publish-v1beta1-components
AustinAbro321 Aug 13, 2026
0c4358f
fix
AustinAbro321 Aug 13, 2026
622c159
cleanup
AustinAbro321 Aug 13, 2026
74f9886
zoci retry
AustinAbro321 Aug 13, 2026
94bb106
unnecessary error
AustinAbro321 Aug 13, 2026
2f23cb3
unnecessary test
AustinAbro321 Aug 13, 2026
5c935a2
dev inspect manifests
AustinAbro321 Aug 14, 2026
b781039
temp fix
AustinAbro321 Aug 14, 2026
d9047dc
fixme
AustinAbro321 Aug 14, 2026
4b8f7c3
improve architecture of loading and pulling
AustinAbro321 Aug 14, 2026
c40b724
fix race?
AustinAbro321 Aug 14, 2026
d61665a
hidden
AustinAbro321 Aug 14, 2026
e2b6e21
fixme
AustinAbro321 Aug 14, 2026
557c037
allow image archives
AustinAbro321 Aug 14, 2026
0cd3332
component publish
AustinAbro321 Aug 14, 2026
2961d9b
delete mutex
AustinAbro321 Aug 14, 2026
1e89061
cache and test
AustinAbro321 Aug 14, 2026
206ec5d
oci cache modifier
AustinAbro321 Aug 14, 2026
16a07de
cache
AustinAbro321 Aug 14, 2026
2d91ffa
test improvements
AustinAbro321 Aug 14, 2026
68f7e7c
note
AustinAbro321 Aug 14, 2026
e1d621c
simplify function
AustinAbro321 Aug 14, 2026
bb7e5b3
refocus tests
AustinAbro321 Aug 17, 2026
8a1bcfd
test
AustinAbro321 Aug 17, 2026
2120aa3
zarf dev template
AustinAbro321 Aug 17, 2026
77fb5d6
publish
AustinAbro321 Aug 17, 2026
8e62fac
fix publish bug
AustinAbro321 Aug 17, 2026
90e430a
lint
AustinAbro321 Aug 17, 2026
1bb00b6
refactor how architecture and flavor work in components
AustinAbro321 Aug 17, 2026
47ed80d
import v1beta1 packages
AustinAbro321 Aug 17, 2026
d3a7957
unit test
AustinAbro321 Aug 17, 2026
5d6c53b
schema test changes
AustinAbro321 Aug 17, 2026
7eef72c
empty json
AustinAbro321 Aug 17, 2026
c76fb57
unpack
AustinAbro321 Aug 17, 2026
7b1fdcc
one of local and remote
AustinAbro321 Aug 17, 2026
8b0e722
omitzer
AustinAbro321 Aug 17, 2026
757de9f
remove kind
AustinAbro321 Aug 17, 2026
619fb7e
imports
AustinAbro321 Aug 17, 2026
098cc5d
file
AustinAbro321 Aug 17, 2026
ef1e893
resource set
AustinAbro321 Aug 17, 2026
9c306f0
isabs
AustinAbro321 Aug 17, 2026
db4d4cd
not yet supported
AustinAbro321 Aug 17, 2026
074e624
implement remote resource imports
AustinAbro321 Aug 17, 2026
7ea19e4
resource set
AustinAbro321 Aug 17, 2026
3b8ee2a
one source
AustinAbro321 Aug 17, 2026
964080f
coment
AustinAbro321 Aug 17, 2026
87d880f
introduce variants
AustinAbro321 Aug 17, 2026
8ff07c5
Merge branch 'main' into publish-v1beta1-components
AustinAbro321 Aug 18, 2026
0b4a721
variants
AustinAbro321 Aug 21, 2026
f46a5f1
Merge branch 'main' into publish-v1beta1-components
AustinAbro321 Aug 21, 2026
e3cdcbd
remote with options
AustinAbro321 Aug 21, 2026
c53c2e9
cleaner defers
AustinAbro321 Aug 21, 2026
42a383d
change publish to be on disk
AustinAbro321 Aug 24, 2026
ac09981
fetch manifest
AustinAbro321 Aug 24, 2026
51e8bdc
ref cache
AustinAbro321 Aug 24, 2026
0b07354
Merge branch 'main' into publish-v1beta1-components
AustinAbro321 Aug 25, 2026
ae4af2a
remove signing
AustinAbro321 Aug 25, 2026
2dfacaf
confirm
AustinAbro321 Aug 25, 2026
02ca3c4
Merge branch 'main' into publish-v1beta1-components
brandtkeller Aug 26, 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
8 changes: 4 additions & 4 deletions src/api/convert/convert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -955,14 +955,14 @@ func TestV1Beta1PkgToV1Alpha1_ComponentBasics(t *testing.T) {
Name: "my-component",
Description: "test component",
Optional: true,
Selector: v1beta1.ComponentSelector{
Architecture: "amd64",
Flavor: "vanilla",
},
ComponentSpec: v1beta1.ComponentSpec{
Target: v1beta1.ComponentTarget{
OS: "linux",
},
Selector: v1beta1.ComponentSelector{
Architecture: "amd64",
Flavor: "vanilla",
},
Import: v1beta1.ComponentImport{
Local: []v1beta1.ComponentImportLocal{{Path: "./path"}},
Remote: []v1beta1.ComponentImportRemote{{URL: "oci://example.com/pkg"}},
Expand Down
8 changes: 5 additions & 3 deletions src/api/v1beta1/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ type Component struct {
// Message to include during package deploy describing the purpose of this component.
Description string `json:"description,omitempty"`
// Do not install this component unless explicitly requested. Defaults to false, meaning the component is required.
Optional bool `json:"optional,omitempty"`
Optional bool `json:"optional,omitempty"`
// Filter when this component is included during package creation based on architecture or flavor.
Selector ComponentSelector `json:"selector,omitempty"`
ComponentSpec `json:",inline"`
}

Expand Down Expand Up @@ -48,9 +50,9 @@ type ComponentSelector struct {
// ComponentImport is a reference to imported Zarf component configs.
type ComponentImport struct {
// Local file path references to component config files to import.
Local []ComponentImportLocal `json:"local,omitempty"`
Local []ComponentImportLocal `json:"local,omitempty" jsonschema:"oneof_required=local"`
// OCI URL references to remote component config files to import; pulled at create time.
Remote []ComponentImportRemote `json:"remote,omitempty"`
Remote []ComponentImportRemote `json:"remote,omitempty" jsonschema:"oneof_required=remote"`
}

// ComponentImportLocal is a local file path reference to a component config.
Expand Down
14 changes: 11 additions & 3 deletions src/api/v1beta1/component_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ type ComponentConfig struct {
Kind PackageKind `json:"kind" jsonschema:"enum=ZarfComponentConfig,default=ZarfComponentConfig"`
// Component metadata.
Metadata ComponentMetadata `json:"metadata"`
// Variant identifies the flavor and architecture represented by this component config.
Variant ComponentVariant `json:"variant,omitzero"`
// The single component this config defines.
Component ComponentSpec `json:"component"`
// Values imports Zarf values files for templating and overriding Helm values.
Expand All @@ -22,11 +24,9 @@ type ComponentConfig struct {
// ComponentSpec is a reduced component definition used in component configs.
type ComponentSpec struct {
// Import a component from another Zarf component config.
Import ComponentImport `json:"import,omitempty"`
Import ComponentImport `json:"import,omitzero"`
// Filter when this component is deployed based on OS.
Target ComponentTarget `json:"target,omitempty"`
// Filter when this component is included during package creation based on architecture or flavor.
Selector ComponentSelector `json:"selector,omitempty"`
// Kubernetes manifests to be included in a generated Helm chart on package deploy.
Manifests []Manifest `json:"manifests,omitempty"`
// Helm charts to install during package deploy.
Expand Down Expand Up @@ -60,6 +60,14 @@ type ComponentMetadata struct {
Annotations map[string]string `json:"annotations,omitempty"`
}

// ComponentVariant identifies a component config variant.
type ComponentVariant struct {
// Flavor identifies the component config flavor.
Flavor string `json:"flavor,omitempty"`
// Architecture identifies the architecture-specific component artifact.
Architecture string `json:"architecture,omitempty" jsonschema:"enum=amd64,enum=arm64"`
}

// ComponentPublishData is written during publish to track details of the component config.
type ComponentPublishData struct {
// The version of Zarf used to build this component config.
Expand Down
71 changes: 71 additions & 0 deletions src/cmd/component.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2021-Present The Zarf Authors

package cmd

import (
"errors"
"path"
"strings"

"github.com/defenseunicorns/pkg/helpers/v2"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/zarf-dev/zarf/src/config/lang"
"github.com/zarf-dev/zarf/src/pkg/component"
"oras.land/oras-go/v2/registry"
)

func newComponentCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "component",
Short: lang.CmdComponentShort,
// Once v1beta1 is available this should be unhidden
Hidden: true,
}

cmd.AddCommand(newComponentPublishCommand(getViper()))
return cmd
}

type componentPublishOptions struct {
ociConcurrency int
retries int
}

func newComponentPublishCommand(v *viper.Viper) *cobra.Command {
o := &componentPublishOptions{}
cmd := &cobra.Command{
Use: "publish COMPONENT_FILE OCI_REPOSITORY",
Short: lang.CmdComponentPublishShort,
Example: lang.CmdComponentPublishExample,
Args: cobra.ExactArgs(2),
RunE: o.run,
}

cmd.Flags().IntVar(&o.ociConcurrency, "oci-concurrency", v.GetInt(VPkgOCIConcurrency), lang.CmdPackageFlagConcurrency)
cmd.Flags().IntVar(&o.retries, "retries", v.GetInt(VPkgPublishRetries), lang.CmdPackageFlagRetries)
return cmd
}

func (o *componentPublishOptions) run(cmd *cobra.Command, args []string) error {
if !helpers.IsOCIURL(args[1]) {
return errors.New("registry must be prefixed with 'oci://'")
}

parts := strings.Split(strings.TrimPrefix(args[1], helpers.OCIURLPrefix), "/")
destination := registry.Reference{
Registry: parts[0],
Repository: path.Join(parts[1:]...),
}
if err := destination.ValidateRegistry(); err != nil {
return err
}

_, err := component.Publish(cmd.Context(), args[0], destination, component.PublishOptions{
OCIConcurrency: o.ociConcurrency,
Retries: o.retries,
RemoteOptions: defaultRemoteOptions(),
})
return err
}
49 changes: 23 additions & 26 deletions src/cmd/dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,31 +132,31 @@ func (o *devGenerateSchemaOptions) run(ctx context.Context, args []string) error
return err
}

loadOpts := load.DefinitionOptions{
Flavor: o.flavor,
SetVariables: o.setPkgTmpl,
IsInteractive: true,
SkipVersionCheck: true,
loadOpts := load.PackageOptions{
DefinitionOptions: load.DefinitionOptions{
Flavor: o.flavor,
SetVariables: o.setPkgTmpl,
IsInteractive: true,
SkipVersionCheck: true,
CachePath: cachePath,
RemoteOptions: defaultRemoteOptions(),
},
SkipValuesSchemaValidation: true,
CachePath: cachePath,
RemoteOptions: defaultRemoteOptions(),
}

defined, err := load.PackageDefinition(ctx, basePath, loadOpts)
loaded, err := load.Package(ctx, basePath, loadOpts)
if err != nil {
return err
}
pkg := defined.PackageDefinition.AsV1alpha1()
defer func() {
if closeErr := loaded.Close(); closeErr != nil {
l.Warn("unable to close loaded package", "error", closeErr)
}
}()
pkg := loaded.Definition.AsV1alpha1()

// Step 1: Merge default values.files to create initial set of default Zarf values
valuesPaths := make([]string, len(pkg.Values.Files))
for i, file := range pkg.Values.Files {
valuesPaths[i] = filepath.Join(basePath, file)
}
zarfValues, err := value.ParseFiles(ctx, valuesPaths, value.ParseFilesOptions{})
if err != nil {
return fmt.Errorf("unable to parse package values files: %w", err)
}
zarfValues := loaded.Values.DeepCopy()

var mappedSchemas []mappedChartSchema

Expand All @@ -178,7 +178,7 @@ func (o *devGenerateSchemaOptions) run(ctx context.Context, args []string) error
ValuesDir: filepath.Join(tmpDir, "values"),
}

err := assemble.PackageChart(ctx, chart, basePath, chartPaths, cachePath, defaultRemoteOptions())
err := assemble.PackageChart(ctx, chart, loaded.Resources, chartPaths, cachePath, defaultRemoteOptions())
if err != nil {
return fmt.Errorf("unable to package chart %q for schema generation: %w", chart.Name, err)
}
Expand Down Expand Up @@ -261,10 +261,7 @@ func (o *devGenerateSchemaOptions) run(ctx context.Context, args []string) error
}

// Step 4: Merge and reconcile any existing schema
existingSchema, mergeErr := value.MergeSchemaFiles(pkg.Values.Schema, defined.ImportedSchemas, basePath)
if mergeErr != nil {
return fmt.Errorf("unable to merge imported schemas for schema generation: %w", mergeErr)
}
existingSchema := loaded.ValuesSchema

if existingSchema != nil {
generatedSchema = value.ReconcileJSONSchema(existingSchema, generatedSchema, o.deleteNotFound)
Expand Down Expand Up @@ -357,7 +354,7 @@ func (o *devInspectDefinitionOptions) run(cmd *cobra.Command, args []string) err
if err != nil {
return err
}
defined, err := load.PackageDefinition(ctx, basePath, loadOpts)
definition, err := load.PackageDefinition(ctx, basePath, loadOpts)
var lintErr *lint.LintError
if errors.As(err, &lintErr) {
PrintFindings(ctx, lintErr)
Expand All @@ -367,12 +364,12 @@ func (o *devInspectDefinitionOptions) run(cmd *cobra.Command, args []string) err
}

// The definition is printed in the apiVersion it was authored in.
if defined.PackageDefinition.OriginalAPIVersion() == v1beta1.APIVersion {
pkg := defined.PackageDefinition.AsV1beta1()
if definition.OriginalAPIVersion() == v1beta1.APIVersion {
pkg := definition.AsV1beta1()
pkg.Build = v1beta1.BuildData{}
return utils.ColorPrintYAML(pkg, nil, false)
}
pkg := defined.PackageDefinition.AsV1alpha1()
pkg := definition.AsV1alpha1()
pkg.Build = v1alpha1.ZarfBuildData{}
return utils.ColorPrintYAML(pkg, nil, false)
}
Expand Down
1 change: 1 addition & 0 deletions src/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ func NewZarfCommand() *cobra.Command {
rootCmd.AddCommand(newDevCommand())
rootCmd.AddCommand(newInitCommand())
rootCmd.AddCommand(newInternalCommand(rootCmd))
rootCmd.AddCommand(newComponentCommand())
rootCmd.AddCommand(newPackageCommand())

rootCmd.AddCommand(newVersionCommand())
Expand Down
6 changes: 6 additions & 0 deletions src/config/lang/english.go
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,12 @@ $ zarf package publish zarf-package-my-app-amd64-1.0.0.tar.zst oci://my-registry
CmdPackagePublishFlagFlavor = "The flavor of components to include in the resulting package. The flavor will be appended to the package tag"
CmdPackagePublishFlagTag = "The tag to be used in the OCI reference for the package in the registry"

CmdComponentShort = "Commands for reusable Zarf components"
CmdComponentPublishShort = "Publishes a v1beta1 Zarf component to a remote registry"
CmdComponentPublishExample = `
# Publish a v1beta1 component config to a remote registry
$ zarf component publish monitoring.yaml oci://ghcr.io/my-org/components
`
CmdPackageSignShort = "Signs an existing Zarf package"
CmdPackageSignLong = "Signs an existing Zarf package with a private key. The package can be a local tarball or pulled from an OCI registry. The signature is created by signing the zarf.yaml file and does not modify the package checksums."
CmdPackageSignExample = `
Expand Down
8 changes: 4 additions & 4 deletions src/internal/api/v1beta1/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -423,16 +423,16 @@ func componentFromGeneric(c types.Component, isInit, migrateFromV1alpha1 bool) v
Name: c.Name,
Description: c.Description,
Optional: optionalFromGeneric(c.Optional, c.Required),
Selector: v1beta1.ComponentSelector{
Architecture: c.Target.Architecture,
Flavor: c.Target.Flavor,
},
ComponentSpec: v1beta1.ComponentSpec{
Repositories: repositoriesFromGeneric(c.Repositories),
StateAccess: stateAccessFromGeneric(c.StateAccess),
Target: v1beta1.ComponentTarget{
OS: c.Target.OS,
},
Selector: v1beta1.ComponentSelector{
Architecture: c.Target.Architecture,
Flavor: c.Target.Flavor,
},
Import: importFromGeneric(c.Import),
Service: serviceFromGeneric(c, isInit),
Actions: actionsFromGeneric(c.Actions),
Expand Down
8 changes: 4 additions & 4 deletions src/internal/api/v1beta1/roundtrip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ func TestConvertGenericRoundTripLossless(t *testing.T) {
Name: "comp",
Description: "comp desc",
Optional: true,
Selector: v1beta1.ComponentSelector{
Architecture: "arm64",
Flavor: "prod",
},
ComponentSpec: v1beta1.ComponentSpec{
Import: v1beta1.ComponentImport{
Local: []v1beta1.ComponentImportLocal{{Path: "path"}},
Expand All @@ -68,10 +72,6 @@ func TestConvertGenericRoundTripLossless(t *testing.T) {
Target: v1beta1.ComponentTarget{
OS: "linux",
},
Selector: v1beta1.ComponentSelector{
Architecture: "arm64",
Flavor: "prod",
},
Service: v1beta1.ServiceRegistry,
Repositories: []v1beta1.Repository{{URL: "https://github.com/example/repo"}},
StateAccess: []v1beta1.StateAccessKey{v1beta1.StateAccessRegistryCredentials},
Expand Down
Loading