|
11 | 11 | from dataclasses import field |
12 | 12 | from pathlib import Path |
13 | 13 | from typing import Literal |
| 14 | +from typing import Sequence |
14 | 15 | from typing import overload |
15 | 16 |
|
16 | 17 | import jinja2 |
|
22 | 23 | from bci_build.container_attributes import PackageType |
23 | 24 | from bci_build.container_attributes import ReleaseStage |
24 | 25 | from bci_build.container_attributes import SupportLevel |
25 | | -from bci_build.containercrate import ContainerCrate |
26 | 26 | from bci_build.os_version import ALL_OS_LTSS_VERSIONS |
27 | 27 | from bci_build.os_version import RELEASED_OS_VERSIONS |
28 | 28 | from bci_build.os_version import OsVersion |
@@ -209,7 +209,7 @@ class BaseContainerImage(abc.ABC): |
209 | 209 | build_flavor: str | None = None |
210 | 210 |
|
211 | 211 | #: create that this container is part of |
212 | | - crate: ContainerCrate = None |
| 212 | + family: ContainerFamily = None |
213 | 213 |
|
214 | 214 | #: Add any replacements via `obs-service-replace_using_package_version |
215 | 215 | #: <https://github.com/openSUSE/obs-service-replace_using_package_version>`_ |
@@ -1108,11 +1108,17 @@ async def write_file_to_dest(fname: str, contents: str | bytes) -> None: |
1108 | 1108 |
|
1109 | 1109 | if self.build_flavor: |
1110 | 1110 | dfile = "Dockerfile" |
1111 | | - tasks.append(write_file_to_dest(dfile, self.crate.default_dockerfile(self))) |
| 1111 | + tasks.append( |
| 1112 | + write_file_to_dest( |
| 1113 | + dfile, self.family.get_default_dockerfile_content(self) |
| 1114 | + ) |
| 1115 | + ) |
1112 | 1116 | files.append(dfile) |
1113 | 1117 |
|
1114 | 1118 | mname = "_multibuild" |
1115 | | - tasks.append(write_file_to_dest(mname, self.crate.multibuild(self))) |
| 1119 | + tasks.append( |
| 1120 | + write_file_to_dest(mname, self.family.get_multibuild_file_content(self)) |
| 1121 | + ) |
1116 | 1122 | files.append(mname) |
1117 | 1123 |
|
1118 | 1124 | tasks.append( |
@@ -1435,6 +1441,85 @@ def prepare_template(self) -> None: |
1435 | 1441 | pass |
1436 | 1442 |
|
1437 | 1443 |
|
| 1444 | +class ContainerFamily: |
| 1445 | + """ContainerFamily is grouping multiple containers build flavors. |
| 1446 | +
|
| 1447 | + This provides package-central functions like generating _service and |
| 1448 | + _multibuild files, checking version_uid |
| 1449 | + """ |
| 1450 | + |
| 1451 | + def __init__( |
| 1452 | + self, |
| 1453 | + containers: Sequence[BaseContainerImage], |
| 1454 | + ): |
| 1455 | + # Assign the family for every container build flavor based on os version and package name |
| 1456 | + # Sample family structure: |
| 1457 | + # { |
| 1458 | + # (OsVersion.TumbleWeed, "test-package-name"): {"flavor1", "flavor2"} |
| 1459 | + # } |
| 1460 | + self._container_families: dict[tuple, set] = {} |
| 1461 | + for container in containers: |
| 1462 | + if container.build_flavor: |
| 1463 | + self._container_families.setdefault( |
| 1464 | + (container.os_version, container.package_name), set() |
| 1465 | + ).add(container.build_flavor) |
| 1466 | + |
| 1467 | + for container in containers: |
| 1468 | + if container.family is not None: |
| 1469 | + raise ValueError("Container is already part of a ContainerFamily") |
| 1470 | + container.family = self |
| 1471 | + |
| 1472 | + def get_all_build_flavors( |
| 1473 | + self, |
| 1474 | + container: BaseContainerImage, |
| 1475 | + ) -> list[str]: |
| 1476 | + """Return all available build flavors for this container in based on its family""" |
| 1477 | + return sorted( |
| 1478 | + self._container_families.get( |
| 1479 | + (container.os_version, container.package_name), [""] |
| 1480 | + ) |
| 1481 | + ) |
| 1482 | + |
| 1483 | + def get_default_dockerfile_content( |
| 1484 | + self, |
| 1485 | + container: BaseContainerImage, |
| 1486 | + ) -> str: |
| 1487 | + buildrelease: str = "" |
| 1488 | + if container.build_release: |
| 1489 | + buildrelease = f"\n#!BuildVersion: workaround-for-an-obs-bug\n#!BuildRelease: {container.build_release}" |
| 1490 | + """Return a default Dockerfile to disable build on default flavor.""" |
| 1491 | + return f"""#!ExclusiveArch: do-not-build |
| 1492 | +#!ForceMultiVersion{buildrelease} |
| 1493 | +
|
| 1494 | +# For this container we only build the Dockerfile.$flavor builds. |
| 1495 | +""" |
| 1496 | + |
| 1497 | + def get_multibuild_file_content( |
| 1498 | + self, |
| 1499 | + container: BaseContainerImage, |
| 1500 | + ) -> str: |
| 1501 | + """Return the _multibuild file string to write for this container based on its family.""" |
| 1502 | + if not self.check_version_in_uid(container): |
| 1503 | + return "" |
| 1504 | + |
| 1505 | + flavors: str = "\n".join( |
| 1506 | + " " * 4 + f"<package>{pkg}</package>" |
| 1507 | + for pkg in self.get_all_build_flavors(container) |
| 1508 | + ) |
| 1509 | + return f"<multibuild>\n{flavors}\n</multibuild>" |
| 1510 | + |
| 1511 | + def check_version_in_uid( |
| 1512 | + self, |
| 1513 | + container: BaseContainerImage, |
| 1514 | + ) -> bool: |
| 1515 | + """ check if version_in_uid is set to False if a container has more than one flavour """ |
| 1516 | + if len(self.get_all_build_flavors(container)) > 1 and getattr( |
| 1517 | + container, "version_in_uid", False |
| 1518 | + ): |
| 1519 | + return False |
| 1520 | + return True |
| 1521 | + |
| 1522 | + |
1438 | 1523 | def generate_disk_size_constraints(size_gb: int) -> str: |
1439 | 1524 | """Creates the contents of a :file:`_constraints` file for OBS to require |
1440 | 1525 | workers with at least ``size_gb`` GB of disk space. |
|
0 commit comments