From b4c0ae77456df60c327a198186a088aeca0c40e4 Mon Sep 17 00:00:00 2001
From: Kalev Lember <klember@redhat.com>
Date: Feb 05 2024 15:59:19 +0000
Subject: [PATCH 1/2] Add a new "build-extension" key and initial support for extensions


This adds a new "build-extension" boolean key to container yaml,
matching how it's named in flatpak-builder, making it possible to build
flatpak extensions.

---

diff --git a/flatpak_module_tools/container_spec.py b/flatpak_module_tools/container_spec.py
index a818dd8..20f56f5 100644
--- a/flatpak_module_tools/container_spec.py
+++ b/flatpak_module_tools/container_spec.py
@@ -159,6 +159,7 @@ class FlatpakSpec(BaseSpec):
         self.base_image = self._get_str('base_image', None)
         self.branch = self._get_str('branch', 'stable')
         self.build_runtime = self._get_bool('build-runtime', False)
+        self.build_extension = self._get_bool('build-extension', False)
         self.cleanup_commands = self._get_str('cleanup-commands', None)
         self.command = self._get_str('command', None)
         self.component = self._get_str('component', None)
diff --git a/flatpak_module_tools/flatpak_builder.py b/flatpak_module_tools/flatpak_builder.py
index fe6c650..6c9769b 100644
--- a/flatpak_module_tools/flatpak_builder.py
+++ b/flatpak_module_tools/flatpak_builder.py
@@ -293,6 +293,7 @@ class FileTreeProcessor:
 
 class BaseFlatpakSourceInfo(ABC):
     runtime: bool
+    extension: bool
     spec: FlatpakSpec
 
     @abstractmethod
@@ -335,6 +336,8 @@ class ModuleFlatpakSourceInfo(BaseFlatpakSourceInfo):
         # A runtime module must have a 'runtime' profile, but can have other
         # profiles for SDKs, minimal runtimes, etc.
         self.runtime = 'runtime' in base_module.mmd.get_profile_names()
+        # Extension support not implemented for module backend
+        self.extension = False
 
         if profile is None:
             profile = 'runtime' if self.runtime else 'default'
@@ -507,6 +510,7 @@ class PackageFlatpakSourceInfo(BaseFlatpakSourceInfo):
         self.spec = spec
         self.runtime = spec.build_runtime
         self.runtime_info = runtime_info
+        self.extension = spec.build_extension
 
     def precheck(self):
         pass
@@ -767,7 +771,7 @@ class FlatpakBuilder:
         if spec.finish_args:
             # shlex.split(None) reads from standard input, so avoid that
             finish_args = shlex.split(spec.finish_args, comments=True)
-        if spec.command and not self.source.runtime:
+        if spec.command and not self.source.runtime and not self.source.extension:
             finish_args = ['--command', spec.command] + finish_args
 
         subprocess.check_call(['flatpak', 'build-finish'] + finish_args + [builddir])
@@ -1051,7 +1055,7 @@ class FlatpakBuilder:
     def build_container(self, tarred_filesystem: str, tar_outfile: bool = True):
         outfile = os.path.join(self.workdir, 'flatpak-oci-image')
 
-        if self.source.runtime:
+        if self.source.runtime or self.source.extension:
             ref_name = self._create_runtime_oci(tarred_filesystem, outfile)
         else:
             ref_name = self._create_app_oci(tarred_filesystem, outfile)

From e97b633c314f97fe641d9d5a772f6d57cd414922 Mon Sep 17 00:00:00 2001
From: Kalev Lember <klember@redhat.com>
Date: Feb 05 2024 15:59:19 +0000
Subject: [PATCH 2/2] Implement missing --extra-data finish arg


This can be useful for extensions that want to download extra data
during install time.

---

diff --git a/flatpak_module_tools/flatpak_builder.py b/flatpak_module_tools/flatpak_builder.py
index 6c9769b..337952f 100644
--- a/flatpak_module_tools/flatpak_builder.py
+++ b/flatpak_module_tools/flatpak_builder.py
@@ -27,6 +27,7 @@ import subprocess
 import tarfile
 from textwrap import dedent
 from xml.etree import ElementTree
+from gi.repository import GLib
 
 from .container_spec import FlatpakSpec
 from .utils import Arch, RuntimeInfo
@@ -787,6 +788,44 @@ class FlatpakBuilder:
 
         return repo
 
+    def _get_extra_data(self, metadata_file):
+        metadata_config = RawConfigParser()
+        metadata_config.optionxform = str  # type: ignore
+        with open(metadata_file, 'r') as f:
+            metadata_config.read_file(f)
+
+        if 'Extra Data' in metadata_config:
+            extra_data = metadata_config['Extra Data']
+
+            extra_data_uri = None
+            extra_data_checksum = None
+            extra_data_name = None
+            extra_data_size = 0
+            extra_data_installed_size = 0
+
+            if 'uri' in extra_data:
+                extra_data_uri = extra_data.get('uri')
+
+            if 'name' in extra_data:
+                extra_data_name = extra_data.get('name')
+            else:
+                extra_data_name = os.path.basename(extra_data_uri)
+
+            if 'checksum' in extra_data:
+                extra_data_checksum = extra_data.get('checksum')
+            if 'size' in extra_data:
+                extra_data_size = extra_data.getint('size')
+            if 'installed-size' in extra_data:
+                extra_data_installed_size = extra_data.getint('installed-size')
+
+            extra_data_variant = GLib.Variant("a(ayttays)",
+                                              [(extra_data_name.encode("UTF-8") + b'\x00',
+                                                int.from_bytes(extra_data_size.to_bytes(8, byteorder="little"), byteorder="big"),
+                                                int.from_bytes(extra_data_installed_size.to_bytes(8, byteorder="little"), byteorder="big"),
+                                                bytes.fromhex(extra_data_checksum),
+                                                extra_data_uri)])
+            return extra_data_variant.print_(True)
+
     def _create_runtime_oci(self, tarred_filesystem, outfile):
         spec = self.source.spec
 
@@ -845,6 +884,11 @@ class FlatpakBuilder:
             commit_args += ['--add-metadata-string',
                             'ostree.endoflife-rebase=' + spec.end_of_life_rebase]
 
+        extra_data = self._get_extra_data(os.path.join(builddir, 'metadata'))
+        if extra_data is not None:
+            commit_args += ['--add-metadata',
+                            "xa.extra-data-sources=" + extra_data]
+
         subprocess.check_call(['ostree', 'commit'] + commit_args)
         subprocess.check_call(['ostree', 'summary', '-u', '--repo', repo])
 

