From: Daan De Meyer Date: Thu, 22 Feb 2024 09:16:22 +0000 (+0100) Subject: Add support for sync scripts X-Git-Tag: v21~35^2 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F2418%2Fhead;p=thirdparty%2Fmkosi.git Add support for sync scripts Sync scripts allow updating various sources automatically before doing a build. --- diff --git a/mkosi/__init__.py b/mkosi/__init__.py index 2522b64d1..343d549ed 100644 --- a/mkosi/__init__.py +++ b/mkosi/__init__.py @@ -411,6 +411,52 @@ def finalize_chroot_scripts(context: Context) -> contextlib.AbstractContextManag return finalize_scripts(scripts, root=context.root) +def run_sync_scripts(context: Context) -> None: + if not context.config.sync_scripts: + return + + env = dict( + ARCHITECTURE=str(context.config.architecture), + SRCDIR="/work/src", + MKOSI_UID=str(INVOKING_USER.uid), + MKOSI_GID=str(INVOKING_USER.gid), + CACHED=one_zero(have_cache(context.config)), + ) + + # We make sure to mount everything in to make ssh work since syncing might involve git which could invoke ssh. + if agent := os.getenv("SSH_AUTH_SOCK"): + env["SSH_AUTH_SOCK"] = agent + + with ( + finalize_source_mounts(context.config, ephemeral=False) as sources, + finalize_host_scripts(context) as hd, + ): + for script in context.config.sync_scripts: + options = [ + *sources, + "--ro-bind", script, "/work/sync", + "--chdir", "/work/src", + ] + + if (p := INVOKING_USER.home()).exists(): + options += ["--ro-bind", p, p] + if (p := Path(f"/run/user/{INVOKING_USER.uid}")).exists(): + options += ["--ro-bind", p, p] + + with complete_step(f"Running sync script {script}…"): + run( + ["/work/sync", "final"], + env=env | context.config.environment, + stdin=sys.stdin, + sandbox=context.sandbox(network=True, options=options, scripts=hd), + # Make sure we run as the invoking user when we're running as root so that files are owned by the + # right user. bubblewrap will automatically map the running user to root in the user namespace it + # creates. + user=INVOKING_USER.uid, + group=INVOKING_USER.gid, + ) + + def run_prepare_scripts(context: Context, build: bool) -> None: if not context.config.prepare_scripts: return @@ -2252,7 +2298,13 @@ def check_inputs(config: Config) -> None: if not p.is_file(): die(f"Initrd {p} is not a file") - for script in config.prepare_scripts + config.build_scripts + config.postinst_scripts + config.finalize_scripts: + for script in itertools.chain( + config.sync_scripts, + config.prepare_scripts, + config.build_scripts, + config.postinst_scripts, + config.finalize_scripts, + ): if not os.access(script, os.X_OK): die(f"{script} is not executable") @@ -3680,6 +3732,8 @@ def run_sync(args: Args, config: Config, *, resources: Path) -> None: for p in config.distribution.package_manager(config).cache_subdirs(src): INVOKING_USER.mkdir(p) + run_sync_scripts(context) + def run_build(args: Args, config: Config, *, resources: Path) -> None: check_inputs(config) diff --git a/mkosi/config.py b/mkosi/config.py index 3d747191c..75b8deeef 100644 --- a/mkosi/config.py +++ b/mkosi/config.py @@ -1226,6 +1226,7 @@ class Config: clean_package_metadata: ConfigFeature source_date_epoch: Optional[int] + sync_scripts: list[Path] prepare_scripts: list[Path] build_scripts: list[Path] postinst_scripts: list[Path] @@ -1955,6 +1956,15 @@ SETTINGS = ( default_factory_depends=("environment",), help="Set the $SOURCE_DATE_EPOCH timestamp", ), + ConfigSetting( + dest="sync_scripts", + long="--sync-script", + metavar="PATH", + section="Content", + parse=config_make_list_parser(delimiter=",", parse=make_path_parser()), + paths=("mkosi.sync",), + help="Sync script to run before starting the build", + ), ConfigSetting( dest="prepare_scripts", long="--prepare-script", @@ -3509,6 +3519,7 @@ def summary(config: Config) -> str: Clean Package Manager Metadata: {config.clean_package_metadata} Source Date Epoch: {none_to_none(config.source_date_epoch)} + Sync Scripts: {line_join_list(config.sync_scripts)} Prepare Scripts: {line_join_list(config.prepare_scripts)} Build Scripts: {line_join_list(config.build_scripts)} Postinstall Scripts: {line_join_list(config.postinst_scripts)} diff --git a/mkosi/resources/mkosi.md b/mkosi/resources/mkosi.md index 6e4bcab47..f046c3038 100644 --- a/mkosi/resources/mkosi.md +++ b/mkosi/resources/mkosi.md @@ -1022,6 +1022,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. +`SyncScripts=`, `--sync-script=` + +: Takes a comma-separated list of paths to executables that are used as + the sync scripts for this image. See the **Scripts** section for + more information. + `PrepareScripts=`, `--prepare-script=` : Takes a comma-separated list of paths to executables that are used as @@ -1062,7 +1068,8 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`, : Takes a boolean. Disabled by default. Configures whether changes to source directories (The working directory and configured using `BuildSources=`) are persisted. If enabled, all source directories - will be reset to their original state after scripts finish executing. + will be reset to their original state after scripts (except sync + scripts) finish executing. `Environment=`, `--environment=` @@ -1832,6 +1839,14 @@ 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.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 + various source repositories before building the image. Specifically, + the `BuildSourcesEphemeral=` setting does not apply to sync scripts, + which means sync scripts can be used to update build sources even if + `BuildSourcesEphemeral=` is enabled. + * If **`mkosi.prepare`** (`PrepareScripts=`) exists, it is first called with the `final` argument, right after the software packages are installed. It is called a second time with the `build` command line diff --git a/tests/test_json.py b/tests/test_json.py index d6da93f61..2d0f2b880 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -278,6 +278,9 @@ def test_config() -> None: "Ssh": false, "SshCertificate": "/path/to/cert", "SshKey": null, + "SyncScripts": [ + "/sync" + ], "Timezone": null, "ToolsTree": null, "ToolsTreeDistribution": null, @@ -408,6 +411,7 @@ def test_config() -> None: ssh = False, ssh_certificate = Path("/path/to/cert"), ssh_key = None, + sync_scripts = [Path("/sync")], timezone = None, tools_tree = None, tools_tree_distribution = None,