Skip to content

Update release-from-fbc for multiple operator FBC#2451

Draft
rayfordj wants to merge 2 commits intoopenshift-eng:mainfrom
rayfordj:extend_release-from-fbc
Draft

Update release-from-fbc for multiple operator FBC#2451
rayfordj wants to merge 2 commits intoopenshift-eng:mainfrom
rayfordj:extend_release-from-fbc

Conversation

@rayfordj
Copy link
Contributor

@rayfordj rayfordj commented Feb 6, 2026

  • Update release-from-fbc for multiple operator FBC (cluster-logging-operator, loki-operator)
  • Filter external dependency related-images (kube-rbac-proxy)
  • Add retries for errors (502 Bad Gateway)
  • job/build%252Frelease-from-fbc/30

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 6, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign lgarciaaco for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found 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

@rayfordj
Copy link
Contributor Author

rayfordj commented Feb 6, 2026

/hold

@rayfordj rayfordj requested a review from ashwindasr February 6, 2026 00:06
@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 Feb 6, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 6, 2026

Walkthrough

Asynchronous label extraction and retry-enabled image info retrieval were added to the release_from_fbc pipeline. FBC pullspecs are now grouped by operator (__doozer_key) for per-operator related-image validation, with external-NVR detection and new public methods for label extraction and NVR group checks.

Changes

Cohort / File(s) Summary
FBC Pipeline Enhancements
pyartcd/pyartcd/pipelines/release_from_fbc.py
Added extract_fbc_labels(self, fbc_pullspec: str) -> Dict[str, Optional[str]] (async label extraction with 3-retry and fallback), added is_nvr_from_current_group(self, nvr: str) -> bool. Refactored image-info retrieval path to support async + retry, expanded categorize_nvrs() to classify external NVRS, and overhauled validate_fbc_related_images() to group pullspecs by operator (__doozer_key), compute per-operator related-images (allowing single-FBC operator leniency), aggregate unique related images, and emit structured per-operator mismatch reporting. Added asyncio, defaultdict, and tenacity-based retry imports plus logging refinements.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
pyartcd/pyartcd/pipelines/release_from_fbc.py (2)

269-269: Remove redundant exception from logging.exception() call.

logging.exception() automatically includes the exception traceback information, so explicitly including {e} in the message is redundant.

Proposed fix
-            self.logger.exception(f"✗ Failed to get image info for FBC {fbc_pullspec}: {e}")
+            self.logger.exception(f"✗ Failed to get image info for FBC {fbc_pullspec}")

309-309: Remove redundant exception from logging.exception() call.

Same as above—logging.exception() already logs the full traceback.

Proposed fix
-            self.logger.exception(f"✗ Failed to get image info for FBC {fbc_pullspec}: {e}")
+            self.logger.exception(f"✗ Failed to get image info for FBC {fbc_pullspec}")

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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

@rayfordj
Copy link
Contributor Author

rayfordj commented Feb 6, 2026

/label tide/merge-method-squash

@openshift-ci openshift-ci bot added the tide/merge-method-squash Denotes a PR that should be squashed by tide when it merges. label Feb 6, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@pyartcd/pyartcd/pipelines/release_from_fbc.py`:
- Around line 618-631: The current fallback assigns the literal 'unknown' to
fbc_to_doozer_key for any FBC without a __doozer_key or '-fbc-' tag, which
groups unrelated FBCs together; instead, make the "unknown" group unique per FBC
by incorporating a unique identifier derived from the FBC (e.g., the
fbc_pullspec or a sanitized/hash of it) when setting fbc_to_doozer_key for that
branch; update the block that inspects tag_part and the else branch that
currently sets fbc_to_doozer_key[fbc_pullspec] = 'unknown' so it assigns a
per-FBC unique string (referencing fbc_pullspec, tag_part, operator_name, and
fbc_to_doozer_key) to avoid false grouping.
- Around line 327-343: The current startswith check using assembly_prefix can
misclassify versions like "6.30" as matching "6.3"; update the matching logic in
the method using assembly_prefix and nvr_version (variables assembly_prefix,
nvr_version and self.assembly) to be delimiter-aware: when
nvr_version.startswith(assembly_prefix) ensure the match is either the entire
string or the next character after the prefix is a delimiter such as '.' or '-'
(i.e., only accept the prefix if len(nvr_version) == len(assembly_prefix) or
nvr_version[len(assembly_prefix)] in {'.','-'}); keep the existing exact
self.assembly startswith check as a fallback.
- Around line 611-613: The loop mapping FBC pullspecs to doozer keys uses
zip(fbc_pullspecs, label_results) which triggers Ruff B905; change the iteration
in the function that defines fbc_to_doozer_key to use zip(fbc_pullspecs,
label_results, strict=True) so mismatched lengths raise immediately (no extra
imports needed) — update the loop header where fbc_pullspecs and label_results
are zipped (the for fbc_pullspec, labels in ... line) to include strict=True.
🧹 Nitpick comments (1)
pyartcd/pyartcd/pipelines/release_from_fbc.py (1)

641-645: Consider parallelizing related-image extraction across FBCs.

Now that multiple FBCs are supported, sequential await can add noticeable latency. A single gather keeps behavior but speeds the slow path.

♻️ Proposed refactor
         fbc_related_images: Dict[str, List[str]] = {}
-        for fbc_pullspec in fbc_pullspecs:
-            related_nvrs = await extract_nvrs_from_fbc(fbc_pullspec, self.product)
-            fbc_related_images[fbc_pullspec] = sorted(related_nvrs)
+        related_tasks = [extract_nvrs_from_fbc(fbc_pullspec, self.product) for fbc_pullspec in fbc_pullspecs]
+        related_results = await asyncio.gather(*related_tasks)
+        for fbc_pullspec, related_nvrs in zip(fbc_pullspecs, related_results):
+            fbc_related_images[fbc_pullspec] = sorted(related_nvrs)

Comment on lines +327 to +343
# Extract major.minor.patch from assembly (e.g., "6.3.3" -> "6.3")
# to allow matching point releases
assembly_parts = self.assembly.split('.')
if len(assembly_parts) >= 2:
assembly_prefix = f"{assembly_parts[0]}.{assembly_parts[1]}"
else:
assembly_prefix = self.assembly

# Check if NVR version starts with the assembly prefix
# This handles cases like "6.3.3-..." matching assembly "6.3.3"
# and "6.3.3.202602032201..." (metadata containers) matching assembly "6.3.3"
if nvr_version.startswith(assembly_prefix):
return True

# Also check if NVR version starts with full assembly (for exact matches)
if nvr_version.startswith(self.assembly):
return True
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid prefix matching that can misclassify versions like 6.30 as 6.3.

startswith(assembly_prefix) will treat 6.30.x as in-group for 6.3. That can incorrectly keep external dependencies and skew shipment content.

🛠️ Proposed fix (add a delimiter-aware prefix check)
-        if nvr_version.startswith(assembly_prefix):
+        if (
+            nvr_version == assembly_prefix
+            or nvr_version.startswith(f"{assembly_prefix}.")
+            or nvr_version.startswith(f"{assembly_prefix}-")
+        ):
             return True
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Extract major.minor.patch from assembly (e.g., "6.3.3" -> "6.3")
# to allow matching point releases
assembly_parts = self.assembly.split('.')
if len(assembly_parts) >= 2:
assembly_prefix = f"{assembly_parts[0]}.{assembly_parts[1]}"
else:
assembly_prefix = self.assembly
# Check if NVR version starts with the assembly prefix
# This handles cases like "6.3.3-..." matching assembly "6.3.3"
# and "6.3.3.202602032201..." (metadata containers) matching assembly "6.3.3"
if nvr_version.startswith(assembly_prefix):
return True
# Also check if NVR version starts with full assembly (for exact matches)
if nvr_version.startswith(self.assembly):
return True
# Extract major.minor.patch from assembly (e.g., "6.3.3" -> "6.3")
# to allow matching point releases
assembly_parts = self.assembly.split('.')
if len(assembly_parts) >= 2:
assembly_prefix = f"{assembly_parts[0]}.{assembly_parts[1]}"
else:
assembly_prefix = self.assembly
# Check if NVR version starts with the assembly prefix
# This handles cases like "6.3.3-..." matching assembly "6.3.3"
# and "6.3.3.202602032201..." (metadata containers) matching assembly "6.3.3"
if (
nvr_version == assembly_prefix
or nvr_version.startswith(f"{assembly_prefix}.")
or nvr_version.startswith(f"{assembly_prefix}-")
):
return True
# Also check if NVR version starts with full assembly (for exact matches)
if nvr_version.startswith(self.assembly):
return True
🤖 Prompt for AI Agents
In `@pyartcd/pyartcd/pipelines/release_from_fbc.py` around lines 327 - 343, The
current startswith check using assembly_prefix can misclassify versions like
"6.30" as matching "6.3"; update the matching logic in the method using
assembly_prefix and nvr_version (variables assembly_prefix, nvr_version and
self.assembly) to be delimiter-aware: when
nvr_version.startswith(assembly_prefix) ensure the match is either the entire
string or the next character after the prefix is a delimiter such as '.' or '-'
(i.e., only accept the prefix if len(nvr_version) == len(assembly_prefix) or
nvr_version[len(assembly_prefix)] in {'.','-'}); keep the existing exact
self.assembly startswith check as a fallback.

Comment on lines +611 to +613
# Map FBC pullspecs to their doozer_key
fbc_to_doozer_key: Dict[str, str] = {}
for fbc_pullspec, labels in zip(fbc_pullspecs, label_results):
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, locate and verify the file exists
find . -name "release_from_fbc.py" -type f

Repository: openshift-eng/art-tools

Length of output: 114


🏁 Script executed:

# Read the specific lines mentioned to verify the snippet
if [ -f "pyartcd/pyartcd/pipelines/release_from_fbc.py" ]; then
  sed -n '609,615p' pyartcd/pyartcd/pipelines/release_from_fbc.py
fi

Repository: openshift-eng/art-tools

Length of output: 369


🏁 Script executed:

# Check if there's a ruff configuration or rules file
find . -name "ruff.toml" -o -name ".ruff.toml" -o -name "pyproject.toml" | head -5

Repository: openshift-eng/art-tools

Length of output: 95


🌐 Web query:

Ruff B905 rule lint strict zip parameter

💡 Result:

Ruff rule B905 (from flake8-bugbear) flags any call to zip(...) that doesn’t explicitly pass strict= when:

  • there are 2+ iterables, or
  • the call includes any starred argument (*iterable). (docs.astral.sh)

Why it matters

zip truncates silently to the shortest iterable by default, which can hide bugs. Adding strict=True makes it raise ValueError on length mismatch; adding strict=False documents that truncation is intended. (docs.astral.sh)

How to fix

  • If lengths must match:
    for a, b in zip(xs, ys, strict=True):
        ...
  • If truncation is intended:
    for a, b in zip(xs, ys, strict=False):
        ...

Ruff can autofix this rule (it will add an explicit strict=...). (docs.astral.sh)

Citations:


🏁 Script executed:

# Check Python version requirement in pyproject.toml
cat pyproject.toml | grep -A 10 "python"

Repository: openshift-eng/art-tools

Length of output: 1264


🏁 Script executed:

# Also check ruff configuration to understand the rules being enforced
cat ruff.toml

Repository: openshift-eng/art-tools

Length of output: 973


Add explicit strict=True to zip() to satisfy Ruff B905.

This removes the lint warning and adds a guard against accidental length mismatches between fbc_pullspecs and label_results.

Proposed fix
-        for fbc_pullspec, labels in zip(fbc_pullspecs, label_results):
+        for fbc_pullspec, labels in zip(fbc_pullspecs, label_results, strict=True):
🧰 Tools
🪛 Ruff (0.14.14)

[warning] 613-613: zip() without an explicit strict= parameter

Add explicit value for parameter strict=

(B905)

🤖 Prompt for AI Agents
In `@pyartcd/pyartcd/pipelines/release_from_fbc.py` around lines 611 - 613, The
loop mapping FBC pullspecs to doozer keys uses zip(fbc_pullspecs, label_results)
which triggers Ruff B905; change the iteration in the function that defines
fbc_to_doozer_key to use zip(fbc_pullspecs, label_results, strict=True) so
mismatched lengths raise immediately (no extra imports needed) — update the loop
header where fbc_pullspecs and label_results are zipped (the for fbc_pullspec,
labels in ... line) to include strict=True.

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 6, 2026

@rayfordj: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/security fd50bf3 link false /test security

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.

Filter external dependency related-images (kube-rbac-proxy)

Add retries for errors (502 Bad Gateway)
@rayfordj rayfordj force-pushed the extend_release-from-fbc branch from fd50bf3 to 38d11e2 Compare February 11, 2026 21:14
@rayfordj rayfordj removed the request for review from ashwindasr February 11, 2026 21:14
@rayfordj rayfordj marked this pull request as draft February 11, 2026 21:14
@openshift-ci openshift-ci bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Feb 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. tide/merge-method-squash Denotes a PR that should be squashed by tide when it merges.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant