From e855c7e2c9a9a5cbe4406c1f9351181a9ebe6283 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sat, 14 Apr 2012 18:38:52 +0200 Subject: [PATCH] nix-channel improvements "nix-channel --add" now accepts a second argument: the channel name. This allows channels to have a nicer name than (say) nixpkgs_unstable. If no name is given, it defaults to the last component of the URL (with "-unstable" or "-stable" removed). Also, channels are now stored in a profile (/nix/var/nix/profiles/per-user/$USER/channels). One advantage of this is that it allows rollbacks (e.g. if "nix-channel --update" gives an undesirable update). --- corepkgs/unpack-channel.nix | 6 +- corepkgs/unpack-channel.sh | 32 +-------- doc/manual/nix-channel.xml | 48 +++++++++----- doc/manual/nix-store.xml | 5 +- scripts/nix-channel.in | 129 +++++++++++++++++------------------- tests/nix-channel.sh | 4 +- 6 files changed, 104 insertions(+), 120 deletions(-) diff --git a/corepkgs/unpack-channel.nix b/corepkgs/unpack-channel.nix index 5e6ccf23fd..eba957dff4 100644 --- a/corepkgs/unpack-channel.nix +++ b/corepkgs/unpack-channel.nix @@ -1,11 +1,11 @@ with import ; -{ system, inputs }: +{ name, src }: derivation { - name = "channels"; + system = builtins.currentSystem; builder = shell; args = [ "-e" ./unpack-channel.sh ]; - inherit system inputs bzip2 tar tr; + inherit name src bzip2 tar tr; PATH = "${nixBinDir}:${coreutils}"; } diff --git a/corepkgs/unpack-channel.sh b/corepkgs/unpack-channel.sh index 7c244a6a85..0b7d89bc46 100644 --- a/corepkgs/unpack-channel.sh +++ b/corepkgs/unpack-channel.sh @@ -1,30 +1,4 @@ mkdir $out -mkdir $out/tmp -cd $out/tmp - -inputs=($inputs) -for ((n = 0; n < ${#inputs[*]}; n += 2)); do - channelName=${inputs[n]} - channelTarball=${inputs[n+1]} - - echo "unpacking channel $channelName" - - $bzip2 -d < $channelTarball | $tar xf - - - if test -e */channel-name; then - channelName="$(cat */channel-name)" - fi - - nr=1 - attrName=$(echo $channelName | $tr -- '- ' '__') - dirName=$attrName - while test -e ../$dirName; do - nr=$((nr+1)) - dirName=$attrName-$nr - done - - mv * ../$dirName # !!! hacky -done - -cd .. -rmdir tmp +cd $out +$bzip2 -d < $src | $tar xf - +mv * $out/$name diff --git a/doc/manual/nix-channel.xml b/doc/manual/nix-channel.xml index 024add8601..22d8900d8f 100644 --- a/doc/manual/nix-channel.xml +++ b/doc/manual/nix-channel.xml @@ -19,7 +19,7 @@ nix-channel - url + url name url @@ -31,32 +31,39 @@ A Nix channel is mechanism that allows you to automatically stay up-to-date with a set of pre-built Nix expressions. A Nix channel is -just a URL that points to a place that contains a set of Nix -expressions, as well as a nix-push manifest. See -also . +just a URL that points to a place containing a set of Nix expressions +and a nix-push manifest. See also . This command has the following operations: - url + url [name] - Adds url to the list of - subscribed channels. + Adds a channel named + name with URL + url to the list of subscribed channels. + If name is omitted, it defaults to the + last component of url, with the + suffixes -stable or + -unstable removed. - url + name - Removes url from the - list of subscribed channels. + Removes the channel named + name from the list of subscribed + channels. - Prints the URLs of all subscribed channels on - standard output. + Prints the names and URLs of all subscribed + channels on standard output. @@ -64,7 +71,7 @@ also . Downloads the Nix expressions of all subscribed channels, makes them the default for nix-env - operations (by symlinking them in the directory + operations (by symlinking them from the directory ~/.nix-defexpr), and performs a nix-pull on the manifests of all channels to make pre-built binaries available. @@ -75,8 +82,8 @@ also . -Note that and -do not automatically perform an update. +Note that does not automatically perform +an update. The list of subscribed channels is stored in ~/.nix-channels. @@ -90,4 +97,15 @@ respectively. +Examples + +To subscribe to the Nixpkgs channel and install the GNU Hello package: + + +$ nix-channel --add http://nixos.org/releases/nixpkgs/channels/nixpkgs-unstable +$ nix-channel --update +$ nix-env -iA nixpkgs.hello + + + diff --git a/doc/manual/nix-store.xml b/doc/manual/nix-store.xml index 6a4ca3f717..6cc765bf27 100644 --- a/doc/manual/nix-store.xml +++ b/doc/manual/nix-store.xml @@ -58,8 +58,9 @@ options. Causes the result of a realisation ( and ) - to be registered as a root of the garbage collector (see ). The root is stored in + to be registered as a root of the garbage collector (see ). The root is stored in path, which must be inside a directory that is scanned for roots by the garbage collector (i.e., typically in a subdirectory of diff --git a/scripts/nix-channel.in b/scripts/nix-channel.in index ebfc246cfa..283071a9c8 100755 --- a/scripts/nix-channel.in +++ b/scripts/nix-channel.in @@ -1,6 +1,8 @@ #! @perl@ -w @perlFlags@ use strict; +use File::Basename; +use File::Path qw(make_path); use Nix::Config; my $manifestDir = $Nix::Config::manifestDir; @@ -11,67 +13,67 @@ my $channelCache = "$Nix::Config::stateDir/channel-cache"; mkdir $channelCache, 0755 unless -e $channelCache; $ENV{'NIX_DOWNLOAD_CACHE'} = $channelCache if -W $channelCache; - # Figure out the name of the `.nix-channels' file to use. -my $home = $ENV{"HOME"}; -die '$HOME not set' unless defined $home; +my $home = $ENV{"HOME"} or die '$HOME not set\n'; my $channelsList = "$home/.nix-channels"; - my $nixDefExpr = "$home/.nix-defexpr"; + +# Figure out the name of the channels profile. +my $userName = getpwuid($<) or die "cannot figure out user name"; +my $profile = "$Nix::Config::stateDir/profiles/per-user/$userName/channels"; +make_path(dirname $profile, mode => 0755); - -my @channels; +my %channels; -# Reads the list of channels from the file $channelsList; +# Reads the list of channels. sub readChannels { return if (!-f $channelsList); open CHANNELS, "<$channelsList" or die "cannot open `$channelsList': $!"; while () { chomp; next if /^\s*\#/; - push @channels, $_; + my ($url, $name) = split ' ', $_; + $url =~ s/\/*$//; # remove trailing slashes + $name = basename $url unless defined $name; + $channels{$name} = $url; } close CHANNELS; } -# Writes the list of channels to the file $channelsList; +# Writes the list of channels. sub writeChannels { open CHANNELS, ">$channelsList" or die "cannot open `$channelsList': $!"; - foreach my $url (@channels) { - print CHANNELS "$url\n"; + foreach my $name (keys %channels) { + print CHANNELS "$channels{$name} $name\n"; } close CHANNELS; } -# Adds a channel to the file $channelsList; +# Adds a channel. sub addChannel { - my $url = shift; + my ($url, $name) = @_; readChannels; - foreach my $url2 (@channels) { - return if $url eq $url2; - } - push @channels, $url; + $channels{$name} = $url; writeChannels; } -# Remove a channel from the file $channelsList; +# Remove a channel. sub removeChannel { - my $url = shift; - my @left = (); + my ($name) = @_; readChannels; - foreach my $url2 (@channels) { - push @left, $url2 if $url ne $url2; - } - @channels = @left; + delete $channels{$name}; writeChannels; + + system("$Nix::Config::binDir/nix-env --profile '$profile' -e '$name'") == 0 + or die "cannot remove channel `$name'"; } -# Fetch Nix expressions and pull cache manifests from the subscribed +# Fetch Nix expressions and pull manifests from the subscribed # channels. sub update { readChannels; @@ -82,64 +84,46 @@ sub update { # Do we have write permission to the manifests directory? die "$0: you do not have write permission to `$manifestDir'!\n" unless -W $manifestDir; - # Pull cache manifests. - foreach my $url (@channels) { - #print "pulling cache manifest from `$url'\n"; + # Download each channel. + my $exprs = ""; + foreach my $name (keys %channels) { + my $url = $channels{$name}; + + # Pull the channel manifest. system("$Nix::Config::binDir/nix-pull", "--skip-wrong-store", "$url/MANIFEST") == 0 - or die "cannot pull cache manifest from `$url'"; - } - - # Create a Nix expression that fetches and unpacks the channel Nix - # expressions. - - my $inputs = "["; - foreach my $url (@channels) { - $url =~ /\/([^\/]+)\/?$/; - my $channelName = $1; - $channelName = "unnamed" unless defined $channelName; + or die "cannot pull manifest from `$url'\n"; + # Download the channel tarball. my $fullURL = "$url/nixexprs.tar.bz2"; - print "downloading Nix expressions from `$fullURL'...\n"; - $ENV{"PRINT_PATH"} = 1; - $ENV{"QUIET"} = 1; - my ($hash, $path) = `$Nix::Config::binDir/nix-prefetch-url '$fullURL'`; + print STDERR "downloading Nix expressions from `$fullURL'...\n"; + my ($hash, $path) = `PRINT_PATH=1 QUIET=1 $Nix::Config::binDir/nix-prefetch-url '$fullURL'`; die "cannot fetch `$fullURL'" if $? != 0; chomp $path; - $inputs .= '"' . $channelName . '"' . " " . $path . " "; + + $exprs .= "'f: f { name = \"$name\"; src = builtins.storePath \"$path\"; }' "; } - $inputs .= "]"; - # Figure out a name for the GC root. - my $userName = getpwuid($<); - die "who ARE you? go away" unless defined $userName; - - my $rootFile = "$Nix::Config::stateDir/gcroots/per-user/$userName/channels"; - - # Build the Nix expression. - print "unpacking channel Nix expressions...\n"; - my $outPath = `\\ - $Nix::Config::binDir/nix-build --out-link '$rootFile' --drv-link '$rootFile'.tmp \\ - '' \\ - --argstr system @system@ --arg inputs '$inputs'` - or die "cannot unpack the channels"; - chomp $outPath; - - unlink "$rootFile.tmp"; + # Unpack the channel tarballs into the Nix store and install them + # into the channels profile. + print STDERR "unpacking channels...\n"; + system("$Nix::Config::binDir/nix-env --profile '$profile' " . + "-f '' -i -E $exprs --quiet") == 0 + or die "cannot unpack the channels"; # Make the channels appear in nix-env. unlink $nixDefExpr if -l $nixDefExpr; # old-skool ~/.nix-defexpr mkdir $nixDefExpr or die "cannot create directory `$nixDefExpr'" if !-e $nixDefExpr; my $channelLink = "$nixDefExpr/channels"; unlink $channelLink; # !!! not atomic - symlink($outPath, $channelLink) or die "cannot symlink `$channelLink' to `$outPath'"; + symlink($profile, $channelLink) or die "cannot symlink `$channelLink' to `$profile'"; } sub usageError { print STDERR < 2; + my $url = shift @ARGV; + my $name = shift @ARGV; + unless (defined $name) { + $name = basename $url; + $name =~ s/-unstable//; + $name =~ s/-stable//; + } + addChannel($url, $name); last; } if ($arg eq "--remove") { usageError if scalar @ARGV != 1; - removeChannel (shift @ARGV); + removeChannel(shift @ARGV); last; } if ($arg eq "--list") { usageError if scalar @ARGV != 0; readChannels; - foreach my $url (@channels) { - print "$url\n"; + foreach my $name (keys %channels) { + print "$name $channels{$name}\n"; } last; } diff --git a/tests/nix-channel.sh b/tests/nix-channel.sh index 4819b57c91..eb1d572953 100644 --- a/tests/nix-channel.sh +++ b/tests/nix-channel.sh @@ -9,9 +9,9 @@ rm -f $TEST_ROOT/.nix-channels export HOME=$TEST_ROOT # Test add/list/remove. -nix-channel --add http://foo/bar +nix-channel --add http://foo/bar xyzzy nix-channel --list | grep -q http://foo/bar -nix-channel --remove http://foo/bar +nix-channel --remove xyzzy [ -e $TEST_ROOT/.nix-channels ] [ "$(cat $TEST_ROOT/.nix-channels)" = '' ]