From d566c68f94f3fed0a91683f471e51b3dfcf2fdfa Mon Sep 17 00:00:00 2001 From: Simon de Vlieger Date: Tue, 17 Jun 2025 17:29:20 +0200 Subject: [PATCH] stages/wsl-distribution: stage to write wsl config This is a new stage to be able to write a (subset) of relevant keys into the `wsl-distribution` configuration file. The file, and these options, are necessary for the enablement of fancy logos and experiences in WSL2. Signed-off-by: Simon de Vlieger --- stages/org.osbuild.wsl-distribution.conf | 60 ++++++++++++++ ...rg.osbuild.wsl-distribution.conf.meta.json | 46 +++++++++++ stages/test/test_wsl_distribution_conf.py | 80 +++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100755 stages/org.osbuild.wsl-distribution.conf create mode 100644 stages/org.osbuild.wsl-distribution.conf.meta.json create mode 100644 stages/test/test_wsl_distribution_conf.py diff --git a/stages/org.osbuild.wsl-distribution.conf b/stages/org.osbuild.wsl-distribution.conf new file mode 100755 index 00000000..01466961 --- /dev/null +++ b/stages/org.osbuild.wsl-distribution.conf @@ -0,0 +1,60 @@ +#!/usr/bin/python3 +import sys + +import iniparse + +import osbuild.api + + +def main(tree, options): + oobe = options.get("oobe", {}) + shortcut = options.get("shortcut", {}) + + # Don't write an empty file + if not oobe and not shortcut: + return 0 + + wsl_conf_path = f"{tree}/etc/wsl-distribution.conf" + wsl_config = iniparse.SafeConfigParser() + + try: + with open(wsl_conf_path, "r", encoding="utf8") as f: + wsl_config.readfp(f) + except FileNotFoundError: + print( + f"WSL distribution configuration file '{wsl_conf_path}' does not exist, will be created." + ) + + if oobe: + if not wsl_config.has_section("oobe"): + wsl_config.add_section("oobe") + + if "default_uid" in oobe: + wsl_config.set("oobe", "defaultUid", str(oobe["default_uid"])) + + if "default_name" in oobe: + wsl_config.set("oobe", "defaultName", oobe["default_name"]) + + if shortcut: + if not wsl_config.has_section("shortcut"): + wsl_config.add_section("shortcut") + + if "enabled" in shortcut: + # this turns the boolean `True` into `true` (or `False`) + wsl_config.set( + "shortcut", "enabled", str(shortcut["enabled"]).lower() + ) + + if "icon" in shortcut: + wsl_config.set("shortcut", "icon", shortcut["icon"]) + + with open(wsl_conf_path, mode="w", encoding="utf8") as f: + wsl_config.write(f) + + return 0 + + +if __name__ == "__main__": + args = osbuild.api.arguments() + r = main(args["tree"], args["options"]) + sys.exit(r) diff --git a/stages/org.osbuild.wsl-distribution.conf.meta.json b/stages/org.osbuild.wsl-distribution.conf.meta.json new file mode 100644 index 00000000..0e146758 --- /dev/null +++ b/stages/org.osbuild.wsl-distribution.conf.meta.json @@ -0,0 +1,46 @@ +{ + "summary": "Configure distribution settings in Windows Subsystem for Linux.", + "description": [ + "The stage configures the WSL distribution settings on the system." + ], + "schema_2": { + "options": { + "additionalProperties": false, + "description": "WSL distribution configuration.", + "properties": { + "oobe": { + "type": "object", + "description": "Configures the [oobe] section.", + "additionalProperties": false, + "minProperties": 1, + "properties": { + "default_uid": { + "type": "integer", + "default": 1000, + "description": "Default login UID." + }, + "default_name": { + "type": "string", + "description": "Name of the distribution." + } + } + }, + "shortcut": { + "type": "object", + "description": "Configures the [shortcut] section.", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable a shortcut icon." + }, + "icon": { + "type": "string", + "description": "Path of the shortcut icon." + } + } + } + } + } + } +} diff --git a/stages/test/test_wsl_distribution_conf.py b/stages/test/test_wsl_distribution_conf.py new file mode 100644 index 00000000..f15d3225 --- /dev/null +++ b/stages/test/test_wsl_distribution_conf.py @@ -0,0 +1,80 @@ +#!/usr/bin/python3 + +import pytest + +from osbuild import testutil + +STAGE_NAME = "org.osbuild.wsl-distribution.conf" + + +@pytest.mark.parametrize("test_data,expected_err", [ + # bad + ({"oobe": "must-be-object"}, "'must-be-object' is not of type 'object'"), + ({"shortcut": "must-be-object"}, "'must-be-object' is not of type 'object'"), + ({"shortcut": {"enabled": 1}}, "1 is not of type 'boolean'"), + ({"oobe": {"default_uid": True}}, "True is not of type 'integer'"), + # good + ({ + "oobe": + { + "default_uid": 1000, + "default_name": "RedHatEnterpriseLinux-10.0", + }, + "shortcut": + { + "enabled": True, + "icon": "/usr/share/pixmaps/fedora-logo.ico", + } + }, "") +]) +def test_wsl_distribution_conf_schema_validation(stage_schema, test_data, expected_err): + test_input = { + "type": STAGE_NAME, + "options": { + } + } + test_input["options"].update(test_data) + res = stage_schema.validate(test_input) + + if expected_err == "": + assert res.valid is True, f"err: {[e.as_dict() for e in res.errors]}" + else: + assert res.valid is False + testutil.assert_jsonschema_error_contains(res, expected_err, expected_num_errs=1) + + +def test_wsl_distribution_conf_writes_file(tmp_path, stage_module): + treepath = tmp_path / "tree" + + etcpath = treepath / "etc" + etcpath.mkdir(parents=True, exist_ok=True) + + confpath = "wsl-distribution.conf" + + fullpath = etcpath / confpath + + options = { + "oobe": { + "default_name": "RedHatEnterpriseLinux-10.0", + "default_uid": 1000, + }, + "shortcut": { + "enabled": True, + "icon": "/usr/share/pixmaps/fedora-logo.ico", + } + } + + stage_module.main(treepath, options) + + assert fullpath.exists() + + confdata = fullpath.read_text() + + assert confdata == """\ +[oobe] +defaultUid = 1000 +defaultName = RedHatEnterpriseLinux-10.0 + +[shortcut] +enabled = true +icon = /usr/share/pixmaps/fedora-logo.ico"""