Template
Render Go templates with structured data. The template executor processes a script body as a Go text/template and writes the result to stdout or a file. It is useful for generating configuration files, reports, or any text output from structured data without spawning a shell.
How It Works
- The
scriptfield is parsed as a Go template. - Data from
with.datais passed to the template as the root context (.). - The rendered output is written to stdout (capturable with
output:), or to a file ifwith.outputis set.
Dagu skips all variable expansion (${VAR}, command substitution, etc.) on the script body. The template engine is the sole evaluator — ${VAR} literals in your template are preserved as-is. Data values in with.data are expanded by Dagu before being passed to the template, so ${VAR} in data values works normally.
with Fields
| Field | Type | Required | Description |
|---|---|---|---|
data | object | No | Key-value pairs accessible as {{ .key }} in the template. Values can be strings, numbers, lists, or nested objects. |
output | string | No | File path to write the rendered output to. If empty, output goes to stdout. Relative paths resolve against the step's working_dir. |
Basic Example
steps:
- name: render
type: template
with:
data:
greeting: hello
script: |
{{ .greeting }}, world!
output: RESULTRESULT captures hello, world!.
Writing to a File
steps:
- name: render
type: template
with:
output: /tmp/report.md
data:
title: Monthly Report
script: |
# {{ .title }}
Generated by Dagu.When with.output is set, the rendered content is written atomically to that path. Parent directories are created automatically. Stdout remains empty.
Relative paths resolve against working_dir:
steps:
- name: render
type: template
working_dir: /opt/reports
with:
output: subdir/output.txt
data:
msg: hello
script: "{{ .msg }}"This writes to /opt/reports/subdir/output.txt.
Using Data from Prior Steps
Data values are expanded by Dagu before the template runs, so captured output variables work:
type: graph
steps:
- id: producer
command: 'echo -n "Alice"'
output: NAME
- id: render
depends:
- producer
type: template
with:
data:
name: ${NAME}
script: "Hello, {{ .name }}!"
output: RESULTRESULT captures Hello, Alice!.
Template Functions
The template executor provides Dagu-specific functions plus functions from slim-sprig's hermetic text-template function map. Dagu removes functions for environment access, network lookup, current time, random generation, and crypto key generation. The Dagu-specific functions use pipeline-compatible argument order, where the pipeline value is the last argument.
Dagu-specific functions
These override or extend slim-sprig with pipeline-friendly argument order:
| Function | Signature | Description |
|---|---|---|
split | split sep s | Split string s by separator sep. Returns []string. |
join | join sep list | Join a list with separator sep. Accepts []string, []any, or any slice. |
count | count v | Length of a slice, map, array, or string. |
add | add b a | Integer addition: a + b. Pipeline: {{ 5 | add 3 }} → 8. |
empty | empty v | Returns true if the value is nil, empty string, or empty collection. |
upper | upper s | Uppercase string. |
lower | lower s | Lowercase string. |
trim | trim s | Trim whitespace from both ends. |
default | default def val | Returns def if val is empty/nil/zero; otherwise returns val. |
Available slim-sprig functions
These non-overridden names come directly from slim-sprig and work as documented in the slim-sprig docs:
- Misc:
hello - Strings:
adler32sum,cat,contains,hasPrefix,hasSuffix,indent,nindent,plural,quote,repeat,replace,sha1sum,sha256sum,splitList,splitn,squote,substr,title,toString,toStrings,trimAll,trimPrefix,trimSuffix,trimall, andtrunc - Numeric and conversion:
add1,atoi,biggest,ceil,div,float64,floor,int,int64,max,maxf,min,minf,mod,mul,round,seq,sub,toDecimal,until, anduntilStep - Defaults and JSON:
all,any,coalesce,compact,fromJson,mustCompact,mustFromJson,mustToJson,mustToPrettyJson,mustToRawJson,ternary,toJson,toPrettyJson, andtoRawJson - Reflection:
deepEqual,kindIs,kindOf,typeIs,typeIsLike, andtypeOf - Paths and file paths:
base,clean,dir,ext,isAbs,osBase,osClean,osDir,osExt, andosIsAbs - Encoding:
b32dec,b32enc,b64dec, andb64enc - Collections and dictionaries:
append,chunk,concat,dict,dig,first,get,has,hasKey,initial,keys,last,list,mustAppend,mustChunk,mustFirst,mustHas,mustInitial,mustLast,mustPrepend,mustPush,mustRest,mustReverse,mustSlice,mustUniq,mustWithout,omit,pick,pluck,prepend,push,rest,reverse,set,slice,sortAlpha,tuple,uniq,unset,values, andwithout - Flow control:
fail - Regex:
mustRegexFind,mustRegexFindAll,mustRegexMatch,mustRegexReplaceAll,mustRegexReplaceAllLiteral,mustRegexSplit,regexFind,regexFindAll,regexMatch,regexQuoteMeta,regexReplaceAll,regexReplaceAllLiteral, andregexSplit - URLs:
urlJoinandurlParse
The names split, join, add, empty, lower, upper, trim, and default are available, but Dagu overrides the slim-sprig implementation with the behavior documented in the Dagu-specific table above.
Blocked functions
These function names are not available and will cause a template parse error if used:
- Environment access:
env,expandenv - Network I/O:
getHostByName - Current time and date helpers:
ago,date,dateInZone,dateModify,date_in_zone,date_modify,duration,durationRound,htmlDate,htmlDateInZone,mustDateModify,mustToDate,must_date_modify,now,toDate, andunixEpoch - Crypto key generation:
buildCustomCert,derivePassword,genCA,genPrivateKey,genSelfSignedCert, andgenSignedCert - Random generation:
randAlpha,randAlphaNum,randAscii,randBytes,randInt,randNumeric,randString, anduuidv4
Missing Key Behavior
Templates use missingkey=error. Referencing a key not present in data causes the step to fail:
steps:
- name: render
type: template
with:
data:
name: test
script: "{{ .undefined_key }}" # Fails with execution errorUse default to handle optional keys safely:
script: '{{ .name | default "Anonymous" }}'Or use get for safe map access:
script: '{{ get .app "owner" | default "unknown" }}'Complex Example
steps:
- name: render-config
type: template
script: |
app={{ .app.name | lower | replace " " "-" }}
owner={{ get .app "owner" | default "unknown" }}
domains={{ get .app "domains" | default (list "localhost") | uniq | sortAlpha | join "," }}
with:
data:
app:
name: My Service
domains:
- api.example.com
- api.example.com
- app.example.com
output: RESULTOutput:
app=my-service
owner=unknown
domains=api.example.com,app.example.comDollar Sign Preservation
Because Dagu skips expansion on the script body, shell-style variables like ${BAR} and backtick expressions pass through unchanged:
steps:
- name: render
type: template
with:
data:
name: test
script: |
export FOO=${BAR}
echo "{{ .name }}"
value=`command`
output: RESULTThe output contains literal ${BAR} and `command`.
Pipeline Chaining
Functions compose naturally in pipelines:
script: '{{ "a,b,c" | split "," | join ";" }}'
# Result: a;b;cscript: '{{ .csv | split "," | count }}'
# With csv: "x,y,z" → 3script: '{{ .domains | uniq | sortAlpha | join "," }}'
# slim-sprig list functions return []any; join accepts both []string and []any