Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 0 additions & 50 deletions src/bci_build/containercrate.py

This file was deleted.

93 changes: 89 additions & 4 deletions src/bci_build/package/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from dataclasses import field
from pathlib import Path
from typing import Literal
from typing import Sequence
from typing import overload

import jinja2
Expand All @@ -22,7 +23,6 @@
from bci_build.container_attributes import PackageType
from bci_build.container_attributes import ReleaseStage
from bci_build.container_attributes import SupportLevel
from bci_build.containercrate import ContainerCrate
from bci_build.os_version import ALL_OS_LTSS_VERSIONS
from bci_build.os_version import RELEASED_OS_VERSIONS
from bci_build.os_version import OsVersion
Expand Down Expand Up @@ -209,7 +209,7 @@ class BaseContainerImage(abc.ABC):
build_flavor: str | None = None

#: create that this container is part of
crate: ContainerCrate = None
family: ContainerFamily = None

#: Add any replacements via `obs-service-replace_using_package_version
#: <https://github.com/openSUSE/obs-service-replace_using_package_version>`_
Expand Down Expand Up @@ -1108,11 +1108,17 @@ async def write_file_to_dest(fname: str, contents: str | bytes) -> None:

if self.build_flavor:
dfile = "Dockerfile"
tasks.append(write_file_to_dest(dfile, self.crate.default_dockerfile(self)))
tasks.append(
write_file_to_dest(
dfile, self.family.get_default_dockerfile_content(self)
)
)
files.append(dfile)

mname = "_multibuild"
tasks.append(write_file_to_dest(mname, self.crate.multibuild(self)))
tasks.append(
write_file_to_dest(mname, self.family.get_multibuild_file_content(self))
)
files.append(mname)

tasks.append(
Expand Down Expand Up @@ -1435,6 +1441,85 @@ def prepare_template(self) -> None:
pass


class ContainerFamily:
Copy link
Member

@dirkmueller dirkmueller Jan 15, 2025

Choose a reason for hiding this comment

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

Hey @rcmadhankumar . I do like the new name suggestion "Family" instead of "Crate". However I was going into a slightly different route in #2085 - perhaps it would make sense to revive that with the updated name and rebase it instead?

I've spent many hours trying to split the dreaded "package" module into smaller, more modular and more consumable chunks, so can we please keep this Family class separate and independent of a several thousand line long module already? I understand there are recursive imports with the type annotations. we either have to omit them or use the string form to avoid them though rather than piling it together into one module.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, there were some recursive import issues, i will try moving it out.

"""ContainerFamily is grouping multiple containers build flavors.

This provides package-central functions like generating _service and
_multibuild files, checking version_uid
"""

def __init__(
self,
containers: Sequence[BaseContainerImage],
):
# Assign the family for every container build flavor based on os version and package name
# Sample family structure:
# {
# (OsVersion.TumbleWeed, "test-package-name"): {"flavor1", "flavor2"}
# }
self._container_families: dict[tuple, set] = {}
for container in containers:
if container.build_flavor:
self._container_families.setdefault(
(container.os_version, container.package_name), set()
).add(container.build_flavor)

for container in containers:
if container.family is not None:
raise ValueError("Container is already part of a ContainerFamily")
container.family = self

def get_all_build_flavors(
self,
container: BaseContainerImage,
) -> list[str]:
"""Return all available build flavors for this container in based on its family"""
return sorted(
self._container_families.get(
(container.os_version, container.package_name), [""]
)
)

def get_default_dockerfile_content(
self,
container: BaseContainerImage,
) -> str:
buildrelease: str = ""
if container.build_release:
buildrelease = f"\n#!BuildVersion: workaround-for-an-obs-bug\n#!BuildRelease: {container.build_release}"
"""Return a default Dockerfile to disable build on default flavor."""
return f"""#!ExclusiveArch: do-not-build
#!ForceMultiVersion{buildrelease}

# For this container we only build the Dockerfile.$flavor builds.
"""

def get_multibuild_file_content(
self,
container: BaseContainerImage,
) -> str:
"""Return the _multibuild file string to write for this container based on its family."""
if not self.check_version_in_uid(container):
return ""

flavors: str = "\n".join(
" " * 4 + f"<package>{pkg}</package>"
for pkg in self.get_all_build_flavors(container)
)
return f"<multibuild>\n{flavors}\n</multibuild>"

def check_version_in_uid(
self,
container: BaseContainerImage,
) -> bool:
"""check if version_in_uid is set to False if a container has more than one flavour"""
if len(self.get_all_build_flavors(container)) > 1 and getattr(
container, "version_in_uid", False
):
return False
return True


def generate_disk_size_constraints(size_gb: int) -> str:
"""Creates the contents of a :file:`_constraints` file for OBS to require
workers with at least ``size_gb`` GB of disk space.
Expand Down
4 changes: 2 additions & 2 deletions src/bci_build/package/apache_tomcat.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

from bci_build.container_attributes import TCP
from bci_build.container_attributes import PackageType
from bci_build.containercrate import ContainerCrate
from bci_build.os_version import CAN_BE_LATEST_OS_VERSION
from bci_build.os_version import OsVersion
from bci_build.package import DOCKERFILE_RUN
from bci_build.package import ApplicationStackContainer
from bci_build.package import ContainerFamily
from bci_build.package import OsContainer
from bci_build.package import Package
from bci_build.package import Replacement
Expand Down Expand Up @@ -150,4 +150,4 @@ def _get_java_packages(jre_major: int) -> list[str]:
)
]

TOMCAT_CRATE = ContainerCrate(TOMCAT_CONTAINERS)
TOMCAT_FAMILIES = ContainerFamily(TOMCAT_CONTAINERS)
4 changes: 2 additions & 2 deletions src/bci_build/package/package_versions.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"5": "2.35",
"6": "2.43",
"7": "2.43",
"Tumbleweed": "2.47",
"Tumbleweed": "2.48",
"version_format": "minor"
},
"helm": {
Expand Down Expand Up @@ -54,7 +54,7 @@
},
"spack": {
"6": "0.21.3",
"7": "0.21.3",
"7": "0.23.0",
"Tumbleweed": "0.23.0"
},
"valkey": {
Expand Down
4 changes: 2 additions & 2 deletions src/bci_build/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,8 @@
<service mode="buildtime" name="{{ image.build_recipe_type }}_label_helper"/>
<service mode="buildtime" name="kiwi_metainfo_helper"/>
{%- set all_build_flavors = [""] %}
{%- if image.crate and image.build_flavor %}
{%- set all_build_flavors = image.crate.all_build_flavors(image) %}
{%- if image.family and image.build_flavor %}
{%- set all_build_flavors = image.family.get_all_build_flavors(image) %}
{%- endif %}
{%- for flavor in all_build_flavors %}
{%- for replacement in image.replacements_via_service %}
Expand Down
5 changes: 3 additions & 2 deletions tests/test_crate.py → tests/test_container_family.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from bci_build.container_attributes import BuildType
from bci_build.containercrate import ContainerCrate
from bci_build.os_version import OsVersion
from bci_build.package import ContainerFamily
from bci_build.package import DevelopmentContainer

_BASE_KWARGS = {
Expand All @@ -19,11 +19,12 @@ def test_multibuild_with_multi_flavor_docker():
**_BASE_KWARGS,
build_recipe_type=BuildType.DOCKER,
build_flavor=flavor,
version_in_uid=False,
)
for flavor in ("flavor1", "flavor2")
]
assert (
ContainerCrate(containers).multibuild(containers[0])
ContainerFamily(containers).get_multibuild_file_content(containers[0])
== """<multibuild>
<package>flavor1</package>
<package>flavor2</package>
Expand Down
4 changes: 2 additions & 2 deletions tests/test_service.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import pytest

from bci_build.container_attributes import BuildType
from bci_build.containercrate import ContainerCrate
from bci_build.os_version import OsVersion
from bci_build.package import ContainerFamily
from bci_build.package import DevelopmentContainer
from bci_build.package import ParseVersion
from bci_build.package import Replacement
Expand Down Expand Up @@ -181,7 +181,7 @@ def test_service_with_multi_flavor_docker():
)
for flavor in ("flavor1", "flavor2")
]
ContainerCrate(containers)
ContainerFamily(containers)

assert (
SERVICE_TEMPLATE.render(
Expand Down
Loading