Skip to content

Commit cb31259

Browse files
committed
Add SplitArtifacts=metainfo
GNOME Software parses .metainfo.xml files to provide nice metadata for artifacts it downloads/updates. Follow the spec and add parameter for it, filling the content from os-release. https://www.freedesktop.org/software/appstream/docs/sect-Metadata-OS.html
1 parent bae79ab commit cb31259

4 files changed

Lines changed: 66 additions & 2 deletions

File tree

mkosi/__init__.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,64 @@ def configure_os_release(context: Context) -> None:
388388
if ArtifactOutput.os_release in context.config.split_artifacts:
389389
shutil.copy(osrelease, context.staging / context.config.output_split_os_release)
390390

391+
if ArtifactOutput.metainfo in context.config.split_artifacts:
392+
# https://www.freedesktop.org/software/appstream/docs/sect-Metadata-OS.html
393+
metainfo = '<?xml version="1.0" encoding="UTF-8"?>\n'
394+
metainfo += '<component type="operating-system">\n'
395+
id = name = home_url = pretty_name = None
396+
for line in osrelease.read_text().splitlines():
397+
if line.startswith("ID="):
398+
id = line.split("=")[1].strip('"')
399+
elif line.startswith("NAME="):
400+
name = line.split("=")[1].strip('"')
401+
elif line.startswith("HOME_URL="):
402+
home_url = line.split("=")[1].strip('"')
403+
elif line.startswith("PRETTY_NAME="):
404+
pretty_name = line.split("=")[1].strip('"')
405+
406+
# The <id> format is reverse DNS style with home_url + id
407+
if home_url:
408+
from urllib.parse import urlparse
409+
410+
url = urlparse(home_url)
411+
if url.netloc:
412+
netloc = url.netloc.split(".")
413+
netloc.reverse()
414+
netloc.remove("www")
415+
id_field = ".".join(netloc)
416+
if id:
417+
id_field += f".{id}"
418+
metainfo += f" <id>{id_field}</id>\n"
419+
else:
420+
metainfo += f" <id>{id}</id>\n"
421+
if name:
422+
metainfo += f" <name>{name}</name>\n"
423+
elif context.config.image_id:
424+
metainfo += f" <name>{context.config.image_id}</name>\n"
425+
metainfo += f" <summary>{id} image built with mkosi</summary>\n"
426+
if pretty_name:
427+
metainfo += f" <description><p>{pretty_name} built with mkosi</p></description>\n"
428+
else:
429+
metainfo += " <description><p>Image built with mkosi</p></description>\n"
430+
if home_url:
431+
metainfo += f' <url type="homepage">{home_url}</url>\n'
432+
metainfo += (
433+
' <icon type="remote">https://brand.systemd.io/assets/svg/systemd-logomark.svg</icon>\n'
434+
)
435+
metainfo += " <metadata_license>FSFAP</metadata_license>\n"
436+
metainfo += " <releases>\n"
437+
timestamp = (
438+
datetime.datetime.fromtimestamp(context.config.source_date_epoch, tz=datetime.timezone.utc)
439+
if context.config.source_date_epoch is not None
440+
else datetime.datetime.now(tz=datetime.timezone.utc)
441+
).isoformat()
442+
metainfo += f' <release version="{context.config.image_version}" date="{timestamp}" type="development">\n' # noqa E501
443+
metainfo += " <description></description>\n"
444+
metainfo += " </release>\n"
445+
metainfo += " </releases>\n"
446+
metainfo += "</component>\n"
447+
(context.staging / context.config.output_split_metainfo).write_text(metainfo)
448+
391449

392450
def configure_extension_release(context: Context) -> None:
393451
if context.config.output_format not in (OutputFormat.sysext, OutputFormat.confext):

mkosi/config.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,7 @@ class ArtifactOutput(StrEnum):
575575
pcrs = enum.auto()
576576
roothash = enum.auto()
577577
os_release = enum.auto()
578+
metainfo = enum.auto()
578579

579580
@staticmethod
580581
def compat_no() -> list["ArtifactOutput"]:
@@ -2132,6 +2133,10 @@ def output_split_roothash(self) -> str:
21322133
def output_split_os_release(self) -> str:
21332134
return f"{self.output}.osrelease"
21342135

2136+
@property
2137+
def output_split_metainfo(self) -> str:
2138+
return f"{self.output}.metainfo.xml"
2139+
21352140
@property
21362141
def output_nspawn_settings(self) -> str:
21372142
return f"{self.output}.nspawn"
@@ -2173,6 +2178,7 @@ def outputs(self) -> list[str]:
21732178
self.output_split_pcrs,
21742179
self.output_split_roothash,
21752180
self.output_split_os_release,
2181+
self.output_split_metainfo,
21762182
self.output_nspawn_settings,
21772183
self.output_checksum,
21782184
self.output_signature,

mkosi/resources/man/mkosi.1.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -624,7 +624,7 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`,
624624
`SplitArtifacts=`, `--split-artifacts=`
625625
: The artifact types to split out of the final image. A comma-delimited
626626
list consisting of `uki`, `kernel`, `initrd`, `os-release`, `prcs`, `partitions`,
627-
`roothash` and `tar`. When building a bootable image `kernel` and `initrd`
627+
`metainfo`, `roothash` and `tar`. When building a bootable image `kernel` and `initrd`
628628
correspond to their artifact found in the image (or in the UKI),
629629
while `uki` copies out the entire UKI. If `pcrs` is specified, a JSON
630630
file containing the pre-calculated TPM2 digests is written out, according

mkosi/resources/mkosi-obs/mkosi.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ LocalMirror=file:///.build.binaries/
1414
[Output]
1515
OutputDirectory=
1616
Checksum=yes
17-
SplitArtifacts=pcrs,roothash
17+
SplitArtifacts=pcrs,roothash,metainfo
1818
CompressOutput=zstd
1919

2020
[Validation]

0 commit comments

Comments
 (0)