diff --git a/.editorconfig b/.editorconfig index 5fcf8bcf..bb6ec010 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,6 +14,6 @@ indent_style = space indent_size = 2 # Python -[{*.py,eos-image-builder,run-build,helpers/{assemble-manifest,fetch-remote-collection-id,generate-ovf-files,kill-chroot-procs,kolibri-pick-content-from-channel,mutable-path,packages-manifest},hooks/{content/{50-flatpak,50-kolibri-content},image/{50-flatpak.chroot,52-ek-content-cache,62-kolibri-options,70-flatpak-manifest,70-ostree-manifest}}}] +[{*.py,eos-image-builder,run-build,helpers/{assemble-manifest,fetch-remote-collection-id,generate-ovf-files,kill-chroot-procs,kolibri-pick-content-from-channel,mutable-path,packages-manifest},hooks/{content/{50-flatpak,50-kolibri-content},image/{50-flatpak.chroot,52-ek-content-cache,62-kolibri-options,70-flatpak-appstream-catalog,70-flatpak-manifest,70-ostree-manifest}}}] indent_size = 4 max_line_length = 88 diff --git a/.flake8 b/.flake8 index 493ae6c5..20c31f82 100644 --- a/.flake8 +++ b/.flake8 @@ -17,6 +17,7 @@ filename = ./hooks/content/50-kolibri-content, ./hooks/image/50-flatpak.chroot, ./hooks/image/62-kolibri-options, + ./hooks/image/70-flatpak-appstream-catalog, ./hooks/image/70-flatpak-manifest, ./hooks/image/70-ostree-manifest, ./run-build diff --git a/config/defaults.ini b/config/defaults.ini index e4ad61dc..8fa9c2be 100644 --- a/config/defaults.ini +++ b/config/defaults.ini @@ -65,6 +65,7 @@ base = bullseye # Packages to install in the buildroot packages_add = + appstream-util attr awscli ca-certificates @@ -88,6 +89,7 @@ packages_add = json-glib-tools jq libfsapfs-utils + librsvg2-common mount openssh-client ostree @@ -145,6 +147,7 @@ hooks_add = 62-kolibri-automatic-provision 62-kolibri-options 63-icon-grid + 70-flatpak-appstream-catalog 70-flatpak-manifest 70-ostree-manifest 80-ldconfig-aux-cache.chroot diff --git a/hooks/image/70-flatpak-appstream-catalog b/hooks/image/70-flatpak-appstream-catalog new file mode 100755 index 00000000..ac20ce29 --- /dev/null +++ b/hooks/image/70-flatpak-appstream-catalog @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 + +# Construct AppStream XML for preinstalled apps + +import glob +import logging +import os +import subprocess + +logging.basicConfig( + level=logging.INFO, + format='+ %(asctime)s %(levelname)s %(name)s: %(message)s', + datefmt='%H:%M:%S' +) +logger = logging.getLogger(os.path.basename(__file__)) + + +def main(): + root = os.path.join(os.environ['OSTREE_VAR'], 'lib/flatpak/exports') + + # Find the metainfo files to include in the AppStream. + ids = [] + for metainfo in glob.glob(os.path.join(root, 'share/metainfo/*.xml')): + # Everything here should end in .metainfo.xml or .appdata.xml, but + # let's be sure. + if not (metainfo.endswith('.metainfo.xml') or + metainfo.endswith('.appdata.xml')): + raise Exception(f'Unexpected metainfo file {metainfo}') + + # It's kind of bad to reject these files here because they're obviously + # valid enough for Flathub to have published them. Reasons that appstream-util + # 0.7.18-1+deb11u1 considers appdata to be invalid include: + # + # - Using a attribute from a more recent version of the spec + # (dozens of apps, including GIMP) + # + # - A
  • element contains a nested element (Pods; OK this is not a + # preinstalled app, but still) + # + # - elements are not in order (FileRoller) + # + # None of these are reasons to exclude the app from the catalogue but there is + # no way to tell the tool to ignore them: if we pass these files to + # appstream-compose it will fail. + # + # TODO: Switch to appstreamcli validate-relax once we are on Bookworm and can + # use appstreamcli compose. + cmd = ('appstream-util', 'validate-relax', '--nonet', metainfo) + proc = subprocess.run(cmd) + if proc.returncode != 0: + logger.warning('Skipping %s considered invalid by appstream-util', metainfo) + continue + + # Convert the filename to an "app-id" as expected by + # appstream-compose. This isn't necessarily the app ID but the stem + # of the metainfo XML file without .metainfo.xml or .appdata.xml. + # Most of the time these are the same. + metainfo_id = os.path.basename(metainfo) + if metainfo_id.endswith('.metainfo.xml'): + metainfo_id = metainfo_id.rpartition('.metainfo.xml')[0] + else: + metainfo_id = metainfo_id.rpartition('.appdata.xml')[0] + logger.info('Including metainfo ID %s', metainfo_id) + ids.append(metainfo_id) + + # Exit if there aren't any desktop metainfo files. + if len(ids) == 0: + logger.info('No metainfo files') + return + + # Compose the AppStream XML. + # TODO: Switch to appstreamcli compose once we are on Bookworm + cmd = [ + 'appstream-compose', + f'--origin={os.environ["EIB_OUTVERSION"]}', + f'--prefix={root}', + f'--output-dir={os.environ["EIB_OUTDIR"]}', + # We don't want to export the icons but we can't tell + # appstream-compose not to do so... + f'--icons-dir={os.environ["EIB_TMPDIR"]}/appstream-compose-icons', + ] + cmd += ids + logger.info('Composing AppStream XML: %s', ' '.join(cmd)) + subprocess.run(cmd, check=True) + + +if __name__ == '__main__': + main()