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
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")
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)
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]
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",
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)}
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
: 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=`
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
"Ssh": false,
"SshCertificate": "/path/to/cert",
"SshKey": null,
+ "SyncScripts": [
+ "/sync"
+ ],
"Timezone": null,
"ToolsTree": null,
"ToolsTreeDistribution": null,
ssh = False,
ssh_certificate = Path("/path/to/cert"),
ssh_key = None,
+ sync_scripts = [Path("/sync")],
timezone = None,
tools_tree = None,
tools_tree_distribution = None,