forgejo-mcp/.tekton/on-tag-push-release.yaml
Christoph Görn 5df98baca9
feat(ci): 🚀 add release-tools image build + publish Tekton pipelines (iteration 2)
Implements sections 3-4 of openspec/changes/release-tools-image/tasks.md.
Produces .tekton/release-tools/ containing:

PipelineRuns (PaC):
- on-pull-request-build.yaml — CEL gated to files.any.exists() under
  image/release-tools/ or .tekton/release-tools/. Builds + verifies +
  SBOM-scans the image; no push.
- on-tag-publish.yaml — triggered on refs/tags/release-tools/v* push.
  Pipeline shape (TOCTOU-safe per adversarial review L1):
    fetch-source → resolve-tag → build → verify-tools → syft-scan →
    push-by-digest → cosign-sign-by-digest → cosign-attach-sbom →
    promote-tag (only NOW does vMAJOR.MINOR.PATCH resolve).

Reusable Tasks:
- build-image.yaml — buildah build against image/release-tools/Containerfile
- verify-image-tools.yaml — runs built image, asserts every bundled
  tool's --version + the offline-npx + shell-present scenarios
- syft-scan-image.yaml — CycloneDX SBOM via syft (uses release-tools
  image itself — dogfooding)
- resolve-release-tools-tag.yaml — strips refs/tags/release-tools/ prefix
- push-image-by-digest.yaml — pushes manifest by digest only, no tag
- cosign-sign-image-by-digest.yaml — signs by digest with
  cosign-signing-key (optional: false, fail-closed per spec asymmetry)
- cosign-attach-sbom.yaml — attaches SBOM as cosign attestation
- promote-image-tag.yaml — crane/skopeo tag promotion AFTER sign

Defensive CEL:
- .tekton/on-tag-push-release.yaml — added negative-CEL annotation so a
  release-tools/vX.Y.Z tag push CANNOT accidentally fire the forgejo-mcp
  release pipeline (adversarial review A1).

Verifier T12 PASS: yaml syntax (all 11 files), pre-commit (gitleaks),
openspec validate release-tools-image — all green.

Registry path is TBD (working assumption quay.io/operate-first/release-tools)
— flagged in pipeline header comments; coordinate with op1st-emea-b4mad
maintainers before first tag-publish run.

Refs: forgejo-mcp-1b4
2026-05-25 18:53:49 +02:00

130 lines
4.2 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
name: on-tag-push-release
annotations:
# PipelinesAsCode: trigger on tag pushes matching v* refs.
# `on-target-branch` is overloaded to match refs/tags/* as well as
# refs/heads/* when on-event is "push" — PaC resolves the ref via
# the underlying provider payload (Codeberg/Forgejo).
pipelinesascode.tekton.dev/on-event: "[push]"
pipelinesascode.tekton.dev/on-target-branch: "[refs/tags/v*]"
# T11 defensive CEL (A1 tag-isolation): PaC's glob [refs/tags/v*] may
# match refs/tags/release-tools/vX.Y.Z on some PaC versions because the
# glob is applied after stripping refs/tags/ and "release-tools/v1.0.0"
# starts with a non-v prefix, but belt-and-suspenders ensures this pipeline
# never fires on a release-tools tag. The expression requires the ref to
# start with refs/tags/v followed by a digit, which excludes
# refs/tags/release-tools/... entirely.
pipelinesascode.tekton.dev/on-cel-expression: |
event == "push" && target_branch.matches("^refs/tags/v[0-9]")
pipelinesascode.tekton.dev/task: "[git-clone, .tekton/tasks/goreleaser-release.yaml, .tekton/tasks/cosign-sign-release.yaml, .tekton/tasks/mcpb-pack.yaml]"
pipelinesascode.tekton.dev/max-keep-runs: "10"
# Releases are not cancellable — let in-flight runs finish even if
# another tag arrives.
pipelinesascode.tekton.dev/cancel-in-progress: "false"
spec:
params:
- name: repo_url
value: "{{ repo_url }}"
- name: revision
value: "{{ revision }}"
- name: target_branch
value: "{{ target_branch }}"
pipelineSpec:
params:
- name: repo_url
- name: revision
- name: target_branch
description: |
PaC passes the matched ref here (e.g. refs/tags/v2.24.0). Stripped
to the bare tag name (v2.24.0) for downstream tasks.
workspaces:
- name: source
tasks:
- name: fetch-source
taskRef:
name: git-clone
workspaces:
- name: output
workspace: source
params:
- name: url
value: $(params.repo_url)
- name: revision
value: $(params.revision)
- name: resolve-tag
runAfter:
- fetch-source
params:
- name: target_branch
value: $(params.target_branch)
taskSpec:
params:
- name: target_branch
results:
- name: tag
description: Bare tag name (e.g. v2.24.0).
steps:
- name: resolve
image: registry.access.redhat.com/ubi9/ubi-minimal:latest
script: |
#!/usr/bin/env sh
set -eu
TAG="$(params.target_branch)"
TAG="${TAG#refs/tags/}"
echo "Resolved tag: $TAG"
printf '%s' "$TAG" > "$(results.tag.path)"
- name: goreleaser
runAfter:
- resolve-tag
taskRef:
name: goreleaser-release
workspaces:
- name: source
workspace: source
params:
- name: TAG
value: $(tasks.resolve-tag.results.tag)
- name: cosign-sign
runAfter:
- goreleaser
taskRef:
name: cosign-sign-release
workspaces:
- name: source
workspace: source
params:
- name: TAG
value: $(tasks.resolve-tag.results.tag)
- name: REPO
value: goern/forgejo-mcp
- name: mcpb-pack
runAfter:
- goreleaser
taskRef:
name: mcpb-pack
workspaces:
- name: source
workspace: source
params:
- name: TAG
value: $(tasks.resolve-tag.results.tag)
- name: REPO
value: goern/forgejo-mcp
workspaces:
- name: source
volumeClaimTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
# Release builds need ~6× CI: cross-compiled binaries +
# SBOMs + checksums + .mcpb bundles per platform.
storage: 8Gi