diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..3ce80ff --- /dev/null +++ b/default.nix @@ -0,0 +1,51 @@ +# if evaluating outside of the store, copy the current directory to the store and import it +# filtering out .gitignore files and .git directories +# if evaluating inside the store, import the outputs.nix file + +let + contains = str: substr: let + str_length = builtins.stringLength str; + substr_length = builtins.stringLength substr; + listOfPossibleSubstrings = builtins.genList (i: builtins.substring i substr_length str) (str_length - substr_length + 1); + in if substr_length > str_length then false else builtins.any (x: x == substr) listOfPossibleSubstrings; + + endsWith = str: substr: let + str_length = builtins.stringLength str; + substr_length = builtins.stringLength substr; + in if substr_length > str_length then false else builtins.substring (str_length - substr_length) str_length str == substr; + + gitignore = builtins.filter (v: + # ignore comments and empty lines + if !(builtins.isString v) then false + else if !builtins.isNull(builtins.match "^#.*" v) then false + else if !builtins.isNull(builtins.match "^$" v) then false + else true + ) (builtins.split "\n" (builtins.readFile ./.gitignore)); + + # checks if a given path matches a gitignore pattern + # string -> bool + matchesGitIgnore = path: builtins.any (pattern: + let + patternLength = builtins.stringLength pattern; + unsupportedPatternMessage = "matchesGitIgnore: Unsupported pattern: ${pattern}"; + in + if pattern == "*" then true + else if pattern == ".*" then true + else if pattern == "*.*" then true + else if builtins.substring 0 2 pattern == "*." then endsWith path (builtins.substring 0 2 pattern) + else if contains pattern "*" then abort unsupportedPatternMessage + else if patternLength > 2 && builtins.substring 0 2 pattern == "./" then abort unsupportedPatternMessage + else if patternLength > 1 && builtins.substring 0 1 pattern == "/" then abort unsupportedPatternMessage + else contains path pattern + ) gitignore; + + currentFilePath = (builtins.unsafeGetAttrPos "any" { any = "any"; }).file; + storePathLength = builtins.stringLength (builtins.toString builtins.storeDir); + evaluatingInStore = (builtins.substring 0 storePathLength currentFilePath) == builtins.storeDir; + + selfInStore = builtins.filterSource (path: type: + type != "unknown" && builtins.baseNameOf path != ".git" && !matchesGitIgnore path + ) ./.; +in +if !(evaluatingInStore) then import selfInStore +else import ./outputs.nix diff --git a/hosts/main.nix b/hosts/main.nix index 33d97a6..0fca5f6 100644 --- a/hosts/main.nix +++ b/hosts/main.nix @@ -1,4 +1,4 @@ -{ pkgs, ... }: +{ inputs, pkgs, ... }: { imports = [ @@ -20,7 +20,7 @@ ../nix-os/services/nix-binary-cache.nix ../nix-os/udev.nix - (builtins.fetchTarball "https://github.com/nix-community/nixos-vscode-server/tarball/fc900c16efc6a5ed972fb6be87df018bcf3035bc") + "${inputs.nixos-vscode-server}" ]; config = { @@ -45,6 +45,8 @@ ]; }; + services.printing.startWhenNeeded = false; + system.stateVersion = "23.05"; }; } diff --git a/inputs.nix b/inputs.nix new file mode 100644 index 0000000..6c3a94d --- /dev/null +++ b/inputs.nix @@ -0,0 +1,19 @@ +let self = { + lock ? import ./lock.nix +, lib ? import "${(self {}).nixpkgs}/lib" +}: + +{ + inherit lock; + nixos-vscode-server = builtins.fetchTarball { + name = "nixos-vscode-server"; + url = "https://github.com/nix-community/nixos-vscode-server/archive/${lock.nixos-vscode-server.revision}.tar.gz"; + sha256 = "${lock.nixos-vscode-server.sha256}"; + }; + nixpkgs = builtins.fetchTarball { + name = "nixpkgs"; + url = "https://github.com/NixOS/nixpkgs/archive/${lock.nixpkgs.revision}.tar.gz"; + sha256 = "${lock.nixpkgs.sha256}"; + }; +}; +in self diff --git a/lib/overlays/version-info-fixup.nix b/lib/overlays/version-info-fixup.nix new file mode 100644 index 0000000..b413842 --- /dev/null +++ b/lib/overlays/version-info-fixup.nix @@ -0,0 +1,8 @@ +{ inputs ? import ../../inputs.nix {} }: + +selfLib: superLib: { + trivial = superLib.trivial // { + versionSuffix = ".git.${builtins.substring 0 12 inputs.lock.nixpkgs.revision}"; + revisionWithDefault = default: inputs.lock.nixpkgs.revision or default; + }; +} diff --git a/lock.nix b/lock.nix new file mode 100644 index 0000000..09cca69 --- /dev/null +++ b/lock.nix @@ -0,0 +1,10 @@ +{ + nixos-vscode-server = { + revision = "fc900c16efc6a5ed972fb6be87df018bcf3035bc"; + sha256 = "1rq8mrlmbzpcbv9ys0x88alw30ks70jlmvnfr2j8v830yy5wvw7h"; + }; + nixpkgs = { + revision = "10c832d0548e9e3a6df7eb51e68c2783212a303e"; + sha256 = "1p206hgfxbz0rmkzaslfrknbdss4n4dnb09pi5466h8ksmm8216q"; + }; +} diff --git a/nix-os/account.nix b/nix-os/account.nix index 2bb15ff..22bdc01 100644 --- a/nix-os/account.nix +++ b/nix-os/account.nix @@ -17,7 +17,7 @@ initialPassword = "nixos"; packages = with pkgs; [ firefox - vivaldi + (vivaldi.override { proprietaryCodecs = true; }) discord-canary unstablePkgs.vscode gimp diff --git a/nix-os/core-desktop.nix b/nix-os/core-desktop.nix index 2f5a087..07cd85d 100644 --- a/nix-os/core-desktop.nix +++ b/nix-os/core-desktop.nix @@ -4,7 +4,7 @@ config = { services.printing.enable = true; - sound.enable = true; + sound.enable = false; hardware.pulseaudio.enable = false; security.rtkit.enable = true; services.pipewire = { @@ -25,4 +25,4 @@ X11Forwarding yes ''; }; -} \ No newline at end of file +} diff --git a/nix-os/core.nix b/nix-os/core.nix index 75cf2dd..cfb55e4 100644 --- a/nix-os/core.nix +++ b/nix-os/core.nix @@ -2,7 +2,7 @@ # your system. Help is available in the configuration.nix(5) man page # and in the NixOS manual (accessible by running 'nixos-help'). -{ config, pkgs, lib, ... }: +{ inputs, lib, pkgs, ... }: let /* @@ -47,7 +47,16 @@ in # Allow unfree packages nixpkgs.config.allowUnfree = true; - nix.package = wrappedNix; + nix = { + package = wrappedNix; + channel.enable = false; + settings.nix-path = [ + "nixpkgs=${pkgs.selfExpr { useConfig = false; }}" + "systemNixpkgs=${pkgs.selfExpr { useConfig = true; name = "systemNixpkgs-self"; }}" + # don't garbage collect the nixpkgs input + "inputsNixpkgs=${inputs.nixpkgs}" + ]; + }; # List packages installed in system profile. To search, run: # $ nix search wget diff --git a/outputs.nix b/outputs.nix new file mode 100644 index 0000000..ad24442 --- /dev/null +++ b/outputs.nix @@ -0,0 +1,56 @@ +{ inputs ? import ./inputs.nix {} }: + +let + +lib = (import "${inputs.nixpkgs}/lib").extend (import ./lib/overlays/version-info-fixup.nix { inherit inputs; }); + +self = { + inherit lib inputs; + modifiedNixpkgs = import ./pkgs/top-level/impure.nix; + modifiedNixpkgsPure = import ./pkgs/top-level/default.nix; + overlays = { + selfExpr = import ./pkgs/overlays/selfExpr.nix { nixpkgsPath = inputs.nixpkgs; }; + unstable = import ./pkgs/overlays/unstable.nix; + versionInfoFixup = import ./pkgs/overlays/version-info-fixup.nix { inherit inputs; }; + }; + nixosConfigurations = let + # list nix file paths in ./hosts to attributes in nixosConfigurations + filePaths = lib.pipe ./hosts [ + builtins.readDir + ( lib.filterAttrs (name: type: + # filter out non-nix files + type == "regular" + # filter out files that don't end in .nix + && lib.hasSuffix ".nix" name + # filter out files that start with . + && !lib.hasPrefix "." name + )) + (lib.mapAttrsToList (name: _: name)) + ]; + nixosSystem = import "${inputs.nixpkgs}/nixos/lib/eval-config.nix"; + in + # mapped list of nix file paths to attrSet with initialized NixOS configurations, + # whose names are derived from file names + lib.pipe filePaths [ + (builtins.map (name: { + name = builtins.substring 0 (builtins.stringLength name - 4) name; + value = nixosSystem { + inherit lib; + modules = [ + ./hosts/${name} + { + config.nixpkgs.overlays = [ + ( import ./pkgs/overlays/selfExpr.nix { nixpkgsPath = "${builtins.toString ./.}/pkgs/top-level/impure.nix"; } ) + ( import "${inputs.nixpkgs}/pkgs/top-level/by-name-overlay.nix" "${builtins.toString ./.}/pkgs/by-name" ) + self.overlays.versionInfoFixup + ]; + } + ]; + specialArgs = { inherit self inputs; }; + }; + })) + builtins.listToAttrs + ]; +}; + +in self diff --git a/pkgs/by-name/ni/nixos-rebuild/_nixos-rebuild b/pkgs/by-name/ni/nixos-rebuild/_nixos-rebuild new file mode 100644 index 0000000..84e8d22 --- /dev/null +++ b/pkgs/by-name/ni/nixos-rebuild/_nixos-rebuild @@ -0,0 +1,165 @@ +#!/usr/bin/env bash + +# We're faking a `nix build` command-line to re-use Nix's own completion +# for the few options passed through to Nix. +_nixos-rebuild_pretend-nix() { + COMP_LINE="nix build ${COMP_LINE}" + # number of prepended chars + (( COMP_POINT = COMP_POINT + 10)) + + COMP_WORDS=( + nix build + "${COMP_WORDS[@]}" + ) + # Add the amount of prepended words + (( COMP_CWORD = COMP_CWORD + 2)) + _complete_nix "nix" +} + +_nixos-rebuild() { + local curr="$2" + local prev="$3" + local subcommandGiven=0 + local word + local subcommand + + __load_completion nix + + # Arrays are re-ordered by the completion, so it's fine to sort them in logical chunks + local all_args=( + --verbose -v + + # nixos-rebuild options + --fast + --no-build-nix + --profile-name -p # name + --rollback + --specialisation -c # name + --use-remote-sudo + --build-host # host + --target-host # host + # Used with list-generations + --json + + # generation switching options + --install-bootloader + + # nix-channel options + --upgrade + --upgrade-all + + # flakes options + --commit-lock-file + --flake # flake-uri + --override-input # input-name flake-uri + --recreate-lock-file + --update-input + --no-flake + --no-registries + --no-update-lock-file + --no-write-lock-file + + # Nix-copy options + --use-substitutes --substitute-on-destination -s + + # Nix options + --option + --impure + --builders # builder-spec + --show-trace + --keep-failed -K + --keep-going -k + --max-jobs -j # number + --log-format # format + -I # NIX_PATH + ) + + local all_subcommands=( + boot + build + build-vm + build-vm-with-bootloader + dry-activate + dry-build + edit + list-generations + switch + test + ) + + # Suggest arguments that can be consumed under some conditions only + for word in "${COMP_WORDS[@]}"; do + for subcommand in "${all_subcommands[@]}"; do + if [[ "$word" == "$subcommand" ]]; then + subcommandGiven=1 + fi + done + done + + # Fake out a way to complete the second arg to some options + case "${COMP_WORDS[COMP_CWORD-2]}" in + "--override-input") + prev="--override-input_2" + ;; + "--option") + prev="--option_2" + ;; + esac + + case "$prev" in + --max-jobs|-j) + COMPREPLY=( ) + ;; + + --profile-name|-p) + if [[ "$curr" == "" ]]; then + COMPREPLY=( /nix/var/nix/profiles/* ) + else + COMPREPLY=( "$curr"* ) + fi + ;; + + --build-host|--target-host|-t|-h) + _known_hosts_real "$curr" + ;; + + --specialisation|-c) + COMPREPLY=() + ;; + + -I) + _nixos-rebuild_pretend-nix + ;; + --builders) + _nixos-rebuild_pretend-nix + ;; + --flake) + _nixos-rebuild_pretend-nix + ;; + --override-input) + _nixos-rebuild_pretend-nix + ;; + --override-input_2) + _nixos-rebuild_pretend-nix + ;; + --log-format) + _nixos-rebuild_pretend-nix + ;; + --option) + _nixos-rebuild_pretend-nix + ;; + --option_2) + _nixos-rebuild_pretend-nix + ;; + + *) + if [[ "$curr" == -* ]] || (( subcommandGiven )); then + COMPREPLY=( $(compgen -W "${all_args[*]}" -- "$2") ) + else + COMPREPLY=( $(compgen -W "${all_subcommands[*]}" -- "$2") ) + fi + ;; + esac +} + +complete -F _nixos-rebuild nixos-rebuild diff --git a/pkgs/by-name/ni/nixos-rebuild/nixos-rebuild.8 b/pkgs/by-name/ni/nixos-rebuild/nixos-rebuild.8 new file mode 100644 index 0000000..1704993 --- /dev/null +++ b/pkgs/by-name/ni/nixos-rebuild/nixos-rebuild.8 @@ -0,0 +1,558 @@ +.Dd January 1, 1980 +.Dt nixos-rebuild 8 +.Os +.Sh NAME +.Nm nixos-rebuild +.Nd reconfigure a NixOS machine +. +. +. +.Sh SYNOPSIS +.Nm +.Bro +.Cm switch | boot | test | build | dry-build | dry-activate | edit | repl | build-vm | build-vm-with-bootloader | list-generations Op Fl -json +.Brc +.br +.Op Fl -upgrade | -upgrade-all +.Op Fl -install-bootloader +.Op Fl -no-build-nix +.Op Fl -fast +.Op Fl -rollback +.br +.Op Fl -file | F Ar path +.Op Fl -attr | A Ar attrPath +.Op Fl -flake Ar flake-uri +.Op Fl -no-flake +.Op Fl -recreate-lock-file +.Op Fl -no-update-lock-file +.Op Fl -no-write-lock-file +.Op Fl -no-registries +.Op Fl -commit-lock-file +.Op Fl -update-input Ar input-path +.Op Fl -override-input Ar input-path flake-url +.br +.Op Fl -profile-name | p Ar name +.Op Fl -specialisation | c Ar name +.br +.Op Fl -build-host Va host +.Op Fl -target-host Va host +.Op Fl -use-remote-sudo +.br +.Op Fl -verbose | v +.Op Fl -quiet +.Op Fl -log-format Ar format +.Op Fl -no-build-output | Q +.Op Fl -max-jobs | j Va number +.Op Fl -cores Va number +.Op Fl -keep-going | k +.Op Fl -keep-failed | K +.Op Fl -fallback +.Op Fl I Va NIX_PATH +.Op Fl -option Ar name value +.Op Fl -repair +.Op Fl -builders Va builder-spec +.Op Fl -accept-flake-config +.Op Fl -print-build-logs | L +.Op Fl -show-trace +.Op Fl -refresh +.Op Fl -impure +.Op Fl -offline +.Op Fl -no-net +. +. +. +.Sh DESCRIPTION +This command updates the system so that it corresponds to the +configuration specified in +.Pa /etc/nixos/configuration.nix Ns +, +.Pa /etc/nixos/flake.nix +or the file and attribute specified by the +.Fl -file +and/or +.Fl -attr +options. Thus, every time you modify the configuration or any other NixOS +module, you must run +.Nm +to make the changes take effect. It builds the new system in +.Pa /nix/store Ns +, runs its activation script, and stop and (re)starts any system services if +needed. Please note that user services need to be started manually as they +aren't detected by the activation script at the moment. +. +.Pp +This command has one required argument, which specifies the desired +operation. It must be one of the following: +.Bl -tag -width indent +.It Cm switch +Build and activate the new configuration, and make it the boot default. That +is, the configuration is added to the GRUB boot menu as the default +menu entry, so that subsequent reboots will boot the system into the new +configuration. Previous configurations activated with +.Ic nixos-rebuild switch +or +.Ic nixos-rebuild boot +remain available in the GRUB menu. +.Pp +Note that if you are using specializations, running just +.Ic nixos-rebuild switch +will switch you back to the unspecialized, base system \(em in that case, you +might want to use this instead: +.Bd -literal -offset indent +$ nixos-rebuild switch --specialisation your-specialisation-name +.Ed +.Pp +This command will build all specialisations and make them bootable just +like regular +.Ic nixos-rebuild switch +does \(em the only thing different is that it will switch to given +specialisation instead of the base system; it can be also used to switch from +the base system into a specialised one, or to switch between specialisations. +. +.It Cm boot +Build the new configuration and make it the boot default (as with +.Ic nixos-rebuild switch Ns +), but do not activate it. That is, the system continues to run the previous +configuration until the next reboot. +. +.It Cm test +Build and activate the new configuration, but do not add it to the GRUB +boot menu. Thus, if you reboot the system (or if it crashes), you will +automatically revert to the default configuration (i.e. the +configuration resulting from the last call to +.Ic nixos-rebuild switch +or +.Ic nixos-rebuild boot Ns +). +.Pp +Note that if you are using specialisations, running just +.Ic nixos-rebuild test +will activate the unspecialised, base system \(em in that case, you might want +to use this instead: +.Bd -literal -offset indent +$ nixos-rebuild test --specialisation your-specialisation-name +.Ed +.Pp +This command can be also used to switch from the base system into a +specialised one, or to switch between specialisations. +. +.It Cm build +Build the new configuration, but neither activate it nor add it to the +GRUB boot menu. It leaves a symlink named +.Pa result +in the current directory, which points to the output of the top-level +.Dq system +derivation. This is essentially the same as doing +.Bd -literal -offset indent +$ nix-build /path/to/nixpkgs/nixos -A system +.Ed +.Pp +Note that you do not need to be root to run +.Ic nixos-rebuild build Ns +\&. +. +.It Cm dry-build +Show what store paths would be built or downloaded by any of the +operations above, but otherwise do nothing. +. +.It Cm dry-activate +Build the new configuration, but instead of activating it, show what +changes would be performed by the activation (i.e. by +.Ic nixos-rebuild test Ns +). For instance, this command will print which systemd units would be restarted. +The list of changes is not guaranteed to be complete. +. +.It Cm edit +Opens +.Pa configuration.nix +in the default editor. +. +.It Cm repl +Opens the configuration in +.Ic nix repl Ns . +. +.It Cm build-vm +Build a script that starts a NixOS virtual machine with the desired +configuration. It leaves a symlink +.Pa result +in the current directory that points (under +.Ql result/bin/run\- Ns Va hostname Ns \-vm Ns +) +at the script that starts the VM. Thus, to test a NixOS configuration in +a virtual machine, you should do the following: +.Bd -literal -offset indent +$ nixos-rebuild build-vm +$ ./result/bin/run-*-vm +.Ed +.Pp +The VM is implemented using the +.Ql qemu +package. For best performance, you should load the +.Ql kvm-intel +or +.Ql kvm-amd +kernel modules to get hardware virtualisation. +.Pp +The VM mounts the Nix store of the host through the 9P file system. The +host Nix store is read-only, so Nix commands that modify the Nix store +will not work in the VM. This includes commands such as +.Nm Ns +; to change the VM’s configuration, you must halt the VM and re-run the commands +above. +.Pp +The VM has its own ext3 root file system, which is automatically created when +the VM is first started, and is persistent across reboots of the VM. It is +stored in +.Ql ./ Ns Va hostname Ns .qcow2 Ns +\&. +.\" The entire file system hierarchy of the host is available in +.\" the VM under +.\" .Pa /hostfs Ns +.\" . +. +.It Cm build-vm-with-bootloader +Like +.Cm build-vm Ns +, but boots using the regular boot loader of your configuration (e.g. GRUB 1 or +2), rather than booting directly into the kernel and initial ramdisk of the +system. This allows you to test whether the boot loader works correctly. \ +However, it does not guarantee that your NixOS configuration will boot +successfully on the host hardware (i.e., after running +.Ic nixos-rebuild switch Ns +), because the hardware and boot loader configuration in the VM are different. +The boot loader is installed on an automatically generated virtual disk +containing a +.Pa /boot +partition. +. +.It Cm list-generations Op Fl -json +List the available generations in a similar manner to the boot loader +menu. It shows the generation number, build date and time, NixOS version, +kernel version and the configuration revision. +There is also a json version of output available. +.El +. +. +. +.Sh OPTIONS +.Bl -tag -width indent +.It Fl -upgrade , -upgrade-all +Update the root user's channel named +.Ql nixos +before rebuilding the system. +.Pp +In addition to the +.Ql nixos +channel, the root user's channels which have a file named +.Ql .update-on-nixos-rebuild +in their base directory will also be updated. +.Pp +Passing +.Fl -upgrade-all +updates all of the root user's channels. +. +.It Fl -install-bootloader +Causes the boot loader to be (re)installed on the device specified by the +relevant configuration options. +. +.It Fl -no-build-nix +Normally, +.Nm +first builds the +.Ql nixUnstable +attribute in Nixpkgs, and uses the resulting instance of the Nix package manager +to build the new system configuration. This is necessary if the NixOS modules +use features not provided by the currently installed version of Nix. This option +disables building a new Nix. +. +.It Fl -fast +Equivalent to +.Fl -no-build-nix Ns +\&. This option is useful if you call +.Nm +frequently (e.g. if you’re hacking on a NixOS module). +. +.It Fl -rollback +Instead of building a new configuration as specified by +.Pa /etc/nixos/configuration.nix Ns +, roll back to the previous configuration. (The previous configuration is +defined as the one before the “current” generation of the Nix profile +.Pa /nix/var/nix/profiles/system Ns +\&.) +. +.It Fl -builders Ar builder-spec +Allow ad-hoc remote builders for building the new system. This requires +the user executing +.Nm +(usually root) to be configured as a trusted user in the Nix daemon. This can be +achieved by using the +.Va nix.settings.trusted-users +NixOS option. Examples values for that option are described in the +.Dq Remote builds +chapter in the Nix manual, (i.e. +.Ql --builders \(dqssh://bigbrother x86_64-linux\(dq Ns +). By specifying an empty string existing builders specified in +.Pa /etc/nix/machines +can be ignored: +.Ql --builders \(dq\(dq +for example when they are not reachable due to network connectivity. +. +.It Fl -profile-name Ar name , Fl p Ar name +Instead of using the Nix profile +.Pa /nix/var/nix/profiles/system +to keep track of the current and previous system configurations, use +.Pa /nix/var/nix/profiles/system-profiles/ Ns Va name Ns +\&. When you use GRUB 2, for every system profile created with this flag, NixOS +will create a submenu named +.Dq NixOS - Profile Va name +in GRUB’s boot menu, containing the current and previous configurations of this profile. +.Pp +For instance, if you want to test a configuration file named +.Pa test.nix +without affecting the default system profile, you would do: +.Bd -literal -offset indent +$ nixos-rebuild switch -p test -I nixos-config=./test.nix +.Ed +.Pp +The new configuration will appear in the GRUB 2 submenu +.Dq NixOS - Profile 'test' Ns +\&. +. +.It Fl -specialisation Ar name , Fl c Ar name +Activates given specialisation; when not specified, switching and testing +will activate the base, unspecialised system. +. +.It Fl -build-host Ar host +Instead of building the new configuration locally, use the specified host +to perform the build. The host needs to be accessible with +.Ic ssh Ns , +and must be able to perform Nix builds. If the option +.Fl -target-host +is not set, the build will be copied back to the local machine when done. +.Pp +Note that, if +.Fl -no-build-nix +is not specified, Nix will be built both locally and remotely. This is because +the configuration will always be evaluated locally even though the building +might be performed remotely. +.Pp +You can include a remote user name in the host name +.Ns ( Va user@host Ns +). You can also set ssh options by defining the +.Ev NIX_SSHOPTS +environment variable. +. +.It Fl -target-host Ar host +Specifies the NixOS target host. By setting this to something other than an +empty string, the system activation will happen on the remote host instead of +the local machine. The remote host needs to be accessible over +.Ic ssh Ns , +and for the commands +.Cm switch Ns +, +.Cm boot +and +.Cm test +you need root access. +.Pp +If +.Fl -build-host +is not explicitly specified or empty, building will take place locally. +.Pp +You can include a remote user name in the host name +.Ns ( Va user@host Ns +). You can also set ssh options by defining the +.Ev NIX_SSHOPTS +environment variable. +.Pp +Note that +.Nm +honors the +.Va nixpkgs.crossSystem +setting of the given configuration but disregards the true architecture of the +target host. Hence the +.Va nixpkgs.crossSystem +setting has to match the target platform or else activation will fail. +. +.It Fl -use-substitutes +When set, nixos-rebuild will add +.Fl -use-substitutes +to each invocation of nix-copy-closure. This will only affect the behavior of +nixos-rebuild if +.Fl -target-host +or +.Fl -build-host +is also set. This is useful when the target-host connection to cache.nixos.org +is faster than the connection between hosts. +. +.It Fl -use-remote-sudo +When set, nixos-rebuild prefixes activation commands that run on the +.Fl -target-host +system with +.Ic sudo Ns +\&. Setting this option allows deploying as a non-root user. +. +.It Fl -file Ar path , Fl F Ar path +Enable and build the NixOS system from the specified file. The file must +evaluate to an attribute set, and it must contain a valid NixOS configuration +at attribute +.Va attrPath Ns +\&. This is useful for building a NixOS system from a nix file that is not +a flake or a NixOS configuration module. Attribute set a with valid NixOS +configuration can be made using +.Va nixos +function in nixpkgs or importing and calling +.Pa nixos/lib/eval-config.nix +from nixpkgs. If specified without +.Fl -attr +option, builds the configuration from the top-level +attribute of the file. +. +.It Fl -attr Ar attrPath , Fl A Ar attrPath +Enable and build the NixOS system from nix file and use the specified attribute +path from file specified by the +.Fl -file +option. If specified without +.Fl -file +option, uses +.Pa default.nix +in current directory. +. +.It Fl -flake Va flake-uri Ns Op Va #name +Build the NixOS system from the specified flake. It defaults to the directory +containing the target of the symlink +.Pa /etc/nixos/flake.nix Ns +, if it exists. The flake must contain an output named +.Ql nixosConfigurations. Ns Va name Ns +\&. If +.Va name +is omitted, it default to the current host name. +. +.It Fl -no-flake +Do not imply +.Fl -flake +if +.Pa /etc/nixos/flake.nix +exists. With this option, it is possible to build non-flake NixOS configurations +even if the current NixOS systems uses flakes. +.El +.Pp +In addition, +.Nm +accepts following options from nix commands that the tool calls: +. +.Pp +flake-related options: +.Bd -offset indent +.Fl -recreate-lock-file Ns , +.Fl -no-update-lock-file Ns , +.Fl -no-write-lock-file Ns , +.Fl -no-registries Ns , +.Fl -commit-lock-file Ns , +.Fl -update-input Ar input-path Ns , +.Fl -override-input Ar input-path flake-url Ns +.Ed +. +.Pp +Builder options: +.Bd -offset indent +.Fl -verbose Ns , +.Fl v Ns , +.Fl -quiet Ns , +.Fl -log-format Ns , +.Fl -no-build-output Ns , +.Fl Q Ns , +.Fl -max-jobs Ns , +.Fl j Ns , +.Fl -cores Ns , +.Fl -keep-going Ns , +.Fl k Ns , +.Fl -keep-failed Ns , +.Fl K Ns , +.Fl -fallback Ns , +.Fl I Ns , +.Fl -option Ns +.Fl -repair Ns , +.Fl -builders Ns , +.Fl -accept-flake-config Ns , +.Fl -print-build-logs Ns , +.Fl L Ns , +.Fl -show-trace Ns , +.Fl -refresh Ns , +.Fl -impure Ns , +.Fl -offline Ns , +.Fl -no-net Ns +.Ed +. +.Pp +See the Nix manual, +.Ic nix flake lock --help +or +.Ic nix-build --help +for details. +. +. +. +.Sh ENVIRONMENT +.Bl -tag -width indent +.It Ev NIXOS_CONFIG +Path to the main NixOS configuration module. Defaults to +.Pa /etc/nixos/configuration.nix Ns +\&. +. +.It Ev NIX_PATH +A colon-separated list of directories used to look up Nix expressions enclosed +in angle brackets (e.g. ). Example: +.Bd -literal -offset indent +nixpkgs=./my-nixpkgs +.Ed +. +.It Ev NIX_SSHOPTS +Additional options to be passed to +.Ic ssh +on the command line. +.Ed +. +.It Ev NIXOS_SWITCH_USE_DIRTY_ENV +Expose the the current environment variables to post activation scripts. Will +skip usage of +.Ic systemd-run +during system activation. Possibly dangerous, specially in remote environments +(e.g.: via SSH). Will be removed in the future. +.El +. +. +. +.Sh FILES +.Bl -tag -width indent +.It Pa /etc/nixos/flake.nix +If this file exists, then +.Nm +will use it as if the +.Fl -flake +option was given. This file may be a symlink to a +.Pa flake.nix +in an actual flake; thus +.Pa /etc/nixos +need not be a flake. +. +.It Pa /run/current-system +A symlink to the currently active system configuration in the Nix store. +. +.It Pa /nix/var/nix/profiles/system +The Nix profile that contains the current and previous system +configurations. Used to generate the GRUB boot menu. +.El +. +. +. +.Sh BUGS +This command should be renamed to something more descriptive. +. +. +. +.Sh AUTHORS +.An -nosplit +.An Eelco Dolstra +and +.An the Nixpkgs/NixOS contributors diff --git a/pkgs/by-name/ni/nixos-rebuild/nixos-rebuild.sh b/pkgs/by-name/ni/nixos-rebuild/nixos-rebuild.sh new file mode 100755 index 0000000..5cfff7a --- /dev/null +++ b/pkgs/by-name/ni/nixos-rebuild/nixos-rebuild.sh @@ -0,0 +1,874 @@ +#! @runtimeShell@ +# shellcheck shell=bash + +if [ -x "@runtimeShell@" ]; then export SHELL="@runtimeShell@"; fi; + +set -e +set -o pipefail +shopt -s inherit_errexit + +export PATH=@path@:$PATH + +showSyntax() { + exec man nixos-rebuild + exit 1 +} + + +# Parse the command line. +origArgs=("$@") +copyFlags=() +extraBuildFlags=() +lockFlags=() +flakeFlags=(--extra-experimental-features 'nix-command flakes') +action= +buildNix=1 +fast= +rollback= +upgrade= +upgrade_all= +profile=/nix/var/nix/profiles/system +specialisation= +buildHost= +targetHost= +remoteSudo= +verboseScript= +noFlake= +attr= +buildFile=default.nix +buildingAttribute=1 +installBootloader= +json= + +# log the given argument to stderr +log() { + echo "$@" >&2 +} + +while [ "$#" -gt 0 ]; do + i="$1"; shift 1 + case "$i" in + --help) + showSyntax + ;; + switch|boot|test|build|edit|repl|dry-build|dry-run|dry-activate|build-vm|build-vm-with-bootloader|list-generations) + if [ "$i" = dry-run ]; then i=dry-build; fi + if [ "$i" = list-generations ]; then + buildNix= + fast=1 + fi + # exactly one action mandatory, bail out if multiple are given + if [ -n "$action" ]; then showSyntax; fi + action="$i" + ;; + --file|-f) + if [ -z "$1" ]; then + log "$0: ‘--file’ requires an argument" + exit 1 + fi + buildFile="$1" + buildingAttribute= + shift 1 + ;; + --attr|-A) + if [ -z "$1" ]; then + log "$0: ‘--attr’ requires an argument" + exit 1 + fi + attr="$1" + buildingAttribute= + shift 1 + ;; + --install-grub) + log "$0: --install-grub deprecated, use --install-bootloader instead" + installBootloader=1 + ;; + --install-bootloader) + installBootloader=1 + ;; + --no-build-nix) + buildNix= + ;; + --rollback) + rollback=1 + ;; + --upgrade) + upgrade=1 + ;; + --upgrade-all) + upgrade=1 + upgrade_all=1 + ;; + --use-substitutes|--substitute-on-destination|-s) + copyFlags+=("-s") + ;; + -I|--max-jobs|-j|--cores|--builders|--log-format) + j="$1"; shift 1 + extraBuildFlags+=("$i" "$j") + ;; + --accept-flake-config|-j*|--quiet|--print-build-logs|-L|--no-build-output|-Q| --show-trace|--keep-going|-k|--keep-failed|-K|--fallback|--refresh|--repair|--impure|--offline|--no-net) + extraBuildFlags+=("$i") + ;; + --verbose|-v|-vv|-vvv|-vvvv|-vvvvv) + verboseScript="true" + extraBuildFlags+=("$i") + ;; + --option) + j="$1"; shift 1 + k="$1"; shift 1 + extraBuildFlags+=("$i" "$j" "$k") + ;; + --fast) + buildNix= + fast=1 + ;; + --profile-name|-p) + if [ -z "$1" ]; then + log "$0: ‘--profile-name’ requires an argument" + exit 1 + fi + if [ "$1" != system ]; then + profile="/nix/var/nix/profiles/system-profiles/$1" + mkdir -p -m 0755 "$(dirname "$profile")" + fi + shift 1 + ;; + --specialisation|-c) + if [ -z "$1" ]; then + log "$0: ‘--specialisation’ requires an argument" + exit 1 + fi + specialisation="$1" + shift 1 + ;; + --build-host) + buildHost="$1" + shift 1 + ;; + --target-host) + targetHost="$1" + shift 1 + ;; + --use-remote-sudo) + remoteSudo=1 + ;; + --flake) + flake="$1" + shift 1 + ;; + --no-flake) + noFlake=1 + ;; + --recreate-lock-file|--no-update-lock-file|--no-write-lock-file|--no-registries|--commit-lock-file) + lockFlags+=("$i") + ;; + --update-input) + j="$1"; shift 1 + lockFlags+=("$i" "$j") + ;; + --override-input) + j="$1"; shift 1 + k="$1"; shift 1 + lockFlags+=("$i" "$j" "$k") + ;; + --json) + json=1 + ;; + *) + log "$0: unknown option \`$i'" + exit 1 + ;; + esac +done + +if [[ -n "$SUDO_USER" ]]; then + useSudo=1 +fi + +# log the given argument to stderr if verbose mode is on +logVerbose() { + if [ -n "$verboseScript" ]; then + echo "$@" >&2 + fi +} + +# Run a command, logging it first if verbose mode is on +runCmd() { + logVerbose "$" "$@" + "$@" +} + +buildHostCmd() { + local c + if [[ "${useSudo:-x}" = 1 ]]; then + c=("sudo") + else + c=() + fi + + if [ -z "$buildHost" ]; then + runCmd "$@" + elif [ -n "$remoteNix" ]; then + runCmd ssh $SSHOPTS "$buildHost" "${c[@]}" env PATH="$remoteNix":'$PATH' "$@" + else + runCmd ssh $SSHOPTS "$buildHost" "${c[@]}" "$@" + fi +} + +targetHostCmd() { + local c + if [[ "${useSudo:-x}" = 1 ]]; then + c=("sudo") + else + c=() + fi + + if [ -z "$targetHost" ]; then + runCmd "${c[@]}" "$@" + else + runCmd ssh $SSHOPTS "$targetHost" "${c[@]}" "$@" + fi +} + +targetHostSudoCmd() { + if [ -n "$remoteSudo" ]; then + useSudo=1 SSHOPTS="$SSHOPTS -t" targetHostCmd "$@" + else + # While a tty might not be necessary, we apply it to be consistent with + # sudo usage, and an experience that is more consistent with local deployment. + SSHOPTS="$SSHOPTS -t" targetHostCmd "$@" + fi +} + +copyToTarget() { + if ! [ "$targetHost" = "$buildHost" ]; then + if [ -z "$targetHost" ]; then + logVerbose "Running nix-copy-closure with these NIX_SSHOPTS: $SSHOPTS" + NIX_SSHOPTS=$SSHOPTS runCmd nix-copy-closure "${copyFlags[@]}" --from "$buildHost" "$1" + elif [ -z "$buildHost" ]; then + logVerbose "Running nix-copy-closure with these NIX_SSHOPTS: $SSHOPTS" + NIX_SSHOPTS=$SSHOPTS runCmd nix-copy-closure "${copyFlags[@]}" --to "$targetHost" "$1" + else + buildHostCmd nix-copy-closure "${copyFlags[@]}" --to "$targetHost" "$1" + fi + fi +} + +nixBuild() { + logVerbose "Building in legacy (non-flake) mode." + if [ -z "$buildHost" ]; then + logVerbose "No --build-host given, running nix-build locally" + runCmd nix-build "$@" + else + logVerbose "buildHost set to \"$buildHost\", running nix-build remotely" + local instArgs=() + local buildArgs=() + local drv= + + while [ "$#" -gt 0 ]; do + local i="$1"; shift 1 + case "$i" in + -o) + local out="$1"; shift 1 + buildArgs+=("--add-root" "$out" "--indirect") + ;; + -A) + local j="$1"; shift 1 + instArgs+=("$i" "$j") + ;; + -I) # We don't want this in buildArgs + shift 1 + ;; + --no-out-link) # We don't want this in buildArgs + ;; + "<"*) # nix paths + instArgs+=("$i") + ;; + *) + buildArgs+=("$i") + ;; + esac + done + + drv="$(runCmd nix-instantiate "${instArgs[@]}" "${extraBuildFlags[@]}")" + if [ -a "$drv" ]; then + logVerbose "Running nix-copy-closure with these NIX_SSHOPTS: $SSHOPTS" + NIX_SSHOPTS=$SSHOPTS runCmd nix-copy-closure --to "$buildHost" "$drv" + buildHostCmd nix-store -r "$drv" "${buildArgs[@]}" + else + log "nix-instantiate failed" + exit 1 + fi + fi +} + +nixFlakeBuild() { + logVerbose "Building in flake mode." + if [[ -z "$buildHost" && -z "$targetHost" && "$action" != switch && "$action" != boot && "$action" != test && "$action" != dry-activate ]] + then + runCmd nix "${flakeFlags[@]}" build "$@" + readlink -f ./result + elif [ -z "$buildHost" ]; then + runCmd nix "${flakeFlags[@]}" build "$@" --out-link "${tmpDir}/result" + readlink -f "${tmpDir}/result" + else + local attr="$1" + shift 1 + local evalArgs=() + local buildArgs=() + local drv= + + while [ "$#" -gt 0 ]; do + local i="$1"; shift 1 + case "$i" in + --recreate-lock-file|--no-update-lock-file|--no-write-lock-file|--no-registries|--commit-lock-file) + evalArgs+=("$i") + ;; + --update-input) + local j="$1"; shift 1 + evalArgs+=("$i" "$j") + ;; + --override-input) + local j="$1"; shift 1 + local k="$1"; shift 1 + evalArgs+=("$i" "$j" "$k") + ;; + --impure) # We don't want this in buildArgs, it's only needed at evaluation time, and unsupported during realisation + ;; + *) + buildArgs+=("$i") + ;; + esac + done + + drv="$(runCmd nix "${flakeFlags[@]}" eval --raw "${attr}.drvPath" "${evalArgs[@]}" "${extraBuildFlags[@]}")" + if [ -a "$drv" ]; then + logVerbose "Running nix with these NIX_SSHOPTS: $SSHOPTS" + NIX_SSHOPTS=$SSHOPTS runCmd nix "${flakeFlags[@]}" copy "${copyFlags[@]}" --derivation --to "ssh://$buildHost" "$drv" + buildHostCmd nix-store -r "$drv" "${buildArgs[@]}" + else + log "nix eval failed" + exit 1 + fi + fi +} + + +if [ -z "$action" ]; then showSyntax; fi + +# Only run shell scripts from the Nixpkgs tree if the action is +# "switch", "boot", or "test". With other actions (such as "build"), +# the user may reasonably expect that no code from the Nixpkgs tree is +# executed, so it's safe to run nixos-rebuild against a potentially +# untrusted tree. +canRun= +if [[ "$action" = switch || "$action" = boot || "$action" = test ]]; then + canRun=1 +fi + +# Verify that user is not trying to use attribute building and flake +# at the same time +if [[ -z $buildingAttribute && -n $flake ]]; then + log "error: '--flake' cannot be used with '--file' or '--attr'" + exit 1 +fi + +# If ‘--upgrade’ or `--upgrade-all` is given, +# run ‘nix-channel --update nixos’. +if [[ -n $upgrade && -z $_NIXOS_REBUILD_REEXEC && -z $flake ]]; then + # If --upgrade-all is passed, or there are other channels that + # contain a file called ".update-on-nixos-rebuild", update them as + # well. Also upgrade the nixos channel. + + for channelpath in /nix/var/nix/profiles/per-user/root/channels/*; do + channel_name=$(basename "$channelpath") + + if [[ "$channel_name" == "nixos" ]]; then + runCmd nix-channel --update "$channel_name" + elif [ -e "$channelpath/.update-on-nixos-rebuild" ]; then + runCmd nix-channel --update "$channel_name" + elif [[ -n $upgrade_all ]] ; then + runCmd nix-channel --update "$channel_name" + fi + done +fi + +# Make sure that we use the Nix package we depend on, not something +# else from the PATH for nix-{env,instantiate,build}. This is +# important, because NixOS defaults the architecture of the rebuilt +# system to the architecture of the nix-* binaries used. So if on an +# amd64 system the user has an i686 Nix package in her PATH, then we +# would silently downgrade the whole system to be i686 NixOS on the +# next reboot. +if [ -z "$_NIXOS_REBUILD_REEXEC" ]; then + export PATH=@nix@/bin:$PATH +fi + +# Use /etc/nixos/flake.nix if it exists. It can be a symlink to the +# actual flake. +if [[ -z $flake && -e /etc/nixos/flake.nix && -z $noFlake ]]; then + flake="$(dirname "$(readlink -f /etc/nixos/flake.nix)")" +fi + +# For convenience, use the hostname as the default configuration to +# build from the flake. +if [[ -n $flake ]]; then + if [[ $flake =~ ^(.*)\#([^\#\"]*)$ ]]; then + flake="${BASH_REMATCH[1]}" + flakeAttr="${BASH_REMATCH[2]}" + fi + if [[ -z $flakeAttr ]]; then + hostname="$(targetHostCmd cat /proc/sys/kernel/hostname)" + if [[ -z $hostname ]]; then + hostname=default + fi + flakeAttr="nixosConfigurations.\"$hostname\"" + else + flakeAttr="nixosConfigurations.\"$flakeAttr\"" + fi +fi + +if [[ ! -z "$specialisation" && ! "$action" = switch && ! "$action" = test ]]; then + log "error: ‘--specialisation’ can only be used with ‘switch’ and ‘test’" + exit 1 +fi + +tmpDir=$(mktemp -t -d nixos-rebuild.XXXXXX) + +if [[ ${#tmpDir} -ge 60 ]]; then + # Very long tmp dirs lead to "too long for Unix domain socket" + # SSH ControlPath errors. Especially macOS sets long TMPDIR paths. + rmdir "$tmpDir" + tmpDir=$(TMPDIR= mktemp -t -d nixos-rebuild.XXXXXX) +fi + +cleanup() { + for ctrl in "$tmpDir"/ssh-*; do + ssh -o ControlPath="$ctrl" -O exit dummyhost 2>/dev/null || true + done + rm -rf "$tmpDir" +} +trap cleanup EXIT + + +# Re-execute nixos-rebuild from the Nixpkgs tree. +if [[ -z $_NIXOS_REBUILD_REEXEC && -n $canRun && -z $fast ]]; then + if [[ -z $buildingAttribute ]]; then + p=$(runCmd nix-build --no-out-link $buildFile -A "${attr:+$attr.}config.system.build.nixos-rebuild" "${extraBuildFlags[@]}") + SHOULD_REEXEC=1 + elif [[ -z $flake ]]; then + if p=$(runCmd nix-build --no-out-link --expr 'with import {}; config.system.build.nixos-rebuild' "${extraBuildFlags[@]}"); then + SHOULD_REEXEC=1 + fi + else + runCmd nix "${flakeFlags[@]}" build --out-link "${tmpDir}/nixos-rebuild" "$flake#$flakeAttr.config.system.build.nixos-rebuild" "${extraBuildFlags[@]}" "${lockFlags[@]}" + if p=$(readlink -e "${tmpDir}/nixos-rebuild"); then + SHOULD_REEXEC=1 + fi + fi + + if [[ -n $SHOULD_REEXEC ]]; then + export _NIXOS_REBUILD_REEXEC=1 + # Manually call cleanup as the EXIT trap is not triggered when using exec + cleanup + runCmd exec "$p/bin/nixos-rebuild" "${origArgs[@]}" + exit 1 + fi +fi + +# Find configuration.nix and open editor instead of building. +if [ "$action" = edit ]; then + if [[ -n $attr || -n $buildFile ]]; then + log "error: '--file' and '--attr' are not supported with 'edit'" + exit 1 + elif [[ -z $flake ]]; then + NIXOS_CONFIG=${NIXOS_CONFIG:-$(runCmd nix-instantiate --find-file nixos-config)} + if [[ -d $NIXOS_CONFIG ]]; then + NIXOS_CONFIG=$NIXOS_CONFIG/default.nix + fi + runCmd exec ${EDITOR:-nano} "$NIXOS_CONFIG" + else + runCmd exec nix "${flakeFlags[@]}" edit "${lockFlags[@]}" -- "$flake#$flakeAttr" + fi + exit 1 +fi + +SSHOPTS="$NIX_SSHOPTS -o ControlMaster=auto -o ControlPath=$tmpDir/ssh-%n -o ControlPersist=60" + +# First build Nix, since NixOS may require a newer version than the +# current one. +if [[ -n "$rollback" || "$action" = dry-build ]]; then + buildNix= +fi + +nixSystem() { + machine="$(uname -m)" + if [[ "$machine" =~ i.86 ]]; then + machine=i686 + fi + echo $machine-linux +} + +prebuiltNix() { + machine="$1" + if [ "$machine" = x86_64 ]; then + echo @nix_x86_64_linux@ + elif [[ "$machine" =~ i.86 ]]; then + echo @nix_i686_linux@ + elif [[ "$machine" = aarch64 ]]; then + echo @nix_aarch64_linux@ + else + log "$0: unsupported platform" + exit 1 + fi +} + +getNixDrv() { + nixDrv= + + if [[ -z $buildingAttribute ]]; then + if nixDrv="$(runCmd nix-instantiate $buildFile --add-root "$tmpDir/nix.drv" --indirect -A ${attr:+$attr.}config.nix.package.out "${extraBuildFlags[@]}")"; then return; fi + fi + if nixDrv="$(runCmd nix-instantiate '' --add-root "$tmpDir/nix.drv" --indirect -A config.nix.package.out "${extraBuildFlags[@]}")"; then return; fi + if nixDrv="$(runCmd nix-instantiate '' --add-root "$tmpDir/nix.drv" --indirect -A nix "${extraBuildFlags[@]}")"; then return; fi + + if ! nixStorePath="$(runCmd nix-instantiate --eval '' -A "$(nixSystem)" | sed -e 's/^"//' -e 's/"$//')"; then + nixStorePath="$(prebuiltNix "$(uname -m)")" + fi + if ! runCmd nix-store -r "$nixStorePath" --add-root "${tmpDir}/nix" --indirect \ + --option extra-binary-caches https://cache.nixos.org/; then + log "warning: don't know how to get latest Nix" + fi + # Older version of nix-store -r don't support --add-root. + [ -e "$tmpDir/nix" ] || ln -sf "$nixStorePath" "$tmpDir/nix" + if [ -n "$buildHost" ]; then + remoteNixStorePath="$(runCmd prebuiltNix "$(buildHostCmd uname -m)")" + remoteNix="$remoteNixStorePath/bin" + if ! buildHostCmd nix-store -r "$remoteNixStorePath" \ + --option extra-binary-caches https://cache.nixos.org/ >/dev/null; then + remoteNix= + log "warning: don't know how to get latest Nix" + fi + fi +} + +if [[ -n $buildNix && -z $flake ]]; then + log "building Nix..." + getNixDrv + if [ -a "$nixDrv" ]; then + nix-store -r "$nixDrv"'!'"out" --add-root "$tmpDir/nix" --indirect >/dev/null + if [ -n "$buildHost" ]; then + nix-copy-closure "${copyFlags[@]}" --to "$buildHost" "$nixDrv" + # The nix build produces multiple outputs, we add them all to the remote path + for p in $(buildHostCmd nix-store -r "$(readlink "$nixDrv")" "${buildArgs[@]}"); do + remoteNix="$remoteNix${remoteNix:+:}$p/bin" + done + fi + fi + PATH="$tmpDir/nix/bin:$PATH" +fi + + +# Update the version suffix if we're building from Git (so that +# nixos-version shows something useful). +if [[ -n $canRun && -z $flake ]]; then + if nixpkgs=$(runCmd nix-instantiate --find-file nixpkgs "${extraBuildFlags[@]}"); then + suffix=$(runCmd $SHELL "$nixpkgs/nixos/modules/installer/tools/get-version-suffix" "${extraBuildFlags[@]}" || true) + if [ -n "$suffix" ]; then + echo -n "$suffix" > "$nixpkgs/.version-suffix" || true + fi + fi +fi + + +if [ "$action" = dry-build ]; then + extraBuildFlags+=(--dry-run) +fi + +if [ "$action" = repl ]; then + # This is a very end user command, implemented using sub-optimal means. + # You should feel free to improve its behavior, as well as resolve tech + # debt in "breaking" ways. Humans adapt quite well. + if [[ -z $buildingAttribute ]]; then + exec nix repl --file $buildFile $attr "${extraBuildFlags[@]}" + elif [[ -z $flake ]]; then + exec nix repl '' "${extraBuildFlags[@]}" + else + if [[ -n "${lockFlags[0]}" ]]; then + # nix repl itself does not support locking flags + log "nixos-rebuild repl does not support locking flags yet" + exit 1 + fi + d='$' + q='"' + bold="$(echo -e '\033[1m')" + blue="$(echo -e '\033[34;1m')" + attention="$(echo -e '\033[35;1m')" + reset="$(echo -e '\033[0m')" + if [[ -e $flake ]]; then + flakePath=$(realpath "$flake") + else + flakePath=$flake + fi + # This nix repl invocation is impure, because usually the flakeref is. + # For a solution that preserves the motd and custom scope, we need + # something like https://github.com/NixOS/nix/issues/8679. + exec nix repl --impure --expr " + let flake = builtins.getFlake ''$flakePath''; + configuration = flake.$flakeAttr; + motd = '' + $d{$q\n$q} + Hello and welcome to the NixOS configuration + $flakeAttr + in $flake + + The following is loaded into nix repl's scope: + + - ${blue}config${reset} All option values + - ${blue}options${reset} Option data and metadata + - ${blue}pkgs${reset} Nixpkgs package set + - ${blue}lib${reset} Nixpkgs library functions + - other module arguments + + - ${blue}flake${reset} Flake outputs, inputs and source info of $flake + + Use tab completion to browse around ${blue}config${reset}. + + Use ${bold}:r${reset} to ${bold}reload${reset} everything after making a change in the flake. + (assuming $flake is a mutable flake ref) + + See ${bold}:?${reset} for more repl commands. + + ${attention}warning:${reset} nixos-rebuild repl does not currently enforce pure evaluation. + ''; + scope = + assert configuration._type or null == ''configuration''; + assert configuration.class or ''nixos'' == ''nixos''; + configuration._module.args // + configuration._module.specialArgs // + { + inherit (configuration) config options; + lib = configuration.lib or configuration.pkgs.lib; + inherit flake; + }; + in builtins.seq scope builtins.trace motd scope + " "${extraBuildFlags[@]}" + fi +fi + +if [ "$action" = list-generations ]; then + if [ ! -L "$profile" ]; then + log "No profile \`$(basename "$profile")' found" + exit 1 + fi + + generation_from_dir() { + generation_dir="$1" + generation_base="$(basename "$generation_dir")" # Has the format "system-123-link" for generation 123 + no_link_gen="${generation_base%-link}" # remove the "-link" + echo "${no_link_gen##*-}" # remove everything before the last dash + } + describe_generation(){ + generation_dir="$1" + generation_number="$(generation_from_dir "$generation_dir")" + nixos_version="$(cat "$generation_dir/nixos-version" 2> /dev/null || echo "Unknown")" + + kernel_dir="$(dirname "$(realpath "$generation_dir/kernel")")" + kernel_version="$(ls "$kernel_dir/lib/modules" || echo "Unknown")" + + configurationRevision="$("$generation_dir/sw/bin/nixos-version" --configuration-revision 2> /dev/null || true)" + + # Old nixos-version output ignored unknown flags and just printed the version + # therefore the following workaround is done not to show the default output + nixos_version_default="$("$generation_dir/sw/bin/nixos-version")" + if [ "$configurationRevision" == "$nixos_version_default" ]; then + configurationRevision="" + fi + + # jq automatically quotes the output => don't try to quote it in output! + build_date="$(stat "$generation_dir" --format=%W | jq 'todate')" + + pushd "$generation_dir/specialisation/" > /dev/null || : + specialisation_list=(*) + popd > /dev/null || : + + specialisations="$(jq --compact-output --null-input '$ARGS.positional' --args -- "${specialisation_list[@]}")" + + if [ "$(basename "$generation_dir")" = "$(readlink "$profile")" ]; then + current_generation_tag="true" + else + current_generation_tag="false" + fi + + # Escape userdefined strings + nixos_version="$(jq -aR <<< "$nixos_version")" + kernel_version="$(jq -aR <<< "$kernel_version")" + configurationRevision="$(jq -aR <<< "$configurationRevision")" + cat << EOF +{ + "generation": $generation_number, + "date": $build_date, + "nixosVersion": $nixos_version, + "kernelVersion": $kernel_version, + "configurationRevision": $configurationRevision, + "specialisations": $specialisations, + "current": $current_generation_tag +} +EOF + } + + find "$(dirname "$profile")" -regex "$profile-[0-9]+-link" | + sort -Vr | + while read -r generation_dir; do + describe_generation "$generation_dir" + done | + if [ -z "$json" ]; then + jq --slurp -r '.[] | [ + ([.generation, (if .current == true then "current" else "" end)] | join(" ")), + (.date | fromdate | strflocaltime("%Y-%m-%d %H:%M:%S")), + .nixosVersion, .kernelVersion, .configurationRevision, + (.specialisations | join(" ")) + ] | @tsv' | + column --separator $'\t' --table --table-columns "Generation,Build-date,NixOS version,Kernel,Configuration Revision,Specialisation" | + ${PAGER:cat} + else + jq --slurp . + fi + exit 0 +fi + + +# Either upgrade the configuration in the system profile (for "switch" +# or "boot"), or just build it and create a symlink "result" in the +# current directory (for "build" and "test"). +if [ -z "$rollback" ]; then + log "building the system configuration..." + if [[ "$action" = switch || "$action" = boot ]]; then + if [[ -z $buildingAttribute ]]; then + pathToConfig="$(nixBuild $buildFile -A "${attr:+$attr.}config.system.build.toplevel" "${extraBuildFlags[@]}")" + elif [[ -z $flake ]]; then + pathToConfig="$(nixBuild '' --no-out-link -A system "${extraBuildFlags[@]}")" + else + pathToConfig="$(nixFlakeBuild "$flake#$flakeAttr.config.system.build.toplevel" "${extraBuildFlags[@]}" "${lockFlags[@]}")" + fi + copyToTarget "$pathToConfig" + targetHostSudoCmd nix-env -p "$profile" --set "$pathToConfig" + elif [[ "$action" = test || "$action" = build || "$action" = dry-build || "$action" = dry-activate ]]; then + if [[ -z $buildingAttribute ]]; then + pathToConfig="$(nixBuild $buildFile -A "${attr:+$attr.}config.system.build.toplevel" "${extraBuildFlags[@]}")" + elif [[ -z $flake ]]; then + pathToConfig="$(nixBuild '' -A system -k "${extraBuildFlags[@]}")" + else + pathToConfig="$(nixFlakeBuild "$flake#$flakeAttr.config.system.build.toplevel" "${extraBuildFlags[@]}" "${lockFlags[@]}")" + fi + elif [ "$action" = build-vm ]; then + if [[ -z $buildingAttribute ]]; then + pathToConfig="$(nixBuild $buildFile -A "${attr:+$attr.}config.system.build.vm" "${extraBuildFlags[@]}")" + elif [[ -z $flake ]]; then + pathToConfig="$(nixBuild '' -A vm -k "${extraBuildFlags[@]}")" + else + pathToConfig="$(nixFlakeBuild "$flake#$flakeAttr.config.system.build.vm" "${extraBuildFlags[@]}" "${lockFlags[@]}")" + fi + elif [ "$action" = build-vm-with-bootloader ]; then + if [[ -z $buildingAttribute ]]; then + pathToConfig="$(nixBuild $buildFile -A "${attr:+$attr.}config.system.build.vmWithBootLoader" "${extraBuildFlags[@]}")" + elif [[ -z $flake ]]; then + pathToConfig="$(nixBuild '' -A vmWithBootLoader -k "${extraBuildFlags[@]}")" + else + pathToConfig="$(nixFlakeBuild "$flake#$flakeAttr.config.system.build.vmWithBootLoader" "${extraBuildFlags[@]}" "${lockFlags[@]}")" + fi + else + showSyntax + fi + # Copy build to target host if we haven't already done it + if ! [[ "$action" = switch || "$action" = boot ]]; then + copyToTarget "$pathToConfig" + fi +else # [ -n "$rollback" ] + if [[ "$action" = switch || "$action" = boot ]]; then + targetHostSudoCmd nix-env --rollback -p "$profile" + pathToConfig="$profile" + elif [[ "$action" = test || "$action" = build ]]; then + systemNumber=$( + targetHostCmd nix-env -p "$profile" --list-generations | + sed -n '/current/ {g; p;}; s/ *\([0-9]*\).*/\1/; h' + ) + pathToConfig="$profile"-${systemNumber}-link + if [ -z "$targetHost" ]; then + ln -sT "$pathToConfig" ./result + fi + else + showSyntax + fi +fi + + +# If we're not just building, then make the new configuration the boot +# default and/or activate it now. +if [[ "$action" = switch || "$action" = boot || "$action" = test || "$action" = dry-activate ]]; then + # Using systemd-run here to protect against PTY failures/network + # disconnections during rebuild. + # See: https://github.com/NixOS/nixpkgs/issues/39118 + cmd=( + "systemd-run" + "-E" "LOCALE_ARCHIVE" # Will be set to new value early in switch-to-configuration script, but interpreter starts out with old value + "-E" "NIXOS_INSTALL_BOOTLOADER=$installBootloader" + "--collect" + "--no-ask-password" + "--pipe" + "--quiet" + "--same-dir" + "--service-type=exec" + "--unit=nixos-rebuild-switch-to-configuration" + "--wait" + ) + # Check if we have a working systemd-run. In chroot environments we may have + # a non-working systemd, so we fallback to not using systemd-run. + # You may also want to explicitly set NIXOS_SWITCH_USE_DIRTY_ENV environment + # variable, since systemd-run runs inside an isolated environment and + # this may break some post-switch scripts. However keep in mind that this + # may be dangerous in remote access (e.g. SSH). + if [[ -n "$NIXOS_SWITCH_USE_DIRTY_ENV" ]]; then + log "warning: skipping systemd-run since NIXOS_SWITCH_USE_DIRTY_ENV is set. This environment variable will be ignored in the future" + cmd=("env" "NIXOS_INSTALL_BOOTLOADER=$installBootloader") + elif ! targetHostSudoCmd "${cmd[@]}" true; then + logVerbose "Skipping systemd-run to switch configuration since it is not working in target host." + cmd=( + "env" + "-i" + "LOCALE_ARCHIVE=$LOCALE_ARCHIVE" + "NIXOS_INSTALL_BOOTLOADER=$installBootloader" + ) + else + logVerbose "Using systemd-run to switch configuration." + fi + if [[ -z "$specialisation" ]]; then + cmd+=("$pathToConfig/bin/switch-to-configuration") + else + cmd+=("$pathToConfig/specialisation/$specialisation/bin/switch-to-configuration") + + if [ -z "$targetHost" ]; then + specialisationExists=$(test -f "${cmd[-1]}") + else + specialisationExists=$(targetHostCmd test -f "${cmd[-1]}") + fi + + if ! $specialisationExists; then + log "error: specialisation not found: $specialisation" + exit 1 + fi + fi + + if ! targetHostSudoCmd "${cmd[@]}" "$action"; then + log "warning: error(s) occurred while switching to the new configuration" + exit 1 + fi +fi + + +if [[ "$action" = build-vm || "$action" = build-vm-with-bootloader ]]; then + cat >&2 <> ~/configuration.nix < ~/hardware-configuration.nix + + + echo Test traditional NixOS configuration + ######################################### + + expect ${writeText "test-nixos-rebuild-repl-expect" '' + ${expectSetup} + spawn nixos-rebuild repl --fast + + expect "nix-repl> " + + send "config.networking.hostName\n" + expect "\"nixos\"" + ''} + + + echo Test flake based NixOS configuration + ######################################### + + # Switch to flake flavored environment + unset NIX_PATH + cat > $NIX_CONF_DIR/nix.conf < ~/hardware-configuration.nix + + cat >~/flake.nix <" + + send "config.networking.hostName\n" + expect_simple "itsme" + + expect_simple "nix-repl>" + send "lib.version\n" + expect_simple ${escapeExpect ( + # The version string is a bit different in the flake lib, so we expect a prefix and ignore the rest + # Furthermore, including the revision (suffix) would cause unnecessary rebuilds. + # Note that a length of 4 only matches e.g. "24. + lib.strings.substring 0 4 (lib.strings.escapeNixString lib.version))} + + # Make sure it's the right lib - should be the flake lib, not Nixpkgs lib. + expect_simple "nix-repl>" + send "lib?nixosSystem\n" + expect_simple "true" + expect_simple "nix-repl>" + send "lib?nixos\n" + expect_simple "true" + ''} + + pushd "$HOME" + expect ${writeText "test-nixos-rebuild-repl-relative-path-expect" '' + ${expectSetup} + spawn sh -c "nixos-rebuild repl --fast --flake .#testconf" + + expect_simple "nix-repl>" + + send "config.networking.hostName\n" + expect_simple "itsme" + ''} + popd + + echo + + ######### + echo Done + touch $out +'' diff --git a/pkgs/overlays/selfExpr.nix b/pkgs/overlays/selfExpr.nix index 391f32c..ae28729 100644 --- a/pkgs/overlays/selfExpr.nix +++ b/pkgs/overlays/selfExpr.nix @@ -1,21 +1,43 @@ { nixpkgsPath ? }: +let + defaultNixpkgsPath = nixpkgsPath; +in + self: super: { selfExpr = let config = builtins.removeAttrs self.config [ "_undeclared" ]; configJson = builtins.toJSON config; - in - self.writeTextFile { - name = "nixpkgs-self"; - destination = "/default.nix"; - text = '' + + getSelfExpr = { + useConfig ? true, + nixpkgsPath ? defaultNixpkgsPath, + ... + }: let + configText = '' + config = (builtins.fromJSON ''' + ${configJson} + ''') // args.config or {}; + ''; + + removedAttrNames = self.lib.optional useConfig "config"; + removedAttrNamesText = builtins.toJSON removedAttrNames; + in '' { ... } @ args: - import ${builtins.toString nixpkgsPath} { - config = (builtins.fromJSON ''' - ${configJson} - ''') // args.config or {}; - } // builtins.removeAttrs args [ "config" ] + import ${nixpkgsPath} { + ${self.lib.optionalString useConfig configText} + } // builtins.removeAttrs args (builtins.fromJSON ''' + ${removedAttrNamesText} + ''') ''; - }; + + mkNixpkgsChannel = args: self.writeTextFile { + name = args.name or "nixpkgs-self"; + destination = "/default.nix"; + text = getSelfExpr args; + } // { + __functor = _: args: mkNixpkgsChannel args; + }; + in mkNixpkgsChannel {}; } diff --git a/pkgs/overlays/unstable.nix b/pkgs/overlays/unstable.nix index 9aa8f2c..2a20c30 100644 --- a/pkgs/overlays/unstable.nix +++ b/pkgs/overlays/unstable.nix @@ -2,12 +2,21 @@ self: super: let nixos = self.config.nixos or true; + unstableRevision = self.config.unstableRevision or null; + unstableRevisionHash = self.config.unstableRevisionHash or null; useUnstable = self.config.useUnstable or true; - unstablePkgsExprs = if nixos - then builtins.fetchTarball "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz" - else builtins.fetchTarball "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz"; - + unstablePkgsExprs = if !builtins.isNull unstableRevision + then if !builtins.isNull unstableRevisionHash + then builtins.fetchTarball { + url = "https://github.com/NixOS/nixpkgs/archive/${unstableRevision}.tar.gz"; + sha256 = unstableRevisionHash; + } + else builtins.fetchTarball "https://github.com/NixOS/nixpkgs/archive/${unstableRevision}.tar.gz" + else if nixos + then builtins.fetchTarball "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz" + else builtins.fetchTarball "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz"; + # Compiled nixpkgs expression eg expressions from a nix channel nixpkgsVersion = builtins.concatStringsSep "." [ (builtins.readFile "${unstablePkgsExprs}/.version") @@ -16,10 +25,10 @@ let nixpkgsRevision = (builtins.readFile "${unstablePkgsExprs}/.git-revision"); unstablePkgsForNixpkgs = nixpkgs: import unstablePkgsExprs { - # localSystem -> pkgs.stdenv.hostPlatform or pkgs.stdenv.hostPlatform ??? - localSystem = nixpkgs.stdenv.hostPlatform; - # crossSystem -> nixpkgs.stdenv.targetPlatform - crossSystem = nixpkgs.stdenv.targetPlatform; + # localSystem -> pkgs.stdenv.buildPlatform + localSystem = nixpkgs.stdenv.buildPlatform; + # crossSystem -> pkgs.stdenv.hostPlatform or pkgs.stdenv.targetPlatform ?? + crossSystem = nixpkgs.stdenv.hostPlatform; # config -> pkgs.config config = nixpkgs.config; # overlays -> partial of pkgs.overlays diff --git a/pkgs/overlays/version-info-fixup.nix b/pkgs/overlays/version-info-fixup.nix new file mode 100644 index 0000000..14d890e --- /dev/null +++ b/pkgs/overlays/version-info-fixup.nix @@ -0,0 +1,5 @@ +{ inputs ? import ../../inputs.nix {} }: + +self: super: { + lib = super.lib.extend (import ../../lib/overlays/version-info-fixup.nix { inherit inputs; }); +} diff --git a/pkgs/top-level/default.nix b/pkgs/top-level/default.nix new file mode 100644 index 0000000..5bfdc46 --- /dev/null +++ b/pkgs/top-level/default.nix @@ -0,0 +1,22 @@ +{ inputs ? import ../../inputs.nix {} +, uninitializedNixpkgs ? import "${inputs.nixpkgs}/pkgs/top-level/default.nix" +, ... +} @ args: + +let + attrsToRemove = [ + "inputs" + "overlays" + "uninitializedNixpkgs" + ]; + + options = (builtins.removeAttrs args attrsToRemove) // { + overlays = (args.overlays or []) ++ [ + ( import ../overlays/selfExpr.nix { nixpkgsPath = ./impure.nix; } ) + ( import ../overlays/unstable.nix ) + ( import ../overlays/version-info-fixup.nix { inherit inputs; } ) + ( import "${inputs.nixpkgs}/pkgs/top-level/by-name-overlay.nix" ../by-name ) + ]; + }; +in +uninitializedNixpkgs options diff --git a/pkgs/top-level/impure.nix b/pkgs/top-level/impure.nix new file mode 100644 index 0000000..20c7b42 --- /dev/null +++ b/pkgs/top-level/impure.nix @@ -0,0 +1,70 @@ +# Modified copy of github:NixOS/nixpkgs pkgs/top-level/impure.nix +# as of commit 242522b8fed8d63f262fd6e747ba1e4372b59a8e + +# I wish I could just import from nixpkgs and set different path to entry point. + +let + homeDir = builtins.getEnv "HOME"; + + # Return ‘x’ if it evaluates, or ‘def’ if it throws an exception. + try = x: def: let res = builtins.tryEval x; in if res.success then res.value else def; +in + +{ localSystem ? { system = args.system or builtins.currentSystem; } +, system ? localSystem.system +, crossSystem ? localSystem +, config ? let + configFile = builtins.getEnv "NIXPKGS_CONFIG"; + configFile2 = homeDir + "/.config/nixpkgs/config.nix"; + configFile3 = homeDir + "/.nixpkgs/config.nix"; # obsolete + in + if configFile != "" && builtins.pathExists configFile then import configFile + else if homeDir != "" && builtins.pathExists configFile2 then import configFile2 + else if homeDir != "" && builtins.pathExists configFile3 then import configFile3 + else {} +, overlays ? let + isDir = path: builtins.pathExists (path + "/."); + pathOverlays = try (toString ) ""; + homeOverlaysFile = homeDir + "/.config/nixpkgs/overlays.nix"; + homeOverlaysDir = homeDir + "/.config/nixpkgs/overlays"; + overlays = path: + # check if the path is a directory or a file + if isDir path then + # it's a directory, so the set of overlays from the directory, ordered lexicographically + let content = builtins.readDir path; in + map (n: import (path + ("/" + n))) + (builtins.filter + (n: + (builtins.match ".*\\.nix" n != null && + # ignore Emacs lock files (.#foo.nix) + builtins.match "\\.#.*" n == null) || + builtins.pathExists (path + ("/" + n + "/default.nix"))) + (builtins.attrNames content)) + else + # it's a file, so the result is the contents of the file itself + import path; + in + if pathOverlays != "" && builtins.pathExists pathOverlays then overlays pathOverlays + else if builtins.pathExists homeOverlaysFile && builtins.pathExists homeOverlaysDir then + throw '' + Nixpkgs overlays can be specified with ${homeOverlaysFile} or ${homeOverlaysDir}, but not both. + Please remove one of them and try again. + '' + else if builtins.pathExists homeOverlaysFile then + if isDir homeOverlaysFile then + throw (homeOverlaysFile + " should be a file") + else overlays homeOverlaysFile + else if builtins.pathExists homeOverlaysDir then + if !(isDir homeOverlaysDir) then + throw (homeOverlaysDir + " should be a directory") + else overlays homeOverlaysDir + else [] +, crossOverlays ? [] +, ... +} @ args: + +# Assertion checked in called nixpkgs upstream, thus removed here + +import ./. (builtins.removeAttrs args [ "system" ] // { + inherit config overlays localSystem; +}) diff --git a/vm.sh b/vm.sh deleted file mode 100755 index 1317383..0000000 --- a/vm.sh +++ /dev/null @@ -1 +0,0 @@ -nix-build '' -A vm -I nixos-config=./hosts/vm.nix $@ && $(ls ./result/bin/run-*) -m 4096 && rm *.qcow2