Task File Syntax
gogo reads task definitions from a gogo.yaml file in the current directory.
Top-Level Fields
| Field | Type | Description |
|---|---|---|
version |
string | Optional version identifier |
default |
string | Task to run when none is given on the CLI — replaces the convention of declaring a default task. Must reference a defined (possibly namespaced) task |
includes |
list of strings | Subdirectories containing other task files (namespaced — see Includes) |
flatten |
list of strings | YAML files whose tasks merge into the current namespace without a prefix (see Includes) |
dotenv |
list of strings | Paths to .env files to load |
vars |
map | Global variables |
sources |
map | Named source-pattern presets, referenced by task sources: (see Sources & Checksums) |
secrets |
map | Named secret URIs (currently op://), referenced by task secrets: (see Secrets) |
interval |
string | Default polling interval for watch mode (e.g. 500ms) |
tasks |
map | Task definitions (see below) |
Task Definition
Each task supports the following fields:
| Field | Type | Description |
|---|---|---|
cmd |
string | A single command to run |
cmds |
list | Multiple commands to run in sequence |
deps |
list | Tasks to run before this one (concurrently) |
dir |
string | Working directory for the task |
dotenv |
list | Paths to .env files to load for this task |
env |
map | Environment variables (supports op:// references for 1Password secrets) |
vars |
map | Task-scoped variables |
secrets |
list or string | Names referencing entries in the top-level secrets: map (see Secrets) |
sources |
list or string | Glob patterns for incremental builds and watch mode. A bare name resolves to a source preset (e.g. sources: go) |
generates |
list | Output file patterns for timestamp-based incremental builds |
status |
list or string | Shell commands probing the task’s end state; all exiting 0 marks it up to date (see Sources & Checksums) |
aliases |
list | Alternative names for the task |
platforms |
list | Restrict task to specific OS/arch (e.g. linux, darwin/arm64) |
requires |
map | Required variables (vars) and environment variables (env) |
preconditions |
list | Shell commands that must succeed before the task runs |
prompt |
string | Confirmation question shown before the task runs; declining aborts (see Prompts) |
silent |
bool | When true, suppress the [task] cmd log line for each command |
Default Task
A top-level default: field names the task that runs when no task is given on the command line:
default: dev
tasks:
dev:
deps: [build, test]
build:
cmd: go build ./...
test:
cmd: go test ./...
gogo # runs `dev`
gogo build # explicit task still wins over `default:`
If default: is unset, gogo falls back to running a task literally named default (the original convention). The two forms are interchangeable; default: simply removes the no-op trampoline:
# Before — trampoline
tasks:
default:
cmds:
- task: dev
dev: { cmd: ... }
# After — top-level field
default: dev
tasks:
dev: { cmd: ... }
A default: that names a non-existent task is rejected at load time.
Commands
A command can be a simple string:
tasks:
build:
cmd: go build ./...
Or a list of commands:
tasks:
lint:
cmds:
- gofmt -w .
- golangci-lint run
A command can also reference another task. The sub-task inherits the calling task’s environment (its vars, dotenv, and env block), so a parent’s environment flows down without having to re-export it:
tasks:
smoke:
env:
TESTSET_SIZE: "2"
MIRROR_FS: "1"
cmds:
- task: gen # both children see TESTSET_SIZE and MIRROR_FS
- task: ingest
The child’s own env block (or a per-call vars: override) wins per key. See Variables for the full precedence rules.
Deferred Cleanup
A command entry can use defer: instead of cmd: to register a cleanup command. Deferred commands run after the task’s other commands finish — even when one of them fails — making them the right place for teardown:
tasks:
test:
cmds:
- docker compose up -d
- defer: docker compose down
- go test ./...
Here docker compose down runs whether go test passes or fails.
Deferred commands follow Go’s defer semantics:
- They run in reverse registration order (last registered, first run).
- Only defers registered before a failure run — a
defer:listed after a failing command never registered, so it is skipped. - A deferred command’s own failure is logged as a warning but does not fail the task: cleanup must not mask the task’s real result, and later defers still run.
- Variables (
{{.VAR}}) are expanded in deferred commands like in regular ones. - Under
--dry, deferred commands are printed but not executed.
defer: only accepts a shell command string — defer: { task: cleanup } is not supported.
Ignoring Command Failures
By default, a failing command aborts the task. Set ignore_error: true on a command to log the failure as a warning and continue with the next command:
tasks:
clean:
cmds:
- cmd: rm bin/app # may not exist — that's fine
ignore_error: true
- echo cleaned
ignore_error is scoped to the individual command — a later command without it still fails the task. It only applies to shell commands: it is not honored on task: sub-calls (a failing child task always propagates) or on defer: entries (whose failures are already ignored).
Prompts
Guard destructive tasks with a prompt: — a confirmation question asked before the task (and its dependencies) runs:
tasks:
deploy:
prompt: Deploy to production?
deps: [build]
cmd: ./deploy.sh
$ gogo deploy
[deploy] Deploy to production? [y/N]:
Only y or yes (case-insensitive) confirms. Anything else — including pressing Enter, EOF, or a closed stdin — declines and aborts with task "deploy": prompt declined, leaving the system untouched: dependencies haven’t run either.
Two cases skip the question:
gogo --yes(or-y) auto-confirms every prompt, keeping guarded tasks scriptable in CI.gogo --dryskips it because nothing will execute anyway.
The prompt is asked before variables and environment are resolved, so the message is shown literally — {{.VAR}} references are not expanded.
Task Descriptions
Comments above a task key are used as the task description, shown by gogo -l:
tasks:
# Build the Go binary
build:
cmd: go build ./...
# Run unit tests
test:
cmd: go test ./...
$ gogo -l
build Build the Go binary
test Run unit tests
Aliases
Tasks can have alternative names:
tasks:
test:
aliases: [t]
cmd: go test ./...
gogo t # same as gogo test
Internal Tasks
Tasks whose name starts with _ are internal — they don’t appear in gogo -l but can still be used as dependencies or called directly:
tasks:
build:
deps: [_generate]
cmd: go build ./...
_generate:
cmd: go generate ./...
Platforms
Restrict a task to specific operating systems or architectures:
tasks:
install-linux:
platforms: [linux]
cmd: apt-get install -y mypackage
build-mac-arm:
platforms: [darwin/arm64]
cmd: make build
Entries can be os (e.g. linux, darwin), os/arch (e.g. linux/amd64), or arch (e.g. arm64). Tasks that don’t match the current platform are silently skipped.
Requires
Validate that variables or environment variables are set before running a task:
tasks:
deploy:
requires:
vars: [VERSION]
env: [DEPLOY_TOKEN]
cmd: deploy --version ${VERSION}
If a required value is missing, gogo prints a clear error and stops.
Passing Variables to Task Calls
When calling a task from cmds, you can pass variables:
tasks:
release:
cmds:
- task: deploy
vars:
ENV: production
VERSION: "2.0"
deploy:
cmd: deploy --env ${ENV} --version ${VERSION}
Call-site variables override the called task’s own vars.
Silent Tasks
By default, gogo prints each command before running it (e.g. [build] go build ./...). Set silent: true to suppress that log line — only the command’s own output is shown:
tasks:
setup:
silent: true
cmds:
- mkdir -p build
- touch build/.keep
silent only affects the calling task’s own cmds. A non-silent task invoked via task: from a silent caller still logs normally.
Field Shorthands
Four fields accept a string or a struct, and the long forms are interchangeable. Use whichever reads best at the call site — the parser normalizes both into the same internal shape (see the UnmarshalYAML methods on Cmd, Dep, Var, and Precondition in taskfile/types.go).
| Field | Short form | Long form |
|---|---|---|
cmd / cmds[] |
cmd: go build ./... |
cmds: [{ cmd: go build ./... }] or cmds: [{ task: build }] (sub-task call, optionally with vars:) |
deps[] |
deps: [build] |
deps: [{ task: build }] |
vars value |
VERSION: 1.0.0 |
VERSION: { value: "1.0.0" } or VERSION: { sh: git describe --tags } |
preconditions[] |
- test -f config.yaml |
- { sh: test -f config.yaml, msg: config.yaml is missing } |
A few non-obvious notes that fall out of the shorthand rules:
cmd:(singular) andcmds:(list) are aliases, not additive. If both are present,cmd:wins and thecmds:list is dropped — seeTask.UnmarshalYAMLintaskfile/types.go. Pick one per task.cmds: [{ task: X }]anddeps: [X]look similar but behave differently:cmdscalls run sequentially in order and inherit the parent’s resolved env;depsrun concurrently as prerequisites and do not inherit env. See Variables › Parent-to-child Env Propagation.- A
Varwritten as a bare string is the same asvalue:; usingsh:switches to dynamic resolution. The two are mutually exclusive in practice — if both are set, the shell command wins (it is whatVar.Sh != ""triggers). - A bare-string
Preconditionreuses the failed shell command as its error message; the map form lets you provide a human-readablemsg:instead.