xx
.md

OS Package

The OS package runs commands and creates dependencies that change a task environment. Its current API version is 1:

load(
    "@os@1",
    "env_append",
    "env_get",
    "env_prepend",
    "env_set",
    "env_unset",
    "run",
)

Task Environments

xx does not pass the invoking shell's full environment to tasks. Each task starts from a clean operating-system environment containing system-provided values and standard utility paths. It also inherits HOME and every XDG_* variable from the invoking process. This avoids accidental dependencies on shell startup files, user-installed tools, and unrelated variables while preserving standard user directories.

When HOME is absent, empty, or relative, xx uses the operating system's user home directory. Missing, empty, or relative standard XDG Base Directory variables receive platform-appropriate defaults. On Linux and macOS these follow the XDG defaults below HOME, /etc/xdg, /usr/local/share, and /usr/share. XDG_RUNTIME_DIR has no standard fallback, so xx only sets it when inherited with an absolute path.

Each task receives its own clone. Dependencies passed through deps modify only that clone and are applied in list order.

Environment values created by env_append and env_prepend are joined with the host's path-list separator when passed to a process: : on Linux and macOS, ; on Windows. This makes functions useful for PATH and other list-like variables.

Variable names are case-insensitive on Windows and case-sensitive on Linux and macOS.

run

run(command, deps=[])

Runs a command from the project root.

command must be a tuple of strings. Its first item is the executable and remaining items are arguments. A trailing comma is required for a one-item Starlark tuple:

run(("tool",))
run(("go", "test", "./..."), deps=[go])

A bare executable name is resolved only through the task's virtual PATH. A relative path containing / or \ is resolved from the project root. Absolute executable paths are also accepted. Standard output and standard error are forwarded to xx's output.

The returned dependency is also registered as a root task, so declaring run(...) is enough to execute it. It preserves environment changes from its dependencies for downstream tasks. Independent root tasks may run concurrently.

env_get

env_get(key)

Reads a variable from the entrypoint's clean base environment while the Starlark file is evaluated. It returns a string when present and None when absent.

It does not read changes created by env_set, env_append, or other dependencies because those changes are activated later for an individual task.

load("@os@1", "env_get")

system_path = env_get("PATH")

env_set

env_set(key, value)

Returns a dependency that replaces the variable with one value:

run(("go", "test", "./..."), deps=[env_set("GOFLAGS", "-trimpath")])

env_unset

env_unset(key)

Returns a dependency that removes the variable:

run(("tool",), deps=[env_unset("TOOL_CONFIG")])

env_append

env_append(key, value, unique=False)

Returns a dependency that appends one item. Set unique=True to leave the variable unchanged when it already contains an equivalent item:

run(
    ("project-linter", "check"),
    deps=[env_append("PATH", "tools/bin", unique=True)],
)

Uniqueness uses exact string comparison on Linux and macOS. On Windows, PATH entries are compared without case sensitivity; other variables use exact value comparison. xx does not clean or resolve paths for this comparison.

env_prepend

env_prepend(key, value, unique=False)

Works like env_append, but inserts the item at the beginning:

run(
    ("my-tool", "check"),
    deps=[env_prepend("PATH", "tools/bin", unique=True)],
)

Relative PATH entries are resolved from the project root when xx looks up an executable.

Ordering and Composition

Order matters. Later dependencies see and can replace earlier results:

deps = [
    env_set("MODE", "check"),
    env_set("MODE", "release"),
]

The command receives MODE=release.

Combine environment dependencies with + for reusable groups:

release_env = env_set("MODE", "release") + env_set("CGO_ENABLED", "0")

run(("tool", "build"), deps=[release_env])

Both forms preserve left-to-right application.

Validation

Keys cannot be empty or contain = or a NUL byte. Values cannot contain a NUL byte. run rejects non-string command items and commands that cannot be found in the declared environment.