org.osbuild.grub2.iso: Add support for default menu selection

Currently the grub2 menu defaults to the first entry. This adds support
for setting the default to a later entry. The default in the official
boot.iso is entry 1 -- booting with the iso checksum check.

This includes a test for the new behavior.
This commit is contained in:
Brian C. Lane 2025-07-22 11:20:22 -07:00 committed by Brian C. Lane
parent 841e89fe08
commit f49621ce44
3 changed files with 38 additions and 2 deletions

View file

@ -8,7 +8,7 @@ import osbuild.api
# The main grub2 configuration file template. Used for UEFI.
# NOTE: Changes to this should also be applied to the org.osbuild.grub2.iso.legacy stage
GRUB2_EFI_CFG_TEMPLATE = """
GRUB2_EFI_CFG_TEMPLATE = """$defaultentry
function load_video {
insmod efi_gop
insmod efi_uga
@ -58,6 +58,9 @@ menuentry 'Install ${product} ${version} in FIPS mode' --class fedora --class gn
}
"""
DEFAULT_ENTRY_TEMPLATE = """set default="${default}"
"""
def main(root, options):
name = options["product"]["name"]
@ -69,6 +72,7 @@ def main(root, options):
kopts = options["kernel"].get("opts")
cfg = options.get("config", {})
timeout = cfg.get("timeout", 60)
default = cfg.get("default") # None indicates not set
fips = options.get("fips", False)
efidir = os.path.join(root, "EFI", "BOOT")
@ -101,9 +105,16 @@ def main(root, options):
"isolabel": isolabel,
"root": " ".join(kopts),
"timeout": timeout,
"defaultentry": "",
"fipsentry": ""
}
# Insert default menu selection
if default is not None:
default_tmpl = string.Template(DEFAULT_ENTRY_TEMPLATE)
defaultentry = default_tmpl.safe_substitute({"default": default})
tplt_variables["defaultentry"] = defaultentry
# Insert optional fips menu entry
if fips:
fips_tmpl = string.Template(FIPS_ENTRY_TEMPLATE)

View file

@ -69,6 +69,10 @@
"type": "integer",
"minimum": 0,
"default": 60
},
"default": {
"description": "Default menu entry",
"type": "integer"
}
}
}

View file

@ -58,13 +58,18 @@ menuentry 'Install Fedora 42 in FIPS mode' --class fedora --class gnu-linux --cl
}
"""
CONFIG_DEFAULT = """set default="1"
"""
@patch("shutil.copy2")
@pytest.mark.parametrize("test_data,expected_conf", [
# default
({}, CONFIG_PART_1 + CONFIG_PART_2),
# fips menu enable
({"fips": True}, CONFIG_PART_1 + CONFIG_FIPS + CONFIG_PART_2)
({"fips": True}, CONFIG_PART_1 + CONFIG_FIPS + CONFIG_PART_2),
# default to menu entry 1
({"config": {"default": 1}}, CONFIG_DEFAULT + CONFIG_PART_1 + CONFIG_PART_2)
])
def test_grub2_iso(mocked_copy2, tmp_path, stage_module, test_data, expected_conf):
treedir = tmp_path / "tree"
@ -155,6 +160,22 @@ def test_grub2_iso(mocked_copy2, tmp_path, stage_module, test_data, expected_con
"fips": True,
}, "",
),
# good + default
(
{
"isolabel": "an-isolabel",
"product": {
"name": "a-name",
"version": "a-version",
},
"kernel": {
"dir": "/path/to",
},
"config": {
"default": 1,
}
}, "",
),
])
def test_schema_validation(stage_schema, test_data, expected_err):
test_input = {