Template Function Reference
Templates use Go text/template with two layers of functions: a sprig subset for general-purpose string/date/math operations, and domain-specific helpers for provider-aware cross-references. This page documents every available function, its signature, and examples.
Function availability by notifier
Not all notifiers get the same functions. Before using a function, check whether your target notifier supports it.
| Function category | SCM comments | Grafana | Jira | Accumulator | Slack | Teams | Discord | |
|---|---|---|---|---|---|---|---|---|
| Sprig subset | β | β | β | β | β | β | β | β |
| Domain helpers | β | β | β | β | β | β | β | β |
Slack, Teams, Discord, and Email use plain Go templates. Use printf for formatting, if/range/eq for logic, and manual string construction for everything else.
Domain helpers
These functions are defined by the relay and take precedence over any sprig equivalents. They produce provider-aware output.
IssueRef
IssueRef(provider string, issueNum int, owner string, repo string) string
Returns the provider-specific markdown syntax for referencing an issue.
| Provider | Same-repo output | Cross-repo output |
|---|---|---|
| github | #42 | owner/repo#42 |
| gitlab | #42 | owner/repo#42 |
| gitea | #42 | owner/repo#42 |
| bitbucket_cloud | #42 | owner/repo#42 |
| bitbucket_server | #42 | owner/repo#42 |
| azure_devops | #42 | #42 (no cross-repo) |
| sourcehut | "" (not supported) | "" |
Examples:
{{ IssueRef .Provider 42 "" "" }}
Renders #42 on most providers, empty string on SourceHut.
{{ IssueRef .Provider 42 "acme" "backend" }}
Renders acme/backend#42 on GitHub/GitLab/Gitea/Bitbucket, #42 on Azure DevOps.
Guard usage (safe to call even when IssueNumber is nil):
{{ if .IssueNumber }}Related: {{ IssueRef .Provider .IssueNumber .Repo.Owner .Repo.Name }}{{ end }}
PRRef
PRRef(provider string, prNum int, owner string, repo string) string
Returns the provider-specific syntax for referencing a pull request or merge request.
| Provider | Same-repo output | Cross-repo output |
|---|---|---|
| github | #42 | owner/repo#42 |
| gitlab | !42 | owner/repo!42 |
| gitea | #42 | owner/repo#42 |
| bitbucket_cloud | #42 | owner/repo#42 |
| bitbucket_server | #42 | owner/repo#42 |
| azure_devops | #42 | #42 (no cross-repo) |
| sourcehut | "" (not supported) | "" |
Note the GitLab difference: merge requests use ! instead of #.
Examples:
{{ PRRef .Provider 123 "" "" }}
Renders #123 on most providers, !123 on GitLab.
{{ PRRef .Provider 123 "acme" "frontend" }}
Renders acme/frontend!123 on GitLab, acme/frontend#123 on GitHub/Gitea/Bitbucket.
UserMention
UserMention(provider string, username string) string
Returns the provider-specific syntax for mentioning a user. All supported providers use @username.
| Provider | Output |
|---|---|
| github | @alice |
| gitlab | @alice |
| gitea | @alice |
| bitbucket_cloud | @alice |
| bitbucket_server | @alice |
| azure_devops | @alice |
| sourcehut | alice (no mention syntax) |
Example:
Assigned to {{ UserMention .Provider "alice" }}
Truncate
Truncate(s string, limit int) string
Rune-aware truncation with ... suffix. Counts runes (Unicode characters), not bytes, so multi-byte characters like emoji donβt cause issues.
- If
limitis 0, returns the original string (no limit). - If the string is shorter than the limit, returns it unchanged.
- If truncated, appends
...(consuming 3 characters of the limit). - If
limitis less than 3, truncates without the ellipsis.
Examples:
{{ Truncate .Description 200 }}
If Description is 250 characters, returns the first 197 characters followed by ....
{{ Truncate .Description 300 }}
If Description is 250 characters, returns the full string unchanged.
{{ Truncate "short" 200 }}
Returns short (already under the limit).
Sprig functions (safe subset)
The relay starts with sprigβs full TxtFuncMap and removes dangerous functions. Here are the most commonly used categories.
Removed functions (security)
These functions are stripped from the template environment:
| Function | Why removed |
|---|---|
env, expandenv, expand | Prevents environment variable exfiltration |
base64Encode, base64Decode | Prevents cryptographic misuse |
genPrivateKey, genCA, genSelfSignedCert | Prevents key generation in templates |
String functions
| Function | Signature | Example | Output |
|---|---|---|---|
upper | upper(string) | {{ .State | upper }} | SUCCESS |
lower | lower(string) | {{ .State | lower }} | success |
title | title(string) | {{ .State | title }} | Success |
trim | trim(string) | {{ .Description | trim }} | trimmed |
trimAll | trimAll(string) | {{ " hello " | trimAll }} | hello |
trimPrefix | trimPrefix(prefix, string) | {{ "build-123" | trimPrefix "build-" }} | 123 |
trimSuffix | trimSuffix(suffix, string) | {{ "123-test" | trimSuffix "-test" }} | 123 |
replace | replace(old, new, string) | {{ "hello world" | replace "world" "relay" }} | hello relay |
trunc | trunc(n, string) | {{ .CommitSHA | trunc 8 }} | first 8 characters |
contains | contains(substr, string) | {{ contains "fail" .Description }} | bool |
hasPrefix | hasPrefix(prefix, string) | {{ hasPrefix "tekton-" .RunName }} | bool |
hasSuffix | hasSuffix(suffix, string) | {{ hasSuffix "-run" .RunName }} | bool |
repeat | repeat(count, string) | {{ repeat 3 "-" }} | --- |
quote | quote(string) | {{ .RunName | quote }} | "my-run" |
squote | squote(string) | {{ .RunName | squote }} | 'my-run' |
cat | cat(args...) | {{ cat "hello" "world" }} | hello world |
split | split(sep, string) | {{ index (split ":" "a:b:c") "_1" }} | b |
splitList | splitList(sep, string) | {{ splitList "/" "a/b/c" }} | [a b c] |
join | join(sep, list) | {{ join "," (list "a" "b") }} | a,b |
regexReplaceAll | regexReplaceAll(pattern, string, repl) | See below | matched pattern replaced |
Truncation in non-sprig notifiers: For Slack, Teams, Discord, and Email, use printf "%.200s" .Description instead of Truncate. The printf function is always available.
Math functions
| Function | Signature | Example | Output |
|---|---|---|---|
add | add(a, b) | {{ add 1 2 }} | 3 |
sub | sub(a, b) | {{ sub 10 3 }} | 7 |
mul | mul(a, b) | {{ mul 3 4 }} | 12 |
div | div(a, b) | {{ div 10 3 }} | 3 |
mod | mod(a, b) | {{ mod 10 3 }} | 1 |
max | max(a, b) | {{ max 5 3 }} | 5 |
min | min(a, b) | {{ min 5 3 }} | 3 |
Date/time functions
| Function | Signature | Example | Output |
|---|---|---|---|
now | now() | {{ now }} | current time |
date | date(fmt, time) | {{ date "2006-01-02" .StartedAt }} | 2024-01-15 |
dateModify | dateModify(mod, time) | {{ dateModify "-24h" now }} | 24h ago |
ago | ago(time) | {{ ago .StartedAt }} | 5m30s |
duration | duration(seconds) | {{ duration 90 }} | 1h30m0s |
unixEpoch | unixEpoch(time) | {{ unixEpoch .StartedAt }} | unix timestamp |
Duration calculation (common pattern):
{{- if and (not .StartedAt.IsZero) (not .FinishedAt.IsZero) -}}
Duration: {{ regexReplaceAll "[.][0-9]+s" (toString (.FinishedAt.Sub .StartedAt)) "s" }}
{{- end }}
Collection functions
| Function | Signature | Example | Output |
|---|---|---|---|
list | list(items...) | {{ list "a" "b" }} | [a b] |
first | first(n, list) | {{ first 2 (list 1 2 3) }} | [1 2] |
last | last(n, list) | {{ last 2 (list 1 2 3) }} | [2 3] |
reverse | reverse(list) | {{ reverse (list 1 2 3) }} | [3 2 1] |
sortAlpha | sortAlpha(list) | {{ sortAlpha (list "c" "a" "b") }} | [a b c] |
uniq | uniq(list) | {{ uniq (list 1 1 2 3) }} | [1 2 3] |
has | has(item, list) | {{ has "a" (list "a" "b") }} | true |
append | append(list, items...) | {{ append (list 1 2) 3 }} | [1 2 3] |
concat | concat(lists...) | {{ concat (list 1) (list 2 3) }} | [1 2 3] |
Logic functions
| Function | Signature | Example | Output |
|---|---|---|---|
ternary | ternary(trueVal, falseVal, condition) | {{ ternary "β
" "β" (eq .State "success") }} | β
or β |
default | default(defaultVal, val) | {{ .PipelineName | default .RunName }} | falls back to RunName |
empty | empty(val) | {{ if empty .Description }}no desc{{ end }} | checks emptiness |
coalesce | coalesce(vals...) | {{ coalesce .Description "N/A" }} | first non-empty |
toJson | toJson(val) | {{ . | toJson }} | JSON string |
Type conversion
| Function | Signature | Example | Output |
|---|---|---|---|
toString | toString(val) | {{ toString 42 }} | "42" |
toInt | toInt(val) | {{ "42" | toInt }} | 42 |
toBool | toBool(val) | {{ toBool "true" }} | true |
String building
| Function | Signature | Example | Output |
|---|---|---|---|
printf | printf(fmt, args...) | {{ printf "%.8s" .CommitSHA }} | first 8 chars |
println | println(args...) | {{ println "hello" "world" }} | hello world\n |
indent | indent(spaces, string) | {{ indent 2 "line1\nline2" }} | indented |
nindent | nindent(spaces, string) | {{ nindent 2 "line1" }} | newline + indented |
Common patterns
Conditional content with default
{{ .PipelineName | default .RunName }}
State-based emoji (non-sprig notifiers)
{{- if eq .State "success" }}β
{{- else if eq .State "failure" }}β
{{- else if eq .State "error" }}β οΈ
{{- else if eq .State "canceled" }}π«
{{- else }}β³
{{- end }}
Truncated description with fallback
{{ Truncate (.Description | default "No description available") 300 }}
Duration formatting
{{- if and (not .StartedAt.IsZero) (not .FinishedAt.IsZero) }}
Duration: {{ regexReplaceAll "[.][0-9]+s" (toString (.FinishedAt.Sub .StartedAt)) "s" }}
{{- end }}
Results table (sprig only)
{{- if .Results }}
<details><summary>Results ({{ len .Results }})</summary>
| Name | Value |
|---|---|
{{- range .Results }}
| {{ .Name }} | {{ Truncate .Value 120 }} |
{{- end }}
</details>
{{- end }}
Results list (non-sprig notifiers)
{{- if .Results }}
Results:
{{- range .Results }}
- {{ .Name }}: {{ printf "%.120s" .Value }}
{{- end }}
{{- end }}