outputs: make compatible with nix run and package lists

This adds new outpus like `format` and `formatNoDeps` which
are compatible with `nix run` so you can do something like

    nix run .#nixosConfigurations.myhostname.config.system.build.formatNoDeps

as originally intended in #78, or add disko to your configuration like

    environment.systemPackages = [
        config.system.build.format
        config.system.build.mount
        config.system.build.destroyFormatMount
    ];

as mentioned in #454.

Fixes part of #454
Supersedes #78

It also deprecates mode `disko` in favor of the clearer
`destroy,format,mount` and adds `format,mount` to allow easier in-place
updates.
This commit is contained in:
Felix Uhl 2024-10-18 22:57:34 +02:00
parent 856a290215
commit daca7be309
9 changed files with 123 additions and 33 deletions

View file

@ -99,7 +99,7 @@ a disk named /dev/sda, you would run the following command to partition, format
and mount the disk. and mount the disk.
```console ```console
sudo nix --experimental-features "nix-command flakes" run github:nix-community/disko/latest -- --mode disko /tmp/disk-config.nix sudo nix --experimental-features "nix-command flakes" run github:nix-community/disko/latest -- --mode destroy,format,mount /tmp/disk-config.nix
``` ```
## Related Tools ## Related Tools

20
cli.nix
View file

@ -17,21 +17,29 @@ let
diskoAttr = diskoAttr =
if noDeps then if noDeps then
{ {
format = "formatScriptNoDeps"; destroy = "_cliDestroyNoDeps";
mount = "mountScriptNoDeps"; format = "_cliFormatNoDeps";
disko = "diskoScriptNoDeps"; mount = "_cliMountNoDeps";
"format,mount" = "_cliFormatMountNoDeps";
"destroy,format,mount" = "_cliDestroyFormatMountNoDeps";
# legacy aliases # legacy aliases
disko = "diskoScriptNoDeps";
create = "createScriptNoDeps"; create = "createScriptNoDeps";
zap_create_mount = "diskoScriptNoDeps"; zap_create_mount = "diskoScriptNoDeps";
}.${mode} }.${mode}
else else
{ {
format = "formatScript"; destroy = "_cliDestroy";
mount = "mountScript"; format = "_cliFormat";
disko = "diskoScript"; mount = "_cliMount";
"format,mount" = "_cliFormatMount";
"destroy,format,mount" = "_cliDestroyFormatMount";
# legacy aliases # legacy aliases
disko = "diskoScript";
create = "createScript"; create = "createScript";
zap_create_mount = "diskoScript"; zap_create_mount = "diskoScript";
}.${mode}; }.${mode};

View file

@ -19,7 +19,22 @@ in
{ {
lib = lib.warn "the .lib.lib output is deprecated" diskoLib; lib = lib.warn "the .lib.lib output is deprecated" diskoLib;
# legacy alias _cliDestroy = cfg: pkgs: ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).destroy;
_cliDestroyNoDeps = cfg: pkgs: ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).destroyNoDeps;
_cliFormat = cfg: pkgs: ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).format;
_cliFormatNoDeps = cfg: pkgs: ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).formatNoDeps;
_cliMount = cfg: pkgs: ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).mount;
_cliMountNoDeps = cfg: pkgs: ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).mountNoDeps;
_cliFormatMount = cfg: pkgs: ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).formatMount;
_cliFormatMountNoDeps = cfg: pkgs: ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).formatMountNoDeps;
_cliDestroyFormatMount = cfg: pkgs: ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).destroyFormatMount;
_cliDestroyFormatMountNoDeps = cfg: pkgs: ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).destroyFormatMountNoDeps;
# legacy aliases
create = cfg: builtins.trace "the create output is deprecated, use format instead" (eval cfg).config.disko.devices._create; create = cfg: builtins.trace "the create output is deprecated, use format instead" (eval cfg).config.disko.devices._create;
createScript = cfg: pkgs: builtins.trace "the create output is deprecated, use format instead" ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).formatScript; createScript = cfg: pkgs: builtins.trace "the create output is deprecated, use format instead" ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).formatScript;
createScriptNoDeps = cfg: pkgs: builtins.trace "the create output is deprecated, use format instead" ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).formatScriptNoDeps; createScriptNoDeps = cfg: pkgs: builtins.trace "the create output is deprecated, use format instead" ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).formatScriptNoDeps;

22
disko
View file

@ -25,10 +25,12 @@ or else from the disko module of a NixOS configuration of that name under .nixos
Options: Options:
* -m, --mode mode * -m, --mode mode
set the mode, either format, mount or disko set the mode, either distroy, format, mount, format,mount or destroy,format,mount
format: create partition tables, zpools, lvms, raids and filesystems destroy: unmount filesystems and destroy partition tables of the selected disks
mount: mount the partition at the specified root-mountpoint format: create partition tables, zpools, lvms, raids and filesystems if they don't exist yet
disko: first unmount and destroy all filesystems on the disks we want to format, then run the create and mount mode mount: mount the partitions at the specified root-mountpoint
format,mount: run format and mount in sequence
destroy,format,mount: run all three modes in sequence. Previously known as --mode disko
* -f, --flake uri * -f, --flake uri
fetch the disko config relative to this flake's root fetch the disko config relative to this flake's root
* --arg name value * --arg name value
@ -126,8 +128,16 @@ nixBuild() {
fi fi
} }
if ! { [[ $mode = "format" ]] || [[ $mode = "mount" ]] || [[ $mode = "disko" ]] || [[ $mode = "create" ]] || [[ $mode = "zap_create_mount" ]] ; }; then if ! {
abort "mode must be either format, mount or disko" # Base modes
[[ $mode = "destroy" ]] || [[ $mode = "format" ]] || [[ $mode = "mount" ]] ||
# Combined modes
[[ $mode = "format,mount" ]] ||
[[ $mode = "destroy,format,mount" ]] || # Replaces --mode disko
# Legacy modes, will be removed in next major version
[[ $mode = "disko" ]] || [[ $mode = "create" ]] || [[ $mode = "zap_create_mount" ]] ;
}; then
abort 'mode must be one of "destroy", "format", "mount", "destroy,format,mount" or "format,mount"'
fi fi
if [[ -n "${flake+x}" ]]; then if [[ -n "${flake+x}" ]]; then

View file

@ -129,7 +129,7 @@ The following step will partition and format your disk, and mount it to `/mnt`.
**Please note: This will erase any existing data on your disk.** **Please note: This will erase any existing data on your disk.**
```console ```console
sudo nix --experimental-features "nix-command flakes" run github:nix-community/disko/latest -- --mode disko /tmp/disk-config.nix sudo nix --experimental-features "nix-command flakes" run github:nix-community/disko/latest -- --mode destroy,format,mount /tmp/disk-config.nix
``` ```
After the command has run, your file system should have been formatted and After the command has run, your file system should have been formatted and

View file

@ -21,10 +21,12 @@ or else from the disko module of a NixOS configuration of that name under .nixos
Options: Options:
* -m, --mode mode * -m, --mode mode
set the mode, either format, mount or disko set the mode, either distroy, format, mount, format,mount or destroy,format,mount
format: create partition tables, zpools, lvms, raids and filesystems destroy: unmount filesystems and destroy partition tables of the selected disks
mount: mount the partition at the specified root-mountpoint format: create partition tables, zpools, lvms, raids and filesystems if they don't exist yet
disko: first unmount and destroy all filesystems on the disks we want to format, then run the create and mount mode mount: mount the partitions at the specified root-mountpoint
format,mount: run format and mount in sequence
destroy,format,mount: run all three modes in sequence. Previously known as --mode disko
* -f, --flake uri * -f, --flake uri
fetch the disko config relative to this flake's root fetch the disko config relative to this flake's root
* --arg name value * --arg name value

View file

@ -444,6 +444,50 @@ let
]; ];
in in
lib.mapAttrs throwIfNoDisksDetected { lib.mapAttrs throwIfNoDisksDetected {
destroy = (diskoLib.writeCheckedBash { inherit pkgs checked; }) "/bin/disko-destroy" ''
export PATH=${lib.makeBinPath destroyDependencies}:$PATH
${cfg.config._destroy}
'';
format = (diskoLib.writeCheckedBash { inherit pkgs checked; }) "/bin/disko-format" ''
export PATH=${lib.makeBinPath (cfg.config._packages pkgs)}:$PATH
${cfg.config._create}
'';
mount = (diskoLib.writeCheckedBash { inherit pkgs checked; }) "/bin/disko-mount" ''
export PATH=${lib.makeBinPath (cfg.config._packages pkgs)}:$PATH
${cfg.config._mount}
'';
formatMount = (diskoLib.writeCheckedBash { inherit pkgs checked; }) "/bin/disko-format-mount" ''
export PATH=${lib.makeBinPath ((cfg.config._packages pkgs) ++ [ pkgs.bash ])}:$PATH
${cfg.config._formatMount}
'';
destroyFormatMount = (diskoLib.writeCheckedBash { inherit pkgs checked; }) "/bin/disko-destroy-format-mount" ''
export PATH=${lib.makeBinPath ((cfg.config._packages pkgs) ++ [ pkgs.bash ] ++ destroyDependencies)}:$PATH
${cfg.config._disko}
'';
# These are useful to skip copying executables uploading a script to an in-memory installer
destroyNoDeps = (diskoLib.writeCheckedBash { inherit pkgs checked; noDeps = true; }) "/bin/disko-destroy" ''
${cfg.config._destroy}
'';
formatNoDeps = (diskoLib.writeCheckedBash { inherit pkgs checked; noDeps = true; }) "/bin/disko-format" ''
${cfg.config._create}
'';
mountNoDeps = (diskoLib.writeCheckedBash { inherit pkgs checked; noDeps = true; }) "/bin/disko-mount" ''
${cfg.config._mount}
'';
formatMountNoDeps = (diskoLib.writeCheckedBash { inherit pkgs checked; noDeps = true; }) "/bin/disko-format-mount" ''
${cfg.config._formatMount}
'';
destroyFormatMountNoDeps = (diskoLib.writeCheckedBash { inherit pkgs checked; noDeps = true; }) "/bin/disko-destroy-format-mount" ''
${cfg.config._disko}
'';
# Legacy scripts, to be removed in version 2.0.0
# They are generally less useful, because the scripts are directly written to their $out path instead of
# into the $out/bin directory, which makes them incompatible with `nix run`
# (see https://github.com/nix-community/disko/pull/78), `lib.buildEnv` and thus `environment.systemPackages`,
# `user.users.<name>.packages` and `home.packages`, see https://github.com/nix-community/disko/issues/454
destroyScript = (diskoLib.writeCheckedBash { inherit pkgs checked; }) "disko-destroy" '' destroyScript = (diskoLib.writeCheckedBash { inherit pkgs checked; }) "disko-destroy" ''
export PATH=${lib.makeBinPath destroyDependencies}:$PATH export PATH=${lib.makeBinPath destroyDependencies}:$PATH
${cfg.config._destroy} ${cfg.config._destroy}
@ -549,6 +593,17 @@ let
${cfg.config._mount} ${cfg.config._mount}
''; '';
}; };
_formatMount = lib.mkOption {
internal = true;
type = lib.types.str;
description = ''
The script to create and mount all devices defined by disko.devices, without wiping the disks first
'';
default = ''
${cfg.config._create}
${cfg.config._mount}
'';
};
_config = lib.mkOption { _config = lib.mkOption {
internal = true; internal = true;
description = '' description = ''

View file

@ -84,7 +84,7 @@ let
${lib.optionalString diskoCfg.testMode '' ${lib.optionalString diskoCfg.testMode ''
export IN_DISKO_TEST=1 export IN_DISKO_TEST=1
''} ''}
${systemToInstall.config.system.build.diskoScript} ${lib.getExe systemToInstall.config.system.build.destroyFormatMount}
''; '';
installer = lib.optionalString cfg.copyNixStore '' installer = lib.optionalString cfg.copyNixStore ''

View file

@ -80,9 +80,9 @@ let
testConfigBooted = testLib.prepareDiskoConfig diskoConfigWithArgs testLib.devices; testConfigBooted = testLib.prepareDiskoConfig diskoConfigWithArgs testLib.devices;
tsp-generator = pkgs.callPackage ../. { checked = true; }; tsp-generator = pkgs.callPackage ../. { checked = true; };
tsp-format = (tsp-generator.formatScript testConfigInstall) pkgs; tsp-format = (tsp-generator._cliFormat testConfigInstall) pkgs;
tsp-mount = (tsp-generator.mountScript testConfigInstall) pkgs; tsp-mount = (tsp-generator._cliMount testConfigInstall) pkgs;
tsp-disko = (tsp-generator.diskoScript testConfigInstall) pkgs; tsp-disko = (tsp-generator._cliDestroyFormatMount testConfigInstall) pkgs;
tsp-config = tsp-generator.config testConfigBooted; tsp-config = tsp-generator.config testConfigBooted;
num-disks = builtins.length (lib.attrNames testConfigBooted.disko.devices.disk); num-disks = builtins.length (lib.attrNames testConfigBooted.disko.devices.disk);
@ -260,24 +260,24 @@ let
machine.succeed("echo -n 'secretsecret' > /tmp/secret.key") machine.succeed("echo -n 'secretsecret' > /tmp/secret.key")
${lib.optionalString (testMode == "direct") '' ${lib.optionalString (testMode == "direct") ''
# running direct mode # running direct mode
machine.succeed("${tsp-format}") machine.succeed("${lib.getExe tsp-format}")
machine.succeed("${tsp-mount}") machine.succeed("${lib.getExe tsp-mount}")
machine.succeed("${tsp-mount}") # verify that mount is idempotent machine.succeed("${lib.getExe tsp-mount}") # verify that mount is idempotent
machine.succeed("${tsp-disko}") # verify that we can destroy and recreate machine.succeed("${lib.getExe tsp-disko}") # verify that we can destroy and recreate
machine.succeed("mkdir -p /mnt/home") machine.succeed("mkdir -p /mnt/home")
machine.succeed("touch /mnt/home/testfile") machine.succeed("touch /mnt/home/testfile")
machine.succeed("${tsp-format}") # verify that format is idempotent machine.succeed("${lib.getExe tsp-format}") # verify that format is idempotent
machine.succeed("test -e /mnt/home/testfile") machine.succeed("test -e /mnt/home/testfile")
''} ''}
${lib.optionalString (testMode == "module") '' ${lib.optionalString (testMode == "module") ''
# running module mode # running module mode
machine.succeed("${nodes.machine.system.build.formatScript}") machine.succeed("${lib.getExe nodes.machine.system.build.format}")
machine.succeed("${nodes.machine.system.build.mountScript}") machine.succeed("${lib.getExe nodes.machine.system.build.mount}")
machine.succeed("${nodes.machine.system.build.mountScript}") # verify that mount is idempotent machine.succeed("${lib.getExe nodes.machine.system.build.mount}") # verify that mount is idempotent
machine.succeed("${nodes.machine.system.build.diskoScript}") # verify that we can destroy and recreate again machine.succeed("${lib.getExe nodes.machine.system.build.destroyFormatMount}") # verify that we can destroy and recreate again
machine.succeed("mkdir -p /mnt/home") machine.succeed("mkdir -p /mnt/home")
machine.succeed("touch /mnt/home/testfile") machine.succeed("touch /mnt/home/testfile")
machine.succeed("${nodes.machine.system.build.formatScript}") # verify that format is idempotent machine.succeed("${lib.getExe nodes.machine.system.build.format}") # verify that format is idempotent
machine.succeed("test -e /mnt/home/testfile") machine.succeed("test -e /mnt/home/testfile")
''} ''}