]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
Add executable `mkosi.version` support
authorMichael Ferrari <nekkodroid404@gmail.com>
Wed, 7 Aug 2024 09:37:48 +0000 (11:37 +0200)
committerMichael Ferrari <nekkodroid404@gmail.com>
Tue, 20 Aug 2024 09:42:39 +0000 (11:42 +0200)
`mkosi.version` is executed during configuration parsing, as opposed
to reading the contents of `mkosi.version`. This allows querying the
version before the build without needing to manually adjust the version
beforehand.

This allows using date based versioning by writing a script outputting
`date '+%Y-%m-%d'` or using git tag based versioning by outputting
`git describe --tags`.

mkosi/config.py
mkosi/resources/mkosi.md
tests/test_config.py

index 19f0fcf5339113ec26e71184dcb55199ec21e672..2c6a4c92e67351efde2f648bc6f2aa627382c63e 100644 (file)
@@ -1142,6 +1142,21 @@ def config_parse_minimum_version(value: Optional[str], old: Optional[GenericVers
     return max(old, new)
 
 
+def file_run_or_read(file: Path) -> str:
+    "Run the specified file and capture its output if it's executable, else read file contents"
+
+    if os.access(file, os.X_OK):
+        return run([file.absolute()], stdout=subprocess.PIPE, env=os.environ).stdout
+
+    content = file.read_text()
+
+    if content.startswith("#!/"):
+        die(f"{file} starts with a shebang ({content.splitlines()[0]})",
+            hint="This file should be executable")
+
+    return content
+
+
 @dataclasses.dataclass(frozen=True)
 class KeySource:
     class Type(StrEnum):
@@ -3677,7 +3692,7 @@ class ParseContext:
                             self.config,
                             s.dest,
                             s.parse(
-                                extra.read_text().rstrip("\n") if s.path_read_text else f,
+                                file_run_or_read(extra).rstrip("\n") if s.path_read_text else f,
                                 getattr(self.config, s.dest, None)
                             ),
                         )
index e67d9951acff45b758c1fe454512a22ae91a7690..50096401085db9b13236410b104b68031abb379a 100644 (file)
@@ -620,13 +620,14 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`,
 `ImageVersion=`, `--image-version=`
 :   Configure the image version. This accepts any string, but it is
     recommended to specify a series of dot separated components. The
-    version may also be configured in a file `mkosi.version` in which
-    case it may be conveniently managed via the `bump` verb or the
-    `--auto-bump` option. When specified the image version is included
-    in the default output file name, i.e. instead of `image.raw` the
-    default will be `image_0.1.raw` for version `0.1` of the image, and
-    similar. The version is also passed via the `$IMAGE_VERSION` to any
-    build scripts invoked (which may be useful to patch it into
+    version may also be configured by reading a `mkosi.version` file (in
+    which case it may be conveniently managed via the `bump` verb or the
+    `--auto-bump` option) or by reading stdout if it is executable (see
+    the **Scripts** section below). When specified the image version is
+    included in the default output file name, i.e. instead of `image.raw`
+    the default will be `image_0.1.raw` for version `0.1` of the image,
+    and similar. The version is also passed via the `$IMAGE_VERSION` to
+    any build scripts invoked (which may be useful to patch it into
     `/usr/lib/os-release` or similar, in particular the `IMAGE_VERSION=`
     field of it).
 
@@ -1137,9 +1138,10 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`,
 
 `RootPassword=`, `--root-password=`,
 :   Set the system root password. If this option is not used, but a `mkosi.rootpw` file is found in the local
-    directory, the password is automatically read from it. If the password starts with `hashed:`, it is treated
-    as an already hashed root password. The root password is also stored in `/usr/lib/credstore` under the
-    appropriate systemd credential so that it applies even if only `/usr` is shipped in the image. To create
+    directory, the password is automatically read from it or if the file is executable it is run as a script
+    and stdout is read instead (see the **Scripts** section below). If the password starts with `hashed:`, it is
+    treated as an already hashed root password. The root password is also stored in `/usr/lib/credstore` under
+    the appropriate systemd credential so that it applies even if only `/usr` is shipped in the image. To create
     an unlocked account without any password use `hashed:` without a hash.
 
 `Autologin=`, `--autologin`
@@ -2076,6 +2078,19 @@ current working directory. The following scripts are supported:
   artifacts from `SplitArtifacts=yes` 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
+  configuration parsing and populates `ImageVersion=` with the output on stdout.
+  This can be used for external version tracking, e.g. with `git describe` or
+  `date '+%Y-%m-%d'`. Note that this script is executed on the host system
+  without any sandboxing.
+
+* If **`mkosi.rootpw`** exists and is executable, it is run during
+  configuration parsing and populates `RootPassword=` with the output
+  on stdout. This can be used to randomly generate a password and can
+  be remembered by outputting it to stderr or by reading `$MKOSI_CONFIG`
+  in another script (e.g. `mkosi.postoutput`). Note that this script is
+  executed on the host system without any sandboxing.
+
 If a script uses the `.chroot` extension, mkosi will chroot into the
 image using `mkosi-chroot` (see below) before executing the script. For
 example, if `mkosi.postinst.chroot` exists, mkosi will chroot into the
index 20db168af1c8f0d5add419e19dae2059f0077466..0bdfbe5c3369c074734b26f0f098dea878de8ec3 100644 (file)
@@ -1275,3 +1275,23 @@ def test_environment(tmp_path: Path) -> None:
 
         assert sub.environment["PassThisEnv"] == "abc"
         assert "TestValue2" not in sub.environment
+
+
+def test_mkosi_version_executable(tmp_path: Path) -> None:
+    d = tmp_path
+
+    version = d / "mkosi.version"
+    version.write_text("#!/bin/sh\necho '1.2.3'\n")
+
+    with chdir(d):
+        with pytest.raises(SystemExit) as error:
+            _, [config] = parse_config()
+
+        assert error.type is SystemExit
+        assert error.value.code != 0
+
+    version.chmod(0o755)
+
+    with chdir(d):
+        _, [config] = parse_config()
+        assert config.image_version == "1.2.3"