about summary refs log tree commit diff
path: root/scripts/nix-channel.in
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/nix-channel.in')
-rwxr-xr-xscripts/nix-channel.in209
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..4a480ae28b04
--- /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($<) 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";
+    }
+}