Skip to content

Commit

Permalink
Make SplitArtifacts= take a list of values
Browse files Browse the repository at this point in the history
This allows more precision on which artifacts are actually split out of
the image and placed into the output directory. Defaults to splitting
the UKI, vmlinuz and the initrd out.
  • Loading branch information
NekkoDroid committed Oct 18, 2024
1 parent 1c6da5a commit f1b93df
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 26 deletions.
23 changes: 18 additions & 5 deletions mkosi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
from mkosi.config import (
PACKAGE_GLOBS,
Args,
ArtifactOutput,
Bootloader,
Cacheonly,
Compression,
Expand Down Expand Up @@ -2100,8 +2101,11 @@ def make_uki(
output,
)

extract_pe_section(context, output, ".linux", context.staging / context.config.output_split_kernel)
extract_pe_section(context, output, ".initrd", context.staging / context.config.output_split_initrd)
if ArtifactOutput.kernel in context.config.split_artifacts:
extract_pe_section(context, output, ".linux", context.staging / context.config.output_split_kernel)

if ArtifactOutput.initrd in context.config.split_artifacts:
extract_pe_section(context, output, ".initrd", context.staging / context.config.output_split_initrd)


def compressor_command(context: Context, compression: Compression) -> list[PathString]:
Expand Down Expand Up @@ -2178,6 +2182,9 @@ def get_uki(context: Context) -> Optional[Path]:


def copy_uki(context: Context) -> None:
if ArtifactOutput.uki not in context.config.split_artifacts:
return

if (context.staging / context.config.output_split_uki).exists():
return

Expand All @@ -2186,6 +2193,9 @@ def copy_uki(context: Context) -> None:


def copy_vmlinuz(context: Context) -> None:
if ArtifactOutput.kernel not in context.config.split_artifacts:
return

if (context.staging / context.config.output_split_kernel).exists():
return

Expand All @@ -2201,6 +2211,9 @@ def copy_vmlinuz(context: Context) -> None:


def copy_initrd(context: Context) -> None:
if ArtifactOutput.initrd not in context.config.split_artifacts:
return

if not want_initrd(context):
return

Expand Down Expand Up @@ -3378,7 +3391,7 @@ def make_extension_image(context: Context, output: Path) -> None:
] # fmt: skip
if context.config.sector_size:
cmdline += ["--sector-size", str(context.config.sector_size)]
if context.config.split_artifacts:
if ArtifactOutput.partitions in context.config.split_artifacts:
cmdline += ["--split=yes"]

with complete_step(f"Building {context.config.output_format} extension image"):
Expand All @@ -3400,7 +3413,7 @@ def make_extension_image(context: Context, output: Path) -> None:

logging.debug(json.dumps(j, indent=4))

if context.config.split_artifacts:
if ArtifactOutput.partitions in context.config.split_artifacts:
for p in (Partition.from_dict(d) for d in j):
if p.split_path:
maybe_compress(context, context.config.compress_output, p.split_path)
Expand Down Expand Up @@ -3645,7 +3658,7 @@ def build_image(context: Context) -> None:
partitions = make_disk(context, msg="Formatting ESP/XBOOTLDR partitions")
grub_bios_setup(context, partitions)

if context.config.split_artifacts:
if ArtifactOutput.partitions in context.config.split_artifacts:
make_disk(context, split=True, msg="Extracting partitions")

copy_nspawn_settings(context)
Expand Down
49 changes: 42 additions & 7 deletions mkosi/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,16 @@ def native(cls) -> "Architecture":
return cls.from_uname(platform.machine())


def parse_boolean(s: str) -> bool:
class ArtifactOutput(enum.Flag):
uki = 1
kernel = 2
initrd = 4
partitions = 8
no = uki | kernel | initrd
yes = uki | kernel | initrd | partitions


def try_parse_boolean(s: str) -> Optional[bool]:

Check notice

Code scanning / CodeQL

Explicit returns mixed with implicit (fall through) returns Note

Mixing implicit and explicit returns may indicate an error as implicit returns always return None.
"Parse 1/true/yes/y/t/on as true and 0/false/no/n/f/off/None as false"

s_l = s.lower()
Expand All @@ -509,7 +518,14 @@ def parse_boolean(s: str) -> bool:
if s_l in {"0", "false", "no", "n", "f", "off", "never"}:
return False

die(f"Invalid boolean literal: {s!r}")

def parse_boolean(s: str) -> bool:
value = try_parse_boolean(s)

if value == None:

Check notice

Code scanning / CodeQL

Testing equality to None Note

Testing for None should use the 'is' operator.
die(f"Invalid boolean literal: {s!r}")

return value


def parse_path(
Expand Down Expand Up @@ -1291,6 +1307,21 @@ def config_parse_key_source(value: Optional[str], old: Optional[KeySource]) -> O
return KeySource(type=type, source=source)


def config_parse_artifact_output(value: Optional[str], old: Optional[ArtifactOutput]) -> Optional[ArtifactOutput]:
if not value:
return None

boolean_value = try_parse_boolean(value)
if boolean_value != None:
return ArtifactOutput.yes if boolean_value else ArtifactOutput.no

try:
new = ArtifactOutput[value]
except KeyError:
die(f"'{value}' is not a valid {ArtifactOutput.__name__}")

return old | new if old else new

class SettingScope(StrEnum):
# Not passed down to subimages
local = enum.auto()
Expand Down Expand Up @@ -1596,7 +1627,7 @@ class Config:
output_mode: Optional[int]
image_id: Optional[str]
image_version: Optional[str]
split_artifacts: bool
split_artifacts: ArtifactOutput
repart_dirs: list[Path]
sysupdate_dir: Optional[Path]
sector_size: Optional[int]
Expand Down Expand Up @@ -2292,11 +2323,11 @@ def parse_ini(path: Path, only_sections: Collection[str] = ()) -> Iterator[tuple
),
ConfigSetting(
dest="split_artifacts",
metavar="BOOL",
nargs="?",
section="Output",
parse=config_parse_boolean,
help="Generate split partitions",
parse=config_parse_artifact_output,
default=ArtifactOutput.no,
help="Split artifacts out of the final image",
),
ConfigSetting(
dest="repart_dirs",
Expand Down Expand Up @@ -4508,7 +4539,7 @@ def summary(config: Config) -> str:
Output Mode: {format_octal_or_default(config.output_mode)}
Image ID: {config.image_id}
Image Version: {config.image_version}
Split Artifacts: {yes_no(config.split_artifacts)}
Split Artifacts: {config.split_artifacts}
Repart Directories: {line_join_list(config.repart_dirs)}
Sector Size: {none_to_default(config.sector_size)}
Overlay: {yes_no(config.overlay)}
Expand Down Expand Up @@ -4783,6 +4814,9 @@ def uki_profile_transformer(
) -> list[UKIProfile]:
return [UKIProfile(profile=profile["Profile"], cmdline=profile["Cmdline"]) for profile in profiles]

def artifact_output_transformer(output: str, fieldtype: type[ArtifactOutput]) -> ArtifactOutput:
return ArtifactOutput[output]

# The type of this should be
# dict[
# type,
Expand Down Expand Up @@ -4824,6 +4858,7 @@ def uki_profile_transformer(
Vmm: enum_transformer,
list[PEAddon]: pe_addon_transformer,
list[UKIProfile]: uki_profile_transformer,
ArtifactOutput: artifact_output_transformer,
}

def json_transformer(key: str, val: Any) -> Any:
Expand Down
17 changes: 12 additions & 5 deletions mkosi/resources/man/mkosi.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -596,13 +596,20 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`,
invoked. The image ID is automatically added to `/usr/lib/os-release`.

`SplitArtifacts=`, `--split-artifacts`
: If specified and building a disk image, pass `--split=yes` to systemd-repart
to have it write out split partition files for each configured partition.
Read the [man](https://www.freedesktop.org/software/systemd/man/systemd-repart.html#--split=BOOL)
: The artifact types to split out of the final image. A comma-delimited
list consisting of `uki`, `kernel`, `initrd` and `partitions`. When
building a bootable image `kernel` and `initrd` correspond to their
artifact found in the image (or in the UKI), while `uki` copies out the
entire UKI.

When building a disk image and `partitions` is specified,
pass `--split=yes` to systemd-repart to have it write out split partition
files for each configured partition. Read the
[man](https://www.freedesktop.org/software/systemd/man/systemd-repart.html#--split=BOOL)
page for more information. This is useful in A/B update scenarios where
an existing disk image shall be augmented with a new version of a
root or `/usr` partition along with its Verity partition and unified
kernel.
kernel. By default `uki`, `kernel` and `initrd` are split out.

`RepartDirectories=`, `--repart-dir=`
: Paths to directories containing systemd-repart partition definition
Expand Down Expand Up @@ -2229,7 +2236,7 @@ current working directory. The following scripts are supported:
* If **`mkosi.clean`** (`CleanScripts=`) exists, it is executed right
after the outputs of a previous build have been cleaned up. A clean
script can clean up any outputs that mkosi does not know about (e.g.
artifacts from `SplitArtifacts=yes` or RPMs built in a build script).
artifacts from `SplitArtifacts=partitions` or RPMs built in a build script).
Note that this script does not use the tools tree even if one is configured.

* If **`mkosi.version`** exists and is executable, it is run during
Expand Down
6 changes: 3 additions & 3 deletions mkosi/sysupdate.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
import sys
from pathlib import Path

from mkosi.config import Args, Config
from mkosi.config import Args, ArtifactOutput, Config
from mkosi.log import die
from mkosi.run import run
from mkosi.types import PathString


def run_sysupdate(args: Args, config: Config) -> None:
if not config.split_artifacts:
die("SplitArtifacts= must be enabled to be able to use mkosi sysupdate")
if ArtifactOutput.partitions not in config.split_artifacts:
die("SplitArtifacts=partitions must be set to be able to use mkosi sysupdate")

if not config.sysupdate_dir:
die(
Expand Down
9 changes: 5 additions & 4 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from mkosi import expand_kernel_specifiers
from mkosi.config import (
Architecture,
ArtifactOutput,
Compression,
Config,
ConfigFeature,
Expand Down Expand Up @@ -228,26 +229,26 @@ def test_parse_config(tmp_path: Path) -> None:
(d / "abc/mkosi.conf.d/abc.conf").write_text(
"""\
[Output]
SplitArtifacts=yes
SplitArtifacts=partitions
"""
)

with chdir(d):
_, [config] = parse_config()
assert config.bootable == ConfigFeature.auto
assert config.split_artifacts is False
assert config.split_artifacts == [ArtifactOutput.uki, ArtifactOutput.kernel, ArtifactOutput.initrd]

# Passing the directory should include both the main config file and the dropin.
_, [config] = parse_config(["--include", os.fspath(d / "abc")] * 2)
assert config.bootable == ConfigFeature.enabled
assert config.split_artifacts is True
assert config.split_artifacts == [ArtifactOutput.partitions]
# The same extra config should not be parsed more than once.
assert config.build_packages == ["abc"]

# Passing the main config file should not include the dropin.
_, [config] = parse_config(["--include", os.fspath(d / "abc/mkosi.conf")])
assert config.bootable == ConfigFeature.enabled
assert config.split_artifacts is False
assert config.split_artifacts == [ArtifactOutput.uki, ArtifactOutput.kernel, ArtifactOutput.initrd]

(d / "mkosi.images").mkdir()

Expand Down
9 changes: 7 additions & 2 deletions tests/test_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from mkosi.config import (
Architecture,
Args,
ArtifactOutput,
BiosBootloader,
Bootloader,
Cacheonly,
Expand Down Expand Up @@ -333,7 +334,11 @@ def test_config() -> None:
}
],
"SourceDateEpoch": 12345,
"SplitArtifacts": true,
"SplitArtifacts": [
"kernel",
"initrd",
"partitions"
],
"Ssh": false,
"SshCertificate": "/path/to/cert",
"SshKey": null,
Expand Down Expand Up @@ -533,7 +538,7 @@ def test_config() -> None:
sign_expected_pcr_certificate=Path("/my/cert"),
skeleton_trees=[ConfigTree(Path("/foo/bar"), Path("/")), ConfigTree(Path("/bar/baz"), Path("/qux"))],
source_date_epoch=12345,
split_artifacts=True,
split_artifacts=ArtifactOutput.yes,
ssh=False,
ssh_certificate=Path("/path/to/cert"),
ssh_key=None,
Expand Down

0 comments on commit f1b93df

Please sign in to comment.