Help us shape the future of Go! Take the 2026 Go Developer Survey

execenv

package
v0.63.0 Latest Latest
Warning

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

Go to latest
Published: Oct 10, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package execenv resolves the environment the app runs subprocesses with, and answers what a variable would hold in the user's terminal.

A desktop launch inherits the launcher's environment, not a shell's: macOS starts an .app bundle with PATH=/usr/bin:/bin:/usr/sbin:/sbin. Session hooks and shell actions are the user's own commands, written against the PATH their terminal has, so running them with the inherited one fails on anything a package manager, a version manager or a language toolchain installed (ADR subprocess-environment). The same gap hides hive's own environment overrides from the app, which is what Getenv answers (ADR hive-env-overrides-resolve-through-the-login-shell).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func SearchDirs

func SearchDirs() []string

SearchDirs returns those prefixes plus the per-user ones. It is where a binary lives when the environment does not say: tmuxbin searches the list for one executable (ADR tmux-discovery), and the resolver appends it to PATH.

Types

type Options

type Options struct {
	Logger zerolog.Logger
	// Shell is the login shell to ask. Empty reads $SHELL; still empty skips
	// the probe.
	Shell string
	// Timeout bounds one probe. Zero means defaultTimeout.
	Timeout time.Duration
	// Probe is the seam tests replace. Zero runs the real login shell.
	Probe func(ctx context.Context, shell string) (map[string]string, error)
}

Options configures a Resolver. The zero value probes $SHELL with the default timeout and logs nothing.

type Resolver

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

Resolver answers what PATH a subprocess runs with. It asks the user's login shell once per run and remembers the answer — a shell's PATH does not change under a running app, and re-running a login shell per hook command would charge every command that shell's startup cost.

func NewResolver

func NewResolver(opts Options) *Resolver

func (*Resolver) Environ

func (r *Resolver) Environ(ctx context.Context) []string

Environ returns the environment subprocesses run with: this process's, with PATH replaced by Path and the login shell's remaining variables filling names this process does not define. A launch that names a variable is more specific than a startup file, so the process wins — which keeps a stale rc file from shadowing a HIVE_DESKTOP_* override the app was started with.

The result is shared between callers: append to it, never write into it.

func (*Resolver) Getenv

func (r *Resolver) Getenv(ctx context.Context, name string) string

Getenv answers a variable the way the user's terminal would: this process's value when it has one, otherwise the login shell's. A variable exported from a startup file is invisible to a launched .app, so a config override the CLI reads from the environment is only reachable through the probe (ADR hive-env-overrides-resolve-through-the-login-shell).

func (*Resolver) LookPath

func (r *Resolver) LookPath(ctx context.Context, name string) (string, error)

LookPath resolves a command name against the same PATH its child will run with. It exists because os/exec does not: exec.Command searches the PATH of the process that calls it and ignores Cmd.Env, which is the one this package is replacing. A name that is already a path is returned untouched.

func (*Resolver) Path

func (r *Resolver) Path(ctx context.Context) string

Path returns the PATH value subprocesses run with: what the login shell reports, then what this process inherited, then the package-manager prefixes, first occurrence winning. A probe that fails is not fatal — the inherited PATH still runs everything in /usr/bin, which is what the app had before.

func (*Resolver) ProbeErr

func (r *Resolver) ProbeErr() error

ProbeErr returns why the login shell did not answer, or nil when it answered or was not asked yet. Getenv returns "" for a variable in both cases, so a caller that falls back on "" uses this to tell an unset variable from a shell that could not be read.

func (*Resolver) SetLogger

func (r *Resolver) SetLogger(logger zerolog.Logger)

SetLogger replaces the logger the probe reports to. A program that resolves its startup paths before its final logger exists builds the Resolver early and hands it the final logger here.

Jump to

Keyboard shortcuts

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