Skip to content

Commit efd9db5

Browse files
committed
Add gen-assembly-lp and prepare-release-lp pipelines for Layered Products
Implements two new pipelines for ACM/MCE assembly-based releases: - gen-assembly-lp: Extracts operand NVRs from FBC images, supports --include for swapping specific builds, and --basis-assembly for lightweight inherited assemblies. Creates a PR to ocp-build-data. - prepare-release-lp: Reads a named assembly from releases.yml, checks for existing bundles before rebuilding, triggers FBC builds, and creates shipment MRs. Uses named assembly (not stream) for deterministic builds. Includes unit tests (24 passing) and CLI registration. Co-authored-by: Cursor <cursoragent@cursor.com> rh-pre-commit.version: 2.3.2 rh-pre-commit.check-secrets: ENABLED
1 parent d09abde commit efd9db5

13 files changed

Lines changed: 3101 additions & 169 deletions

File tree

doozer/doozerlib/cli/fbc.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -649,21 +649,21 @@ async def _rebase_and_build(
649649
operator_meta: ImageMetadata,
650650
bundle_build: KonfluxBundleBuildRecord,
651651
git_auth_secret: Optional[str] = None,
652-
) -> str:
652+
) -> Tuple[str, str]:
653653
"""Rebase and build FBC for the given operator and bundle build.
654654
655655
:param rebaser: FBC rebaser instance
656656
:param builder: FBC builder instance
657657
:param operator_meta: Operator metadata
658658
:param bundle_build: Bundle build record
659659
:param git_auth_secret: Name of the transient git-auth Secret for PipelineRuns
660-
:return: NVR of the FBC build
660+
:return: Tuple of (NVR, pullspec) of the FBC build
661661
"""
662662
existing_fbc_build = await self._check_existing_fbc_build(operator_meta, bundle_build)
663663
if existing_fbc_build:
664664
self._logger.info(f"Found existing FBC build: {existing_fbc_build.nvr}")
665665
if not self.force:
666-
return existing_fbc_build.nvr
666+
return existing_fbc_build.nvr, existing_fbc_build.image_pullspec or ""
667667
self._logger.info("Force flag is set, rebuilding FBC")
668668

669669
if self.reset_to_prod:
@@ -673,8 +673,16 @@ async def _rebase_and_build(
673673
self._logger.info(f"Rebasing fbc for {operator_meta.name}...")
674674
nvr = await rebaser.rebase(operator_meta, bundle_build, self.version, self.release)
675675
self._logger.info(f"Building fbc for {operator_meta.name}...")
676-
await builder.build(operator_meta, operator_nvr=bundle_build.operator_nvr, git_auth_secret=git_auth_secret)
677-
return nvr
676+
_, pipelinerun_dict = await builder.build(
677+
operator_meta, operator_nvr=bundle_build.operator_nvr, git_auth_secret=git_auth_secret
678+
)
679+
results = pipelinerun_dict.get('status', {}).get('results', [])
680+
image_url = next((r['value'] for r in results if r['name'] == 'IMAGE_URL'), None)
681+
image_digest = next((r['value'] for r in results if r['name'] == 'IMAGE_DIGEST'), None)
682+
pullspec = f"{image_url.split(':')[0]}@{image_digest}" if image_url and image_digest else ""
683+
if not pullspec:
684+
LOGGER.warning("Could not extract pullspec from pipelinerun results for %s", nvr)
685+
return nvr, pullspec
678686

679687
async def run(self):
680688
"""Rebase and build fbc fragments for given operator NVRs or all latest operator NVRs for the group and assembly"""
@@ -705,7 +713,7 @@ async def run(self):
705713
assert runtime.source_resolver is not None, "source_resolver is not initialized. Doozer bug?"
706714
assert runtime.group_config is not None, "group_config is not initialized. Doozer bug?"
707715

708-
self._logger.info(f"Processing {len(dgk_operator_builds)} operator(s): {list(dgk_operator_builds.keys())}")
716+
self._logger.info(f"Processing {len(dgk_bundle_builds)} operator(s): {list(dgk_bundle_builds.keys())}")
709717

710718
# Set up rebase and build components once
711719
if self.major_minor:
@@ -786,6 +794,7 @@ async def run(self):
786794
try:
787795
results = await asyncio.gather(*tasks, return_exceptions=True)
788796
successful_nvrs = []
797+
successful_pullspecs = []
789798
failed_tasks = []
790799
errors = []
791800

@@ -804,7 +813,9 @@ async def run(self):
804813
errors.append(error_details)
805814
LOGGER.error(f"{error_msg}; {stack_trace}")
806815
else:
807-
successful_nvrs.append(result)
816+
nvr, pullspec = result
817+
successful_nvrs.append(nvr)
818+
successful_pullspecs.append(pullspec)
808819
finally:
809820
refresh_task.cancel()
810821
try:
@@ -824,6 +835,7 @@ async def run(self):
824835
if self.output == 'json':
825836
output_data = {
826837
"nvrs": successful_nvrs,
838+
"pullspecs": successful_pullspecs,
827839
"errors": errors,
828840
"failed_count": len(failed_tasks),
829841
"success_count": len(successful_nvrs),

doozer/doozerlib/runtime.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from artcommonlib.model import Missing, Model
3030
from artcommonlib.pushd import Dir
3131
from artcommonlib.runtime import GroupRuntime
32-
from artcommonlib.util import isolate_el_version_in_brew_tag
32+
from artcommonlib.util import isolate_el_version_in_brew_tag, isolate_major_minor_in_group
3333
from artcommonlib.variants import BuildVariant
3434
from jira import JIRA
3535
from semver import Version
@@ -612,10 +612,13 @@ def initialize(
612612
self.streams = assembly_streams_config(self.get_releases_config(), self.assembly, org_stream_model)
613613

614614
strict_mode = True
615+
major, minor = isolate_major_minor_in_group(self.group)
616+
is_openshift_group = major is not None and minor is not None
615617
if (
616618
self.assembly_type == AssemblyTypes.STREAM
617619
or not self.assembly
618620
or self.assembly in ['stream', 'test', 'microshift']
621+
or not is_openshift_group
619622
):
620623
strict_mode = False
621624

doozer/doozerlib/util.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -412,8 +412,15 @@ def get_release_name(
412412
release_offset: Optional[int],
413413
):
414414
major, minor = isolate_major_minor_in_group(group_name)
415+
416+
if assembly_type == AssemblyTypes.STANDARD:
417+
if release_offset is not None:
418+
raise ValueError("release_offset can't be set for a STANDARD release.")
419+
return assembly_name
420+
415421
if major is None or minor is None:
416-
raise ValueError(f"Invalid group name: {group_name}")
422+
raise ValueError(f"Cannot determine major.minor from group name: {group_name}")
423+
417424
if assembly_type == AssemblyTypes.CUSTOM:
418425
if release_offset is None:
419426
raise ValueError("release_offset is required for a CUSTOM release.")
@@ -422,10 +429,6 @@ def get_release_name(
422429
if release_offset is not None:
423430
raise ValueError(f"release_offset can't be set for a {assembly_type.value} release.")
424431
release_name = f"{major}.{minor}.0-{assembly_name}"
425-
elif assembly_type == AssemblyTypes.STANDARD:
426-
if release_offset is not None:
427-
raise ValueError("release_offset can't be set for a STANDARD release.")
428-
release_name = f"{assembly_name}"
429432
else:
430433
raise ValueError(f"Assembly type {assembly_type} is not supported.")
431434
return release_name

doozer/tests/cli/test_fbc.py

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,17 @@ async def test_run_with_operator_nvrs(self, mock_rebaser_class, mock_builder_cla
148148
mock_rebaser_class.return_value = mock_rebaser
149149

150150
mock_builder = mock.AsyncMock()
151+
mock_builder.build.return_value = (
152+
"test-pipelinerun",
153+
{
154+
"status": {
155+
"results": [
156+
{"name": "IMAGE_URL", "value": "quay.io/test/image:test-fbc-1.0.0-1"},
157+
{"name": "IMAGE_DIGEST", "value": "sha256:abc123"},
158+
]
159+
}
160+
},
161+
)
151162
mock_builder_class.return_value = mock_builder
152163

153164
mock_importer = mock.AsyncMock()
@@ -182,6 +193,17 @@ async def test_run_without_operator_nvrs(self, mock_rebaser_class, mock_builder_
182193
mock_rebaser_class.return_value = mock_rebaser
183194

184195
mock_builder = mock.AsyncMock()
196+
mock_builder.build.return_value = (
197+
"test-pipelinerun",
198+
{
199+
"status": {
200+
"results": [
201+
{"name": "IMAGE_URL", "value": "quay.io/test/image:test-fbc-1.0.0-1"},
202+
{"name": "IMAGE_DIGEST", "value": "sha256:abc123"},
203+
]
204+
}
205+
},
206+
)
185207
mock_builder_class.return_value = mock_builder
186208

187209
mock_importer = mock.AsyncMock()
@@ -199,7 +221,11 @@ async def test_run_existing_fbc_build_found_no_force(self, mock_rebaser_class, m
199221
bundle_build = self._create_mock_bundle_build(
200222
"test-operator-bundle", "test-operator-bundle-1.0.0-1", "test-operator-1.0.0-1"
201223
)
202-
existing_fbc = mock.Mock(spec=KonfluxFbcBuildRecord, nvr="test-fbc-1.0.0-1")
224+
existing_fbc = mock.Mock(
225+
spec=KonfluxFbcBuildRecord,
226+
nvr="test-fbc-1.0.0-1",
227+
image_pullspec="quay.io/test/image@sha256:existing123",
228+
)
203229

204230
self.runtime.konflux_db.get_build_records_by_nvrs.return_value = [operator_build]
205231
self.runtime.images = ["test-operator"]
@@ -233,7 +259,11 @@ async def test_run_existing_fbc_build_found_with_force(
233259
bundle_build = self._create_mock_bundle_build(
234260
"test-operator-bundle", "test-operator-bundle-1.0.0-1", "test-operator-1.0.0-1"
235261
)
236-
existing_fbc = mock.Mock(spec=KonfluxFbcBuildRecord, nvr="test-fbc-1.0.0-1")
262+
existing_fbc = mock.Mock(
263+
spec=KonfluxFbcBuildRecord,
264+
nvr="test-fbc-1.0.0-1",
265+
image_pullspec="quay.io/test/image@sha256:existing123",
266+
)
237267

238268
self.runtime.konflux_db.get_build_records_by_nvrs.return_value = [operator_build]
239269
self.runtime.images = ["test-operator"]
@@ -248,6 +278,17 @@ async def test_run_existing_fbc_build_found_with_force(
248278
mock_rebaser_class.return_value = mock_rebaser
249279

250280
mock_builder = mock.AsyncMock()
281+
mock_builder.build.return_value = (
282+
"test-pipelinerun",
283+
{
284+
"status": {
285+
"results": [
286+
{"name": "IMAGE_URL", "value": "quay.io/test/image:test-fbc-1.0.0-2"},
287+
{"name": "IMAGE_DIGEST", "value": "sha256:def456"},
288+
]
289+
}
290+
},
291+
)
251292
mock_builder_class.return_value = mock_builder
252293

253294
mock_importer = mock.AsyncMock()
@@ -297,6 +338,17 @@ async def mock_fbc_search(*args, **kwargs):
297338
mock_rebaser_class.return_value = mock_rebaser
298339

299340
mock_builder = mock.AsyncMock()
341+
mock_builder.build.return_value = (
342+
"test-pipelinerun",
343+
{
344+
"status": {
345+
"results": [
346+
{"name": "IMAGE_URL", "value": "quay.io/test/image:test-operator-1-fbc-1.0.0-1"},
347+
{"name": "IMAGE_DIGEST", "value": "sha256:abc123"},
348+
]
349+
}
350+
},
351+
)
300352
mock_builder_class.return_value = mock_builder
301353

302354
mock_importer = mock.AsyncMock()

elliott/elliottlib/cli/find_builds_cli.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,5 +857,6 @@ async def find_builds_konflux_all_types(runtime: Runtime) -> dict[str, list]:
857857
'non_payload': sorted([record.nvr for _, is_payload, record in records_with_olm if not is_payload]),
858858
'olm_builds': sorted([b.nvr for b in olm_records]),
859859
'olm_builds_not_found': sorted([b.nvr for b in olm_records_not_found]),
860+
'olm_operator_nvrs': sorted([b.nvr for b in operator_builds]),
860861
}
861862
return builds_tuple

pyartcd/pyartcd/__main__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
cleanup_locks,
1919
fbc_import_from_index,
2020
gen_assembly,
21+
gen_assembly_lp,
2122
images_health,
2223
layered_products_scan_konflux,
2324
monitor_nightly_delays,
@@ -31,6 +32,7 @@
3132
olm_bundle_konflux,
3233
operator_sdk_sync,
3334
prepare_release_konflux,
35+
prepare_release_lp,
3436
promote,
3537
quay_doomsday_backup,
3638
rebuild,
@@ -76,6 +78,7 @@
7678
"cleanup_locks",
7779
"fbc_import_from_index",
7880
"gen_assembly",
81+
"gen_assembly_lp",
7982
"images_health",
8083
"layered_products_scan_konflux",
8184
"monitor_nightly_delays",
@@ -89,6 +92,7 @@
8992
"olm_bundle_konflux",
9093
"operator_sdk_sync",
9194
"prepare_release_konflux",
95+
"prepare_release_lp",
9296
"promote",
9397
"quay_doomsday_backup",
9498
"rebuild",

0 commit comments

Comments
 (0)