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

== Description

Updatecli orders execution at two different levels:

* **between manifests**, with the root keys `id` and `dependson`, described on this page
* **between the resources of one manifest**, with the resource key `dependson`, described in <<_resource_ordering,Resource ordering>>

The two are independent: `dependson` at the root of a manifest refers to other manifests, while
`dependson` inside a source, condition, or target refers to other resources of the same manifest.

== Manifest ordering

By default, Updatecli treats each manifest as an independent pipeline and does not guarantee ordering.
When you run multiple manifests, Updatecli can order them explicitly before execution.

Manifest ordering is controlled with two root keys:

* `id`: declares the manifest identifier that other manifests can depend on
* `dependson`: lists the manifest IDs that must run first

This is useful when one manifest prepares data, policies, or files that another manifest relies on.

[IMPORTANT]
====
`id` and `dependson` only affect the execution order of manifests.

They do *not* change:

* branch naming
* pull request grouping
* workflow grouping

For those behaviors, keep using `pipelineid`.
====

== Parameters

=== `id`

The `id` key defines a manifest dependency identifier.

[source,yaml]
----
name: Base image updates
id: base-images
----

Use `id` when another manifest must wait for this one to complete.

[NOTE]
====
Multiple manifests may intentionally share the same `id`.
When another manifest depends on that `id`, Updatecli waits for *all* matching manifests to run first.
====

=== `dependson`

The `dependson` key lists the manifest IDs that must be executed before the current manifest.

[source,yaml]
----
name: Application updates
id: app
dependson:
  - base-images
  - shared-policies
----

Each entry in `dependson` must reference an existing manifest `id`.

== Example

In this example, the application manifest only runs after the base image manifest has been evaluated.

++++
<details><summary>manifests/base.yaml</summary>

<pre>
name: Base image updates
id: base-images

sources:
  alpine:
    name: Get latest Alpine version
    kind: dockerimage
    spec:
      image: alpine
      tag: latest

targets:
  dockerfile:
    name: Update Dockerfile base image
    kind: dockerfile
    spec:
      file: Dockerfile
      instruction: FROM
</pre>
</details>
++++

++++
<details><summary>manifests/app.yaml</summary>

<pre>
name: Application updates
id: app
dependson:
  - base-images

sources:
  version:
    name: Get latest release
    kind: githubrelease
    spec:
      owner: updatecli
      repository: updatecli

targets:
  yaml:
    name: Update application version
    kind: yaml
    spec:
      file: values.yaml
      key: $.image.tag
</pre>
</details>
++++

If you run Updatecli against the `manifests/` directory, it executes `base.yaml` before `app.yaml`.

== Behavior

=== Shared IDs

If multiple manifests share the same `id`, a dependency waits for all of them.

[source,yaml]
----
# policies/a.yaml
name: Policy A
id: shared-policy

# policies/b.yaml
name: Policy B
id: shared-policy

# app.yaml
name: App
dependson:
  - shared-policy
----

In this case, `app.yaml` runs after both `policies/a.yaml` and `policies/b.yaml`.

=== Legacy manifests

Manifests without an explicit `id` continue to work as before.
They keep their existing execution order, but they cannot be referenced from another manifest using `dependson`.

=== Invalid dependencies

Updatecli stops with an error when:

* a manifest depends on an unknown `id`
* a manifest depends on its own `id`
* manifest dependencies create a cycle

=== Autodiscovery

Pipelines generated by link:/docs/core/autodiscovery/[autodiscovery] inherit the `id` of the
manifest declaring the `autodiscovery` key, and their `dependson` is merged with the parent one.
Ordering a discovered set relative to a handwritten manifest therefore only requires declaring
`id` and `dependson` on the autodiscovery manifest itself.

[#_resource_ordering]
== Resource ordering

Inside a single manifest, Updatecli builds a dependency graph of the sources, conditions and targets,
and runs each resource as soon as the resources it depends on are done.

=== Implicit dependencies

Most manifests never need an explicit `dependson`, because the following already create an edge in
the graph:

[cols="1,3",options=header]
|===
| Declaration | Resulting dependency

| `sourceid: mySource` on a condition or a target
| That resource waits for `mySource`. When the manifest has exactly one source, `sourceid` is filled in automatically.

| `{{ source "mySource" }}` anywhere in a resource configuration
| The resource waits for `mySource`, even if `sourceid` points somewhere else. The same holds for `{{ condition "..." }}`, `{{ target "..." }}`, and `{{ pipeline "..." }}`.

| any condition in the manifest
| Every target waits for every condition, unless the target sets `disableconditions: true`.
|===

=== Explicit dependencies

`dependson` accepts a list of rules following the format `(<category>#)<resourceID>(:<operator>)`:

* `<category>` is `source`, `condition`, or `target`. When omitted, it defaults to the category of the resource declaring the dependency.
* `<resourceID>` is the key of another resource in the same manifest.
* `<operator>` is `and` or `or`, defaulting to `and`.
`and` requires every listed dependency to succeed, `or` requires at least one.

[source,yaml]
----
sources:
  tag:
    kind: dockerimage
    spec:
      image: golang

  digest:
    kind: dockerdigest
    # Same category, so this means "source#tag"
    dependson:
      - tag
    spec:
      image: golang
      tag: '{{ source "tag" }}'

targets:
  dockerfile:
    kind: dockerfile
    sourceid: digest
    disableconditions: true
    # Cross-category dependency, with an explicit operator
    dependson:
      - condition#imageExists:and
    spec:
      file: Dockerfile
      instruction:
        keyword: FROM
        matcher: golang
----

A resource whose dependencies are not met is **skipped**, not failed.
Note that a condition dependency must explicitly succeed: a skipped condition does not unlock the
resources depending on it.

=== Depending on a change rather than a success

Targets additionally accept `dependsonchange: true`.
The target then only runs when the **targets** it depends on actually modified something.
Dependencies on sources and conditions are ignored while this flag is set.

See link:/docs/core/target/[target] for a complete example.

=== Invalid resource dependencies

Updatecli refuses to run a manifest when a `dependson` entry references an unknown resource, or when
the resource dependencies form a loop.

TIP: Use `dependson` together with `disablesourceinput: true`, or without `conditionids`, to break a cycle that Updatecli detects between two resources.

=== Visualizing the graph

To check what Updatecli actually derived from a manifest, render the graph:

[source,bash]
----
updatecli manifest show --graph --graph-flavor mermaid
----

`--graph-flavor` accepts `dot` (the default, for Graphviz) and `mermaid`.
For the example above, the mermaid output looks like:

[source]
----
graph TD
    source#version(["version (shell)"])
    source#version --> target#file
    target#file("file (file)")
    condition#exists{"exists (shell)"}
    condition#exists --> target#file
----

== Deprecated keys

[cols="1,3",options=header]
|===
| Key | Replacement

| `depends_on` on a resource
| `dependson`

| `conditionids` on a target
| `dependson` with `condition#` keys, combined with `disableconditions: true`
|===

== Go Further

* To understand the full manifest structure, see **link:/docs/core/configuration/[configuration]**
* To group manifests into the same workflow, see `pipelineid` in **link:/docs/core/configuration/[configuration]**
* To know more about the resources being ordered, see **link:/docs/core/source/[source]**, **link:/docs/core/condition/[condition]**, and **link:/docs/core/target/[target]**
