Skip to content

feat: add workflow for generating a production FBC from the main stag…#221

Open
knrc wants to merge 1 commit intomainfrom
knrc_fbc_to_production_branch
Open

feat: add workflow for generating a production FBC from the main stag…#221
knrc wants to merge 1 commit intomainfrom
knrc_fbc_to_production_branch

Conversation

@knrc
Copy link
Contributor

@knrc knrc commented Feb 5, 2026

User description

…ing branch

Now that the main branch could include multiple, in progress, streams we need to have a clean production version. The proposal is to create a production branch through a manual workflow invocation (at least for now), generate the PR and follow the push pipelines to create the appropriate components.

@JasonPowr @osmman please take a look and let me know what you think.


PR Type

Enhancement


Description

  • Add automated workflow to generate production FBC from main branch

  • Filter bundles by registry availability using skopeo validation

  • Create production PRs with derived versions and OCP compatibility

  • Add Tekton pipeline configurations for multi-version production builds


Diagram Walkthrough

flowchart LR
  main["Main Branch<br/>Multiple Streams"]
  workflow["Create Production<br/>Workflow"]
  filter["Filter Bundles<br/>by Registry"]
  derive["Derive Version &<br/>Channels"]
  copy["Copy to<br/>Production Branch"]
  pr["Create PR"]
  tekton["Tekton Pipelines<br/>Multi-OCP Versions"]
  
  main -->|workflow_dispatch| workflow
  workflow -->|filter_for_production.sh| filter
  workflow -->|derive step| derive
  filter -->|configure| copy
  derive -->|bundle info| copy
  copy -->|git push| pr
  pr -->|triggers| tekton
Loading

File Walkthrough

Relevant files
Enhancement
2 files
filter_for_production.sh
Filter bundles by registry availability                                   
+76/-0   
create-production-pr.yaml
Automated production PR creation workflow                               
+186/-0 
Configuration changes
7 files
fbc-v4-14-production-push.yaml
Tekton pipeline for v4.14 production builds                           
+46/-0   
fbc-v4-15-production-push.yaml
Tekton pipeline for v4.15 production builds                           
+46/-0   
fbc-v4-16-production-push.yaml
Tekton pipeline for v4.16 production builds                           
+48/-0   
fbc-v4-17-production-push.yaml
Tekton pipeline for v4.17 production builds                           
+48/-0   
fbc-v4-18-production-push.yaml
Tekton pipeline for v4.18 production builds                           
+49/-0   
fbc-v4-19-production-push.yaml
Tekton pipeline for v4.19 production builds                           
+52/-0   
fbc-v4-20-production-push.yaml
Tekton pipeline for v4.20 production builds                           
+52/-0   

@knrc knrc requested review from JasonPowr and osmman February 5, 2026 21:49
@knrc knrc self-assigned this Feb 5, 2026
@qodo-code-review
Copy link

qodo-code-review bot commented Feb 5, 2026

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Command injection

Description: The script executes ${SKOPEO_CMD} and expands ${AUTH_FILE} unquoted in a command context
(if ${SKOPEO_CMD} inspect ... ${AUTH_FILE} ...), so if an attacker can influence these
environment variables they could inject additional shell tokens/commands (e.g.,
SKOPEO_CMD="skopeo; curl ... | bash"), leading to arbitrary command execution during the
workflow run.
filter_for_production.sh [22-44]

Referred Code
SKOPEO_CMD=${SKOPEO_CMD:-skopeo}
AUTH_FILE=${AUTH_FILE:-}
CACHE_FILE="${SKOPEO_CACHE_FILE:-${TMPDIR:-/tmp}/skopeo-cache.$$}"

check_image_exists() {
  local image="$1"
  local cache_key="${image##*@}"

  # Check cache first
  if grep -q "^${cache_key}=exists$" "$CACHE_FILE" 2>/dev/null; then
    return 0
  elif grep -q "^${cache_key}=notfound$" "$CACHE_FILE" 2>/dev/null; then
    return 1
  fi

  # Not cached, check registry
  if ${SKOPEO_CMD} inspect --no-tags ${AUTH_FILE} "docker://${image}" &>/dev/null; then
    echo "${cache_key}=exists" >> "$CACHE_FILE"
    return 0
  else
    echo "${cache_key}=notfound" >> "$CACHE_FILE"


 ... (clipped 2 lines)
Unpinned pipeline reference

Description: The PipelineRun fetches and executes a remote Tekton pipeline from
https://github.com/securesign/pipelines.git pinned only to the floating main branch, which
is a supply-chain risk because any change to that branch can alter CI behavior and
potentially execute malicious steps in the build environment.
fbc-v4-14-production-push.yaml [31-40]

Referred Code
pipelineRef:
  params:
  - name: url
    value: https://github.com/securesign/pipelines.git
  - name: revision
    value: main
  - name: pathInRepo
    value: pipelines/fbc-builder.yaml
  resolver: git
taskRunTemplate:
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Unsafe iteration/parsing: The script uses for IMAGE in $(yq ...) word-splitting and unescaped interpolation into yq
expressions, which can break on unexpected values and lacks explicit handling for
parse/lookup edge cases.

Referred Code
for IMAGE in $(yq e '.entries[] | select(.schema == "olm.bundle") | .image' "$GRAPH"); do
  [[ -z "$IMAGE" ]] && continue

  BUNDLE=$(yq e ".entries[] | select(.schema == \"olm.bundle\" and .image == \"$IMAGE\") | .name" "$GRAPH")
  echo -n "Checking $BUNDLE... "

  if check_image_exists "$IMAGE"; then
    echo "EXISTS"
  else
    echo "NOT FOUND - removing"
    # Remove bundle entry
    yq -i "del(.entries[] | select(.schema == \"olm.bundle\" and .image == \"$IMAGE\"))" "$GRAPH"
    # Remove from channel entries
    yq -i "(.entries[] | select(.schema == \"olm.channel\").entries) |= map(select(.name != \"$BUNDLE\"))" "$GRAPH"
    # Remove from skips arrays
    yq -i "(.entries[] | select(.schema == \"olm.channel\").entries[].skips) |= map(select(. != \"$BUNDLE\"))" "$GRAPH"
    # Remove from replaces references
    yq -i "del(.entries[] | select(.schema == \"olm.channel\").entries[] | select(.replaces == \"$BUNDLE\").replaces)" "$GRAPH"
  fi

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Missing explicit audit: The workflow performs critical actions (pushing a branch and creating a PR) without
explicitly emitting structured audit events containing actor, action, and outcome beyond
default GitHub Actions logging.

Referred Code
- name: Create PR
  working-directory: fbc-production
  run: |
    git config user.name "RHTAS-build-bot"
    git config user.email "RHTAS-build-bot@users.noreply.github.com"

    BRANCH="release-${{ env.OPERATOR_NAME }}-v${{ steps.derive.outputs.bundle_version }}"
    git checkout -b "$BRANCH"
    git add .

    # Check if there are changes to commit
    if git diff --cached --quiet; then
      echo "No changes to commit"
      exit 0
    fi

    git commit -m ":package: Release ${{ env.OPERATOR_NAME }} v${{ steps.derive.outputs.bundle_version }}"
    git push -u origin "$BRANCH"

    gh pr create \
      --base production \


 ... (clipped 12 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Unvalidated graph inputs: Values read from graph.yaml (e.g., .image and .name) are interpolated directly into
command arguments and yq queries without sanitization/escaping, which may enable malformed
input to cause unexpected behavior.

Referred Code
for IMAGE in $(yq e '.entries[] | select(.schema == "olm.bundle") | .image' "$GRAPH"); do
  [[ -z "$IMAGE" ]] && continue

  BUNDLE=$(yq e ".entries[] | select(.schema == \"olm.bundle\" and .image == \"$IMAGE\") | .name" "$GRAPH")
  echo -n "Checking $BUNDLE... "

  if check_image_exists "$IMAGE"; then
    echo "EXISTS"
  else
    echo "NOT FOUND - removing"
    # Remove bundle entry
    yq -i "del(.entries[] | select(.schema == \"olm.bundle\" and .image == \"$IMAGE\"))" "$GRAPH"
    # Remove from channel entries
    yq -i "(.entries[] | select(.schema == \"olm.channel\").entries) |= map(select(.name != \"$BUNDLE\"))" "$GRAPH"
    # Remove from skips arrays
    yq -i "(.entries[] | select(.schema == \"olm.channel\").entries[].skips) |= map(select(. != \"$BUNDLE\"))" "$GRAPH"
    # Remove from replaces references
    yq -i "del(.entries[] | select(.schema == \"olm.channel\").entries[] | select(.replaces == \"$BUNDLE\").replaces)" "$GRAPH"
  fi

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link

qodo-code-review bot commented Feb 5, 2026

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Consolidate duplicated Tekton pipeline definitions

The PR adds many duplicated Tekton PipelineRun files for different OCP versions,
creating a maintenance issue. It is suggested to use a templating tool like
Kustomize or a script to generate these files from a single base template.

Examples:

.tekton/fbc-v4-16-production-push.yaml [1-48]
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
  annotations:
    build.appstudio.openshift.io/repo: https://github.com/securesign/fbc?rev={{revision}}
    build.appstudio.redhat.com/commit_sha: '{{revision}}'
    build.appstudio.redhat.com/target_branch: '{{target_branch}}'
    pipelinesascode.tekton.dev/max-keep-runs: "3"
    pipelinesascode.tekton.dev/on-cel-expression: event == "push" && target_branch
      == "production" && ( "v4.16/rhtas-operator/***".pathChanged() || ".tekton/fbc-v4-16-production-push.yaml".pathChanged()

 ... (clipped 38 lines)
.tekton/fbc-v4-17-production-push.yaml [1-48]
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
  annotations:
    build.appstudio.openshift.io/repo: https://github.com/securesign/fbc?rev={{revision}}
    build.appstudio.redhat.com/commit_sha: '{{revision}}'
    build.appstudio.redhat.com/target_branch: '{{target_branch}}'
    pipelinesascode.tekton.dev/max-keep-runs: "3"
    pipelinesascode.tekton.dev/on-cel-expression: event == "push" && target_branch
      == "production" && ( "v4.17/rhtas-operator/***".pathChanged() || ".tekton/fbc-v4-17-production-push.yaml".pathChanged()

 ... (clipped 38 lines)

Solution Walkthrough:

Before:

# .tekton/fbc-v4-16-production-push.yaml
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
  name: fbc-v4-16-production-on-push
  annotations:
    ... on-cel-expression: ... "v4.16/rhtas-operator/***".pathChanged() ...
spec:
  params:
  - name: output-image
    value: quay.io/securesign/fbc-v4-16-production:{{revision}}
  - name: path-context
    value: v4.16/rhtas-operator
  ...

# .tekton/fbc-v4-17-production-push.yaml
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
  name: fbc-v4-17-production-on-push
  annotations:
    ... on-cel-expression: ... "v4.17/rhtas-operator/***".pathChanged() ...
spec:
  params:
  - name: output-image
    value: quay.io/securesign/fbc-v4-17-production:{{revision}}
  - name: path-context
    value: v4.17/rhtas-operator
  ...
# ... and 5 more similar files

After:

# .tekton/base/pipelinerun.yaml (Template)
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
  name: fbc-VERSION-production-on-push
  annotations:
    ... on-cel-expression: ... "VERSION/rhtas-operator/***".pathChanged() ...
spec:
  params:
  - name: output-image
    value: quay.io/securesign/fbc-VERSION-production:{{revision}}
  - name: path-context
    value: VERSION/rhtas-operator
  ...

# .tekton/overlays/v4.16/kustomization.yaml
resources:
- ../../base
namePrefix: fbc-v4.16-
patches:
- path: patch.yaml

# A script or CI step would then run:
# for version in v4.14 v4.15 ...; do
#   kustomize build .tekton/overlays/$version > .tekton/fbc-$version-production-push.yaml
# done
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies significant duplication across seven new Tekton PipelineRun files, which poses a major maintenance challenge, and proposes a valid architectural improvement using templating.

Medium
Possible issue
Add ocp-release-version param

Add the ocp-release-version parameter with the value "v4.14" to the spec.params
section of the PipelineRun.

.tekton/fbc-v4-14-production-push.yaml [29-30]

 - name: revision
   value: '{{revision}}'
+- name: ocp-release-version
+  value: "v4.14"
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: This suggestion correctly identifies a missing parameter that is present in other similar files in the PR, ensuring consistency and correct behavior of the pipeline.

Medium
General
Combine multiple yq commands into one

Combine the four separate yq -i commands inside the loop into a single command
to improve performance by reducing file I/O.

utils/filter_for_production.sh [62-68]

-yq -i "del(.entries[] | select(.schema == \"olm.bundle\" and .image == \"$IMAGE\"))" "$GRAPH"
-# Remove from channel entries
-yq -i "(.entries[] | select(.schema == \"olm.channel\").entries) |= map(select(.name != \"$BUNDLE\"))" "$GRAPH"
-# Remove from skips arrays
-yq -i "(.entries[] | select(.schema == \"olm.channel\").entries[].skips) |= map(select(. != \"$BUNDLE\"))" "$GRAPH"
-# Remove from replaces references
-yq -i "del(.entries[] | select(.schema == \"olm.channel\").entries[] | select(.replaces == \"$BUNDLE\").replaces)" "$GRAPH"
+yq -i "
+  del(.entries[] | select(.schema == \"olm.bundle\" and .image == \"$IMAGE\")) |
+  (.entries[] | select(.schema == \"olm.channel\").entries) |= map(select(.name != \"$BUNDLE\")) |
+  (.entries[] | select(.schema == \"olm.channel\").entries[].skips) |= map(select(. != \"$BUNDLE\")) |
+  del(.entries[] | select(.schema == \"olm.channel\").entries[] | select(.replaces == \"$BUNDLE\").replaces)
+" "$GRAPH"
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies an inefficiency and proposes a valid optimization by combining multiple yq commands, which reduces I/O operations and improves performance.

Low
Optimize cache checking in shell script

Refactor the check_image_exists function to use a single grep and a case
statement instead of two separate grep calls for checking the cache.

utils/filter_for_production.sh [26-45]

 check_image_exists() {
   local image="$1"
   local cache_key="${image##*@}"
+  local status
 
   # Check cache first
-  if grep -q "^${cache_key}=exists$" "$CACHE_FILE" 2>/dev/null; then
-    return 0
-  elif grep -q "^${cache_key}=notfound$" "$CACHE_FILE" 2>/dev/null; then
-    return 1
-  fi
+  status=$(grep "^${cache_key}=" "$CACHE_FILE" 2>/dev/null | cut -d'=' -f2)
+  case "$status" in
+    exists) return 0 ;;
+    notfound) return 1 ;;
+  esac
 
   # Not cached, check registry
   if ${SKOPEO_CMD} inspect --no-tags ${AUTH_FILE} "docker://${image}" &>/dev/null; then
     echo "${cache_key}=exists" >> "$CACHE_FILE"
     return 0
   else
     echo "${cache_key}=notfound" >> "$CACHE_FILE"
     return 1
   fi
 }
  • Apply / Chat
Suggestion importance[1-10]: 3

__

Why: The suggestion offers a valid micro-optimization by reducing two grep calls to one, which is slightly more efficient, but the performance gain is likely minimal.

Low
  • Update

creationTimestamp: null
labels:
appstudio.openshift.io/application: fbc-v4-14
appstudio.openshift.io/component: fbc-v4-14-production
Copy link
Member

Choose a reason for hiding this comment

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

We will need to onboard new components for each OCP Version

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@JasonPowr I looked into this some more, I think these tekton files might be legacy and no longer needed now that we have with the rhtas-fbc templates. I think this may have implications for your other PR, I'll comment on that one as well.

Copy link
Contributor Author

@knrc knrc Feb 6, 2026

Choose a reason for hiding this comment

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

Never mind, it looks like the pipeline ones may be ephemeral and only used for the e2e integration tests. These ones are still needed.

- name: git-url
value: '{{source_url}}'
- name: output-image
value: quay.io/securesign/fbc-v4-14-production:{{revision}}
Copy link
Member

Choose a reason for hiding this comment

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

We will also need to create new image repos in quay.io/securesign

@@ -0,0 +1,46 @@
apiVersion: tekton.dev/v1
Copy link
Member

Choose a reason for hiding this comment

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

Usually konflux will create the on-push/on-pull-request pipelines when the component is on boarded, this will probably be OK once the component is on boarded though.

--body "## Automated Production Release PR

### Release Details
- **Operator**: ${{ env.OPERATOR_NAME }}
Copy link
Member

Choose a reason for hiding this comment

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

I think the formatting here is stopping the action from running

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, I'll check this shortly

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@JasonPowr sorry about that, the indentation was causing the error. I've tidied it up and moved the body to a variable to make it easier to read.

@knrc
Copy link
Contributor Author

knrc commented Feb 6, 2026

FYI I realised during the night that I missed some use cases in my testing of the script parts. I'm just working through those, so expect an update in a bit.

@knrc knrc force-pushed the knrc_fbc_to_production_branch branch from 0fc8fb7 to 1140249 Compare February 6, 2026 15:48
…ing branch

Signed-off-by: Kevin Conner <kconner@redhat.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants