|
| 1 | +--- |
| 2 | +title: PipelineRun Support Markdown Output |
| 3 | +authors: |
| 4 | + |
| 5 | +creation-date: 2025-09-25 |
| 6 | +last-updated: 2025-09-25 |
| 7 | +status: proposed |
| 8 | +--- |
| 9 | + |
| 10 | +# TEP-0003: PipelineRun Support Markdown Output |
| 11 | + |
| 12 | +## Record of Changes |
| 13 | + |
| 14 | +| Order | Change of Content | Reason for Change | Change Time | Change Executor | Approved By | |
| 15 | +|:------|:-----------------------------------------------|:--------------------------|:------------|:----------------|:-----------------| |
| 16 | +| 1 | Design: PipelineRun Support Markdown Output | publish | 2025-09-25 | [email protected] | [email protected] | |
| 17 | +| 2 | Add Conclusion and Non-Functional Requirements | add discussion conclusion | 2025-09-28 | [email protected] | [email protected] | |
| 18 | + |
| 19 | +## Summary |
| 20 | + |
| 21 | +The Tekton PipelineRun details page should allow users to view a summary of an execution report. |
| 22 | +This document describes the design for displaying **markdown-formatted** reports on the pipeline overview page. |
| 23 | + |
| 24 | +## Motivation |
| 25 | + |
| 26 | +Currently, users can only write task execution results to the pipeline **results**, |
| 27 | +and they cannot quickly understand task outcomes on the overview page. |
| 28 | +We need a custom report output capability so users can display task results as markdown on the overview page. |
| 29 | + |
| 30 | +### Goals |
| 31 | + |
| 32 | +- The overview page supports displaying **markdown** reports. |
| 33 | +- Users can output a custom report within a **Task**. |
| 34 | +- Reports remain visible on the overview page even when the PipelineRun **fails**. |
| 35 | + |
| 36 | +### Non-Goals |
| 37 | + |
| 38 | +- Provide report output templates. |
| 39 | +- Support unlimited report size. |
| 40 | + |
| 41 | +## Design Details |
| 42 | + |
| 43 | +### Constraints |
| 44 | + |
| 45 | +Writing Tekton Pipeline **results** has the following constraints: |
| 46 | + |
| 47 | +1. Only when the **Task** referenced by a **Pipeline result** succeeds (including **finally** tasks) will that result be written to the PipelineRun. |
| 48 | +2. If the referenced **Task** fails, although the **TaskRun** `status.results` will still be written, the **PipelineRun** `status.results` will **not** include that result. |
| 49 | + |
| 50 | +Tekton considers results produced by failed tasks to be unreliable and therefore not suitable as pipeline-level results. |
| 51 | + |
| 52 | +In earlier versions, TaskRuns also did not emit such results, and subsequent tasks could not reference them. That issue was resolved by [#6510 feat: support to produce results from a failed task](https://github.com/tektoncd/pipeline/pull/6510). |
| 53 | + |
| 54 | +However, pipeline results still cannot reference failed tasks. This issue remains open and currently has no community PR that fully addresses it: [#3749 Publish results when task and pipeline runs fail](https://github.com/tektoncd/pipeline/issues/3749). |
| 55 | + |
| 56 | +### Proposal |
| 57 | + |
| 58 | +We considered the following approaches to work around the above limitations (**recommended: 1 and 2**): |
| 59 | + |
| 60 | +1. **Use a `finally` task to output a result.** |
| 61 | +2. **Read reports directly from `TaskRun` results.** |
| 62 | +3. Pass the report via a **workspace** and print it in a `finally` task log. |
| 63 | +4. Modify Tekton source code so pipeline results can pull values from **failed** tasks. |
| 64 | + |
| 65 | +#### Option 1: Output the result via a `finally` task |
| 66 | + |
| 67 | +Assume the task that produces the report is named `scan`: |
| 68 | + |
| 69 | +1. Add a `finally` task named `report`, which reads the `scan` task's result and writes it to its own result. This task must succeed. |
| 70 | +2. Configure pipeline results to reference the `report` task, e.g. `$(finally.report.results.string-result)`. |
| 71 | +3. In the pipeline editor UI, let users specify which results to treat as reports (for example, via an annotation like `style.tekton.dev/reports-in-results: [report1, report2]`). |
| 72 | + |
| 73 | +This way, a **guaranteed-success** `report` task circumvents the limitation that the pipeline cannot read results from a failed task—without changing the PipelineRun's overall status (if `scan` fails, the PipelineRun still fails). |
| 74 | + |
| 75 | +**Points to note:** |
| 76 | + |
| 77 | +1. If `scan` is configured with `onError: continue`, consider adding an `exit` `finally` task that runs **only** when `$(tasks.scan.status)` is `Failed`, and simply `exit 1`. |
| 78 | +2. The UI currently does not allow selecting results from **finally** tasks; we need to fix the validation rule (error: `The result value must match the expression $(tasks..*.results..*)`). |
| 79 | + |
| 80 | +**Advantages** |
| 81 | + |
| 82 | +1. Minimal work: mainly implement the overview-page rendering for markdown reports. |
| 83 | +2. Aligns with Tekton's design: PipelineRun results originate from successful tasks. |
| 84 | + |
| 85 | +**Disadvantage** |
| 86 | + |
| 87 | +1. Some user configuration overhead: users must add a `finally` task, which could be confusing. |
| 88 | +2. Multiple reporting tasks may require multiple `finally` tasks. |
| 89 | +3. Single Task result size is capped at **4 KB**. |
| 90 | + |
| 91 | +#### Option 2: Read reports from `TaskRun` results |
| 92 | + |
| 93 | +Assume the report-producing task is `scan`. |
| 94 | + |
| 95 | +Add report collection settings to the pipeline editor and store them in annotations, e.g., to read `string-result` from the `scan` task: |
| 96 | + |
| 97 | +1. `style.tekton.dev/reports-scan-display-name: SonarQube Scan Report` |
| 98 | +2. `style.tekton.dev/reports-scan-result: string-result` |
| 99 | + |
| 100 | +On the PipelineRun overview page: |
| 101 | + |
| 102 | +1. Parse the collection rules from annotations. |
| 103 | +2. Based on the specified `pipelineTaskName`, find the corresponding **TaskRun** from `status.childReferences`. |
| 104 | +3. Read the report from the TaskRun's referenced **result**. |
| 105 | + |
| 106 | +Here is an example of the PipelineRun annotations and `status.childReferences`: |
| 107 | + |
| 108 | +```yaml |
| 109 | +apiVersion: tekton.dev/v1 |
| 110 | +kind: PipelineRun |
| 111 | +metadata: |
| 112 | + annotations: |
| 113 | + style.tekton.dev/reports-scan-display-name: SonarQube Scan Report |
| 114 | + style.tekton.dev/reports-scan-result: string-result |
| 115 | +status: |
| 116 | + childReferences: |
| 117 | + - apiVersion: tekton.dev/v1 |
| 118 | + kind: TaskRun |
| 119 | + name: result-test-pfqzz-scan |
| 120 | + pipelineTaskName: scan |
| 121 | + - apiVersion: tekton.dev/v1 |
| 122 | + kind: TaskRun |
| 123 | + name: result-test-pfqzz-report |
| 124 | + pipelineTaskName: report |
| 125 | +``` |
| 126 | +
|
| 127 | +**Advantages** |
| 128 | +
|
| 129 | +1. Less user configuration: only specify the **Task** and the **result** key. |
| 130 | +2. Annotations offer good extensibility for future report customization options. |
| 131 | +
|
| 132 | +**Disadvantage** |
| 133 | +
|
| 134 | +1. Requires FE work: the UI needs to read from **TaskRun** results. |
| 135 | +2. CLI users cannot read the report directly from the PipelineRun, which may be a suboptimal experience (this feature primarily targets UI users). |
| 136 | +3. Single Task result size is capped at **4 KB**. |
| 137 | +
|
| 138 | +#### Option 3: Output the report via a workspace |
| 139 | +
|
| 140 | +Assume the report-producing task is `scan`: |
| 141 | + |
| 142 | +1. Add a `finally` task named `report` that mounts the **same workspace** as `scan`. |
| 143 | +2. The `scan` task writes the report into that workspace; the `report` task prints the report to logs. |
| 144 | +3. The overview page reads the report content from the `report` task's logs. |
| 145 | + |
| 146 | +**Advantages** |
| 147 | + |
| 148 | +1. Suitable for **long text**: the workspace size is configurable and not limited to 4 KB. |
| 149 | + |
| 150 | +**Disadvantage** |
| 151 | + |
| 152 | +1. Requires an extra workspace to pass the report. |
| 153 | +2. Logs may be cleaned up, causing the report to be lost. |
| 154 | + |
| 155 | +#### Option 4: Modify Tekton source code |
| 156 | + |
| 157 | +Change Tekton so that a pipeline result can reference outputs from **failed** tasks. |
| 158 | + |
| 159 | +Although the community has requested this ([#3749](https://github.com/tektoncd/pipeline/issues/3749)), it is still unresolved. To date, only the ability for **failed TaskRuns** to **emit and pass** results has been merged ([#6510](https://github.com/tektoncd/pipeline/pull/6510)). |
| 160 | + |
| 161 | +The official stance is that results from failed tasks are unreliable and should not be surfaced as pipeline-level results. It is possible this will not be prioritized upstream. Implementing and maintaining a fork that changes core Tekton Pipeline behavior would incur non-trivial effort and upgrade burden. |
| 162 | + |
| 163 | +**Recommendation:** prioritize the other approaches first, and dive deeper into this option only if necessary. |
| 164 | + |
| 165 | +## Conclusion |
| 166 | + |
| 167 | +1. **Adopt Option 2:** The frontend should read the report **directly from TaskRun results** and render it on the **Summary** tab. |
| 168 | +2. **Standardize a result key:** Agree on a specific result key (e.g., **`markdown`**, type **string**). Tasks write the markdown-formatted report to this result. |
| 169 | +3. **Render across TaskRuns:** On the PipelineRun **Summary** page, the frontend iterates over all **TaskRuns**; if the specified result is non‑empty, render it as **markdown**. |
| 170 | +4. **Future formats:** If other report formats are needed later, introduce additional **well-named result keys** accordingly. |
| 171 | +5. **With Tekton Results:** When **tekton-results** is deployed and a **TaskRun** has been garbage‑collected, the UI must support fetching the archived **results** to continue rendering the report. |
| 172 | + |
| 173 | +## Non-Functional Requirements |
| 174 | + |
| 175 | +### Performance |
| 176 | +- The Summary tab should load within **P50 ≤ 1.5s** and **P95 ≤ 3s** for PipelineRuns with ≤ 20 TaskRuns; for larger runs, render progressively as data arrives. |
| 177 | +- Fetch TaskRun `status.results` concurrently with request de-duplication; cache per-PipelineRun results for ~30 seconds to reduce API QPS. |
| 178 | +- Respect Tekton per-result size limits (≤ 4 KB per Task result). The UI should cap aggregated render size to ~64 KB and clearly indicate when content is truncated. |
| 179 | + |
| 180 | +### Reliability |
| 181 | +- Primary data source is TaskRun `status.results`. If TaskRuns have been garbage-collected and **tekton-results** is available, fall back to archived data. |
| 182 | +- Even when the PipelineRun **fails**, surface available reports from failed TaskRuns. |
| 183 | +- Apply bounded retries with exponential backoff on API calls; on persistent errors, degrade gracefully by showing partial content with actionable error details. |
| 184 | + |
| 185 | +### Security |
| 186 | +- Enforce namespace-scoped RBAC: only users with `get/list` permission on TaskRuns can view reports; do not broaden privileges. |
| 187 | +- Sanitize Markdown strictly (no inline HTML or scripts; external links opened safely; images disabled unless explicitly allowed). |
| 188 | +- Mask obvious secrets/tokens in rendered content and avoid writing sensitive data to caches or logs. |
| 189 | +- Emit audit entries for viewing/report access (timestamp, actor, resource). |
| 190 | + |
| 191 | +### Usability |
| 192 | +- Card-per-TaskRun presentation showing task name, status, timing, and data source (live vs. archive). |
| 193 | +- Support headings, lists, tables, code blocks, and inline code; provide a one-click copy action; collapse long sections. |
| 194 | +- Provide skeleton loading, clear error cards, and “truncated” indicators; ensure keyboard accessibility, internationalization, and dark-mode readiness. |
| 195 | + |
| 196 | +### High Availability |
| 197 | +- Target feature-level availability of **≥ 99.9% per month** (successful Summary tab load as the SLI). |
| 198 | +- Stateless components with horizontal scaling; health probes enabled; sensible timeouts and circuit breakers. |
| 199 | +- If dependencies (K8s API or tekton-results) are degraded, continue in a read-only/partial mode using cached or already-fetched data. |
| 200 | + |
| 201 | +### Data Migration |
| 202 | +- Not involved for this feature. |
0 commit comments