install

package
v1.225.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 5, 2026 License: Apache-2.0 Imports: 22 Imported by: 0

Documentation

Overview

Package install implements the single, unified install path for `atmos vendor pull`/`update`: fetching a declared vendor.yaml source or component.yaml component/mixin into a scratch directory, copying it to its target, and recording a vendor.lock.yaml receipt. It has no Bubble Tea dependency, so Install and FilterPending are directly unit-testable and reusable by any non-interactive call path; internal/exec/vendor_model.go's TUI model calls Install from a thin tea.Cmd closure.

Index

Constants

View Source
const (
	// LockEnforcementSilent re-fetches a drifted package with no reporting -- today's exact
	// behavior, preserved byte-for-byte as one of the three enforcement levels.
	LockEnforcementSilent = "silent"
	// LockEnforcementWarn re-fetches a drifted package and prints one warning per package naming
	// why it drifted. The default when vendor.lock.enforcement is unset.
	LockEnforcementWarn = "warn"
	// LockEnforcementStrict refuses to run (before any fetch/copy/write) when a drifted package is
	// found and --refresh-lock was not explicitly passed.
	LockEnforcementStrict = "strict"
)

Variables

View Source
var (
	// ErrMixinEmpty indicates a local-file mixin was declared with an empty uri.
	ErrMixinEmpty = errors.New("mixin URI cannot be empty")
	// ErrLockDriftBlocked indicates vendor.lock.enforcement: strict rejected a pull because one or
	// more packages have drifted from their vendor.lock.yaml receipt and --refresh-lock was not
	// passed to explicitly re-resolve them.
	ErrLockDriftBlocked = errors.New("vendor lock drift blocked by enforcement: strict")
	// ErrVersionRangeRequiresGitSource indicates a semver-range `version:` was declared on a
	// source with no tag-listing mechanism in this codebase (OCI, local-file, or plain HTTP/S3).
	// Ranges are Git-only for now; see ResolveDeclaredVersion's doc comment.
	ErrVersionRangeRequiresGitSource = errors.New("a semver-range version: requires a Git source")
	// ErrCopyPackage indicates a fetched package's content failed to copy to its target path.
	ErrCopyPackage = errors.New("failed to copy package")
	// ErrRecordVendorLock indicates a vendor.yaml target's vendor.lock.yaml receipt failed to write.
	ErrRecordVendorLock = errors.New("failed to record vendor lock")
	// ErrRecordComponentVendorLock indicates a component.yaml component's vendor.lock.yaml receipt
	// failed to write.
	ErrRecordComponentVendorLock = errors.New("failed to record component vendor lock")
	// ErrRecordMixinVendorLock indicates a component.yaml mixin's vendor.lock.yaml receipt failed
	// to write.
	ErrRecordMixinVendorLock = errors.New("failed to record mixin vendor lock")
	// ErrDryRunDetectionFailed indicates a dry run's custom Git detection probe failed.
	ErrDryRunDetectionFailed = errors.New("dry-run: detection failed")
	// ErrDownloadPackage indicates a go-getter fetch of a remote source failed.
	ErrDownloadPackage = errors.New("failed to download package")
	// ErrProcessOCIImage indicates an OCI-registry source failed to pull or unpack.
	ErrProcessOCIImage = errors.New("failed to process OCI image")
)

Sentinel errors for pkg/vendoring/install. These are package-local rather than centralized in errors/errors.go, matching the precedent pkg/vendoring/lockfile set for this refactor: they are internal invariants of this package's own install contract, meaningful only to this package's own callers and tests, not a cross-package contract other packages match against.

Functions

func ComponentOrMixinsCopy

func ComponentOrMixinsCopy(sourceFile, finalTarget string) error

ComponentOrMixinsCopy covers 2 cases: file-to-folder and file-to-file copy.

func ResolveDeclaredVersion

func ResolveDeclaredVersion(ctx context.Context, atmosConfig *schema.AtmosConfiguration, params *VersionResolveParams) (string, error)

ResolveDeclaredVersion resolves a declared `version:` value to the concrete version that should be templated into a source URI (and, indirectly, any target path that itself templates {{.Version}}).

Fast path: when RawVersion is not a semver range (version.IsSemverConstraint), it is returned unchanged with no lock or network access whatsoever -- this is the overwhelmingly common case (an exact tag/commit/branch pin) and must stay free.

Range path: the first resolution for a given (Name, Discriminator, SourceForGitURI) triple lists the source's remote Git tags and applies the same constraint-filtering pipeline version.ResolveVersionConstraints already provides for `atmos vendor update`'s bump search (RawVersion as the primary constraint, Constraints.ExcludedVersions/NoPrereleases layered on top). The result is recorded in vendor.lock.yaml under a dedicated "version-range" lock entry -- see versionResolveKind's doc comment for why this is a receipt distinct from the source's real installed-files materialization receipt, rather than the exact same entry: an installed target path may itself contain a {{.Version}} placeholder, so it isn't knowable until after this resolution completes, and can't double as the pre-resolution lookup key. Every subsequent pull with the same declared range and a matching lock entry reuses the recorded resolution with zero network calls, until RefreshLock is set or the declared range string itself changes.

Ranges are only supported for Git sources (the only source type with a tag-listing mechanism in this codebase); a range declared on an OCI, local-file, or plain HTTP/S3 source returns ErrVersionRangeRequiresGitSource.

func ResolveEffectiveVersion

func ResolveEffectiveVersion(in *ResolveEffectiveVersionInputs) (resolved, raw string, err error)

ResolveEffectiveVersion resolves in.RawVersion -- which may be an exact pin (returned unchanged, with no lock or network access) or a semver range (resolved via ResolveDeclaredVersion, reusing the first resolution recorded in vendor.lock.yaml on every later pull that declares the same range) -- to the concrete version used for source-URI/target-path templating. Raw is non-empty only when in.RawVersion was actually a range, so callers can carry it through to VendorPackage.RawVersion for lockfile provenance on the eventual install receipt.

Types

type AtmosPackageParams

type AtmosPackageParams struct {
	Name              string
	URI               string
	TargetPath        string
	Version           string
	RawVersion        string
	PkgType           PkgType
	SourceIsLocalFile bool
	Source            schema.AtmosVendorSource
}

AtmosPackageParams are the fields NewAtmosVendorPackage needs to build one vendor.yaml target's VendorPackage. A struct rather than positional parameters: Options Pattern (CLAUDE.md) applies once a constructor has more than four parameters.

type ComponentPackageParams

type ComponentPackageParams struct {
	Name              string
	URI               string
	ComponentPath     string
	Version           string
	RawVersion        string
	PkgType           PkgType
	SourceIsLocalFile bool
	Spec              *schema.VendorComponentSpec
	IsMixin           bool
	MixinFilename     string
}

ComponentPackageParams are the fields NewComponentVendorPackage needs to build a component.yaml component or mixin's VendorPackage. A struct rather than positional parameters: Options Pattern (CLAUDE.md) applies once a constructor has more than four parameters.

type CopyContext

type CopyContext struct {
	SrcDir   string
	DstDir   string
	BaseDir  string
	Excluded []string
	Included []string
}

CopyContext groups parameters for directory copy operations.

type FileCopier

type FileCopier struct {
	// contains filtered or unexported fields
}

FileCopier provides file copying operations with injectable dependencies for testing.

func NewFileCopier

func NewFileCopier(fs filesystem.FileSystem, glob filesystem.GlobMatcher, ioCopy filesystem.IOCopier) *FileCopier

NewFileCopier creates a new FileCopier with the given dependencies.

type InstallOptions

type InstallOptions struct {
	// DryRun performs only the side effects a real fetch would also trigger for
	// go-getter-unsupported URI schemes (custom Git detection), without writing anything.
	DryRun bool
	// RefreshLock bypasses FilterPending's materialization check, forcing every package back
	// through Install even when an existing vendor.lock.yaml receipt says it's unchanged.
	RefreshLock bool
	// LockEnforcement is one of LockEnforcementSilent/Warn/Strict, governing how FilterPending
	// reacts to a drifted package. Empty is treated as LockEnforcementWarn, the config default.
	LockEnforcement string
}

InstallOptions configures Install and FilterPending. A single struct rather than adjacent bool parameters: this repo's Options Pattern mandate applies at two or more adjacent same-typed parameters, which DryRun/RefreshLock were before this refactor (see ExecuteComponentVendorInternal's pre-refactor `dryRun bool, refreshLock bool` signature).

type PkgType

type PkgType int

PkgType classifies how a package's URI must be fetched.

const (
	// PkgTypeRemote is fetched via go-getter (git, http(s), s3, gcs, ...).
	PkgTypeRemote PkgType = iota
	// PkgTypeOci is fetched from an OCI-compatible registry.
	PkgTypeOci
	// PkgTypeLocal is copied from the local filesystem.
	PkgTypeLocal
)

func DeterminePackageType

func DeterminePackageType(useOciScheme, useLocalFileSystem bool) PkgType

DeterminePackageType classifies a resolved source URI into the PkgType fetch dispatch uses, from the two scheme/filesystem probes every vendor.yaml and component.yaml source resolution already performs (OCI scheme stripped, then a local-filesystem check).

func (PkgType) String

func (p PkgType) String() string

String renders a PkgType for logging and as the lock file's "kind" field.

type PrefixCopyContext

type PrefixCopyContext struct {
	SrcDir     string
	DstDir     string
	GlobalBase string
	Prefix     string
	Excluded   []string
}

PrefixCopyContext groups parameters for prefix-based copy operations.

type ResolveEffectiveVersionInputs

type ResolveEffectiveVersionInputs struct {
	AtmosConfig *schema.AtmosConfiguration
	// Name identifies the declaring source/target -- typically the component name.
	Name string
	// Source is the source's raw, un-templated URI/source string (before {{.Version}} is
	// substituted) -- see VersionResolveParams.SourceForGitURI.
	Source      string
	RawVersion  string
	Constraints *schema.VendorConstraints
	// Discriminator disambiguates multiple version declarations that would otherwise share the
	// same Name (e.g. a vendor.yaml source's per-target version overrides).
	Discriminator string
	RefreshLock   bool
	// Lister overrides the remote Git tag lister; nil defaults to version.DefaultLister.
	Lister version.RemoteLister
}

ResolveEffectiveVersionInputs bundles ResolveEffectiveVersion's inputs. A struct rather than positional parameters: this repo's Options Pattern threshold (CLAUDE.md, >4 total parameters) was crossed once Discriminator/RefreshLock/Lister joined the original Name/Source/RawVersion/ Constraints.

type Result

type Result struct {
	Name string
	Err  error
}

Result reports the outcome of installing a single VendorPackage.

func Install

func Install(atmosConfig *schema.AtmosConfiguration, pkg VendorPackage, opts InstallOptions) (Result, error)

Install installs pkg into its declared target, or -- when opts.DryRun is set -- performs only the dry-run side effect (custom-detector probing) without writing anything. It is a plain, synchronous function with no Bubble Tea dependency: directly unit-testable, and reusable by any future non-interactive/CI-only call path. The TUI (internal/exec/vendor_model.go) calls this from a thin tea.Cmd closure that translates the returned Result into its own tea.Msg.

type VendorPackage

type VendorPackage struct {
	// Name identifies the package for progress/status reporting: a vendor.yaml source's
	// component name (or its URI when no component name is declared), a component.yaml's
	// component name, or "mixin <uri>" for a mixin.
	Name string
	// Version is the declared version/ref, shown alongside Name in status output. Empty when
	// the source has no explicit version. For a range-declared `version:`, this is the resolved
	// concrete version (what's actually fetched), not the raw range -- see RawVersion.
	Version string
	// RawVersion is the source's originally-declared version string, populated only when it
	// differs from the resolved Version above -- i.e. only for a range-declared `version:` (see
	// pkg/vendoring/install/version_resolve.go). Empty for an exact-pinned version:, matching
	// lockfile.Source.VersionConstraint's own omitempty convention.
	RawVersion string
	// contains filtered or unexported fields
}

VendorPackage is the single installable unit for `atmos vendor pull`/`update`: either one target of a vendor.yaml source, or a component.yaml's component or one of its mixins. Build one via NewAtmosVendorPackage or NewComponentVendorPackage; callers never need to know which concrete installer backs it.

func FilterPending

func FilterPending(atmosConfig *schema.AtmosConfiguration, packages []VendorPackage, opts InstallOptions) ([]VendorPackage, error)

FilterPending drops every package an existing vendor.lock.yaml receipt already proves is unchanged, leaving only the packages Install still needs to fetch. When opts.DryRun or opts.RefreshLock is set, the materialization check is skipped entirely and every package is returned as-is (matching the pre-unification "if !dryRun && !refreshLock { filter... }" guard duplicated at all three call sites this replaces).

For every drifted (non-materialized) package, opts.LockEnforcement governs what happens next:

  • LockEnforcementSilent: the package is added to pending with no reporting -- the only level whose observable behavior matches this function before enforcement levels existed.
  • LockEnforcementWarn (the default, including "" and any unrecognized value): the package is added to pending, and one ui.Warningf line is printed naming the package and why it drifted.
  • LockEnforcementStrict: the package is withheld from pending and its name+reason are collected. If any package drifted, FilterPending returns ErrLockDriftBlocked (wrapping every collected package+reason) instead of a partial pending list, so a strict caller never fetches anything on a run it's about to fail.

func NewAtmosVendorPackage

func NewAtmosVendorPackage(params *AtmosPackageParams) VendorPackage

NewAtmosVendorPackage builds a VendorPackage for a single target of a vendor.yaml source.

func NewComponentVendorPackage

func NewComponentVendorPackage(params *ComponentPackageParams) VendorPackage

NewComponentVendorPackage builds a VendorPackage for a component.yaml's component (IsMixin false) or one of its mixins (IsMixin true, MixinFilename set).

func (VendorPackage) IsMixin

func (pkg VendorPackage) IsMixin() bool

IsMixin reports whether pkg is a component.yaml mixin.

func (VendorPackage) MixinFilename

func (pkg VendorPackage) MixinFilename() string

MixinFilename returns the mixin's declared output filename, or "" for a non-mixin package.

func (VendorPackage) PkgType

func (pkg VendorPackage) PkgType() PkgType

PkgType reports how pkg's URI must be fetched.

func (VendorPackage) SourceIsLocalFile

func (pkg VendorPackage) SourceIsLocalFile() bool

SourceIsLocalFile reports whether pkg's URI names a single local file (as opposed to a local directory, or a remote/OCI source) -- see fetchOptions.SourceIsLocalFile's doc comment for how this changes fetch/copy behavior.

func (VendorPackage) Target

func (pkg VendorPackage) Target() string

Target returns pkg's destination directory: a vendor.yaml target's path, or a component.yaml component/mixin's component directory.

func (VendorPackage) URI

func (pkg VendorPackage) URI() string

URI returns pkg's declared source URI (scheme-stripped for OCI; see lockDeclaredSource).

type VersionResolveParams

type VersionResolveParams struct {
	// RawVersion is the source's (or per-target override's) declared version string, exactly as
	// written in vendor.yaml/component.yaml -- an exact pin, a semver range, or an opaque literal
	// (commit SHA, branch name).
	RawVersion string
	// Name identifies the declaring source/target for the lock cache key -- typically the
	// component name (or the raw source URI, when a source declares no component name).
	Name string
	// Discriminator, when non-empty, is folded into the lock cache key alongside Name. Needed when
	// multiple declarations share one Name but must resolve/cache independently -- e.g. a
	// vendor.yaml source's per-target `targets[].version` overrides, which all share the source's
	// Component name.
	Discriminator string
	// SourceForGitURI is the source's raw, un-templated URI/source string (before {{.Version}} is
	// substituted). Used both to extract the Git remote to list tags from, and as part of the lock
	// cache key -- it is stable across pulls regardless of how the range resolves, unlike an
	// installed target path, which may itself template {{.Version}} and therefore isn't knowable
	// until after this resolution completes.
	SourceForGitURI string
	// Constraints are the source's separate `constraints:` block. Only ExcludedVersions/
	// NoPrereleases are applied here -- Constraints.Version is an unrelated concept (the ceiling
	// `atmos vendor update`'s bump search may not exceed) and is never consulted by this function.
	Constraints *schema.VendorConstraints
	// RefreshLock forces fresh resolution even when a matching lock entry exists.
	RefreshLock bool
	// Lister lists remote Git tags; defaults to version.DefaultLister when nil.
	Lister version.RemoteLister
}

VersionResolveParams configures ResolveDeclaredVersion.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL