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
- Variables
- func ComponentOrMixinsCopy(sourceFile, finalTarget string) error
- func ResolveDeclaredVersion(ctx context.Context, atmosConfig *schema.AtmosConfiguration, ...) (string, error)
- func ResolveEffectiveVersion(in *ResolveEffectiveVersionInputs) (resolved, raw string, err error)
- type AtmosPackageParams
- type ComponentPackageParams
- type CopyContext
- type FileCopier
- type InstallOptions
- type PkgType
- type PrefixCopyContext
- type ResolveEffectiveVersionInputs
- type Result
- type VendorPackage
- type VersionResolveParams
Constants ¶
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 ¶
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 ¶
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.
func DeterminePackageType ¶
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).
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 ¶
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.