diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000000..a6828c98fb5 --- /dev/null +++ b/flake.nix @@ -0,0 +1,50 @@ +# Experimental flake interface to Nixpkgs. +# See https://github.com/NixOS/rfcs/pull/49 for details. +{ + edition = 201909; + + description = "A collection of packages for the Nix package manager"; + + outputs = { self }: + let + + jobs = import ./pkgs/top-level/release.nix { + nixpkgs = self; + }; + + lib = import ./lib; + + systems = [ "x86_64-linux" "i686-linux" "x86_64-darwin" "aarch64-linux" ]; + + forAllSystems = f: lib.genAttrs systems (system: f system); + + in + { + lib = lib // { + nixosSystem = { modules, ... } @ args: + import ./nixos/lib/eval-config.nix (args // { + modules = modules ++ + [ { system.nixos.versionSuffix = + ".${lib.substring 0 8 self.lastModified}.${self.shortRev or "dirty"}"; + system.nixos.revision = lib.mkIf (self ? rev) self.rev; + } + ]; + }); + }; + + checks.x86_64-linux.tarball = jobs.tarball; + + htmlDocs = { + nixpkgsManual = jobs.manual; + nixosManual = (import ./nixos/release-small.nix { + nixpkgs = self; + }).nixos.manual.x86_64-linux; + }; + + legacyPackages = forAllSystems (system: import ./. { inherit system; }); + + nixosModules = { + notDetected = import ./nixos/modules/installer/scan/not-detected.nix; + }; + }; +} diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix index 59ed1e507e2..01ff5ecf148 100644 --- a/lib/tests/misc.nix +++ b/lib/tests/misc.nix @@ -148,7 +148,7 @@ runTests { "${builtins.storeDir}/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11"; in { storePath = isStorePath goodPath; - storePathDerivation = isStorePath (import ../.. {}).hello; + storePathDerivation = isStorePath (import ../.. { system = "x86_64-linux"; }).hello; storePathAppendix = isStorePath "${goodPath}/bin/python"; nonAbsolute = isStorePath (concatStrings (tail (stringToCharacters goodPath))); diff --git a/lib/tests/release.nix b/lib/tests/release.nix index 737d142d253..069c015d783 100644 --- a/lib/tests/release.nix +++ b/lib/tests/release.nix @@ -2,7 +2,7 @@ pkgs.runCommandNoCC "nixpkgs-lib-tests" { buildInputs = [ pkgs.nix (import ./check-eval.nix) ]; - NIX_PATH="nixpkgs=${pkgs.path}"; + NIX_PATH = "nixpkgs=${toString pkgs.path}"; } '' datadir="${pkgs.nix}/share" export TEST_ROOT=$(pwd)/test-tmp diff --git a/nixos/doc/manual/man-nixos-rebuild.xml b/nixos/doc/manual/man-nixos-rebuild.xml index 495dbc8859b..f4f663b84f0 100644 --- a/nixos/doc/manual/man-nixos-rebuild.xml +++ b/nixos/doc/manual/man-nixos-rebuild.xml @@ -77,7 +77,14 @@ builder-spec + + + + flake-uri + + + @@ -129,14 +136,17 @@ Description - This command updates the system so that it corresponds to the configuration - specified in /etc/nixos/configuration.nix. Thus, every - time you modify /etc/nixos/configuration.nix or any - NixOS module, you must run nixos-rebuild to make the - changes take effect. It builds the new system in - /nix/store, 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. + This command updates the system so that it corresponds to the + configuration specified in + /etc/nixos/configuration.nix or + /etc/nixos/flake.nix. Thus, every time you + modify the configuration or any other NixOS module, you must run + nixos-rebuild to make the changes take + effect. It builds the new system in + /nix/store, 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. @@ -508,6 +518,24 @@ + + + + flake-uri[name] + + + + Build the NixOS system from the specified flake. It defaults to + the directory containing the target of the symlink + /etc/nixos/flake.nix, if it exists. The + flake must contain an output named + nixosConfigurations.name. If + name is omitted, it default to the + current host name. + + + + @@ -554,6 +582,21 @@ + + + /etc/nixos/flake.nix + + + + If this file exists, then nixos-rebuild will + use it as if the option was given. This + file may be a symlink to a flake.nix in an + actual flake; thus /etc/nixos need not be a + flake. + + + + /run/current-system diff --git a/nixos/doc/manual/man-nixos-version.xml b/nixos/doc/manual/man-nixos-version.xml index e9ad8bddcac..aada08c5b4a 100644 --- a/nixos/doc/manual/man-nixos-version.xml +++ b/nixos/doc/manual/man-nixos-version.xml @@ -12,16 +12,22 @@ - nixos-version + nixos-version - + + + + + + + Description @@ -84,12 +90,16 @@ + Options + This command accepts the following options: + + @@ -107,6 +117,21 @@ + + + + + + + + Print a JSON representation of the versions of NixOS and the + top-level configuration flake. + + + + + + diff --git a/nixos/modules/installer/tools/nixos-rebuild.sh b/nixos/modules/installer/tools/nixos-rebuild.sh index 7db323d38e6..354274478a3 100644 --- a/nixos/modules/installer/tools/nixos-rebuild.sh +++ b/nixos/modules/installer/tools/nixos-rebuild.sh @@ -3,6 +3,9 @@ if [ -x "@shell@" ]; then export SHELL="@shell@"; fi; set -e +set -o pipefail + +export PATH=@path@:$PATH showSyntax() { exec man nixos-rebuild @@ -13,6 +16,7 @@ showSyntax() { # Parse the command line. origArgs=("$@") extraBuildFlags=() +lockFlags=() action= buildNix=1 fast= @@ -58,7 +62,7 @@ while [ "$#" -gt 0 ]; do j="$1"; shift 1 extraBuildFlags+=("$i" "$j") ;; - --show-trace|--keep-failed|-K|--keep-going|-k|--verbose|-v|-vv|-vvv|-vvvv|-vvvvv|--fallback|--repair|--no-build-output|-Q|-j*) + --show-trace|--keep-failed|-K|--keep-going|-k|--verbose|-v|-vv|-vvv|-vvvv|-vvvvv|--fallback|--repair|--no-build-output|-Q|-j*|-L|--refresh|--no-net) extraBuildFlags+=("$i") ;; --option) @@ -93,6 +97,22 @@ while [ "$#" -gt 0 ]; do --use-remote-sudo) maybeSudo=(sudo --) ;; + --flake) + flake="$1" + shift 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") + ;; *) echo "$0: unknown option \`$i'" exit 1 @@ -202,7 +222,7 @@ fi # If ‘--upgrade’ is given, run ‘nix-channel --update nixos’. -if [ -n "$upgrade" -a -z "$_NIXOS_REBUILD_REEXEC" ]; then +if [[ -n $upgrade && -z $_NIXOS_REBUILD_REEXEC && -z $flake ]]; then nix-channel --update nixos # If there are other channels that contain a file called @@ -225,8 +245,15 @@ 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 ]]; then + flake="$(dirname "$(readlink -f /etc/nixos/flake.nix)")" +fi + # Re-execute nixos-rebuild from the Nixpkgs tree. -if [ -z "$_NIXOS_REBUILD_REEXEC" -a -n "$canRun" -a -z "$fast" ]; then +# FIXME: get nixos-rebuild from $flake. +if [[ -z $_NIXOS_REBUILD_REEXEC && -n $canRun && -z $fast && -z $flake ]]; then if p=$(nix-build --no-out-link --expr 'with import {}; config.system.build.nixos-rebuild' "${extraBuildFlags[@]}"); then export _NIXOS_REBUILD_REEXEC=1 exec $p/bin/nixos-rebuild "${origArgs[@]}" @@ -234,10 +261,37 @@ if [ -z "$_NIXOS_REBUILD_REEXEC" -a -n "$canRun" -a -z "$fast" ]; then fi 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 + read -r hostname < /proc/sys/kernel/hostname + if [[ -z $hostname ]]; then + hostname=default + fi + flakeAttr="nixosConfigurations.\"$hostname\"" + else + flakeAttr="nixosConfigurations.\"$flakeAttr\"" + fi +fi + +# Resolve the flake. +if [[ -n $flake ]]; then + flake=$(nix flake info --json "${extraBuildFlags[@]}" "${lockFlags[@]}" -- "$flake" | jq -r .url) +fi + # Find configuration.nix and open editor instead of building. if [ "$action" = edit ]; then - NIXOS_CONFIG=${NIXOS_CONFIG:-$(nix-instantiate --find-file nixos-config)} - exec "${EDITOR:-nano}" "$NIXOS_CONFIG" + if [[ -z $flake ]]; then + NIXOS_CONFIG=${NIXOS_CONFIG:-$(nix-instantiate --find-file nixos-config)} + exec "${EDITOR:-nano}" "$NIXOS_CONFIG" + else + exec nix edit "${lockFlags[@]}" -- "$flake#$flakeAttr" + fi exit 1 fi @@ -296,7 +350,7 @@ prebuiltNix() { remotePATH= -if [ -n "$buildNix" ]; then +if [[ -n $buildNix && -z $flake ]]; then echo "building Nix..." >&2 nixDrv= if ! nixDrv="$(nix-instantiate '' --add-root $tmpDir/nix.drv --indirect -A config.nix.package.out "${extraBuildFlags[@]}")"; then @@ -337,7 +391,7 @@ fi # Update the version suffix if we're building from Git (so that # nixos-version shows something useful). -if [ -n "$canRun" ]; then +if [[ -n $canRun && -z $flake ]]; then if nixpkgs=$(nix-instantiate --find-file nixpkgs "${extraBuildFlags[@]}"); then suffix=$($SHELL $nixpkgs/nixos/modules/installer/tools/get-version-suffix "${extraBuildFlags[@]}" || true) if [ -n "$suffix" ]; then @@ -358,15 +412,37 @@ fi if [ -z "$rollback" ]; then echo "building the system configuration..." >&2 if [ "$action" = switch -o "$action" = boot ]; then - pathToConfig="$(nixBuild '' --no-out-link -A system "${extraBuildFlags[@]}")" + if [[ -z $flake ]]; then + pathToConfig="$(nixBuild '' --no-out-link -A system "${extraBuildFlags[@]}")" + else + outLink=$tmpDir/result + nix build "$flake#$flakeAttr.config.system.build.toplevel" \ + "${extraBuildFlags[@]}" "${lockFlags[@]}" --out-link $outLink + pathToConfig="$(readlink -f $outLink)" + fi copyToTarget "$pathToConfig" targetHostCmd nix-env -p "$profile" --set "$pathToConfig" elif [ "$action" = test -o "$action" = build -o "$action" = dry-build -o "$action" = dry-activate ]; then - pathToConfig="$(nixBuild '' -A system -k "${extraBuildFlags[@]}")" + if [[ -z $flake ]]; then + pathToConfig="$(nixBuild '' -A system -k "${extraBuildFlags[@]}")" + else + nix build "$flake#$flakeAttr.config.system.build.toplevel" "${extraBuildFlags[@]}" "${lockFlags[@]}" + pathToConfig="$(readlink -f ./result)" + fi elif [ "$action" = build-vm ]; then - pathToConfig="$(nixBuild '' -A vm -k "${extraBuildFlags[@]}")" + if [[ -z $flake ]]; then + pathToConfig="$(nixBuild '' -A vm -k "${extraBuildFlags[@]}")" + else + echo "$0: 'build-vm' is not supported with '--flake'" >&2 + exit 1 + fi elif [ "$action" = build-vm-with-bootloader ]; then - pathToConfig="$(nixBuild '' -A vmWithBootLoader -k "${extraBuildFlags[@]}")" + if [[ -z $flake ]]; then + pathToConfig="$(nixBuild '' -A vmWithBootLoader -k "${extraBuildFlags[@]}")" + else + echo "$0: 'build-vm-with-bootloader' is not supported with '--flake'" >&2 + exit 1 + fi else showSyntax fi diff --git a/nixos/modules/installer/tools/nixos-version.sh b/nixos/modules/installer/tools/nixos-version.sh index 190c49a33ec..fb0fe26116a 100644 --- a/nixos/modules/installer/tools/nixos-version.sh +++ b/nixos/modules/installer/tools/nixos-version.sh @@ -6,8 +6,17 @@ case "$1" in exit 1 ;; --hash|--revision) + if ! [[ @revision@ =~ ^[0-9a-f]+$ ]]; then + echo "$0: Nixpkgs commit hash is unknown" + exit 1 + fi echo "@revision@" ;; + --json) + cat < [--nixos-path ] [--system-path ] [--config-file ] [--config ] [--ensure-unique-name] [--auto-start] [--bridge ] [--port ] [--host-address ] [--local-address ] + nixos-container create + [--nixos-path ] + [--system-path ] + [--config ] + [--config-file ] + [--flake ] + [--ensure-unique-name] + [--auto-start] + [--bridge ] + [--port ] + [--host-address ] + [--local-address ] nixos-container destroy nixos-container start nixos-container stop nixos-container terminate nixos-container status - nixos-container update [--config ] [--config-file ] + nixos-container update + [--config ] + [--config-file ] + [--flake ] nixos-container login nixos-container root-login nixos-container run -- args... @@ -49,6 +63,8 @@ my $signal; my $configFile; my $hostAddress; my $localAddress; +my $flake; +my $flakeAttr = "container"; GetOptions( "help" => sub { showHelp() }, @@ -63,6 +79,7 @@ GetOptions( "config-file=s" => \$configFile, "host-address=s" => \$hostAddress, "local-address=s" => \$localAddress, + "flake=s" => \$flake, ) or exit 1; if (defined $hostAddress and !defined $localAddress or defined $localAddress and !defined $hostAddress) { @@ -76,6 +93,11 @@ if (defined $configFile and defined $extraConfig) { "Please define on or the other, but not both"; } +if (defined $flake && $flake =~ /^(.*)#([^#"]+)$/) { + $flake = $1; + $flakeAttr = $2; +} + # Execute the selected action. mkpath("/etc/containers", 0, 0755); @@ -97,8 +119,6 @@ sub writeNixOSConfig { my $localExtraConfig = ""; - - if ($extraConfig) { $localExtraConfig = $extraConfig } elsif ($configFile) { @@ -121,6 +141,14 @@ EOF write_file($nixosConfigFile, $nixosConfig); } +sub buildFlake { + system("nix", "build", "-o", "$systemPath.tmp", "--", + "$flake#nixosConfigurations.\"$flakeAttr\".config.system.build.toplevel") == 0 + or die "$0: failed to build container from flake '$flake'\n"; + $systemPath = readlink("$systemPath.tmp") or die; + unlink("$systemPath.tmp"); +} + if ($action eq "create") { # Acquire an exclusive lock to prevent races with other # invocations of ‘nixos-container create’. @@ -176,6 +204,7 @@ if ($action eq "create") { push @conf, "HOST_BRIDGE=$bridge\n"; push @conf, "HOST_PORT=$port\n"; push @conf, "AUTO_START=$autoStart\n"; + push @conf, "FLAKE=$flake\n" if defined $flake; write_file($confFile, \@conf); close($lock); @@ -191,6 +220,10 @@ if ($action eq "create") { mkpath($profileDir, 0, 0755); # Build/set the initial configuration. + if (defined $flake) { + buildFlake(); + } + if (defined $systemPath) { system("nix-env", "-p", "$profileDir/system", "--set", $systemPath) == 0 or die "$0: failed to set initial container configuration\n"; @@ -326,19 +359,35 @@ elsif ($action eq "status") { } elsif ($action eq "update") { - my $nixosConfigFile = "$root/etc/nixos/configuration.nix"; - # FIXME: may want to be more careful about clobbering the existing - # configuration.nix. - if ((defined $extraConfig && $extraConfig ne "") || - (defined $configFile && $configFile ne "")) { - writeNixOSConfig $nixosConfigFile; + # Unless overriden on the command line, rebuild the flake recorded + # in the container config file. FIXME: read the container config + # in a more sensible way. + if (!defined $flake && !defined $configFile && !defined $extraConfig) { + my $s = read_file($confFile); + $s =~ /^FLAKE=(.*)$/m; + $flake = $1; } - system("nix-env", "-p", "$profileDir/system", - "-I", "nixos-config=$nixosConfigFile", "-f", "", - "--set", "-A", "system") == 0 - or die "$0: failed to build container configuration\n"; + if (defined $flake) { + buildFlake(); + system("nix-env", "-p", "$profileDir/system", "--set", $systemPath) == 0 + or die "$0: failed to set container configuration\n"; + } else { + my $nixosConfigFile = "$root/etc/nixos/configuration.nix"; + + # FIXME: may want to be more careful about clobbering the existing + # configuration.nix. + if ((defined $extraConfig && $extraConfig ne "") || + (defined $configFile && $configFile ne "")) { + writeNixOSConfig $nixosConfigFile; + } + + system("nix-env", "-p", "$profileDir/system", + "-I", "nixos-config=$nixosConfigFile", "-f", "", + "--set", "-A", "system") == 0 + or die "$0: failed to build container configuration\n"; + } if (isContainerRunning) { print STDERR "reloading container...\n"; diff --git a/pkgs/top-level/make-tarball.nix b/pkgs/top-level/make-tarball.nix index 288971403f1..ff0b0568819 100644 --- a/pkgs/top-level/make-tarball.nix +++ b/pkgs/top-level/make-tarball.nix @@ -17,7 +17,10 @@ releaseTools.sourceTarball { inherit officialRelease; version = pkgs.lib.fileContents ../../.version; - versionSuffix = "pre${toString nixpkgs.revCount}.${nixpkgs.shortRev}"; + versionSuffix = "pre${ + if nixpkgs ? lastModified + then builtins.substring 0 8 nixpkgs.lastModified + else toString nixpkgs.revCount}.${nixpkgs.shortRev or "dirty"}"; buildInputs = [ nix.out jq lib-tests ]; @@ -25,7 +28,7 @@ releaseTools.sourceTarball { eval "$preConfigure" releaseName=nixpkgs-$VERSION$VERSION_SUFFIX echo -n $VERSION_SUFFIX > .version-suffix - echo -n ${nixpkgs.rev or nixpkgs.shortRev} > .git-revision + echo -n ${nixpkgs.rev or nixpkgs.shortRev or "dirty"} > .git-revision echo "release name is $releaseName" echo "git-revision is $(cat .git-revision)" ''; diff --git a/pkgs/top-level/release.nix b/pkgs/top-level/release.nix index b8ce1fcbce5..78d70c0239a 100644 --- a/pkgs/top-level/release.nix +++ b/pkgs/top-level/release.nix @@ -14,9 +14,9 @@ , supportedSystems ? [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" ] , limitedSupportedSystems ? [ "i686-linux" ] # Strip most of attributes when evaluating to spare memory usage -, scrubJobs ? true +, scrubJobs ? true # Attributes passed to nixpkgs. Don't build packages marked as unfree. -, nixpkgsArgs ? { config = { allowUnfree = false; inHydra = true; }; } +, nixpkgsArgs ? { config = { allowUnfree = false; inHydra = true; }; } }: with import ./release-lib.nix { inherit supportedSystems scrubJobs nixpkgsArgs; };