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).
This commit is contained in:
parent
969a14599d
commit
e855c7e2c9
|
@ -1,11 +1,11 @@
|
||||||
with import <nix/config.nix>;
|
with import <nix/config.nix>;
|
||||||
|
|
||||||
{ system, inputs }:
|
{ name, src }:
|
||||||
|
|
||||||
derivation {
|
derivation {
|
||||||
name = "channels";
|
system = builtins.currentSystem;
|
||||||
builder = shell;
|
builder = shell;
|
||||||
args = [ "-e" ./unpack-channel.sh ];
|
args = [ "-e" ./unpack-channel.sh ];
|
||||||
inherit system inputs bzip2 tar tr;
|
inherit name src bzip2 tar tr;
|
||||||
PATH = "${nixBinDir}:${coreutils}";
|
PATH = "${nixBinDir}:${coreutils}";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,4 @@
|
||||||
mkdir $out
|
mkdir $out
|
||||||
mkdir $out/tmp
|
cd $out
|
||||||
cd $out/tmp
|
$bzip2 -d < $src | $tar xf -
|
||||||
|
mv * $out/$name
|
||||||
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
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<cmdsynopsis>
|
<cmdsynopsis>
|
||||||
<command>nix-channel</command>
|
<command>nix-channel</command>
|
||||||
<group choice='req'>
|
<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>--remove</option> <replaceable>url</replaceable></arg>
|
||||||
<arg choice='plain'><option>--list</option></arg>
|
<arg choice='plain'><option>--list</option></arg>
|
||||||
<arg choice='plain'><option>--update</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
|
<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
|
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
|
just a URL that points to a place containing a set of Nix expressions
|
||||||
expressions, as well as a <command>nix-push</command> manifest. See
|
and a <command>nix-push</command> manifest. <phrase
|
||||||
also <xref linkend="sec-channels" />.</para>
|
condition="manual">See also <xref linkend="sec-channels"
|
||||||
|
/>.</phrase></para>
|
||||||
|
|
||||||
<para>This command has the following operations:
|
<para>This command has the following operations:
|
||||||
|
|
||||||
<variablelist>
|
<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
|
<listitem><para>Adds a channel named
|
||||||
subscribed channels.</para></listitem>
|
<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>
|
||||||
|
|
||||||
<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
|
<listitem><para>Removes the channel named
|
||||||
list of subscribed channels.</para></listitem>
|
<replaceable>name</replaceable> from the list of subscribed
|
||||||
|
channels.</para></listitem>
|
||||||
|
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry><term><option>--list</option></term>
|
<varlistentry><term><option>--list</option></term>
|
||||||
|
|
||||||
<listitem><para>Prints the URLs of all subscribed channels on
|
<listitem><para>Prints the names and URLs of all subscribed
|
||||||
standard output.</para></listitem>
|
channels on standard output.</para></listitem>
|
||||||
|
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
@ -64,7 +71,7 @@ also <xref linkend="sec-channels" />.</para>
|
||||||
|
|
||||||
<listitem><para>Downloads the Nix expressions of all subscribed
|
<listitem><para>Downloads the Nix expressions of all subscribed
|
||||||
channels, makes them the default for <command>nix-env</command>
|
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
|
<filename>~/.nix-defexpr</filename>), and performs a
|
||||||
<command>nix-pull</command> on the manifests of all channels to
|
<command>nix-pull</command> on the manifests of all channels to
|
||||||
make pre-built binaries available.</para></listitem>
|
make pre-built binaries available.</para></listitem>
|
||||||
|
@ -75,8 +82,8 @@ also <xref linkend="sec-channels" />.</para>
|
||||||
|
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>Note that <option>--add</option> and <option>--remove</option>
|
<para>Note that <option>--add</option> does not automatically perform
|
||||||
do not automatically perform an update.</para>
|
an update.</para>
|
||||||
|
|
||||||
<para>The list of subscribed channels is stored in
|
<para>The list of subscribed channels is stored in
|
||||||
<filename>~/.nix-channels</filename>.</para>
|
<filename>~/.nix-channels</filename>.</para>
|
||||||
|
@ -90,4 +97,15 @@ respectively.</para>
|
||||||
|
|
||||||
</refsection>
|
</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>
|
</refentry>
|
||||||
|
|
|
@ -58,8 +58,9 @@ options.</phrase></para>
|
||||||
|
|
||||||
<listitem><para>Causes the result of a realisation
|
<listitem><para>Causes the result of a realisation
|
||||||
(<option>--realise</option> and <option>--force-realise</option>)
|
(<option>--realise</option> and <option>--force-realise</option>)
|
||||||
to be registered as a root of the garbage collector (see <xref
|
to be registered as a root of the garbage collector<phrase
|
||||||
linkend="ssec-gc-roots" />). The root is stored in
|
condition="manual"> (see <xref linkend="ssec-gc-roots"
|
||||||
|
/>)</phrase>. The root is stored in
|
||||||
<replaceable>path</replaceable>, which must be inside a directory
|
<replaceable>path</replaceable>, which must be inside a directory
|
||||||
that is scanned for roots by the garbage collector (i.e.,
|
that is scanned for roots by the garbage collector (i.e.,
|
||||||
typically in a subdirectory of
|
typically in a subdirectory of
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#! @perl@ -w @perlFlags@
|
#! @perl@ -w @perlFlags@
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
|
use File::Basename;
|
||||||
|
use File::Path qw(make_path);
|
||||||
use Nix::Config;
|
use Nix::Config;
|
||||||
|
|
||||||
my $manifestDir = $Nix::Config::manifestDir;
|
my $manifestDir = $Nix::Config::manifestDir;
|
||||||
|
@ -11,67 +13,67 @@ my $channelCache = "$Nix::Config::stateDir/channel-cache";
|
||||||
mkdir $channelCache, 0755 unless -e $channelCache;
|
mkdir $channelCache, 0755 unless -e $channelCache;
|
||||||
$ENV{'NIX_DOWNLOAD_CACHE'} = $channelCache if -W $channelCache;
|
$ENV{'NIX_DOWNLOAD_CACHE'} = $channelCache if -W $channelCache;
|
||||||
|
|
||||||
|
|
||||||
# Figure out the name of the `.nix-channels' file to use.
|
# Figure out the name of the `.nix-channels' file to use.
|
||||||
my $home = $ENV{"HOME"};
|
my $home = $ENV{"HOME"} or die '$HOME not set\n';
|
||||||
die '$HOME not set' unless defined $home;
|
|
||||||
my $channelsList = "$home/.nix-channels";
|
my $channelsList = "$home/.nix-channels";
|
||||||
|
|
||||||
my $nixDefExpr = "$home/.nix-defexpr";
|
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 {
|
sub readChannels {
|
||||||
return if (!-f $channelsList);
|
return if (!-f $channelsList);
|
||||||
open CHANNELS, "<$channelsList" or die "cannot open `$channelsList': $!";
|
open CHANNELS, "<$channelsList" or die "cannot open `$channelsList': $!";
|
||||||
while (<CHANNELS>) {
|
while (<CHANNELS>) {
|
||||||
chomp;
|
chomp;
|
||||||
next if /^\s*\#/;
|
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;
|
close CHANNELS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Writes the list of channels to the file $channelsList;
|
# Writes the list of channels.
|
||||||
sub writeChannels {
|
sub writeChannels {
|
||||||
open CHANNELS, ">$channelsList" or die "cannot open `$channelsList': $!";
|
open CHANNELS, ">$channelsList" or die "cannot open `$channelsList': $!";
|
||||||
foreach my $url (@channels) {
|
foreach my $name (keys %channels) {
|
||||||
print CHANNELS "$url\n";
|
print CHANNELS "$channels{$name} $name\n";
|
||||||
}
|
}
|
||||||
close CHANNELS;
|
close CHANNELS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Adds a channel to the file $channelsList;
|
# Adds a channel.
|
||||||
sub addChannel {
|
sub addChannel {
|
||||||
my $url = shift;
|
my ($url, $name) = @_;
|
||||||
readChannels;
|
readChannels;
|
||||||
foreach my $url2 (@channels) {
|
$channels{$name} = $url;
|
||||||
return if $url eq $url2;
|
|
||||||
}
|
|
||||||
push @channels, $url;
|
|
||||||
writeChannels;
|
writeChannels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Remove a channel from the file $channelsList;
|
# Remove a channel.
|
||||||
sub removeChannel {
|
sub removeChannel {
|
||||||
my $url = shift;
|
my ($name) = @_;
|
||||||
my @left = ();
|
|
||||||
readChannels;
|
readChannels;
|
||||||
foreach my $url2 (@channels) {
|
delete $channels{$name};
|
||||||
push @left, $url2 if $url ne $url2;
|
|
||||||
}
|
|
||||||
@channels = @left;
|
|
||||||
writeChannels;
|
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.
|
# channels.
|
||||||
sub update {
|
sub update {
|
||||||
readChannels;
|
readChannels;
|
||||||
|
@ -82,64 +84,46 @@ sub update {
|
||||||
# Do we have write permission to the manifests directory?
|
# Do we have write permission to the manifests directory?
|
||||||
die "$0: you do not have write permission to `$manifestDir'!\n" unless -W $manifestDir;
|
die "$0: you do not have write permission to `$manifestDir'!\n" unless -W $manifestDir;
|
||||||
|
|
||||||
# Pull cache manifests.
|
# Download each channel.
|
||||||
foreach my $url (@channels) {
|
my $exprs = "";
|
||||||
#print "pulling cache manifest from `$url'\n";
|
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
|
system("$Nix::Config::binDir/nix-pull", "--skip-wrong-store", "$url/MANIFEST") == 0
|
||||||
or die "cannot pull cache manifest from `$url'";
|
or die "cannot pull manifest from `$url'\n";
|
||||||
}
|
|
||||||
|
|
||||||
# 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;
|
|
||||||
|
|
||||||
|
# Download the channel tarball.
|
||||||
my $fullURL = "$url/nixexprs.tar.bz2";
|
my $fullURL = "$url/nixexprs.tar.bz2";
|
||||||
print "downloading Nix expressions from `$fullURL'...\n";
|
print STDERR "downloading Nix expressions from `$fullURL'...\n";
|
||||||
$ENV{"PRINT_PATH"} = 1;
|
my ($hash, $path) = `PRINT_PATH=1 QUIET=1 $Nix::Config::binDir/nix-prefetch-url '$fullURL'`;
|
||||||
$ENV{"QUIET"} = 1;
|
|
||||||
my ($hash, $path) = `$Nix::Config::binDir/nix-prefetch-url '$fullURL'`;
|
|
||||||
die "cannot fetch `$fullURL'" if $? != 0;
|
die "cannot fetch `$fullURL'" if $? != 0;
|
||||||
chomp $path;
|
chomp $path;
|
||||||
$inputs .= '"' . $channelName . '"' . " " . $path . " ";
|
|
||||||
|
$exprs .= "'f: f { name = \"$name\"; src = builtins.storePath \"$path\"; }' ";
|
||||||
}
|
}
|
||||||
$inputs .= "]";
|
|
||||||
|
|
||||||
# Figure out a name for the GC root.
|
# Unpack the channel tarballs into the Nix store and install them
|
||||||
my $userName = getpwuid($<);
|
# into the channels profile.
|
||||||
die "who ARE you? go away" unless defined $userName;
|
print STDERR "unpacking channels...\n";
|
||||||
|
system("$Nix::Config::binDir/nix-env --profile '$profile' " .
|
||||||
my $rootFile = "$Nix::Config::stateDir/gcroots/per-user/$userName/channels";
|
"-f '<nix/unpack-channel.nix>' -i -E $exprs --quiet") == 0
|
||||||
|
or die "cannot unpack the 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;
|
|
||||||
|
|
||||||
unlink "$rootFile.tmp";
|
|
||||||
|
|
||||||
# Make the channels appear in nix-env.
|
# Make the channels appear in nix-env.
|
||||||
unlink $nixDefExpr if -l $nixDefExpr; # old-skool ~/.nix-defexpr
|
unlink $nixDefExpr if -l $nixDefExpr; # old-skool ~/.nix-defexpr
|
||||||
mkdir $nixDefExpr or die "cannot create directory `$nixDefExpr'" if !-e $nixDefExpr;
|
mkdir $nixDefExpr or die "cannot create directory `$nixDefExpr'" if !-e $nixDefExpr;
|
||||||
my $channelLink = "$nixDefExpr/channels";
|
my $channelLink = "$nixDefExpr/channels";
|
||||||
unlink $channelLink; # !!! not atomic
|
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 {
|
sub usageError {
|
||||||
print STDERR <<EOF;
|
print STDERR <<EOF;
|
||||||
Usage:
|
Usage:
|
||||||
nix-channel --add URL
|
nix-channel --add URL [CHANNEL-NAME]
|
||||||
nix-channel --remove URL
|
nix-channel --remove CHANNEL-NAME
|
||||||
nix-channel --list
|
nix-channel --list
|
||||||
nix-channel --update
|
nix-channel --update
|
||||||
EOF
|
EOF
|
||||||
|
@ -154,22 +138,29 @@ while (scalar @ARGV) {
|
||||||
my $arg = shift @ARGV;
|
my $arg = shift @ARGV;
|
||||||
|
|
||||||
if ($arg eq "--add") {
|
if ($arg eq "--add") {
|
||||||
usageError if scalar @ARGV != 1;
|
usageError if scalar @ARGV < 1 || scalar @ARGV > 2;
|
||||||
addChannel (shift @ARGV);
|
my $url = shift @ARGV;
|
||||||
|
my $name = shift @ARGV;
|
||||||
|
unless (defined $name) {
|
||||||
|
$name = basename $url;
|
||||||
|
$name =~ s/-unstable//;
|
||||||
|
$name =~ s/-stable//;
|
||||||
|
}
|
||||||
|
addChannel($url, $name);
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($arg eq "--remove") {
|
if ($arg eq "--remove") {
|
||||||
usageError if scalar @ARGV != 1;
|
usageError if scalar @ARGV != 1;
|
||||||
removeChannel (shift @ARGV);
|
removeChannel(shift @ARGV);
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($arg eq "--list") {
|
if ($arg eq "--list") {
|
||||||
usageError if scalar @ARGV != 0;
|
usageError if scalar @ARGV != 0;
|
||||||
readChannels;
|
readChannels;
|
||||||
foreach my $url (@channels) {
|
foreach my $name (keys %channels) {
|
||||||
print "$url\n";
|
print "$name $channels{$name}\n";
|
||||||
}
|
}
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,9 @@ rm -f $TEST_ROOT/.nix-channels
|
||||||
export HOME=$TEST_ROOT
|
export HOME=$TEST_ROOT
|
||||||
|
|
||||||
# Test add/list/remove.
|
# 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 --list | grep -q http://foo/bar
|
||||||
nix-channel --remove http://foo/bar
|
nix-channel --remove xyzzy
|
||||||
|
|
||||||
[ -e $TEST_ROOT/.nix-channels ]
|
[ -e $TEST_ROOT/.nix-channels ]
|
||||||
[ "$(cat $TEST_ROOT/.nix-channels)" = '' ]
|
[ "$(cat $TEST_ROOT/.nix-channels)" = '' ]
|
||||||
|
|
Loading…
Reference in a new issue