# Yaml<no value>
// <!-- Required for asciidoctor -->
:toc:
// Set toclevels to be at least your hugo [markup.tableOfContents.endLevel] config key
:toclevels: 4

[cols="1^,1^,1^",options=header]
|===
| source | condition | target
| &#10004; | &#10004; | &#10004;
|===

== Description

The `yaml` resource reads and writes a key in a YAML file, addressed by a yamlpath expression.

**source**::
Returns the value of one key from one file.

**condition**::
Checks that the key holds `value`, or the source output when `value` is unset. With `keyonly`, checks only that the key exists.

**target**::
Writes the value at the key. Keys already holding it are reported as up to date and left untouched, so a target that changes nothing does not create a commit.

== Parameters

{{< resourceparameters "sources" "yaml" >}}

The spec has two mutually exclusive pairs, and breaking either aborts the run before the pipeline starts:

`file` / `files`:: One is mandatory. Setting both gives `the attributes 'spec.file' and 'spec.files' are mutually exclusive`; duplicates inside `files` are rejected too.
`key` / `keys`:: One is mandatory. Setting both gives `'key' and 'keys' are mutually exclusive`; duplicates inside `keys` are rejected.

Several parameters work in only one stage, which the table above does not make obvious:

`keys`:: **Target only.** Updates several keys with the same value in one go - useful for an image tag repeated across a chart. A source or condition must use `key`.
`keyonly`:: **Condition only.** Passes when the key exists, whatever its value.
`comment`:: **Target only.** Appended inline after the value, and only when Updatecli actually changes it.
`searchpattern`:: **Not supported in a source.** It turns `file`/`files` into glob patterns rather than exact names. A source using it fails with `validation error in sources of type 'yaml': the attribute 'spec.searchpattern' is not supported for source`.

== Engines

`engine` picks the YAML library, because none of them handles every case well:

[cols="1,3",options=header]
|===
| Value | Behaviour
| `default` | Default when unset. Equivalent to `go-yaml` and to an empty value.
| `go-yaml` | Same as `default`.
| `yamlpath` | Required for filter expressions such as `$.repos[?(@.repository == 'website')].owner`.
|===

== Key syntax

Arrays are indexed with `key[x]`, counting from zero, and can be chained with dots, `key.array[3].key`:

[source,yaml]
----
COUNTRY_CODE:
- BE
- FR
- LU
----

Here `COUNTRY_CODE[1]` is `FR`.

=== Escaping dots

A dot separates path elements, so a key that itself contains dots is escaped with a backslash. These two files are different:

[source,yaml]
----
image.tag: latest     # one key named "image.tag"  -> image\.tag
----

[source,yaml]
----
image:                # a key "tag" nested under "image" -> image.tag
    tag: latest
----

The escaped form must be written in **single quotes**, otherwise YAML consumes the backslash before Updatecli sees it:

[source,yaml]
----
targets:
  helm-chart-label:
    name: bump chart dependencies
    kind: yaml
    disablesourceinput: true
    spec:
      file: "/tmp/gateway.yaml"
      key: 'spec\.values\.labels\.service\.istio\.io/canonical-revision'
      value: "test"
----

== Multi-document files

`documentindex` selects one document in a `---`-separated file, counting from zero. Its default is not "the first document" but something different per stage:

* **source** - the value is taken from the first document matching the query.
* **condition** - every document is evaluated.
* **target** - **every document is updated.**

That last one surprises people. Given this file:

[source,yaml]
----
kind: Deployment
image:
  tag: 1.0.0
---
kind: Service
image:
  tag: 2.0.0
----

a target on `$.image.tag` with no `documentindex` rewrites both, even though the run report mentions a single key:

[source,yaml]
----
kind: Deployment
image:
  tag: 9.9.9 # updated by updatecli
---
kind: Service
image:
  tag: 9.9.9 # updated by updatecli
----

Set `documentindex: 0` to touch only the first one.

== Remote files

For a source and a condition, `file` accepts `https://`, `http://` and `file://`. A target cannot write back to a URL and fails with `URL scheme is not supported for YAML target`.

== Example

[source,yaml]
----
# updatecli.yaml
{{<include "assets/code_example/docs/plugins/resources/yaml/updatecli.d/default.yaml">}}
----

What it says:

**Source**::
Retrieve the version of the Jenkins helm chart from `https://charts.jenkins.io`.

**Condition**::
Check that `charts/jenkins/requirements.yaml` has a `dependencies` array whose first element is `jenkins`. If not, the pipeline stops here.

**Target**::
Update that first element to the source value in the GitHub repository `olblak/chart`, then publish the change as a pull request against `master` from a working branch.

== Links

* link:/docs/core/versionfilter/["Version Filtering" page]
* link:https://github.com/goccy/go-yaml/issues/290[goccy/go-yaml#290] - why field paths matching on key/value are not supported
