使用模板自定义任务概览

本指南展示了如何使用基于 ConfigMap 的模板,在 TaskRunsPipelineRunsOverview 标签页中渲染丰富的 HTML 报告。

与其将完整的 HTML 或 Markdown 报告写入单个 Task 结果,不如让你的 Tasks 输出小型结构化指标,UI 则使用来自 ConfigMap 的模板来渲染最终的概览。

适用场景:

  • 你希望为某个 Task 提供一致且精美的概览(例如代码扫描摘要)。
  • 你的 Markdown 概览内容接近或超过Task 结果的有效大小限制。
  • 你希望在多个 Tasks 或集群间复用相同的布局。

模板渲染是 overview-markdown 的一种替代方案。如果 Task 仍然输出名为 overview-markdown 的结果,UI 会优先渲染该 Markdown,并跳过模板渲染。

目录

前提条件

  • 集群中已安装 Tekton Pipelines
  • 具有在共享模板命名空间 kube-public 中创建 ConfigMaps 的权限。

工作原理

  1. 你的 Task 输出一个或多个包含指标或摘要数据的结果。
  2. 你的 Task 元数据包含注解,告诉 UI:
    • 应使用哪个 ConfigMap 作为模板(通过标签选择器)。
    • 应读取哪些 Task 结果 并传递给模板。
  3. UI:
    • 检查 TaskRun 是否有名为 overview-markdown 的结果。如果有,则渲染该 Markdown 并停止
    • 否则,读取 TaskRun 的模板注解。
    • 查找匹配的 ConfigMap 并加载 template.ejs
    • 读取 TaskRun 中声明的结果,并合并成单个 JSON 负载。
    • 使用该负载评估 EJS 模板,并在 Overview 标签页中渲染生成的 HTML。

操作步骤

1. 在 Markdown 和模板之间选择

当满足以下条件时,使用 overview-markdown 结果:

  • 内容较短,易于保持在结果大小限制内。
  • 只需要非常简单的格式。

当满足以下条件时,使用模板 ConfigMap

  • 需要更复杂的布局(列、徽章、进度条等)。
  • 已有结构化指标,想要更美观的展示。
  • 希望多个 Tasks 共享同一概览布局。

如果两者都配置了,overview-markdown 优先。只需不向 overview-markdown 结果写入内容,即可查看模板输出。

2. 创建模板 ConfigMap

创建一个包含你的 EJS 模板及标识所属 Task 的标签的 ConfigMap

EJS 是一种简单的模板语言,允许你用纯 JavaScript 生成 HTML 标记。 无需拘泥于组织方式,无需重新发明迭代和控制流,完全是纯 JavaScript。 详情请参见:

示例:

apiVersion: v1
kind: ConfigMap
metadata:
  name: my-task-0.1-overview-template
  # configmap 应位于 kube-public 命名空间
  namespace: kube-public
  labels:
    # 此模板对应的 Task
    style.tekton.dev/overview-template-task: my-task
    # Task/模板配对的版本
    style.tekton.dev/overview-template-task-version: "0.1"
    # UI 应使用的引擎
    style.tekton.dev/overview-template-engine: ejs
data:
  template.ejs: |
    <%
      const m = metrics || {};
      const asNumber = (value, fallback = 0) => {
        const parsed = Number(value);
        return Number.isFinite(parsed) ? parsed : fallback;
      };
    %>

    <div style="font:12px sans-serif;margin:8px 0">
      <div style="display:flex;align-items:center;margin-bottom:6px">
        <strong>My Task Summary</strong>
        <% if (m.report_url) { %>
          <a href="<%= m.report_url %>" target="_blank" rel="noreferrer" style="margin-left:8px">
            查看报告
          </a>
        <% } %>
        <span style="margin-left:auto;color:#5c1">
          状态: <%= m.status || "UNKNOWN" %>
        </span>
      </div>

      <div style="display:flex;flex-wrap:wrap;gap:6px;color:#666">
        <div style="flex:1;min-width:110px">
          <b><%= asNumber(m.total) %></b> 总数
        </div>
        <div style="flex:1;min-width:110px">
          <b><%= asNumber(m.passed) %></b> 通过
        </div>
        <div style="flex:1;min-width:110px">
          <b><%= asNumber(m.failed) %></b> 失败
        </div>
      </div>
    </div>

要点:

  • 模板文件名始终为 template.ejs
  • UI 会将结果中的变量注入模板。
  • 你可以使用标准的 EJS 语法<% %><%= %>)实现简单的逻辑和格式化。

3. 在 Task 中添加结果

在你的 Task 中定义一个结果,用于承载模板使用的指标。

示例 Task

apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: my-task
spec:
  results:
    - name: TASK_METRICS
      type: object
      description: 概览模板的摘要指标。
      properties:
        total: {}
        passed: {}
        failed: {}
        status: {}
        report_url: {}
  steps:
    - name: run-and-collect-metrics
      image: <your-image>
      script: |
        #!/usr/bin/env sh
        set -eu

        # TODO: 替换为真实值
        total=10
        passed=9
        failed=1
        status="OK"
        report_url="https://example.com/report/123"

        printf '{"total": "%s", "passed": "%s", "failed": "%s", "status": "%s", "report_url": "%s"}' \
          "$total" \
          "$passed" \
          "$failed" \
          "$status" \
          "$report_url" \
          > "$(results.TASK_METRICS.path)"

指导原则:

  • 保持结果小且扁平。使用简单字段,如计数、百分比、状态和 URL。
  • 不要嵌入大量文本。
  • 如果使用对象类型结果,确保步骤写入有效的 JSON(无尾随逗号,正确引用)。

4. 注解 Task 以绑定模板

使用注解告诉 UI 应使用哪个模板以及传递哪些结果。

单结果示例:

apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: my-task
  annotations:
    # 1) 模板 ConfigMap 的选择器
    style.tekton.dev/overview-template-selector: >
      style.tekton.dev/overview-template-task=my-task,
      style.tekton.dev/overview-template-task-version=0.1

    # 2) 从 TaskRun 读取哪些结果
    #    此处:将 TASK_METRICS 作为模板中的 "metrics"
    #    如果不指定别名,默认使用结果名(TASK_METRICS)。
    style.tekton.dev/overview-template-result-key: TASK_METRICS:metrics
    
spec:
  results:
    - name: TASK_METRICS
      type: object
  steps:
    - name: run-and-collect-metrics
      image: <your-image>
      # ...

概念上,UI 会构造如下负载:

{
  "metrics": {
    "total": "10",
    "passed": "9",
    "failed": "1",
    "status": "OK",
    "report_url": "https://example.com/report/123"
  }
}

然后用该负载评估 template.ejs,你可以访问 metrics.totalmetrics.status 等。

你也可以将多个 Task 结果合并到一个模板渲染中。适用于:

  • 一个结果包含指标。
  • 另一个结果包含完整报告的 URL。

示例:

metadata:
  annotations:
    style.tekton.dev/overview-template-selector: >
      style.tekton.dev/overview-template-task=sonarqube-scanner,
      style.tekton.dev/overview-template-task-version=0.5

    # 多个结果,用逗号分隔
    # 别名映射:
    # - CODE_SCAN_METRICS -> metrics
    # - SCAN_RESULT_URL   -> SCAN_RESULT_URL(无别名)
    # - SCAN_PROJECT      -> SCAN_PROJECT(无别名)
    style.tekton.dev/overview-template-result-key: |
      CODE_SCAN_METRICS:metrics,SCAN_RESULT_URL,SCAN_PROJECT

UI 会构造类似如下的负载:

{
  "metrics": {
    "bugs": "2",
    "vulnerabilities": "1"
  },
  "SCAN_RESULT_URL": "https://devops-sonar.example.net/dashboard?id=project&branch=main",
  "SCAN_PROJECT": ["foo", "bar"]
}

5. 运行 Task 并检查 Overview 标签页

  1. ConfigMapTask 应用到集群。

  2. 创建一个使用该 TaskTaskRunPipelineRun,例如:

    apiVersion: tekton.dev/v1
    kind: TaskRun
    metadata:
      name: my-task-run
    spec:
      taskRef:
        name: my-task
  3. 等待运行完成。

  4. 在 UI 中打开该运行,选择 Overview 标签页。

如果配置正确,你应能看到模板生成的 HTML。

如果 Task 同时输出了 overview-markdown,则显示 Markdown 而非模板。

提示与约定

  • 优先使用小型指标结果。 将结果视为紧凑摘要。
  • 保持模板逻辑简单。 关注展示,避免在模板中进行复杂业务逻辑或大量计算。
  • 防御性处理缺失数据。 在模板中使用默认值,避免字段缺失导致渲染失败。

故障排查

Overview 标签页无内容

  • Task 是否输出了空的 overview-markdown
    • 如果是,UI 会显示该空 Markdown 并忽略模板。只需不写入 overview-markdown 结果即可查看模板输出。
  • TaskRun 是否包含 overview-template-result-key 中列出的结果?
    • 检查 TaskRun YAML 中的 .status.results
  • overview-template-selector 的选择器是否在 kube-public 命名空间中精确匹配一个 ConfigMap
    • 使用 kubectl get configmap -l <your-selector> -n kube-public 测试。

模板渲染但数据为空或错误

  • 如果使用对象结果,确认结果有效且包含模板期望的字段。
  • 检查 overview-template-result-key 中的名称是否与 Task 结果名称完全匹配。

结果大小或终止消息限制

  • 从结果中移除非必要字段。
  • 将详细的逐文件报告推送到日志或外部系统,结果中仅包含摘要数字和 URL。
  • 记住每个 Task 仍然在所有步骤和结果间共享单个终止消息预算。
  • 修改结果限制

选择了错误的模板

  • 确保 overview-template-selector 足够具体(例如同时包含 Task 名称和版本)。
  • 避免为无关模板复用相同标签。
  • 如果多个 ConfigMaps 匹配选择器,收紧标签以确保只选中一个。

当你拥有模板 ConfigMap指标结果和正确的Task 注解后,即可构建可复用、版本化的 Task 概览布局,而无需修改 UI 本身。