2010-11-29 16:14:43 +00:00
|
|
|
# This script mirrors a remote Nix channel in the local filesystem.
|
|
|
|
# It downloads the remote manifest, then any NAR files that are not
|
2012-04-04 14:45:24 +00:00
|
|
|
# already available in the target directory.
|
2010-11-29 16:14:43 +00:00
|
|
|
|
2009-04-16 09:25:42 +00:00
|
|
|
use strict;
|
2011-11-23 16:15:33 +00:00
|
|
|
use Nix::Manifest;
|
|
|
|
use Nix::GeneratePatches;
|
2009-04-16 09:25:42 +00:00
|
|
|
use File::Basename;
|
|
|
|
use File::stat;
|
2010-11-29 16:14:43 +00:00
|
|
|
|
2009-04-16 09:25:42 +00:00
|
|
|
|
2012-04-04 14:45:24 +00:00
|
|
|
if (scalar @ARGV < 4 || scalar @ARGV > 6) {
|
|
|
|
print STDERR "Syntax: perl mirror-channel.pl <src-channel-url> <dst-channel-dir> <nar-dir> <nar-url> [<all-patches-manifest [<nix-exprs-url>]]\n";
|
2009-04-16 09:25:42 +00:00
|
|
|
exit 1;
|
|
|
|
}
|
|
|
|
|
2012-04-04 14:45:24 +00:00
|
|
|
my $curl = "curl --location --no-progress-bar --show-error --fail";
|
2010-01-26 09:17:50 +00:00
|
|
|
|
2009-04-16 09:25:42 +00:00
|
|
|
my $srcChannelURL = $ARGV[0];
|
2010-06-23 14:07:47 +00:00
|
|
|
my $dstChannelPath = $ARGV[1];
|
|
|
|
my $narPath = $ARGV[2];
|
|
|
|
my $narURL = $ARGV[3];
|
2012-04-04 14:45:24 +00:00
|
|
|
my $allPatchesManifest = $ARGV[4] || "";
|
|
|
|
my $nixexprsURL = $ARGV[5] || "$srcChannelURL/nixexprs.tar.bz2";
|
2009-04-16 09:25:42 +00:00
|
|
|
|
|
|
|
die "$dstChannelPath doesn't exist\n" unless -d $dstChannelPath;
|
2010-06-23 14:07:47 +00:00
|
|
|
die "$narPath doesn't exist\n" unless -d $narPath;
|
2009-04-16 09:25:42 +00:00
|
|
|
|
2010-11-30 13:05:32 +00:00
|
|
|
my $manifestPath = "$dstChannelPath/MANIFEST";
|
|
|
|
|
2010-11-29 16:14:43 +00:00
|
|
|
|
2012-04-04 14:45:24 +00:00
|
|
|
# Fetch the manifest.
|
|
|
|
system("$curl '$srcChannelURL/MANIFEST' > $dstChannelPath/MANIFEST") == 0 or die;
|
2010-11-29 16:14:43 +00:00
|
|
|
|
2010-01-26 09:38:13 +00:00
|
|
|
|
2012-04-04 14:45:24 +00:00
|
|
|
# Mirror nixexprs.tar.bz2.
|
|
|
|
system("$curl '$nixexprsURL' > $dstChannelPath/nixexprs.tar.bz2") == 0 or die "cannot download `$nixexprsURL'";
|
2009-04-16 09:25:42 +00:00
|
|
|
|
|
|
|
|
2010-11-29 16:14:43 +00:00
|
|
|
# Read the manifest.
|
2011-07-31 23:24:08 +00:00
|
|
|
my (%narFiles, %patches);
|
2012-04-04 14:45:24 +00:00
|
|
|
readManifest("$dstChannelPath/MANIFEST", \%narFiles, \%patches);
|
2009-04-16 09:25:42 +00:00
|
|
|
|
|
|
|
%patches = (); # not supported yet
|
|
|
|
|
|
|
|
my $size = scalar (keys %narFiles);
|
|
|
|
print "$size store paths in manifest\n";
|
|
|
|
|
2010-11-29 16:14:43 +00:00
|
|
|
|
2010-01-26 09:51:05 +00:00
|
|
|
# Protect against Hydra problems that leave the channel empty.
|
|
|
|
die "cowardly refusing to mirror an empty channel" if $size == 0;
|
|
|
|
|
2010-11-29 16:14:43 +00:00
|
|
|
|
2009-04-16 09:25:42 +00:00
|
|
|
# Download every file that we don't already have, and update every URL
|
|
|
|
# to point to the mirror. Also fill in the size and hash fields in
|
|
|
|
# the manifest in order to be compatible with Nix < 0.13.
|
|
|
|
|
|
|
|
while (my ($storePath, $files) = each %narFiles) {
|
|
|
|
foreach my $file (@{$files}) {
|
2009-10-16 11:33:48 +00:00
|
|
|
my $narHash = $file->{narHash};
|
2009-04-16 09:25:42 +00:00
|
|
|
my $srcURL = $file->{url};
|
2009-10-16 11:33:48 +00:00
|
|
|
my $dstName = $narHash;
|
|
|
|
$dstName =~ s/:/_/; # `:' in filenames might cause problems
|
2010-06-23 14:07:47 +00:00
|
|
|
my $dstFile = "$narPath/$dstName";
|
|
|
|
my $dstURL = "$narURL/$dstName";
|
2009-04-16 09:25:42 +00:00
|
|
|
|
|
|
|
$file->{url} = $dstURL;
|
2012-04-04 14:45:24 +00:00
|
|
|
if (! -e $dstFile) {
|
2009-04-16 09:25:42 +00:00
|
|
|
print "downloading $srcURL\n";
|
2010-06-23 14:07:47 +00:00
|
|
|
my $dstFileTmp = "$narPath/.tmp.$$.nar.$dstName";
|
2010-07-28 08:25:22 +00:00
|
|
|
system("$curl '$srcURL' > $dstFileTmp") == 0 or die "failed to download `$srcURL'";
|
2010-12-06 00:53:36 +00:00
|
|
|
|
|
|
|
# Verify whether the downloaded file is a bzipped NAR file
|
|
|
|
# that matches the NAR hash given in the manifest.
|
2012-04-04 14:45:24 +00:00
|
|
|
my $hash = `bunzip2 < $dstFileTmp | nix-hash --type sha256 --flat /dev/stdin` or die;
|
2010-12-06 00:53:36 +00:00
|
|
|
chomp $hash;
|
|
|
|
die "hash mismatch in downloaded file `$srcURL'" if "sha256:$hash" ne $file->{narHash};
|
|
|
|
|
2009-04-16 09:25:42 +00:00
|
|
|
rename($dstFileTmp, $dstFile) or die "cannot rename $dstFileTmp";
|
|
|
|
}
|
2010-08-27 09:01:45 +00:00
|
|
|
|
2012-04-04 14:45:24 +00:00
|
|
|
$file->{size} = stat($dstFile)->size or die "cannot get size of $dstFile";
|
|
|
|
|
|
|
|
my $hashFile = "$narPath/.hash.$dstName";
|
|
|
|
my $hash;
|
|
|
|
if (-e $hashFile) {
|
|
|
|
open HASH, "<$hashFile" or die;
|
|
|
|
$hash = <HASH>;
|
|
|
|
close HASH;
|
|
|
|
} else {
|
|
|
|
$hash = `nix-hash --flat --type sha256 --base32 '$dstFile'` or die;
|
|
|
|
chomp $hash;
|
|
|
|
open HASH, ">$hashFile" or die;
|
|
|
|
print HASH $hash;
|
|
|
|
close HASH;
|
|
|
|
}
|
|
|
|
$file->{hash} = "sha256:$hash";
|
2009-04-16 09:25:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-08 09:09:28 +00:00
|
|
|
|
2010-11-29 16:14:43 +00:00
|
|
|
# Read all the old patches and propagate the useful ones. We use the
|
|
|
|
# file "all-patches" to keep track of all patches that have been
|
|
|
|
# generated in the past, so that patches are not lost if (for
|
|
|
|
# instance) a package temporarily disappears from the source channel,
|
|
|
|
# or if multiple instances of this script are running concurrently.
|
2012-04-04 14:45:24 +00:00
|
|
|
my (%dummy, %allPatches);
|
|
|
|
readManifest($allPatchesManifest, \%dummy, \%allPatches)
|
|
|
|
if $allPatchesManifest ne "" && -f $allPatchesManifest;
|
2010-11-29 16:14:43 +00:00
|
|
|
propagatePatches \%allPatches, \%narFiles, \%patches;
|
|
|
|
|
|
|
|
|
|
|
|
# Make the temporary manifest available.
|
2012-04-04 14:45:24 +00:00
|
|
|
writeManifest("$dstChannelPath/MANIFEST", \%narFiles, \%patches);
|