> ## Documentation Index
> Fetch the complete documentation index at: https://errand.nuvrel.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Blocks

> Reference for all four block types: `errand`, `variable`, `computed`, and `task`.

A playbook is made up of four block types. Each has a distinct role in defining how a run behaves.

## errand block

The `errand` block sets playbook-level configuration. It is optional and can appear at most once across all `.ern` files.

```hcl theme={null}
errand {
  working_dir = "<path>"
}
```

### Attributes

<ParamField body="working_dir" type="string">
  The default working directory for all tasks in the playbook. Tasks with their own `working_dir` override this value. Relative paths are resolved from the directory where `errand` was invoked.
</ParamField>

### Example

```hcl theme={null}
errand {
  working_dir = "./service/api"
}

task "test" {
  commands = ["go test ./..."]
}

task "build" {
  depends_on = [task.test]
  commands   = ["go build -o bin/api ./cmd/api"]
}
```

Both `test` and `build` run from `./service/api` without repeating `working_dir` in each task.

### Notes

* If no `errand` block is declared, tasks run from the directory where `errand` was invoked unless they specify their own `working_dir`.
* Declaring `errand` more than once is an error.

***

## variable block

Variables are named inputs. Declare them in the playbook and pass values at run time with `--var`. Reference them as `var.<name>`.

```hcl theme={null}
variable "<name>" {
  description = "<text>"
  type        = <type>
  default     = <value>
}
```

All attributes are optional. A variable with no `default` is required: Errand reports an error if no value is provided.

### Attributes

<ParamField body="description" type="string">
  A human-readable description shown in error messages.
</ParamField>

<ParamField body="type" type="type constraint">
  A type constraint. When set, Errand validates and converts the provided value. See [types](/language/types) for all available types.
</ParamField>

<ParamField body="default" type="any">
  The value used when no `--var` flag is passed. Must be compatible with `type` when both are set.
</ParamField>

### Passing values

```bash theme={null}
errand run deploy --var env=production --var replicas=3
```

Multiple `--var` flags are supported. Values are strings on the command line and converted to the declared type automatically.

### Examples

Required variable, no default means it must be supplied:

```hcl theme={null}
variable "registry" {
  description = "Container registry host"
  type        = string
}

task "push" {
  commands = ["docker push ${var.registry}/myapp:latest"]
}
```

Variable with a default:

```hcl theme={null}
variable "port" {
  description = "Port the server listens on"
  type        = number
  default     = 8080
}

task "run" {
  commands = ["./bin/server --port ${var.port}"]
}
```

Object variable, structured inputs keep related values together:

```hcl theme={null}
variable "db" {
  description = "Database connection settings"
  type = object({
    host = string
    port = optional(number, 5432)
    name = string
  })
}

task "migrate" {
  commands = [
    "psql -h ${var.db.host} -p ${var.db.port} -d ${var.db.name} -f schema.sql",
  ]
}
```

Pass an object from the command line:

```bash theme={null}
errand run migrate --var 'db={ host = "db.prod", name = "appdb" }'
```

The `port` field is omitted, so its default `5432` applies.

Boolean variable used in a conditional command:

```hcl theme={null}
variable "verbose" {
  type    = bool
  default = false
}

task "test" {
  commands = [
    "go test ${var.verbose ? \"-v\" : \"\"} ./...",
  ]
}
```

### Notes

* Variable names must be unique across all `.ern` files in the playbook.
* Access object fields with dot notation: `var.db.host`.
* Access list elements by index: `var.services[0]`.

***

## computed block

A `computed` block evaluates an expression at runtime and makes the result available as `computed.<name>`. Use it to derive values from variables, the environment, or built-in functions without duplicating logic across tasks.

```hcl theme={null}
computed "<name>" {
  description = "<text>"
  expression  = <expression>
}
```

`expression` is required. `description` is optional.

### Attributes

<ParamField body="description" type="string">
  A human-readable description of the computed value.
</ParamField>

<ParamField body="expression" type="expression" required>
  The expression to evaluate. Can reference variables (`var.<name>`), call built-in functions, and combine values with string interpolation.
</ParamField>

### Evaluation order

Computed blocks are evaluated before tasks run, in dependency order. If a `computed` block references a variable, that variable is resolved first automatically. If two `computed` blocks reference each other, Errand reports a cycle error.

### Examples

Read from the environment with a fallback:

```hcl theme={null}
computed "registry" {
  description = "Container registry from CI environment"
  expression  = env("CI_REGISTRY", "registry.example.com")
}

task "push" {
  commands = ["docker push ${computed.registry}/myapp:latest"]
}
```

Build a value from multiple inputs:

```hcl theme={null}
variable "image" {
  type    = string
  default = "myapp"
}

variable "tag" {
  type = string
}

computed "full_image" {
  description = "Fully qualified image reference"
  expression  = "${env("CI_REGISTRY", "registry.example.com")}/${var.image}:${var.tag}"
}

task "build" {
  commands = ["docker build -t ${computed.full_image} ."]
}

task "push" {
  depends_on = [task.build]
  commands   = ["docker push ${computed.full_image}"]
}
```

Chain computed values: `image_tag` depends on `version`, so Errand evaluates `version` first:

```hcl theme={null}
computed "version" {
  expression = trimspace(file("VERSION"))
}

computed "image_tag" {
  expression = "${var.app}-${computed.version}"
}
```

### Notes

* Computed names must be unique across all `.ern` files in the playbook.
* Computed values are read-only. Tasks cannot modify them.

***

## task block

A task is a named group of shell commands. Tasks can depend on other tasks, run conditionally, and be scoped to a specific directory.

```hcl theme={null}
task "<name>" {
  description = "<text>"
  working_dir = "<path>"
  condition   = <boolean expression>
  depends_on  = [<task references>]
  commands    = [<string expressions>]
}
```

Only `commands` is required.

### Attributes

<ParamField body="description" type="string">
  A human-readable description of what the task does.
</ParamField>

<ParamField body="working_dir" type="string">
  The directory to run commands in. Overrides the playbook-level `working_dir` from the `errand` block. Relative paths are resolved from the directory where `errand` was invoked.
</ParamField>

<ParamField body="condition" type="boolean expression">
  A boolean expression. When it evaluates to `false`, the task is skipped. Tasks that depend on a skipped task are also skipped.
</ParamField>

<ParamField body="depends_on" type="list of task references">
  An explicit list of tasks that must complete before this task runs. Write each as `task.<name>`. Errand also infers dependencies from expression references in `commands` and `condition`.
</ParamField>

<ParamField body="commands" type="list of string expressions" required>
  Shell commands to execute in order. Each command is a string expression and can use variables, computed values, and built-in functions. Commands stop at the first failure.
</ParamField>

### Examples

Task with dependencies, Errand runs `test` and `lint` before `build`:

```hcl theme={null}
task "lint" {
  commands = ["golangci-lint run ./..."]
}

task "test" {
  commands = ["go test ./..."]
}

task "build" {
  description = "Compile the binary"
  depends_on  = [task.lint, task.test]
  commands    = ["go build -o bin/app ./cmd/app"]
}
```

Conditional task, skip when a lock file is absent:

```hcl theme={null}
task "install" {
  description = "Install dependencies"
  condition   = fileexists("package-lock.json")
  commands    = ["npm ci"]
}
```

Aggregate task, runs dependencies only, no commands of its own:

```hcl theme={null}
task "ci" {
  description = "Full CI pipeline"
  depends_on  = [task.lint, task.test, task.build]
  commands    = []
}
```

### Default task

When you call `errand run` with no task name, Errand runs the task named `default`:

```hcl theme={null}
task "default" {
  depends_on = [task.test, task.build]
  commands   = []
}
```

### Notes

* Task names must be unique across all `.ern` files in the playbook.
* Commands run inside a built-in POSIX-compatible shell interpreter. No external shell is required, including on Windows.
* If a command fails, remaining commands in the same task are skipped and the task is marked as failed.
* Tasks that do not depend on a failed task continue to run normally.
