Documentation
¶
Overview ¶
Package provenance is the pure-Go leaf that owns the env-debug data model and its human/JSON rendering. It imports NEITHER compose-go NOR internal/engine — engine imports IT for the shared types, so this package stays a fast, dependency-light leaf (the CI seam check guards that compose-go never leaks in).
Index ¶
- func RenderGapReportHuman(w io.Writer, gr GapReport, s Styler)
- func RenderGapReportJSON(w io.Writer, gr GapReport) error
- func RenderHuman(w io.Writer, r Report, o HumanOpts)
- func RenderJSON(w io.Writer, r Report) error
- type Effect
- type EnvEntry
- type GapReport
- type GapSite
- type HumanOpts
- type OverviewEntry
- type OverviewLayer
- type Report
- type ServiceEnv
- type ServiceVal
- type Source
- type Styler
- type VarTrace
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RenderGapReportHuman ¶
RenderGapReportHuman writes a human gap report: one ⚠ line per gap site plus a red summary, or a single green ok line when clean. nil styler => plain.
func RenderGapReportJSON ¶
RenderGapReportJSON writes the gap report as indented JSON (never styled).
func RenderHuman ¶
RenderHuman writes the selected view as plain (or styled) text. The styler is resolved once (nil ⇒ plain) and threaded into each view.
Types ¶
type Effect ¶
type Effect struct {
Service string `json:"service"`
Field string `json:"field"`
Resolved string `json:"resolved"`
Gap bool `json:"gap"` // ${VAR} is env_file-only -> falls back at the run
}
Effect is one place a variable's ${VAR} reference took effect in the compose model: the service, the dotted+[i] field path, and the resolved string.
Resolved is the REAL run value — the value the live `docker compose` run would interpolate against the Layer-1-only env. A var defined ONLY in a service env_file: resolves to its `:-default`/empty fallback here (Gap=true), because service env_files are runtime-only and never feed interpolation (v3, #3435).
type EnvEntry ¶
type EnvEntry struct {
Key string `json:"key"`
Value string `json:"value"`
Source Source `json:"source"`
}
EnvEntry is one key in a service's effective environment with its source (C).
type GapReport ¶
GapReport is the gap-report projection of a Report: every gap site + a count.
func CollectGaps ¶
CollectGaps projects a Report into its gap sites. Order is deterministic: var name, then the engine-sorted Effect order (service, field). A gap site is every Effect of a var whose Gap is true (engine already set Gap = referenced && !InChain && len(RuntimeDefs)>0, internal/engine/provenance.go:424).
type GapSite ¶
type GapSite struct {
Var string `json:"var"`
Service string `json:"service"`
Field string `json:"field"`
Fallback string `json:"fallback"`
Fix string `json:"fix"`
}
GapSite is one place an env_file:-only ${VAR} reaches the compose model and so falls back at the real run (the #3435 gap). Field names mirror Effect (service/field); Fallback is the value the run interpolates (Effect.Resolved, human-normalized via stripVarPrefix).
type HumanOpts ¶
type HumanOpts struct {
Trace string // non-empty: single-var trace (A + B-lite)
Effective bool // per-service env (C)
Service string // filter Effective to one service
Chain bool // Layer-1-only list (default view) — renders Report.ChainFiles
Files bool // two-group view: interpolation (Report.Files) + runtime-only (Report.Services)
Value string // non-empty: print the winning value only
Overview bool // per-file layering overview (raw values, +/~/· markers) — renders Report.Layers
// Header inputs for the --overview mode (best-effort; from chain.Resolve + cmd).
ComposeEnv string // resolved CENVKIT_ENV value
ComposeEnvSource string // where it came from: "shell" | ".env" | "default"
ProjectDir string // project dir (basename used as the overview title)
Style Styler // optional color/formatting; nil ⇒ plain (byte-identical)
}
HumanOpts selects which view RenderHuman emits. Exactly one mode is active at a time; the switch in RenderHuman picks the first set field in precedence order.
type OverviewEntry ¶
type OverviewEntry struct {
Key string `json:"key"`
RawValue string `json:"raw_value"` // literal as written; ${...} unexpanded
}
OverviewEntry is one KEY=VALUE line of a file, captured LITERALLY in declaration order for the --overview lens: RawValue is exactly as written, with ${...} left UNexpanded (resolved values are --effective's job). Sourced from a thin ordered line read, NOT compose-go's dotenv parser (which is unordered and expands refs).
type OverviewLayer ¶
type OverviewLayer struct {
File string `json:"file"` // abs path, or "(inline environment:)"
Layer string `json:"layer"` // "layer1" | "env_file" | "environment"
Service string `json:"service,omitempty"` // "" for chain; service name for runtime layers
Entries []OverviewEntry `json:"entries"` // declaration order
}
OverviewLayer is one file (or the inline-environment pseudo-layer) contributing to the layering overview, with its raw entries in declaration order. Layers are ordered on the Report: all Layer-1 chain files (chain order) first, then per active service (sorted) its env_file: layers (declared order) followed by its inline-environment layer. The +/~/· markers are NOT stored — they are derived at render time from an accumulator walk.
type Report ¶
type Report struct {
Files []string `json:"files"` // new COMPOSE_ENV_FILES order (Layer-1 ONLY; == ChainFiles)
ChainFiles []string `json:"chain_files"` // Layer-1 chain order (kept for the --chain path; D2)
Vars map[string]VarTrace `json:"vars"` // A + B-lite + gap (InChain/RuntimeDefs/Gap)
Services []ServiceEnv `json:"services,omitempty"` // C (also the runtime-only group for --files)
Layers []OverviewLayer `json:"layers,omitempty"` // ordered raw per-file layers for --overview (populated only when WantLayers)
}
Report is the whole env-debug picture: the ordered COMPOSE_ENV_FILES, the per-variable traces (A + B-lite), and the per-service effective env (C, empty in chain-only mode).
Post-v3 (Layer-2 debug-only): Files is the new COMPOSE_ENV_FILES = Layer-1 ONLY, so Files == ChainFiles by construction. Both fields are retained (D2): the runtime-only Layer-2 set the `--files` two-group view shows comes from Services (service `env_file:` paths), NOT from Files. `--chain` (and the bare default view) renders ChainFiles — secrets stay last WITHIN the Layer-1 chain (acceptance TestScenario12 [12.4]).
type ServiceEnv ¶
type ServiceEnv struct {
Service string `json:"service"`
Entries []EnvEntry `json:"entries"`
EnvFiles []string `json:"env_files,omitempty"` // declared env_file: paths, declared order (runtime-only; for --files)
}
ServiceEnv is a service's effective environment with per-key sources (C).
EnvFiles is the service's DECLARED `env_file:` paths in declared order — the runtime-only set the `--files` two-group view renders. It is kept separate from Entries because Entries' per-key Source can be rewritten to "(inline environment:)" when an inline `environment:` key shadows an env_file key; if EVERY key of a file is overridden, that file would vanish from an entries-derived list. Declaring the paths here keeps `--files` faithful to what the service actually loads at runtime, regardless of inline overrides.
type ServiceVal ¶
type ServiceVal struct {
Service string `json:"service"`
File string `json:"file"`
Value string `json:"value"`
}
ServiceVal is one service `env_file:` definition of a variable: the service it is declared under, the env_file path, and the value. It is gap EVIDENCE — runtime-only (per the service's container), NOT part of the interpolation env.
type Source ¶
type Source struct {
File string `json:"file"`
Layer string `json:"layer"` // layer1 | layer2 | env_file | environment
}
Source identifies where a value came from: a concrete file (or a synthetic "(environment)" / "(inline environment:)" marker) and which layer set it.
type Styler ¶
type Styler interface {
Header(s string) string // section headers — bold cyan
MarkerNew() string // "+" new — green
MarkerOverride() string // "~" override — yellow
MarkerRepeat() string // "·" repeat — dim
Arrow() string // "→" between old/new — dim
Key(s string) string // KEY name — bold
Value(s string) string // a value — normal/readable
Old(s string) string // the shadowed (old) value — dim
Path(s string) string // file path — cyan
Service(s string) string // service name — bold magenta
SourceLabel(s string) string
Gap(s string) string // gap line body — red
GapName(s string) string // the gapped var name — bold red
Hint(s string) string // a dim advisory line (e.g. empty-chain hint) — dim
Ok(s string) string // validate ok — green
Fail(s string) string // validate fail — red
Created(s string) string // init created — green
Skipped(s string) string // init skipped — dim
ErrorMsg(s string) string
}
Styler renders semantic elements with optional color/formatting. It is defined HERE (not in internal/style) so internal/provenance imports NO styling library — the lipgloss-backed implementation lives in internal/style and is injected via HumanOpts.Style. A nil Style falls back to plainStyler (byte-identical plain), so callers that don't set it (and the existing render tests) are unchanged.
Methods either style a passed string (Header(s) → styled s) or, for the markers and arrow which have no payload, return the styled glyph (MarkerNew() → "+").
type VarTrace ¶
type VarTrace struct {
Name string `json:"name"`
Value string `json:"value"`
Winner Source `json:"winner"`
Overridden []Source `json:"overridden,omitempty"`
Effects []Effect `json:"effects,omitempty"`
InChain bool `json:"in_chain"` // resolvable from the interpolation (Layer-1 + shell) env?
RuntimeDefs []ServiceVal `json:"runtime_defs,omitempty"` // service env_file: defs (runtime-only; gap evidence)
Gap bool `json:"gap"` // referenced && !InChain && len(RuntimeDefs)>0
}
VarTrace is the full story for one variable: its winning value + source, the sources it overrode (A), where it took effect in the compose model (B-lite), and — post-v3 — whether it is a runtime-vs-interpolation gap.
Value/Winner/Overridden attribute over the INTERPOLATION env only (Layer-1 chain + shell overlay). A var defined only in a service env_file: has no chain winner (empty Winner) and InChain=false; its env_file definitions live in RuntimeDefs as gap evidence.