build_dir: Optional[Path]
use_subvolumes: ConfigFeature
repart_offline: bool
+ history: bool
proxy_url: Optional[str]
proxy_exclude: list[str]
default=True,
scope=SettingScope.universal,
),
+ ConfigSetting(
+ dest="history",
+ metavar="BOOL",
+ section="Build",
+ parse=config_parse_boolean,
+ help="Whether mkosi can store information about previous builds",
+ ),
ConfigSetting(
dest="proxy_url",
# Compare inodes instead of paths so we can't get tricked by bind mounts and such.
self.includes: set[tuple[int, int]] = set()
self.immutable: set[str] = set()
+ self.only_sections: tuple[str, ...] = tuple()
def expand_specifiers(self, text: str, path: Path) -> str:
percent = False
):
continue
+ if self.only_sections and s.section not in self.only_sections:
+ continue
+
for f in s.paths:
extra = parse_path(
f,
files = getattr(self.config, 'files')
files += [abs_path]
- for section, k, v in parse_ini(path, only_sections={s.section for s in SETTINGS}):
+ for section, k, v in parse_ini(path, only_sections=self.only_sections or {s.section for s in SETTINGS}):
if not k and not v:
continue
# First, we parse the command line arguments into a separate namespace.
argparser = create_argument_parser()
argparser.parse_args(argv, context.cli)
- context.parse_new_includes()
args = load_args(context.cli)
# If --debug was passed, apply it as soon as possible.
if not args.verb.needs_config():
return args, ()
+ if (
+ args.verb.needs_build() and
+ args.verb != Verb.build and
+ not args.force and
+ Path(".mkosi-private/history/latest.json").exists()
+ ):
+ prev = Config.from_json(Path(".mkosi-private/history/latest.json").read_text())
+
+ # If we're operating on a previously built image (qemu, boot, shell, ...), we're not rebuilding the
+ # image and the configuration of the latest build is available, we load the config that was used to build the
+ # previous image from there instead of parsing configuration files, except for the Host section settings which
+ # we allow changing without requiring a rebuild of the image.
+ for s in SETTINGS:
+ if s.section in ("Include", "Host"):
+ continue
+
+ if hasattr(context.cli, s.dest) and getattr(context.cli, s.dest) != getattr(prev, s.dest):
+ logging.warning(f"Ignoring {s.long} from the CLI. Run with -f to rebuild the image with this setting")
+
+ setattr(context.cli, s.dest, getattr(prev, s.dest))
+ if hasattr(context.config, s.dest):
+ delattr(context.config, s.dest)
+
+ context.only_sections = ("Include", "Host",)
+ else:
+ prev = None
+
+ context.parse_new_includes()
+
# One of the specifiers needs access to the directory, so make sure it is available.
setattr(context.config, "directory", args.directory)
setattr(context.config, "files", [])
for s in SETTINGS:
setattr(config, s.dest, context.finalize_value(s))
+ if prev:
+ return args, (load_config(config),)
+
images = []
# If Dependencies= was not explicitly specified on the CLI or in the configuration,
Build Directory: {none_to_none(config.build_dir)}
Use Subvolumes: {config.use_subvolumes}
Repart Offline: {yes_no(config.repart_offline)}
+ Save History: {yes_no(config.history)}
{bold("HOST CONFIGURATION")}:
Proxy URL: {none_to_none(config.proxy_url)}
devices have to be used to ensure the SELinux extended attributes end
up in the generated XFS filesystem.
+`History=`, `--history=`
+: Takes a boolean. If enabled, mkosi will write information about the
+ latest build to the `.mkosi-private` subdirectory in the directory
+ from which it was invoked. This information is then used to restore
+ the config of the latest build when running any verb that needs a
+ build without specifying `--force`.
+
+ To give an example of why this is useful, if you run
+ `mkosi -O my-custom-output-dir -f` followed by `mkosi qemu`, `mkosi`
+ will fail saying the image hasn't been built yet. If you run
+ `mkosi -O my-custom-output-dir --history=yes -f` followed by
+ `mkosi qemu`, it will boot the image built in the previous step as
+ expected.
+
### [Host] Section
`ProxyUrl=`, `--proxy-url=`