From c918e4cc709d2d599d7ee8087661eb2840e5c09b Mon Sep 17 00:00:00 2001 From: Marcin Owsiany Date: Thu, 29 Jan 2026 12:52:28 +0100 Subject: [PATCH 01/21] chore(operator): wrapper for bundle helpers --- operator/Makefile | 6 ++-- operator/bundle_helpers/dispatch.sh | 32 +++++++++++++++++++ .../prepare-bundle-manifests.sh | 2 +- 3 files changed, 36 insertions(+), 4 deletions(-) create mode 100755 operator/bundle_helpers/dispatch.sh diff --git a/operator/Makefile b/operator/Makefile index a25efe522b009..1697e63e69643 100644 --- a/operator/Makefile +++ b/operator/Makefile @@ -462,10 +462,10 @@ bundle: yq manifests kustomize operator-sdk ## Generate bundle manifests and met # Yet we want most of the contents autogenerated from the Makefile variables as a single source of truth. # Therefore we append ".extra" file to the end of bundle's dockerfile. cat bundle.Dockerfile.extra >> bundle.Dockerfile -# Run a python script to fix the orders in the specDescriptors (children must not appear before their parents). +# Fix the orders in the specDescriptors (children must not appear before their parents). set -euo pipefail ;\ $(ACTIVATE_PYTHON) ;\ - bundle_helpers/fix-spec-descriptor-order.py \ + ./bundle_helpers/dispatch.sh fix-spec-descriptor-order \ bundle/manifests/rhacs-operator.clusterserviceversion.yaml.fixed mv bundle/manifests/rhacs-operator.clusterserviceversion.yaml.fixed \ @@ -477,7 +477,7 @@ bundle-post-process: test-bundle-helpers operator-sdk ## Post-process CSV file t set -euo pipefail ;\ $(ACTIVATE_PYTHON) ;\ first_version=3.62.0 `# 3.62.0 is the first operator version ever released` ;\ - candidate_version=$$(./bundle_helpers/patch-csv.py \ + candidate_version=$$(./bundle_helpers/dispatch.sh patch-csv \ --use-version $(VERSION) \ --first-version $${first_version} \ --operator-image $(IMG) \ diff --git a/operator/bundle_helpers/dispatch.sh b/operator/bundle_helpers/dispatch.sh new file mode 100755 index 0000000000000..1cab887ffc1d4 --- /dev/null +++ b/operator/bundle_helpers/dispatch.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# +# Wrapper script for bundle helper tools. +# +# Provides an abstraction layer that allows switching between Python and Go +# implementations of bundle helper scripts without changing Makefile or Dockerfiles. +# The implementation is selected via the USE_GO_BUNDLE_HELPER environment variable. +# +# Usage: dispatch.sh [args...] + +set -euo pipefail + +if [[ $# -lt 1 ]]; then + echo "Usage: $0 [args...]" >&2 + echo "Available scripts: fix-spec-descriptor-order, patch-csv" >&2 + exit 1 +fi + +script_name="$1" +shift + +script_dir="$(dirname "$0")" + +case "$script_name" in +fix-spec-descriptor-order|patch-csv) + if [[ "${USE_GO_BUNDLE_HELPER:-false}" == "true" ]]; then + echo "No Go implementation of $script_name available yet." >&2 + exit 1 + fi + exec "${script_dir}/${script_name}.py" "$@" + ;; +esac diff --git a/operator/bundle_helpers/prepare-bundle-manifests.sh b/operator/bundle_helpers/prepare-bundle-manifests.sh index d08bfc9f8a61a..b363fac844030 100755 --- a/operator/bundle_helpers/prepare-bundle-manifests.sh +++ b/operator/bundle_helpers/prepare-bundle-manifests.sh @@ -9,6 +9,6 @@ mkdir -p build/ rm -rf build/bundle cp -a bundle build/ -"$(dirname "$0")/patch-csv.py" "$@" \ +"$(dirname "$0")/dispatch.sh" patch-csv "$@" \ < bundle/manifests/rhacs-operator.clusterserviceversion.yaml \ > build/bundle/manifests/rhacs-operator.clusterserviceversion.yaml From c7311efb2807f938aab850f7cbbcfe881be2c29b Mon Sep 17 00:00:00 2001 From: Marcin Owsiany Date: Thu, 29 Jan 2026 13:48:41 +0100 Subject: [PATCH 02/21] chore(operator): Go implemetation of fix-spec-descriptor-order --- .github/workflows/test-bundle-helpers.yaml | 198 ++++++++++++++++++ .../bundle_helpers/cmd/fix_descriptors.go | 88 ++++++++ operator/bundle_helpers/cmd/patch_csv.go | 7 + operator/bundle_helpers/dispatch.sh | 13 +- operator/bundle_helpers/main.go | 35 ++++ operator/bundle_helpers/pkg/csv/patcher.go | 3 + .../bundle_helpers/pkg/csv/related_images.go | 3 + operator/bundle_helpers/pkg/csv/replaces.go | 3 + operator/bundle_helpers/pkg/csv/version.go | 3 + .../bundle_helpers/pkg/descriptor/sorter.go | 163 ++++++++++++++ .../bundle_helpers/pkg/rewrite/rewriter.go | 3 + .../pkg/yamlformat/pyaml_compat.go | 41 ++++ operator/bundle_helpers/yaml-normalizer.py | 17 ++ 13 files changed, 568 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/test-bundle-helpers.yaml create mode 100644 operator/bundle_helpers/cmd/fix_descriptors.go create mode 100644 operator/bundle_helpers/cmd/patch_csv.go create mode 100644 operator/bundle_helpers/main.go create mode 100644 operator/bundle_helpers/pkg/csv/patcher.go create mode 100644 operator/bundle_helpers/pkg/csv/related_images.go create mode 100644 operator/bundle_helpers/pkg/csv/replaces.go create mode 100644 operator/bundle_helpers/pkg/csv/version.go create mode 100644 operator/bundle_helpers/pkg/descriptor/sorter.go create mode 100644 operator/bundle_helpers/pkg/rewrite/rewriter.go create mode 100644 operator/bundle_helpers/pkg/yamlformat/pyaml_compat.go create mode 100755 operator/bundle_helpers/yaml-normalizer.py diff --git a/.github/workflows/test-bundle-helpers.yaml b/.github/workflows/test-bundle-helpers.yaml new file mode 100644 index 0000000000000..e4ebd5a09c114 --- /dev/null +++ b/.github/workflows/test-bundle-helpers.yaml @@ -0,0 +1,198 @@ +name: Test Bundle Helpers + +on: + pull_request: + paths: + - 'operator/**' + - '.github/workflows/test-bundle-helpers.yaml' + push: + branches: + - master + paths: + - 'operator/**' + +jobs: + test-python-implementation: + name: Test Python Implementation + runs-on: ubuntu-latest + defaults: + run: + working-directory: operator + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip + pip install -r bundle_helpers/requirements-gha.txt + + - name: Run Python unit tests + run: make test-bundle-helpers + + - name: Generate bundle with Python + env: + USE_GO_BUNDLE_HELPER: false + run: | + make bundle bundle-post-process + mkdir -p /tmp/artifacts + cp -r bundle /tmp/artifacts/bundle-python + if [ -d build/bundle ]; then + cp -r build/bundle /tmp/artifacts/build-bundle-python + fi + + - name: Upload Python bundle artifacts + uses: actions/upload-artifact@v4 + with: + name: bundle-python + path: /tmp/artifacts/ + retention-days: 1 + + test-go-implementation: + name: Test Go Implementation + runs-on: ubuntu-latest + # Only run if Go implementation exists + continue-on-error: true + defaults: + run: + working-directory: operator + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Run Go unit tests + run: | + cd bundle_helpers + go test ./... + + - name: Generate bundle with Go + env: + USE_GO_BUNDLE_HELPER: true + run: | + make bundle bundle-post-process + mkdir -p /tmp/artifacts + cp -r bundle /tmp/artifacts/bundle-go + if [ -d build/bundle ]; then + cp -r build/bundle /tmp/artifacts/build-bundle-go + fi + + - name: Upload Go bundle artifacts + if: steps.check-go.outputs.exists == 'true' + uses: actions/upload-artifact@v4 + with: + name: bundle-go + path: /tmp/artifacts/ + retention-days: 1 + + compare-implementations: + name: Compare Python and Go Outputs + runs-on: ubuntu-latest + needs: [test-python-implementation, test-go-implementation] + # Only run comparison if Go implementation exists + if: always() && needs.test-go-implementation.result == 'success' + + steps: + - name: Download Python bundle + uses: actions/download-artifact@v4 + with: + name: bundle-python + path: /tmp/python + + - name: Download Go bundle + uses: actions/download-artifact@v4 + with: + name: bundle-go + path: /tmp/go + continue-on-error: true + + - name: Install comparison tools + run: | + sudo apt-get update + sudo apt-get install -y diffutils + + - name: Compare bundle outputs + run: | + if [ ! -d /tmp/go/bundle-go ]; then + echo "Go bundle not available yet - skipping comparison" + exit 0 + fi + + echo "=== Comparing Python and Go bundle outputs ===" + + if diff -r /tmp/python/bundle-python /tmp/go/bundle-go; then + echo "✓ SUCCESS: Bundle outputs are identical" + else + echo "✗ FAILURE: Bundle outputs differ" + echo "" + echo "Detailed diff of CSV files:" + diff -u /tmp/python/bundle-python/manifests/*.clusterserviceversion.yaml \ + /tmp/go/bundle-go/manifests/*.clusterserviceversion.yaml || true + exit 1 + fi + + if [ -d /tmp/python/build-bundle-python ] && [ -d /tmp/go/build-bundle-go ]; then + echo "" + echo "=== Comparing build/bundle outputs ===" + if diff -r /tmp/python/build-bundle-python /tmp/go/build-bundle-go; then + echo "✓ SUCCESS: Build bundle outputs are identical" + else + echo "✗ FAILURE: Build bundle outputs differ" + exit 1 + fi + fi + + status-check: + name: Bundle Helper Tests Status + runs-on: ubuntu-latest + needs: [test-python-implementation, test-go-implementation, compare-implementations, e2e-test] + if: always() + + steps: + - name: Check test results + run: | + python_result="${{ needs.test-python-implementation.result }}" + go_result="${{ needs.test-go-implementation.result }}" + compare_result="${{ needs.compare-implementations.result }}" + e2e_result="${{ needs.e2e-test.result }}" + + echo "Python tests: $python_result" + echo "Go tests: $go_result" + echo "Comparison: $compare_result" + echo "E2E test: $e2e_result" + + # Python tests must always pass + if [ "$python_result" != "success" ]; then + echo "✗ Python tests failed" + exit 1 + fi + + # E2E test must pass + if [ "$e2e_result" != "success" ]; then + echo "✗ E2E test failed" + exit 1 + fi + + # Go tests and comparison are optional until Go is fully implemented + if [ "$go_result" == "success" ]; then + echo "✓ Go implementation available and tested" + if [ "$compare_result" != "success" ] && [ "$compare_result" != "skipped" ]; then + echo "✗ Go/Python comparison failed" + exit 1 + fi + else + echo "⚠ Go implementation not yet available (expected during migration)" + fi + + echo "✓ All required tests passed" diff --git a/operator/bundle_helpers/cmd/fix_descriptors.go b/operator/bundle_helpers/cmd/fix_descriptors.go new file mode 100644 index 0000000000000..83cc1700a95d3 --- /dev/null +++ b/operator/bundle_helpers/cmd/fix_descriptors.go @@ -0,0 +1,88 @@ +package cmd + +import ( + "bytes" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + + "github.com/stackrox/rox/operator/bundle_helpers/pkg/descriptor" + "gopkg.in/yaml.v3" +) + +// FixSpecDescriptorOrder fixes the ordering of specDescriptors in a CSV file. +// It reads from stdin and writes to stdout, matching the Python script behavior. +func FixSpecDescriptorOrder(args []string) error { + if len(args) > 0 && (args[0] == "-h" || args[0] == "--help") { + fmt.Println("Usage: bundle-helper fix-spec-descriptor-order < input.yaml > output.yaml") + fmt.Println() + fmt.Println("Fixes the ordering of specDescriptors in a ClusterServiceVersion YAML file.") + fmt.Println("Ensures parent descriptors appear before their children.") + return nil + } + + // Read CSV from stdin + data, err := io.ReadAll(os.Stdin) + if err != nil { + return fmt.Errorf("failed to read stdin: %w", err) + } + + // Parse YAML into a map (like Python's yaml.safe_load) + var csvDoc map[string]interface{} + if err := yaml.Unmarshal(data, &csvDoc); err != nil { + return fmt.Errorf("failed to parse YAML: %w", err) + } + + // Process descriptors + if err := descriptor.FixCSVDescriptorsMap(csvDoc); err != nil { + return fmt.Errorf("failed to fix descriptors: %w", err) + } + + // Encode to YAML using Go's yaml.v3 + var buf bytes.Buffer + encoder := yaml.NewEncoder(&buf) + encoder.SetIndent(2) + if err := encoder.Encode(csvDoc); err != nil { + return fmt.Errorf("failed to encode YAML: %w", err) + } + if err := encoder.Close(); err != nil { + return fmt.Errorf("failed to close encoder: %w", err) + } + + // Normalize through Python to match PyYAML's exact formatting + // This is the "escape hatch" mentioned in the migration plan + return normalizeYAMLOutput(buf.Bytes(), os.Stdout) +} + +// normalizeYAMLOutput pipes YAML through the Python normalizer to match PyYAML formatting. +// This handles formatting quirks (quote styles, line wrapping, etc.) while keeping +// all business logic in Go. +func normalizeYAMLOutput(goYAML []byte, w io.Writer) error { + // Find the yaml-normalizer.py script + execPath, err := os.Executable() + if err != nil { + return fmt.Errorf("failed to get executable path: %w", err) + } + scriptDir := filepath.Dir(execPath) + normalizerPath := filepath.Join(scriptDir, "..", "yaml-normalizer.py") + + // If running from source (not installed), try the current directory + if _, err := os.Stat(normalizerPath); os.IsNotExist(err) { + wd, _ := os.Getwd() + normalizerPath = filepath.Join(wd, "yaml-normalizer.py") + } + + // Run the normalizer + cmd := exec.Command(normalizerPath) + cmd.Stdin = bytes.NewReader(goYAML) + cmd.Stdout = w + cmd.Stderr = os.Stderr + + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to normalize YAML: %w", err) + } + + return nil +} diff --git a/operator/bundle_helpers/cmd/patch_csv.go b/operator/bundle_helpers/cmd/patch_csv.go new file mode 100644 index 0000000000000..ce6139c6a909d --- /dev/null +++ b/operator/bundle_helpers/cmd/patch_csv.go @@ -0,0 +1,7 @@ +package cmd + +// PatchCSV patches a ClusterServiceVersion YAML file with version and image information. +// This is a placeholder implementation - to be implemented in future phases. +func PatchCSV(args []string) error { + panic("not yet implemented") +} diff --git a/operator/bundle_helpers/dispatch.sh b/operator/bundle_helpers/dispatch.sh index 1cab887ffc1d4..684c9898615d7 100755 --- a/operator/bundle_helpers/dispatch.sh +++ b/operator/bundle_helpers/dispatch.sh @@ -12,7 +12,6 @@ set -euo pipefail if [[ $# -lt 1 ]]; then echo "Usage: $0 [args...]" >&2 - echo "Available scripts: fix-spec-descriptor-order, patch-csv" >&2 exit 1 fi @@ -21,12 +20,8 @@ shift script_dir="$(dirname "$0")" -case "$script_name" in -fix-spec-descriptor-order|patch-csv) - if [[ "${USE_GO_BUNDLE_HELPER:-false}" == "true" ]]; then - echo "No Go implementation of $script_name available yet." >&2 - exit 1 - fi +if [[ "${USE_GO_BUNDLE_HELPER:-false}" == "true" ]]; then + exec go run "${script_dir}/main.go" "$script_name" "$@" +else exec "${script_dir}/${script_name}.py" "$@" - ;; -esac +fi diff --git a/operator/bundle_helpers/main.go b/operator/bundle_helpers/main.go new file mode 100644 index 0000000000000..8745603da4dec --- /dev/null +++ b/operator/bundle_helpers/main.go @@ -0,0 +1,35 @@ +package main + +import ( + "fmt" + "os" + + "github.com/stackrox/rox/operator/bundle_helpers/cmd" +) + +func main() { + if len(os.Args) < 2 { + fmt.Fprintf(os.Stderr, "Usage: %s [args...]\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "Available commands:\n") + fmt.Fprintf(os.Stderr, " fix-spec-descriptor-order Fix specDescriptor ordering\n") + fmt.Fprintf(os.Stderr, " patch-csv Patch ClusterServiceVersion file (not yet implemented)\n") + os.Exit(1) + } + + command := os.Args[1] + args := os.Args[2:] + + switch command { + case "fix-spec-descriptor-order": + if err := cmd.FixSpecDescriptorOrder(args); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + case "patch-csv": + fmt.Fprintf(os.Stderr, "patch-csv command not yet implemented\n") + os.Exit(1) + default: + fmt.Fprintf(os.Stderr, "Unknown command: %s\n", command) + os.Exit(1) + } +} diff --git a/operator/bundle_helpers/pkg/csv/patcher.go b/operator/bundle_helpers/pkg/csv/patcher.go new file mode 100644 index 0000000000000..59855dc53ee8a --- /dev/null +++ b/operator/bundle_helpers/pkg/csv/patcher.go @@ -0,0 +1,3 @@ +package csv + +// Placeholder for CSV patching logic diff --git a/operator/bundle_helpers/pkg/csv/related_images.go b/operator/bundle_helpers/pkg/csv/related_images.go new file mode 100644 index 0000000000000..8806b3533e5e1 --- /dev/null +++ b/operator/bundle_helpers/pkg/csv/related_images.go @@ -0,0 +1,3 @@ +package csv + +// Placeholder for related images handling logic diff --git a/operator/bundle_helpers/pkg/csv/replaces.go b/operator/bundle_helpers/pkg/csv/replaces.go new file mode 100644 index 0000000000000..c48d358a31ad0 --- /dev/null +++ b/operator/bundle_helpers/pkg/csv/replaces.go @@ -0,0 +1,3 @@ +package csv + +// Placeholder for replace calculation logic diff --git a/operator/bundle_helpers/pkg/csv/version.go b/operator/bundle_helpers/pkg/csv/version.go new file mode 100644 index 0000000000000..e2c3c1f0fdc2a --- /dev/null +++ b/operator/bundle_helpers/pkg/csv/version.go @@ -0,0 +1,3 @@ +package csv + +// Placeholder for version handling logic diff --git a/operator/bundle_helpers/pkg/descriptor/sorter.go b/operator/bundle_helpers/pkg/descriptor/sorter.go new file mode 100644 index 0000000000000..a43e0cbce7a18 --- /dev/null +++ b/operator/bundle_helpers/pkg/descriptor/sorter.go @@ -0,0 +1,163 @@ +package descriptor + +import ( + "fmt" + "sort" + "strings" +) + +// FixCSVDescriptorsMap processes all CRDs in a CSV and fixes their specDescriptors. +// This function works with map[string]interface{} to match Python's behavior. +func FixCSVDescriptorsMap(csvDoc map[string]interface{}) error { + // Navigate to spec.customresourcedefinitions.owned + spec, ok := csvDoc["spec"].(map[string]interface{}) + if !ok { + return fmt.Errorf("spec not found or not a map") + } + + crds, ok := spec["customresourcedefinitions"].(map[string]interface{}) + if !ok { + return fmt.Errorf("customresourcedefinitions not found or not a map") + } + + owned, ok := crds["owned"].([]interface{}) + if !ok { + return fmt.Errorf("owned not found or not a list") + } + + // Process each CRD + for _, crdItem := range owned { + crd, ok := crdItem.(map[string]interface{}) + if !ok { + continue + } + + if err := processSpecDescriptorsMap(crd); err != nil { + return err + } + } + + return nil +} + +// processSpecDescriptorsMap processes specDescriptors for a single CRD. +func processSpecDescriptorsMap(crd map[string]interface{}) error { + descs, ok := crd["specDescriptors"] + if !ok { + // No specDescriptors, that's OK + return nil + } + + descriptors, ok := descs.([]interface{}) + if !ok { + return fmt.Errorf("specDescriptors is not a list") + } + + // Fix descriptor order + fixDescriptorOrderMap(descriptors) + + // Allow relative field dependencies + allowRelativeFieldDependenciesMap(descriptors) + + return nil +} + +// fixDescriptorOrderMap performs a stable sort based on the parent path. +// This ensures children always come after their parents. +// Mimics Python: descriptors.sort(key=lambda d: f'.{d["path"]}'.rsplit('.', 1)[0]) +func fixDescriptorOrderMap(descriptors []interface{}) { + sort.SliceStable(descriptors, func(i, j int) bool { + pathI := getDescriptorPathMap(descriptors[i]) + pathJ := getDescriptorPathMap(descriptors[j]) + parentI := getParentPath(pathI) + parentJ := getParentPath(pathJ) + return parentI < parentJ + }) +} + +// getParentPath extracts the parent path from a descriptor path. +// Mimics Python: f'.{d["path"]}'.rsplit('.', 1)[0] +func getParentPath(path string) string { + // Add a '.' in front for simplicity + fullPath := "." + path + // Split by last '.' and take the first part + lastDot := strings.LastIndex(fullPath, ".") + if lastDot == -1 { + return "" + } + return fullPath[:lastDot] +} + +// getDescriptorPathMap extracts the 'path' field from a descriptor map. +func getDescriptorPathMap(desc interface{}) string { + descMap, ok := desc.(map[string]interface{}) + if !ok { + return "" + } + + path, ok := descMap["path"].(string) + if !ok { + return "" + } + + return path +} + +// allowRelativeFieldDependenciesMap converts relative field dependency paths to absolute. +func allowRelativeFieldDependenciesMap(descriptors []interface{}) { + for _, desc := range descriptors { + descMap, ok := desc.(map[string]interface{}) + if !ok { + continue + } + + path, _ := descMap["path"].(string) + xDescsRaw, ok := descMap["x-descriptors"] + if !ok { + continue + } + + xDescs, ok := xDescsRaw.([]interface{}) + if !ok { + continue + } + + // Process each x-descriptor + for i, xDescRaw := range xDescs { + xDesc, ok := xDescRaw.(string) + if !ok { + continue + } + + if !strings.HasPrefix(xDesc, "urn:alm:descriptor:com.tectonic.ui:fieldDependency:") { + continue + } + + // Split by ':' and get the last two parts (field and value) + parts := strings.Split(xDesc, ":") + if len(parts) < 2 { + continue + } + + field := parts[len(parts)-2] + val := parts[len(parts)-1] + + // Check if field starts with '.' (relative path) + if !strings.HasPrefix(field, ".") { + continue + } + + // Convert relative to absolute + // Mimics Python: f'.{d["path"]}'.rsplit('.', 1)[0][1:] + field + parentPath := getParentPath(path) + if len(parentPath) > 0 { + parentPath = parentPath[1:] // Remove leading '.' + } + absoluteField := parentPath + field + + // Reconstruct the x-descriptor + prefix := "urn:alm:descriptor:com.tectonic.ui:fieldDependency:" + xDescs[i] = prefix + absoluteField + ":" + val + } + } +} diff --git a/operator/bundle_helpers/pkg/rewrite/rewriter.go b/operator/bundle_helpers/pkg/rewrite/rewriter.go new file mode 100644 index 0000000000000..0b5684a4b8671 --- /dev/null +++ b/operator/bundle_helpers/pkg/rewrite/rewriter.go @@ -0,0 +1,3 @@ +package rewrite + +// Placeholder for generic dictionary rewriter diff --git a/operator/bundle_helpers/pkg/yamlformat/pyaml_compat.go b/operator/bundle_helpers/pkg/yamlformat/pyaml_compat.go new file mode 100644 index 0000000000000..574b5aae8602d --- /dev/null +++ b/operator/bundle_helpers/pkg/yamlformat/pyaml_compat.go @@ -0,0 +1,41 @@ +package yamlformat + +import ( + "bytes" + "io" + + "gopkg.in/yaml.v3" +) + +// EncodePyYAMLStyle encodes data to YAML matching PyYAML's safe_dump() style. +// This includes: +// - Single quotes for simple string values +// - Empty string as '' not "" +// - Flow style for arrays/maps where appropriate +func EncodePyYAMLStyle(w io.Writer, data interface{}) error { + var buf bytes.Buffer + + encoder := yaml.NewEncoder(&buf) + encoder.SetIndent(2) + + if err := encoder.Encode(data); err != nil { + return err + } + if err := encoder.Close(); err != nil { + return err + } + + // Post-process to match PyYAML style + output := buf.Bytes() + output = normalizeToPyYAMLStyle(output) + + _, err := w.Write(output) + return err +} + +// normalizeToPyYAMLStyle converts Go yaml.v3 output to match PyYAML style. +func normalizeToPyYAMLStyle(input []byte) []byte { + // For now, just return as-is + // We'll implement specific transformations if needed + return input +} diff --git a/operator/bundle_helpers/yaml-normalizer.py b/operator/bundle_helpers/yaml-normalizer.py new file mode 100755 index 0000000000000..7d8cb21a14297 --- /dev/null +++ b/operator/bundle_helpers/yaml-normalizer.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +""" +Thin YAML reformatter that normalizes Go-generated YAML to match PyYAML output. +This script has NO knowledge of CSV/bundle structure - it only normalizes formatting. + +This is the "escape hatch" mentioned in the migration plan (Section 2.2). +All business logic remains in Go; this only handles YAML formatting quirks. +""" +import sys +import yaml + +# Read YAML from stdin +doc = yaml.safe_load(sys.stdin) + +# Write YAML to stdout with PyYAML's formatting +# Note: yaml.safe_dump() adds a trailing newline, and print() adds another +print(yaml.safe_dump(doc)) From 70be9c36366e1e013682859fe2fd06fe5cda838b Mon Sep 17 00:00:00 2001 From: Marcin Owsiany Date: Thu, 29 Jan 2026 13:54:17 +0100 Subject: [PATCH 03/21] fixes --- .github/workflows/test-bundle-helpers.yaml | 21 ++++++++++----------- operator/bundle_helpers/dispatch.sh | 10 +++++++++- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/.github/workflows/test-bundle-helpers.yaml b/.github/workflows/test-bundle-helpers.yaml index e4ebd5a09c114..e443f221651a3 100644 --- a/.github/workflows/test-bundle-helpers.yaml +++ b/.github/workflows/test-bundle-helpers.yaml @@ -1,15 +1,17 @@ name: Test Bundle Helpers on: - pull_request: - paths: - - 'operator/**' - - '.github/workflows/test-bundle-helpers.yaml' push: + tags: + - '*' branches: - - master - paths: - - 'operator/**' + - master + - release-* + pull_request: + types: + - opened + - reopened + - synchronize jobs: test-python-implementation: @@ -57,8 +59,6 @@ jobs: test-go-implementation: name: Test Go Implementation runs-on: ubuntu-latest - # Only run if Go implementation exists - continue-on-error: true defaults: run: working-directory: operator @@ -89,7 +89,6 @@ jobs: fi - name: Upload Go bundle artifacts - if: steps.check-go.outputs.exists == 'true' uses: actions/upload-artifact@v4 with: name: bundle-go @@ -156,7 +155,7 @@ jobs: status-check: name: Bundle Helper Tests Status runs-on: ubuntu-latest - needs: [test-python-implementation, test-go-implementation, compare-implementations, e2e-test] + needs: [test-python-implementation, test-go-implementation, compare-implementations] if: always() steps: diff --git a/operator/bundle_helpers/dispatch.sh b/operator/bundle_helpers/dispatch.sh index 684c9898615d7..83ac70d66b090 100755 --- a/operator/bundle_helpers/dispatch.sh +++ b/operator/bundle_helpers/dispatch.sh @@ -21,7 +21,15 @@ shift script_dir="$(dirname "$0")" if [[ "${USE_GO_BUNDLE_HELPER:-false}" == "true" ]]; then - exec go run "${script_dir}/main.go" "$script_name" "$@" + case "$script_name" in + fix-spec-descriptor-order) + exec go run "${script_dir}/main.go" "$script_name" "$@" + ;; + *) + echo >&2 "No Go implementation of $script_name yet, falling back to Python one!" + exec "${script_dir}/${script_name}.py" "$@" + ;; + esac else exec "${script_dir}/${script_name}.py" "$@" fi From 5df5a87b8ac81eda9dbd09cf63b72dcf6c81785c Mon Sep 17 00:00:00 2001 From: Marcin Owsiany Date: Mon, 9 Feb 2026 12:39:15 +0100 Subject: [PATCH 04/21] merge --- .github/workflows/test-bundle-helpers.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-bundle-helpers.yaml b/.github/workflows/test-bundle-helpers.yaml index e443f221651a3..6215654bd9eca 100644 --- a/.github/workflows/test-bundle-helpers.yaml +++ b/.github/workflows/test-bundle-helpers.yaml @@ -33,7 +33,7 @@ jobs: - name: Install Python dependencies run: | python -m pip install --upgrade pip - pip install -r bundle_helpers/requirements-gha.txt + pip install -r bundle_helpers/requirements.txt - name: Run Python unit tests run: make test-bundle-helpers From 1c09d588c505533f7329bdc3f5ca16926bee02fd Mon Sep 17 00:00:00 2001 From: Marcin Owsiany Date: Mon, 9 Feb 2026 12:40:28 +0100 Subject: [PATCH 05/21] tidy --- .github/workflows/test-bundle-helpers.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test-bundle-helpers.yaml b/.github/workflows/test-bundle-helpers.yaml index 6215654bd9eca..0cfdb4238f4af 100644 --- a/.github/workflows/test-bundle-helpers.yaml +++ b/.github/workflows/test-bundle-helpers.yaml @@ -74,6 +74,7 @@ jobs: - name: Run Go unit tests run: | + go mod tidy cd bundle_helpers go test ./... From 56f040c123302d087eb295003987aa5b42652347 Mon Sep 17 00:00:00 2001 From: Marcin Owsiany Date: Tue, 10 Feb 2026 06:53:37 +0100 Subject: [PATCH 06/21] move the tidy --- .github/workflows/test-bundle-helpers.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-bundle-helpers.yaml b/.github/workflows/test-bundle-helpers.yaml index 0cfdb4238f4af..23552e9c2ef74 100644 --- a/.github/workflows/test-bundle-helpers.yaml +++ b/.github/workflows/test-bundle-helpers.yaml @@ -74,7 +74,6 @@ jobs: - name: Run Go unit tests run: | - go mod tidy cd bundle_helpers go test ./... @@ -82,6 +81,7 @@ jobs: env: USE_GO_BUNDLE_HELPER: true run: | + go mod tidy make bundle bundle-post-process mkdir -p /tmp/artifacts cp -r bundle /tmp/artifacts/bundle-go From 4437f974a93209bffb134ea85c10334ae219f6fe Mon Sep 17 00:00:00 2001 From: Marcin Owsiany Date: Tue, 10 Feb 2026 07:34:21 +0100 Subject: [PATCH 07/21] go mod tidy --- .github/workflows/test-bundle-helpers.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test-bundle-helpers.yaml b/.github/workflows/test-bundle-helpers.yaml index 23552e9c2ef74..c55d68c25de36 100644 --- a/.github/workflows/test-bundle-helpers.yaml +++ b/.github/workflows/test-bundle-helpers.yaml @@ -42,6 +42,7 @@ jobs: env: USE_GO_BUNDLE_HELPER: false run: | + go mod tidy make bundle bundle-post-process mkdir -p /tmp/artifacts cp -r bundle /tmp/artifacts/bundle-python From d033313374419d53de454b20bd443c3fbb18fa54 Mon Sep 17 00:00:00 2001 From: Marcin Owsiany Date: Tue, 10 Feb 2026 07:52:08 +0100 Subject: [PATCH 08/21] fix deps, checkout --- .github/workflows/test-bundle-helpers.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/test-bundle-helpers.yaml b/.github/workflows/test-bundle-helpers.yaml index c55d68c25de36..79bc798ba2aed 100644 --- a/.github/workflows/test-bundle-helpers.yaml +++ b/.github/workflows/test-bundle-helpers.yaml @@ -24,6 +24,9 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} - name: Set up Python uses: actions/setup-python@v5 @@ -67,6 +70,9 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} - name: Set up Go uses: actions/setup-go@v5 @@ -78,6 +84,18 @@ jobs: cd bundle_helpers go test ./... + # For python fallback + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + # For python fallback + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip + pip install -r bundle_helpers/requirements.txt + - name: Generate bundle with Go env: USE_GO_BUNDLE_HELPER: true From 7848d77d41873e3b00330ce46912db1b4742e4f1 Mon Sep 17 00:00:00 2001 From: Marcin Owsiany Date: Tue, 10 Feb 2026 07:54:05 +0100 Subject: [PATCH 09/21] cleanup e2e --- .github/workflows/test-bundle-helpers.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test-bundle-helpers.yaml b/.github/workflows/test-bundle-helpers.yaml index 79bc798ba2aed..4486fe5f29e8c 100644 --- a/.github/workflows/test-bundle-helpers.yaml +++ b/.github/workflows/test-bundle-helpers.yaml @@ -184,7 +184,6 @@ jobs: python_result="${{ needs.test-python-implementation.result }}" go_result="${{ needs.test-go-implementation.result }}" compare_result="${{ needs.compare-implementations.result }}" - e2e_result="${{ needs.e2e-test.result }}" echo "Python tests: $python_result" echo "Go tests: $go_result" From 75f64a3076093cdd3fa9ae4658d3d6cf35a81abc Mon Sep 17 00:00:00 2001 From: Marcin Owsiany Date: Tue, 10 Feb 2026 08:03:36 +0100 Subject: [PATCH 10/21] fix normalizer path --- operator/bundle_helpers/cmd/fix_descriptors.go | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/operator/bundle_helpers/cmd/fix_descriptors.go b/operator/bundle_helpers/cmd/fix_descriptors.go index 83cc1700a95d3..21db9b8a30df0 100644 --- a/operator/bundle_helpers/cmd/fix_descriptors.go +++ b/operator/bundle_helpers/cmd/fix_descriptors.go @@ -60,19 +60,8 @@ func FixSpecDescriptorOrder(args []string) error { // This handles formatting quirks (quote styles, line wrapping, etc.) while keeping // all business logic in Go. func normalizeYAMLOutput(goYAML []byte, w io.Writer) error { - // Find the yaml-normalizer.py script - execPath, err := os.Executable() - if err != nil { - return fmt.Errorf("failed to get executable path: %w", err) - } - scriptDir := filepath.Dir(execPath) - normalizerPath := filepath.Join(scriptDir, "..", "yaml-normalizer.py") - - // If running from source (not installed), try the current directory - if _, err := os.Stat(normalizerPath); os.IsNotExist(err) { - wd, _ := os.Getwd() - normalizerPath = filepath.Join(wd, "yaml-normalizer.py") - } + wd, _ := os.Getwd() + normalizerPath := filepath.Join(wd, "bundle_helpers", "yaml-normalizer.py") // Run the normalizer cmd := exec.Command(normalizerPath) From 71806a3ee1b05e214121dda418ebdd0f55b9f7bf Mon Sep 17 00:00:00 2001 From: Marcin Owsiany Date: Tue, 10 Feb 2026 08:17:55 +0100 Subject: [PATCH 11/21] fix comparison --- .github/workflows/test-bundle-helpers.yaml | 29 ++++++++-------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/.github/workflows/test-bundle-helpers.yaml b/.github/workflows/test-bundle-helpers.yaml index 4486fe5f29e8c..73646edc41409 100644 --- a/.github/workflows/test-bundle-helpers.yaml +++ b/.github/workflows/test-bundle-helpers.yaml @@ -143,33 +143,24 @@ jobs: - name: Compare bundle outputs run: | - if [ ! -d /tmp/go/bundle-go ]; then - echo "Go bundle not available yet - skipping comparison" - exit 0 - fi - echo "=== Comparing Python and Go bundle outputs ===" - if diff -r /tmp/python/bundle-python /tmp/go/bundle-go; then + if diff -ruN /tmp/python/bundle-python /tmp/go/bundle-go; then echo "✓ SUCCESS: Bundle outputs are identical" else echo "✗ FAILURE: Bundle outputs differ" - echo "" - echo "Detailed diff of CSV files:" - diff -u /tmp/python/bundle-python/manifests/*.clusterserviceversion.yaml \ - /tmp/go/bundle-go/manifests/*.clusterserviceversion.yaml || true exit 1 fi + echo "Pruning createdAt lines..." + sed -i '/^ createdAt:/d' /tmp/*/build-bundle-*/manifests/rhacs-operator.clusterserviceversion.yaml - if [ -d /tmp/python/build-bundle-python ] && [ -d /tmp/go/build-bundle-go ]; then - echo "" - echo "=== Comparing build/bundle outputs ===" - if diff -r /tmp/python/build-bundle-python /tmp/go/build-bundle-go; then - echo "✓ SUCCESS: Build bundle outputs are identical" - else - echo "✗ FAILURE: Build bundle outputs differ" - exit 1 - fi + echo "" + echo "=== Comparing build/bundle outputs ===" + if diff -ruN /tmp/python/build-bundle-python /tmp/go/build-bundle-go; then + echo "✓ SUCCESS: Build bundle outputs are identical" + else + echo "✗ FAILURE: Build bundle outputs differ" + exit 1 fi status-check: From cacbec6e4c68a15039e0c3aee31005e1dd3f6de1 Mon Sep 17 00:00:00 2001 From: Marcin Owsiany Date: Tue, 10 Feb 2026 08:29:49 +0100 Subject: [PATCH 12/21] improve comparison, fix summary --- .github/workflows/test-bundle-helpers.yaml | 26 +++++++++------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test-bundle-helpers.yaml b/.github/workflows/test-bundle-helpers.yaml index 73646edc41409..c05d195f765b1 100644 --- a/.github/workflows/test-bundle-helpers.yaml +++ b/.github/workflows/test-bundle-helpers.yaml @@ -151,7 +151,8 @@ jobs: echo "✗ FAILURE: Bundle outputs differ" exit 1 fi - echo "Pruning createdAt lines..." + echo "" + echo "=== Pruning createdAt lines..." sed -i '/^ createdAt:/d' /tmp/*/build-bundle-*/manifests/rhacs-operator.clusterserviceversion.yaml echo "" @@ -162,6 +163,10 @@ jobs: echo "✗ FAILURE: Build bundle outputs differ" exit 1 fi + echo "" + echo "=== Listing contents" + find /tmp/python /tmp/go -ls + find /tmp/python /tmp/go -type f | xargs md5sum | sort status-check: name: Bundle Helper Tests Status @@ -179,29 +184,20 @@ jobs: echo "Python tests: $python_result" echo "Go tests: $go_result" echo "Comparison: $compare_result" - echo "E2E test: $e2e_result" - # Python tests must always pass if [ "$python_result" != "success" ]; then echo "✗ Python tests failed" exit 1 fi - # E2E test must pass - if [ "$e2e_result" != "success" ]; then - echo "✗ E2E test failed" + if [ "$go_result" != "success" ]; then + echo "✗ Go tests failed" exit 1 fi - # Go tests and comparison are optional until Go is fully implemented - if [ "$go_result" == "success" ]; then - echo "✓ Go implementation available and tested" - if [ "$compare_result" != "success" ] && [ "$compare_result" != "skipped" ]; then - echo "✗ Go/Python comparison failed" - exit 1 - fi - else - echo "⚠ Go implementation not yet available (expected during migration)" + if [ "$compare_result" != "success" ]; then + echo "✗ Go/Python comparison failed" + exit 1 fi echo "✓ All required tests passed" From 36d1c8f1e6d5723ad771e8c6e6b9ac5bcecca57f Mon Sep 17 00:00:00 2001 From: Marcin Owsiany Date: Tue, 10 Feb 2026 08:45:21 +0100 Subject: [PATCH 13/21] keep linter happy --- .github/workflows/test-bundle-helpers.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-bundle-helpers.yaml b/.github/workflows/test-bundle-helpers.yaml index c05d195f765b1..f8737c022d446 100644 --- a/.github/workflows/test-bundle-helpers.yaml +++ b/.github/workflows/test-bundle-helpers.yaml @@ -166,7 +166,7 @@ jobs: echo "" echo "=== Listing contents" find /tmp/python /tmp/go -ls - find /tmp/python /tmp/go -type f | xargs md5sum | sort + find /tmp/python /tmp/go -type f -print0 | xargs -0 md5sum | sort status-check: name: Bundle Helper Tests Status From 0be9b322da0b0afb9d9c7ebfe9f1c4a28e894bbd Mon Sep 17 00:00:00 2001 From: Marcin Owsiany Date: Tue, 10 Feb 2026 08:53:31 +0100 Subject: [PATCH 14/21] lint --- operator/bundle_helpers/pkg/yamlformat/pyaml_compat.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator/bundle_helpers/pkg/yamlformat/pyaml_compat.go b/operator/bundle_helpers/pkg/yamlformat/pyaml_compat.go index 574b5aae8602d..ca2e9918ab0c8 100644 --- a/operator/bundle_helpers/pkg/yamlformat/pyaml_compat.go +++ b/operator/bundle_helpers/pkg/yamlformat/pyaml_compat.go @@ -10,7 +10,7 @@ import ( // EncodePyYAMLStyle encodes data to YAML matching PyYAML's safe_dump() style. // This includes: // - Single quotes for simple string values -// - Empty string as '' not "" +// - Empty strings using single quotes // - Flow style for arrays/maps where appropriate func EncodePyYAMLStyle(w io.Writer, data interface{}) error { var buf bytes.Buffer From 60adc1a2082f6f915c642e7ddbf62be432448126 Mon Sep 17 00:00:00 2001 From: Marcin Owsiany Date: Tue, 10 Feb 2026 08:56:59 +0100 Subject: [PATCH 15/21] lint --- operator/bundle_helpers/main.go | 8 ++++---- operator/bundle_helpers/pkg/descriptor/sorter.go | 11 ++++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/operator/bundle_helpers/main.go b/operator/bundle_helpers/main.go index 8745603da4dec..c7570a2123409 100644 --- a/operator/bundle_helpers/main.go +++ b/operator/bundle_helpers/main.go @@ -10,9 +10,9 @@ import ( func main() { if len(os.Args) < 2 { fmt.Fprintf(os.Stderr, "Usage: %s [args...]\n", os.Args[0]) - fmt.Fprintf(os.Stderr, "Available commands:\n") - fmt.Fprintf(os.Stderr, " fix-spec-descriptor-order Fix specDescriptor ordering\n") - fmt.Fprintf(os.Stderr, " patch-csv Patch ClusterServiceVersion file (not yet implemented)\n") + fmt.Fprint(os.Stderr, "Available commands:\n") + fmt.Fprint(os.Stderr, " fix-spec-descriptor-order Fix specDescriptor ordering\n") + fmt.Fprint(os.Stderr, " patch-csv Patch ClusterServiceVersion file (not yet implemented)\n") os.Exit(1) } @@ -26,7 +26,7 @@ func main() { os.Exit(1) } case "patch-csv": - fmt.Fprintf(os.Stderr, "patch-csv command not yet implemented\n") + fmt.Fprint(os.Stderr, "patch-csv command not yet implemented\n") os.Exit(1) default: fmt.Fprintf(os.Stderr, "Unknown command: %s\n", command) diff --git a/operator/bundle_helpers/pkg/descriptor/sorter.go b/operator/bundle_helpers/pkg/descriptor/sorter.go index a43e0cbce7a18..e7d76d4141fe4 100644 --- a/operator/bundle_helpers/pkg/descriptor/sorter.go +++ b/operator/bundle_helpers/pkg/descriptor/sorter.go @@ -1,9 +1,10 @@ package descriptor import ( - "fmt" "sort" "strings" + + "github.com/pkg/errors" ) // FixCSVDescriptorsMap processes all CRDs in a CSV and fixes their specDescriptors. @@ -12,17 +13,17 @@ func FixCSVDescriptorsMap(csvDoc map[string]interface{}) error { // Navigate to spec.customresourcedefinitions.owned spec, ok := csvDoc["spec"].(map[string]interface{}) if !ok { - return fmt.Errorf("spec not found or not a map") + return errors.New("spec not found or not a map") } crds, ok := spec["customresourcedefinitions"].(map[string]interface{}) if !ok { - return fmt.Errorf("customresourcedefinitions not found or not a map") + return errors.New("customresourcedefinitions not found or not a map") } owned, ok := crds["owned"].([]interface{}) if !ok { - return fmt.Errorf("owned not found or not a list") + return errors.New("owned not found or not a list") } // Process each CRD @@ -50,7 +51,7 @@ func processSpecDescriptorsMap(crd map[string]interface{}) error { descriptors, ok := descs.([]interface{}) if !ok { - return fmt.Errorf("specDescriptors is not a list") + return errors.New("specDescriptors is not a list") } // Fix descriptor order From a525713cd6a3d771b4a50e04996468674c6de3b8 Mon Sep 17 00:00:00 2001 From: Marcin Owsiany Date: Tue, 10 Feb 2026 09:18:39 +0100 Subject: [PATCH 16/21] try multiple modes --- .github/workflows/test-bundle-helpers.yaml | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/.github/workflows/test-bundle-helpers.yaml b/.github/workflows/test-bundle-helpers.yaml index f8737c022d446..ae284b977f0b8 100644 --- a/.github/workflows/test-bundle-helpers.yaml +++ b/.github/workflows/test-bundle-helpers.yaml @@ -13,8 +13,15 @@ on: - reopened - synchronize + jobs: test-python-implementation: + strategy: + matrix: + related_img_mode: + - konflux + - omit + - downstream name: Test Python Implementation runs-on: ubuntu-latest defaults: @@ -61,6 +68,12 @@ jobs: retention-days: 1 test-go-implementation: + strategy: + matrix: + related_img_mode: + - konflux + - omit + - downstream name: Test Go Implementation runs-on: ubuntu-latest defaults: @@ -116,6 +129,12 @@ jobs: retention-days: 1 compare-implementations: + strategy: + matrix: + related_img_mode: + - konflux + - omit + - downstream name: Compare Python and Go Outputs runs-on: ubuntu-latest needs: [test-python-implementation, test-go-implementation] @@ -169,6 +188,12 @@ jobs: find /tmp/python /tmp/go -type f -print0 | xargs -0 md5sum | sort status-check: + strategy: + matrix: + related_img_mode: + - konflux + - omit + - downstream name: Bundle Helper Tests Status runs-on: ubuntu-latest needs: [test-python-implementation, test-go-implementation, compare-implementations] From b016252f15deaa730c996565cfacfcc71957a151 Mon Sep 17 00:00:00 2001 From: Marcin Owsiany Date: Tue, 10 Feb 2026 09:24:43 +0100 Subject: [PATCH 17/21] pass related images mode --- .github/workflows/test-bundle-helpers.yaml | 2 ++ operator/Makefile | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-bundle-helpers.yaml b/.github/workflows/test-bundle-helpers.yaml index ae284b977f0b8..e353146e73b7e 100644 --- a/.github/workflows/test-bundle-helpers.yaml +++ b/.github/workflows/test-bundle-helpers.yaml @@ -51,6 +51,7 @@ jobs: - name: Generate bundle with Python env: USE_GO_BUNDLE_HELPER: false + RELATED_IMAGES_MODE: ${{ matrix.related_img_mode }} run: | go mod tidy make bundle bundle-post-process @@ -112,6 +113,7 @@ jobs: - name: Generate bundle with Go env: USE_GO_BUNDLE_HELPER: true + RELATED_IMAGES_MODE: ${{ matrix.related_img_mode }} run: | go mod tidy make bundle bundle-post-process diff --git a/operator/Makefile b/operator/Makefile index 1697e63e69643..68e3e29f2a256 100644 --- a/operator/Makefile +++ b/operator/Makefile @@ -472,6 +472,8 @@ bundle: yq manifests kustomize operator-sdk ## Generate bundle manifests and met bundle/manifests/rhacs-operator.clusterserviceversion.yaml $(OPERATOR_SDK) bundle validate ./bundle --select-optional suite=operatorframework +RELATED_IMAGES_MODE ?= omit + .PHONY: bundle-post-process bundle-post-process: test-bundle-helpers operator-sdk ## Post-process CSV file to include correct operator versions, etc. set -euo pipefail ;\ @@ -495,7 +497,7 @@ bundle-post-process: test-bundle-helpers operator-sdk ## Post-process CSV file t --use-version=$(VERSION) \ --first-version=$${first_version} \ --operator-image=$(IMG) \ - --related-images-mode=omit \ + --related-images-mode=$(RELATED_IMAGES_MODE) \ $${unreleased_opt:-} # Check that the resulting bundle still passes validations. $(OPERATOR_SDK) bundle validate ./build/bundle --select-optional suite=operatorframework From b1417b7ca19d1d94199cc022a7b13e398e38bcb4 Mon Sep 17 00:00:00 2001 From: Marcin Owsiany Date: Tue, 10 Feb 2026 09:45:10 +0100 Subject: [PATCH 18/21] do not fail fast, set related image vars --- .github/workflows/test-bundle-helpers.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/test-bundle-helpers.yaml b/.github/workflows/test-bundle-helpers.yaml index e353146e73b7e..d91febd4c7a5b 100644 --- a/.github/workflows/test-bundle-helpers.yaml +++ b/.github/workflows/test-bundle-helpers.yaml @@ -13,6 +13,17 @@ on: - reopened - synchronize +env: + RELATED_IMAGE_MAIN: foo + RELATED_IMAGE_SCANNER: foo + RELATED_IMAGE_SCANNER_SLIM: foo + RELATED_IMAGE_SCANNER_DB: foo + RELATED_IMAGE_SCANNER_DB_SLIM: foo + RELATED_IMAGE_COLLECTOR: foo + RELATED_IMAGE_ROXCTL: foo + RELATED_IMAGE_CENTRAL_DB: foo + RELATED_IMAGE_SCANNER_V4_DB: foo + RELATED_IMAGE_SCANNER_V4: foo jobs: test-python-implementation: @@ -70,6 +81,7 @@ jobs: test-go-implementation: strategy: + fail-fast: false matrix: related_img_mode: - konflux @@ -132,6 +144,7 @@ jobs: compare-implementations: strategy: + fail-fast: false matrix: related_img_mode: - konflux From 7568bb55f156f4c096284dc21d23832442a98913 Mon Sep 17 00:00:00 2001 From: Marcin Owsiany Date: Tue, 10 Feb 2026 10:12:31 +0100 Subject: [PATCH 19/21] matrix artifacts --- .github/workflows/test-bundle-helpers.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-bundle-helpers.yaml b/.github/workflows/test-bundle-helpers.yaml index d91febd4c7a5b..9e33d23dd603f 100644 --- a/.github/workflows/test-bundle-helpers.yaml +++ b/.github/workflows/test-bundle-helpers.yaml @@ -75,7 +75,7 @@ jobs: - name: Upload Python bundle artifacts uses: actions/upload-artifact@v4 with: - name: bundle-python + name: bundle-python-${{ matrix.related_img_mode }} path: /tmp/artifacts/ retention-days: 1 @@ -138,7 +138,7 @@ jobs: - name: Upload Go bundle artifacts uses: actions/upload-artifact@v4 with: - name: bundle-go + name: bundle-go-${{ matrix.related_img_mode }} path: /tmp/artifacts/ retention-days: 1 @@ -160,13 +160,13 @@ jobs: - name: Download Python bundle uses: actions/download-artifact@v4 with: - name: bundle-python + name: bundle-python-${{ matrix.related_img_mode }} path: /tmp/python - name: Download Go bundle uses: actions/download-artifact@v4 with: - name: bundle-go + name: bundle-go-${{ matrix.related_img_mode }} path: /tmp/go continue-on-error: true From 4d52f0e13cd16844a0e4ba15c5aba40ba64be7c0 Mon Sep 17 00:00:00 2001 From: Marcin Owsiany Date: Tue, 10 Feb 2026 11:19:14 +0100 Subject: [PATCH 20/21] set -e and git diff at end --- .github/workflows/test-bundle-helpers.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/test-bundle-helpers.yaml b/.github/workflows/test-bundle-helpers.yaml index 9e33d23dd603f..8b1c200d9beb7 100644 --- a/.github/workflows/test-bundle-helpers.yaml +++ b/.github/workflows/test-bundle-helpers.yaml @@ -64,6 +64,7 @@ jobs: USE_GO_BUNDLE_HELPER: false RELATED_IMAGES_MODE: ${{ matrix.related_img_mode }} run: | + set -euo pipefail go mod tidy make bundle bundle-post-process mkdir -p /tmp/artifacts @@ -71,6 +72,7 @@ jobs: if [ -d build/bundle ]; then cp -r build/bundle /tmp/artifacts/build-bundle-python fi + git diff || true - name: Upload Python bundle artifacts uses: actions/upload-artifact@v4 @@ -127,6 +129,7 @@ jobs: USE_GO_BUNDLE_HELPER: true RELATED_IMAGES_MODE: ${{ matrix.related_img_mode }} run: | + set -euo pipefail go mod tidy make bundle bundle-post-process mkdir -p /tmp/artifacts @@ -134,6 +137,7 @@ jobs: if [ -d build/bundle ]; then cp -r build/bundle /tmp/artifacts/build-bundle-go fi + git diff || true - name: Upload Go bundle artifacts uses: actions/upload-artifact@v4 From 7a223d5ae48aafe945affc225cef714a1690be3d Mon Sep 17 00:00:00 2001 From: Marcin Owsiany Date: Tue, 10 Feb 2026 11:59:56 +0100 Subject: [PATCH 21/21] branding --- .github/workflows/test-bundle-helpers.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test-bundle-helpers.yaml b/.github/workflows/test-bundle-helpers.yaml index 8b1c200d9beb7..43e608d403a66 100644 --- a/.github/workflows/test-bundle-helpers.yaml +++ b/.github/workflows/test-bundle-helpers.yaml @@ -14,6 +14,7 @@ on: - synchronize env: + ROX_PRODUCT_BRANDING: RHACS_BRANDING RELATED_IMAGE_MAIN: foo RELATED_IMAGE_SCANNER: foo RELATED_IMAGE_SCANNER_SLIM: foo