calamares-nixos-extensions: add filesystem type chooser with btrfs support (#475297)

This commit is contained in:
tomberek
2026-01-22 02:45:16 +00:00
committed by GitHub
3 changed files with 120 additions and 8 deletions
@@ -20,8 +20,20 @@ extraMounts:
mountPoint: /sys/firmware/efi/efivars
efi: true
# Ensure the right fmask/dmask is set on the ESP, as it will be
# picked up by nixos-generate-config later
# Btrfs subvolume layout for NixOS
# Empty subvolume for / means no subvolume (top-level) for GRUB compatibility
btrfsSubvolumes:
- mountPoint: /
subvolume: ""
- mountPoint: /home
subvolume: /home
- mountPoint: /nix
subvolume: /nix
# Mount options by filesystem type
# nixos-generate-config picks these up for hardware-configuration.nix
mountOptions:
- filesystem: efi
options: [ fmask=0077, dmask=0077 ]
- filesystem: btrfs
options: [ compress=zstd, noatime ]
@@ -12,11 +12,29 @@ userSwapChoices:
luksGeneration: luks2
defaultFileSystemType: "ext4"
availableFileSystemTypes:
- ext4
- btrfs
- xfs
- f2fs
showNotEncryptedBootMessage: false
bios:
mountPoint: "/boot"
minimumSize: 512MiB
recommendedSize: 1GiB
label: "BOOT"
partitionLayout:
- name: "root"
- name: "boot"
filesystem: "ext4"
mountPoint: "/boot"
size: 1GiB
- name: "root"
filesystem: "unknown"
noEncrypt: false
mountPoint: "/"
size: 100%
@@ -45,6 +45,15 @@ cfgbootbios = """ # Bootloader.
"""
cfgbootbiosbtrfs = """ # Bootloader.
boot.loader.grub.enable = true;
boot.loader.grub.device = "@@bootdev@@";
boot.loader.grub.useOSProber = true;
# Use provided UUIDs instead of blkid probing (required for btrfs subvolumes)
boot.loader.grub.fsIdentifier = "provided";
"""
cfgbootnone = """ # Disable bootloader.
boot.loader.grub.enable = false;
@@ -360,6 +369,46 @@ def catenate(d, key, *values):
d[key] = "".join(values)
def fix_btrfs_subvolumes(hardware_config, partitions):
"""
Fix btrfs subvolume configuration in hardware-configuration.nix.
nixos-generate-config generates incorrect subvol options for btrfs
by setting them to mount points instead of actual subvolume names.
This function corrects the subvol options to match what Calamares configured.
"""
# Map of mount points to their correct btrfs subvolume names
# These match what is configured in mount.conf btrfsSubvolumes
# Root uses top-level (no subvolume) for GRUB compatibility
subvol_map = {
"/home": "home",
"/nix": "nix",
}
# Check if root is actually btrfs
root_is_btrfs = False
for part in partitions:
if part.get("mountPoint") == "/" and part.get("fs") == "btrfs":
root_is_btrfs = True
break
if not root_is_btrfs:
return hardware_config
libcalamares.utils.debug("Fixing btrfs subvolume configuration")
# Fix subvol options for each mount point
# nixos-generate-config may generate "subvol=/" instead of "subvol=@"
for mount_point, correct_subvol in subvol_map.items():
# Match any "subvol=<something>" and replace with correct subvolume
# This handles cases like "subvol=/" or "subvol=/home"
pattern = r'(fileSystems\."{}"[^;]*"subvol=)[^"]*"'.format(re.escape(mount_point))
replacement = r'\g<1>{}"'.format(correct_subvol)
hardware_config = re.sub(pattern, replacement, hardware_config, flags=re.DOTALL)
return hardware_config
def run():
"""NixOS Configuration."""
@@ -388,11 +437,22 @@ def run():
# Pick config parts and prepare substitution
# Check if root filesystem is btrfs
root_is_btrfs = False
for part in gs.value("partitions"):
if part.get("mountPoint") == "/" and part.get("fs") == "btrfs":
root_is_btrfs = True
break
# Check bootloader
if fw_type == "efi":
cfg += cfgbootefi
elif bootdev != "nodev":
cfg += cfgbootbios
# Use btrfs-specific config to avoid blkid issues with subvolumes
if root_is_btrfs:
cfg += cfgbootbiosbtrfs
else:
cfg += cfgbootbios
catenate(variables, "bootdev", bootdev)
else:
cfg += cfgbootnone
@@ -734,9 +794,20 @@ def run():
libcalamares.utils.error(e.output.decode("utf8"))
return (_("nixos-generate-config failed"), _(e.output.decode("utf8")))
# Check for unfree stuff in hardware-configuration.nix
# Read and fix hardware-configuration.nix
hf = open(root_mount_point + "/etc/nixos/hardware-configuration.nix", "r")
htxt = hf.read()
hf.close()
hardware_modified = False
# Fix btrfs subvolume configuration if needed
htxt_fixed = fix_btrfs_subvolumes(htxt, gs.value("partitions"))
if htxt_fixed != htxt:
htxt = htxt_fixed
hardware_modified = True
# Check for unfree stuff in hardware-configuration.nix
search = re.search(r"boot\.extraModulePackages = \[ (.*) \];", htxt)
# Check if any extraModulePackages are defined, and remove if only free packages are allowed
@@ -765,14 +836,17 @@ def run():
)
)
expkgs.remove(pkg)
hardwareout = re.sub(
htxt = re.sub(
r"boot\.extraModulePackages = \[ (.*) \];",
"boot.extraModulePackages = [ {}];".format(
"".join(map(lambda x: x + " ", expkgs))
),
htxt,
)
# Write the hardware-configuration.nix file
hardware_modified = True
# Write the hardware-configuration.nix file if modified
if hardware_modified:
libcalamares.utils.host_env_process_output(
[
"cp",
@@ -780,7 +854,7 @@ def run():
root_mount_point + "/etc/nixos/hardware-configuration.nix",
],
None,
hardwareout,
htxt,
)
# Write the configuration.nix file
@@ -789,6 +863,14 @@ def run():
status = _("Installing NixOS")
libcalamares.job.setprogress(0.3)
try:
subprocess.check_output(
["pkexec", "chmod", "755", root_mount_point],
stderr=subprocess.STDOUT,
)
except subprocess.CalledProcessError as e:
libcalamares.utils.warning("Failed to set permissions on {}: {}".format(root_mount_point, e.output))
# build nixos-install command
nixosInstallCmd = [ "pkexec" ]
nixosInstallCmd.extend(generateProxyStrings())