11import logging
22import textwrap
3+ import uuid
34from pathlib import Path
45from subprocess import PIPE , CompletedProcess
56from typing import Any
@@ -390,6 +391,202 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]:
390391 )
391392
392393
394+ @patch .dict (nr .process .os .environ , {}, clear = True )
395+ @patch (get_qualified_name (nr .process .subprocess .run ), autospec = True )
396+ @patch (get_qualified_name (nr .cleanup_ssh , nr ), autospec = True )
397+ @patch (get_qualified_name (nr .nix .uuid4 , nr .nix ), autospec = True )
398+ def test_execute_nix_switch_build_target_host (
399+ mock_uuid4 : Any ,
400+ mock_cleanup_ssh : Any ,
401+ mock_run : Any ,
402+ tmp_path : Path ,
403+ ) -> None :
404+ config_path = tmp_path / "test"
405+ config_path .touch ()
406+
407+ def run_side_effect (args : list [str ], ** kwargs : Any ) -> CompletedProcess [str ]:
408+ if args [0 ] == "nix" :
409+ return CompletedProcess ([], 0 , str (config_path ))
410+ elif args [0 ] == "nix-instantiate" and "--find-file" in args :
411+ return CompletedProcess ([], 1 )
412+ elif args [0 ] == "nix-instantiate" :
413+ return CompletedProcess ([], 0 , str (config_path ))
414+ elif args [0 ] == "ssh" and "nix-store" in args :
415+ return CompletedProcess ([], 0 , "/tmp/tmpdir/config" )
416+ elif args [0 ] == "ssh" and "mktemp" in args :
417+ return CompletedProcess ([], 0 , "/tmp/tmpdir" )
418+ elif args [0 ] == "ssh" and "readlink" in args :
419+ return CompletedProcess ([], 0 , str (config_path ))
420+ else :
421+ return CompletedProcess ([], 0 )
422+
423+ mock_run .side_effect = run_side_effect
424+ mock_uuid4 .return_value = uuid .UUID (int = 0 )
425+
426+ nr .execute (
427+ [
428+ "nixos-rebuild" ,
429+ "switch" ,
430+ "--no-flake" ,
431+ "--sudo" ,
432+ "--build-host" ,
433+ "user@build-host" ,
434+ "--target-host" ,
435+ "user@target-host" ,
436+ "--no-reexec" ,
437+ # https://github.com/NixOS/nixpkgs/issues/381457
438+ "-I" ,
439+ "nixos-config=./configuration.nix" ,
440+ "-I" ,
441+ "nixpkgs=$HOME/.nix-defexpr/channels/pinned_nixpkgs" ,
442+ ]
443+ )
444+
445+ assert mock_run .call_count == 10
446+ mock_run .assert_has_calls (
447+ [
448+ call (
449+ [
450+ "nix-instantiate" ,
451+ "--find-file" ,
452+ "nixpkgs" ,
453+ "--include" ,
454+ "nixos-config=./configuration.nix" ,
455+ "--include" ,
456+ "nixpkgs=$HOME/.nix-defexpr/channels/pinned_nixpkgs" ,
457+ ],
458+ check = False ,
459+ stdout = PIPE ,
460+ ** DEFAULT_RUN_KWARGS ,
461+ ),
462+ call (
463+ [
464+ "nix-instantiate" ,
465+ "<nixpkgs/nixos>" ,
466+ "--attr" ,
467+ "config.system.build.toplevel" ,
468+ "--add-root" ,
469+ nr .tmpdir .TMPDIR_PATH / "00000000000000000000000000000000" ,
470+ "--include" ,
471+ "nixos-config=./configuration.nix" ,
472+ "--include" ,
473+ "nixpkgs=$HOME/.nix-defexpr/channels/pinned_nixpkgs" ,
474+ ],
475+ check = True ,
476+ stdout = PIPE ,
477+ ** DEFAULT_RUN_KWARGS ,
478+ ),
479+ call (
480+ ["nix-copy-closure" , "--to" , "user@build-host" , config_path ],
481+ check = True ,
482+ ** DEFAULT_RUN_KWARGS ,
483+ ),
484+ call (
485+ [
486+ "ssh" ,
487+ * nr .process .SSH_DEFAULT_OPTS ,
488+ "user@build-host" ,
489+ "--" ,
490+ "mktemp" ,
491+ "-d" ,
492+ "-t" ,
493+ "nixos-rebuild.XXXXX" ,
494+ ],
495+ check = True ,
496+ stdout = PIPE ,
497+ ** DEFAULT_RUN_KWARGS ,
498+ ),
499+ call (
500+ [
501+ "ssh" ,
502+ * nr .process .SSH_DEFAULT_OPTS ,
503+ "user@build-host" ,
504+ "--" ,
505+ "nix-store" ,
506+ "--realise" ,
507+ str (config_path ),
508+ "--add-root" ,
509+ "/tmp/tmpdir/00000000000000000000000000000000" ,
510+ ],
511+ check = True ,
512+ stdout = PIPE ,
513+ ** DEFAULT_RUN_KWARGS ,
514+ ),
515+ call (
516+ [
517+ "ssh" ,
518+ * nr .process .SSH_DEFAULT_OPTS ,
519+ "user@build-host" ,
520+ "--" ,
521+ "readlink" ,
522+ "-f" ,
523+ "/tmp/tmpdir/config" ,
524+ ],
525+ check = True ,
526+ stdout = PIPE ,
527+ ** DEFAULT_RUN_KWARGS ,
528+ ),
529+ call (
530+ [
531+ "ssh" ,
532+ * nr .process .SSH_DEFAULT_OPTS ,
533+ "user@build-host" ,
534+ "--" ,
535+ "rm" ,
536+ "-rf" ,
537+ "/tmp/tmpdir" ,
538+ ],
539+ check = False ,
540+ ** DEFAULT_RUN_KWARGS ,
541+ ),
542+ call (
543+ [
544+ "nix" ,
545+ "copy" ,
546+ "--from" ,
547+ "ssh://user@build-host" ,
548+ "--to" ,
549+ "ssh://user@target-host" ,
550+ config_path ,
551+ ],
552+ check = True ,
553+ ** DEFAULT_RUN_KWARGS ,
554+ ),
555+ call (
556+ [
557+ "ssh" ,
558+ * nr .process .SSH_DEFAULT_OPTS ,
559+ "user@target-host" ,
560+ "--" ,
561+ "sudo" ,
562+ "nix-env" ,
563+ "-p" ,
564+ "/nix/var/nix/profiles/system" ,
565+ "--set" ,
566+ str (config_path ),
567+ ],
568+ check = True ,
569+ ** DEFAULT_RUN_KWARGS ,
570+ ),
571+ call (
572+ [
573+ "ssh" ,
574+ * nr .process .SSH_DEFAULT_OPTS ,
575+ "user@target-host" ,
576+ "--" ,
577+ "sudo" ,
578+ "env" ,
579+ "NIXOS_INSTALL_BOOTLOADER=0" ,
580+ str (config_path / "bin/switch-to-configuration" ),
581+ "switch" ,
582+ ],
583+ check = True ,
584+ ** DEFAULT_RUN_KWARGS ,
585+ ),
586+ ]
587+ )
588+
589+
393590@patch .dict (nr .process .os .environ , {}, clear = True )
394591@patch (get_qualified_name (nr .process .subprocess .run ), autospec = True )
395592@patch (get_qualified_name (nr .cleanup_ssh , nr ), autospec = True )
@@ -469,7 +666,7 @@ def run_side_effect(args: list[str], **kwargs: Any) -> CompletedProcess[str]:
469666 "sudo" ,
470667 "env" ,
471668 "NIXOS_INSTALL_BOOTLOADER=0" ,
472- f" { config_path / ' bin/switch-to-configuration' } " ,
669+ str ( config_path / " bin/switch-to-configuration" ) ,
473670 "switch" ,
474671 ],
475672 check = True ,
@@ -493,7 +690,7 @@ def test_execute_nix_switch_flake_build_host(
493690 def run_side_effect (args : list [str ], ** kwargs : Any ) -> CompletedProcess [str ]:
494691 if args [0 ] == "nix" and "eval" in args :
495692 return CompletedProcess ([], 0 , str (config_path ))
496- if args [0 ] == "ssh" and "nix" in args :
693+ elif args [0 ] == "ssh" and "nix" in args :
497694 return CompletedProcess ([], 0 , str (config_path ))
498695 else :
499696 return CompletedProcess ([], 0 )
0 commit comments