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 categorySCM commentsGrafanaJiraAccumulatorSlackTeamsDiscordEmail
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.

ProviderSame-repo outputCross-repo output
github#42owner/repo#42
gitlab#42owner/repo#42
gitea#42owner/repo#42
bitbucket_cloud#42owner/repo#42
bitbucket_server#42owner/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.

ProviderSame-repo outputCross-repo output
github#42owner/repo#42
gitlab!42owner/repo!42
gitea#42owner/repo#42
bitbucket_cloud#42owner/repo#42
bitbucket_server#42owner/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.

ProviderOutput
github@alice
gitlab@alice
gitea@alice
bitbucket_cloud@alice
bitbucket_server@alice
azure_devops@alice
sourcehutalice (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.

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:

FunctionWhy removed
env, expandenv, expandPrevents environment variable exfiltration
base64Encode, base64DecodePrevents cryptographic misuse
genPrivateKey, genCA, genSelfSignedCertPrevents key generation in templates

String functions

FunctionSignatureExampleOutput
upperupper(string){{ .State | upper }}SUCCESS
lowerlower(string){{ .State | lower }}success
titletitle(string){{ .State | title }}Success
trimtrim(string){{ .Description | trim }}trimmed
trimAlltrimAll(string){{ " hello " | trimAll }}hello
trimPrefixtrimPrefix(prefix, string){{ "build-123" | trimPrefix "build-" }}123
trimSuffixtrimSuffix(suffix, string){{ "123-test" | trimSuffix "-test" }}123
replacereplace(old, new, string){{ "hello world" | replace "world" "relay" }}hello relay
trunctrunc(n, string){{ .CommitSHA | trunc 8 }}first 8 characters
containscontains(substr, string){{ contains "fail" .Description }}bool
hasPrefixhasPrefix(prefix, string){{ hasPrefix "tekton-" .RunName }}bool
hasSuffixhasSuffix(suffix, string){{ hasSuffix "-run" .RunName }}bool
repeatrepeat(count, string){{ repeat 3 "-" }}---
quotequote(string){{ .RunName | quote }}"my-run"
squotesquote(string){{ .RunName | squote }}'my-run'
catcat(args...){{ cat "hello" "world" }}hello world
splitsplit(sep, string){{ index (split ":" "a:b:c") "_1" }}b
splitListsplitList(sep, string){{ splitList "/" "a/b/c" }}[a b c]
joinjoin(sep, list){{ join "," (list "a" "b") }}a,b
regexReplaceAllregexReplaceAll(pattern, string, repl)See belowmatched 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

FunctionSignatureExampleOutput
addadd(a, b){{ add 1 2 }}3
subsub(a, b){{ sub 10 3 }}7
mulmul(a, b){{ mul 3 4 }}12
divdiv(a, b){{ div 10 3 }}3
modmod(a, b){{ mod 10 3 }}1
maxmax(a, b){{ max 5 3 }}5
minmin(a, b){{ min 5 3 }}3

Date/time functions

FunctionSignatureExampleOutput
nownow(){{ now }}current time
datedate(fmt, time){{ date "2006-01-02" .StartedAt }}2024-01-15
dateModifydateModify(mod, time){{ dateModify "-24h" now }}24h ago
agoago(time){{ ago .StartedAt }}5m30s
durationduration(seconds){{ duration 90 }}1h30m0s
unixEpochunixEpoch(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

FunctionSignatureExampleOutput
listlist(items...){{ list "a" "b" }}[a b]
firstfirst(n, list){{ first 2 (list 1 2 3) }}[1 2]
lastlast(n, list){{ last 2 (list 1 2 3) }}[2 3]
reversereverse(list){{ reverse (list 1 2 3) }}[3 2 1]
sortAlphasortAlpha(list){{ sortAlpha (list "c" "a" "b") }}[a b c]
uniquniq(list){{ uniq (list 1 1 2 3) }}[1 2 3]
hashas(item, list){{ has "a" (list "a" "b") }}true
appendappend(list, items...){{ append (list 1 2) 3 }}[1 2 3]
concatconcat(lists...){{ concat (list 1) (list 2 3) }}[1 2 3]

Logic functions

FunctionSignatureExampleOutput
ternaryternary(trueVal, falseVal, condition){{ ternary "βœ…" "❌" (eq .State "success") }}βœ… or ❌
defaultdefault(defaultVal, val){{ .PipelineName | default .RunName }}falls back to RunName
emptyempty(val){{ if empty .Description }}no desc{{ end }}checks emptiness
coalescecoalesce(vals...){{ coalesce .Description "N/A" }}first non-empty
toJsontoJson(val){{ . | toJson }}JSON string

Type conversion

FunctionSignatureExampleOutput
toStringtoString(val){{ toString 42 }}"42"
toInttoInt(val){{ "42" | toInt }}42
toBooltoBool(val){{ toBool "true" }}true

String building

FunctionSignatureExampleOutput
printfprintf(fmt, args...){{ printf "%.8s" .CommitSHA }}first 8 chars
printlnprintln(args...){{ println "hello" "world" }}hello world\n
indentindent(spaces, string){{ indent 2 "line1\nline2" }}indented
nindentnindent(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 }}