Skip to content

ART-20930: add golang-builder-shipment pipeline - #3231

Closed
lgarciaaco wants to merge 3 commits into
openshift-eng:mainfrom
lgarciaaco:art-20930/golang-builder-shipment-v2
Closed

ART-20930: add golang-builder-shipment pipeline#3231
lgarciaaco wants to merge 3 commits into
openshift-eng:mainfrom
lgarciaaco:art-20930/golang-builder-shipment-v2

Conversation

@lgarciaaco

@lgarciaaco lgarciaaco commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

What

Adds artcd golang-builder-shipment — a new pyartcd pipeline that creates
shipment MRs in ocp-shipment-data for golang builder images, moving them from
silent auto-release to a shipment-gated, ERT-approved delivery path.

Why

Golang builders currently release via base-image-release with no ERT gate.
ART-20920 requires them to go through the new ocp-art-golang-builder-{prod,ec}-rhel9
ReleasePlanAdmissions (already merged in konflux-release-data).

What the pipeline does

  1. Accepts golang RPM NVRs (--golang-nvrs) or direct Konflux image NVRs
  2. Resolves RPM NVRs → Konflux image NVRs via KonfluxDb
  3. Reads software_lifecycle.phase from ocp-build-data to select prod or ec ReleasePlan
  4. Builds a ShipmentConfig YAML using elliott snapshot new
  5. Commits it to ocp-shipment-data and opens a draft GitLab MR for ERT approval

Also removes the now-superseded golang builder auto-release path from
process_release_from_fbc_bugs_cli.

Test

32 unit tests for golang_builder_shipment, 15 for process_release_from_fbc_bugs_cli — all green.

Jira

https://redhat.atlassian.net/browse/ART-20930

Notes

Supersedes #3083 — that PR accumulated unrelated lockfile/OKD/quay/microshift
changes on a long-lived branch. This PR contains only the ART-20930 scope (6 files).

Summary by CodeRabbit

  • New Features

    • Added a Golang builder shipment pipeline supporting image and RPM build identifiers.
    • Automatically resolves release environments, shipment configuration, and corresponding image builds.
    • Creates draft merge requests for shipment data, with support for Jira references, repositories, lifecycle data, and dry runs.
    • Successful Golang image builds can now trigger shipment creation and record the resulting merge request.
  • Tests

    • Added comprehensive coverage for configuration, validation, authentication, error handling, and shipment workflows.

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Aug 5, 2026
@openshift-ci-robot

openshift-ci-robot commented Aug 5, 2026

Copy link
Copy Markdown

@lgarciaaco: This pull request references ART-20930 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 task to target the "5.0.0" version, but no target version was set.

Details

In response to this:

What

Adds artcd golang-builder-shipment — a new pyartcd pipeline that creates
shipment MRs in ocp-shipment-data for golang builder images, moving them from
silent auto-release to a shipment-gated, ERT-approved delivery path.

Why

Golang builders currently release via base-image-release with no ERT gate.
ART-20920 requires them to go through the new ocp-art-golang-builder-{prod,ec}-rhel9
ReleasePlanAdmissions (already merged in konflux-release-data).

What the pipeline does

  1. Accepts golang RPM NVRs (--golang-nvrs) or direct Konflux image NVRs
  2. Resolves RPM NVRs → Konflux image NVRs via KonfluxDb
  3. Reads software_lifecycle.phase from ocp-build-data to select prod or ec ReleasePlan
  4. Builds a ShipmentConfig YAML using elliott snapshot new
  5. Commits it to ocp-shipment-data and opens a draft GitLab MR for ERT approval

Also removes the now-superseded golang builder auto-release path from
process_release_from_fbc_bugs_cli.

Test

32 unit tests for golang_builder_shipment, 15 for process_release_from_fbc_bugs_cli — all green.

Jira

https://redhat.atlassian.net/browse/ART-20930

Notes

Supersedes #3083 — that PR accumulated unrelated lockfile/OKD/quay/microshift
changes on a long-lived branch. This PR contains only the ART-20930 scope (6 files).

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

openshift-ci Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

[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 locriandev 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

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

Adds a golang-builder-shipment CLI pipeline. It resolves Golang NVRs and lifecycle data, creates shipment YAML and draft GitLab merge requests, and triggers shipment creation after successful Golang builder image builds.

Changes

Golang builder shipment

Layer / File(s) Summary
CLI resolution and registration
pyartcd/pyartcd/pipelines/golang_builder_shipment.py, pyartcd/tests/pipelines/test_golang_builder_shipment.py, pyartcd/pyartcd/__main__.py, pyartcd/pyartcd/pipelines/__init__.py
The CLI resolves RPM NVRs, lifecycle environments, and Golang groups. It validates inputs and registers the pipeline exports.
Shipment handler and configuration
doozer/doozerlib/backend/golang_builder_shipment.py, doozer/tests/backend/test_golang_builder_shipment.py, artcommon/artcommonlib/constants.py
GolangBuilderShipmentHandler configures repositories, creates inline or Elliott snapshots, and builds ShipmentConfig data with release plans and metadata.
Repository update and merge request
doozer/doozerlib/backend/golang_builder_shipment.py, doozer/tests/backend/test_golang_builder_shipment.py
The handler writes shipment YAML, commits and pushes changes, and creates draft GitLab merge requests. Dry runs return a placeholder URL.
Successful build integration
doozer/doozerlib/backend/konflux_image_builder.py, doozer/tests/backend/test_golang_builder_shipment.py
Successful Golang builder image builds invoke shipment creation, record the merge request URL, and log shipment failures without failing the build.

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

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant KonfluxBuildRecords
  participant GolangBuilderShipmentHandler
  participant Elliott
  participant ShipmentDataRepository
  participant GitLab
  CLI->>KonfluxBuildRecords: resolve RPM NVRs to image NVRs
  CLI->>GolangBuilderShipmentHandler: create shipment from NVRs
  GolangBuilderShipmentHandler->>Elliott: create snapshot
  Elliott-->>GolangBuilderShipmentHandler: return snapshot YAML
  GolangBuilderShipmentHandler->>ShipmentDataRepository: write and push shipment YAML
  GolangBuilderShipmentHandler->>GitLab: create draft merge request
  GitLab-->>CLI: return merge request URL
Loading

Possibly related PRs

Suggested reviewers: ashwindasr


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
No-Sensitive-Data-In-Logs ❌ Error _setup_repos embeds GITLAB_TOKEN in the remote URL, and GitRepository invokes cmd_assert_async, which logs the full command at exectools.py:919. Use Git credential handling that does not place the token in command arguments, or redact authenticated URLs before command logging; also avoid raw authenticated data_path URLs.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Ai-Attribution ⚠️ Warning The PR context mentions coderabbitai, but all three feature commits lack Assisted-by or Generated-by trailers; no AI Co-Authored-By trailer appears in the feature range. Add the required Assisted-by or Generated-by trailer for the AI-assisted work, and do not use Co-Authored-By for AI tools.
No-Injection-Vectors ❓ Inconclusive Temporary placeholder while investigation continues. Need loader and shell-safety evidence.
✅ Passed checks (7 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the addition of the golang-builder-shipment pipeline, which is the pull request's main change.
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.
No-Weak-Crypto ✅ Passed No weak cryptographic algorithms (MD5, SHA1, DES, RC4, 3DES, Blowfish, ECB), custom crypto implementations, or non-constant-time secret comparisons detected in the PR changes.
Container-Privileges ✅ Passed This PR adds only Python code and constant definitions, not Kubernetes/container manifests. No container privilege settings (privileged, hostPID, hostNetwork, hostIPC, SYS_ADMIN, allowPrivilegeEsca...
No-Hardcoded-Secrets ✅ Passed No hardcoded secrets found. All credentials are obtained from environment variables (GITLAB_TOKEN, QUAY_AUTH_FILE, BUILD_URL). Test files use only mock values. URLs do not embed credentials.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (4)
pyartcd/tests/pipelines/test_golang_builder_shipment.py (3)

613-623: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

TestShipmentFilePath does not test production code.

The test builds expected_prefix and actual from the same local literals and compares them. It always passes and it never calls create_shipment_mr or any function in golang_builder_shipment.py. It gives no protection against a change in the shipment path convention.

Assert the real path instead. Call create_shipment_mr with a mocked repository and check the filepath passed to write_file, including the stream.image.<timestamp>.yaml filename.

🤖 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 `@pyartcd/tests/pipelines/test_golang_builder_shipment.py` around lines 613 -
623, Replace the self-comparison in TestShipmentFilePath.test_path_format with
an invocation of create_shipment_mr using a mocked repository and the relevant
shipment inputs. Capture the filepath supplied to write_file and assert it
matches the expected shipment directory plus stream.image.<timestamp>.yaml
filename, using mocks for nondeterministic values such as the timestamp.

381-394: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add tests for resolve_konflux_image_nvrs.

The suite covers derive_golang_group, resolve_lifecycle_env, and _create_snapshot, but it never exercises resolve_konflux_image_nvrs. That function has two uncovered failure paths: isolate_el_version_in_release returns None, and KonfluxDb.search_builds_by_fields yields no record. Both raise and both are reachable from the CLI --golang-nvrs flow. Patch KonfluxDb and add cases for the success path and for both errors.

🤖 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 `@pyartcd/tests/pipelines/test_golang_builder_shipment.py` around lines 381 -
394, Extend TestCreateSnapshotErrors with tests for resolve_konflux_image_nvrs,
patching KonfluxDb and covering successful NVR resolution plus failures when
isolate_el_version_in_release returns None and when search_builds_by_fields
yields no record. Verify both error paths raise as expected and preserve the CLI
--golang-nvrs behavior.

64-93: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a test for the constructed raw URL.

These tests confirm the parsed phase but never assert the URL passed to session.get. The URL construction at Lines 140-147 of golang_builder_shipment.py contains the branch for github.com and the non-GitHub fallback, and neither branch is covered. Record the call arguments in _FakeSession.get and assert the resulting URL for a github.com base and for a GitLab-style base.

🤖 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 `@pyartcd/tests/pipelines/test_golang_builder_shipment.py` around lines 64 -
93, Extend TestResolveLifecycleEnv to capture session.get arguments through
_FakeSession.get, then add assertions covering both URL-construction branches in
resolve_lifecycle_env: verify the expected raw URL for a github.com base and for
a GitLab-style base. Keep the existing lifecycle phase and error behavior tests
unchanged.
pyartcd/pyartcd/pipelines/golang_builder_shipment.py (1)

79-93: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Anchor the NVR pattern and validate the image name.

The pattern at Line 81 is unanchored and starts with a free v(\d+). Any NVR that contains a substring such as v1.2.3.el9 matches, including images that are not golang builders. The function then returns a golang group derived from unrelated input. Anchor the pattern and require the golang builder name prefix.

The path instructions ask for anchored regexes.

♻️ Proposed change
-        m = re.search(r"v(\d+)\.(\d+)\.\d+.*\.el(\d+)", nvr)
+        m = re.match(rf"^{re.escape(GOLANG_BUILDER_CVE_COMPONENT)}-v(\d+)\.(\d+)\.\d+.*\.el(\d+)$", nvr)
🤖 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 `@pyartcd/pyartcd/pipelines/golang_builder_shipment.py` around lines 79 - 93,
Update the Konflux NVR regex in the loop within the golang group derivation
function to anchor the full string and require the golang builder image-name
prefix before matching its version and EL suffix. Preserve the existing group
construction, while allowing the documented trailing NVR content only after the
validated prefix/version structure.

Source: Path instructions

🤖 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 `@elliott/tests/test_process_release_from_fbc_bugs_cli.py`:
- Around line 189-192: Update the test for the missing-pscomponent path to
assert that “OADP-8888” remains in result.issues.fixed, while preserving the
existing type, CVE, and mock-call assertions.

In `@pyartcd/pyartcd/pipelines/golang_builder_shipment.py`:
- Around line 428-436: The _get_project flow currently uses self.gitlab_url
regardless of the repository URL host. Ensure the GitLab client is created with
the host derived from the shipment repository URL, or validate that both
shipment URLs match self.gitlab_url before client creation, and preserve
project-path lookup for the validated host.
- Around line 140-147: Update the base_url normalization near the raw_url
construction to remove both trailing slashes and a trailing “.git” suffix from
data_path or constants.OCP_BUILD_DATA_URL before building the GitHub or GHE URL.
Preserve the existing provider-specific raw_url logic and branch/group.yml path.
- Around line 499-507: Update the input validation around resolved_nvrs and
golang_nvrs to raise click.UsageError when both positional Konflux image NVRs
and --golang-nvrs are provided. Perform this conflict check before the existing
resolution branch, while preserving normal resolution when only golang_nvrs is
supplied and the existing missing-input error otherwise.
- Around line 108-122: Update the extra_patterns construction in the Go builder
shipment search to escape go_version before embedding it in the NVR pattern,
then anchor the resulting regular expression to the complete version boundary so
v1.25.9 cannot match v1.25.91. Preserve the existing search fields, ordering,
and limit behavior.
- Around line 234-238: Update basic_auth_url to reject any URL whose parsed
scheme is not HTTPS before embedding the token, while preserving the configured
host and explicit port when reconstructing the authenticated URL. Keep the
existing path handling and token placement unchanged.
- Around line 67-70: Update resolve_release_plan to select a ReleasePlan
matching the requested RHEL major version, adding EL-specific entries to
GOLANG_BUILDER_SHIPMENT_RELEASE_PLAN_MAP as needed; alternatively, validate the
input and reject RHEL 8 before shipment creation. Ensure derive_golang_group and
resolve_konflux_image_nvrs cannot lead to an RHEL 8 shipment using an RHEL 9
plan.
- Around line 354-364: Update the `cmd_gather_async` call in the shipment flow
to capture stderr by removing `stderr=None` or passing
`asyncio.subprocess.PIPE`, ensuring the existing `RuntimeError` includes elliott
diagnostics. In the `finally` cleanup for `builds_file`, make `os.unlink`
tolerate an already-missing file by using `missing_ok=True`, preserving any
original exception.

---

Nitpick comments:
In `@pyartcd/pyartcd/pipelines/golang_builder_shipment.py`:
- Around line 79-93: Update the Konflux NVR regex in the loop within the golang
group derivation function to anchor the full string and require the golang
builder image-name prefix before matching its version and EL suffix. Preserve
the existing group construction, while allowing the documented trailing NVR
content only after the validated prefix/version structure.

In `@pyartcd/tests/pipelines/test_golang_builder_shipment.py`:
- Around line 613-623: Replace the self-comparison in
TestShipmentFilePath.test_path_format with an invocation of create_shipment_mr
using a mocked repository and the relevant shipment inputs. Capture the filepath
supplied to write_file and assert it matches the expected shipment directory
plus stream.image.<timestamp>.yaml filename, using mocks for nondeterministic
values such as the timestamp.
- Around line 381-394: Extend TestCreateSnapshotErrors with tests for
resolve_konflux_image_nvrs, patching KonfluxDb and covering successful NVR
resolution plus failures when isolate_el_version_in_release returns None and
when search_builds_by_fields yields no record. Verify both error paths raise as
expected and preserve the CLI --golang-nvrs behavior.
- Around line 64-93: Extend TestResolveLifecycleEnv to capture session.get
arguments through _FakeSession.get, then add assertions covering both
URL-construction branches in resolve_lifecycle_env: verify the expected raw URL
for a github.com base and for a GitLab-style base. Keep the existing lifecycle
phase and error behavior tests unchanged.
🪄 Autofix

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: openshift-eng/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: d179fa10-5d2b-47e9-a4f0-93c73d268c46

📥 Commits

Reviewing files that changed from the base of the PR and between a88c697 and d6ab558.

📒 Files selected for processing (6)
  • elliott/elliottlib/cli/process_release_from_fbc_bugs_cli.py
  • elliott/tests/test_process_release_from_fbc_bugs_cli.py
  • pyartcd/pyartcd/__main__.py
  • pyartcd/pyartcd/pipelines/__init__.py
  • pyartcd/pyartcd/pipelines/golang_builder_shipment.py
  • pyartcd/tests/pipelines/test_golang_builder_shipment.py

Comment on lines +189 to +192
self.assertEqual(result.type, "RHBA")
self.assertIsNone(result.cves)
mock_get_delivery.assert_not_called()
mock_get_konflux.assert_not_called()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert JIRA retention for the missing-pscomponent path.

This test does not verify that OADP-8888 remains in result.issues.fixed. A regression that drops the JIRA before this skip path would still pass.

Proposed test update
         self.assertEqual(result.type, "RHBA")
         self.assertIsNone(result.cves)
+        fixed_ids = [issue.id for issue in result.issues.fixed]
+        self.assertIn("OADP-8888", fixed_ids)
         mock_get_delivery.assert_not_called()
         mock_get_konflux.assert_not_called()
📝 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
self.assertEqual(result.type, "RHBA")
self.assertIsNone(result.cves)
mock_get_delivery.assert_not_called()
mock_get_konflux.assert_not_called()
self.assertEqual(result.type, "RHBA")
self.assertIsNone(result.cves)
fixed_ids = [issue.id for issue in result.issues.fixed]
self.assertIn("OADP-8888", fixed_ids)
mock_get_delivery.assert_not_called()
mock_get_konflux.assert_not_called()
🤖 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 `@elliott/tests/test_process_release_from_fbc_bugs_cli.py` around lines 189 -
192, Update the test for the missing-pscomponent path to assert that “OADP-8888”
remains in result.issues.fixed, while preserving the existing type, CVE, and
mock-call assertions.

Comment thread pyartcd/pyartcd/pipelines/golang_builder_shipment.py Outdated
Comment on lines +108 to +122
extra_patterns = {"nvr": f"{GOLANG_BUILDER_CVE_COMPONENT}-v{go_version}"}
record = await anext(
db.search_builds_by_fields(
where={
"name": GOLANG_BUILDER_IMAGE_NAME,
"el_target": f"el{el_v}",
"artifact_type": str(ArtifactType.IMAGE),
"outcome": str(KonfluxBuildOutcome.SUCCESS),
"engine": str(Engine.KONFLUX),
},
extra_patterns=extra_patterns,
limit=1,
),
None,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect search_builds_by_fields signature, default ordering, and extra_patterns semantics.
fd -t f 'konflux_db.py' | xargs -r -I{} ast-grep outline {} --items all
fd -t f 'konflux_db.py' | xargs -r rg -n -C 12 'def search_builds_by_fields|extra_patterns|order_by|ORDER BY|limit'

Repository: openshift-eng/art-tools

Length of output: 50379


🏁 Script executed:

#!/bin/bash
set -e
file=$(fd -t f 'konflux_db.py' | head -n 1)
sed -n '751,845p' "$file"
printf '\n--- caller and version sources ---\n'
rg -n -C 10 'resolve_konflux_image_nvrs|GOLANG_BUILDER_CVE_COMPONENT|go_version|extra_patterns=\{"nvr"' pyartcd artcommon
printf '\n--- regex behavior ---\n'
python3 - <<'PY'
import re
pattern = "openshift-golang-builder-container-v1.25.9"
values = [
    "openshift-golang-builder-container-v1.25.9-123",
    "openshift-golang-builder-container-v1.25.91-123",
    "openshift-golang-builder-container-v1.25.90-123",
    "openshift-golang-builder-container-v1.25.9",
]
for value in values:
    print(value, bool(re.search(pattern, value)))
PY

Repository: openshift-eng/art-tools

Length of output: 50380


Anchor and escape the Go version pattern.

search_builds_by_fields already orders by start_time DESC. However, REGEXP_CONTAINS treats the current pattern as an unanchored regular expression, so v1.25.9 can match v1.25.91. Escape go_version and anchor the pattern to the NVR version boundary.

🤖 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 `@pyartcd/pyartcd/pipelines/golang_builder_shipment.py` around lines 108 - 122,
Update the extra_patterns construction in the Go builder shipment search to
escape go_version before embedding it in the NVR pattern, then anchor the
resulting regular expression to the complete version boundary so v1.25.9 cannot
match v1.25.91. Preserve the existing search fields, ordering, and limit
behavior.

Comment thread pyartcd/pyartcd/pipelines/golang_builder_shipment.py
Comment thread pyartcd/pyartcd/pipelines/golang_builder_shipment.py Outdated
Comment on lines +354 to +364
quay_auth_file = os.getenv("QUAY_AUTH_FILE")
if quay_auth_file:
cmd.append(f"--pull-secret={quay_auth_file}")

rc, stdout, stderr = await exectools.cmd_gather_async(cmd, stderr=None, check=False)
if rc != 0:
raise RuntimeError(f"elliott snapshot new failed (rc={rc}): {stderr or stdout}")
if stdout:
_LOGGER.info("elliott snapshot new output:\n%s", stdout)
finally:
os.unlink(builds_file)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect cmd_gather_async to confirm what stderr=None means for the returned tuple.
fd -t f 'exectools.py' | xargs -r rg -n -C 25 'async def cmd_gather_async'

Repository: openshift-eng/art-tools

Length of output: 4186


🏁 Script executed:

#!/bin/bash
sed -n '720,805p' artcommon/artcommonlib/exectools.py
printf '\n--- target imports and runtime metadata ---\n'
sed -n '1,45p' pyartcd/pyartcd/pipelines/golang_builder_shipment.py
rg -n 'python_requires|requires-python|Python|python_version|missing_ok' pyproject.toml setup.cfg setup.py tox.ini .github pyartcd artcommon 2>/dev/null | head -80

Repository: openshift-eng/art-tools

Length of output: 7804


🏁 Script executed:

#!/bin/bash
printf '%s\n' '--- target method ---'
sed -n '320,385p' pyartcd/pyartcd/pipelines/golang_builder_shipment.py

printf '%s\n' '--- related stderr usage ---'
rg -n -C 4 'cmd_gather_async\(.*|stderr=None|elliott snapshot new|os\.unlink\(builds_file\)' pyartcd artcommon | head -240

printf '%s\n' '--- tests for the target pipeline ---'
fd -i -t f 'golang_builder_shipment' pyartcd | xargs -r -n1 sh -c 'echo "--- $0"; rg -n -C 5 "_create_snapshot|cmd_gather_async|RuntimeError|NamedTemporaryFile" "$0"'

Repository: openshift-eng/art-tools

Length of output: 28515


🏁 Script executed:

#!/bin/bash
python3 - <<'PY'
import asyncio
import os
import sys
import tempfile
from pathlib import Path

async def run(**kwargs):
    proc = await asyncio.create_subprocess_exec(
        sys.executable, "-c",
        "import sys; print('out'); print('err', file=sys.stderr); sys.exit(7)",
        stdout=asyncio.subprocess.PIPE,
        **kwargs,
    )
    out, err = await proc.communicate()
    return proc.returncode, out.decode() if out else "", err.decode() if err else ""

async def main():
    inherited_rc, inherited_out, inherited_err = await run(stderr=None)
    captured_rc, captured_out, captured_err = await run(stderr=asyncio.subprocess.PIPE)
    print("stderr=None:", inherited_rc, repr(inherited_out), repr(inherited_err))
    print("stderr=PIPE:", captured_rc, repr(captured_out), repr(captured_err))

    path = tempfile.mktemp()
    try:
        open(path, "w").close()
        try:
            try:
                raise RuntimeError("command failure")
            finally:
                os.unlink(path)
                raise OSError("cleanup failure")
        except BaseException as exc:
            print("finally exception:", type(exc).__name__, str(exc))
    finally:
        try:
            Path(path).unlink(missing_ok=True)
        except OSError:
            pass

asyncio.run(main())
PY

Repository: openshift-eng/art-tools

Length of output: 265


Capture elliott diagnostics and make cleanup tolerant of missing files.

  • Remove stderr=None or pass asyncio.subprocess.PIPE; otherwise cmd_gather_async returns an empty stderr, so the failure message omits elliott diagnostics.
  • Use missing_ok=True so a missing temporary file does not replace the original exception.
Proposed cleanup guard
         finally:
-            os.unlink(builds_file)
+            Path(builds_file).unlink(missing_ok=True)
🤖 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 `@pyartcd/pyartcd/pipelines/golang_builder_shipment.py` around lines 354 - 364,
Update the `cmd_gather_async` call in the shipment flow to capture stderr by
removing `stderr=None` or passing `asyncio.subprocess.PIPE`, ensuring the
existing `RuntimeError` includes elliott diagnostics. In the `finally` cleanup
for `builds_file`, make `os.unlink` tolerate an already-missing file by using
`missing_ok=True`, preserving any original exception.

Comment thread pyartcd/pyartcd/pipelines/golang_builder_shipment.py Outdated
Comment on lines +499 to +507
if not resolved_nvrs and golang_nvrs:
rpm_nvrs = [n.strip() for n in golang_nvrs.replace(",", " ").split() if n.strip()]
_LOGGER.info("Resolving golang RPM NVRs to Konflux image NVRs: %s", rpm_nvrs)
resolved_nvrs = await resolve_konflux_image_nvrs(rpm_nvrs)
if not golang_group:
golang_group = derive_golang_group(rpm_nvrs)

if not resolved_nvrs:
raise click.UsageError("Provide Konflux image NVRs as arguments or --golang-nvrs with golang RPM NVRs")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

--golang-nvrs is ignored without notice.

The condition at Line 499 requires not resolved_nvrs. If the user supplies positional NVRs and --golang-nvrs together, the pipeline silently drops --golang-nvrs. Raise a click.UsageError when both are set, so the user learns the inputs conflict.

🐛 Proposed fix
     resolved_nvrs: List[str] = list(nvrs)
 
+    if resolved_nvrs and golang_nvrs:
+        raise click.UsageError("Provide either positional Konflux image NVRs or --golang-nvrs, not both")
+
     if not resolved_nvrs and golang_nvrs:
🤖 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 `@pyartcd/pyartcd/pipelines/golang_builder_shipment.py` around lines 499 - 507,
Update the input validation around resolved_nvrs and golang_nvrs to raise
click.UsageError when both positional Konflux image NVRs and --golang-nvrs are
provided. Perform this conflict check before the existing resolution branch,
while preserving normal resolution when only golang_nvrs is supplied and the
existing missing-input error otherwise.

Adds artcd golang-builder-shipment command that creates shipment MRs in
ocp-shipment-data for golang builder images. Moves golang builders from
silent auto-release to a shipment-gated ERT-approved delivery path targeting
the new ocp-art-golang-builder-{prod,ec}-rhel9 ReleasePlans.

- Accepts golang RPM NVRs or Konflux image NVRs
- Auto-detects prod/ec ReleasePlan from ocp-build-data software_lifecycle.phase
- Resolves RPM NVRs to Konflux image NVRs via KonfluxDb
- Opens a draft MR in ocp-shipment-data with YAML shipment config
- Removes auto-release path for golang builders from process_release_from_fbc_bugs

Jira: ART-20930
@lgarciaaco
lgarciaaco force-pushed the art-20930/golang-builder-shipment-v2 branch from d6ab558 to 01a8dad Compare August 5, 2026 10:38
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
pyartcd/tests/pipelines/test_golang_builder_shipment.py (1)

50-93: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the requested URL in _FakeSession.get.

_FakeSession.get discards its arguments. No test checks the raw_url that resolve_lifecycle_env builds. The branch selection at Lines 144-147 of golang_builder_shipment.py and the .git normalization gap are therefore untested.

Record the URL and assert it in at least one test.

💚 Proposed test change
 class _FakeSession:
     def __init__(self, response):
         self._response = response
+        self.requested_urls = []
 
     def get(self, *args, **kwargs):
+        if args:
+            self.requested_urls.append(args[0])
         return self._response
🤖 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 `@pyartcd/tests/pipelines/test_golang_builder_shipment.py` around lines 50 -
93, Update the test helper _FakeSession.get to record the requested URL, then
assert the expected normalized raw_url in at least one TestResolveLifecycleEnv
test. Ensure coverage verifies the resolve_lifecycle_env branch that builds the
URL and removes the “.git” suffix.
pyartcd/pyartcd/pipelines/golang_builder_shipment.py (1)

388-391: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Avoid reaching into GitRepository._directory.

The pipeline uses the private attribute _directory to create the target directory, then uses the public write_file for the file itself. This couples the pipeline to GitRepository internals.

Expose a public accessor for the repository directory on GitRepository, or let write_file create parent directories.

🤖 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 `@pyartcd/pyartcd/pipelines/golang_builder_shipment.py` around lines 388 - 391,
Remove the direct use of the private GitRepository._directory in the
target-directory setup around the shipment path construction. Expose and use a
public repository-directory accessor on GitRepository, or update write_file to
create parent directories and rely on it, while preserving the existing relative
target path and directory creation behavior.
🤖 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 `@pyartcd/pyartcd/pipelines/golang_builder_shipment.py`:
- Around line 210-217: Update the initialization of shipment_data_repo_push_url
alongside shipment_data_repo_pull_url so the shipment_data_repo_url CLI override
applies to both repository URLs, preserving config and template fallbacks when
the override is absent. Keep the existing MR target and branch push behavior
consistent with the same repository.
- Around line 79-93: Update the NVR group-derivation loop to evaluate every NVR
instead of returning on the first match, collecting each successfully derived
group from both the Konflux regex and RPM parsing paths. After processing all
NVRs, return the common group only when all derived values match; raise
ValueError when any NVR derives a different group, while preserving the existing
failure for NVRs that cannot be parsed.

In `@pyartcd/tests/pipelines/test_golang_builder_shipment.py`:
- Around line 613-623: Update TestShipmentFilePath.test_path_format to exercise
create_shipment_mr (or the relevant path-producing helper) and assert against
its returned/generated shipment path, using the expected convention
independently of the implementation. Remove the test if that function cannot be
invoked meaningfully; do not compare two paths constructed from the same
literals.

---

Nitpick comments:
In `@pyartcd/pyartcd/pipelines/golang_builder_shipment.py`:
- Around line 388-391: Remove the direct use of the private
GitRepository._directory in the target-directory setup around the shipment path
construction. Expose and use a public repository-directory accessor on
GitRepository, or update write_file to create parent directories and rely on it,
while preserving the existing relative target path and directory creation
behavior.

In `@pyartcd/tests/pipelines/test_golang_builder_shipment.py`:
- Around line 50-93: Update the test helper _FakeSession.get to record the
requested URL, then assert the expected normalized raw_url in at least one
TestResolveLifecycleEnv test. Ensure coverage verifies the resolve_lifecycle_env
branch that builds the URL and removes the “.git” suffix.
🪄 Autofix

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: openshift-eng/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ba5d79d6-afba-43d0-bbf7-31581af21280

📥 Commits

Reviewing files that changed from the base of the PR and between a88c697 and 01a8dad.

📒 Files selected for processing (4)
  • pyartcd/pyartcd/__main__.py
  • pyartcd/pyartcd/pipelines/__init__.py
  • pyartcd/pyartcd/pipelines/golang_builder_shipment.py
  • pyartcd/tests/pipelines/test_golang_builder_shipment.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • pyartcd/pyartcd/pipelines/init.py
  • pyartcd/pyartcd/main.py

Comment thread pyartcd/pyartcd/pipelines/golang_builder_shipment.py Outdated
Comment on lines +210 to +217
self.shipment_data_repo_pull_url = (
shipment_data_repo_url
or runtime.config.get("shipment_config", {}).get("shipment_data_url")
or SHIPMENT_DATA_URL_TEMPLATE
)
self.shipment_data_repo_push_url = (
runtime.config.get("shipment_config", {}).get("shipment_data_push_url") or SHIPMENT_DATA_URL_TEMPLATE
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

--shipment-data-repo-url changes only the pull URL.

shipment_data_repo_url feeds shipment_data_repo_pull_url. shipment_data_repo_push_url ignores it and falls back to config or SHIPMENT_DATA_URL_TEMPLATE. The CLI help states the option overrides the "ocp-shipment-data repo URL".

An operator who sets the option gets the override as the MR target project at Line 436, while the branch is still pushed to the default repository at Line 435. The MR then points at a source branch that does not exist in the source project.

Apply the override to both URLs, or rename the option and document that it sets the target repository only.

🤖 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 `@pyartcd/pyartcd/pipelines/golang_builder_shipment.py` around lines 210 - 217,
Update the initialization of shipment_data_repo_push_url alongside
shipment_data_repo_pull_url so the shipment_data_repo_url CLI override applies
to both repository URLs, preserving config and template fallbacks when the
override is absent. Keep the existing MR target and branch push behavior
consistent with the same repository.

Comment thread pyartcd/tests/pipelines/test_golang_builder_shipment.py Outdated
@openshift-ci

openshift-ci Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

@lgarciaaco: The following tests 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 01a8dad link false /test security
ci/prow/art-pre-commit-check 01a8dad link false /test art-pre-commit-check

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.

Move golang builder shipment logic to doozer backend handler (like
BaseImageHandler). Trigger inline from konflux_image_builder for golang
builders (non-fatal). pyartcd CLI becomes thin wrapper.
@lgarciaaco
lgarciaaco force-pushed the art-20930/golang-builder-shipment-v2 branch from d96f246 to 71f8baa Compare August 5, 2026 14:27

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (3)
doozer/tests/backend/test_golang_builder_shipment.py (1)

76-79: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the unused elliott mocks from these two tests.

test_build_shipment_config_prod and test_build_shipment_config_ec call _build_inline_snapshot and _build_shipment_config only. Neither test reaches cmd_gather_async or os.unlink, so the patches and the YAML fixtures are dead setup that suggests the wrong code path is under test.

Also applies to: 125-128

🤖 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 `@doozer/tests/backend/test_golang_builder_shipment.py` around lines 76 - 79,
Remove the unused patches from test_build_shipment_config_prod and
test_build_shipment_config_ec by updating the test decorators around
_build_inline_snapshot and _build_shipment_config. The cmd_gather_async and
os.unlink mocks are never exercised in these tests, so drop those patch lines
and keep only the setup that matches the code path actually under test,
including the existing YAML fixture usage if it is still needed for the config
assertions.
pyartcd/pyartcd/pipelines/golang_builder_shipment.py (1)

186-196: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the unreachable --ocp-version guard.

Lines 186-187 either set golang_group or raise from derive_golang_group. golang_group is therefore always truthy at Line 190, so the click.UsageError at Line 191 cannot run.

♻️ Proposed cleanup
     if not ocp_version:
-        if not golang_group:
-            raise click.UsageError("--ocp-version is required when --golang-group cannot be derived")
         _LOGGER.warning(
🤖 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 `@pyartcd/pyartcd/pipelines/golang_builder_shipment.py` around lines 186 - 196,
Remove the redundant `if not golang_group` check and its `click.UsageError` from
the `if not ocp_version` branch in the Golang shipment pipeline;
`derive_golang_group` already establishes or raises for the missing group, so
retain only the warning and prod-environment defaulting behavior.
pyartcd/tests/pipelines/test_golang_builder_shipment.py (1)

13-28: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

TestResolveReleasePlan duplicates the doozer test suite.

resolve_release_plan and GOLANG_BUILDER_SHIPMENT_RELEASE_PLAN_MAP now live in doozerlib.backend.golang_builder_shipment, and doozer/tests/backend/test_golang_builder_shipment.py Lines 16-31 already assert the same behavior with the same class name. Keep the coverage in the doozer suite and remove it here, so the plan names only need one update.

🤖 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 `@pyartcd/tests/pipelines/test_golang_builder_shipment.py` around lines 13 -
28, Remove the duplicate TestResolveReleasePlan test class and its
resolve_release_plan/map assertions from this test module. Retain the coverage
in doozer/tests/backend/test_golang_builder_shipment.py, where
GolangBuilderShipmentHandler and GOLANG_BUILDER_SHIPMENT_RELEASE_PLAN_MAP are
defined and already tested.
🤖 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 `@doozer/doozerlib/backend/golang_builder_shipment.py`:
- Around line 53-67: Update derive_golang_group to derive and collect a group
for every NVR instead of returning on the first match, then raise ValueError
when the collected groups are not identical; return the shared group only when
all NVRs agree, preserving the existing error for NVRs that cannot derive a
group.
- Around line 358-364: Update the elliott invocation in
golang_builder_shipment.py around the cmd_gather_async call so stderr is
captured and included in the RuntimeError when rc is nonzero, falling back to
stdout only if stderr is empty. Also make the finally block that unlinks
builds_file tolerate FileNotFoundError so a missing temporary file does not mask
the original failure.
- Around line 47-50: Update GOLANG_BUILDER_SHIPMENT_RELEASE_PLAN_MAP and
resolve_release_plan so the selected ReleasePlan reflects the EL version
returned by derive_golang_group, preventing el8 NVRs from using the existing
rhel9 plans; alternatively, validate and reject non-RHEL-9 input before creating
the shipment.
- Around line 436-445: Update the GitLab client setup around _get_project to
ensure repository URL hosts cannot be silently discarded. Either derive the API
base URL from the push repository URL before constructing gl, or validate that
both push and pull URL hosts match self.gitlab_url and reject mismatches before
project lookup.
- Around line 303-306: Update the Environments construction to assign a
stage-specific ReleasePlan to environments.stage instead of reusing
release_plan, while retaining the production ReleasePlan for environments.prod.
Ensure release new environment selection passes the correct stage or production
plan through shipment.environments, and add coverage for both environment
selections.

In `@doozer/tests/backend/test_golang_builder_shipment.py`:
- Around line 466-474: Update TestShipmentFilePath.test_path_format so it
verifies the path produced by _create_shipment_mr via
shipment_data_repo.write_file instead of comparing two Path values built from
the same literals. Use the visible _create_shipment_mr and write_file symbols as
the assertion target, or remove this test if it cannot observe the generated
shipment path layout.
- Around line 6-13: Reorder the imports in the test module so the
doozerlib.backend.golang_builder_shipment import appears before
doozerlib.constants, matching Ruff/isort ordering while preserving all imported
symbols.

In `@pyartcd/pyartcd/pipelines/golang_builder_shipment.py`:
- Around line 200-212: Ensure GolangBuilderShipmentHandler receives a runtime
group that resolves to the OCP version when ocp_version is omitted, rather than
golang_group. Update _CliRuntimeAdapter construction or its group assignment to
use the OCP group name, or pass the established explicit placeholder through the
handler while preserving explicit --ocp-version behavior.

---

Nitpick comments:
In `@doozer/tests/backend/test_golang_builder_shipment.py`:
- Around line 76-79: Remove the unused patches from
test_build_shipment_config_prod and test_build_shipment_config_ec by updating
the test decorators around _build_inline_snapshot and _build_shipment_config.
The cmd_gather_async and os.unlink mocks are never exercised in these tests, so
drop those patch lines and keep only the setup that matches the code path
actually under test, including the existing YAML fixture usage if it is still
needed for the config assertions.

In `@pyartcd/pyartcd/pipelines/golang_builder_shipment.py`:
- Around line 186-196: Remove the redundant `if not golang_group` check and its
`click.UsageError` from the `if not ocp_version` branch in the Golang shipment
pipeline; `derive_golang_group` already establishes or raises for the missing
group, so retain only the warning and prod-environment defaulting behavior.

In `@pyartcd/tests/pipelines/test_golang_builder_shipment.py`:
- Around line 13-28: Remove the duplicate TestResolveReleasePlan test class and
its resolve_release_plan/map assertions from this test module. Retain the
coverage in doozer/tests/backend/test_golang_builder_shipment.py, where
GolangBuilderShipmentHandler and GOLANG_BUILDER_SHIPMENT_RELEASE_PLAN_MAP are
defined and already tested.
🪄 Autofix

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: openshift-eng/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: d9a4a064-7eef-47d3-9e7b-96db6eb84d3f

📥 Commits

Reviewing files that changed from the base of the PR and between 01a8dad and 71f8baa.

📒 Files selected for processing (5)
  • doozer/doozerlib/backend/golang_builder_shipment.py
  • doozer/doozerlib/backend/konflux_image_builder.py
  • doozer/tests/backend/test_golang_builder_shipment.py
  • pyartcd/pyartcd/pipelines/golang_builder_shipment.py
  • pyartcd/tests/pipelines/test_golang_builder_shipment.py

Comment on lines +47 to +50
GOLANG_BUILDER_SHIPMENT_RELEASE_PLAN_MAP = {
"prod": "ocp-art-golang-builder-prod-rhel9",
"ec": "ocp-art-golang-builder-ec-rhel9",
}

Copy link
Copy Markdown
Contributor

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

RHEL 8 shipments get an RHEL 9 ReleasePlan.

derive_golang_group at Lines 53-67 accepts el8 NVRs and produces groups such as rhel-8-golang-1.25. Both entries in GOLANG_BUILDER_SHIPMENT_RELEASE_PLAN_MAP end with -rhel9, and resolve_release_plan only keys on the environment. An RHEL 8 shipment therefore targets the RHEL 9 ReleasePlan.

Key the plan on the EL version as well, or reject non-RHEL-9 input before the shipment is created.

🤖 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 `@doozer/doozerlib/backend/golang_builder_shipment.py` around lines 47 - 50,
Update GOLANG_BUILDER_SHIPMENT_RELEASE_PLAN_MAP and resolve_release_plan so the
selected ReleasePlan reflects the EL version returned by derive_golang_group,
preventing el8 NVRs from using the existing rhel9 plans; alternatively, validate
and reject non-RHEL-9 input before creating the shipment.

Comment on lines +53 to +67
def derive_golang_group(nvrs: List[str]) -> str:
"""Derive the ocp-build-data golang group from NVR patterns."""
for nvr in nvrs:
m = re.search(r"v(\d+)\.(\d+)\.\d+.*\.el(\d+)", nvr)
if m:
return f"rhel-{m.group(3)}-golang-{m.group(1)}.{m.group(2)}"

parsed = parse_nvr(nvr)
if parsed["name"] == "golang":
major_minor = ".".join(parsed["version"].split(".")[:2])
el_v = isolate_el_version_in_release(parsed["release"])
if el_v is not None:
return f"rhel-{el_v}-golang-{major_minor}"

raise ValueError(f"Cannot derive golang group from NVRs: {nvrs}")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Validate that all NVRs derive the same golang group.

The loop returns on the first NVR that matches. create_shipment_from_nvrs sorts nvrs first, so mixed EL versions or mixed Go minor versions silently produce a shipment labeled with the group of one NVR, while every NVR still enters that shipment and target directory.

Collect the derived group for every NVR and raise when the values disagree.

🤖 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 `@doozer/doozerlib/backend/golang_builder_shipment.py` around lines 53 - 67,
Update derive_golang_group to derive and collect a group for every NVR instead
of returning on the first match, then raise ValueError when the collected groups
are not identical; return the shared group only when all NVRs agree, preserving
the existing error for NVRs that cannot derive a group.

Comment on lines +303 to +306
environments = Environments(
stage=ShipmentEnv(releasePlan=release_plan),
prod=ShipmentEnv(releasePlan=release_plan),
)

Copy link
Copy Markdown
Contributor

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
echo '--- how other shipment builders populate Environments ---'
rg -n -C 6 'Environments\(' --type=py .
echo '--- stage release plan naming ---'
rg -n 'releasePlan|release_plan.*stage|stage.*release_plan' --type=py elliott doozer pyartcd | head -60

Repository: openshift-eng/art-tools

Length of output: 24882


🏁 Script executed:

#!/bin/bash
set -eu
echo '--- target builder and tests ---'
sed -n '250,330p' doozer/doozerlib/backend/golang_builder_shipment.py
sed -n '80,185p' doozer/tests/backend/test_golang_builder_shipment.py

echo '--- environment selection and release-plan use ---'
sed -n '270,375p' elliott/elliottlib/cli/konflux_release_cli.py
sed -n '35,110p' elliott/elliottlib/cli/shipment_cli.py

echo '--- golang-builder configuration references ---'
rg -n -C 4 'golang-builder|ocp-art-golang-builder|releasePlan' --glob 'config.y*ml' --glob '*.yaml' --glob '*.yml' .

Repository: openshift-eng/art-tools

Length of output: 19002


🏁 Script executed:

#!/bin/bash
set -eu
echo '--- all builder call sites and release-plan construction ---'
rg -n -C 10 '_build_shipment_config|release_plan|release-plan|releasePlan' doozer/doozerlib/backend/golang_builder_shipment.py
echo '--- handler command flow and environment selection ---'
sed -n '1,260p' doozer/doozerlib/backend/golang_builder_shipment.py
echo '--- tests covering stage/prod or env behavior ---'
rg -n -C 8 'env=|stage|prod|release_plan|releasePlan' doozer/tests/backend/test_golang_builder_shipment.py
echo '--- shipment release command callers ---'
rg -n -C 8 'get_release_config|new_release|release_env|shipment.environments' elliott/elliottlib pyartcd/pyartcd doozer/doozerlib --glob '*.py'

Repository: openshift-eng/art-tools

Length of output: 50379


🏁 Script executed:

#!/bin/bash
set -eu
echo '--- repository-wide exact release-plan references ---'
rg -n -F 'ocp-art-golang-builder-prod-rhel9' . || true
rg -n -F 'ocp-art-golang-builder-ec-rhel9' . || true

echo '--- release command environment options and shipment path handling ---'
sed -n '400,485p' elliott/elliottlib/cli/konflux_release_cli.py
rg -n -C 8 'shipment/|environments.*stage|environments.*prod|releasePlan' elliott/elliottlib pyartcd/pyartcd doozer/doozerlib --glob '*.py' | head -240

echo '--- model documentation and environment semantics ---'
sed -n '90,155p' elliott/elliottlib/shipment_model.py
rg -n -C 5 'stage.*prod|prod.*stage|release plan|ReleasePlan' README.md elliott pyartcd doozer --glob '*.md' --glob '*.py' | head -240

Repository: openshift-eng/art-tools

Length of output: 48241


🌐 Web query:

"ocp-art-golang-builder-prod-rhel9" "ocp-art-golang-builder-ec-rhel9"

💡 Result:

The identifiers ocp-art-golang-builder-prod-rhel9 and ocp-art-golang-builder-ec-rhel9 refer to specific release plans or target configurations used by the Red Hat OpenShift Automated Release Team (ART) in their internal build pipelines [1]. These identifiers are associated with the golang-builder-shipment pipeline, which is part of the ART tooling infrastructure [1]. Specifically: 1. Relationship to ART pipelines: These identifiers map lifecycle phases to their respective Release Plans within the ART golang-builder-shipment pipeline [1]. The prod lifecycle phase is mapped to ocp-art-golang-builder-prod-rhel9, and the ec (likely early access or equivalent) lifecycle phase is mapped to ocp-art-golang-builder-ec-rhel9 [1]. 2. Function: This pipeline helps automate the generation of shipment configuration files (ShipmentConfig YAML) used when creating snapshot builds or managing the distribution of OpenShift components [1]. 3. Context: ART manages various golang builder images (e.g., registry.ci.openshift.org/ocp/builder:rhel-9-golang-...) which are used to ensure that CI/CD and productized builds are consistent [2][3]. These internal identifiers, however, function as administrative labels within ART's orchestration code (e.g., in pyartcd) to handle the automated lifecycle management of these builder assets [1]. In summary, these are not end-user or publicly consumable container image names, but rather internal configuration keys used by the OpenShift ART team to automate the release and shipment workflows for Go-based builder images [1].

Citations:


🌐 Web query:

site:github.com/openshift-eng/art-tools/pull/3083 golang builder shipment stage prod releasePlan

💡 Result:

The terms builder, shipment, stage, prod, and releasePlan refer to components within the OpenShift Automated Release Team (ART) tooling, specifically regarding the golang-builder-shipment pipeline [1]. In this context: 1. golang-builder-shipment: This is a specific automation pipeline implemented in the pyartcd library, designed to manage the shipment of Go-based components [1]. 2. releasePlan: This refers to a configuration mapping used to define the target environment for a build [1]. The pipeline maps software lifecycle phases to specific release plans [1]. For example, the prod lifecycle phase is mapped to the ocp-art-golang-builder-prod-rhel9 release plan, and the ec (early candidate) phase is mapped to ocp-art-golang-builder-ec-rhel9 [1]. 3. stage/prod: These represent the environment targets within the shipment configuration [1]. When assembling the shipment, the pipeline assigns the resolved release plan to both the stage and prod environment fields in the configuration [1]. The pipeline handles environment lifecycle detection by fetching the software lifecycle phase (such as release or pre-release) from build metadata (specifically group.yml in ocp-build-data) and resolving it to the appropriate environment and release plan [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
echo '--- introducing commits and concise history for the target file ---'
git log --oneline --all -- doozer/doozerlib/backend/golang_builder_shipment.py | head -20
echo '--- test assertions for both shipment environments ---'
rg -n -C 12 'environments\.(stage|prod)|ShipmentEnv|stage.*releasePlan|prod.*releasePlan' doozer/tests/backend/test_golang_builder_shipment.py pyartcd/tests/pipelines/test_golang_builder_shipment.py

Repository: openshift-eng/art-tools

Length of output: 5041


🏁 Script executed:

#!/bin/bash
set -eu
python3 - <<'PY'
import json
import urllib.request

for url in [
    "https://api.github.com/repos/openshift-eng/art-tools/commits/71f8baab",
    "https://api.github.com/repos/openshift-eng/art-tools/pulls/3083",
]:
    print(f"--- {url} ---")
    try:
        with urllib.request.urlopen(url, timeout=10) as response:
            data = json.load(response)
        for key in ("sha", "message", "title", "body", "html_url"):
            if key in data:
                value = data[key]
                if isinstance(value, str):
                    print(f"{key}: {value[:5000]}")
                else:
                    print(f"{key}: {value}")
    except Exception as exc:
        print(f"request failed: {exc}")
PY

Repository: openshift-eng/art-tools

Length of output: 613


Use a stage-specific ReleasePlan. release new --env stage passes shipment.environments.stage.releasePlan directly to spec.releasePlan. Since both entries use release_plan, a stage release can use ocp-art-golang-builder-prod-rhel9. Add the stage ReleasePlan and test both environment selections.

🤖 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 `@doozer/doozerlib/backend/golang_builder_shipment.py` around lines 303 - 306,
Update the Environments construction to assign a stage-specific ReleasePlan to
environments.stage instead of reusing release_plan, while retaining the
production ReleasePlan for environments.prod. Ensure release new environment
selection passes the correct stage or production plan through
shipment.environments, and add coverage for both environment selections.

Comment on lines +358 to +364
rc, stdout, stderr = await exectools.cmd_gather_async(cmd, stderr=None, check=False)
if rc != 0:
raise RuntimeError(f"elliott snapshot new failed (rc={rc}): {stderr or stdout}")
if stdout:
self.logger.info("elliott snapshot new output:\n%s", stdout)
finally:
os.unlink(builds_file)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Capture elliott stderr and tolerate a missing temporary file.

stderr=None makes cmd_gather_async return an empty stderr, so the RuntimeError at Line 360 loses the elliott diagnostics whenever stdout is also empty. In the finally block, os.unlink raises FileNotFoundError if the file is already gone, and that replaces the original exception.

🐛 Proposed fix
-            rc, stdout, stderr = await exectools.cmd_gather_async(cmd, stderr=None, check=False)
+            rc, stdout, stderr = await exectools.cmd_gather_async(cmd, check=False)
             if rc != 0:
                 raise RuntimeError(f"elliott snapshot new failed (rc={rc}): {stderr or stdout}")
             if stdout:
                 self.logger.info("elliott snapshot new output:\n%s", stdout)
         finally:
-            os.unlink(builds_file)
+            Path(builds_file).unlink(missing_ok=True)
🤖 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 `@doozer/doozerlib/backend/golang_builder_shipment.py` around lines 358 - 364,
Update the elliott invocation in golang_builder_shipment.py around the
cmd_gather_async call so stderr is captured and included in the RuntimeError
when rc is nonzero, falling back to stdout only if stderr is empty. Also make
the finally block that unlinks builds_file tolerate FileNotFoundError so a
missing temporary file does not mask the original failure.

Comment on lines +436 to +445
gitlab_token = os.getenv("GITLAB_TOKEN")
gl = python_gitlab.Gitlab(self.gitlab_url, private_token=gitlab_token)

def _get_project(url):
parsed = urlparse(url)
project_path = parsed.path.strip("/").removesuffix(".git")
return gl.projects.get(project_path)

source_project = _get_project(self.shipment_data_repo_push_url)
target_project = _get_project(self.shipment_data_repo_pull_url)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

The API host and the repository URL host can disagree.

gl targets self.gitlab_url. _get_project keeps only the path of the push and pull URLs and discards their host. If a caller passes shipment_data_repo_pull_url or shipment_data_repo_push_url for a different GitLab host, the project lookup runs against self.gitlab_url. The lookup then either fails or resolves an unrelated project with the same path, and the MR is created in the wrong place.

Derive the API base URL from the push URL, or validate that both hosts equal self.gitlab_url before you create the client.

🤖 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 `@doozer/doozerlib/backend/golang_builder_shipment.py` around lines 436 - 445,
Update the GitLab client setup around _get_project to ensure repository URL
hosts cannot be silently discarded. Either derive the API base URL from the push
repository URL before constructing gl, or validate that both push and pull URL
hosts match self.gitlab_url and reject mismatches before project lookup.

Comment on lines +6 to +13
from artcommonlib.model import Model
from doozerlib.constants import ART_IMAGES_BASE_APPLICATION
from doozerlib.backend.golang_builder_shipment import (
GOLANG_BUILDER_SHIPMENT_RELEASE_PLAN_MAP,
GolangBuilderShipmentHandler,
derive_golang_group,
resolve_env_from_runtime,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the import order to clear the Ruff I001 failure.

The unit-tests job fails with Ruff I001. doozerlib.constants precedes doozerlib.backend.golang_builder_shipment, so isort reorders the block.

🔧 Proposed fix
 from artcommonlib.model import Model
-from doozerlib.constants import ART_IMAGES_BASE_APPLICATION
 from doozerlib.backend.golang_builder_shipment import (
     GOLANG_BUILDER_SHIPMENT_RELEASE_PLAN_MAP,
     GolangBuilderShipmentHandler,
     derive_golang_group,
     resolve_env_from_runtime,
 )
+from doozerlib.constants import ART_IMAGES_BASE_APPLICATION
📝 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
from artcommonlib.model import Model
from doozerlib.constants import ART_IMAGES_BASE_APPLICATION
from doozerlib.backend.golang_builder_shipment import (
GOLANG_BUILDER_SHIPMENT_RELEASE_PLAN_MAP,
GolangBuilderShipmentHandler,
derive_golang_group,
resolve_env_from_runtime,
)
from artcommonlib.model import Model
from doozerlib.backend.golang_builder_shipment import (
GOLANG_BUILDER_SHIPMENT_RELEASE_PLAN_MAP,
GolangBuilderShipmentHandler,
derive_golang_group,
resolve_env_from_runtime,
)
from doozerlib.constants import ART_IMAGES_BASE_APPLICATION
🤖 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 `@doozer/tests/backend/test_golang_builder_shipment.py` around lines 6 - 13,
Reorder the imports in the test module so the
doozerlib.backend.golang_builder_shipment import appears before
doozerlib.constants, matching Ruff/isort ordering while preserving all imported
symbols.

Source: Pipeline failures

Comment on lines +466 to +474
class TestShipmentFilePath(unittest.TestCase):
def test_path_format(self):
application = "art-images-base"
product = "ocp"
golang_group = "rhel-9-golang-1.25"
env = "prod"
expected_prefix = Path("shipment") / "ocp" / "rhel-9-golang-1.25" / "art-images-base" / "prod"
actual = Path("shipment") / product / golang_group / application / env
self.assertEqual(actual, expected_prefix)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

test_path_format cannot fail.

expected_prefix and actual are both built in the test from the same literals. The test never calls _create_shipment_mr. If the path layout at Lines 396-402 changes, this test still passes.

Assert on the path that _create_shipment_mr writes through shipment_data_repo.write_file, or remove the test.

🤖 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 `@doozer/tests/backend/test_golang_builder_shipment.py` around lines 466 - 474,
Update TestShipmentFilePath.test_path_format so it verifies the path produced by
_create_shipment_mr via shipment_data_repo.write_file instead of comparing two
Path values built from the same literals. Use the visible _create_shipment_mr
and write_file symbols as the assertion target, or remove this test if it cannot
observe the generated shipment path layout.

Comment on lines +200 to +212
adapter = _CliRuntimeAdapter(runtime, golang_group)
handler = GolangBuilderShipmentHandler(
runtime=adapter,
dry_run=runtime.dry_run,
art_jira=art_jira,
ocp_version=ocp_version,
shipment_data_repo_pull_url=shipment_data_repo_url,
)
mr_url = await handler.create_shipment_from_nvrs(
resolved_nvrs,
golang_group=golang_group,
env=env,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Release notes get the golang group as the OCP version when --ocp-version is omitted.

--ocp-version is now optional, so ocp_version=None reaches GolangBuilderShipmentHandler. _resolve_ocp_version then falls back to self.runtime.group (doozer/doozerlib/backend/golang_builder_shipment.py Lines 145-152). _CliRuntimeAdapter sets self.group = golang_group at Line 132, and the regex openshift-(\d+\.\d+) does not match a value such as rhel-9-golang-1.25. The handler therefore returns the golang group, and it is embedded in the shipment synopsis, topic, and description, and in the MR body.

Pass an explicit placeholder, or set the adapter group to the OCP group name so the version resolution stays correct.

🤖 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 `@pyartcd/pyartcd/pipelines/golang_builder_shipment.py` around lines 200 - 212,
Ensure GolangBuilderShipmentHandler receives a runtime group that resolves to
the OCP version when ocp_version is omitted, rather than golang_group. Update
_CliRuntimeAdapter construction or its group assignment to use the OCP group
name, or pass the established explicit placeholder through the handler while
preserving explicit --ocp-version behavior.

@lgarciaaco
lgarciaaco marked this pull request as draft August 5, 2026 14:49
@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 Aug 5, 2026
- Add REDHAT_GITLAB_URL constant to artcommonlib
- Move GolangBuilderShipmentHandler import to top-level in konflux_image_builder.py
- Remove config.get("shipment_config") dead lookup (no such config in doozer)
- Replace runtime.working_dir with tempfile.mkdtemp
- Hardcode product="ocp" as module constant
- Remove dead _resolve_ocp_version method
- Remove resolve_env_from_lifecycle_phase (CLI has its own)
- Fetch GITLAB_TOKEN once in _setup_repos, reuse as self._gitlab_token
- Make basic_auth_url a module-level function
- Use REDHAT_GITLAB_URL as default gitlab_url
- Remove working_dir/config/product from _CliRuntimeAdapter

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
doozer/doozerlib/backend/golang_builder_shipment.py (2)

331-331: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Insecure Deserialization (CWE-502): Deserialization of Untrusted Data

Reachability path
● Entry
  doozer/doozerlib/backend/konflux_image_builder.py:439
  GolangBuilderShipmentHandler
│
▼
● Sink
  doozer/doozerlib/backend/golang_builder_shipment.py

Use a safe YAML loader for Elliott output.

yaml.load(stdout) parses Elliott output before the dict validation at Lines 332-337. Unsafe YAML tags can construct attacker-controlled Python objects. Use yaml.safe_load(stdout) and reject unsupported schema values.

As per coding guidelines, flag yaml.load without SafeLoader. As per path instructions, do not use yaml.load() on untrusted data.

#!/bin/bash
set -euo pipefail

# Inspect the declared YAML dependency and all unsafe loader call sites.
rg -n -i --glob 'pyproject.toml' --glob 'requirements*.txt' --glob 'setup.py' \
  --glob 'tox.ini' --glob 'Pipfile*' 'pyyaml|yaml' . || true
rg -n -C 4 --glob '*.py' '\byaml\.load\s*\(' doozer elliott || true
🤖 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 `@doozer/doozerlib/backend/golang_builder_shipment.py` at line 331, Update the
YAML parsing in the shipment builder around snapshot_obj to use
yaml.safe_load(stdout) instead of yaml.load, then preserve or strengthen the
existing validation so unsupported or non-dictionary schema values are rejected
before further processing.

92-107: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Sensitive Data Exposure (CWE-319): Cleartext Transmission of Sensitive Information

Reachability: Internal

Reachability path
● Entry
  doozer/doozerlib/backend/konflux_image_builder.py:439
  GolangBuilderShipmentHandler
│
▼
● Sink
  doozer/doozerlib/backend/golang_builder_shipment.py

Require HTTPS for gitlab_url and shipment_data_repo_push_url.

Reject HTTP overrides before basic_auth_url injects GITLAB_TOKEN or python_gitlab.Gitlab receives it. The pull URL does not receive the token.

🤖 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 `@doozer/doozerlib/backend/golang_builder_shipment.py` around lines 92 - 107,
Validate gitlab_url and shipment_data_repo_push_url in the GolangBuilderShipment
initializer before either URL is passed to basic_auth_url or
python_gitlab.Gitlab, rejecting any non-HTTPS override while preserving the
default URL behavior. Leave shipment_data_repo_pull_url unrestricted because it
does not receive the GitLab token.
🤖 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 `@doozer/doozerlib/backend/golang_builder_shipment.py`:
- Line 104: Ensure the temporary repository created for _shipment_data_repo_dir
is removed after every shipment flow, including failures. Update both public
shipment-flow methods to perform cleanup in finally blocks, or replace the raw
mkdtemp ownership with TemporaryDirectory and invoke cleanup, while preserving
existing success and error behavior.
- Line 133: Update both shipment paths in the handler, including the logic
around ocp_version and the corresponding line near the second shipment path, to
obtain and pass the actual OpenShift version from the build context instead of
falling back to golang_group. Ensure _build_shipment_config receives the real
OCP version when the production caller omits the handler’s ocp_version.

In `@doozer/tests/backend/test_golang_builder_shipment.py`:
- Line 211: Remove the `_gitlab_token = "test-token"` assignment from the test
setup in the mocked GitLab client test, leaving the remaining fixture
initialization unchanged.

---

Outside diff comments:
In `@doozer/doozerlib/backend/golang_builder_shipment.py`:
- Line 331: Update the YAML parsing in the shipment builder around snapshot_obj
to use yaml.safe_load(stdout) instead of yaml.load, then preserve or strengthen
the existing validation so unsupported or non-dictionary schema values are
rejected before further processing.
- Around line 92-107: Validate gitlab_url and shipment_data_repo_push_url in the
GolangBuilderShipment initializer before either URL is passed to basic_auth_url
or python_gitlab.Gitlab, rejecting any non-HTTPS override while preserving the
default URL behavior. Leave shipment_data_repo_pull_url unrestricted because it
does not receive the GitLab token.
🪄 Autofix

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: openshift-eng/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: a548b6b9-3f3f-42d4-ada6-468dd5e941db

📥 Commits

Reviewing files that changed from the base of the PR and between 71f8baa and 15b2588.

📒 Files selected for processing (5)
  • artcommon/artcommonlib/constants.py
  • doozer/doozerlib/backend/golang_builder_shipment.py
  • doozer/doozerlib/backend/konflux_image_builder.py
  • doozer/tests/backend/test_golang_builder_shipment.py
  • pyartcd/pyartcd/pipelines/golang_builder_shipment.py
💤 Files with no reviewable changes (1)
  • pyartcd/pyartcd/pipelines/golang_builder_shipment.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • doozer/doozerlib/backend/konflux_image_builder.py

self.ocp_version = ocp_version
self.logger = getattr(runtime, "logger", logging.getLogger(__name__))
self.gitlab_url = gitlab_url
self._shipment_data_repo_dir = Path(tempfile.mkdtemp(prefix="golang-shipment-"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Remove the temporary repository after each shipment flow.

Line 104 creates a persistent temporary directory. Both public shipment flows populate it, but neither removes it after success or failure. The production builder creates handlers per shipment, so repository checkouts accumulate under the temporary directory.

Run cleanup from a finally block in both public flows, or retain a TemporaryDirectory owner and call its cleanup method.

🤖 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 `@doozer/doozerlib/backend/golang_builder_shipment.py` at line 104, Ensure the
temporary repository created for _shipment_data_repo_dir is removed after every
shipment flow, including failures. Update both public shipment-flow methods to
perform cleanup in finally blocks, or replace the raw mkdtemp ownership with
TemporaryDirectory and invoke cleanup, while preserving existing success and
error behavior.

golang_group = derive_golang_group([nvr])
env = resolve_env_from_runtime(self.runtime)
release_plan = self.resolve_release_plan(env)
ocp_version = self.ocp_version or golang_group

Copy link
Copy Markdown
Contributor

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 use golang_group as the OpenShift version.

These fallbacks set ocp_version to values such as rhel-9-golang-1.25. The production caller in doozer/doozerlib/backend/konflux_image_builder.py constructs this handler without ocp_version. _build_shipment_config then writes that Golang group into RHBA release notes and the MR description as an OpenShift version.

Pass the actual OpenShift version from the build context. Update both shipment paths.

Also applies to: 184-184

🤖 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 `@doozer/doozerlib/backend/golang_builder_shipment.py` at line 133, Update both
shipment paths in the handler, including the logic around ocp_version and the
corresponding line near the second shipment path, to obtain and pass the actual
OpenShift version from the build context instead of falling back to
golang_group. Ensure _build_shipment_config receives the real OCP version when
the production caller omits the handler’s ocp_version.

@patch("doozerlib.backend.golang_builder_shipment.python_gitlab")
async def test_creates_mr_with_correct_title(self, mock_gitlab):
handler = self._make_handler(dry_run=False)
handler._gitlab_token = "test-token"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the literal token fixture.

Line 211 assigns a string literal to _gitlab_token. Ruff S105 reports this as a hardcoded password. Remove this assignment because the mocked GitLab client does not need a credential in this test. As per coding guidelines, “Flag hardcoded secrets … and variables named api_key, secret, token, or password assigned to string literals.”

Proposed fix
-        handler._gitlab_token = "test-token"
📝 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
handler._gitlab_token = "test-token"
🧰 Tools
🪛 Ruff (0.16.1)

[error] 211-211: Possible hardcoded password assigned to: "_gitlab_token"

(S105)

🤖 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 `@doozer/tests/backend/test_golang_builder_shipment.py` at line 211, Remove the
`_gitlab_token = "test-token"` assignment from the test setup in the mocked
GitLab client test, leaving the remaining fixture initialization unchanged.

Sources: Coding guidelines, Linters/SAST tools

@lgarciaaco

Copy link
Copy Markdown
Contributor Author

Work in progress — continuing on branch art-20930/golang-builder-shipment-v3. Closing for now, branch preserved.

@lgarciaaco lgarciaaco closed this Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants