Reading the werf configuration, werf uses a built-in Go template engine (text/template) and expands the function set with Sprig and werf functions.

When organizing the configuration, it can be split into separate files in template directory.

Built-in Go template features

To work effectively, we recommend you looking at all the features, or at least the following sections:

Sprig functions

The Sprig library provides over 70 template functions:

Among all functions, werf does not support the expandenv function and has its own implementation for the env function.

werf functions

various environments

.Env

The .Env variable allows organizing configuration for several environments (testing, production, staging, and so on) and switching between them by the --env=<environment_name> option.

In helm templates, there is the .Values.werf.env variable that can be used the same way

current commit information

.Commit.Hash

{{ .Commit.Hash }} provides current commit SHA. It’s best to avoid using .Commit.Hash if possible, since it might trigger many unneeded stage rebuilds.

.Commit.Date

{{ .Commit.Date.Human }} provides commit date in Human form.

{{ .Commit.Date.Unix }} provides commit date in Unix epoch form.

Example: rebuild whole image every month
image: app
from: ubuntu
git:
- add: /
  to: /app
  stageDependencies:
    install:
    - "*"
fromCacheVersion: {{ div .Commit.Date.Unix (mul 60 60 24 30) }}
shell:
  beforeInstall:
  - apt-get update
  - apt-get install -y nodejs npm
  install:
  - npm ci

templating

include

The include function brings in another template, and then pass the results to other template functions.

Syntax:

{{ include "<TEMPLATE_NAME>" <VALUES> }}
Example: how to use a common configuration
project: my-project
configVersion: 1
---

image: app1
from: alpine
ansible:
beforeInstall:
{{- include "(component) ruby" . }}
---
image: app2
from: alpine
ansible:
beforeInstall:
{{- include "(component) ruby" . }}

{{- define "(component) ruby" }}
- command: gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
- get_url:
    url: https://raw.githubusercontent.com/rvm/rvm/master/binscripts/rvm-installer
    dest: /tmp/rvm-installer
- name: "Install rvm"
  command: bash -e /tmp/rvm-installer
- name: "Install ruby 2.3.4"
  raw: bash -lec {{`{{ item | quote }}`}}
  with_items:
  - rvm install 2.3.4
  - rvm use --default 2.3.4
  - gem install bundler --no-ri --no-rdoc
  - rvm cleanup all
{{- end }}

tpl

The tpl function allows evaluating string as a template inside a template.

Syntax:

{{ tpl "<STRING>" <VALUES> }}
  • <STRING> — the content of a project file, an environment variable value, or an arbitrary string.
  • <VALUES> — the template values. If you use the current context, ., all templates and values (including those described in the template directory files) can be used in the template.
Example: how to use project files as the werf configuration partials
{{ $_ := set . "BaseImage" "node:14.3" }}

project: app
configVersion: 1
---

{{ range $path, $content := .Files.Glob "**/werf-partial.yaml" }}
{{ tpl $content $ }}
{{ end }}

{{- define "common install commands" }}
- npm install
- npm run build
{{- end }}
image: backend
from: {{ .BaseImage }}
git:
- add: /backend
  to: /app/backend
shell:
  install:
  - cd /app/backend
{{- include "common install commands" . | indent 2 }}
image: frontend
from: {{ .BaseImage }}
git:
- add: /frontend
  to: /app/frontend
shell:
  install:
  - cd /app/frontend
{{- include "common install commands" . | indent 2 }}

environment variables

env

The env function reads an environment variable.

Syntax:

{{ env "<ENV_NAME>" }}
{{ env "<ENV_NAME>" "default_value" }}

By default, the use of the env function is not allowed by giterminism (read more about it here)

project files

.Files.Get

The function .Files.Get gets a certain project file content.

Syntax:

{{ .Files.Get "<FILE_PATH>" }}

By default, the use of files that have non-committed changes is not allowed by giterminism (read more about it here)

Example: how to add a certain file to stapel image without git directive (shell)
project: my-project
configVersion: 1
---

image: app
from: alpine
shell:
  setup:
  - |
    head -c -1 <<'EOF' > /etc/nginx/nginx.conf
{{ .Files.Get ".werf/nginx.conf" | indent 4 }}
    EOF
Example: how to add a certain file to stapel image without git directive (ansible)
project: my-project
configVersion: 1
---

image: app
from: alpine
ansible:
  setup:
  - name: "Setup /etc/nginx/nginx.conf"
    copy:
      content: |
{{ .Files.Get ".werf/nginx.conf" | indent 8 }}
      dest: /etc/nginx/nginx.conf

.Files.Glob

The function .Files.Glob allows getting project files with a glob and working with their content.

The function supports shell pattern matching and **. The function results can be merged with the merge sprig function (e.g., {{ $filesDict := merge (.Files.Glob "glob1") (.Files.Glob "glob2")).

Syntax:

{{ .Files.Glob "<GLOB>" }}

By default, the use of files that have non-committed changes is not allowed by giterminism (read more about it here)

Example: how to add files by a glob to stapel image without git directive (shell)
project: my-project
configVersion: 1
---

image: app
from: alpine
shell:
  install: mkdir /app
  setup:
{{ range $path, $content := .Files.Glob "modules/*/images/*/{Dockerfile,werf.inc.yaml}" }}
  - |
    head -c -1 <<EOF > /app/{{ base $path }}
{{ $content | indent 4 }}
    EOF
{{ end }}
Example: how to add files by a glob to stapel image without git directive (ansible)
project: my-project
configVersion: 1
---

image: app
from: alpine
ansible:
  install:
  - raw: mkdir /app
  setup:
{{ range $path, $content := .Files.Glob "modules/*/images/*/{Dockerfile,werf.inc.yaml}" }}
  - name: "Setup /app/{{ base $path }}"
    copy:
      content: |
{{ $content | indent 8 }}
      dest: /app/{{ base $path }}
{{ end }}

others

required

The required function declares a particular values entry as required for template rendering. If the value is empty, the template rendering will fail with a user submitted error message.

Syntax:

value: {{ required "<ERROR_MSG>" <VALUE> }}

fromYaml

The fromYaml functions decodes a YAML document into a structure.

Syntax:

value: {{ fromYaml "<STRING>" }}
Example: how to read YAML file and then use a value
{{- $values := .Files.Get "werf_values.yaml" | fromYaml -}} # or fromYaml (.Files.Get "werf_values.yaml")
from: {{- $values.image.from }}

Template directory

Template files can be stored in a reserved directory (.werf by default) with the extension .tmpl (arbitrary nesting .werf/**/*.tmpl is supported).

Template files and the werf configuration file define a common context:

  • Template file is a complete template and can be used with the include function by the relative path ({{ include "directory/partial.tmpl" . }}).
  • The template defined with the define function in one template file is available in any other, including the werf configuration file.

Example: how to use template files

werf.yaml
project: my-project
configVersion: 1
---
image: app
from: java:8-jdk-alpine
shell:
  beforeInstall:
  - mkdir /app
  - adduser -Dh /home/gordon gordon
import:
- artifact: appserver
  add: '/usr/src/atsea/target/AtSea-0.0.1-SNAPSHOT.jar'
  to: '/app/AtSea-0.0.1-SNAPSHOT.jar'
  after: install
- artifact: storefront
  add: /usr/src/atsea/app/react-app/build
  to: /static
  after: install
docker:
  ENTRYPOINT: ["java", "-jar", "/app/AtSea-0.0.1-SNAPSHOT.jar"]
  CMD: ["--spring.profiles.active=postgres"]
---
{{ include "artifact/appserver.tmpl" . }}
---
{{ include "artifact/storefront.tmpl" . }}
.werf/artifact/appserver.tmpl
artifact: appserver
from: maven:latest
git:
- add: '/app'
  to: '/usr/src/atsea'
shell:
  install:
  - cd /usr/src/atsea
  - mvn -B -f pom.xml -s /usr/share/maven/ref/settings-docker.xml dependency:resolve
  - mvn -B -s /usr/share/maven/ref/settings-docker.xml package -DskipTests
.werf/artifact/storefront.tmpl
artifact: storefront
from: node:latest
git:
- add: /app/react-app
  to: /usr/src/atsea/app/react-app
shell:
  install:
  - cd /usr/src/atsea/app/react-app
  - npm install
  - npm run build

Example: how to use templates defined in a template file

werf.yaml
{{ $_ := set . "RubyVersion" "2.3.4" }}
{{ $_ := set . "BaseImage" "alpine" }}

project: my-project
configVersion: 1
---

image: rails
from: {{ .BaseImage }}
ansible:
  beforeInstall:
  {{- include "(component) mysql client" . }}
  {{- include "(component) ruby" . }}
  install:
  {{- include "(component) Gemfile dependencies" . }}
.werf/ansible/components.tmpl
{{- define "(component) Gemfile dependencies" }}
  - file:
      path: /root/.ssh
      state: directory
      owner: root
      group: root
      recurse: yes
  - name: "Setup ssh known_hosts used in Gemfile"
    shell: |
      set -e
      ssh-keyscan github.com >> /root/.ssh/known_hosts
      ssh-keyscan mygitlab.myorg.com >> /root/.ssh/known_hosts
    args:
      executable: /bin/bash
  - name: "Install Gemfile dependencies with bundler"
    shell: |
      set -e
      source /etc/profile.d/rvm.sh
      cd /app
      bundle install --without development test --path vendor/bundle
    args:
      executable: /bin/bash
{{- end }}

{{- define "(component) mysql client" }}
  - name: "Install mysql client"
    apt:
      name: "{{`{{ item }}`}}"
      update_cache: yes
    with_items:
    - libmysqlclient-dev
    - mysql-client
    - g++
{{- end }}

{{- define "(component) ruby" }}
  - command: gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
  - get_url:
      url: https://raw.githubusercontent.com/rvm/rvm/master/binscripts/rvm-installer
      dest: /tmp/rvm-installer
  - name: "Install rvm"
    command: bash -e /tmp/rvm-installer
  - name: "Install ruby {{ .RubyVersion }}"
    raw: bash -lec {{`{{ item | quote }}`}}
    with_items:
    - rvm install {{ .RubyVersion }}
    - rvm use --default {{ .RubyVersion }}
    - gem install bundler --no-ri --no-rdoc
    - rvm cleanup all
{{- end }}