From: Luca Boccassi Date: Wed, 28 Jan 2026 22:43:21 +0000 (+0000) Subject: Add MakeScriptsExecutable= setting to optionally try to make scripts executable befor... X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=842a37ed6cc5c01e9d8a423d3caa7f7d0fb9451c;p=thirdparty%2Fmkosi.git Add MakeScriptsExecutable= setting to optionally try to make scripts executable before bailing out If it fails, it was going to die() anyway. OBS sources defined inline (ie, not in a tarball) cannot have the mode preserved, so it's not possible to have mkosi.build or so as a bare script in an OBS project, one needs to tar it up and extract it again later, which means it cannot be edited by the inline editor, which is very convenient for small and trivial builds like an addon. --- diff --git a/mkosi/__init__.py b/mkosi/__init__.py index 03ec72e82..7cb78c9ad 100644 --- a/mkosi/__init__.py +++ b/mkosi/__init__.py @@ -649,13 +649,21 @@ def finalize_config_json(config: Config) -> Iterator[Path]: yield Path(f.name) +def check_script(config: Config, script: Path) -> None: + if not os.access(script, os.X_OK): + if config.make_scripts_executable: + logging.warning(f"{script} is not executable, attempting to chmod it") + os.chmod(script, os.stat(script).st_mode | stat.S_IXUSR) + else: + die(f"{script} is not executable") + + def run_configure_scripts(config: Config) -> Config: if not config.configure_scripts: return config for script in config.configure_scripts: - if not os.access(script, os.X_OK): - die(f"{script} is not executable") + check_script(config, script) env = dict( DISTRIBUTION=str(config.distribution), @@ -703,8 +711,7 @@ def run_sync_scripts(config: Config) -> None: return for script in config.sync_scripts: - if not os.access(script, os.X_OK): - die(f"{script} is not executable") + check_script(config, script) env = dict( DISTRIBUTION=str(config.distribution), @@ -2723,8 +2730,7 @@ def check_inputs(config: Config) -> None: config.finalize_scripts, config.postoutput_scripts, ): - if not os.access(script, os.X_OK): - die(f"{script} is not executable") + check_script(config, script) if config.secure_boot and not config.secure_boot_key: die( @@ -4592,8 +4598,7 @@ def run_clean_scripts(config: Config) -> None: return for script in config.clean_scripts: - if not os.access(script, os.X_OK): - die(f"{script} is not executable") + check_script(config, script) env = dict( DISTRIBUTION=str(config.distribution), diff --git a/mkosi/config.py b/mkosi/config.py index ab00e552c..a502e1e23 100644 --- a/mkosi/config.py +++ b/mkosi/config.py @@ -2151,6 +2151,7 @@ class Config: proxy_peer_certificate: Optional[Path] proxy_client_certificate: Optional[Path] proxy_client_key: Optional[Path] + make_scripts_executable: bool nspawn_settings: Optional[Path] ephemeral: bool @@ -4015,6 +4016,14 @@ SETTINGS: list[ConfigSetting[Any]] = [ help="Set the proxy client key", scope=SettingScope.multiversal, ), + ConfigSetting( + dest="make_scripts_executable", + metavar="BOOL", + section="Build", + parse=config_parse_boolean, + default=False, + help="Whether mkosi will try to make build/postinst/finalize scripts executable if they are not", + ), # Runtime section ConfigSetting( dest="nspawn_settings", @@ -5788,6 +5797,8 @@ def summary(config: Config) -> str: Proxy Client Certificate: {none_to_none(config.proxy_client_certificate)} Proxy Client Key: {none_to_none(config.proxy_client_key)} + Automatically set +x on scripts: {yes_no(config.make_scripts_executable)} + {bold("HOST CONFIGURATION")}: NSpawn Settings: {none_to_none(config.nspawn_settings)} Ephemeral: {config.ephemeral} diff --git a/mkosi/resources/man/mkosi.1.md b/mkosi/resources/man/mkosi.1.md index 90c0da5d1..c3da06a7f 100644 --- a/mkosi/resources/man/mkosi.1.md +++ b/mkosi/resources/man/mkosi.1.md @@ -1753,6 +1753,10 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`, Currently, setting a proxy client key is only supported when **dnf** or **dnf5** is used to build the image. +`MakeScriptsExecutable=`, `--make-scripts-executable=` +: If one of the hook scripts (see `SCRIPTS` section) is not marked as executable, attempt to chmod it + instead of failing outright. Defaults to `no`. + ### [Runtime] Section (previously known as the [Host] section) `NSpawnSettings=`, `--settings=` diff --git a/mkosi/resources/mkosi-obs/mkosi.conf b/mkosi/resources/mkosi-obs/mkosi.conf index 811ccb598..a939b18ee 100644 --- a/mkosi/resources/mkosi-obs/mkosi.conf +++ b/mkosi/resources/mkosi-obs/mkosi.conf @@ -8,3 +8,6 @@ SecureBoot=no SignExpectedPcr=no Verity=defer Checksum=yes + +[Build] +MakeScriptsExecutable=yes diff --git a/tests/test_json.py b/tests/test_json.py index a17fdcfae..308b27279 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -254,6 +254,7 @@ def test_config() -> None: "Machine": "machine", "MachineId": "b58253b0-cc92-4a34-8782-bcd99b20d07f", "MakeInitrd": false, + "MakeScriptsExecutable": false, "ManifestFormat": [ "json", "changelog" @@ -508,6 +509,7 @@ def test_config() -> None: locale="en_C.UTF-8", machine_id=uuid.UUID("b58253b0cc924a348782bcd99b20d07f"), machine="machine", + make_scripts_executable=False, make_initrd=False, manifest_format=[ManifestFormat.json, ManifestFormat.changelog], maxmem=123,