Types and control flow¶
Type System¶
Primitive Types¶
- string: Text values, support interpolation
- number: Integer and floating-point numbers
- boolean:
trueorfalse - duration: Time durations (e.g., "5m", "2h", "30s")
Collection Types¶
- array: Ordered list of values
[1, 2, 3] - object: Key-value pairs
{name: "value", count: 42}
Special Types¶
- command: Shell command that can be executed
- path: File system path with validation
- url: URL with protocol validation
- regex: Regular expression pattern
- secret: Secure value (not logged in plain text)
Type Inference¶
The compiler infers types based on context:
let count be 42 # number
let name be "hello" # string
let enabled be true # boolean
let timeout be "5m" # duration
let files be ["a.txt", "b.txt"] # array of strings
let config be {port: 8080} # object
Type Constraints¶
Parameters can specify type constraints:
requires port as number between 1000 and 9999
requires timeout as duration
requires files as list of paths
requires config as object
Control Flow¶
Conditional Execution¶
Simple Conditions¶
# Boolean conditions
if enabled:
start service
if not maintenance_mode:
accept traffic
# Comparison conditions
if replicas > 0:
scale deployment
if version >= "2.0.0":
use new features
# Empty/non-empty conditions
if $features is empty:
info "No features specified"
if $features is not empty:
info "Features: {$features}"
if $name is "":
warn "Name is required"
# Stable semantic-version ordering
if $release_version is older than version "{$latest_version}":
fail "Release {$release_version} is behind {$latest_version}"
if $candidate is newer than version "{$current_version}":
info "An upgrade is available"
# Folder/directory empty conditions
if folder "build" is empty:
info "Build directory is empty"
if folder "dist" is not empty:
info "Distribution files exist"
if directory "/tmp/cache" is empty:
run "rm -rf /tmp/cache"
if dir "{$output_path}" is not empty:
warn "Output directory contains files"
# Exact file-content comparisons
if file "./generated.json" matches file "./vendored.json":
info "The files contain the same bytes"
if file "./generated.json" not matches file "./vendored.json":
fail "The vendored file is out of date"
older than version and newer than version compare numeric stable
MAJOR.MINOR.PATCH values. Both operands may be parameters, captured variables,
or interpolated strings. Values such as v1.2.3, release candidates, and partial
versions are rejected; normalize or extract a stable version before comparing.
file … matches file … compares the complete contents byte-for-byte. It does
not normalize line endings, whitespace, text encoding, or structured data.
file … not matches file … negates that result. Both paths support variable
interpolation, resolve using the task's current workdir, and must be readable;
an unreadable or missing operand fails condition evaluation with a path-specific
error.
When-Otherwise Conditions¶
The when-otherwise syntax provides a clean alternative to if-else for simple conditional logic. There are no meaningful differences between when-otherwise or if-else. Use whatever feels more semantic for your specific use case.
# Basic when-otherwise
when $platform is "windows":
step "Building Windows binary with .exe extension"
otherwise:
step "Building Unix binary without extension"
# When without otherwise (optional else clause)
when $environment is "production":
step "Deploy with production settings"
step "Enable monitoring"
# Nested when-otherwise
when $platform is "windows":
info "Windows platform detected"
when $arch is "amd64":
step "Building for Windows x64"
otherwise:
step "Building for Windows ARM"
otherwise:
info "Unix-like platform detected"
when $platform is "mac":
step "Building for macOS"
otherwise:
step "Building for Linux"
# When-otherwise in loops (matrix execution)
for each $platform in ["windows", "linux", "mac"]:
when $platform is "windows":
run "GOOS={$platform} go build -o app.exe"
otherwise:
run "GOOS={$platform} go build -o app"
Supported Condition Types:
- String equality:
$var is "value" - String inequality:
$var is not "value" - Stable version ordering:
$left is older than version "{$right}",$left is newer than version "{$right}" - Exact file comparison:
file "a" matches file "b",file "a" not matches file "b" - Empty checks:
$var is empty,$var is not empty - All condition types supported by
ifstatements
Key Features:
- Clean, readable syntax for simple conditions
- Optional
otherwiseclause (equivalent toelse) - Full nesting support
- Works seamlessly with loops and matrix execution
- Consistent variable scoping rules
Smart Detection Conditions¶
# Tool availability detection
if docker is available:
build container
else:
error "Docker is required"
if docker is not available:
error "Docker is required for this task"
fail "Missing dependency"
if kubernetes is available:
deploy to cluster
# Multiple tool availability check
# For "is available": ALL tools must be available (AND logic)
if docker,"docker-compose" is available:
info "Docker and Docker Compose are both available"
# Alternative: use 'are' for better readability with multiple tools
if docker,"docker-compose" are available:
info "Docker and Docker Compose are both available"
# For "is not available": ANY tool must be unavailable (OR logic)
if docker,"docker-compose",kubectl is not available:
error "One or more required tools are missing"
else:
info "All required tools are available"
# Alternative: use 'are not' for better readability
if docker,"docker-compose",kubectl are not available:
error "One or more required tools are missing"
# For "is running": ALL tools must be running (AND logic)
if docker,"docker compose" are running:
info "Docker CLI and daemon are ready"
# For "is not running": ANY tool may be down (OR logic)
if docker,"docker compose" are not running:
fail "Docker is installed, but the runtime is not reachable"
# Availability can be chained with a version check
if "golangci-lint" is available and version >= "2.12":
info "golangci-lint is installed and new enough"
else:
fail "golangci-lint >= 2.12 is required"
# File/directory detection
if file "package.json" exists:
install npm dependencies
if directory ".git" exists:
commit changes
# Service detection
when symfony is detected:
run symfony console commands
when node project exists:
use npm or yarn
Filesystem condition paths expand the current user's home directory and process
environment variables before they are resolved against the task workdir. The
supported forms are ~, ~/path, $NAME/path, and ${NAME}/path. Undefined
environment variables and named-user home paths such as ~generic/path remain
literal.
if folder "~/Library/Audio/Plug-Ins/VST3" exists:
info "The macOS VST3 folder exists"
if folder "$HOME/.config/example" not exists:
warning "The example configuration folder is missing"
if directory "$HOME/.cache/example" does not exist:
create folder "$HOME/.cache/example"
folder, directory, and dir are interchangeable. The negative existence
forms not exists and does not exist are also equivalent.
Port Conditions¶
A port condition probes a TCP port natively (no external tools) and branches on whether something is listening:
if port 5432 is open on "localhost":
info "PostgreSQL is already running"
else:
start local database container
if port {redis_port} is not open on "{redis_host}" with timeout "2s":
info "Redis is down - skipping cache warmup"
is open is true when a TCP dial succeeds, i.e. the port is in use. Host,
port, and the optional with timeout value all support interpolation; the
timeout accepts Go durations ("2s") or bare seconds and defaults to 5
seconds. In --dry-run mode no connection is opened and the condition
evaluates as if the port were closed.
Docker Conditions¶
A Docker condition asks the Docker daemon whether a resource exists and branches on the answer. Networks are supported:
if docker network "proxy" exists:
info "Reusing the existing proxy network"
else:
call task "create-proxy-network"
when docker network "{app_network}" not exists:
warn "Network {app_network} is missing - orchestration will create it"
otherwise:
info "Network ready"
The network name supports interpolation. In --dry-run mode the daemon is not
queried and the condition evaluates as if the network were missing. The
docker <resource> "<name>" [not] exists shape is designed to grow further
resource variants (containers, images, volumes) over time.
Compound Conditions¶
# Logical operators
if docker is running and kubernetes is available:
deploy containerized application
if environment is "production" or environment is "staging":
require approval
# Parentheses for grouping
if (environment is "production" and git repo is clean) or force_deploy:
proceed with deployment
Iteration¶
Simple Iteration¶
for each $item in $collection:
process item
# With index
for each item at index in collection:
info "Processing item {index}: {item}"
Parallel Execution¶
for each region in ["us-east", "eu-west"] in parallel:
deploy to {region}
# Parallel with synchronization
run in parallel:
- unit_tests -> test_results.unit
- integration_tests -> test_results.integration
- security_scan -> test_results.security
wait for all to complete
Retry with Backoff¶
Pause between attempts with a fixed wait (seconds, minutes, or hours):
for each $attempt in ["1", "2", "3"]:
try:
run "./flaky-deploy.sh"
break
catch:
warn "Attempt {$attempt} failed, backing off"
wait {$attempt} seconds
Filtered Iteration¶
for each file in "src/**/*.js" where file is modified:
lint {file}
for each container in docker containers where status is "running":
check health of {container}
Loop Control¶
for each $service in $services:
if service is healthy:
continue
try:
restart service
catch:
error "Failed to restart {service}"
break # Exit loop on critical failure