grml

command module
v1.4.1 Latest Latest
Warning

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

Go to latest
Published: Apr 28, 2026 License: GPL-3.0 Imports: 1 Imported by: 0

README

grml — A simple build automation tool written in Go

grml is a Makefile alternative. Build targets are declared in a grml.yaml file at the project root, using YAML syntax. Running grml opens an interactive shell that exposes those targets as commands; passing a target on the command line runs it once and exits.

A worked example lives in sample/cd sample && grml.

asciicast

Installation

From source
go install github.com/desertbit/grml@latest

Usage

grml [flags] [command...]
Flag Description
-d, --directory root directory containing the grml file (default: current directory)
-f, --file grml file relative to the root (default: grml.yaml)
-v, --verbose trace each shell command as it runs (set -x)

The -f flag lets you keep multiple manifests side by side — e.g. grml.yaml for in-container work and grml.host.yaml for tasks that must run on the host.

Without a target, grml drops into an interactive shell with tab completion. Built-in commands:

Command Description
reload re-read the grml file (preserves option values)
verbose <bool> toggle verbose mode at runtime
options print current option values
options check toggle bool options interactively
options set <name> pick a value for a choice option

Manifest reference

Top-level keys
Key Description
version manifest schema version, currently 3 (required)
project project name, exposed as ${PROJECT} (required)
env ordered map of environment variables, supporting ${VAR} interpolation
options user-tweakable options: bools (check) or lists of strings (single choice)
interpreter sh (default) or bash
import shell files sourced before every exec body
commands command tree
Per-command keys
Key Description
help help text (supports ${VAR} interpolation from env)
alias list of alternative names
args positional arguments, exposed as env vars of the same name
env env vars for an included subgrml file, scoped to the commands in that file (see Per-include env)
options options for an included subgrml file, with their own options check / options set UI under that command (see Per-include options)
import shell files for an included subgrml file, sourced only when running commands in that file (see Per-include imports)
deps other commands to run first; see Dep paths for the syntax
exec shell body to run
commands nested sub-commands
include load the rest of this command's definition from another YAML file
Implicit environment variables

The process environment is inherited and the following are always set:

Variable Value
ROOT absolute path to the root directory containing the grml file
PROJECT project name from the manifest
NUMCPU number of CPU cores
LOCAL_ROOT absolute path to the directory of the current subgrml file — only set inside included subtrees (in root commands, use ${ROOT} instead)

Each option is also exported: bools as true/false, choices as the active value. Each args entry is exported when the command runs.

Variable interpolation

${VAR} is expanded by grml inside env values, import paths, and help strings. Inside exec bodies, expansion is performed by the shell at runtime — env vars, options, args, and any other shell-visible variables are all available there.

Dep paths

A deps entry is one of:

Syntax Resolves to
foo.bar absolute path from the root command tree
.bar relative to the current command (i.e. its child bar)
~.bar relative to the nearest enclosing include point — its child bar (root if no include)

~. lets an included subgrml file reference its own siblings without knowing the name the root manifest gave it. For example, commands/release.yaml can say deps: [~.tag] whether the root mounts it as release:, rel:, or anything else.

Per-include env

An included subgrml file can declare its own env: block at the top. Those values layer on top of the root env (root values stay visible) and apply only to commands defined inside that file. Same-named root keys are overridden within the included file; commands outside it are unaffected. LOCAL_ROOT is auto-defined to the included file's directory, so a subgrml can refer to its own files via ${LOCAL_ROOT}/<file> without hard-coding the path. Subgrml commands also run with their working directory set to ${LOCAL_ROOT}, so exec bodies can reference sibling files by relative path. Root commands keep ${ROOT} as their cwd.

# commands/release.yaml — included from the root manifest as the 'release' command
env:
    DESTBIN:      ${PROJECT}-${VERSION}-release   # overrides root DESTBIN, only inside this file
    RELEASE_NOTE: ${PROJECT} ${VERSION} release   # only visible to commands in this file

help: cut a ${VERSION} release
commands:
    publish:
        help: publish ${DESTBIN} artifacts        # uses the per-include DESTBIN
        exec: |
            echo "publishing ${BINDIR}/${DESTBIN}"
Per-include options

An included subgrml file can declare its own options: block. Each subgrml's options live in their own namespace — two subgrmls can each have a debug option without colliding, and there's no need to prefix names manually.

The interactive shell exposes a separate options UI under each subgrml's command:

grml » options                  # root manifest's options
grml » labrat options           # labrat's options
grml » labrat options check     # toggle labrat's bool options
grml » labrat options set foo   # pick a value for labrat's choice option
grml » closer options           # closer's options (independent of labrat's)

When running a command, the env vars exported are the merged options from every applicable scope: root first, then each ancestor scope down to the command's own scope. Inner scopes shadow outer scopes for same-named options, so a command inside labrat always sees its own debug, never the root one.

Per-include imports

An included subgrml file can declare its own import: block, parallel to the root manifest's import:. Listed scripts are sourced only when running commands defined inside that file (and any descendants), and they run after the env is in place — so top-level statements in the script can use the per-include env.

Paths are written relative to the included file's own directory, so a self-contained subgrml can ship its helpers alongside its YAML:

commands/
  release.yaml      # subgrml: import: [release.sh]
  release.sh        # sourced only when running release.* commands

Sourcing order for any given command: root manifest's import: first, then per-include import: from outermost ancestor down to the command's own scope. Last-sourced wins for function/variable definitions.

Shell builtins

grml injects helpers under the grml_* namespace into every exec body and import script. They work under both sh and bash.

Helper Description
grml_option <name> exit 0 if the named option/env var equals true (bool option check)
grml_option <name> <value> exit 0 if the named option/env var equals <value> (choice check)
grml_if <name> <if-str> <else-str> print if-str when option is true, else else-str (bool form)
grml_if <name> <value> <if-str> <else-str> print if-str when option equals value, else else-str (choice form)

Examples:

# Branch on a bool option:
if grml_option debug; then
    go build -gcflags="all=-N -l" -o "${BINDIR}/${DESTBIN}"
else
    go build -o "${BINDIR}/${DESTBIN}"
fi

# Inline-if for picking between two strings (avoids a temporary if/else):
suffix=$(grml_if debug '-debug' '')
echo "publishing ${BINDIR}/${DESTBIN}${suffix}"

# Choice-form: pick a string based on the active value of a choice option.
gpu_flag=$(grml_if runtime cuda '--cuda' '--cpu')

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
internal
app
cmd
sample module

Jump to

Keyboard shortcuts

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