config

package
v1.223.1-rc.2 Latest Latest
Warning

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

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

Documentation

Overview

Package config provides scaffold configuration management: the AtmosScaffoldConfig manifest kind, the interactive setup form, and the project record written to generated projects.

NOTE: This package uses github.com/charmbracelet/huh v0.6.0 instead of v0.7.0 due to a Terminal.app layout breaking issue in v0.7.0. See: https://github.com/charmbracelet/huh/issues/631

The issue causes line duplication when navigating between fields in Mac OS Terminal.app with tab key or arrow keys. This was introduced around commit hash 310cd4a379ac and affects all versions up through 0.7.0. Version 0.6.0 and earlier work correctly.

The package is split across a few files by responsibility to keep each one focused and under the repo's file-length limit:

  • config.go: the AtmosScaffoldConfig manifest types and registration.
  • persistence.go: loading/saving scaffold config and project records.
  • validation.go: field-value validation and type coercion.
  • form.go: the interactive huh-based setup form.

Index

Constants

View Source
const (
	ColorWhite  = "White"
	ColorBlack  = "Black"
	ColorBlue   = "Blue"
	ColorRed    = "Red"
	ColorGrey   = "Gray"
	ColorPurple = "Magenta"
)

Color constants for consistent styling using lipgloss named colors.

View Source
const ScaffoldConfigDir = ".atmos"

ScaffoldConfigDir is the directory name for user scaffold configuration.

View Source
const ScaffoldConfigFileName = "scaffold.yaml"

ScaffoldConfigFileName is the name of the scaffold configuration file.

View Source
const ScaffoldKind = "AtmosScaffoldConfig"

ScaffoldKind is the manifest kind for scaffold templates and project records.

View Source
const SourceEmbedded = "embedded"

SourceEmbedded marks a project record generated from a template embedded in the Atmos binary.

Variables

This section is empty.

Functions

func CoerceFieldValueTypes

func CoerceFieldValueTypes(scaffoldConfig *ScaffoldConfig, values map[string]interface{}) error

CoerceFieldValueTypes converts string values for boolean-typed fields (confirm/bool/boolean) to native Go bools, in place. --set (and other external string sources) always supplies raw strings; without this, a value like "false" stays the truthy non-empty string "false" for both Go template interpolation and When condition evaluation (e.g. `answers.x == true` never matches a string). Values that aren't strings (YAML defaults, or bools already returned by an interactive confirm prompt) are left untouched. Invalid external boolean values return an error rather than silently changing the result of a conditional expression.

func DecodeHooks

func DecodeHooks(raw map[string]any) (map[string]hooks.Hook, error)

DecodeHooks converts a ScaffoldSpec's raw Hooks map into typed pkg/hooks.Hook values by round-tripping through YAML, the same technique pkg/hooks.StepFromHook uses to decode a hook's own `with:` block -- Hook is designed to unmarshal from YAML, not to be reflected into JSON Schema.

func DeepMerge

func DeepMerge(scaffoldConfig *ScaffoldConfig, userValues map[string]interface{}) map[string]interface{}

DeepMerge merges scaffold field defaults with user values. Field order is irrelevant for the merge itself, but defaults come from the ordered field list.

func GetConfigPath

func GetConfigPath() (string, error)

GetConfigPath returns the path where the config directory should be stored based on the user's home directory and returns an error if the user home directory cannot be determined.

func GetConfigurationSummary

func GetConfigurationSummary(scaffoldConfig *ScaffoldConfig, mergedValues map[string]interface{}, valueSources map[string]string) ([][]string, []string)

GetConfigurationSummary returns table rows and header representing scaffold configuration, merged values, and their sources. Rows follow the declared field order.

func HasScaffoldConfig

func HasScaffoldConfig(files []types.File) bool

HasScaffoldConfig checks if a configuration contains a scaffold.yaml file.

func HasUserConfig

func HasUserConfig(targetPath string) bool

HasUserConfig checks if a generated project at the specified targetPath contains a project record, returning true if the file exists.

func LoadUserValues

func LoadUserValues(targetPath string) (map[string]interface{}, error)

LoadUserValues loads previously saved answers from the project record in targetPath. Returns an empty map if no record exists.

func MissingRequiredValues

func MissingRequiredValues(scaffoldConfig *ScaffoldConfig, values map[string]interface{}) []string

MissingRequiredValues returns the names of required fields that have no usable (non-nil, non-empty-string) value in the provided values map. Used to fail fast in non-interactive mode instead of generating a broken project. A field whose When condition evaluates false against the already-known values is not prompted for interactively either, so it is never treated as missing here.

func PromptForScaffoldConfig

func PromptForScaffoldConfig(scaffoldConfig *ScaffoldConfig, userValues map[string]interface{}) error

PromptForScaffoldConfig prompts the user for scaffold configuration values using a dynamic form built from the provided ScaffoldConfig; userValues supplies initial values and is populated with results; returns an error on failure.

func ReadAtmosScaffoldSection

func ReadAtmosScaffoldSection(targetPath string) (map[string]interface{}, error)

ReadAtmosScaffoldSection reads only the scaffold section from atmos.yaml.

NOTE: This is a temporary shim for the init experiment. In the full atmos CLI, this functionality will be integrated into the main atmos configuration handling system which has robust support for reading and validating atmos.yaml files.

Use yaml.v3 directly instead of Viper to preserve the original key casing. Viper's AllSettings() lowercases all keys, which mangles mixed-case fields such as projectName → projectname.

func ReadScaffoldConfig

func ReadScaffoldConfig(targetPath string) (map[string]interface{}, error)

ReadScaffoldConfig reads scaffold configuration from atmos.yaml at the provided targetPath; returns an empty map and nil error when the file does not exist; returns a wrapped error when reading or parsing fails.

Use yaml.v3 directly instead of Viper to preserve the original key casing. Viper's AllSettings() lowercases all keys, which mangles mixed-case fields such as projectName → projectname.

func SaveProjectRecord

func SaveProjectRecord(targetPath string, templateConfig *ScaffoldConfig, source, baseRef string, values map[string]interface{}) error

SaveProjectRecord writes the AtmosScaffoldConfig project record to .atmos/scaffold.yaml within targetPath. The record is the template's own manifest with the user's answers and provenance merged in:

  • metadata identifies the template (name, version) at generation time
  • spec.fields snapshots the questionnaire so the project is self-describing
  • spec.values holds the answers
  • spec.source and spec.baseRef record provenance for future updates

The record is marshaled directly to YAML (never through viper) so field name casing is preserved exactly.

func ValidateFieldValues

func ValidateFieldValues(scaffoldConfig *ScaffoldConfig, values map[string]interface{}) error

ValidateFieldValues validates every active declared field against its required, type, pattern, and option constraints. It deliberately ignores undeclared values so templates can continue accepting extensibility values supplied through --set.

Types

type Config

type Config map[string]interface{}

Config represents the user's configuration values as a generic map to support dynamic fields from scaffold.yaml.

type FieldDefinition

type FieldDefinition struct {
	Name        string           `yaml:"name" json:"name" jsonschema:"description=Field name used as the template variable"`
	Type        string           `` /* 178-byte string literal not displayed */
	Label       string           `yaml:"label,omitempty" json:"label,omitempty" jsonschema:"description=Short prompt label"`
	Description string           `yaml:"description,omitempty" json:"description,omitempty" jsonschema:"description=Longer help text shown with the prompt"`
	Required    bool             `yaml:"required,omitempty" json:"required,omitempty" jsonschema:"description=Whether a value must be provided"`
	Default     any              `yaml:"default,omitempty" json:"default,omitempty" jsonschema:"description=Default value"`
	Options     []string         `yaml:"options,omitempty" json:"options,omitempty" jsonschema:"description=Choices for select and multiselect fields"`
	Placeholder string           `yaml:"placeholder,omitempty" json:"placeholder,omitempty" jsonschema:"description=Placeholder text for input fields"`
	Validation  *FieldValidation `` /* 127-byte string literal not displayed */
	// When gates whether this field is prompted for, evaluated against
	// answers collected from fields declared earlier in Fields (as the
	// `answers` CEL variable). Empty always prompts. Schema-restricted to a
	// predicate/CEL string or a list (implicit all): invopop reflects
	// Condition's oneOf branches alongside a sibling additionalProperties:
	// false (Condition has no exported fields), so including "object" here
	// would make the {all:/any:/not:} map form fail schema validation even
	// though pkg/condition parses it -- confirmed empirically, not assumed.
	// Use CEL's &&/||/! for compound conditions instead.
	When condition.Condition `` /* 247-byte string literal not displayed */
}

FieldDefinition defines a single questionnaire field: its name, prompt type, presentation, validation, and default value.

type FieldValidation

type FieldValidation struct {
	// Pattern is a regular expression the value must match (for input fields).
	Pattern string `yaml:"pattern,omitempty" json:"pattern,omitempty" jsonschema:"description=Regular expression the value must match"`
	// Message is the error message shown when validation fails.
	Message string `yaml:"message,omitempty" json:"message,omitempty" jsonschema:"description=Error message shown when validation fails"`
}

FieldValidation constrains the allowed values for a FieldDefinition.

type FileSpec

type FileSpec struct {
	// Path is the file's path as discovered in the template's file tree
	// (matched against the file's original, pre-template-rendering path).
	Path string `yaml:"path" json:"path" jsonschema:"description=File path as discovered in the template's file tree"`
	// When gates generation of this file. Evaluated against the collected
	// answers (as the `answers` CEL variable); a false result skips the
	// file entirely. Empty always generates the file. Schema-restricted to
	// a predicate/CEL string or a list (implicit all) -- the {all:/any:/not:}
	// map form is deliberately excluded here (see the comment on
	// FieldDefinition.When for why) even though pkg/condition itself parses
	// it; use CEL's &&/||/! instead.
	When condition.Condition `` /* 243-byte string literal not displayed */
}

FileSpec optionally gates whether an auto-discovered template file is generated, based on the collected field answers.

type ScaffoldConfig

type ScaffoldConfig = manifest.Manifest[ScaffoldSpec]

ScaffoldConfig is the AtmosScaffoldConfig manifest. The same document kind serves two roles:

  • In a template: metadata plus spec.fields describe the questionnaire.
  • In a generated project (.atmos/scaffold.yaml): the same document with spec.values (the user's answers) and provenance (spec.source, spec.baseRef) merged in, making the project self-describing for future updates.

func LoadProjectRecord

func LoadProjectRecord(targetPath string) (*ScaffoldConfig, error)

LoadProjectRecord loads the AtmosScaffoldConfig project record from .atmos/scaffold.yaml within targetPath. Returns nil without error if no record exists.

func LoadScaffoldConfigFromContent

func LoadScaffoldConfigFromContent(content string) (*ScaffoldConfig, error)

LoadScaffoldConfigFromContent loads and validates an AtmosScaffoldConfig manifest from YAML content.

func LoadScaffoldConfigFromFile

func LoadScaffoldConfigFromFile(configPath string) (*ScaffoldConfig, error)

LoadScaffoldConfigFromFile loads and validates an AtmosScaffoldConfig manifest from the specified YAML file.

type ScaffoldSpec

type ScaffoldSpec struct {
	// Source records where the template came from (e.g. "embedded", a local
	// path, or a git URL once remote sources are supported). Written to
	// project records; ignored in template manifests.
	Source string `` /* 130-byte string literal not displayed */
	// BaseRef records the git ref used as the three-way merge base when the
	// project was generated. Written to project records.
	BaseRef string `yaml:"baseRef,omitempty" json:"baseRef,omitempty" jsonschema:"description=Git ref used as the three-way merge base"`
	// Delimiters optionally overrides the Go template delimiters used when
	// rendering template files (exactly two entries: left and right).
	Delimiters []string `` /* 144-byte string literal not displayed */
	// Fields defines the questionnaire shown when generating from this
	// template. Order is preserved: fields prompt in the order listed.
	Fields []FieldDefinition `yaml:"fields,omitempty" json:"fields,omitempty" jsonschema:"description=Ordered questionnaire fields"`
	// Values holds the user's answers. Written to project records; may also
	// provide preset values in template manifests.
	Values map[string]any `yaml:"values,omitempty" json:"values,omitempty" jsonschema:"description=Field values (answers) keyed by field name"`
	// Files optionally gates generation of specific auto-discovered template
	// files based on collected answers. Files not listed here always
	// generate (subject to the existing path-templating skip behavior).
	Files []FileSpec `` /* 138-byte string literal not displayed */
	// Hooks runs step-backed actions around generation, keyed by hook name.
	// Reuses the exact schema and vocabulary of stack-level lifecycle hooks
	// (events, kind, when, type, with) -- see pkg/hooks.Hook and the
	// atmos-hooks skill. Only kind: step and kind: steps are supported for
	// scaffold hooks; the events are before.scaffold.generate and
	// after.scaffold.generate. Kept as a raw map (not map[string]hooks.Hook)
	// because pkg/hooks.Hook has no json tags -- like the stacks JSON Schema,
	// which hand-authors its own "hooks" definition rather than reflecting
	// the Go struct, this stays untyped for schema purposes and is decoded
	// into hooks.Hook via DecodeHooks at the point of use.
	Hooks map[string]any `` /* 147-byte string literal not displayed */
}

ScaffoldSpec is the spec of an AtmosScaffoldConfig manifest.

Jump to

Keyboard shortcuts

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