2012-01-05 21:07:12 +00:00
|
|
|
|
#! @perl@ -w @perlFlags@
|
2004-04-21 14:54:05 +00:00
|
|
|
|
|
2014-08-29 15:48:25 +00:00
|
|
|
|
use utf8;
|
2004-04-21 14:54:05 +00:00
|
|
|
|
use strict;
|
2012-04-14 16:38:52 +00:00
|
|
|
|
use File::Basename;
|
2012-05-01 20:35:18 +00:00
|
|
|
|
use File::Path qw(mkpath);
|
2012-01-03 01:51:38 +00:00
|
|
|
|
use Nix::Config;
|
2012-09-13 15:35:46 +00:00
|
|
|
|
use Nix::Manifest;
|
2015-04-20 04:34:29 +00:00
|
|
|
|
use File::Temp qw(tempdir);
|
2004-04-21 14:54:05 +00:00
|
|
|
|
|
2014-08-29 15:48:25 +00:00
|
|
|
|
binmode STDERR, ":encoding(utf8)";
|
|
|
|
|
|
2012-09-13 16:11:40 +00:00
|
|
|
|
Nix::Config::readConfig;
|
|
|
|
|
|
2012-01-03 01:51:38 +00:00
|
|
|
|
my $manifestDir = $Nix::Config::manifestDir;
|
2006-05-08 20:00:28 +00:00
|
|
|
|
|
2004-04-21 14:54:05 +00:00
|
|
|
|
|
2007-08-10 00:28:44 +00:00
|
|
|
|
# Turn on caching in nix-prefetch-url.
|
2012-01-03 01:51:38 +00:00
|
|
|
|
my $channelCache = "$Nix::Config::stateDir/channel-cache";
|
2007-08-10 00:28:44 +00:00
|
|
|
|
mkdir $channelCache, 0755 unless -e $channelCache;
|
2007-08-22 14:52:22 +00:00
|
|
|
|
$ENV{'NIX_DOWNLOAD_CACHE'} = $channelCache if -W $channelCache;
|
2007-08-10 00:28:44 +00:00
|
|
|
|
|
2004-04-21 14:54:05 +00:00
|
|
|
|
# Figure out the name of the `.nix-channels' file to use.
|
2012-04-14 16:38:52 +00:00
|
|
|
|
my $home = $ENV{"HOME"} or die '$HOME not set\n';
|
2004-04-21 14:54:05 +00:00
|
|
|
|
my $channelsList = "$home/.nix-channels";
|
2007-09-17 16:08:24 +00:00
|
|
|
|
my $nixDefExpr = "$home/.nix-defexpr";
|
2004-04-21 14:54:05 +00:00
|
|
|
|
|
2012-04-14 16:38:52 +00:00
|
|
|
|
# Figure out the name of the channels profile.
|
2014-02-26 12:48:23 +00:00
|
|
|
|
my $userName = getpwuid($<) || $ENV{"USER"} or die "cannot figure out user name";
|
2012-04-14 16:38:52 +00:00
|
|
|
|
my $profile = "$Nix::Config::stateDir/profiles/per-user/$userName/channels";
|
2012-05-01 20:35:18 +00:00
|
|
|
|
mkpath(dirname $profile, 0, 0755);
|
2012-08-01 21:56:11 +00:00
|
|
|
|
|
2012-04-14 16:38:52 +00:00
|
|
|
|
my %channels;
|
2004-04-21 14:54:05 +00:00
|
|
|
|
|
|
|
|
|
|
2012-04-14 16:38:52 +00:00
|
|
|
|
# Reads the list of channels.
|
2004-04-21 14:54:05 +00:00
|
|
|
|
sub readChannels {
|
|
|
|
|
return if (!-f $channelsList);
|
2014-08-20 15:00:17 +00:00
|
|
|
|
open CHANNELS, "<$channelsList" or die "cannot open ‘$channelsList’: $!";
|
2004-04-21 14:54:05 +00:00
|
|
|
|
while (<CHANNELS>) {
|
|
|
|
|
chomp;
|
2006-09-25 11:11:16 +00:00
|
|
|
|
next if /^\s*\#/;
|
2012-04-14 16:38:52 +00:00
|
|
|
|
my ($url, $name) = split ' ', $_;
|
|
|
|
|
$url =~ s/\/*$//; # remove trailing slashes
|
|
|
|
|
$name = basename $url unless defined $name;
|
|
|
|
|
$channels{$name} = $url;
|
2004-04-21 14:54:05 +00:00
|
|
|
|
}
|
|
|
|
|
close CHANNELS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-04-14 16:38:52 +00:00
|
|
|
|
# Writes the list of channels.
|
2004-04-21 14:54:05 +00:00
|
|
|
|
sub writeChannels {
|
2014-08-20 15:00:17 +00:00
|
|
|
|
open CHANNELS, ">$channelsList" or die "cannot open ‘$channelsList’: $!";
|
2012-04-14 16:38:52 +00:00
|
|
|
|
foreach my $name (keys %channels) {
|
|
|
|
|
print CHANNELS "$channels{$name} $name\n";
|
2004-04-21 14:54:05 +00:00
|
|
|
|
}
|
|
|
|
|
close CHANNELS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-04-14 16:38:52 +00:00
|
|
|
|
# Adds a channel.
|
2004-04-21 14:54:05 +00:00
|
|
|
|
sub addChannel {
|
2012-04-14 16:38:52 +00:00
|
|
|
|
my ($url, $name) = @_;
|
2014-10-14 09:28:13 +00:00
|
|
|
|
die "invalid channel URL ‘$url’" unless $url =~ /^(file|http|https):\/\//;
|
|
|
|
|
die "invalid channel identifier ‘$name’" unless $name =~ /^[a-zA-Z0-9_][a-zA-Z0-9_\-\.]*$/;
|
2004-04-21 14:54:05 +00:00
|
|
|
|
readChannels;
|
2012-04-14 16:38:52 +00:00
|
|
|
|
$channels{$name} = $url;
|
2004-04-21 14:54:05 +00:00
|
|
|
|
writeChannels;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-04-14 16:38:52 +00:00
|
|
|
|
# Remove a channel.
|
2005-02-17 10:06:12 +00:00
|
|
|
|
sub removeChannel {
|
2012-04-14 16:38:52 +00:00
|
|
|
|
my ($name) = @_;
|
2005-02-17 10:06:12 +00:00
|
|
|
|
readChannels;
|
2012-09-13 15:35:46 +00:00
|
|
|
|
my $url = $channels{$name};
|
|
|
|
|
deleteOldManifests($url . "/MANIFEST", undef) if defined $url;
|
2012-04-14 16:38:52 +00:00
|
|
|
|
delete $channels{$name};
|
2005-02-17 10:06:12 +00:00
|
|
|
|
writeChannels;
|
2012-04-14 16:38:52 +00:00
|
|
|
|
|
|
|
|
|
system("$Nix::Config::binDir/nix-env --profile '$profile' -e '$name'") == 0
|
2014-08-20 15:00:17 +00:00
|
|
|
|
or die "cannot remove channel ‘$name’\n";
|
2005-02-17 10:06:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-04-14 16:38:52 +00:00
|
|
|
|
# Fetch Nix expressions and pull manifests from the subscribed
|
2004-04-21 14:54:05 +00:00
|
|
|
|
# channels.
|
|
|
|
|
sub update {
|
2012-05-07 21:55:56 +00:00
|
|
|
|
my @channelNames = @_;
|
2009-11-13 10:08:31 +00:00
|
|
|
|
|
2012-08-01 21:56:11 +00:00
|
|
|
|
readChannels;
|
2006-05-08 20:00:28 +00:00
|
|
|
|
|
2012-04-14 16:38:52 +00:00
|
|
|
|
# Download each channel.
|
|
|
|
|
my $exprs = "";
|
|
|
|
|
foreach my $name (keys %channels) {
|
2012-05-07 21:55:56 +00:00
|
|
|
|
next if scalar @channelNames > 0 && ! grep { $_ eq $name } @{channelNames};
|
2012-08-01 21:56:11 +00:00
|
|
|
|
|
2012-04-14 16:38:52 +00:00
|
|
|
|
my $url = $channels{$name};
|
2012-04-18 11:40:18 +00:00
|
|
|
|
my $origUrl = "$url/MANIFEST";
|
2012-04-14 19:04:22 +00:00
|
|
|
|
|
2015-04-20 04:34:29 +00:00
|
|
|
|
# We want to download the url to a file to see if it's a tarball while also checking if we
|
|
|
|
|
# got redirected in the process, so that we can grab the various parts of a nix channel
|
|
|
|
|
# definition from a consistent location if the redirect changes mid-download.
|
|
|
|
|
my $tmpdir = tempdir( CLEANUP => 1 );
|
|
|
|
|
my $filename;
|
|
|
|
|
($url, $filename) = `cd $tmpdir && $Nix::Config::curl --silent --write-out '%{url_effective}\n%{filename_effective}' -L '$url' -O`;
|
|
|
|
|
chomp $url;
|
2015-08-07 03:32:17 +00:00
|
|
|
|
die "$0: unable to check ‘$url’\n" if $? != 0;
|
2004-10-20 14:42:38 +00:00
|
|
|
|
|
2012-04-14 19:05:28 +00:00
|
|
|
|
# If the URL contains a version number, append it to the name
|
|
|
|
|
# attribute (so that "nix-env -q" on the channels profile
|
|
|
|
|
# shows something useful).
|
|
|
|
|
my $cname = $name;
|
|
|
|
|
$cname .= $1 if basename($url) =~ /(-\d.*)$/;
|
|
|
|
|
|
2015-04-20 04:34:29 +00:00
|
|
|
|
my $path;
|
|
|
|
|
my $ret = -1;
|
2015-06-11 23:56:34 +00:00
|
|
|
|
if (-e "$tmpdir/$filename" && $filename =~ /\.tar\.(gz|bz2|xz)$/) {
|
|
|
|
|
# Get our temporary download into the store.
|
2015-04-20 04:34:29 +00:00
|
|
|
|
(my $hash, $path) = `PRINT_PATH=1 QUIET=1 $Nix::Config::binDir/nix-prefetch-url 'file://$tmpdir/$filename'`;
|
|
|
|
|
chomp $path;
|
|
|
|
|
|
|
|
|
|
# Try unpacking the expressions to see if they'll be valid for us to process later.
|
2015-06-11 23:56:34 +00:00
|
|
|
|
# Like anything in nix, this will cache the result so we don't do it again outside of the loop below.
|
2015-04-20 04:34:29 +00:00
|
|
|
|
$ret = system("$Nix::Config::binDir/nix-build --no-out-link -E 'import <nix/unpack-channel.nix> " .
|
|
|
|
|
"{ name = \"$cname\"; channelName = \"$name\"; src = builtins.storePath \"$path\"; }'");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# The URL doesn't unpack directly, so let's try treating it like a full channel folder with files in it
|
|
|
|
|
my $extraAttrs = "";
|
|
|
|
|
if ($ret != 0) {
|
|
|
|
|
# Check if the channel advertises a binary cache.
|
|
|
|
|
my $binaryCacheURL = `$Nix::Config::curl --silent '$url'/binary-cache-url`;
|
|
|
|
|
my $getManifest = ($Nix::Config::config{"force-manifest"} // "false") eq "true";
|
|
|
|
|
if ($? == 0 && $binaryCacheURL ne "") {
|
|
|
|
|
$extraAttrs .= "binaryCacheURL = \"$binaryCacheURL\"; ";
|
|
|
|
|
deleteOldManifests($origUrl, undef);
|
|
|
|
|
} else {
|
|
|
|
|
$getManifest = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($getManifest) {
|
|
|
|
|
# No binary cache, so pull the channel manifest.
|
|
|
|
|
mkdir $manifestDir, 0755 unless -e $manifestDir;
|
|
|
|
|
die "$0: you do not have write permission to ‘$manifestDir’!\n" unless -W $manifestDir;
|
|
|
|
|
$ENV{'NIX_ORIG_URL'} = $origUrl;
|
|
|
|
|
system("$Nix::Config::binDir/nix-pull", "--skip-wrong-store", "$url/MANIFEST") == 0
|
|
|
|
|
or die "cannot pull manifest from ‘$url’\n";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Download the channel tarball.
|
|
|
|
|
my $fullURL = "$url/nixexprs.tar.xz";
|
|
|
|
|
system("$Nix::Config::curl --fail --silent --head '$fullURL' > /dev/null") == 0 or
|
|
|
|
|
$fullURL = "$url/nixexprs.tar.bz2";
|
|
|
|
|
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’\n" if $? != 0;
|
|
|
|
|
chomp $path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Regardless of where it came from, add the expression representing this channel to accumulated expression
|
2012-08-01 21:56:11 +00:00
|
|
|
|
$exprs .= "'f: f { name = \"$cname\"; channelName = \"$name\"; src = builtins.storePath \"$path\"; $extraAttrs }' ";
|
2012-04-14 16:38:52 +00:00
|
|
|
|
}
|
2004-04-21 14:54:05 +00:00
|
|
|
|
|
2012-04-14 16:38:52 +00:00
|
|
|
|
# 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";
|
2005-02-17 10:06:12 +00:00
|
|
|
|
|
2007-09-17 16:08:24 +00:00
|
|
|
|
# Make the channels appear in nix-env.
|
|
|
|
|
unlink $nixDefExpr if -l $nixDefExpr; # old-skool ~/.nix-defexpr
|
2014-08-20 15:00:17 +00:00
|
|
|
|
mkdir $nixDefExpr or die "cannot create directory ‘$nixDefExpr’" if !-e $nixDefExpr;
|
2007-09-17 16:08:24 +00:00
|
|
|
|
my $channelLink = "$nixDefExpr/channels";
|
|
|
|
|
unlink $channelLink; # !!! not atomic
|
2014-08-20 15:00:17 +00:00
|
|
|
|
symlink($profile, $channelLink) or die "cannot symlink ‘$channelLink’ to ‘$profile’";
|
2005-02-17 10:06:12 +00:00
|
|
|
|
}
|
2004-10-20 14:42:38 +00:00
|
|
|
|
|
2005-02-17 10:06:12 +00:00
|
|
|
|
|
2012-10-03 20:37:06 +00:00
|
|
|
|
die "$0: argument expected\n" if scalar @ARGV == 0;
|
2005-04-07 14:35:44 +00:00
|
|
|
|
|
|
|
|
|
|
2004-04-21 14:54:05 +00:00
|
|
|
|
while (scalar @ARGV) {
|
|
|
|
|
my $arg = shift @ARGV;
|
|
|
|
|
|
|
|
|
|
if ($arg eq "--add") {
|
2014-08-20 15:00:17 +00:00
|
|
|
|
die "$0: ‘--add’ requires one or two arguments\n" if scalar @ARGV < 1 || scalar @ARGV > 2;
|
2012-04-14 16:38:52 +00:00
|
|
|
|
my $url = shift @ARGV;
|
|
|
|
|
my $name = shift @ARGV;
|
|
|
|
|
unless (defined $name) {
|
|
|
|
|
$name = basename $url;
|
|
|
|
|
$name =~ s/-unstable//;
|
|
|
|
|
$name =~ s/-stable//;
|
|
|
|
|
}
|
|
|
|
|
addChannel($url, $name);
|
2004-04-21 14:54:05 +00:00
|
|
|
|
last;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-17 10:06:12 +00:00
|
|
|
|
if ($arg eq "--remove") {
|
2014-08-20 15:00:17 +00:00
|
|
|
|
die "$0: ‘--remove’ requires one argument\n" if scalar @ARGV != 1;
|
2012-04-14 16:38:52 +00:00
|
|
|
|
removeChannel(shift @ARGV);
|
2005-02-17 10:06:12 +00:00
|
|
|
|
last;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($arg eq "--list") {
|
2014-08-20 15:00:17 +00:00
|
|
|
|
die "$0: ‘--list’ requires one argument\n" if scalar @ARGV != 0;
|
2005-02-17 10:06:12 +00:00
|
|
|
|
readChannels;
|
2012-04-14 16:38:52 +00:00
|
|
|
|
foreach my $name (keys %channels) {
|
|
|
|
|
print "$name $channels{$name}\n";
|
2005-02-17 10:06:12 +00:00
|
|
|
|
}
|
|
|
|
|
last;
|
|
|
|
|
}
|
|
|
|
|
|
2004-04-21 14:54:05 +00:00
|
|
|
|
elsif ($arg eq "--update") {
|
2012-05-07 21:55:56 +00:00
|
|
|
|
update(@ARGV);
|
2004-04-21 14:54:05 +00:00
|
|
|
|
last;
|
|
|
|
|
}
|
2012-08-01 21:56:11 +00:00
|
|
|
|
|
2014-10-14 10:07:56 +00:00
|
|
|
|
elsif ($arg eq "--rollback") {
|
|
|
|
|
die "$0: ‘--rollback’ has at most one argument\n" if scalar @ARGV > 1;
|
|
|
|
|
my $generation = shift @ARGV;
|
|
|
|
|
my @args = ("$Nix::Config::binDir/nix-env", "--profile", $profile);
|
|
|
|
|
if (defined $generation) {
|
|
|
|
|
die "invalid channel generation number ‘$generation’" unless $generation =~ /^[0-9]+$/;
|
|
|
|
|
push @args, "--switch-generation", $generation;
|
|
|
|
|
} else {
|
|
|
|
|
push @args, "--rollback";
|
|
|
|
|
}
|
|
|
|
|
system(@args) == 0 or exit 1;
|
|
|
|
|
last;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-17 10:06:12 +00:00
|
|
|
|
elsif ($arg eq "--help") {
|
2012-10-03 20:37:06 +00:00
|
|
|
|
exec "man nix-channel" or die;
|
2005-02-17 10:06:12 +00:00
|
|
|
|
}
|
2004-04-21 14:54:05 +00:00
|
|
|
|
|
2012-07-11 22:07:41 +00:00
|
|
|
|
elsif ($arg eq "--version") {
|
|
|
|
|
print "nix-channel (Nix) $Nix::Config::version\n";
|
|
|
|
|
exit 0;
|
|
|
|
|
}
|
2012-08-01 21:56:11 +00:00
|
|
|
|
|
2004-04-21 14:54:05 +00:00
|
|
|
|
else {
|
2014-08-20 15:00:17 +00:00
|
|
|
|
die "unknown argument ‘$arg’; try ‘--help’\n";
|
2004-04-21 14:54:05 +00:00
|
|
|
|
}
|
|
|
|
|
}
|