freshness

package
v1.226.0-test.12 Latest Latest
Warning

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

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

Documentation

Overview

Package freshness computes the checksum.changed/timestamp.changed/preconditions.success/ sources/artifacts facts consumed via the existing `when:` condition engine for a step's `inputs:`/`artifacts:`/`preconditions:`, and persists sources-hash state across runs for checksum comparison. See pkg/condition for how these facts are exposed to `when:`.

Index

Constants

This section is empty.

Variables

View Source
var ErrGlobInvalid = errors.New("invalid sources/artifacts glob pattern")

ErrGlobInvalid is returned when an inputs.sources/artifacts.paths glob pattern is malformed (distinct from "matched nothing," which is not an error).

Functions

func EffectiveWhen

func EffectiveWhen(when schema.Condition, declared StepDeclarations) schema.Condition

EffectiveWhen returns when, or an implicit condition synthesized from which of declared.Inputs/Artifacts/Preconditions are present, if when is unset (zero) -- so declaring `inputs:`/`artifacts:`/`preconditions:` alone (no explicit `when:`) is enough to skip a step whose work is already done, matching go-task's zero-boilerplate default.

func MentionsAnyFreshnessFact

func MentionsAnyFreshnessFact(when schema.Condition) bool

MentionsAnyFreshnessFact reports whether when references any freshness-derived CEL identifier. Callers that need a cheap "might this step run" answer without a freshness.Checker on hand yet (facts would all read as their Go zero value, i.e. always "unchanged" -- wrong even for a step's very first-ever run) should treat a true result as "assume runnable" and defer the real decision to wherever Compute's actual facts get evaluated, rather than evaluating when here against an empty Context.

func StateDir

func StateDir(basePath string) string

StateDir returns the project-relative directory where freshness state (recorded sources hashes for checksum.changed) persists, rooted under the project's own base path rather than an XDG user-cache directory specifically so it composes with the existing CI-cache feature: a user can add this same directory to ci.cache.includes: and freshness state survives across CI runs the same way `.terraform/` would.

Types

type Checker

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

Checker computes freshness Facts for a step's schema.Inputs/Artifacts/Preconditions and persists checksum state across runs. Constructed via NewChecker with Options (>2-3 logical dependencies), defaulting to real production implementations.

func NewChecker

func NewChecker(opts ...Option) *Checker

NewChecker constructs a Checker with real production defaults, overridable via Option.

func (*Checker) Compute

func (c *Checker) Compute(effectiveWhen schema.Condition, declared StepDeclarations, id StepIdentity) (Facts, error)

Compute lazily computes only the facts effectiveWhen actually references (see schema.Condition.MentionsCELIdentifier), so a step whose `when:` only mentions `timestamp` never pays the cost of hashing file content, and a step that never references the bare `sources`/`artifacts` identifiers never pays the cost of building per-file records. Id.BaseDir is the step's own working directory; id.StateDir is where checksum state persists; id.Scope+id.StepName form the stable per-step identity used to key that state (see stateKey).

func (*Checker) RecordSuccess

func (c *Checker) RecordSuccess(inputs *schema.Inputs, baseDir, stateDir, scope, stepName string) error

RecordSuccess persists the current sources checksum for the next run's comparison. Call ONLY after the step's own Execute() returns success -- a failed step must never mark itself falsely up to date. Preconditions has no persisted state (exec.LookPath is always evaluated live), so it has nothing to record here -- callers gate this call on inputs/artifacts being declared at all, not on inputs.Sources being non-empty: an artifacts-only step (inputs == nil) still needs a record of the (empty) sources hash, or checksumChanged's `!found` branch reports "changed" forever and the step never stabilizes to "skip" even once its artifacts exist and are unchanged.

type Facts

type Facts struct {
	ChecksumChanged      bool
	TimestampChanged     bool
	PreconditionsSuccess bool
	// Sources/Artifacts are structured per-file records, populated only when a step's `when:`
	// actually references the bare `sources`/`artifacts` identifiers (see Compute) -- building
	// per-file mtime/checksum data is wasted work for the common case where only the
	// checksum.changed/timestamp.changed convenience facts are used.
	Sources   []condition.FileFact
	Artifacts []condition.FileFact
}

Facts carries the freshness facts computed for one step evaluation, merged into the caller's schema.ConditionContext before evaluating `when:` (see pkg/condition's ChecksumChanged/ TimestampChanged/PreconditionsSuccess/Sources/Artifacts fields).

type Globber

type Globber interface {
	Glob(baseDir, pattern string) ([]string, error)
}

Globber resolves a glob pattern (relative to baseDir) to matching absolute file paths. Abstracted for testability (see mockGlobber in tests) -- the real implementation wraps pkg/filesystem.GetGlobMatches, the same doublestar-based, LRU-cached glob engine already used elsewhere in Atmos, rather than pkg/filematch (used by `atmos validate schema`'s `matches:`), because filematch.MatchFiles resolves against the process's current working directory with no baseDir parameter, and freshness must resolve relative to the step's own working directory, which can differ from process CWD.

func NewGlobber

func NewGlobber() Globber

NewGlobber returns the real, production Globber.

type LookupTool

type LookupTool func(name string) (string, error)

LookupTool resolves one preconditions.tools entry, mirroring exec.LookPath's signature. Abstracted for testability; the default is exec.LookPath directly -- no shell involved, so there's no which-vs-where cross-platform mismatch to work around.

type Option

type Option func(*Checker)

Option configures a Checker.

func WithGlobber

func WithGlobber(g Globber) Option

WithGlobber overrides the Globber (default: NewGlobber(), wrapping pkg/filesystem).

func WithHasher

func WithHasher(h func([]string) (string, error)) Option

WithHasher overrides the content hasher (default: pkg/hashfile.HashFiles).

func WithLookupTool

func WithLookupTool(l LookupTool) Option

WithLookupTool overrides the LookupTool (default: exec.LookPath).

func WithStateStore

func WithStateStore(s StateStore) Option

WithStateStore overrides the StateStore (default: NewStateStore(), one JSON file per key).

type Record

type Record struct {
	SourcesHash string `json:"sources_hash"`
}

Record is the persisted state for one step's checksum-based freshness check.

type StateStore

type StateStore interface {
	Load(stateDir, key string) (Record, bool, error)
	Save(stateDir, key string, r Record) error
}

StateStore persists/retrieves the last-recorded Record for a step, keyed by a caller-computed stable identity (see Checker.stateKey). Abstracted for testability -- the real implementation is one JSON file per key under stateDir, guarded by pkg/cache.FileLock on platforms where it provides real mutual exclusion. On Windows, pkg/cache.FileLock is explicitly best-effort (no native locking there); Save always writes to a uniquely-named temp file before the final rename, so concurrent writers never collide on the temp file itself regardless of platform, but a concurrent Save and Load can still transiently fail against each other on Windows if they land on the exact same instant -- callers already treat a Save/RecordSuccess failure as log-and-continue, not fatal, which is the correct posture for a best-effort cache.

func NewStateStore

func NewStateStore() StateStore

NewStateStore returns the real, production StateStore.

type StepDeclarations

type StepDeclarations struct {
	Inputs        *schema.Inputs
	Artifacts     *schema.Artifacts
	Preconditions *schema.Preconditions
}

StepDeclarations groups a step's three freshness-related declarations (Inputs/Artifacts/ Preconditions are deliberate siblings, not one nested inside another -- see their own doc comments in pkg/schema/task.go for why). Grouped into one struct so EffectiveWhen/Compute stay within the project's per-function argument limit, and so callers build the group once and pass it to both.

type StepIdentity

type StepIdentity struct {
	BaseDir  string
	StateDir string
	Scope    string
	StepName string
}

StepIdentity groups the location/identity parameters needed to compute and persist freshness state for one step.

Jump to

Keyboard shortcuts

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