about summary refs log blame commit diff
path: root/scripts/nix-channel.in
blob: b3251975b52bf10acbeab86808a5124bf83dd9e9 (plain) (tree)
1
2
3
4
5
6
7
8
9
                        

           

                             
                
 
                                            
 
 
                                      
                                                          
                                                  
                                                               
 
                                                         
                                                 
                                         
                                      
 





                                                                            

 
                             

                                  
                                                                             

                        
                         



                                                   




                   
                              
                   
                                                                             

                                                  




                   
                 
                
                          
                 
                            



                  
                   
                   
                    
                 
                            
                  


                                                                               


 
                                                              



                 
                                                         
                                                    
 

                                                                                           
 



                                       







                                                                      

                                    
                                                                                           
                                                        
 
                                       
                                              

                                                                                                    

                                                 
 

                                                                                      
 





                                                                     
 




                                                                                        
                                                                                         
 
 



                       

                                      



                      


 


                                



                          








                                                           


             

                                        
                                   





                                        

                                             



             
                                
                                        


               



                              

          
                                                    

     
#! @perl@ -w @perlFlags@

use strict;
use File::Basename;
use File::Path qw(make_path);
use Nix::Config;

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";
make_path(dirname $profile, mode => 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;
    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 manifests from the subscribed
# channels.
sub update {
    readChannels;

    # Create the manifests directory if it doesn't exist.
    mkdir $manifestDir, 0755 unless -e $manifestDir;

    # Do we have write permission to the manifests directory?
    die "$0: you do not have write permission to `$manifestDir'!\n" unless -W $manifestDir;

    # Download each channel.
    my $exprs = "";
    foreach my $name (keys %channels) {
        my $url = $channels{$name};

        # 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;
        
        # Pull the channel manifest.
        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.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'" if $? != 0;
        chomp $path;

        $exprs .= "'f: f { name = \"$name\"; src = builtins.storePath \"$path\"; }' ";
    }

    # 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'";
}


sub usageError {
    print STDERR <<EOF;
Usage:
  nix-channel --add URL [CHANNEL-NAME]
  nix-channel --remove CHANNEL-NAME
  nix-channel --list
  nix-channel --update
EOF
    exit 1;
}


usageError if scalar @ARGV == 0;


while (scalar @ARGV) {
    my $arg = shift @ARGV;

    if ($arg eq "--add") {
        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);
        last;
    }

    if ($arg eq "--list") {
        usageError if scalar @ARGV != 0;
        readChannels;
        foreach my $name (keys %channels) {
            print "$name $channels{$name}\n";
        }
        last;
    }

    elsif ($arg eq "--update") {
        usageError if scalar @ARGV != 0;
        update;
        last;
    }
    
    elsif ($arg eq "--help") {
        usageError;
    }

    else {
        die "unknown argument `$arg'; try `--help'";
    }
}