On Forgejo/Codeberg the container registry deletes the underlying
MANIFEST when any of its tags is deleted. promote-image-tag's
`skopeo delete :build-tmp` cleanup therefore wiped the :vX.Y.Z and
:latest tags that shared the same digest — only the separate cosign
.sig/.att manifests survived. This silently broke the v2.27.0 image
publish (first release to run the delete step added in f026f8c).
Fix (shared by the forgejo-mcp and release-tools publish pipelines):
- push-image-by-digest pushes :vX.Y.Z directly (new TAG param); no
:build-tmp staging tag is ever created.
- promote-image-tag only ADDS :latest (stable releases); it no longer
copies :vX.Y.Z or deletes any tag.
- new delete-tag-on-failure task runs in `finally`, gated on
attach-sbom not succeeding, to remove the briefly-unsigned :vX.Y.Z
so the fail-closed "never advertise unsigned" guarantee is kept.
Both PipelineRuns validated with `oc create --dry-run=server`.
Refs: forgejo-mcp-9r4
103 lines
4.4 KiB
YAML
103 lines
4.4 KiB
YAML
apiVersion: tekton.dev/v1
|
|
kind: Task
|
|
metadata:
|
|
name: release-tools-push-image-by-digest
|
|
annotations:
|
|
tekton.dev/displayName: "push release-tools image to registry by digest (no tag)"
|
|
spec:
|
|
description: |
|
|
Push the built image to the registry under its version tag (vX.Y.Z) and
|
|
record the resulting manifest digest for downstream sign/attest.
|
|
|
|
# bd forgejo-mcp-9r4: the earlier design pushed an ephemeral :build-tmp tag
|
|
# and deleted it after promotion. On Forgejo/Codeberg, deleting ANY tag
|
|
# deletes the underlying manifest — so removing :build-tmp also removed the
|
|
# :vX.Y.Z and :latest tags that shared the same digest (only the separate
|
|
# cosign .sig/.att manifests survived). We now push :vX.Y.Z directly with
|
|
# no staging tag. cosign-sign + attach-sbom operate on the digest in place;
|
|
# promote-image-tag adds :latest only after both succeed. The tag is
|
|
# briefly unsigned (~1 min) until cosign-sign runs; a signing failure
|
|
# deletes the unsigned :vX.Y.Z via the pipeline's finally cleanup.
|
|
params:
|
|
- name: IMAGE
|
|
description: |
|
|
Local image tag to push (e.g. release-tools:build-tmp). Must already
|
|
exist in the buildah store in the workspace.
|
|
# Path A (decided 2026-05-25): codeberg.org/operate-first/release-tools
|
|
# Registry path working assumption: codeberg.org/operate-first/release-tools
|
|
type: string
|
|
- name: REGISTRY_IMAGE
|
|
description: |
|
|
Full registry path without tag (e.g. codeberg.org/operate-first/release-tools).
|
|
# Path A (decided 2026-05-25): codeberg.org/operate-first/release-tools
|
|
# Working assumption: codeberg.org/operate-first/release-tools
|
|
type: string
|
|
default: "codeberg.org/operate-first/release-tools"
|
|
- name: TAG
|
|
description: Human-readable version tag to push (e.g. v1.0.0).
|
|
type: string
|
|
- name: STORAGE_DRIVER
|
|
description: buildah storage driver.
|
|
type: string
|
|
default: vfs
|
|
workspaces:
|
|
- name: source
|
|
mountPath: /workspace/source
|
|
- name: registry-credentials
|
|
mountPath: /workspace/registry-creds
|
|
optional: false
|
|
results:
|
|
- name: IMAGE_DIGEST
|
|
description: sha256 digest of the pushed manifest (e.g. sha256:abc123...).
|
|
- name: IMAGE_URL
|
|
description: Registry URL without tag or digest (e.g. codeberg.org/operate-first/release-tools). Used by Tekton Chains to bind image to SLSA provenance.
|
|
- name: IMAGE_REPO_DIGEST
|
|
description: Full digest reference (e.g. codeberg.org/operate-first/release-tools@sha256:abc123).
|
|
steps:
|
|
- name: push-by-digest
|
|
image: quay.io/buildah/stable:latest
|
|
securityContext:
|
|
privileged: true
|
|
workingDir: $(workspaces.source.path)
|
|
script: |
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Configure registry auth
|
|
REGISTRY_AUTH_FILE="$(workspaces.registry-credentials.path)/.dockerconfigjson"
|
|
if [ ! -f "${REGISTRY_AUTH_FILE}" ]; then
|
|
REGISTRY_AUTH_FILE="$(workspaces.registry-credentials.path)/config.json"
|
|
fi
|
|
|
|
# Use same workspace store as build-image task.
|
|
BUILDAH_STORE="$(workspaces.source.path)/.buildah"
|
|
LOCAL_IMAGE="localhost/$(params.IMAGE)"
|
|
REGISTRY_IMAGE="$(params.REGISTRY_IMAGE)"
|
|
|
|
TAG="$(params.TAG)"
|
|
DEST="${REGISTRY_IMAGE}:${TAG}"
|
|
|
|
echo "Pushing ${LOCAL_IMAGE} to docker://${DEST} ..."
|
|
|
|
# Push directly to the version tag — NO ephemeral :build-tmp staging
|
|
# tag (bd forgejo-mcp-9r4: deleting a staging tag on Forgejo/Codeberg
|
|
# deletes the shared manifest and takes the real tags with it). The
|
|
# tag is briefly unsigned until cosign-sign runs next; a signing
|
|
# failure removes it via the pipeline's finally cleanup. --digestfile
|
|
# still records the manifest digest so sign/attest can pin by digest.
|
|
buildah --root "${BUILDAH_STORE}" push \
|
|
--storage-driver="$(params.STORAGE_DRIVER)" \
|
|
--authfile="${REGISTRY_AUTH_FILE}" \
|
|
--digestfile=/tmp/pushed-digest \
|
|
"${LOCAL_IMAGE}" \
|
|
"docker://${DEST}" \
|
|
2>&1 | tee /dev/stderr
|
|
|
|
DIGEST=$(cat /tmp/pushed-digest)
|
|
REPO_DIGEST="${REGISTRY_IMAGE}@${DIGEST}"
|
|
|
|
printf '%s' "${DIGEST}" > "$(results.IMAGE_DIGEST.path)"
|
|
printf '%s' "${REGISTRY_IMAGE}" > "$(results.IMAGE_URL.path)"
|
|
printf '%s' "${REPO_DIGEST}" > "$(results.IMAGE_REPO_DIGEST.path)"
|
|
|
|
echo "Pushed by digest: ${REPO_DIGEST}"
|