diff options
Diffstat (limited to 'scripts/nix-channel.in')
-rwxr-xr-x | scripts/nix-channel.in | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/scripts/nix-channel.in b/scripts/nix-channel.in new file mode 100755 index 000000000000..407f27490410 --- /dev/null +++ b/scripts/nix-channel.in @@ -0,0 +1,209 @@ +#! @perl@ -w @perlFlags@ + +use strict; +use File::Basename; +use File::Path qw(mkpath); +use Nix::Config; +use Nix::Manifest; + +Nix::Config::readConfig; + +my $manifestDir = $Nix::Config::manifestDir; + + +# Turn on caching in nix-prefetch-url. +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"} 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($<) || $ENV{"USER"} or die "cannot figure out user name"; +my $profile = "$Nix::Config::stateDir/profiles/per-user/$userName/channels"; +mkpath(dirname $profile, 0, 0755); + +my %channels; + + +# 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*\#/; + 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. +sub writeChannels { + open CHANNELS, ">$channelsList" or die "cannot open ‘$channelsList’: $!"; + foreach my $name (keys %channels) { + print CHANNELS "$channels{$name} $name\n"; + } + close CHANNELS; +} + + +# Adds a channel. +sub addChannel { + my ($url, $name) = @_; + readChannels; + $channels{$name} = $url; + writeChannels; +} + + +# Remove a channel. +sub removeChannel { + my ($name) = @_; + readChannels; + my $url = $channels{$name}; + deleteOldManifests($url . "/MANIFEST", undef) if defined $url; + delete $channels{$name}; + writeChannels; + + system("$Nix::Config::binDir/nix-env --profile '$profile' -e '$name'") == 0 + or die "cannot remove channel ‘$name’\n"; +} + + +# Fetch Nix expressions and pull manifests from the subscribed +# channels. +sub update { + my @channelNames = @_; + + readChannels; + + # Download each channel. + my $exprs = ""; + foreach my $name (keys %channels) { + next if scalar @channelNames > 0 && ! grep { $_ eq $name } @{channelNames}; + + my $url = $channels{$name}; + my $origUrl = "$url/MANIFEST"; + + # Check if $url is a redirect. If so, follow it now to ensure + # consistency if the redirection is changed between + # downloading the manifest and the tarball. + my $headers = `$Nix::Config::curl --silent --head '$url'`; + die "$0: unable to check ‘$url’\n" if $? != 0; + $headers =~ s/\r//g; + $url = $1 if $headers =~ /^Location:\s*(.*)\s*$/m; + + # Check if the channel advertises a binary cache. + my $binaryCacheURL = `$Nix::Config::curl --silent '$url'/binary-cache-url`; + my $extraAttrs = ""; + 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; + + # 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.*)$/; + + $exprs .= "'f: f { name = \"$cname\"; channelName = \"$name\"; src = builtins.storePath \"$path\"; $extraAttrs }' "; + } + + # 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($profile, $channelLink) or die "cannot symlink ‘$channelLink’ to ‘$profile’"; +} + + +die "$0: argument expected\n" if scalar @ARGV == 0; + + +while (scalar @ARGV) { + my $arg = shift @ARGV; + + if ($arg eq "--add") { + die "$0: ‘--add’ requires one or two arguments\n" 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") { + die "$0: ‘--remove’ requires one argument\n" if scalar @ARGV != 1; + removeChannel(shift @ARGV); + last; + } + + if ($arg eq "--list") { + die "$0: ‘--list’ requires one argument\n" if scalar @ARGV != 0; + readChannels; + foreach my $name (keys %channels) { + print "$name $channels{$name}\n"; + } + last; + } + + elsif ($arg eq "--update") { + update(@ARGV); + last; + } + + elsif ($arg eq "--help") { + exec "man nix-channel" or die; + } + + elsif ($arg eq "--version") { + print "nix-channel (Nix) $Nix::Config::version\n"; + exit 0; + } + + else { + die "unknown argument ‘$arg’; try ‘--help’\n"; + } +} |