|
| 1 | +# Guidelines for Using Tekton `when` Conditions |
| 2 | + |
| 3 | +This document defines when pipeline-level `when` conditions can and cannot be used in this |
| 4 | +project's Tekton pipelines. These rules exist because a skipped task still participates in the |
| 5 | +pipeline graph: its results become empty strings, `runAfter` dependents still execute, and any |
| 6 | +downstream task that consumes those empty results may silently misbehave or fail. |
| 7 | + |
| 8 | +--- |
| 9 | + |
| 10 | +## Rules at a Glance |
| 11 | + |
| 12 | +| Scenario | Allowed | |
| 13 | +| ---------------------------------------------------------------------------------------------------------------------------- | ------- | |
| 14 | +| Task has no downstream dependents | ✅ Yes | |
| 15 | +| `when` input is a pipeline parameter | ✅ Yes | |
| 16 | +| `when` input is a task result, the source task cannot be skipped, AND the guarded task has no task results in its own params | ✅ Yes | |
| 17 | +| `when` input is a task result AND the source task can itself be skipped | ❌ No | |
| 18 | +| `when` input is a task result AND the guarded task also consumes task results in its params | ❌ No | |
| 19 | + |
| 20 | +--- |
| 21 | + |
| 22 | +## Rule 1 — No downstream dependents |
| 23 | + |
| 24 | +A `when` condition is always safe when no other task depends on the guarded task's results or |
| 25 | +uses it in a `runAfter` chain. There is no cascading effect if the task is skipped. |
| 26 | + |
| 27 | +```yaml |
| 28 | +# Safe: nothing downstream reads this task's results |
| 29 | +- name: notify-slack |
| 30 | + taskRef: |
| 31 | + name: send-slack-notification |
| 32 | + when: |
| 33 | + - input: $(tasks.check.results.failed) |
| 34 | + operator: in |
| 35 | + values: ["true"] |
| 36 | + params: |
| 37 | + - name: message |
| 38 | + value: "Pipeline failed" |
| 39 | +``` |
| 40 | +
|
| 41 | +--- |
| 42 | +
|
| 43 | +## Rule 2 — `when` input is a pipeline parameter |
| 44 | + |
| 45 | +Using a pipeline parameter as the `when` condition input is always safe. Pipeline parameters are |
| 46 | +resolved before any task runs and are guaranteed to be non-empty (or have a known default). No |
| 47 | +task result propagation is involved. |
| 48 | + |
| 49 | +```yaml |
| 50 | +# Safe: condition is a pipeline param, not a task result |
| 51 | +- name: publish-pyxis-data |
| 52 | + taskRef: |
| 53 | + name: publish-pyxis-data |
| 54 | + when: |
| 55 | + - input: $(params.cert_project_required) |
| 56 | + operator: in |
| 57 | + values: ["true"] |
| 58 | + params: |
| 59 | + - name: cert_project_id |
| 60 | + value: $(params.cert_project_id) |
| 61 | +``` |
| 62 | + |
| 63 | +--- |
| 64 | + |
| 65 | +## Rule 3 — `when` input is a task result, source task cannot be skipped, guarded task has no task results in params |
| 66 | + |
| 67 | +A task result can be used as a `when` condition input when **all three** of the following hold: |
| 68 | + |
| 69 | +1. The task that produces the result **cannot itself be skipped** — it has no `when` condition and |
| 70 | + always runs. If the source task can be skipped, its result becomes an empty string, making the |
| 71 | + `when` condition unreliable (it may silently evaluate as if the condition were false, with no |
| 72 | + indication that the source task was skipped rather than producing a genuine empty value). |
| 73 | +2. The guarded task's own params **contain no task results** — all param values come from pipeline |
| 74 | + parameters, literal strings, or workspace references. |
| 75 | +3. Downstream tasks that receive the guarded task's results must already handle empty values (via |
| 76 | + their own `when` conditions or inner script guards), since a skipped task's results become empty |
| 77 | + strings. |
| 78 | + |
| 79 | +```yaml |
| 80 | +# NOT allowed: source task (some-upstream-task) has its own when condition and can be skipped. |
| 81 | +# If it is skipped, its result is "", and the when condition below becomes meaningless. |
| 82 | +- name: some-upstream-task |
| 83 | + when: |
| 84 | + - input: $(params.flag) |
| 85 | + operator: in |
| 86 | + values: ["true"] |
| 87 | + ... |
| 88 | +
|
| 89 | +- name: guarded-task |
| 90 | + when: |
| 91 | + - input: $(tasks.some-upstream-task.results.value) # ❌ source can be skipped |
| 92 | + operator: notin |
| 93 | + values: [""] |
| 94 | + ... |
| 95 | +``` |
| 96 | + |
| 97 | +```yaml |
| 98 | +# Safe: when uses a task result, source task always runs (no when condition), guarded task |
| 99 | +# params are all pipeline params |
| 100 | +- name: link-pull-request-with-open-status |
| 101 | + taskRef: |
| 102 | + name: link-pull-request |
| 103 | + when: |
| 104 | + - input: $(tasks.get-ci-results.results.test_result_id) # get-ci-results always runs ✓ |
| 105 | + operator: notin |
| 106 | + values: [""] |
| 107 | + params: |
| 108 | + - name: pipeline_image |
| 109 | + value: $(params.pipeline_image) # pipeline param — OK |
| 110 | + - name: pyxis_url |
| 111 | + value: $(params.pyxis_url) # pipeline param — OK |
| 112 | +``` |
| 113 | + |
| 114 | +--- |
| 115 | + |
| 116 | +## Rule 4 — `when` input is a task result, guarded task also consumes task results in params ❌ |
| 117 | + |
| 118 | +This combination is **not allowed**. When the `when` condition is based on a task result and the |
| 119 | +guarded task also consumes task results in its own params, skipping the task creates an ambiguous |
| 120 | +state in the result propagation chain: |
| 121 | + |
| 122 | +1. The upstream tasks that produced the param results ran and completed. |
| 123 | +2. The guarded task is skipped — those results are not consumed and not transformed. |
| 124 | +3. Any downstream task expecting the guarded task's output receives empty strings, with no clear |
| 125 | + signal about whether the skip was intentional. |
| 126 | + |
| 127 | +This makes the pipeline hard to reason about and can cause silent failures downstream. |
| 128 | + |
| 129 | +```yaml |
| 130 | +# NOT allowed: when uses a task result, AND params also reference task results |
| 131 | +- name: get-pyxis-certification-data |
| 132 | + taskRef: |
| 133 | + name: get-pyxis-certification-data |
| 134 | + when: |
| 135 | + - input: $(tasks.certification-project-check.results.certification_project_id) # task result |
| 136 | + operator: notin |
| 137 | + values: [""] |
| 138 | + params: |
| 139 | + - name: cert_project_id |
| 140 | + value: $(tasks.certification-project-check.results.certification_project_id) # also task result ❌ |
| 141 | + - name: pyxis_url |
| 142 | + value: $(tasks.set-env.results.pyxis_url) # also task result ❌ |
| 143 | +``` |
| 144 | + |
| 145 | +**How to fix this:** instead, add an inner guard in the task script and rely on the task's own |
| 146 | +early-exit logic to handle the empty-input case, as was the established workaround pattern in |
| 147 | +this project. |
| 148 | + |
| 149 | +```bash |
| 150 | +# In the task script — guard against empty cert_project_id at the script level |
| 151 | +if [ "$(params.cert_project_id)" == "" ]; then |
| 152 | + echo -n | tee "$(results.foo.path)" |
| 153 | + exit 0 |
| 154 | +fi |
| 155 | +``` |
| 156 | + |
| 157 | +--- |
| 158 | + |
| 159 | +## Summary Decision Tree |
| 160 | + |
| 161 | +``` |
| 162 | +Is the task being considered for a when condition? |
| 163 | +│ |
| 164 | +├── Does it have no downstream dependents? |
| 165 | +│ └── YES → ✅ Use when condition freely |
| 166 | +│ |
| 167 | +├── Is the when condition input a pipeline parameter? |
| 168 | +│ └── YES → ✅ Use when condition freely |
| 169 | +│ |
| 170 | +└── Is the when condition input a task result? |
| 171 | + │ |
| 172 | + ├── Can the source task itself be skipped (does it have a when condition)? |
| 173 | + │ └── YES → ❌ Do NOT use when condition |
| 174 | + │ Use an inner script guard (exit 0) instead |
| 175 | + │ |
| 176 | + └── Does the guarded task's params contain any task results? |
| 177 | + ├── NO → ✅ Use when condition (self-contained skip) |
| 178 | + └── YES → ❌ Do NOT use when condition |
| 179 | + Use an inner script guard (exit 0) instead |
| 180 | +``` |
| 181 | +
|
| 182 | +--- |
0 commit comments