initctx

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Sep 14, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package initctx reads the room for a bare `lo init`: where the user stands (a project root, a subdirectory, a service directory, a submodule under an umbrella project, an empty or a bare directory), what git says about it, what the project already has, and whether a terminal is attached. Detect is the read side; Decide turns the state and the wizard's answers into the ordered list of actions the cli executes through the existing subcommands; Ask is the huh form layer.

Everything here is pure over the filesystem except the two git reads, which go through the execx.Runner seam so the tests script them.

Index

Constants

This section is empty.

Variables

View Source
var ErrAborted = errors.New("lo init: aborted")

ErrAborted is returned when the user leaves the form (Ctrl-C, Esc).

Functions

func Confirm

func Confirm(tio IO, network bool) (bool, error)

Confirm is the summary step's question; network says whether a step needs the network (the question mentions it).

func DefaultDir

func DefaultDir(s State) string

DefaultDir is the project directory a new project defaults to: the git root when the user stands below it, else the working directory.

func DefaultEnv

func DefaultEnv(s State, dir string) string

DefaultEnv is the environment file a directory suggests: the one it already has, else mise.

func WriteCard

func WriteCard(w io.Writer, s State)

WriteCard prints the state card.

func WriteOptions

func WriteOptions(w io.Writer, s State)

WriteOptions prints the menu of an existing project as hints (the `--plan` output when there is nothing to do).

func WriteSummary

func WriteSummary(w io.Writer, p Plan, cwd string)

WriteSummary prints the plan: the steps, then the equivalent commands (run from Plan.Dir; a `cd` line names it when it is not cwd).

Types

type Action

type Action struct {
	Kind ActionKind
	// Summary says what the step writes or runs, in one line.
	Summary string
	// Command is the flag-twin command line, run from Plan.Dir.
	Command string
	// Network is whether the step needs the network.
	Network bool
	// The parameters the executor hands to the functions behind Command.
	Name, Env, Domain, Driver, Service, ServicePath, Implementation string
	Groups                                                          []string
}

Action is one step of a Plan.

type ActionKind

type ActionKind string

ActionKind names one action of a Plan.

const (
	// ActionWriteProjectFiles — clusters/, lok8s.yaml, the .gitignore
	// entries and (with Env) the environment file, plus (with Domain) the
	// first cluster spec: one `lo init project` run.
	ActionWriteProjectFiles ActionKind = "project files"
	// ActionGitInit — `git init` in the project directory.
	ActionGitInit ActionKind = "git init"
	// ActionWriteEnvFile — the environment file into an existing project.
	ActionWriteEnvFile ActionKind = "environment file"
	// ActionWriteClusterSpec — clusters/<domain>/cluster.lok8s.yaml into
	// an existing project.
	ActionWriteClusterSpec ActionKind = "cluster spec"
	// ActionEjectBash — `lo assets eject bash`.
	ActionEjectBash ActionKind = "eject bash"
	// ActionSetImplementation — spec.implementation.default in lok8s.yaml.
	ActionSetImplementation ActionKind = "implementation"
	// ActionToolchainInstall — `lo toolchain install` (network).
	ActionToolchainInstall ActionKind = "toolchain"
	// ActionUse — `lo use <domain>`.
	ActionUse ActionKind = "use"
	// ActionAddService — `lo init service <name>`.
	ActionAddService ActionKind = "service"
	// ActionAddTests — `lo init test`.
	ActionAddTests ActionKind = "tests"
	// ActionRegisterService — the service directory into services.yaml.
	ActionRegisterService ActionKind = "register service"
)

type Answers

type Answers struct {
	// Dir is the project directory for a new project ("" = the situation's
	// default: the working directory, or the git root below which the
	// user stands). Ignored inside an existing project (its root).
	Dir string
	// Name is metadata.name ("" = the directory name).
	Name string
	// Env is the environment file: mise, direnv or none ("" = the one the
	// directory already has, else mise). Inside an existing project it
	// only matters when none exists yet.
	Env string
	// GitInit asks for `git init` (honoured only without a repository).
	GitInit bool
	// Domain and Driver describe the first (or an added) cluster spec;
	// "" = none.
	Domain, Driver string
	// Use makes Domain the active domain (`lo use`).
	Use bool
	// Toolchain runs `lo toolchain install` with Groups (nil = the
	// default groups).
	Toolchain bool
	Groups    []string
	// Implementation switches spec.implementation.default ("" = keep;
	// "bash" ejects the tree first when the project has none).
	Implementation string
	// Service adds a service (`lo init service <name>`); Tests the
	// Playwright suite; Register adds the service directory the user
	// stands in to services.yaml.
	Service  string
	Tests    bool
	Register bool
}

Answers is what the wizard asked (or the defaults `--plan` assumes). Every field has a flag twin on an existing subcommand; Decide prints it.

func Ask

func Ask(s State, tio IO) (Answers, error)

Ask runs the conversation for s and returns the answers. Nothing is written: the caller decides (Decide), prints the summary and asks Confirm before it executes anything.

func DefaultAnswers

func DefaultAnswers(s State) Answers

DefaultAnswers are what `--plan` assumes without a conversation: a new project gets its files where the situation suggests, `git init` when git exists and there is no repository, the environment file the directory already has (else mise) and the toolchain; an existing project gets nothing (the card, and the menu as hints).

type Domain

type Domain struct {
	Name string
	// Kind is the driver of cluster.lok8s.yaml (lowercase), "deploy" for
	// a deploy-only domain, "?" when unreadable.
	Kind string
}

Domain is one directory under clusters/ that carries a spec.

type Git

type Git struct {
	// Available is whether git ran at all.
	Available bool
	// Root is the repository root ("" = not a repository).
	Root string
	// AtRoot is whether cwd is the repository root.
	AtRoot bool
	// Dirty is whether `git status --porcelain` printed anything.
	Dirty bool
	// Submodule is whether Root carries a `.git` FILE (a submodule
	// checkout, or a worktree) rather than a directory.
	Submodule bool
}

Git is what git says about the working directory.

type IO

type IO struct {
	In  io.Reader
	Out io.Writer
	// Accessible runs the fields as line prompts instead of the
	// interactive forms (off a TTY; the tests).
	Accessible bool
}

IO is where a form reads and writes.

type Option

type Option struct {
	Key     string
	Label   string
	Command string
}

Option is one entry of the "what to add" menu in an existing project, with the flag twin the card prints as a hint.

func Options

func Options(s State) []Option

Options lists the menu for an existing project: what the situation allows, in the order the wizard offers it.

type Plan

type Plan struct {
	Situation Situation
	// Dir is the project directory every action runs in (absolute).
	Dir string
	// Name is the project name the actions use.
	Name string
	// Actions in execution order; empty = nothing to do.
	Actions []Action
}

Plan is the ordered list of actions for one `lo init` run.

func Decide

func Decide(s State, a Answers) Plan

Decide turns the state and the answers into the plan. Answers that do not apply to the situation are ignored (a `git init` inside a repository, a registration outside a service directory).

func (Plan) Commands

func (p Plan) Commands() []string

Commands lists the flag-twin command lines in order.

func (Plan) Network

func (p Plan) Network() bool

Network reports whether any action needs the network.

type Project

type Project struct {
	// Root is the project root (the marker walk from cwd).
	Root string
	// AtRoot is whether cwd is Root.
	AtRoot bool
	// Name is metadata.name of the project file ("" when the marker is
	// clusters/ alone).
	Name string
	// ProjectFile is whether Root/lok8s.yaml is a `kind: Project` file.
	ProjectFile bool
	// Clusters is whether Root/clusters exists.
	Clusters bool
	// Domains lists the domains under clusters/ that carry a spec.
	Domains []Domain
	// Active is clusters/.active ("" when unset or invalid).
	Active string
	// EnvFile is the environment file present: "mise" (mise.toml),
	// "direnv" (.envrc), "" (none). With both present, mise.
	EnvFile string
	// BYAML is whether .bin/b.yaml exists; BYAMLMarker whether `lo
	// toolchain install` wrote it.
	BYAML, BYAMLMarker bool
	// ToolsMissing lists the pinned tools that do not resolve under the
	// project (b, kustomize, the two exec plugins); nil = all present.
	ToolsMissing []string
	// BashTree is whether Root/.lok8s/lo exists (an ejected or vendored
	// bash tree).
	BashTree bool
	// Implementation is spec.implementation.default ("go" or "bash";
	// "go" when the block is absent), ImplementationErr the loader's
	// error text when the block is invalid.
	Implementation    string
	ImplementationErr string
	// Services is whether Root/services.yaml exists; Tests whether
	// Root/tests is a directory.
	Services, Tests bool
}

Project is what exists in the project the user stands in.

type Situation

type Situation int

Situation is one of the five conversations `lo init` can have.

const (
	// SituationUnknown is the zero value; Detect never returns it.
	SituationUnknown Situation = iota
	// SituationEmptyDir — an empty directory (a `.git` entry does not
	// count) and no project above: the welcome conversation.
	SituationEmptyDir
	// SituationGitBelowRoot — inside a git repository, below its root,
	// and no project above: the wizard suggests the git root.
	SituationGitBelowRoot
	// SituationBareDir — a non-empty directory, no project above, and
	// either no git or cwd is the git root: the wizard says what it sees
	// and offers here or a subdirectory.
	SituationBareDir
	// SituationProjectRoot — cwd is a project root: the status card, then
	// what to add.
	SituationProjectRoot
	// SituationInsideProject — cwd is inside a project (a subdirectory, a
	// service directory, a submodule under the umbrella project): the
	// card, plus the offer to register a service directory.
	SituationInsideProject
)

func (Situation) String

func (s Situation) String() string

String is the situation as the card names it.

type State

type State struct {
	// Cwd is the working directory, absolute.
	Cwd string
	// Empty is whether Cwd has no entries besides `.git`.
	Empty bool
	// Entries counts the entries of Cwd besides `.git`.
	Entries int
	// ServiceDir is whether Cwd holds a kind-less lok8s.yaml (a service
	// directory).
	ServiceDir bool
	// Git is the git state; Project the project state (nil = no project
	// above Cwd).
	Git     Git
	Project *Project
	// Terminal is set by the caller (DetectTerminal); Detect leaves it
	// zero.
	Terminal Terminal
}

State is what Detect found.

func Detect

func Detect(ctx context.Context, cwd string, r execx.Runner) (State, error)

Detect reads the room from cwd. r runs git (nil = no git: the state reports it unavailable).

func (State) ServiceName

func (s State) ServiceName() string

ServiceName is the service name a service directory implies (its base name), "" outside one.

func (State) Situation

func (s State) Situation() Situation

Situation classifies the state.

type Terminal

type Terminal struct {
	// StdinTTY and StdoutTTY report whether the two streams are terminals.
	StdinTTY, StdoutTTY bool
	// CI is whether the CI environment variable is set (any value).
	CI bool
	// Yes is the --yes flag.
	Yes bool
}

Terminal is what decides between the wizard and the help text.

func DetectTerminal

func DetectTerminal(stdin, stdout *os.File, yes bool) Terminal

DetectTerminal reads the two streams and the CI variable.

func (Terminal) Interactive

func (t Terminal) Interactive() bool

Interactive reports whether the wizard may run: both streams are terminals, CI is unset and --yes was not given.

Jump to

Keyboard shortcuts

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