]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
Add `mkosi.postoutput` scripts
authorMichael Ferrari <nekkodroid404@gmail.com>
Sun, 9 Jun 2024 08:23:11 +0000 (10:23 +0200)
committerJörg Behrmann <behrmann@physik.fu-berlin.de>
Mon, 10 Jun 2024 12:13:03 +0000 (14:13 +0200)
mkosi/__init__.py
mkosi/config.py
mkosi/resources/mkosi.md
tests/test_json.py

index b607e6adb6716ca4e985d8531b7ce6c42fdb425f..0405f56c3c5ee5bc758746f9f70748622392a822 100644 (file)
@@ -830,6 +830,47 @@ def run_finalize_scripts(context: Context) -> None:
                 )
 
 
+def run_postoutput_scripts(context: Context) -> None:
+    if not context.config.postoutput_scripts:
+        return
+
+    env = dict(
+        DISTRIBUTION=str(context.config.distribution),
+        RELEASE=context.config.release,
+        ARCHITECTURE=str(context.config.architecture),
+        SRCDIR="/work/src",
+        OUTPUTDIR="/work/out",
+        MKOSI_UID=str(INVOKING_USER.uid),
+        MKOSI_GID=str(INVOKING_USER.gid),
+        MKOSI_CONFIG="/work/config.json",
+    )
+
+    if context.config.profile:
+        env["PROFILE"] = context.config.profile
+
+    with (
+        finalize_source_mounts(context.config, ephemeral=context.config.build_sources_ephemeral) as sources,
+        finalize_config_json(context.config) as json,
+    ):
+        for script in context.config.postoutput_scripts:
+            with complete_step(f"Running post-output script {script}…"):
+                run(
+                    ["/work/postoutput"],
+                    env=env | context.config.environment,
+                    sandbox=context.config.sandbox(
+                        binary=None,
+                        mounts=[
+                            *sources,
+                            Mount(script, "/work/postoutput", ro=True),
+                            Mount(json, "/work/config.json", ro=True),
+                            Mount(context.staging, "/work/out"),
+                        ],
+                        options=["--dir", "/work/src", "--chdir", "/work/src", "--dir", "/work/out"]
+                    ),
+                    stdin=sys.stdin,
+                )
+
+
 def certificate_common_name(context: Context, certificate: Path) -> str:
     output = run(
         [
@@ -2744,6 +2785,7 @@ def check_inputs(config: Config) -> None:
         config.build_scripts,
         config.postinst_scripts,
         config.finalize_scripts,
+        config.postoutput_scripts,
     ):
         if not os.access(script, os.X_OK):
             die(f"{script} is not executable")
@@ -3853,6 +3895,7 @@ def build_image(context: Context) -> None:
         output_base.unlink(missing_ok=True)
         output_base.symlink_to(context.config.output_with_compression)
 
+    run_postoutput_scripts(context)
     finalize_staging(context)
 
     print_output_size(context.config.output_dir_or_cwd() / context.config.output_with_compression)
index a6a9eddee0c022607984b9be01ce634ba87bf5e3..859ea68c4bf3afe702ad6a908883dbcf841ca3ca 100644 (file)
@@ -1417,6 +1417,7 @@ class Config:
     build_scripts: list[Path]
     postinst_scripts: list[Path]
     finalize_scripts: list[Path]
+    postoutput_scripts: list[Path]
     clean_scripts: list[Path]
     build_sources: list[ConfigTree]
     build_sources_ephemeral: bool
@@ -2290,6 +2291,17 @@ SETTINGS = (
         help="Postinstall script to run outside image",
         compat_names=("FinalizeScript",),
     ),
+    ConfigSetting(
+        dest="postoutput_scripts",
+        long="--postoutput-script",
+        metavar="PATH",
+        name="PostOutputScripts",
+        section="Content",
+        parse=config_make_list_parser(delimiter=",", parse=make_path_parser()),
+        paths=("mkosi.postoutput",),
+        path_default=False,
+        help="Output postprocessing script to run outside image",
+    ),
     ConfigSetting(
         dest="build_sources",
         metavar="PATH",
@@ -3360,6 +3372,7 @@ def parse_config(argv: Sequence[str] = (), *, resources: Path = Path("/")) -> tu
                         *config.build_scripts,
                         *config.postinst_scripts,
                         *config.finalize_scripts,
+                        *config.postoutput_scripts,
                     )
 
                 with chdir(path if path.is_dir() else Path.cwd()):
@@ -3985,6 +3998,7 @@ def summary(config: Config) -> str:
                       Build Scripts: {line_join_list(config.build_scripts)}
                 Postinstall Scripts: {line_join_list(config.postinst_scripts)}
                    Finalize Scripts: {line_join_list(config.finalize_scripts)}
+                 Postoutput Scripts: {line_join_list(config.postoutput_scripts)}
                       Build Sources: {line_join_list(config.build_sources)}
             Build Sources Ephemeral: {yes_no(config.build_sources_ephemeral)}
                  Script Environment: {line_join_list(env)}
index ee101451c7ac6b2d325096b8eb8581bcbd707cd2..7a12aa544926cc5da754bcffec170ce7375e557a 100644 (file)
@@ -878,6 +878,11 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`,
     the finalize scripts for this image. See the **Scripts** section for more
     information.
 
+`PostOutputScripts=`, `--postoutput-script`
+:   Takes a comma-separated list of paths to executables that are used as
+    the post output scripts for this image. See the **Scripts** section for more
+    information.
+
 `BuildSources=`, `--build-sources=`
 :   Takes a comma separated list of colon separated path pairs. The first
     path of each pair refers to a directory to mount from the host. The
@@ -1951,6 +1956,7 @@ Then, for each image, we execute the following steps:
 1. Run finalize scripts (`mkosi.finalize`)
 1. Generate unified kernel image if configured to do so
 1. Generate final output format
+1. Run post-output scripts (`mkosi.postoutput`)
 
 # Scripts
 
@@ -2017,6 +2023,11 @@ current working directory. The following scripts are supported:
 * If **`mkosi.finalize`** (`FinalizeScripts=`) exists, it is executed as
   the last step of preparing an image.
 
+* If **`mkosi.postoutput`** (`PostOutputScripts=`) exists, it is executed right
+  after all the output files have been generated, before they are finally
+  moved into the output directory. This can be used to generate additional or
+  alternative outputs, e.g. `SHA256FILES` or SBOM manifests.
+
 * If **`mkosi.clean`** (`CleanScripts=`) exists, it is executed right
   after the outputs of a previous build have been cleaned up. A clean
   script can clean up any outputs that mkosi does not know about (e.g.
@@ -2134,33 +2145,33 @@ Scripts executed by mkosi receive the following environment variables:
 
 Consult this table for which script receives which environment variables:
 
-| Variable            | `configure` | `sync` | `prepare` | `build` | `postinst` | `finalize` | `clean` |
-|---------------------|:-----------:|:------:|:---------:|:-------:|:----------:|:----------:|:-------:|
-| `ARCHITECTURE`      | ✓           | ✓      | ✓         | ✓       | ✓          | ✓          | ✓       |
-| `QEMU_ARCHITECTURE` | ✓           |        |           |         |            |            |         |
-| `DISTRIBUTION`      | ✓           | ✓      | ✓         | ✓       | ✓          | ✓          | ✓       |
-| `RELEASE`           | ✓           | ✓      | ✓         | ✓       | ✓          | ✓          | ✓       |
-| `PROFILE`           | ✓           | ✓      | ✓         | ✓       | ✓          | ✓          | ✓       |
-| `CACHED`            |             | ✓      |           |         |            |            |         |
-| `CHROOT_SCRIPT`     |             |        | ✓         | ✓       | ✓          | ✓          |         |
-| `SRCDIR`            | ✓           | ✓      | ✓         | ✓       | ✓          | ✓          | ✓       |
-| `CHROOT_SRCDIR`     |             |        | ✓         | ✓       | ✓          | ✓          |         |
-| `BUILDDIR`          |             |        |           | ✓       |            |            |         |
-| `CHROOT_BUILDDIR`   |             |        |           | ✓       |            |            |         |
-| `DESTDIR`           |             |        |           | ✓       |            |            |         |
-| `CHROOT_DESTDIR`    |             |        |           | ✓       |            |            |         |
-| `OUTPUTDIR`         |             |        |           | ✓       | ✓          | ✓          | ✓       |
-| `CHROOT_OUTPUTDIR`  |             |        |           | ✓       | ✓          | ✓          |         |
-| `BUILDROOT`         |             |        | ✓         | ✓       | ✓          | ✓          |         |
-| `PACKAGEDIR`        |             |        | ✓         | ✓       | ✓          | ✓          |         |
-| `ARTIFACTDIR`       |             |        | ✓         | ✓       | ✓          | ✓          |         |
-| `WITH_DOCS`         |             |        | ✓         | ✓       |            |            |         |
-| `WITH_TESTS`        |             |        | ✓         | ✓       |            |            |         |
-| `WITH_NETWORK`      |             |        | ✓         | ✓       | ✓          | ✓          |         |
-| `SOURCE_DATE_EPOCH` |             |        | ✓         | ✓       | ✓          | ✓          | ✓       |
-| `MKOSI_UID`         | ✓           | ✓      | ✓         | ✓       | ✓          | ✓          | ✓       |
-| `MKOSI_GID`         | ✓           | ✓      | ✓         | ✓       | ✓          | ✓          | ✓       |
-| `MKOSI_CONFIG`      |             | ✓      | ✓         | ✓       | ✓          | ✓          | ✓       |
+| Variable            | `configure` | `sync` | `prepare` | `build` | `postinst` | `finalize` | `postoutput` | `clean` |
+|---------------------|:-----------:|:------:|:---------:|:-------:|:----------:|:----------:|:------------:|:-------:|
+| `ARCHITECTURE`      | ✓           | ✓      | ✓         | ✓       | ✓          | ✓          | ✓            | ✓       |
+| `QEMU_ARCHITECTURE` | ✓           |        |           |         |            |            |              |         |
+| `DISTRIBUTION`      | ✓           | ✓      | ✓         | ✓       | ✓          | ✓          | ✓            | ✓       |
+| `RELEASE`           | ✓           | ✓      | ✓         | ✓       | ✓          | ✓          | ✓            | ✓       |
+| `PROFILE`           | ✓           | ✓      | ✓         | ✓       | ✓          | ✓          |              | ✓       |
+| `CACHED`            |             | ✓      |           |         |            |            |              |         |
+| `CHROOT_SCRIPT`     |             |        | ✓         | ✓       | ✓          | ✓          |              |         |
+| `SRCDIR`            | ✓           | ✓      | ✓         | ✓       | ✓          | ✓          | ✓            | ✓       |
+| `CHROOT_SRCDIR`     |             |        | ✓         | ✓       | ✓          | ✓          |              |         |
+| `BUILDDIR`          |             |        |           | ✓       |            |            |              |         |
+| `CHROOT_BUILDDIR`   |             |        |           | ✓       |            |            |              |         |
+| `DESTDIR`           |             |        |           | ✓       |            |            |              |         |
+| `CHROOT_DESTDIR`    |             |        |           | ✓       |            |            |              |         |
+| `OUTPUTDIR`         |             |        |           | ✓       | ✓          | ✓          | ✓            | ✓       |
+| `CHROOT_OUTPUTDIR`  |             |        |           | ✓       | ✓          | ✓          |              |         |
+| `BUILDROOT`         |             |        | ✓         | ✓       | ✓          | ✓          |              |         |
+| `PACKAGEDIR`        |             |        | ✓         | ✓       | ✓          | ✓          |              |         |
+| `ARTIFACTDIR`       |             |        | ✓         | ✓       | ✓          | ✓          |              |         |
+| `WITH_DOCS`         |             |        | ✓         | ✓       |            |            |              |         |
+| `WITH_TESTS`        |             |        | ✓         | ✓       |            |            |              |         |
+| `WITH_NETWORK`      |             |        | ✓         | ✓       | ✓          | ✓          |              |         |
+| `SOURCE_DATE_EPOCH` |             |        | ✓         | ✓       | ✓          | ✓          |              | ✓       |
+| `MKOSI_UID`         | ✓           | ✓      | ✓         | ✓       | ✓          | ✓          | ✓            | ✓       |
+| `MKOSI_GID`         | ✓           | ✓      | ✓         | ✓       | ✓          | ✓          | ✓            | ✓       |
+| `MKOSI_CONFIG`      |             | ✓      | ✓         | ✓       | ✓          | ✓          | ✓            | ✓       |
 
 Additionally, when a script is executed, a few scripts are made
 available via `$PATH` to simplify common usecases.
index 051a21ca783bc5091d93df807f8732fedba93682..fd44bd3d2e769b921647ab14bb6956f80313ebae 100644 (file)
@@ -216,6 +216,9 @@ def test_config() -> None:
             "PostInstallationScripts": [
                 "/bar/qux"
             ],
+            "PostOutputScripts": [
+                "/foo/src"
+            ],
             "PrepareScripts": [
                 "/run/foo"
             ],
@@ -431,6 +434,7 @@ def test_config() -> None:
         packages=[],
         passphrase=None,
         postinst_scripts=[Path("/bar/qux")],
+        postoutput_scripts=[Path("/foo/src")],
         prepare_scripts=[Path("/run/foo")],
         profile="profile",
         proxy_client_certificate=Path("/my/client/cert"),