Ask apt to give us the deb filenames directly

Guessing filenames is boring. What if we could ask apt to tell us which
debs it downloaded (or found lying around elsewhere) directly? Turns out
we can rather easily avoiding a bunch of guesswork.
David Kalnischkies 2 years ago
parent 487237f9ae
commit 71067316ee

@ -3072,7 +3072,7 @@ $CMD \$include --mode=$defaultmode --variant=$variant \
--setup-hook='sync-in "'"\$tmpdir"'" /var/cache/apt/archives/partial' \ --setup-hook='sync-in "'"\$tmpdir"'" /var/cache/apt/archives/partial' \
$DEFAULT_DIST - $mirror > test1.tar $DEFAULT_DIST - $mirror > test1.tar
cmp orig.tar test1.tar cmp orig.tar test1.tar
$CMD \$include --mode=$defaultmode --variant=$variant --skip=download/empty \ $CMD \$include --mode=$defaultmode --variant=$variant \
--customize-hook='touch "\$1"/var/cache/apt/archives/partial' \ --customize-hook='touch "\$1"/var/cache/apt/archives/partial' \
--setup-hook='mkdir -p "\$1"/var/cache/apt/archives/' \ --setup-hook='mkdir -p "\$1"/var/cache/apt/archives/' \
--setup-hook='sync-in "'"\$tmpdir"'" /var/cache/apt/archives/' \ --setup-hook='sync-in "'"\$tmpdir"'" /var/cache/apt/archives/' \

@ -874,20 +874,9 @@ sub run_dpkg_progress {
sub run_apt_progress { sub run_apt_progress {
my $options = shift; my $options = shift;
my @debs = @{ $options->{PKGS} // [] }; my @debs = @{ $options->{PKGS} // [] };
my $tmpeipp;
if (exists $options->{EIPP_RES}) {
(undef, $tmpeipp) = tempfile(
"mmdebstrap.eipp.XXXXXXXXXXXX",
OPEN => 0,
TMPDIR => 1
);
}
my $get_exec = sub { my $get_exec = sub {
my @prefix = (); my @prefix = ();
my @opts = (); my @opts = ();
if (exists $options->{EIPP_RES}) {
push @opts, "-oDir::Log::Planner=$tmpeipp";
}
return ( return (
@prefix, @prefix,
@{ $options->{ARGV} }, @{ $options->{ARGV} },
@ -942,60 +931,44 @@ sub run_apt_progress {
} }
}; };
run_progress $get_exec, $line_handler, $line_has_error, $options->{CHDIR}; run_progress $get_exec, $line_handler, $line_has_error, $options->{CHDIR};
if (exists $options->{EIPP_RES}) {
info "parsing EIPP results...";
open my $fh, '<', $tmpeipp
or error "failed to open $tmpeipp for reading: $!";
my $inst = 0;
my $pkg;
my $arch;
my $ver;
while (my $line = <$fh>) {
chomp $line;
if ($line ne "") {
if ($line =~ /^Status: .+/) {
$inst = 1;
} elsif ($line =~ /^Package: (.*)/) {
$pkg = $1;
} elsif ($line =~ /^Architecture: (.*)/) {
$arch = $1;
} elsif ($line =~ /^Version: (.*)/) {
$ver = $1;
}
next;
}
if ($inst == 0 && defined $pkg && defined $arch && defined $ver) {
push @{ $options->{EIPP_RES} }, ["$pkg:$arch", $ver];
}
$inst = 0;
undef $pkg;
undef $ver;
}
close $fh;
unlink $tmpeipp;
}
return; return;
} }
sub run_apt_download_progress { sub run_apt_download_progress {
my $options = shift; my $options = shift;
my %result = shift; my $tmplistofdebs;
if ($options->{dryrun}) { if ($options->{dryrun}) {
info "simulate downloading packages with apt..."; info "simulate downloading packages with apt...";
} else { } else {
info "downloading packages with apt..."; info "downloading packages with apt...";
(undef, $tmplistofdebs) = tempfile(
"mmdebstrap.listofdebs.XXXXXXXXXXXX",
OPEN => 0,
TMPDIR => 1
);
} }
return run_apt_progress({ run_apt_progress({
ARGV => [ ARGV => [
'apt-get', 'apt-get',
'--yes', '--yes',
'-oDebug::pkgDpkgPm=1', '-oDebug::pkgDpkgPm=1',
'-oDir::Log=/dev/null', '-oDir::Log=/dev/null',
$options->{dryrun} ? '-oAPT::Get::Simulate=true' : (), $options->{dryrun}
? '-oAPT::Get::Simulate=true'
: "-oDpkg::Pre-Install-Pkgs::=cat > $tmplistofdebs",
@{ $options->{APT_ARGV} }, @{ $options->{APT_ARGV} },
], ],
%result
}); });
if ($tmplistofdebs) {
open my $fh, '<', $tmplistofdebs
or error "failed to open $tmplistofdebs for reading: $!";
my @listofdebs = <$fh>;
close $fh;
unlink $tmplistofdebs;
chomp(@listofdebs);
return @listofdebs;
}
return [];
} }
sub run_chroot { sub run_chroot {
@ -2050,21 +2023,14 @@ sub run_update() {
sub run_download() { sub run_download() {
my $options = shift; my $options = shift;
# We use /var/cache/apt/archives/ to figure out which packages apt chooses
# to install. That's why the directory must be empty if:
# - /var/cache/apt/archives exists, and
# - no simulation run is done, and
# - the variant is not extract or custom or the number to be
# installed packages not zero
#
# In the future we want to replace downloading packages with "apt-get # In the future we want to replace downloading packages with "apt-get
# install --download-only" and installing them with dpkg by just installing # install" and installing them with dpkg by just installing the essential
# the essential packages with apt from the outside with # packages with apt from the outside with DPkg::Chroot-Directory.
# DPkg::Chroot-Directory. We are not doing that because then the preinst # We are not doing that because then the preinst script of base-passwd will
# script of base-passwd will not be called early enough and packages will # not be called early enough and packages will fail to install because they
# fail to install because they are missing /etc/passwd. # are missing /etc/passwd.
my @cached_debs = (); my @cached_debs = ();
my @dl_debs = (); my @dl_debs;
if ( if (
!$options->{dryrun} !$options->{dryrun}
&& ((none { $_ eq $options->{variant} } ('extract', 'custom')) && ((none { $_ eq $options->{variant} } ('extract', 'custom'))
@ -2084,14 +2050,6 @@ sub run_download() {
push @cached_debs, $deb; push @cached_debs, $deb;
} }
closedir $dh; closedir $dh;
if (scalar @cached_debs > 0) {
if (any { $_ eq 'download/empty' } @{ $options->{skip} }) {
info "skipping download/empty as requested";
} else {
error("/var/cache/apt/archives/ inside the chroot contains: "
. (join ', ', (sort @cached_debs)));
}
}
} }
# To figure out the right package set for the apt variant we can use: # To figure out the right package set for the apt variant we can use:
@ -2100,14 +2058,6 @@ sub run_download() {
# apt and libapt treats apt as essential. If we want to install less # apt and libapt treats apt as essential. If we want to install less
# (essential variant) then we have to compute the package set ourselves. # (essential variant) then we have to compute the package set ourselves.
# Same if we want to install priority based variants. # Same if we want to install priority based variants.
my %result = ();
if (not $options->{dryrun}) {
# if there are already packages in /var/cache/apt/archives/,
# we need to know which are part of the solution by apt
if (scalar @cached_debs > 0) {
$result{EIPP_RES} = \@dl_debs;
}
}
if (any { $_ eq $options->{variant} } ('extract', 'custom')) { if (any { $_ eq $options->{variant} } ('extract', 'custom')) {
if (scalar @{ $options->{include} } == 0) { if (scalar @{ $options->{include} } == 0) {
info "nothing to download -- skipping..."; info "nothing to download -- skipping...";
@ -2126,11 +2076,10 @@ sub run_download() {
} }
} }
run_apt_download_progress({ @dl_debs = run_apt_download_progress({
APT_ARGV => @apt_argv, APT_ARGV => @apt_argv,
dryrun => $options->{dryrun}, dryrun => $options->{dryrun},
}, },
%result
); );
} elsif ($options->{variant} eq 'apt') { } elsif ($options->{variant} eq 'apt') {
# if we just want to install Essential:yes packages, apt and their # if we just want to install Essential:yes packages, apt and their
@ -2146,11 +2095,10 @@ sub run_download() {
# remind me in 5+ years that I said that after I wrote # remind me in 5+ years that I said that after I wrote
# in the bugreport: "Are you crazy?!? Nobody in his # in the bugreport: "Are you crazy?!? Nobody in his
# right mind would even suggest depending on it!") # right mind would even suggest depending on it!")
run_apt_download_progress({ @dl_debs = run_apt_download_progress({
APT_ARGV => ['dist-upgrade'], APT_ARGV => ['dist-upgrade'],
dryrun => $options->{dryrun}, dryrun => $options->{dryrun},
}, },
%result
); );
} elsif ( } elsif (
any { $_ eq $options->{variant} } any { $_ eq $options->{variant} }
@ -2160,7 +2108,7 @@ sub run_download() {
# 17:27 < DonKult> (?essential includes 'apt' through) # 17:27 < DonKult> (?essential includes 'apt' through)
# 17:30 < josch> DonKult: no, because pkgCacheGen::ForceEssential ","; # 17:30 < josch> DonKult: no, because pkgCacheGen::ForceEssential ",";
# 17:32 < DonKult> touché # 17:32 < DonKult> touché
run_apt_download_progress({ @dl_debs = run_apt_download_progress({
APT_ARGV => [ APT_ARGV => [
'install', 'install',
'?narrow(' '?narrow('
@ -2178,75 +2126,26 @@ sub run_download() {
], ],
dryrun => $options->{dryrun}, dryrun => $options->{dryrun},
}, },
%result
); );
} else { } else {
error "unknown variant: $options->{variant}"; error "unknown variant: $options->{variant}";
} }
my @essential_pkgs; my @essential_pkgs;
if (scalar @cached_debs > 0 && scalar @dl_debs > 0) { # strip the the chroot directory from the filenames
my $archives = "/var/cache/apt/archives/"; foreach my $deb (@dl_debs) {
# for each package in @dl_debs, check if it's in if (rindex $deb, $options->{root}, 0) {
# /var/cache/apt/archives/ and add it to @essential_pkgs error "package file $deb not in chroot directory"
foreach my $p (@dl_debs) { . " -- use copy:// instead of file://";
my ($pkg, $ver_epoch) = @{$p};
# apt appends the architecture at the end of the package name
($pkg, my $arch) = split ':', $pkg, 2;
# apt replaces the colon by its percent encoding %3a
my $ver = $ver_epoch;
$ver =~ s/:/%3a/;
# the architecture returned by apt is the native architecture.
# Since we don't know whether the package is architecture
# independent or not, we first try with the native arch and then
# with "all" and only error out if neither exists.
if (-e "$options->{root}/$archives/${pkg}_${ver}_$arch.deb") {
push @essential_pkgs, "$archives/${pkg}_${ver}_$arch.deb";
} elsif (-e "$options->{root}/$archives/${pkg}_${ver}_all.deb") {
push @essential_pkgs, "$archives/${pkg}_${ver}_all.deb";
} else {
error( "cannot find package for $pkg:$arch (= $ver_epoch) "
. "in /var/cache/apt/archives/");
}
}
} else {
# collect the .deb files that were downloaded by apt from the content
# of /var/cache/apt/archives/
if (!$options->{dryrun}) {
my $apt_archives = "/var/cache/apt/archives/";
opendir my $dh, "$options->{root}/$apt_archives"
or error "cannot read $apt_archives";
while (my $deb = readdir $dh) {
if ($deb !~ /\.deb$/) {
next;
} }
$deb = "$apt_archives/$deb"; if (-e $deb) {
if (!-f "$options->{root}/$deb") { push @essential_pkgs, substr($deb, length($options->{root}));
next; } else {
error "cannot find package file $deb";
} }
push @essential_pkgs, $deb;
} }
closedir $dh;
if (scalar @essential_pkgs == 0) { # Unpack order matters for e.g. timestamps on directories
# check if a file:// URI was used
open(my $pipe_apt, '-|', 'apt-get', 'indextargets', '--format',
'$(URI)', 'Created-By: Packages')
or error "cannot start apt-get indextargets: $!";
while (my $uri = <$pipe_apt>) {
if ($uri =~ /^file:\/\//) {
error
"nothing got downloaded -- use copy:// instead of"
. " file://";
}
}
error "nothing got downloaded";
}
}
}
# Unpack order matters. Since we create this list using two different
# methods but we want both methods to have the same result, we sort the
# list before returning it.
@essential_pkgs = sort @essential_pkgs; @essential_pkgs = sort @essential_pkgs;
return (\@essential_pkgs, \@cached_debs); return (\@essential_pkgs, \@cached_debs);
@ -6678,15 +6577,13 @@ the B<setup> step. This can be disabled using B<--skip=update>.
=item B<download> =item B<download>
Checks whether F</var/cache/apt/archives/> is empty. This can be disabled with In the B<extract> and B<custom> variants, C<apt-get install> is used to
B<--skip=download/empty>. In the B<extract> and B<custom> variants, C<apt-get download all the packages requested via the B<--include> option. The B<apt>
--download-only install> is used to download all the packages requested via the variant uses the fact that libapt treats the C<apt> packages as implicitly
B<--include> option. The B<apt> variant uses the fact that libapt treats the essential to download only all C<Essential:yes> packages plus apt using
C<apt> packages as implicitly essential to download only all C<Essential:yes> C<apt-get dist-upgrade>. In the remaining variants, all Packages files
packages plus apt using C<apt-get --download-only dist-upgrade>. In the downloaded by the B<update> step are inspected to find the C<Essential:yes>
remaining variants, all Packages files downloaded by the B<update> step are package set as well as all packages of the required priority.
inspected to find the C<Essential:yes> package set as well as all packages of
the required priority.
=item B<extract> =item B<extract>
@ -6914,7 +6811,7 @@ apt-cacher-ng, you can use the B<sync-in> and B<sync-out> special hooks to
synchronize a directory outside the chroot with F</var/cache/apt/archives> synchronize a directory outside the chroot with F</var/cache/apt/archives>
inside the chroot. inside the chroot.
$ mmdebstrap --variant=apt --skip=download/empty --skip=essential/unlink \ $ mmdebstrap --variant=apt --skip=essential/unlink \
--setup-hook='mkdir -p ./cache "$1"/var/cache/apt/archives/' \ --setup-hook='mkdir -p ./cache "$1"/var/cache/apt/archives/' \
--setup-hook='sync-in ./cache /var/cache/apt/archives/' \ --setup-hook='sync-in ./cache /var/cache/apt/archives/' \
--customize-hook='sync-out /var/cache/apt/archives ./cache' \ --customize-hook='sync-out /var/cache/apt/archives ./cache' \

Loading…
Cancel
Save