]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
Add support for configure scripts 2552/head
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Tue, 26 Mar 2024 09:49:26 +0000 (10:49 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Tue, 26 Mar 2024 11:31:37 +0000 (12:31 +0100)
These allow dynamically modifying the configuration.

mkosi/__init__.py
mkosi/config.py
mkosi/resources/mkosi.md
tests/test_json.py

index 3046e5adc79ec7c5d7ffbd334a0e3eee7f79e9d6..2f5cc191476cc81b9a3658a9719c92b5cc5087c9 100644 (file)
@@ -423,6 +423,46 @@ def finalize_config_json(config: Config) -> Iterator[Path]:
         yield Path(f.name)
 
 
+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")
+
+    env = dict(
+        DISTRIBUTION=str(config.distribution),
+        RELEASE=config.release,
+        ARCHITECTURE=str(config.architecture),
+        SRCDIR="/work/src",
+        MKOSI_UID=str(INVOKING_USER.uid),
+        MKOSI_GID=str(INVOKING_USER.gid),
+    )
+
+    if config.profile:
+        env["PROFILE"] = config.profile
+
+    with finalize_source_mounts(config, ephemeral=False) as sources:
+        for script in config.configure_scripts:
+            with complete_step(f"Running configure script {script}…"):
+                result = run(
+                    ["/work/configure", "final"],
+                    env=env | config.environment,
+                    sandbox=config.sandbox(
+                        tools=Path("/"),
+                        mounts=[*sources, Mount(script, "/work/configure", ro=True)],
+                        options=["--dir", "/work/src", "--chdir", "/work/src"]
+                    ),
+                    input=config.to_json(indent=None),
+                    stdout=subprocess.PIPE,
+                )
+
+                config = Config.from_json(result.stdout)
+
+    return config
+
+
 def run_sync_scripts(context: Context) -> None:
     if not context.config.sync_scripts:
         return
@@ -1679,6 +1719,10 @@ def finalize_default_initrd(
 
     _, [config] = parse_config(cmdline + ["build"], resources=resources)
 
+    make_executable(*config.configure_scripts)
+
+    run_configure_scripts(config)
+
     make_executable(
         *config.prepare_scripts,
         *config.postinst_scripts,
@@ -4183,12 +4227,15 @@ def run_verb(args: Args, images: Sequence[Config], *, resources: Path) -> None:
     if args.verb == Verb.genkey:
         return generate_key_cert_pair(args)
 
+    if args.verb == Verb.bump:
+        return bump_image_version()
+
     if all(config == Config.default() for config in images):
         die("No configuration found",
             hint="Make sure you're running mkosi from a directory with configuration files")
 
-    if args.verb == Verb.bump:
-        return bump_image_version()
+    for i, config in enumerate(images):
+        images[i] = run_configure_scripts(config)
 
     if args.verb == Verb.summary:
         if args.json:
index 588058d575180081aea84f06b0175e96f1b5fdfa..9b54940e2da083925fbaa05649df84bd7c9c6b94 100644 (file)
@@ -1324,6 +1324,7 @@ class Config:
     clean_package_metadata: ConfigFeature
     source_date_epoch: Optional[int]
 
+    configure_scripts: list[Path]
     sync_scripts: list[Path]
     prepare_scripts: list[Path]
     build_scripts: list[Path]
@@ -1586,6 +1587,7 @@ class Config:
         network: bool = False,
         devices: bool = False,
         relaxed: bool = False,
+        tools: Optional[Path] = None,
         scripts: Optional[Path] = None,
         mounts: Sequence[Mount] = (),
         options: Sequence[PathString] = (),
@@ -1603,7 +1605,7 @@ class Config:
             devices=devices,
             relaxed=relaxed,
             scripts=scripts,
-            tools=self.tools(),
+            tools=tools or self.tools(),
             mounts=mounts,
             options=options,
         )
@@ -2073,6 +2075,15 @@ SETTINGS = (
         default_factory_depends=("environment",),
         help="Set the $SOURCE_DATE_EPOCH timestamp",
     ),
+    ConfigSetting(
+        dest="configure_scripts",
+        long="--configure-script",
+        metavar="PATH",
+        section="Content",
+        parse=config_make_list_parser(delimiter=",", parse=make_path_parser()),
+        paths=("mkosi.configure",),
+        help="Configure script to run before doing anything",
+    ),
     ConfigSetting(
         dest="sync_scripts",
         long="--sync-script",
@@ -3732,6 +3743,7 @@ def summary(config: Config) -> str:
      Clean Package Manager Metadata: {config.clean_package_metadata}
                   Source Date Epoch: {none_to_none(config.source_date_epoch)}
 
+                  Configure Scripts: {line_join_list(config.configure_scripts)}
                        Sync Scripts: {line_join_list(config.sync_scripts)}
                     Prepare Scripts: {line_join_list(config.prepare_scripts)}
                       Build Scripts: {line_join_list(config.build_scripts)}
index 086722913b5d09b0b4eae57891a55caaacd77459..6e732346cf254c27f3daeb7dd23f9a2e6547ead4 100644 (file)
@@ -1026,6 +1026,12 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`,
   package manager executable is *not* present at the end of the
   installation.
 
+`ConfigureScripts=`, `--configure-script=`
+
+: Takes a comma-separated list of paths to executables that are used as
+  the configure scripts for this image. See the **Scripts** section for
+  more information.
+
 `SyncScripts=`, `--sync-script=`
 
 : Takes a comma-separated list of paths to executables that are used as
@@ -1907,6 +1913,7 @@ Then, for each image, we execute the following steps:
 
 1. Copy package manager trees into the workspace
 1. Sync the package manager repository metadata
+1. Run sync scripts (`mkosi.sync`)
 1. Copy base trees (`--base-tree=`) into the image
 1. Reuse a cached image if one is available
 1. Copy a snapshot of the package manager repository metadata into the
@@ -1950,6 +1957,13 @@ are mounted into the current working directory before running the script
 in the current working directory. `$SRCDIR` is set to point to the
 current working directory. The following scripts are supported:
 
+* If **`mkosi.configure`** (`ConfigureScripts=`) exists, it is executed
+  after parsing the configuration files. This script may be used to
+  dynamically modify the configuration. It receives the configuration
+  serialized as JSON on stdin and should output the modified
+  configuration serialized as JSON on stdout. Note that this script does
+  not use the tools tree even if one is configured.
+
 * If **`mkosi.sync`** (`SyncScripts=`) exists, it is executed before the
   image is built. This script may be used to update various sources that
   are used to build the image. One use case is to run `git pull` on
@@ -2078,30 +2092,30 @@ Scripts executed by mkosi receive the following environment variables:
 
 Consult this table for which script receives which environment variables:
 
-| Variable            | `mkosi.sync` | `mkosi.prepare` | `mkosi.build` | `mkosi.postinst` | `mkosi.finalize` |
-|---------------------|--------------|-----------------|---------------|------------------|------------------|
-| `ARCHITECTURE`      | X            | X               | X             | X                | X                |
-| `DISTRIBUTION`      | X            | X               | X             | X                | X                |
-| `RELEASE`           | X            | X               | X             | X                | X                |
-| `PROFILE`           | X            | X               | X             | X                | X                |
-| `CACHED`            | X            |                 |               |                  |                  |
-| `CHROOT_SCRIPT`     |              | X               | X             | X                | X                |
-| `SRCDIR`            | X            | X               | X             | X                | X                |
-| `CHROOT_SRCDIR`     |              | X               | X             | X                | X                |
-| `BUILDDIR`          |              |                 | X             |                  |                  |
-| `CHROOT_BUILDDIR`   |              |                 | X             |                  |                  |
-| `DESTDIR`           |              |                 | X             |                  |                  |
-| `CHROOT_DESTDIR`    |              |                 | X             |                  |                  |
-| `OUTPUTDIR`         |              |                 | X             | X                | X                |
-| `CHROOT_OUTPUTDIR`  |              |                 | X             | X                | X                |
-| `BUILDROOT`         |              | X               | X             | X                | X                |
-| `WITH_DOCS`         |              | X               | X             |                  |                  |
-| `WITH_TESTS`        |              | X               | X             |                  |                  |
-| `WITH_NETWORK`      |              | X               | X             |                  |                  |
-| `SOURCE_DATE_EPOCH` |              | X               | X             | X                | X                |
-| `MKOSI_UID`         | X            | X               | X             | X                | X                |
-| `MKOSI_GID`         | X            | X               | X             | X                | X                |
-| `MKOSI_CONFIG`      | X            | X               | X             | X                | X                |
+| Variable            | `mkosi.configure` | `mkosi.sync` | `mkosi.prepare` | `mkosi.build` | `mkosi.postinst` | `mkosi.finalize` |
+|---------------------|-------------------|--------------|-----------------|---------------|------------------|------------------|
+| `ARCHITECTURE`      | X                 | X            | X               | X             | X                | X                |
+| `DISTRIBUTION`      | X                 | X            | X               | X             | X                | X                |
+| `RELEASE`           | X                 | X            | X               | X             | X                | X                |
+| `PROFILE`           | X                 | X            | X               | X             | X                | X                |
+| `CACHED`            |                   | X            |                 |               |                  |                  |
+| `CHROOT_SCRIPT`     |                   |              | X               | X             | X                | X                |
+| `SRCDIR`            | X                 | X            | X               | X             | X                | X                |
+| `CHROOT_SRCDIR`     |                   |              | X               | X             | X                | X                |
+| `BUILDDIR`          |                   |              |                 | X             |                  |                  |
+| `CHROOT_BUILDDIR`   |                   |              |                 | X             |                  |                  |
+| `DESTDIR`           |                   |              |                 | X             |                  |                  |
+| `CHROOT_DESTDIR`    |                   |              |                 | X             |                  |                  |
+| `OUTPUTDIR`         |                   |              |                 | X             | X                | X                |
+| `CHROOT_OUTPUTDIR`  |                   |              |                 | X             | X                | X                |
+| `BUILDROOT`         |                   |              | X               | X             | X                | X                |
+| `WITH_DOCS`         |                   |              | X               | X             |                  |                  |
+| `WITH_TESTS`        |                   |              | X               | X             |                  |                  |
+| `WITH_NETWORK`      |                   |              | X               | X             |                  |                  |
+| `SOURCE_DATE_EPOCH` |                   |              | X               | X             | X                | X                |
+| `MKOSI_UID`         | X                 | X            | X               | X             | X                | X                |
+| `MKOSI_GID`         | X                 | X            | X               | X             | X                | X                |
+| `MKOSI_CONFIG`      |                   | X            | X               | X             | X                | X                |
 
 Additionally, when a script is executed, a few scripts are made
 available via `$PATH` to simplify common usecases.
index 3cd112935a1cb9bdfe6d546cfe1a54450b87cf0b..f9b0ad70ec0b626fbd9d755c58613b7585ae75a6 100644 (file)
@@ -114,6 +114,9 @@ def test_config() -> None:
             "CleanPackageMetadata": "auto",
             "CompressLevel": 3,
             "CompressOutput": "bz2",
+            "ConfigureScripts": [
+                "/configure"
+            ],
             "Credentials": {
                 "credkey": "credval"
             },
@@ -345,6 +348,7 @@ def test_config() -> None:
         clean_package_metadata = ConfigFeature.auto,
         compress_level = 3,
         compress_output = Compression.bz2,
+        configure_scripts = [Path("/configure")],
         credentials =  {"credkey": "credval"},
         dependencies = ("dep1",),
         distribution = Distribution.fedora,