Compare commits

...

22 commits
1.5.5 ... main

Author SHA1 Message Date
28a3a15955
release 1.5.7 2025-04-08 12:29:53 +02:00
9e7d7d0d67
README.md: add Charles Short to list of contributors 2025-04-08 06:15:56 +02:00
ff91e58219
mmdebstrap-autopkgtest-build-qemu: shfmt --binary-next-line --case-indent --indent 2 --simplify 2025-04-08 06:15:56 +02:00
38ebe6aa1f
add two FIXME comments to be solved later... 2025-04-08 06:15:55 +02:00
Jochen Sprickerhof
c7803e2e2e
m-a-b-q: add --sshkey= arg
autopkgtest-virt-qemu opens an ssh port by default. This makes it
usable.
2025-04-08 06:15:55 +02:00
fe758b8441
tests/chrootless-foreign: enable build-essential as and were fixed in dpkg 2025-04-08 06:15:55 +02:00
b04758b133
tests/as-debootstrap-unshare-wrapper: allow disabling auto-apt-proxy by setting an empty http_proxy environment variable 2025-04-08 06:15:55 +02:00
bf6aaa30ac
run_progress(): print exit code or signal on failure 2025-04-08 06:15:55 +02:00
d7107567d1
support loong64
Thanks: Helmut Grohne
2025-04-08 06:15:54 +02:00
712fdcf1ab
coverage.sh: indicate what file exceeded maximum line length 2025-04-08 06:15:54 +02:00
59e5870e7b
gpgvnoexpkeysig: check if gpgv exists and print error otherwise 2025-04-08 06:15:54 +02:00
b78afd9e92
tests/chrootless: account for more moving parts in the underlying test system 2025-04-08 06:15:54 +02:00
5761c527a0
mmdebstrap: fix formatting of last commit 2025-04-08 06:15:46 +02:00
7709ad49bb Add support for elxr
eLxr is a debian derative based on Debian 12. We use
mmdebstrap to bootstrap our edge images. This patch
allows users to use the correct mirror, and right
suite when bootstrapping eLxr.

Signed-off-by: Charles Short <charles.short@windriver.com>
2025-04-08 03:28:16 +00:00
Jochen Sprickerhof
9366f1fee7
Use format string for shell printf
Fixes:

sh: 1: printf: usage: printf format [arg ...]

due to Dir::Etc::trusted no longer being a thing since apt 2.9.24.
2025-01-29 06:26:22 +01:00
b1ba7dab3d
tests/create-foreign-tarball: also remove padlock.so on x86_64 to equalize the test cases 2025-01-13 13:30:04 +01:00
792867a390
release 1.5.6 2025-01-13 13:28:55 +01:00
917a879c4b
place DPkg::Pre-Install-Pkgs workaround into a new config file
Using APT_CONFIG will render the setting ineffective.

Debian-Bug: 
2025-01-11 09:11:04 +01:00
dc5bbb7173
tests/chrootless: make sure that nothing outside the chroot changes 2025-01-11 09:04:07 +01:00
e62f32b098
keep some 20220613 formatting until 20250105 is in Debian stable 2025-01-10 10:53:11 +01:00
fcf1c407de
tests/empty-suite: do not rely on existing ./cache/mmdebstrap-unstable-apt.tar
This is useful for running this test standalone or as part of the Debian
package autopkgtest where it is run in the optional set of tests.
2025-01-10 01:36:14 +01:00
1a17c70ffb
coverage.txt: mark empty-suite with Needs-APT-Config: true 2025-01-10 00:27:49 +01:00
13 changed files with 153 additions and 37 deletions

View file

@ -1,3 +1,15 @@
1.5.7 (2025-04-08)
------------------
- support for elxr Debian derivative
- support loong64
- mmdebstrap-autopkgtest-build-qemu: add --sshkey=...
1.5.6 (2025-01-11)
------------------
- bugfix release
1.5.5 (2025-01-09)
------------------

View file

@ -157,12 +157,13 @@ Contributors
============
- Johannes Schauer Marin Rodrigues (main author)
- Helmut Grohne
- Jochen Sprickerhof
- Helmut Grohne
- Gioele Barabucci
- Benjamin Drung
- Josh Triplett
- Konstantin Demin
- Charles Short
- Chris Hofstaedtler
- Colin Watson
- David Kalnischkies

View file

@ -26,7 +26,7 @@ if [ -e "$MMSCRIPT" ]; then
rm "$TMPFILE"
if [ "$(sed -e '/^__END__$/,$d' "$MMSCRIPT" | wc --max-line-length)" -gt 79 ]; then
echo "exceeded maximum line length of 79 characters" >&2
echo "$MMSCRIPT exceeded maximum line length of 79 characters" >&2
exit 1
fi

View file

@ -435,3 +435,4 @@ Test: zombie-reaping
Modes: unshare
Test: empty-suite
Needs-APT-Config: true

View file

@ -47,5 +47,10 @@ case $GPGSTATUSFD in
;;
esac
if ! command -v gpgv >&2; then
eval 'echo "[GNUPG:] ERROR gpgv executable not found" >&'"$GPGSTATUSFD"
exit 1
fi
# we need eval because we cannot redirect a variable fd
eval 'exec gpgv "$@" '"$GPGSTATUSFD"'>&1 | sed "s/^\[GNUPG:\] EXPKEYSIG /[GNUPG:] GOODSIG /" >&'"$GPGSTATUSFD"

View file

@ -23,7 +23,7 @@
use strict;
use warnings;
our $VERSION = '1.5.5';
our $VERSION = '1.5.7';
use English;
use Getopt::Long;
@ -40,7 +40,8 @@ use Fcntl
qw(S_IFCHR S_IFBLK FD_CLOEXEC F_GETFD F_SETFD LOCK_EX O_RDONLY O_DIRECTORY);
use List::Util qw(any none);
use POSIX
qw(SIGINT SIGHUP SIGPIPE SIGTERM SIG_BLOCK SIG_UNBLOCK strftime isatty);
qw(SIGINT SIGHUP SIGPIPE SIGTERM SIG_BLOCK SIG_UNBLOCK strftime isatty
WIFEXITED WEXITSTATUS WIFSIGNALED WTERMSIG);
use Carp;
use Term::ANSIColor;
use Socket;
@ -1963,10 +1964,7 @@ sub run_progress {
}
close($pipe);
my $fail = 0;
if ($? != 0 or $has_error) {
$fail = 1;
}
my $proc_exit = $?;
waitpid $pid2, 0;
$? == 0 or error "progress parsing failed";
@ -1977,11 +1975,29 @@ sub run_progress {
# only print failure after progress output finished or otherwise it
# might interfere with the remaining output
if ($fail) {
if ($proc_exit != 0 or $has_error) {
if ($verbosity_level >= 1) {
print STDERR $output;
}
error((join ' ', $get_exec->('<$fd>')) . ' failed');
my $what = '';
if ($proc_exit != 0) {
if (POSIX::WIFEXITED($proc_exit)) {
my $exit = POSIX::WEXITSTATUS($proc_exit);
$what
.= "process exited with $exit and error in console output";
} elsif (POSIX::WIFSIGNALED($proc_exit)) {
my $sig = POSIX::WTERMSIG($proc_exit);
$what = "process was killed by signal $sig";
} else {
$what = "process failed with unknown status code";
}
if ($has_error) {
$what .= " and error in console output";
}
} else {
$what .= "error in console output";
}
error((join ' ', $get_exec->('<$fd>')) . " failed: $what");
}
return;
}
@ -2870,6 +2886,9 @@ sub setup {
}
if (-e $options->{apttrusted} && !-r $options->{apttrusted}) {
# FIXME: obtain the keyring file with the permissions of the user
# outside of the unshared namespace
# FIXME: apt no longer sets Dir::Etc::trusted by default
warning "cannot read $options->{apttrusted}";
}
if (-e $options->{apttrustedparts} && !-r $options->{apttrustedparts}) {
@ -3108,7 +3127,11 @@ sub run_setup() {
# dpkg-preconfigure should not be needed as we also have set
# DEBIAN_FRONTEND=noninteractive and DEBCONF_NONINTERACTIVE_SEEN=true and
# should thus never see debconf prompts. See #1091442 for details.
print $conf "#clear DPkg::Pre-Install-Pkgs;\n";
{
open my $tmp, '>', "$options->{root}/etc/apt/apt.conf.d/99debconf"
or error "cannot open /etc/apt/apt.conf.d/99debconf: $!";
print $tmp "#clear DPkg::Pre-Install-Pkgs;\n";
}
close $conf;
@ -4164,6 +4187,8 @@ sub run_cleanup() {
# clean up temporary configuration file
unlink "$options->{root}/etc/apt/apt.conf.d/00mmdebstrap"
or warning "failed to unlink /etc/apt/apt.conf.d/00mmdebstrap: $!";
unlink "$options->{root}/etc/apt/apt.conf.d/99debconf"
or warning "failed to unlink /etc/apt/apt.conf.d/99debconf: $!";
if (defined $ENV{APT_CONFIG} && -e $ENV{APT_CONFIG}) {
unlink $ENV{APT_CONFIG}
@ -5121,6 +5146,7 @@ sub get_suite_by_vendor {
'ubuntu' => {},
'tanglu' => {},
'kali' => {},
'elxr' => {},
);
# pre-fill with some known values
@ -5142,6 +5168,9 @@ sub get_suite_by_vendor {
foreach my $suite ('kali-dev', 'kali-rolling', 'kali-bleeding-edge') {
$suite_by_vendor{'kali'}->{$suite} = 0;
}
foreach my $suite ('aria') {
$suite_by_vendor{'elxr'}->{$suite} = 0;
}
foreach
my $suite ('trusty', 'xenial', 'zesty', 'artful', 'bionic', 'cosmic') {
$suite_by_vendor{'ubuntu'}->{$suite} = 0;
@ -5190,6 +5219,9 @@ sub get_suite_by_vendor {
} elsif ($target eq "kali"
and not exists $suite_by_vendor{'kali'}->{$suite}) {
$suite_by_vendor{'kali'}->{$suite} = 0;
} elsif ($target eq "elxr"
and not exists $suite_by_vendor{'elxr'}->{$suite}) {
$suite_by_vendor{'elxr'}->{$suite} = 0;
}
}
closedir($dh);
@ -5244,6 +5276,8 @@ sub get_keyring_by_suite {
return '/usr/share/keyrings/tanglu-archive-keyring.gpg';
} elsif ($vendor eq 'kali') {
return '/usr/share/keyrings/kali-archive-keyring.gpg';
} elsif ($vendor eq 'elxr') {
return '/usr/share/keyrings/elxr-archive-keyring.gpg';
} else {
error "unknown vendor: $vendor";
}
@ -5280,6 +5314,7 @@ sub get_sourceslist_by_suite {
my @ubuntustable = keys %{ $suite_by_vendor->{'ubuntu'} };
my @tanglustable = keys %{ $suite_by_vendor->{'tanglu'} };
my @kali = keys %{ $suite_by_vendor->{'kali'} };
my @elxr = keys %{ $suite_by_vendor->{'elxr'} };
my $mirror = 'http://deb.debian.org/debian';
my $secmirror = 'http://security.debian.org/debian-security';
@ -5317,6 +5352,8 @@ sub get_sourceslist_by_suite {
$mirror = 'http://archive.tanglu.org/tanglu';
} elsif (any { $_ eq $suite } @kali) {
$mirror = 'https://http.kali.org/kali';
} elsif (any { $_ eq $suite } @elxr) {
$mirror = 'https://mirror.elxr.dev';
}
my $sourceslist = '';
$sourceslist .= "deb$signedby $mirror $suite $compstr\n";
@ -5542,9 +5579,9 @@ sub main() {
# obtain the correct defaults for the keyring locations that apt knows
# about
my $apttrusted
= `eval \$(apt-config shell v Dir::Etc::trusted/f); printf \$v`;
= `eval \$(apt-config shell v Dir::Etc::trusted/f); printf %s \$v`;
my $apttrustedparts
= `eval \$(apt-config shell v Dir::Etc::trustedparts/d); printf \$v`;
= `eval \$(apt-config shell v Dir::Etc::trustedparts/d); printf %s \$v`;
chomp(my $hostarch = `dpkg --print-architecture`);
my $options = {
@ -5865,7 +5902,7 @@ sub main() {
}
# setting PATH for chroot, ldconfig, start-stop-daemon...
my $defaultpath = `eval \$(apt-config shell v DPkg::Path); printf \$v`;
my $defaultpath = `eval \$(apt-config shell v DPkg::Path); printf %s \$v`;
if (length $ENV{PATH}) {
## no critic (Variables::RequireLocalizedPunctuationVars)
$ENV{PATH} = "$ENV{PATH}:$defaultpath";
@ -6128,6 +6165,7 @@ sub main() {
armhf => 'arm',
hppa => 'hppa',
i386 => 'i386',
loong64 => 'loongarch64',
m68k => 'm68k',
mips => 'mips',
mips64 => 'mips64',
@ -6803,6 +6841,7 @@ sub main() {
if (any { $_ eq $options->{format} }
('tar', 'squashfs', 'ext2', 'ext4', 'null')) {
if ($options->{format} ne 'null') {
#<<< perltidy 20220613 formatting
if (any { $_ eq $options->{variant} } ('extract', 'custom')
and $options->{mode} eq 'fakechroot') {
info "creating a tarball, squashfs, ext2 or ext4 image in"
@ -6810,6 +6849,7 @@ sub main() {
. " custom variants because there might be no tar inside the"
. " chroot";
}
#>>>
# try to fail early if target tarball or squashfs image cannot be
# opened for writing
if ($options->{target} ne '-') {
@ -6840,12 +6880,14 @@ sub main() {
# in unshare and root mode, other users than the current user need to
# access the rootfs, most prominently, the _apt user. Thus, make the
# temporary directory world readable.
#<<< perltidy 20220613 formatting
if (
any { $_ eq $options->{mode} } ('unshare', 'root')
or ($EFFECTIVE_USER_ID == 0 and $options->{mode} eq 'chrootless')
) {
chmod 0755, $options->{root} or error "cannot chmod root: $!";
}
#>>>
} elsif ($options->{format} eq 'directory') {
# user does not seem to have specified a tarball as output, thus work
# directly in the supplied directory
@ -7331,9 +7373,11 @@ sub main() {
error "cannot copy to standard output: $!";
}
} else {
#<<< perltidy 20220613 formatting
if (any { $_ eq $options->{format} }
('squashfs', 'ext2', 'ext4')
or defined $tar_compressor) {
#>>>
my @argv = ();
if ($options->{format} eq 'squashfs') {
push @argv, 'tar2sqfs',
@ -8450,8 +8494,8 @@ its core, what B<mmdebstrap> does can be put into a 14 line shell script:
Apt::Architecture "$(dpkg --print-architecture)";
Apt::Architectures "$(dpkg --print-architecture)";
Dir "$(cd "$2" && pwd)";
Dir::Etc::Trusted "$(eval "$(apt-config shell v Dir::Etc::Trusted/f)"; printf "$v")";
Dir::Etc::TrustedParts "$(eval "$(apt-config shell v Dir::Etc::TrustedParts/d)"; printf "$v")";
Dir::Etc::Trusted "$(eval "$(apt-config shell v Dir::Etc::Trusted/f)"; printf %s "$v")";
Dir::Etc::TrustedParts "$(eval "$(apt-config shell v Dir::Etc::TrustedParts/d)"; printf %s "$v")";
END
echo "deb http://deb.debian.org/debian/ $1 main" > "$2/etc/apt/sources.list"
APT_CONFIG="$2/apt.conf" apt-get update

View file

@ -95,6 +95,12 @@ explicitly select --boot=efi, operation will fail.
Passes an additional B<--keyring> parameter to B<mmdebstrap>.
=item B<--sshkey>=F<sshkey>
Install the given ssh public key file into the virtual machine image for the root user.
This option also causes the ssh server to be installed.
By default, no key or server is installed.
=back
=head1 EXAMPLES
@ -131,7 +137,7 @@ die() {
exit 1
}
usage() {
die "usage: $0 [--architecture=|--apt-proxy=|--keyring=|--mirror=|--script=|--size=] --boot=efi <RELEASE> <IMAGE>"
die "usage: $0 [--architecture=|--apt-proxy=|--keyring=|--sshkey=|--mirror=|--script=|--size=] --boot=efi <RELEASE> <IMAGE>"
}
usage_error() {
echo "error: $*" 1>&2
@ -146,6 +152,7 @@ KEYRING=
RELEASE=
SIZE=25G
SCRIPT=
SSHKEY=
# consumed by setup-testbed
export AUTOPKGTEST_BUILD_QEMU=1
@ -172,6 +179,9 @@ opt_apt_proxy() {
opt_keyring() {
KEYRING="$1"
}
opt_sshkey() {
SSHKEY=$1
}
opt_mirror() {
# consumed by setup-testbed
export MIRROR="$1"
@ -202,14 +212,14 @@ positional_7() {
while test "$#" -gt 0; do
case "$1" in
--architecture=* | --arch=* | --boot=* | --keyring=* | --mirror=* | --script=* | --size=*)
--architecture=* | --arch=* | --boot=* | --keyring=* | --sshkey=* | --mirror=* | --script=* | --size=*)
optname="${1%%=*}"
"opt_${optname#--}" "${1#*=}"
;;
--apt-proxy=*)
opt_apt_proxy "${1#*=}"
;;
--architecture | --arch | --boot | --keyring | --mirror | --script | --size)
--architecture | --arch | --boot | --keyring | --sshkey | --mirror | --script | --size)
test "$#" -ge 2 || usage_error "missing argument for $1"
"opt_${1#--}" "$2"
shift
@ -296,6 +306,10 @@ esac
test "$(dpkg-query -f '${db:Status-Status}' -W "dpkg-dev")" = installed \
|| die "please install dpkg-dev"
if test -n "$SSHKEY" && ! test -f "$SSHKEY"; then
die "error: ssh keyfile '$SSHKEY' not found"
fi
dpkg-checkbuilddeps -d "autopkgtest, dosfstools, e2fsprogs, fdisk, mount, mtools, passwd, uidmap, libarchive13, systemd-boot-efi:$ARCHITECTURE $BINUTILS" /dev/null \
|| die "please install the required packages listed above"
@ -360,6 +374,14 @@ if test -n "$SCRIPT"; then
'--customize-hook=rm -f "$1/userscript"'
fi
# add ssh key for root
if test -n "$SSHKEY"; then
set -- "$@" \
--include=openssh-server \
'--customize-hook=mkdir -m700 -p "$1/root/.ssh"' \
"--customize-hook=upload $SSHKEY /root/.ssh/authorized_keys"
fi
set -- "$@" \
"--customize-hook=download vmlinuz '$WORKDIR/kernel'" \
"--customize-hook=download initrd.img '$WORKDIR/initrd'" \

View file

@ -7,7 +7,7 @@ while [ "$#" -gt 0 ]; do
key="$1"
case "$key" in
SUDO)
SUDO=sudo
SUDO="sudo --preserve-env"
;;
*)
echo "Unknown argument: $key"
@ -33,6 +33,7 @@ ret=0
ret=0
(
exec 3>&- 4>&-
# shellcheck disable=SC2086
env --chdir=./shared $SUDO sh -x ./test.sh 2>&1
) || ret=$?
echo $ret >&3

View file

@ -28,11 +28,14 @@ fi
# https://bugs.debian.org/1031105
# https://salsa.debian.org/installer-team/debootstrap/-/merge_requests/90
AUTOPROXY=
eval "$(apt-config shell AUTOPROXY Acquire::http::Proxy-Auto-Detect)"
if [ -n "$AUTOPROXY" ] && [ -x "$AUTOPROXY" ] && [ -e /tmp/.auto-apt-proxy-0 ]; then
TMP_APT_CONFIG=$(mktemp)
echo 'Dir "/dev/null";' >"$TMP_APT_CONFIG"
chmod 644 "$TMP_APT_CONFIG"
# allow an empty http_proxy variable to disable this
if [ -n "${http_proxy-}" ]; then
eval "$(apt-config shell AUTOPROXY Acquire::http::Proxy-Auto-Detect)"
if [ -n "$AUTOPROXY" ] && [ -x "$AUTOPROXY" ] && [ -e /tmp/.auto-apt-proxy-0 ]; then
TMP_APT_CONFIG=$(mktemp)
echo 'Dir "/dev/null";' >"$TMP_APT_CONFIG"
chmod 644 "$TMP_APT_CONFIG"
fi
fi
$prefix {{ CMD }} --variant=custom --mode={{ MODE }} \

View file

@ -2,7 +2,29 @@
set -eu
export LC_ALL=C.UTF-8
export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }}
trap "rm -f /tmp/chrootless.tar /tmp/root.tar" EXIT INT TERM
trap "rm -f /tmp/chrootless.tar /tmp/root.tar /tmp/before.md5 /tmp/before.tartv /tmp/after.md5 /tmp/after.tartv" EXIT INT TERM
rootfsmd5() {
bname="$1"
# dhclient changes /etc/resolv.conf, so it changes /etc and we thus must
# manually adjust the timestamp.
# There is a race condition here. dhclient could change /etc after touch but
# before tar packages it.
touch --date="@{{ SOURCE_DATE_EPOCH }}" /etc
(tar --one-file-system --anchored \
--exclude="./etc/resolv.conf" \
--exclude="./var/lib/dhcp/dhclient*.leases" \
--exclude="./var/log/journal/*" \
--exclude="./var/log/wtmp" \
--exclude="./var/backups" \
--exclude="./var/lib/apt" \
--exclude="./var/lib/systemd/timers" \
-C / --sort=name \
-c ./usr ./bin ./etc ./lib ./sbin ./var \
| tee /dev/fd/3 | md5sum >"/tmp/$bname.md5") 3>&1 | tar tv >"/tmp/$bname.tartv"
}
rootfsmd5 before
for INCLUDE in '' 'apt' 'apt,build-essential' 'systemd-sysv'; do
for MODE in root chrootless; do
{{ CMD }} --mode=$MODE --variant={{ VARIANT }} \
@ -12,3 +34,9 @@ for INCLUDE in '' 'apt' 'apt,build-essential' 'systemd-sysv'; do
cmp /tmp/root.tar /tmp/chrootless.tar || diffoscope /tmp/root.tar /tmp/chrootless.tar
rm /tmp/chrootless.tar /tmp/root.tar
done
rootfsmd5 after
if ! cmp /tmp/before.md5 /tmp/after.md5; then
echo "found changes outside the chroot:" >&2
diff -u /tmp/before.tartv /tmp/after.tartv
exit 1
fi

View file

@ -26,12 +26,7 @@ fi
[ "$(id -u)" -eq 0 ]
[ -e "/proc/sys/fs/binfmt_misc/qemu-$(deb2qemu "$arch")" ]
# dpkg is unable to install architecture arch:all packages with a
# dependency on an arch:any package (perl-modules-5.34 in this case)
# inside foreign architecture chrootless chroots, because dpkg will use
# its own architecture as the native architecture, see #825385 and #1020533
# So we are not testing the installation of apt,build-essential here.
for INCLUDE in '' 'apt' 'systemd-sysv'; do
for INCLUDE in '' 'apt' 'apt,build-essential' 'systemd-sysv'; do
echo 1 >"/proc/sys/fs/binfmt_misc/qemu-$(deb2qemu "$arch")"
arch-test "$arch"
{{ CMD }} --mode=root --architecture="$arch" --variant={{ VARIANT }} \

View file

@ -72,6 +72,7 @@ $prefix {{ CMD }} --mode={{ MODE }} --variant=apt --architectures="$foreign_arch
| grep -v "^\\./usr/lib/$native_gnu/ld-linux-aarch64\\.so\\.1$" \
| grep -v "^\\./usr/lib/$native_gnu/libmvec\\.so\\.1$" \
| grep -v "^\\./usr/lib/$native_gnu/perl/5\\.[0-9][.0-9]\\+/.*\\.ph$" \
| grep -v "^\\./usr/lib/$native_gnu/engines-3/padlock\\.so$" \
| grep -v "^\\./usr/share/doc/[^/]\\+/changelog\\(\\.Debian\\)\\?\\.$native_arch\\.gz$" \
| grep -v '^\./usr/share/man/man8/i386\.8\.gz$' \
| grep -v '^\./usr/share/man/man8/x86_64\.8\.gz$'

View file

@ -3,11 +3,14 @@ set -eu
export LC_ALL=C.UTF-8
export SOURCE_DATE_EPOCH={{ SOURCE_DATE_EPOCH }}
trap "rm -f /tmp/debian-chroot.tar" EXIT INT TERM
trap "rm -f /tmp/debian-chroot1.tar /tmp/debian-chroot2.tar" EXIT INT TERM
{{ CMD }} --variant={{ VARIANT }} --verbose \
{{ CMD }} --variant={{ VARIANT }} \
{{ DIST }} /tmp/debian-chroot1.tar {{ MIRROR }}
{{ CMD }} --variant={{ VARIANT }} \
--setup-hook='echo deb {{ MIRROR }} {{ DIST }} main >> "$1"/etc/apt/sources.list' \
'' /tmp/debian-chroot.tar
'' /tmp/debian-chroot2.tar
cmp ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.tar /tmp/debian-chroot.tar \
|| diffoscope ./cache/mmdebstrap-{{ DIST }}-{{ VARIANT }}.tar /tmp/debian-chroot.tar
cmp /tmp/debian-chroot1.tar /tmp/debian-chroot2.tar \
|| diffoscope /tmp/debian-chroot1.tar /tmp/debian-chroot2.tar