diff --git a/crates/lib/src/bootloader.rs b/crates/lib/src/bootloader.rs index be1d5882b..22593d192 100644 --- a/crates/lib/src/bootloader.rs +++ b/crates/lib/src/bootloader.rs @@ -105,7 +105,12 @@ pub(crate) fn install_via_bootupd( println!("Installing bootloader via bootupd"); // Build the bootupctl arguments - let mut bootupd_args: Vec<&str> = vec!["backend", "install", "--write-uuid"]; + let mut bootupd_args: Vec<&str> = vec!["backend", "install"]; + if configopts.skip_boot_uuid_stamp { + bootupd_args.push("--with-static-configs") + } else { + bootupd_args.push("--write-uuid"); + } if let Some(v) = verbose { bootupd_args.push(v); } diff --git a/crates/lib/src/install.rs b/crates/lib/src/install.rs index 3536c14cb..0e0ee0ab7 100644 --- a/crates/lib/src/install.rs +++ b/crates/lib/src/install.rs @@ -368,6 +368,11 @@ pub(crate) struct InstallConfigOpts { /// The stateroot name to use. Defaults to `default`. #[clap(long)] pub(crate) stateroot: Option, + + /// Don't pass --write-uuid to bootupd during bootloader installation. + #[clap(long)] + #[serde(default)] + pub(crate) skip_boot_uuid_stamp: bool, } #[derive(Debug, Default, Clone, clap::Parser, Serialize, Deserialize, PartialEq, Eq)] @@ -1512,7 +1517,7 @@ async fn verify_target_fetch( /// Preparation for an install; validates and prepares some (thereafter immutable) global state. async fn prepare_install( - config_opts: InstallConfigOpts, + mut config_opts: InstallConfigOpts, source_opts: InstallSourceOpts, target_opts: InstallTargetOpts, mut composefs_options: InstallComposefsOpts, @@ -1637,8 +1642,15 @@ async fn prepare_install( } let install_config = config::load_config()?; - if install_config.is_some() { + if let Some(ref config) = install_config { tracing::debug!("Loaded install configuration"); + // Merge config file values into config_opts (CLI takes precedence) + // Only apply config file value if CLI didn't explicitly set it + if !config_opts.skip_boot_uuid_stamp { + if let Some(skip) = config.skip_boot_uuid_stamp { + config_opts.skip_boot_uuid_stamp = skip; + } + } } else { tracing::debug!("No install configuration found"); } diff --git a/crates/lib/src/install/config.rs b/crates/lib/src/install/config.rs index d62e51c88..dd63534b3 100644 --- a/crates/lib/src/install/config.rs +++ b/crates/lib/src/install/config.rs @@ -85,6 +85,10 @@ pub(crate) struct InstallConfiguration { pub(crate) root_mount_spec: Option, /// Mount specification for the /boot filesystem. pub(crate) boot_mount_spec: Option, + /// Wether to skip writing the boot partition UUID to the bootloader configuration. + /// When true, uses --with-static-configs instead of --write-uuid for bootupd. + /// Defaults to false if not specified (the boot UUID is written by default). + pub(crate) skip_boot_uuid_stamp: Option, } fn merge_basic(s: &mut Option, o: Option, _env: &EnvProperties) { @@ -160,6 +164,11 @@ impl Mergeable for InstallConfiguration { merge_basic(&mut self.stateroot, other.stateroot, env); merge_basic(&mut self.root_mount_spec, other.root_mount_spec, env); merge_basic(&mut self.boot_mount_spec, other.boot_mount_spec, env); + merge_basic( + &mut self.skip_boot_uuid_stamp, + other.skip_boot_uuid_stamp, + env, + ); if let Some(other_kargs) = other.kargs { self.kargs .get_or_insert_with(Default::default) @@ -731,4 +740,53 @@ boot-mount-spec = "" assert_eq!(install.root_mount_spec.as_deref().unwrap(), ""); assert_eq!(install.boot_mount_spec.as_deref().unwrap(), ""); } + + #[test] + fn test_parse_skip_boot_uuid_stamp() { + // Test parsing true + let c: InstallConfigurationToplevel = toml::from_str( + r#"[install] +skip-boot-uuid-stamp = true +"#, + ) + .unwrap(); + assert_eq!(c.install.unwrap().skip_boot_uuid_stamp.unwrap(), true); + + // Test parsing false + let c: InstallConfigurationToplevel = toml::from_str( + r#"[install] +skip-boot-uuid-stamp = false +"#, + ) + .unwrap(); + assert_eq!(c.install.unwrap().skip_boot_uuid_stamp.unwrap(), false); + + // Test default (not specified) is None + let c: InstallConfigurationToplevel = toml::from_str( + r#"[install] +root-fs-type = "xfs" +"#, + ) + .unwrap(); + assert!(c.install.unwrap().skip_boot_uuid_stamp.is_none()); + } + + #[test] + fn test_merge_skip_boot_uuid_stamp() { + let env = EnvProperties { + sys_arch: "x86_64".to_string(), + }; + let mut install: InstallConfiguration = toml::from_str( + r#"skip-boot-uuid-stamp = false +"#, + ) + .unwrap(); + let other = InstallConfiguration { + skip_boot_uuid_stamp: Some(true), + ..Default::default() + }; + install.merge(other, &env); + // skip_boot_uuid_stamp should be overridden to true + assert_eq!(install.skip_boot_uuid_stamp.unwrap(), true); + } } diff --git a/docs/src/man/bootc-install-config.5.md b/docs/src/man/bootc-install-config.5.md index 985892ebe..64b1c9afb 100644 --- a/docs/src/man/bootc-install-config.5.md +++ b/docs/src/man/bootc-install-config.5.md @@ -33,6 +33,9 @@ The `install` section supports these subfields: - `boot-mount-spec`: A string specifying the /boot filesystem mount specification. If not provided and /boot is a separate mount, its UUID will be used. An empty string signals to omit boot mount kargs entirely. +- `skip-boot-uuid-stamp`: A boolean that controls whether to skip writing partition UUIDs + to the bootloader configuration. When `true`, bootupd is invoked with `--with-static-configs` + instead of `--write-uuid`. Defaults to `false` (UUIDs are written by default). # filesystem