Skip to content

OCPEDGE-2815: tekton: add production and staging final pipelines#2884

Open
jeff-roche wants to merge 2 commits into
openshift:mainfrom
jeff-roche:konflux-final-pipelines
Open

OCPEDGE-2815: tekton: add production and staging final pipelines#2884
jeff-roche wants to merge 2 commits into
openshift:mainfrom
jeff-roche:konflux-final-pipelines

Conversation

@jeff-roche

@jeff-roche jeff-roche commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds two Konflux final pipelines to keep OLM catalog templates current across the release lifecycle, and a small supporting change to generate_catalog_template.sh:

  • operator-version-bump-production-final-pipeline — runs after a successful operator production release. Bumps OPERATOR_VERSION in release/container-build.args and opens a PR. Does not touch catalog templates directly; merging the PR triggers the normal build cascade (operator rebuild, then bundle rebuild) that eventually reaches the bundle's staging release.
  • catalog-candidate-update-staging-final-pipeline — runs after the lvm-operator-bundle component releases to staging. Regenerates both catalog templates via generate_catalog_template.sh and opens/updates a persistent PR if anything changed; skips cleanly if there's nothing new.
  • release/hack/generate_catalog_template.sh — candidate bundle tag listing and digest resolution now goes through the public quay.io/redhat-user-workloads/... tenant workload repo instead of registry.stage.redhat.io, avoiding the need for a staging registry pull secret in automation. Output image references still use the staging registry path.

Design notes

  • Both final pipelines authenticate as a GitHub App and create commits via the GitHub GraphQL createCommitOnBranch mutation rather than git push, so commits are signed/verified by GitHub without a separate signing key.
  • PRs opened by both pipelines are labeled approved/lgtm and titled with the NO-JIRA: prefix, matching this repo's existing mintmaker automation conventions.
  • The staging pipeline reuses a single persistent branch/PR (force-pushing updates) rather than opening a new PR on every staging release, to avoid PR pileup.
  • The staging pipeline mounts and merges the existing registry-redhat-io-docker and components-namespace-pull pull secrets to authenticate skopeo against both registry.redhat.io and the private Quay tenant repo in the same run.
  • An earlier design considered writing a placeholder tag into the candidate template for mintmaker to later resolve into a digest. That was dropped after confirming mintmaker's custom manager (and Konflux's native nudge system) only refresh digests that already exist — they don't resolve bare tags — so no pipeline ever writes a placeholder; the staging pipeline always writes a real, already-resolved digest.

Post-implementation steps (tracked in OCPEDGE-2815)

Provisioning the github-app-credentials secret and wiring both pipelines into their respective Konflux ReleasePlans is tracked separately, since that infrastructure lives outside this repo.

AI-Assisted Contribution

This PR was developed collaboratively with Claude Code. The two .tekton/ pipeline YAMLs were AI-generated based on human-directed design discussion (trigger model, auth approach, idempotency, PR-reuse strategy, signed-commit requirement). The generate_catalog_template.sh change was authored directly by the human contributor. All changes have been human-reviewed.

Testing

Both pipelines depend on live Konflux infrastructure (ReleasePlans, secrets) that hasn't been provisioned yet, so they haven't been run end-to-end. What was validated locally:

  • Both YAML files parse correctly and were checked against the existing .tekton/ conventions in this repo
  • Embedded bash scripts were linted with shellcheck (no real issues — only expected false positives from Tekton's $(results.x.path)/$(params.x) templating syntax, which shellcheck reads as literal command substitution)
  • Manual trace of script logic caught and fixed a real bug (trailing-newline loss from $(...) command substitution) and a missing Content-Type: application/json header on several REST calls

A manual test plan for each pipeline (post-secret-provisioning) is documented in a follow-up doc tracked internally.

Summary by CodeRabbit

  • New Features

    • Added automation to regenerate release catalog updates and open or update pull requests when changes are detected.
    • Added automation to update operator versions, including automatic patch increments and pull request creation.
    • Automated pull requests receive standard approval and review labels.
  • Bug Fixes

    • Improved candidate catalog generation by using the appropriate bundle image source for tag and digest resolution.

Use quay.io/redhat-user-workloads/logical-volume-manag-tenant/lvm-operator-bundle
for tag listing and candidate digest resolution instead of
registry.stage.redhat.io, avoiding the need for a staging registry pull
secret in automation. The staging registry path is still used when
writing the candidate template's output image references.

Assisted-by: Claude <noreply@anthropic.com>
Signed-off-by: Jeff Roche <jeroche@redhat.com>
Add two Konflux final pipelines to keep OLM catalog templates current
across the release lifecycle:

- operator-version-bump-production-final-pipeline: runs after a
  successful operator production release. Bumps OPERATOR_VERSION in
  release/container-build.args and opens a PR. Does not touch catalog
  templates; merging the PR triggers the normal build cascade (operator
  rebuild, then bundle rebuild) that eventually reaches the bundle's
  staging release.

- catalog-candidate-update-staging-final-pipeline: runs after the
  lvm-operator-bundle component releases to staging. Regenerates both
  catalog templates via generate_catalog_template.sh and opens or
  updates a persistent PR if anything changed; skips cleanly otherwise.

Both pipelines authenticate as a GitHub App and create commits via the
GitHub GraphQL createCommitOnBranch mutation so they are signed/verified
by GitHub without needing a separate signing key. PRs are labeled
approved/lgtm and titled with the NO-JIRA: prefix to match existing
automation conventions in this repo.

Assisted-by: Claude <noreply@anthropic.com>
Signed-off-by: Jeff Roche <jeroche@redhat.com>
@openshift-ci openshift-ci Bot added the size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. label Jul 17, 2026
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Walkthrough

Adds Tekton pipelines for catalog candidate updates and operator version bumps, including signed GitHub commits, pull request management, labels, and results. Catalog generation now uses Quay bundle image data for tags and candidate digests.

Changes

Catalog candidate update

Layer / File(s) Summary
Catalog template image sources
release/hack/generate_catalog_template.sh
Catalog tags and candidate digest lookups use the Quay bundle image, and candidate version calculation is updated.
Catalog update and pull request flow
.tekton/catalog-candidate-update-staging-final-pipeline.yaml
Adds catalog generation, change detection, signed GitHub commits, candidate branch management, pull request reuse or creation, labels, and task results.

Operator version bump

Layer / File(s) Summary
Version bump pipeline contract
.tekton/operator-version-bump-production-final-pipeline.yaml
Declares inputs, outputs, task parameters, and result wiring for operator version updates.
Version bump and pull request flow
.tekton/operator-version-bump-production-final-pipeline.yaml
Reads and increments OPERATOR_VERSION, creates a signed commit, manages the pull request, applies labels, and publishes results.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

Catalog candidate update

sequenceDiagram
  participant TektonTask
  participant ReleaseBranch
  participant GitHubREST
  participant GitHubGraphQL
  TektonTask->>ReleaseBranch: Clone and regenerate catalog templates
  TektonTask->>GitHubREST: Create or reset candidate branch
  TektonTask->>GitHubGraphQL: Create signed commit
  TektonTask->>GitHubREST: Reuse or create PR and apply labels
Loading

Operator version bump

sequenceDiagram
  participant TektonTask
  participant GitHubREST
  participant GitHubGraphQL
  TektonTask->>GitHubREST: Read release branch version
  TektonTask->>GitHubREST: Create or reset bump branch
  TektonTask->>GitHubGraphQL: Create signed version commit
  TektonTask->>GitHubREST: Reuse or create PR and apply labels
Loading

Possibly related issues

  • openshift/bpfman-catalog#119 — The catalog-update pipeline directly invokes catalog-template generation, matching the issue’s proposed Makefile/catalog regeneration workflow.
🚥 Pre-merge checks | ✅ 15
✅ Passed checks (15 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed Only two Tekton YAMLs changed; no Ginkgo test titles or dynamic title patterns appear in the touched files.
Test Structure And Quality ✅ Passed No Ginkgo test files or test code were changed in this PR; it only adds Tekton YAML and a shell script.
Microshift Test Compatibility ✅ Passed No new Ginkgo e2e tests were added; the commit only changes two Tekton YAMLs and one shell script, with no It/Describe/Context/When blocks.
Single Node Openshift (Sno) Test Compatibility ✅ Passed PASS: The PR only adds Tekton pipelines; no Ginkgo tests or multi-node/SNO-sensitive test code was added.
Topology-Aware Scheduling Compatibility ✅ Passed Only Tekton pipelines/scripts were added; no nodeSelector, affinity, tolerations, spread constraints, or replica logic appear.
Ote Binary Stdout Contract ✅ Passed Only Tekton YAML changed; no OTE binary process-level code or stdout writes were touched.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed No Ginkgo e2e tests were added; the commit only changes two Tekton pipeline YAMLs.
No-Weak-Crypto ✅ Passed Only SHA-256 signing for GitHub App JWTs appears; no MD5/SHA1/DES/RC4/3DES/Blowfish, custom crypto, or secret comparisons found.
Container-Privileges ✅ Passed Neither new Tekton pipeline contains privileged, hostPID/Network/IPC, allowPrivilegeEscalation, or securityContext settings.
No-Sensitive-Data-In-Logs ✅ Passed Reviewed all new scripts; no set -x or secret/PII printing found, and only public URLs/version info and PR metadata are echoed.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding production and staging final Tekton pipelines.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@jeff-roche jeff-roche changed the title tekton: add production and staging final pipelines OCPEDGE-2815: tekton: add production and staging final pipelines Jul 17, 2026
@openshift-ci-robot

openshift-ci-robot commented Jul 17, 2026

Copy link
Copy Markdown

@jeff-roche: This pull request references OCPEDGE-2815 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the spike to target the "5.0.0" version, but no target version was set.

Details

In response to this:

Summary

Adds two Konflux final pipelines to keep OLM catalog templates current across the release lifecycle, and a small supporting change to generate_catalog_template.sh:

  • operator-version-bump-production-final-pipeline — runs after a successful operator production release. Bumps OPERATOR_VERSION in release/container-build.args and opens a PR. Does not touch catalog templates directly; merging the PR triggers the normal build cascade (operator rebuild, then bundle rebuild) that eventually reaches the bundle's staging release.
  • catalog-candidate-update-staging-final-pipeline — runs after the lvm-operator-bundle component releases to staging. Regenerates both catalog templates via generate_catalog_template.sh and opens/updates a persistent PR if anything changed; skips cleanly if there's nothing new.
  • release/hack/generate_catalog_template.sh — candidate bundle tag listing and digest resolution now goes through the public quay.io/redhat-user-workloads/... tenant workload repo instead of registry.stage.redhat.io, avoiding the need for a staging registry pull secret in automation. Output image references still use the staging registry path.

Design notes

  • Both final pipelines authenticate as a GitHub App and create commits via the GitHub GraphQL createCommitOnBranch mutation rather than git push, so commits are signed/verified by GitHub without a separate signing key.
  • PRs opened by both pipelines are labeled approved/lgtm and titled with the NO-JIRA: prefix, matching this repo's existing mintmaker automation conventions.
  • The staging pipeline reuses a single persistent branch/PR (force-pushing updates) rather than opening a new PR on every staging release, to avoid PR pileup.
  • The staging pipeline mounts and merges the existing registry-redhat-io-docker and components-namespace-pull pull secrets to authenticate skopeo against both registry.redhat.io and the private Quay tenant repo in the same run.
  • An earlier design considered writing a placeholder tag into the candidate template for mintmaker to later resolve into a digest. That was dropped after confirming mintmaker's custom manager (and Konflux's native nudge system) only refresh digests that already exist — they don't resolve bare tags — so no pipeline ever writes a placeholder; the staging pipeline always writes a real, already-resolved digest.

Post-implementation steps (tracked in OCPEDGE-2815)

Provisioning the github-app-credentials secret and wiring both pipelines into their respective Konflux ReleasePlans is tracked separately, since that infrastructure lives outside this repo.

AI-Assisted Contribution

This PR was developed collaboratively with Claude Code. The two .tekton/ pipeline YAMLs were AI-generated based on human-directed design discussion (trigger model, auth approach, idempotency, PR-reuse strategy, signed-commit requirement). The generate_catalog_template.sh change was authored directly by the human contributor. All changes have been human-reviewed.

Testing

Both pipelines depend on live Konflux infrastructure (ReleasePlans, secrets) that hasn't been provisioned yet, so they haven't been run end-to-end. What was validated locally:

  • Both YAML files parse correctly and were checked against the existing .tekton/ conventions in this repo
  • Embedded bash scripts were linted with shellcheck (no real issues — only expected false positives from Tekton's $(results.x.path)/$(params.x) templating syntax, which shellcheck reads as literal command substitution)
  • Manual trace of script logic caught and fixed a real bug (trailing-newline loss from $(...) command substitution) and a missing Content-Type: application/json header on several REST calls

A manual test plan for each pipeline (post-secret-provisioning) is documented in a follow-up doc tracked internally.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Jul 17, 2026
@openshift-ci
openshift-ci Bot requested review from jaypoulz and jerpeter1 July 17, 2026 17:10
@openshift-ci

openshift-ci Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: jeff-roche

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jul 17, 2026
@jeff-roche

Copy link
Copy Markdown
Contributor Author

These pipelines depend on https://redhat.atlassian.net/browse/DPP-21582 which will install the app I created to this repo so that we can add some credentials to konflux for the version bump and catalog regen automations

@jeff-roche

Copy link
Copy Markdown
Contributor Author

/hold for DPP app install approval since this relies on that.

@openshift-ci openshift-ci Bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Jul 17, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.tekton/catalog-candidate-update-staging-final-pipeline.yaml:
- Around line 70-75: Remove the obsolete components-namespace-pull secret
dependency from the pipeline: delete its volume definition, corresponding volume
mount, and any authentication merge referencing it. Keep the
registry-redhat-io-docker configuration and public Quay repository usage
unchanged.
- Around line 182-202: Update the GitHub API workflow around GITHUB_TOKEN
creation and the PR/ref/label operations through the completion output to use
curl --fail-with-body, so HTTP failures stop the pipeline instead of being
treated as successful responses. Validate required response fields, including a
non-empty token and valid PR URL/identifier, before publishing CHANGED=true or
reporting success; preserve the existing successful flow when all API responses
are valid.
- Around line 216-226: Add a signed-off-by trailer to the generated commit by
extending the GraphQL `message` object in the `CreateCommitOnBranch` mutation
with the required body field, while preserving the existing headline and commit
inputs.
- Around line 89-99: Update the tool installation steps around the skopeo and yq
downloads to avoid mutable or unverified artifacts: use a pinned tool image or
fixed release URLs, pin yq to an explicit version instead of querying latest,
and verify each downloaded binary against its published SHA256 checksum before
chmod or use. Keep the existing TOOL_DIR installation flow intact.
- Around line 66-76: Add a shared emptyDir volume for /workspace/tools in the
pipeline pod, then mount it at that path in both the install-tools and
update-and-pr steps so installed binaries are available across containers.

In @.tekton/operator-version-bump-production-final-pipeline.yaml:
- Around line 150-161: Update the branch handling around PR_BRANCH so it first
checks for the matching automation-created PR and verifies the existing branch
belongs to that PR before resetting it. If the branch exists without a matching
PR, fail the pipeline instead of force-resetting it; retain branch creation only
when no branch exists.
- Around line 134-148: Validate NEXT_VERSION after resolving the requested or
auto-incremented value and before writing the result, modifying NEW_CONTENT, or
constructing PR_BRANCH. Require a well-formed semantic version and ensure it is
strictly greater than CURRENT_VERSION; reject malformed, equal, or lower values
with a clear configuration error and stop the task. Use the existing version
components or an appropriate comparison method, and ensure the validated value
is safe for the sed expression and branch name.
- Around line 212-223: Update the PR creation and labeling curl calls in the
pipeline to use fail-on-HTTP-error behavior, then validate that the parsed
PR_URL/html_url and PR_NUMBER/number are non-empty and not null before applying
labels or writing results.PR_URL. Ensure any invalid API response causes the
step to fail rather than continuing with a missing PR reference.
- Around line 165-182: Update the GraphQL payload in the commit-creation block
to extend message beyond headline with a body describing the operator version
bump and a Signed-off-by trailer, using the contributor identity expected by
CONTRIBUTING.md. Keep the existing headline, branch, expectedHeadOid, and
fileChanges fields unchanged.

In `@release/hack/generate_catalog_template.sh`:
- Around line 4-6: Make the tag discovery and digest lookup commands in the
catalog template generation script fail explicitly when either registry query
fails. Validate the result of skopeo list-tags and the later digest lookup
before generating or writing any template updates, and stop execution rather
than allowing empty tag data or an empty “@” reference to be emitted.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: fdea3ba7-23d6-47a3-9d57-1f3c40cc007e

📥 Commits

Reviewing files that changed from the base of the PR and between e46f568 and 9cd388a.

📒 Files selected for processing (3)
  • .tekton/catalog-candidate-update-staging-final-pipeline.yaml
  • .tekton/operator-version-bump-production-final-pipeline.yaml
  • release/hack/generate_catalog_template.sh

Comment on lines +66 to +76
volumes:
- name: github-app-credentials
secret:
secretName: github-app-credentials
- name: registry-redhat-io-docker
secret:
secretName: registry-redhat-io-docker
- name: components-namespace-pull
secret:
secretName: components-namespace-pull
steps:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=".tekton/catalog-candidate-update-staging-final-pipeline.yaml"

echo "=== line count ==="
wc -l "$file"

echo
echo "=== lines 1-260 with numbers ==="
cat -n "$file" | sed -n '1,260p'

Repository: openshift/lvm-operator

Length of output: 14430


Mount a shared emptyDir for /workspace/tools. install-tools writes the binaries into its own container filesystem, so update-and-pr can’t see them without a shared volume mounted in both steps.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.tekton/catalog-candidate-update-staging-final-pipeline.yaml around lines 66
- 76, Add a shared emptyDir volume for /workspace/tools in the pipeline pod,
then mount it at that path in both the install-tools and update-and-pr steps so
installed binaries are available across containers.

Comment on lines +70 to +75
- name: registry-redhat-io-docker
secret:
secretName: registry-redhat-io-docker
- name: components-namespace-pull
secret:
secretName: components-namespace-pull

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Remove the obsolete Quay pull-secret dependency.

The script now uses the public Quay repository, but this required secret volume still prevents the Task from starting when components-namespace-pull is absent. Remove its volume, mount, and auth merge.

Also applies to: 112-134

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.tekton/catalog-candidate-update-staging-final-pipeline.yaml around lines 70
- 75, Remove the obsolete components-namespace-pull secret dependency from the
pipeline: delete its volume definition, corresponding volume mount, and any
authentication merge referencing it. Keep the registry-redhat-io-docker
configuration and public Quay repository usage unchanged.

Comment on lines +89 to +99
echo "Installing skopeo..."
curl -sL "https://github.com/lework/skopeo-binary/releases/download/v1.17.0/skopeo-linux-amd64" \
-o "$TOOL_DIR/skopeo"
chmod +x "$TOOL_DIR/skopeo"

echo "Installing yq..."
YQ_VER=$(curl -s https://api.github.com/repos/mikefarah/yq/releases/latest \
| grep -oP '"tag_name": "\K[^"]+')
curl -sL "https://github.com/mikefarah/yq/releases/download/${YQ_VER}/yq_linux_amd64" \
-o "$TOOL_DIR/yq"
chmod +x "$TOOL_DIR/yq"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "File: .tekton/catalog-candidate-update-staging-final-pipeline.yaml"
wc -l .tekton/catalog-candidate-update-staging-final-pipeline.yaml
sed -n '1,180p' .tekton/catalog-candidate-update-staging-final-pipeline.yaml

echo
echo "Search for skopeo/yq/tool install patterns"
rg -n "skopeo|yq|latest|checksum|sha256|TOOL_DIR|curl -sL" .tekton -S

Repository: openshift/lvm-operator

Length of output: 23109


Pin the downloaded tools and verify checksums. skopeo is fetched from a third-party release URL without integrity checks, and yq is still resolved from the mutable latest tag. Use a pinned tool image or fixed release URLs with SHA256 checksums.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.tekton/catalog-candidate-update-staging-final-pipeline.yaml around lines 89
- 99, Update the tool installation steps around the skopeo and yq downloads to
avoid mutable or unverified artifacts: use a pinned tool image or fixed release
URLs, pin yq to an explicit version instead of querying latest, and verify each
downloaded binary against its published SHA256 checksum before chmod or use.
Keep the existing TOOL_DIR installation flow intact.

Comment on lines +182 to +202
GITHUB_TOKEN=$(curl -sS -X POST \
-H "Authorization: Bearer ${JWT}" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/app/installations/${INSTALLATION_ID}/access_tokens" \
| jq -r '.token')

AUTH_HEADER="Authorization: Bearer ${GITHUB_TOKEN}"

# ── Create or reset the persistent candidate-update branch to release-branch HEAD ──
PR_BRANCH="catalog-candidate-update-${Y_STREAM}"
REF_STATUS=$(curl -sS -o /dev/null -w '%{http_code}' -H "$AUTH_HEADER" \
"${API}/git/ref/heads/${PR_BRANCH}")
if [ "$REF_STATUS" = "200" ]; then
curl -sS -X PATCH -H "$AUTH_HEADER" -H "Content-Type: application/json" \
"${API}/git/refs/heads/${PR_BRANCH}" \
-d "{\"sha\":\"${BASE_SHA}\",\"force\":true}" > /dev/null
else
curl -sS -X POST -H "$AUTH_HEADER" -H "Content-Type: application/json" \
"${API}/git/refs" \
-d "{\"ref\":\"refs/heads/${PR_BRANCH}\",\"sha\":\"${BASE_SHA}\"}" > /dev/null
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Do not report success after failed GitHub API requests.

curl -sS accepts HTTP error responses; token/ref/PR/label failures can therefore reach Lines 270-271 and publish CHANGED=true with a null or invalid PR URL. Use --fail-with-body and validate required response fields before continuing.

Also applies to: 231-268

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.tekton/catalog-candidate-update-staging-final-pipeline.yaml around lines
182 - 202, Update the GitHub API workflow around GITHUB_TOKEN creation and the
PR/ref/label operations through the completion output to use curl
--fail-with-body, so HTTP failures stop the pipeline instead of being treated as
successful responses. Validate required response fields, including a non-empty
token and valid PR URL/identifier, before publishing CHANGED=true or reporting
success; preserve the existing successful flow when all API responses are valid.

Comment on lines +216 to +226
--arg headline "chore: update candidate catalog template for ${Y_STREAM}" \
--arg oid "$BASE_SHA" \
--argjson additions "$ADDITIONS" \
'{
query: "mutation($input: CreateCommitOnBranchInput!) { createCommitOnBranch(input: $input) { commit { url oid } } }",
variables: {
input: {
branch: { repositoryNameWithOwner: $repo, branchName: $branch },
message: { headline: $headline },
expectedHeadOid: $oid,
fileChanges: { additions: $additions }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Include the required commit sign-off.

The generated commit only supplies a headline. Add a signed-off-by trailer through the GraphQL message body so automated commits satisfy repository policy.

As per coding guidelines, “sign off commits with -s.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.tekton/catalog-candidate-update-staging-final-pipeline.yaml around lines
216 - 226, Add a signed-off-by trailer to the generated commit by extending the
GraphQL `message` object in the `CreateCommitOnBranch` mutation with the
required body field, while preserving the existing headline and commit inputs.

Source: Coding guidelines

Comment on lines +134 to +148
# ── Determine next version ──
NEXT_VERSION="$NEXT_VERSION_PARAM"
if [ -z "$NEXT_VERSION" ]; then
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"
NEXT_VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))"
echo "Auto-incremented to: $NEXT_VERSION"
fi
echo "Next version: $NEXT_VERSION"
echo -n "$NEXT_VERSION" > $(results.NEXT_VERSION.path)

NEW_CONTENT=$(echo "$FILE_CONTENT" | sed "s/^OPERATOR_VERSION=.*/OPERATOR_VERSION=${NEXT_VERSION}/")
# $(...) strips trailing newlines, so re-add the one the original file had
NEW_CONTENT_B64=$(printf '%s\n' "$NEW_CONTENT" | base64 -w0)

PR_BRANCH="bump-operator-version-${NEXT_VERSION}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Validate that the requested version is a strict upgrade.

An arbitrary next-version is inserted into the file, branch name, and sed expression. Reject malformed, equal, or lower versions before modifying content; otherwise a configuration error can corrupt or downgrade OPERATOR_VERSION.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.tekton/operator-version-bump-production-final-pipeline.yaml around lines
134 - 148, Validate NEXT_VERSION after resolving the requested or
auto-incremented value and before writing the result, modifying NEW_CONTENT, or
constructing PR_BRANCH. Require a well-formed semantic version and ensure it is
strictly greater than CURRENT_VERSION; reject malformed, equal, or lower values
with a clear configuration error and stop the task. Use the existing version
components or an appropriate comparison method, and ensure the validated value
is safe for the sed expression and branch name.

Comment on lines +150 to +161
# ── Create or reset the PR branch to point at the release branch's HEAD ──
REF_STATUS=$(curl -sS -o /dev/null -w '%{http_code}' -H "$AUTH_HEADER" \
"${API}/git/ref/heads/${PR_BRANCH}")
if [ "$REF_STATUS" = "200" ]; then
curl -sS -X PATCH -H "$AUTH_HEADER" -H "Content-Type: application/json" \
"${API}/git/refs/heads/${PR_BRANCH}" \
-d "{\"sha\":\"${BRANCH_SHA}\",\"force\":true}" > /dev/null
else
curl -sS -X POST -H "$AUTH_HEADER" -H "Content-Type: application/json" \
"${API}/git/refs" \
-d "{\"ref\":\"refs/heads/${PR_BRANCH}\",\"sha\":\"${BRANCH_SHA}\"}" > /dev/null
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Do not force-reset an arbitrary pre-existing branch.

Any branch named bump-operator-version-${NEXT_VERSION} is overwritten before confirming it belongs to this automation. Check for the matching PR first, and fail rather than force-resetting an unrelated branch.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.tekton/operator-version-bump-production-final-pipeline.yaml around lines
150 - 161, Update the branch handling around PR_BRANCH so it first checks for
the matching automation-created PR and verifies the existing branch belongs to
that PR before resetting it. If the branch exists without a matching PR, fail
the pipeline instead of force-resetting it; retain branch creation only when no
branch exists.

Comment on lines +165 to +182
GRAPHQL_PAYLOAD=$(jq -n \
--arg repo "${OWNER}/${REPO}" \
--arg branch "$PR_BRANCH" \
--arg headline "chore: bump OPERATOR_VERSION to ${NEXT_VERSION}" \
--arg oid "$BRANCH_SHA" \
--arg path "$FILE_PATH" \
--arg contents "$NEW_CONTENT_B64" \
'{
query: "mutation($input: CreateCommitOnBranchInput!) { createCommitOnBranch(input: $input) { commit { url oid } } }",
variables: {
input: {
branch: { repositoryNameWithOwner: $repo, branchName: $branch },
message: { headline: $headline },
expectedHeadOid: $oid,
fileChanges: { additions: [ { path: $path, contents: $contents } ] }
}
}
}')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## File excerpt\n'
sed -n '145,205p' .tekton/operator-version-bump-production-final-pipeline.yaml

printf '\n## Search for commit conventions\n'
rg -n --hidden --glob '!**/.git/**' -e 'Signed-off-by|sign off|sign-off|explain why|commit.*body|createCommitOnBranch|headline' .

printf '\n## Find other GraphQL commit creations\n'
rg -n --hidden --glob '!**/.git/**' -e 'createCommitOnBranch|CreateCommitOnBranchInput|Signed-off-by' .

Repository: openshift/lvm-operator

Length of output: 7480


🌐 Web query:

GitHub GraphQL createCommitOnBranch input message body sign-off trailer signed-off-by documentation

💡 Result:

The createCommitOnBranch GraphQL mutation in the GitHub API allows you to create a commit by providing a message object as part of the CreateCommitOnBranchInput [1]. The message object includes the following fields: - headline (String!): The summary line of the commit message. - body (String): The optional extended description of the commit message. There is no dedicated field in the createCommitOnBranch input schema specifically for Git trailers like Signed-off-by [1]. However, Git trailers can be included by appending them to the body field of the message object [1]. When constructing the body string, you should format the trailer according to standard Git conventions (e.g., Signed-off-by: Name <email@example.com>). Because the GitHub GraphQL API does not natively process these as distinct structural fields, they are treated as plain text within the commit message body [1]. GitHub will render them in the commit view, and they will persist as part of the commit's metadata [2]. Key technical notes: - Commits created via createCommitOnBranch are automatically signed by GitHub's web-flow key and will appear as "Verified" in the GitHub UI [3][4][5]. - If you include a Co-authored-by or Signed-off-by trailer in the body, GitHub may mark the commit as "Partially verified" if the signature attests to the API call but not the claims made within the custom trailers [2]. - The API does not allow you to manually specify the author or committer; these are set to the authenticated user or application by default [3][5]. Attribution for other individuals should be managed via trailers like Co-authored-by [2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## Repo commit policy docs\n'
rg -n --hidden --glob '!**/.git/**' -e 'Signed-off-by|sign-off|commit message|explain why in the body|body explaining why|automation identity|createCommitOnBranch' README* .tekton docs .github . 2>/dev/null | head -n 200

Repository: openshift/lvm-operator

Length of output: 3372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

for f in README.md CONTRIBUTING.md CODEOWNERS .github/pull_request_template.md .github/commit_template.md .tekton/operator-version-bump-production-final-pipeline.yaml; do
  if [ -f "$f" ]; then
    echo "### $f"
    sed -n '1,220p' "$f" | rg -n --no-heading 'Signed-off-by|sign-off|commit message|headline|body|createCommitOnBranch|automation identity|explain why'
  fi
done

Repository: openshift/lvm-operator

Length of output: 174


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## CONTRIBUTING commit guidance\n'
sed -n '225,266p' CONTRIBUTING.md

printf '\n## Pipeline comment around the commit step\n'
sed -n '158,190p' .tekton/operator-version-bump-production-final-pipeline.yaml

Repository: openshift/lvm-operator

Length of output: 3195


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '225,266p' CONTRIBUTING.md

Repository: openshift/lvm-operator

Length of output: 1344


Add a commit body and sign-off trailer
message only sets the headline here; add a body explaining why the bump is needed and a Signed-off-by: footer to match CONTRIBUTING.md.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.tekton/operator-version-bump-production-final-pipeline.yaml around lines
165 - 182, Update the GraphQL payload in the commit-creation block to extend
message beyond headline with a body describing the operator version bump and a
Signed-off-by trailer, using the contributor identity expected by
CONTRIBUTING.md. Keep the existing headline, branch, expectedHeadOid, and
fileChanges fields unchanged.

Source: Coding guidelines

Comment on lines +212 to +223
PR_RESPONSE=$(curl -sS -X POST -H "$AUTH_HEADER" -H "Content-Type: application/json" "${API}/pulls" -d "$PR_JSON")
PR_URL=$(echo "$PR_RESPONSE" | jq -r '.html_url')
PR_NUMBER=$(echo "$PR_RESPONSE" | jq -r '.number')
echo "Created PR: $PR_URL"
fi

# ── Apply labels (idempotent - safe to re-apply on a reused PR) ──
curl -sS -X POST -H "$AUTH_HEADER" -H "Content-Type: application/json" \
"${API}/issues/${PR_NUMBER}/labels" \
-d '{"labels":["approved","lgtm"]}' > /dev/null

echo -n "$PR_URL" > $(results.PR_URL.path)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=".tekton/operator-version-bump-production-final-pipeline.yaml"

# Show the surrounding context for the cited lines
nl -ba "$file" | sed -n '180,240p'

Repository: openshift/lvm-operator

Length of output: 200


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=".tekton/operator-version-bump-production-final-pipeline.yaml"

awk 'NR>=180 && NR<=240 { printf "%6d  %s\n", NR, $0 }' "$file"

Repository: openshift/lvm-operator

Length of output: 3362


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=".tekton/operator-version-bump-production-final-pipeline.yaml"

awk 'NR>=140 && NR<=180 { printf "%6d  %s\n", NR, $0 }' "$file"

Repository: openshift/lvm-operator

Length of output: 2788


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=".tekton/operator-version-bump-production-final-pipeline.yaml"

awk 'NR>=1 && NR<=120 { printf "%6d  %s\n", NR, $0 }' "$file"

Repository: openshift/lvm-operator

Length of output: 5865


Fail on GitHub API errors in PR creation/labeling. The curl -sS calls here ignore HTTP failures, so the step can still succeed with PR_URL=null and no labels. Use -f and reject empty html_url/number before writing the result.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.tekton/operator-version-bump-production-final-pipeline.yaml around lines
212 - 223, Update the PR creation and labeling curl calls in the pipeline to use
fail-on-HTTP-error behavior, then validate that the parsed PR_URL/html_url and
PR_NUMBER/number are non-empty and not null before applying labels or writing
results.PR_URL. Ensure any invalid API response causes the step to fail rather
than continuing with a missing PR reference.

Comment on lines +4 to +6
quay_bundle_path="quay.io/redhat-user-workloads/logical-volume-manag-tenant/lvm-operator-bundle"
staging_bundle_path="registry.stage.redhat.io/lvms4/lvms-operator-bundle"
lvms_all_tags="$(skopeo list-tags docker://${staging_bundle_path})"
lvms_all_tags="$(skopeo list-tags docker://${quay_bundle_path})"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Fail instead of generating incomplete templates when Quay lookup fails.

This script has no errexit; failed tag discovery can silently produce no updates, while a failed digest lookup can write an empty @ reference. Validate both commands before continuing.

Proposed fix
-lvms_all_tags="$(skopeo list-tags docker://${quay_bundle_path})"
+if ! lvms_all_tags="$(skopeo list-tags "docker://${quay_bundle_path}")"; then
+    echo "Failed to list candidate bundle tags" >&2
+    exit 1
+fi
+[[ -n "${lvms_all_tags}" ]] || exit 1
...
-                digests["${ver}"]=$(skopeo inspect "docker://${quay_bundle_path}:${ver}" --format "{{.Digest}}")
+                if ! digest=$(skopeo inspect "docker://${quay_bundle_path}:${ver}" --format "{{.Digest}}") ||
+                    [[ -z "${digest}" ]]; then
+                    echo "Failed to resolve candidate digest for ${ver}" >&2
+                    exit 1
+                fi
+                digests["${ver}"]="${digest}"

Also applies to: 81-82

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@release/hack/generate_catalog_template.sh` around lines 4 - 6, Make the tag
discovery and digest lookup commands in the catalog template generation script
fail explicitly when either registry query fails. Validate the result of skopeo
list-tags and the later digest lookup before generating or writing any template
updates, and stop execution rather than allowing empty tag data or an empty “@”
reference to be emitted.

@openshift-ci

openshift-ci Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

@jeff-roche: all tests passed!

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants