# templates

text with values filled in (!tpl)

A template is text in which winch fills in the values of each host.

## Syntax

The !tpl tag marks a template, a Go text/template. It builds text, such as a path or a line. The template files of a recipe use the same language.

## Data

A template reads these names with a leading dot:

|  |  |
| --- | --- |
| .vars | the variables of the host, with the vars of the recipe step |
| .facts | the facts of the host; run winch doc facts |
| .host | name, short, address, groups, and vars of the host; address is the name when the host has no address |
| .item | the current element in a step with loop |

Inside range, "." is the element. $.vars reads the variables.

## Where templates go

Any module argument can be a template or an expression, and so can then.

## Functions and actions

A function takes its arguments after its name, separated by spaces: {{ join .vars.names ", " }}. Templates also have the actions of Go's text/template: if, else, range, with, define, template, block, break, and continue, and the variables $x := value.

## Missing values

A template that reads a missing map key fails. Use get for a map key that may be missing. A variable needs a value or a default; get does not read the host's own .vars. A template that prints a null value fails.

## Quoting

A value goes into a command as it is. Quote a value that must stay one word with quote. Quote a YAML value that starts with a quote, \[, {, \&, \*, or !, or that contains ": " or " \#". A template that starts with {{ needs quotes.

## Other hosts

Templates have no match(). To list other hosts, give a recipe step a variable with an !expr that uses match(), and range over it in a template of the recipe.

## When they run

winch evaluates templates on the machine that runs winch, during the plan. They cannot read the result of a step. winch checks each one when it loads the project: an unknown name or a syntax error is an error. An error while winch evaluates one for a host fails that host. The other hosts go on.

## Functions

| Function | Meaning |
| --- | --- |
| [`and`](#templates.and) | and x y: the first empty argument, or the last argument. |
| [`call`](#templates.call) | call f args: the result of the function f. |
| [`eq`](#templates.eq) | eq a b: true when a equals b. |
| [`ge`](#templates.ge) | ge a b: true when a \>= b. |
| [`get`](#templates.get) | get m key: the value of key in the map m, or nothing when m has no such key. |
| [`gt`](#templates.gt) | gt a b: true when a \> b. |
| [`has`](#templates.has) | has list v: true when list contains v. |
| [`html`](#templates.html) | html s: s with the special characters of HTML escaped. |
| [`index`](#templates.index) | index x k: the element of the map or the list x at the key or the index k. |
| [`join`](#templates.join) | join list sep: the elements of list as text, joined with sep. |
| [`js`](#templates.js) | js s: s escaped for a JavaScript string. |
| [`json`](#templates.json) | json v: v as JSON on one line. |
| [`le`](#templates.le) | le a b: true when a \<= b. |
| [`len`](#templates.len) | len x: the length of a string, a list, or a map. |
| [`lt`](#templates.lt) | lt a b: true when a \< b. |
| [`ne`](#templates.ne) | ne a b: true when a does not equal b. |
| [`not`](#templates.not) | not x: true when x is empty or false. |
| [`or`](#templates.or) | or x y: the first argument that is not empty, or the last argument. |
| [`print`](#templates.print) | print args: the arguments as text, as Go's fmt.Sprint gives them. |
| [`printf`](#templates.printf) | printf format args: the arguments formatted as Go's fmt.Sprintf does: {{ printf "%05d" .vars.uid }}. |
| [`println`](#templates.println) | println args: the arguments as text, with spaces between them and a newline at the end. |
| [`quote`](#templates.quote) | quote s: s in single quotes for sh, each ' as '\\''. |
| [`slice`](#templates.slice) | slice x i j: the part of the string or the list x from the index i up to j. |
| [`sqlquote`](#templates.sqlquote) | sqlquote s: s as a string literal for MariaDB, or for PostgreSQL with E before it: in single quotes, each ' as '', each \\ as \\\\. |
| [`title`](#templates.title) | title s: s with the first letter of each word in upper case. |
| [`urlquery`](#templates.urlquery) | urlquery s: s escaped for the query of a URL. |

### `templates.and` {#templates.and}

and x y: the first empty argument, or the last argument. In an expression, and is an operator.

### `templates.call` {#templates.call}

call f args: the result of the function f. Project data holds no functions.

### `templates.eq` {#templates.eq}

eq a b: true when a equals b. With more arguments, true when a equals any of them. In an expression, use ==.

### `templates.ge` {#templates.ge}

ge a b: true when a \>= b.

### `templates.get` {#templates.get}

get m key: the value of key in the map m, or nothing when m has no such key. It reads a key that a map may leave out: {{ with get . "endpoint" }}Endpoint = {{ . }}{{ end }}. It does not read the host's own .vars: a variable that may have no value needs a default.

### `templates.gt` {#templates.gt}

gt a b: true when a \> b.

### `templates.has` {#templates.has}

has list v: true when list contains v. In an expression, use v in list.

### `templates.html` {#templates.html}

html s: s with the special characters of HTML escaped.

### `templates.index` {#templates.index}

index x k: the element of the map or the list x at the key or the index k. More keys read deeper. A missing map key is an error. Use get for a key that may be missing.

### `templates.join` {#templates.join}

join list sep: the elements of list as text, joined with sep.

### `templates.js` {#templates.js}

js s: s escaped for a JavaScript string.

### `templates.json` {#templates.json}

json v: v as JSON on one line. Map keys are in sorted order, and non-ASCII characters stay UTF-8. The output is also a valid YAML value, so it writes a string into a JSON or a YAML file: "data-root": {{ json .vars.docker\_root }}.

### `templates.le` {#templates.le}

le a b: true when a \<= b.

### `templates.len` {#templates.len}

len x: the length of a string, a list, or a map.

### `templates.lt` {#templates.lt}

lt a b: true when a \< b.

### `templates.ne` {#templates.ne}

ne a b: true when a does not equal b.

### `templates.not` {#templates.not}

not x: true when x is empty or false. In an expression, not and ! are operators.

### `templates.or` {#templates.or}

or x y: the first argument that is not empty, or the last argument. In an expression, or is an operator.

### `templates.print` {#templates.print}

print args: the arguments as text, as Go's fmt.Sprint gives them.

### `templates.printf` {#templates.printf}

printf format args: the arguments formatted as Go's fmt.Sprintf does: {{ printf "%05d" .vars.uid }}.

### `templates.println` {#templates.println}

println args: the arguments as text, with spaces between them and a newline at the end.

### `templates.quote` {#templates.quote}

quote s: s in single quotes for sh, each ' as '\\''. A value goes into a command as it is. Quote a value that must stay one word: then: !tpl "postmap {{ quote .vars.map }}".

### `templates.slice` {#templates.slice}

slice x i j: the part of the string or the list x from the index i up to j. In an expression, use x\[i:j\].

### `templates.sqlquote` {#templates.sqlquote}

sqlquote s: s as a string literal for MariaDB, or for PostgreSQL with E before it: in single quotes, each ' as '', each \\ as \\\\. It assumes the default SQL mode, without NO\_BACKSLASH\_ESCAPES, and a UTF-8 client.

### `templates.title` {#templates.title}

title s: s with the first letter of each word in upper case.

### `templates.urlquery` {#templates.urlquery}

urlquery s: s escaped for the query of a URL.

## Example

```
# winch.yaml
name: demo
hosts:
  web1.acme.com: {}
  web2.acme.com: {}
vars:
  sites: [blog, shop]
  admins: [ann, bob]
steps:
  - name: Write the sites
    file:
      path: !tpl /etc/nginx/sites-enabled/{{ .item }}.conf
      content: !tpl "server_name {{ .item }}.{{ .host.short }};\n"
    loop: !expr vars.sites
  - name: List the admins
    file:
      path: /etc/app/admins.conf
      content: !tpl "admins ={{ range .vars.admins }} {{ . }}{{ end }}\n"
  - name: List the other hosts
    recipe: peers
    vars:
      peers_hosts: !expr filter(match("all"), .name != host.name)
recipes:
  peers:
    steps:
      - file:
          path: /etc/peers
          content: !tpl "{{ range .vars.peers_hosts }}{{ .name }} {{ .address }}\n{{ end }}"
```
