diff --git a/src/bci_build/containercrate.py b/src/bci_build/containercrate.py
new file mode 100644
index 000000000..a0fc87ebb
--- /dev/null
+++ b/src/bci_build/containercrate.py
@@ -0,0 +1,40 @@
+"""Crate to handle multibuild containers in the generator."""
+
+
+class ContainerCrate:
+ """ContainerCrate is combining multiple container build flavors.
+
+ This provides package-central functions like generating _service and
+ _multibuild files.
+ """
+
+ def __init__(self, containers: list):
+ """Assign the crate for every container."""
+ self._all_build_flavors: dict[tuple, set] = {}
+ for container in containers:
+ if container.build_flavor:
+ self._all_build_flavors.setdefault(
+ (container.os_version, container.package_name), set()
+ ).add(container.build_flavor)
+
+ for container in containers:
+ if container.crate is not None:
+ raise ValueError("Container is already part of a ContainerCrate")
+ container.crate = self
+ container.all_build_flavors = self.all_build_flavors(container)
+
+ def all_build_flavors(self, container):
+ """Return all build flavors for this container in the crate"""
+ return sorted(
+ self._all_build_flavors.get(
+ (container.os_version, container.package_name), [""]
+ )
+ )
+
+ def multibuild(self, container):
+ """Return the _multibuild file string to write for this ContainerCrate."""
+ flavors: str = "\n".join(
+ " " * 4 + f"{pkg}"
+ for pkg in sorted(self.all_build_flavors(container))
+ )
+ return f"\n{flavors}\n"
diff --git a/src/bci_build/package/__init__.py b/src/bci_build/package/__init__.py
index a05d6c55d..009eae5d3 100644
--- a/src/bci_build/package/__init__.py
+++ b/src/bci_build/package/__init__.py
@@ -16,6 +16,7 @@
import jinja2
from packaging import version
+from bci_build.containercrate import ContainerCrate
from bci_build.templates import DOCKERFILE_TEMPLATE
from bci_build.templates import INFOHEADER_TEMPLATE
from bci_build.templates import KIWI_TEMPLATE
@@ -458,6 +459,12 @@ class BaseContainerImage(abc.ABC):
default_factory=dict
)
+ #: build flavors to produce for this container variant
+ build_flavor: str | None = None
+
+ #: create that this container is part of
+ crate: ContainerCrate = None
+
#: Add any replacements via `obs-service-replace_using_package_version
#: `_
#: that are used in this image into this list.
@@ -1137,9 +1144,13 @@ def title(self) -> str:
"""
return f"{self.os_version.distribution_base_name} BCI {self.pretty_name}"
+ @property
+ def readme_name(self) -> str:
+ return f"README.{self.build_flavor}.md" if self.build_flavor else "README.md"
+
@property
def readme_path(self) -> str:
- return f"{self.package_name}/README.md"
+ return f"{self.package_name}/{self.readme_name}"
@property
def readme_url(self) -> str:
@@ -1150,7 +1161,7 @@ def readme_url(self) -> str:
if self.os_version.is_tumbleweed:
return f"https://raw.githubusercontent.com/SUSE/BCI-dockerfile-generator/{self.os_version.deployment_branch_name}/{self.readme_path}"
- return "%SOURCEURL%/README.md"
+ return f"%SOURCEURL%/{self.readme_name}"
@property
def readme(self) -> str:
@@ -1278,14 +1289,17 @@ async def write_file_to_dest(fname: str, contents: str | bytes) -> None:
await write_to_file(os.path.join(dest, fname), contents)
if self.build_recipe_type == BuildType.DOCKER:
- fname = "Dockerfile"
infoheader = textwrap.indent(INFOHEADER_TEMPLATE, "# ")
+ fname = (
+ f"Dockerfile.{self.build_flavor}" if self.build_flavor else "Dockerfile"
+ )
dockerfile = DOCKERFILE_TEMPLATE.render(
image=self,
INFOHEADER=infoheader,
DOCKERFILE_RUN=DOCKERFILE_RUN,
LOG_CLEAN=LOG_CLEAN,
+ BUILD_FLAVOR=self.build_flavor,
)
if dockerfile[-1] != "\n":
dockerfile += "\n"
@@ -1320,9 +1334,21 @@ async def write_file_to_dest(fname: str, contents: str | bytes) -> None:
False
), f"got an unexpected build_recipe_type: '{self.build_recipe_type}'"
+ if self.build_flavor:
+ mname = "_multibuild"
+ tasks.append(
+ asyncio.ensure_future(
+ write_file_to_dest(mname, self.crate.multibuild(self))
+ )
+ )
+ files.append(mname)
+
tasks.append(
asyncio.ensure_future(
- write_file_to_dest("_service", SERVICE_TEMPLATE.render(image=self))
+ write_file_to_dest(
+ "_service",
+ SERVICE_TEMPLATE.render(image=self),
+ )
)
)
@@ -1361,8 +1387,8 @@ async def write_file_to_dest(fname: str, contents: str | bytes) -> None:
tasks.append(write_file_to_dest(fname, contents))
if "README.md" not in self.extra_files:
- files.append("README.md")
- tasks.append(write_file_to_dest("README.md", self.readme))
+ files.append(self.readme_name)
+ tasks.append(write_file_to_dest(self.readme_name, self.readme))
await asyncio.gather(*tasks)
diff --git a/src/bci_build/package/apache_tomcat.py b/src/bci_build/package/apache_tomcat.py
index 62dfd81be..31d849e8a 100644
--- a/src/bci_build/package/apache_tomcat.py
+++ b/src/bci_build/package/apache_tomcat.py
@@ -2,6 +2,7 @@
import datetime
+from bci_build.containercrate import ContainerCrate
from bci_build.package import CAN_BE_LATEST_OS_VERSION
from bci_build.package import DOCKERFILE_RUN
from bci_build.package import OsContainer
@@ -52,9 +53,9 @@ def _get_sac_supported_until(
TOMCAT_CONTAINERS = [
ApplicationCollectionContainer(
name="apache-tomcat",
- package_name=f"apache-tomcat-{tomcat_ver.partition('.')[0]}-java-{jre_version}-image"
+ package_name=f"apache-tomcat-{tomcat_ver.partition('.')[0]}-image"
if os_version.is_tumbleweed
- else f"sac-apache-tomcat-{tomcat_ver.partition('.')[0]}-java{jre_version}-image",
+ else f"sac-apache-tomcat-{tomcat_ver.partition('.')[0]}-image",
pretty_name="Apache Tomcat",
custom_description=(
"Apache Tomcat is a free and open-source implementation of the Jakarta Servlet, "
@@ -73,6 +74,7 @@ def _get_sac_supported_until(
supported_until=_get_sac_supported_until(
os_version=os_version, tomcat_ver=tomcat_ver, jre_major=jre_version
),
+ build_flavor=f"openjdk{jre_version}",
additional_versions=[f"%%tomcat_version%%-openjdk{jre_version}"],
from_target_image=f"{_build_tag_prefix(os_version)}/bci-micro:{OsContainer.version_to_container_os_version(os_version)}",
package_list=[
@@ -124,6 +126,9 @@ def _get_sac_supported_until(
("10.1", OsVersion.TUMBLEWEED, 17),
("9", OsVersion.TUMBLEWEED, 17),
("10.1", OsVersion.SP6, 21),
+ ("10.1", OsVersion.SP6, 17),
# (10.1, OsVersion.SP7, 21),
)
]
+
+TOMCAT_CRATE = ContainerCrate(TOMCAT_CONTAINERS)
diff --git a/src/bci_build/templates.py b/src/bci_build/templates.py
index 42ce416c0..85e6a8588 100644
--- a/src/bci_build/templates.py
+++ b/src/bci_build/templates.py
@@ -194,13 +194,24 @@
"""
+{%- set all_build_flavors = [""] %}
+{%- if image.crate and image.build_flavor %}
+{%- set all_build_flavors = image.crate.all_build_flavors(image) %}
+{%- endif %}
+{%- for flavor in all_build_flavors %}
{%- for replacement in image.replacements_via_service %}
- {% if replacement.file_name != None %}{{replacement.file_name}}{% elif (image.build_recipe_type|string) == "docker" %}Dockerfile{% else %}{{ image.package_name }}.kiwi{% endif %}
+
+{%- if replacement.file_name != None %}{{replacement.file_name}}
+{%- elif (image.build_recipe_type|string) == "docker" %}{% if flavor %}Dockerfile.{{ flavor }}{% else %}Dockerfile{% endif %}
+{%- else %}{{ image.package_name }}.kiwi
+{%- endif %}
{{ replacement.regex_in_build_description }}
{{ replacement.package_name }}{% if replacement.parse_version %}
{{ replacement.parse_version }}{% endif %}
- {% endfor %}
+
+{%- endfor -%}
+{% endfor %}
"""
)
diff --git a/src/staging/bot.py b/src/staging/bot.py
index c7ddded7a..3a82ecd75 100644
--- a/src/staging/bot.py
+++ b/src/staging/bot.py
@@ -759,23 +759,18 @@ async def write_pkg_configs(
will be added
"""
- tasks = [
- self._write_pkg_meta(
- bci,
- git_branch_name=git_branch_name,
- target_obs_project=target_obs_project,
- )
- for bci in packages
- ]
-
+ tasks = []
+ pkg_metas_to_generate = set()
for bci in packages:
- tasks.append(
- self._write_pkg_meta(
- bci,
- git_branch_name=git_branch_name,
- target_obs_project=target_obs_project,
+ if bci.package_name not in pkg_metas_to_generate:
+ pkg_metas_to_generate.add(bci.package_name)
+ tasks.append(
+ self._write_pkg_meta(
+ bci,
+ git_branch_name=git_branch_name,
+ target_obs_project=target_obs_project,
+ )
)
- )
await asyncio.gather(*tasks)
diff --git a/tests/test_service.py b/tests/test_service.py
index b6e5795f7..da9ac19fb 100644
--- a/tests/test_service.py
+++ b/tests/test_service.py
@@ -1,3 +1,4 @@
+from bci_build.containercrate import ContainerCrate
from bci_build.package import BuildType
from bci_build.package import DevelopmentContainer
from bci_build.package import OsVersion
@@ -108,3 +109,38 @@ def test_service_with_replacement_docker():
"""
)
+
+
+def test_service_with_multi_flavor_docker():
+ containers = [
+ DevelopmentContainer(
+ **_BASE_KWARGS,
+ build_recipe_type=BuildType.DOCKER,
+ build_flavor=flavor,
+ replacements_via_service=[
+ Replacement(regex_in_build_description="%%my_ver%%", package_name="sh"),
+ ],
+ )
+ for flavor in ("flavor1", "flavor2")
+ ]
+ containercrate = ContainerCrate(containers)
+
+ assert (
+ SERVICE_TEMPLATE.render(
+ image=containers[0],
+ )
+ == """
+
+
+
+ Dockerfile.flavor1
+ %%my_ver%%
+ sh
+
+
+ Dockerfile.flavor2
+ %%my_ver%%
+ sh
+
+"""
+ )