about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2012-04-14T16·38+0200
committerEelco Dolstra <eelco.dolstra@logicblox.com>2012-04-14T16·38+0200
commite855c7e2c9a9a5cbe4406c1f9351181a9ebe6283 (patch)
tree1de5115d72e5ec55080a831297058dd0eb49b3be
parent969a14599d2f7bfd02971475b5b2be49fb965117 (diff)
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).
-rw-r--r--corepkgs/unpack-channel.nix6
-rw-r--r--corepkgs/unpack-channel.sh32
-rw-r--r--doc/manual/nix-channel.xml48
-rw-r--r--doc/manual/nix-store.xml5
-rwxr-xr-xscripts/nix-channel.in129
-rw-r--r--tests/nix-channel.sh4
6 files changed, 104 insertions, 120 deletions
diff --git a/corepkgs/unpack-channel.nix b/corepkgs/unpack-channel.nix
index 5e6ccf23fd47..eba957dff4e7 100644
--- a/corepkgs/unpack-channel.nix
+++ b/corepkgs/unpack-channel.nix
@@ -1,11 +1,11 @@
 with import <nix/config.nix>;
 
-{ 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 7c244a6a8552..0b7d89bc4630 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 024add8601e8..22d8900d8f1c 100644
--- a/doc/manual/nix-channel.xml
+++ b/doc/manual/nix-channel.xml
@@ -19,7 +19,7 @@
   <cmdsynopsis>
     <command>nix-channel</command>
     <group choice='req'>
-      <arg choice='plain'><option>--add</option> <replaceable>url</replaceable></arg>
+      <arg choice='plain'><option>--add</option> <replaceable>url</replaceable> <arg choice='opt'><replaceable>name</replaceable></arg></arg>
       <arg choice='plain'><option>--remove</option> <replaceable>url</replaceable></arg>
       <arg choice='plain'><option>--list</option></arg>
       <arg choice='plain'><option>--update</option></arg>
@@ -31,32 +31,39 @@
 
 <para>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 <command>nix-push</command> manifest.  See
-also <xref linkend="sec-channels" />.</para>
+just a URL that points to a place containing a set of Nix expressions
+and a <command>nix-push</command> manifest.  <phrase
+condition="manual">See also <xref linkend="sec-channels"
+/>.</phrase></para>
 
 <para>This command has the following operations:
 
 <variablelist>
 
-  <varlistentry><term><option>--add</option> <replaceable>url</replaceable></term>
+  <varlistentry><term><option>--add</option> <replaceable>url</replaceable> [<replaceable>name</replaceable>]</term>
 
-    <listitem><para>Adds <replaceable>url</replaceable> to the list of
-    subscribed channels.</para></listitem>
+    <listitem><para>Adds a channel named
+    <replaceable>name</replaceable> with URL
+    <replaceable>url</replaceable> to the list of subscribed channels.
+    If <replaceable>name</replaceable> is omitted, it defaults to the
+    last component of <replaceable>url</replaceable>, with the
+    suffixes <literal>-stable</literal> or
+    <literal>-unstable</literal> removed.</para></listitem>
 
   </varlistentry>
 
-  <varlistentry><term><option>--remove</option> <replaceable>url</replaceable></term>
+  <varlistentry><term><option>--remove</option> <replaceable>name</replaceable></term>
 
-    <listitem><para>Removes <replaceable>url</replaceable> from the
-    list of subscribed channels.</para></listitem>
+    <listitem><para>Removes the channel named
+    <replaceable>name</replaceable> from the list of subscribed
+    channels.</para></listitem>
 
   </varlistentry>
 
   <varlistentry><term><option>--list</option></term>
 
-    <listitem><para>Prints the URLs of all subscribed channels on
-    standard output.</para></listitem>
+    <listitem><para>Prints the names and URLs of all subscribed
+    channels on standard output.</para></listitem>
 
   </varlistentry>
 
@@ -64,7 +71,7 @@ also <xref linkend="sec-channels" />.</para>
 
     <listitem><para>Downloads the Nix expressions of all subscribed
     channels, makes them the default for <command>nix-env</command>
-    operations (by symlinking them in the directory
+    operations (by symlinking them from the directory
     <filename>~/.nix-defexpr</filename>), and performs a
     <command>nix-pull</command> on the manifests of all channels to
     make pre-built binaries available.</para></listitem>
@@ -75,8 +82,8 @@ also <xref linkend="sec-channels" />.</para>
 
 </para>
 
-<para>Note that <option>--add</option> and <option>--remove</option>
-do not automatically perform an update.</para>
+<para>Note that <option>--add</option> does not automatically perform
+an update.</para>
 
 <para>The list of subscribed channels is stored in
 <filename>~/.nix-channels</filename>.</para>
@@ -90,4 +97,15 @@ respectively.</para>
 
 </refsection>
 
+<refsection><title>Examples</title>
+
+<para>To subscribe to the Nixpkgs channel and install the GNU Hello package:</para>
+
+<screen>
+$ nix-channel --add http://nixos.org/releases/nixpkgs/channels/nixpkgs-unstable
+$ nix-channel --update
+$ nix-env -iA nixpkgs.hello</screen>
+
+</refsection>
+
 </refentry>
diff --git a/doc/manual/nix-store.xml b/doc/manual/nix-store.xml
index 6a4ca3f717ff..6cc765bf27ca 100644
--- a/doc/manual/nix-store.xml
+++ b/doc/manual/nix-store.xml
@@ -58,8 +58,9 @@ options.</phrase></para>
 
     <listitem><para>Causes the result of a realisation
     (<option>--realise</option> and <option>--force-realise</option>)
-    to be registered as a root of the garbage collector (see <xref
-    linkend="ssec-gc-roots" />).  The root is stored in
+    to be registered as a root of the garbage collector<phrase
+    condition="manual"> (see <xref linkend="ssec-gc-roots"
+    />)</phrase>.  The root is stored in
     <replaceable>path</replaceable>, 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 ebfc246cfa30..283071a9c8b1 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";
-    
 
-my @channels;
+# 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;
 
 
-# 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 (<CHANNELS>) {
         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 . " ";
-    }
-    $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 \\
-        '<nix/unpack-channel.nix>' \\
-        --argstr system @system@ --arg inputs '$inputs'`
-        or die "cannot unpack the channels";
-    chomp $outPath;
+        $exprs .= "'f: f { name = \"$name\"; src = builtins.storePath \"$path\"; }' ";
+    }
 
-    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 '<nix/unpack-channel.nix>' -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 <<EOF;
 Usage:
-  nix-channel --add URL
-  nix-channel --remove URL
+  nix-channel --add URL [CHANNEL-NAME]
+  nix-channel --remove CHANNEL-NAME
   nix-channel --list
   nix-channel --update
 EOF
@@ -154,22 +138,29 @@ while (scalar @ARGV) {
     my $arg = shift @ARGV;
 
     if ($arg eq "--add") {
-        usageError if scalar @ARGV != 1;
-        addChannel (shift @ARGV);
+        usageError if scalar @ARGV < 1 || scalar @ARGV > 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 4819b57c91c0..eb1d572953d7 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)" = '' ]