script

package
v1.233.0-rc.0 Latest Latest
Warning

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

Go to latest
Published: Oct 16, 2026 License: Apache-2.0 Imports: 23 Imported by: 0

README

Embedded script host

script.Engine is the boundary between Atmos execution hosts and embedded languages. Workflow steps, custom commands, hooks, and standalone files pass a Spec to an engine and receive a Result. Starlark is currently the only registered language.

Registration and file dispatch

An interpreter registers its name, engine, and optional standalone file metadata:

script.Register("starlark", engine,
    script.WithExtensions(".star"),
    script.WithAtmosShebang(),
)

Workflow interpreter: values use the name registry. Standalone files use the extension registry. Extensions are case-sensitive and include the leading dot. A registered extension takes precedence over a file's shebang. Selection uses the invoked filename, before resolving symlinks; the resolved path is used to read the source and locate imports.

Explicit paths with an Atmos shebang use the registered shebang fallback when there is no recognized extension. Starlark owns that fallback. Bare extensionless names remain Atmos commands. Unknown extensions without an Atmos shebang do not select an embedded interpreter.

Registration rejects invalid metadata and conflicting extension or shebang owners before changing the registry. Replacing an engine under the same name preserves its associations. This allows CLI initialization to inject the completed command catalog without losing standalone dispatch.

Stdin execution

atmos - [args...] reads a standalone script from stdin. The optional -- immediately following - is consumed; later arguments belong to the script. atmos --interpreter=starlark - [args...] selects an engine by its registered name. Without that option, the registered Atmos shebang fallback is used. A bare -- does not select stdin execution.

Detection does not read stdin. The CLI reads it through EOF when the standalone command runs, after normal initialization. File.Stdin distinguishes stdin from a physical file; File.Path is then -. Starlark reports <stdin> in tracebacks, exposes ctx.script = None, and resolves relative imports from the working directory. The standalone command parser still provides typed arguments, flags, validation, and help.

Shared host services

  • PrepareSpec normalizes paths and provides discard streams for absent writers. Callers pass their own invocation copy; maps and slices are treated as read-only.
  • RunProcess owns capture, optional streaming, cancellation, and exit-status policy. check=false tolerates ordinary nonzero exits, but still reports launch, signal, cancellation, and I/O failures. Terraform's plan exit-code exception is explicit. The interpreter supplies writers with masking and task attribution. Optional execution policies come from pkg/automation: one timeout bounds all attempts and backoff. Only ordinary nonzero exits with Check=true retry; output conditions inspect each attempt, and returned capture is from the last attempt.
  • ProcessEnvironment copies inherited values and applies explicit overrides. Empty process environments remain empty rather than inheriting host secrets.
  • Tools owns pins and installed directories per invocation. A successful pin is reused; conflicting versions fail. Failed or canceled installation does not publish state. Interpreters decide when declarations are permitted.
  • Diagnostic carries immutable authored error metadata through wrappers. EnrichDiagnostics applies it at the reporting boundary, innermost first, so outer metadata wins and shared causes are not applied twice. The native error builder and reporting path continue to own rendering and telemetry.

Host configuration errors use ErrScript. Starlark evaluation failures retain ErrStarlark; its binding also preserves the existing Starlark process and argument classifications when translating shared service errors.

Execution contract

Each invocation owns its mutable state. Engines must honor cancellation in both language evaluation and host operations. Dry runs validate source without executing scripts or host services; recursive validation of imported modules is not implied.

Stdout and stderr are separate from the explicit result. HasOutput=false means no result was produced. When present, strings pass through unchanged, including empty strings; other supported language values are JSON-encoded by the engine. The Starlark binding treats top-level None as no output; null values inside containers are JSON-encoded. Workflow named outputs remain the runner's responsibility.

Language bindings own argument conversion, module resolution, language values, callbacks, thread rules, and tracebacks. Parallel scheduling and retries reuse Atmos services, while callable isolation remains interpreter-specific. The host contract does not prescribe an event loop, cross-language values, or a universal module loader.

Step library

Spec.Steps supplies the language-independent automation.StepLibrary Go API. Hosts construct it from their existing step variables and workflow context. Starlark only maps function names, converts arguments/results, and forks the API for each parallel task attempt. The step registry owns validation and execution. See the automation API for the Go contract and ownership rules. A standalone program and an embedded script use the same interface.

Documentation

Overview

Package script defines embedded interpreter registration and shared host services.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AnchorPath

func AnchorPath(args []string, dir string) []string

AnchorPath returns args with a relative script path (the first argument) made absolute against dir, so a later change of the working directory cannot change which file it names. Stdin selections, flags, absolute paths, and arguments that are not script paths come back unchanged. The input is never modified.

func DiagnoseLeadingFlags

func DiagnoseLeadingFlags(args []string, takesValue func(name string, short bool) (takes, found bool), optionalValue func(name string, short bool) bool) error

DiagnoseLeadingFlags explains a command line that looks like a script invocation but whose leading flags Atmos cannot place. It returns nil when the arguments are not such an invocation, so the caller continues with normal command handling. Two mistakes are recognized:

  • A flag with an optional value written with a space, as in `--profile dev ./tool.star`. The flag cannot take the next word, so the word would be read as a command name.
  • An unknown flag before the script path, as in `--bogus ./tool.star`.

The takesValue callback is the same one SplitGlobalFlags uses. The optionalValue callback reports whether a known flag accepts an optional value (a non-boolean flag with a bare form).

func EnrichDiagnostics

func EnrichDiagnostics(builder *errUtils.ErrorBuilder, err error)

EnrichDiagnostics applies metadata innermost first, visiting each diagnostic once. Outer settings take precedence; distinct parallel failures retain theirs.

func InterpreterForFile

func InterpreterForFile(path string) (string, bool)

InterpreterForFile resolves a registered extension without reading the file.

func MayBeFile

func MayBeFile(args []string) bool

MayBeFile reports whether the first argument selects stdin, a registered extension, or an explicit script path. It looks only at the spelling, so callers can decide cheaply whether a file-system check is worthwhile.

func PrepareSpec

func PrepareSpec(spec *Spec) error

PrepareSpec normalizes invocation paths and supplies discard streams. The caller must pass its own copy; input maps and slices remain read-only throughout execution.

func ProcessEnvironment

func ProcessEnvironment(base []string, values map[string]string) []string

ProcessEnvironment copies inherited values and applies per-call overrides in stable order. Every duplicate inherited key is removed before its override.

func Register

func Register(name string, engine Engine, options ...RegistrationOption)

Register installs an interpreter, panicking on invalid or conflicting metadata. Replacing an engine under the same name preserves its existing extension and shebang associations, so hosts can inject services after CLI initialization.

func SelectionEnv

func SelectionEnv(profiles []string, identity string) map[string]string

SelectionEnv returns the environment entries that make a child Atmos process use the same profiles and identity as the running one. A global flag written on the command line, such as `--profile=dev`, exists only in the parent's memory; without these entries a nested `atmos` call would silently run with the default profile. Empty selections add no entry, and the bare `--identity` interactive-selection sentinel is never forwarded because a child cannot answer it.

func SplitGlobalFlags

func SplitGlobalFlags(args []string, takesValue func(name string, short bool) (takes, found bool)) (globals, rest []string, ok bool)

SplitGlobalFlags separates the leading Atmos global flags from the rest of the arguments, so a script path may follow them: `atmos --chdir=dir ./tool arg`. The takesValue callback reports whether a flag name (long without dashes, or one-letter shorthand) consumes the next argument when it is written without `=`, and found=false for flags Atmos does not define. A leading `--` ends the global flags. An unknown flag means the arguments are not a script invocation, so the arguments come back as rest with ok=false.

func ValidateWorkingDirectory

func ValidateWorkingDirectory(dir string) error

ValidateWorkingDirectory fails fast, before any code runs, when the directory is unusable.

Types

type CommandInput

type CommandInput struct {
	Args, Flags map[string]any
	Help        bool
	// Usage turns a rejection of the parsed input, such as a failed validate callback, into the
	// host's usage error: exit status 2, the usage line, and a --help hint. Nil when the host has
	// no such presentation.
	Usage func(cause error) error
}

CommandInput contains validated inputs, or Help when only usage was requested. Optional positional arguments that were omitted have a nil value.

type CommandParser

type CommandParser func(context.Context, CommandSpec) (CommandInput, error)

CommandParser is the host boundary for standalone command parsing and help. The language runtime calls user functions only after parsing succeeds.

type CommandSpec

type CommandSpec struct {
	Name, Description string
	Args              []*flags.PositionalArgSpec
	Flags             []flags.Flag
}

CommandSpec describes a standalone program's command-line interface using the same flag and positional argument definitions as native Atmos commands.

type Component

type Component struct {
	ComponentRef
	Implementation, Path string
	Config               map[string]any
	Env                  map[string]string
}

Component is resolved execution data, separate from the provider's command API.

type ComponentRef

type ComponentRef struct{ Name, Stack, Type string }

ComponentRef identifies a logical component instance in a stack.

type ComponentResolver

type ComponentResolver func(context.Context, ComponentRef) (*Component, error)

ComponentResolver resolves execution-time values, including YAML functions.

type Diagnostic

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

Diagnostic carries authored error metadata through module, retry and parallel wrappers. It is immutable; enrichment is applied only at the reporting boundary. Language bindings validate and snapshot arguments before constructing updates.

func NewDiagnostic

func NewDiagnostic(message string) *Diagnostic

NewDiagnostic creates an authored error without a language-specific wrapper.

func (*Diagnostic) Err

func (d *Diagnostic) Err() error

Err creates a distinct raised error from this immutable diagnostic template. Separate raises retain their own identity when parallel failures are aggregated.

func (*Diagnostic) Error

func (d *Diagnostic) Error() string

Error returns the message followed by its authored causes.

func (*Diagnostic) Message

func (d *Diagnostic) Message() string

Message returns the authored message without its cause chain.

func (*Diagnostic) Unwrap

func (d *Diagnostic) Unwrap() []error

Unwrap exposes causes without allowing callers to mutate the diagnostic.

func (*Diagnostic) With

func (d *Diagnostic) With(enrich func(*errUtils.ErrorBuilder)) *Diagnostic

With returns a new diagnostic with an additional metadata update. The update must capture immutable values and must not mutate shared state.

func (*Diagnostic) WithCause

func (d *Diagnostic) WithCause(cause error) *Diagnostic

WithCause returns a new diagnostic preserving the original cause chain.

type Engine

type Engine interface {
	Execute(context.Context, Spec) (Result, error)
}

Engine executes an embedded language without starting an interpreter process. Implementations isolate invocation state and honor context cancellation, including host operations. DryRun validates source without evaluating it or invoking host services; it does not promise recursive module validation. Streams carry process output separately from Result. Language values must not escape this boundary.

func Get

func Get(name string) (Engine, bool)

Get looks up an embedded interpreter; executable paths remain external.

type Evaluation

type Evaluation struct {
	Source, Filename string
	Line             int32
	Context          ValueMap
}

Evaluation describes a configuration function body and its source location. It deliberately supplies no process, filesystem, authentication, or step host.

type File

type File struct {
	// Path is the absolute, symlink-resolved location of the script, or - for stdin.
	Path string
	// Invoked preserves the spelling used in usage hints.
	Invoked     string
	Args        []string
	Interpreter string
	// Stdin selects source from standard input; Path is the marker "-", not a filesystem path.
	Stdin bool
}

File describes an explicitly invoked standalone program.

func DetectFile

func DetectFile(args []string) (*File, error)

DetectFile recognizes registered extensions, explicit paths with an Atmos shebang, and the standalone stdin marker (-), optionally preceded by --interpreter. It never infers script execution from terminal state or consumes script flags. Bare extensionless names remain CLI commands, avoiding command-name collisions.

type HookContext

type HookContext struct {
	Name, Event string
	Component   *Component
	Operation   Operation
	// ProcessOverrides preserves hook-level env precedence over component env.
	ProcessOverrides map[string]string
}

HookContext carries the component snapshot and lifecycle facts for one hook. It is host-owned and must not be mutated while an invocation is running.

type Operation

type Operation struct {
	Command        string
	Status, Error  *string
	ExitCode       *int
	Stdout, Stderr *string
}

Operation describes the parent lifecycle operation, not a hook subprocess. Nil result fields mean unavailable (including before-execution hooks).

type ProcessCall

type ProcessCall struct {
	Argv             []string
	Dir              string
	Env              []string
	Check, Stream    bool
	AllowPlanChanges bool
	Stdout, Stderr   io.Writer
	Policy           automation.ExecutionPolicy
}

ProcessCall describes a host command after language-specific argument conversion. Env is explicit; an empty environment never inherits the Atmos process environment.

type ProcessOutput

type ProcessOutput struct {
	Stdout, Stderr string
	ExitCode       int
}

ProcessOutput contains captured output, independently of whether it was streamed.

func RunProcess

func RunProcess(ctx context.Context, runner process.Runner, call *ProcessCall) (ProcessOutput, error)

RunProcess applies capture, streaming, cancellation and exit-status policy. Writers are supplied by the host/binding, retaining its masking and task attribution.

type RegistrationOption

type RegistrationOption func(*registration)

RegistrationOption declares how an interpreter is selected for standalone files.

func WithAtmosShebang

func WithAtmosShebang() RegistrationOption

WithAtmosShebang selects the interpreter for an extensionless Atmos shebang and for stdin scripts without an explicit --interpreter. Only one interpreter may own this fallback; a registered extension takes precedence.

func WithExtensions

func WithExtensions(extensions ...string) RegistrationOption

WithExtensions associates case-sensitive file extensions (including the dot) with an interpreter.

type Result

type Result struct {
	Value     string
	HasOutput bool
}

Result holds an explicitly produced output. Strings pass through unchanged; other values are JSON-encoded by the interpreter. HasOutput distinguishes no output from an empty string or JSON null.

type Spec

type Spec struct {
	Name, Source, WorkingDirectory string
	// SourcePath is the file Source was read from, when it came from one. It names the program
	// in diagnostics and anchors relative imports to that file's directory, instead of
	// WorkingDirectory. Name stays the step name used in hints.
	SourcePath string
	// ProjectRoot is the Atmos project base path. When set, paths under it are shown relative to
	// it (for example `scripts/boom.star:7:8`) in error messages and tracebacks. It only affects
	// display: import resolution and script context paths keep using absolute paths.
	ProjectRoot      string
	Env              map[string]string
	Flags, Arguments map[string]any
	// Args holds positional host arguments for embedded invocations.
	Args           []string
	ProcessEnv     []string
	Stdout, Stderr io.Writer
	DryRun         bool
	// Parallel marks execution inside a control runner that cannot grant exclusive terminal access.
	Parallel              bool
	Component             *ComponentRef
	ResolveComponent      ComponentResolver
	ProcessOverrides      map[string]string
	Hook                  *HookContext
	AtmosWorkingDirectory string
	File                  *File
	InstallTools          ToolInstaller
	// CI reports into the active CI provider (summaries, comments, checks, outputs) and
	// renders locally when none is detected. Hosts construct it with ci.NewReporter from
	// the invocation's Atmos configuration; nil means a local-only reporter.
	// pkg/ci must never import pkg/script, pkg/runner, or pkg/workflow, or this field
	// would create an import cycle.
	CI           ci.Reporter
	ParseCommand CommandParser
	Steps        automation.StepLibrary
	// Viewport shows a subprocess's live output in a bounded terminal viewport for
	// output="viewport". Nil means the host has none, and the output streams instead.
	Viewport ViewportRunner
}

Spec describes one invocation. Env is explicit script input; ProcessEnv is the effective environment for child processes and is not exposed as globals.

type ToolInstaller

type ToolInstaller func(context.Context, map[string]string) ([]string, error)

ToolInstaller provisions pinned tools and returns their executable directories.

type Tools

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

Tools owns pinned tool state for one invocation. Interpreters control when declarations are permitted (for example, before starting parallel tasks). Installation and environment snapshots are serialized to avoid partial state.

func NewTools

func NewTools(install ToolInstaller) *Tools

NewTools creates invocation-local tool state around the host installer.

func (*Tools) Environment

func (t *Tools) Environment(base []string) []string

Environment returns an isolated environment with installed tool directories ahead of the caller's PATH, preserving declaration order.

func (*Tools) Install

func (t *Tools) Install(ctx context.Context, name, version string) error

Install provisions a pin once and rejects conflicting versions. Failed or canceled installations do not alter the invocation's pins or PATH.

type UsageFailure

type UsageFailure struct{ Err error }

UsageFailure carries a host-presented usage error through a language runtime unchanged. The user mistyped the command line; the script did not fail, so the runtime reports Err as-is, without a language prefix or a traceback.

func (*UsageFailure) Error

func (u *UsageFailure) Error() string

Error returns the usage message.

func (*UsageFailure) Unwrap

func (u *UsageFailure) Unwrap() error

Unwrap exposes the usage error so errors.Is sees ErrScriptUsage.

type ValueEvaluator

type ValueEvaluator interface {
	EvaluateValue(context.Context, Evaluation) (any, error)
}

ValueEvaluator is the optional, typed configuration capability of an engine. Results contain only native configuration values, never interpreter objects.

type ValueMap

type ValueMap interface {
	Keys() []string
	Get(string) (any, bool, error)
}

ValueMap is a read-only configuration mapping. Get resolves a value only when it is accessed, allowing hosts to track dependencies between computed values.

type ViewportRunner

type ViewportRunner func(title string, run func(stdout, stderr io.Writer) error) error

ViewportRunner runs work while the host shows the output written to the writers it passes in a live viewport (and falls back to plain streaming when the terminal cannot show one). The title names the work. It returns the work's own error.

Directories

Path Synopsis
Package standalone holds the input handling shared by standalone scripts that declare their own command-line interface: value parsing, positional validation, help annotations, and usage errors.
Package standalone holds the input handling shared by standalone scripts that declare their own command-line interface: value parsing, positional validation, help annotations, and usage errors.
Package starlark embeds Starlark with cancellable, isolated parallel tasks.
Package starlark embeds Starlark with cancellable, isolated parallel tasks.
internal/convert
Package convert shares Starlark value conversion and argument diagnostics between the runtime and independent standard-library bindings.
Package convert shares Starlark value conversion and argument diagnostics between the runtime and independent standard-library bindings.
stdlib/cli
Package cli exposes native Atmos command definitions to standalone programs.
Package cli exposes native Atmos command definitions to standalone programs.
stdlib/digest
Package digest exposes message digests to the Atmos Automation Language.
Package digest exposes message digests to the Atmos Automation Language.

Jump to

Keyboard shortcuts

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