apiVersion: tekton.dev/v1 kind: PipelineRun metadata: name: forgejo-mcp-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, .tekton/release-tools/tasks/build-image.yaml, .tekton/release-tools/tasks/push-image-by-digest.yaml, .tekton/release-tools/tasks/cosign-sign-image-by-digest.yaml, .tekton/release-tools/tasks/cosign-attach-sbom.yaml, .tekton/release-tools/tasks/promote-image-tag.yaml, .tekton/release-tools/tasks/delete-tag-on-failure.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" # Tekton Chains: upload in-toto SLSA v1.0 provenance for the published app # image to the Rekor transparency log. Chains reads the IMAGE_URL + # IMAGE_DIGEST pipeline results below and attaches a cosign attestation # binding the image digest → PipelineRun → git commit → builder identity. # Verify with: cosign verify-attestation --type slsaprovenance \ # --key cosign-images.pub codeberg.org/goern/forgejo-mcp: chains.tekton.dev/transparency-upload: "true" # dn0 — App OCI image publish (decided 2026-06-01): # Registry: codeberg.org/goern/forgejo-mcp (mirrors the signed-binary # release namespace). Tags: vMAJOR.MINOR.PATCH + latest (promote-image-tag). # Single-arch amd64 (reuses release-tools-build-image, which is not a # manifest-list build). Image build runs in parallel with goreleaser — it # compiles its own binary inside the Containerfile and only depends on # resolve-tag. # Required Secrets in op1st-pipelines (operator-confirmable preconditions): # - codeberg-pusher (dockerconfigjson — push auth). Its PAT MUST # carry `write:package` on goern/forgejo-mcp. The cluster cannot validate # token scope; confirm once via the Codeberg UI before the first vX.Y.Z # tag. The release-tools image proved this Secret can push to codeberg.org, # but that auth targets the operate-first org — package-write on goern is # a SEPARATE grant that must be present on the same PAT identity. # - cosign-signing-key-images (cosign.key/.password — optional: false, # fail-closed). A missing key fails the run; no tag is ever promoted. 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. results: # Tekton Chains reads IMAGE_URL + IMAGE_DIGEST at the Pipeline level to # bind the published app image to the SLSA provenance attestation it # generates. Without these, Chains produces subject-less provenance that # cannot be verified against a specific image digest. - name: IMAGE_URL value: $(tasks.push-image-by-digest.results.IMAGE_URL) - name: IMAGE_DIGEST value: $(tasks.push-image-by-digest.results.IMAGE_DIGEST) workspaces: - name: source - name: registry-credentials description: | Kubernetes Secret containing .dockerconfigjson (or config.json) with push credentials for codeberg.org/goern/forgejo-mcp. Used by the image push, cosign-sign, SBOM-attach, and tag-promote tasks. 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 # dn0 — App OCI image (codeberg.org/goern/forgejo-mcp). Reuses the # release-tools image tasks. The Containerfile compiles its own binary, so # it is logically independent of goreleaser. It MUST NOT run in parallel # with the goreleaser-family tasks, however: build-image runs buildah # privileged-as-root and writes its ~1GB store (.buildah) into the SHARED # `source` PVC, while goreleaser/cosign-sign/mcpb-pack read+write the same # volume as uid 1000. On the single-node topolvm cluster this concurrency # corrupted goreleaser's view of the workspace — the first release that # added this task failed every time goreleaser ran beside buildah # (compile fork/exec EACCES, then `error reading .git`). Gating on all # three goreleaser-family tasks gives buildah the PVC to itself. - name: build-image runAfter: - goreleaser - cosign-sign - mcpb-pack taskRef: name: release-tools-build-image workspaces: - name: source workspace: source params: - name: IMAGE value: "forgejo-mcp:$(tasks.resolve-tag.results.tag)" - name: CONTEXT value: "." - name: CONTAINERFILE value: Containerfile # Inject the release version so the in-image binary reports the tag # instead of the Containerfile's ARG VERSION=dev default. - name: BUILD_ARGS value: "VERSION=$(tasks.resolve-tag.results.tag)" # bd forgejo-mcp-9r4: push the :vX.Y.Z tag directly (no :build-tmp # staging tag). It is signed in place by cosign-sign next; the finally # block deletes it if signing/attestation fail. - name: push-image-by-digest runAfter: - build-image taskRef: name: release-tools-push-image-by-digest workspaces: - name: source workspace: source - name: registry-credentials workspace: registry-credentials params: - name: IMAGE value: "forgejo-mcp:$(tasks.resolve-tag.results.tag)" - name: REGISTRY_IMAGE value: "codeberg.org/goern/forgejo-mcp" - name: TAG value: $(tasks.resolve-tag.results.tag) # Fail-closed: cosign-signing-key-images is optional: false in the task. # If absent, this fails and no human-readable tag is promoted. - name: cosign-sign-image runAfter: - push-image-by-digest taskRef: name: release-tools-cosign-sign-image-by-digest workspaces: - name: registry-credentials workspace: registry-credentials params: - name: IMAGE_REPO_DIGEST value: $(tasks.push-image-by-digest.results.IMAGE_REPO_DIGEST) - name: attach-image-sbom runAfter: - cosign-sign-image taskRef: name: release-tools-cosign-attach-sbom workspaces: - name: source workspace: source - name: registry-credentials workspace: registry-credentials params: - name: IMAGE_REPO_DIGEST value: $(tasks.push-image-by-digest.results.IMAGE_REPO_DIGEST) # Add :latest ONLY after sign + SBOM succeed (skipped for pre-releases). # The :vX.Y.Z tag was already pushed + signed upstream; this no longer # writes it or deletes any staging tag (bd forgejo-mcp-9r4). - name: promote-image-tag runAfter: - push-image-by-digest - cosign-sign-image - attach-image-sbom taskRef: name: release-tools-promote-image-tag workspaces: - name: registry-credentials workspace: registry-credentials params: - name: IMAGE_REPO_DIGEST value: $(tasks.push-image-by-digest.results.IMAGE_REPO_DIGEST) - name: TAG value: $(tasks.resolve-tag.results.tag) - name: REGISTRY_IMAGE value: "codeberg.org/goern/forgejo-mcp" # Fail-closed cleanup (bd forgejo-mcp-9r4): if attach-image-sbom did not # succeed — which also covers a skipped attach when cosign-sign failed # first — the :vX.Y.Z tag was pushed but never signed/attested. Delete it # so no unsigned image stays advertised. :latest is not yet promoted at # that point, so :vX.Y.Z is the manifest's only tag and the delete is safe. finally: - name: delete-unsigned-tag when: - input: "$(tasks.attach-image-sbom.status)" operator: notin values: ["Succeeded"] taskRef: name: release-tools-delete-tag-on-failure workspaces: - name: registry-credentials workspace: registry-credentials params: - name: REGISTRY_IMAGE value: "codeberg.org/goern/forgejo-mcp" - name: TAG value: $(tasks.resolve-tag.results.tag) workspaces: - name: source volumeClaimTemplate: spec: accessModes: - ReadWriteOnce resources: requests: # Release builds need ~6× CI: cross-compiled binaries + # SBOMs + checksums + .mcpb bundles per platform. dn0 adds a # container image build (buildah store + layers) and a syft # image SBOM scan into the same workspace — bumped 8Gi → 12Gi. storage: 12Gi - name: registry-credentials secret: # dn0 (decided 2026-06-01): push to codeberg.org/goern/forgejo-mcp. # Reuse the existing codeberg-pusher dockerconfigjson Secret in # op1st-pipelines. Its auth covers codeberg.org, but for push to the # goern namespace to succeed the underlying PAT MUST carry # `write:package` on goern/forgejo-mcp — a separate grant from the # operate-first package-write the release-tools image relies on. # Operator confirms out-of-band (cluster cannot validate token scope). secretName: codeberg-pusher