--- /dev/null
+#!/bin/bash
+#
+# This script prepares a Devuan installer 'application' as a root
+# filesystem packed up as an initrd.
+
+export http_proxy=http://10.10.10.100:3142
+
+: ${LINUX:=linux-image-6.1.0-25-arm64}
+
+: ${DISTNAME:=daedalus}
+: ${DISTVERS:=5.1.0}
+: ${TARGET_ARCH:=arm64}
+: ${SOURCE_SERVER:=http://deb.devuan.org/merged}
+: ${SECTIONS:="main non-free-firmware main/debian-installer"}
+: ${BUILD:=build}
+: ${SOURCES:=$BUILD/sources}
+: ${MEDIA:=$BUILD/media}
+: ${INITRD:=$BUILD/initrd}
+: ${UDEBSDIR:=$INITRD/udebs}
+: ${UDEBSLIST:=$(pwd)/installer-packages.list}
+
+mkdir -p $SOURCES $MEDIA/{pool,dists} $INITRD
+
+# Declare global tables:
+# DEBURL is a mapping: package => download source path
+# DEBDIR is a mapping: package => download target dir
+# PKGS is a subset of DEBURL with only packages to download
+typeset -A DEBURL DEBDIR PKGS
+
+# Helper function to print a message to stderr and exit(1). This is
+# used as bailout when something seems badly wrong.
+die() { echo "$*" >&1 ; exit 1 ; }
+
+# Helper function to reduce a Pacakges file into a series of mappings
+# of the form "package=filename"
+deb_url_map() {
+ sed '/Package:/{s/[^ ]* //;h;d};/Filename:/!d;s/[^ ]* /=/;H;x;s/\n//' $1
+}
+
+# Helper function that resolves alternative dependency to preferred
+# choice, which is one already selected (in PKGS) or else the first of
+# options.
+check_depends() {
+ local P
+ for P in $@ ; do [ -n "${PKGS[$P]}" ] && return 0 ; done
+ echo "$1"
+}
+
+# Helper function that enumerates all dependent and recommended
+# packages of a given package. Uses check_depends to handle
+# alternative dependencies
+depends() {
+ local D
+ sed '/^Package: '$1'$/,/^$/!d;/^\(Depends\|Pre-Depends\|Recommends\):/!d;s/^[^:]*: //;s/([^)]*)//g' $SOURCES/*-Packages | tr , '\012' | \
+ while read D ; do check_depends ${D//\|/ } ; done
+}
+
+# Helper function that expands a given package recursively into all
+# its depends and recommends and installs them all in PKGS.
+expand_depends() {
+ local F P
+ for P in $(depends $1) ; do
+ F="${DEBURL[$P]}"
+ [ -z "$F" ] && continue
+ [ -z "${PKGS["$P"]}" ] || continue
+ PKGS["$P"]="$F"
+ expand_depends $P
+ done
+}
+
+# Helper function to download package $1 and all its dependencies
+#
+download() {
+ local URI="${DEBURL["$1"]}"
+ [ -z "$URI" ] && return 1 # Just ignore missing packages
+ local F="${DEBDIR["$1"]}/${URI##*/}"
+ [ -f "$F" ] && return 0 # alread y downloaded
+ PKGS["$1"]="$URI"
+ local URL="$SOURCE_SERVER/$URI"
+ mkdir -p "${F%/*}"
+ for I in {1..5} ; do
+ wget -O $F -nv "$URL" && break
+ echo "err $I: $URL" >&2
+ rm $F
+ done
+ if [ -f "$F" ] ; then
+ [ -n "$2" ] && cp "$F" "$2/${F##*/}"
+ for D in $(depends $1) ; do download $D $2 ; done
+ return 0
+ fi
+ echo "** download error for package $1" >&2
+ return 1;
+}
+
+# Helper function to enumerate pool packages
+pool_packages() {
+ local T SRC=$SOURCES/non-free-firmware-Packages
+ {
+ # This step prepares the list of all packages of required,
+ # important and standard priority
+ for T in required important standard ; do
+ sed '/^Package:/{s/[^ ]* //;h;d};/^Priority: '$T'$/!d;x' \
+ $SOURCES/*-Packages
+ done
+
+ # This step prepares the list of all packages of
+ # non-free-firmware
+ if [ -r $SRC ] ; then
+ sed '/^Package:/!d;s/[^ ]* //' $SRC
+ fi
+
+ # This step includes additional packages from files
+ # pool-packages*.list
+ sed '/#/d;/^$/d' pool-packages*.list
+ } | sort -u
+}
+
+############################################################
+
+#===== Step 1: Setup "The Sources".
+#
+# This which means to download Packages files for the $DISTNAME,
+# $SECTIONS and $TARGET_ARCH concerned into the $SOURCES directory.
+
+for S in $SECTIONS ; do
+ BASE=$SOURCE_SERVER/dists/$DISTNAME/$S/binary-$TARGET_ARCH/Packages
+ INTO=$SOURCES/${S/\//_}-Packages
+ [ -f $INTO ] && continue
+ for E in .xz .gz "" ; do
+ if wget -nv -O $INTO$E $BASE$E ; then
+ case "$E" in
+ .xz) unxz $INTO$E ;;
+ .gz) gunzip $INTO$E ;;
+ esac
+ break;
+ else
+ rm -f $INTO$E
+ fi
+ done
+ [ -f $INTO ] || die "Cannot get $BASE"
+done
+
+#==== Step 2: Prepare DEBURL and DEBDIR from the Packages files as
+# mappings form package name to download filename and pool section
+# respectively
+
+for S in $SECTIONS ; do
+ for M in $(deb_url_map "$SOURCES/${S/\//_}-Packages") ; do
+ echo "$M" >&2
+ P="${M%%=*}"
+ F="${M#*=}"
+ DEBURL["$P"]="$F"
+ DEBDIR["$P"]="$MEDIA/pool/$S"
+ done
+done
+
+#==== Step 3: load up packages to be unpacked for the initrd. This are
+# packages enumerated in $UDEBSLIST, expanded for depends and
+# recommends.
+
+mkdir -p $UDEBSDIR
+export KERNELVERSION="${LINUX#linux-image-}"
+for P in $(envsubst '${KERNELVERSION}' < $UDEBSLIST | sed '/#/d;/^$/d') ; do
+ download $P $UDEBSDIR
+done
+
+typeset -A INST
+
+# Helper function that resolves alternative dependency to preferred
+# choice, which is one already selected (in PKGS) or else the first of
+# options.
+check_installed_depends() {
+ local P
+ for P in $@ ; do [ -n "${INST[$P]}" ] && return 0 ; done
+ for P in $@ ; do [ -n "${PKGS[$P]}" ] && echo $P && return 0 ; done
+}
+
+# Helper function that enumerates all dependent and recommended
+# packages of a given package. Uses check_depends to handle
+# alternative dependencies
+install_after() {
+ local D
+ sed '/^Package: '$1'$/,/^$/!d;/^\(Depends\|Pre-Depends\|Recommends\):/!d;s/^[^:]*: //;s/([^)]*)//g' $SOURCES/*-Packages | tr , '\012' | \
+ while read D ; do check_installed_depends ${D//\|/ } ; done
+}
+
+# Does $1 occur in $2...
+mentioned() {
+ local X="${@:2}"
+ [ -n "$X" ] && [ -z "${X##*$1*}" ]
+}
+
+# Helper function to enumerate package dependencies in install order
+install_order() {
+ local ORDER=( ${@:1} ) BEFORE=( $(install_after $1) )
+ for D in ${BEFORE[@]} ; do
+ mentioned $D ${@:1} && continue
+ install_order $D ${BEFORE[@]} $1 ${@:1}
+ done
+ echo $1
+}
+
+# Prepare for unpacking
+mkdir -p $INITRD/var/log $INITRD/var/lib/dpkg
+
+# Set up silly-links
+for D in bin sbin lib lib64 ; do
+ mkdir -p $INITRD/usr/$D
+ ln -sTf usr/$D $INITRD/$D
+done
+
+echo "######## Installing installer ######### ${#PKGS[@]}" >&2
+# Use busybox-static rather than busybox-udeb
+download busybox-static $UDEBSDIR
+rm $UDEBSDIR/busybox-udeb_*
+
+mydpkg() {
+ fakechroot fakeroot dpkg --force-architecture \
+ --admindir=$INITRD/var/lib/dpkg --log=$INITRD/var/log/dpkg.log \
+ --root=$INITRD $@
+}
+
+mydpkg --unpack $UDEBSDIR/busybox-static_*.deb
+$INITRD/bin/busybox --list | while read B ; do
+ case "$B" in
+ linuxrc|busybox) : ;;
+ acpid|adjtimex|arp|devmem|freeramdisk|ipneigh|klogd| \
+ loadkmap|logread|mdev|run-init|syslogd|tunctl|uevent|vconfig| \
+ watchdog|arping|brctl|crond|fsfreeze|i2cdetect|i2cdump| \
+ i2cget|i2cset|i2ctransfer|loadfont|mim|partprobe| \
+ rdate|telnetd|ubirename|udhcpd)
+ ln -s ../bin/busybox $INITRD/sbin/$B
+ ;;
+ *)
+ ln -s busybox $INITRD/bin/$B
+ ;;
+ esac
+done
+
+echo "# Patch to pretend debian-installer is installed" >&2
+STATUS=$INITRD/var/lib/dpkg/status
+if ! grep -q 'Package: debian-installer' ${STATUS} ; then
+ # Patch ${STATUS} file to pretend debian-installer is installed
+ VERSION="$(date +"%Y%m%d.%H%M%S-installer")"
+ cat <<EOF >> ${STATUS}
+Package: debian-installer
+Status: install ok installed
+Maintainer: $TARGET_ARCH installer
+Architecture: $TARGET_ARCH
+Version: $VERSION
+Description: devuan installation image
+
+Package: busybox-udeb
+Status: install ok installed
+Maintainer: $TARGET_ARCH installer
+Architecture: $TARGET_ARCH
+Version: $VERSION
+Description: not actually, but busybox-static is installed
+
+EOF
+fi
+
+mydpkg --force-overwrite --path-include="$UDEBSDIR/*.deb" \
+ --unpack $UDEBSDIR/*.udeb
+
+# Configure in good order
+configured() {
+ local F="$INITRD/var/lib/dpkg/status"
+ local X="$(sed '/Package: '$1'$/,/^$/!d;/^Status:/!d' $F)"
+ [ -z "${X#*installed}" ]
+}
+
+for DEB in $UDEBSDIR/*.udeb ; do
+ F="${DEB##*/}"
+ P="${F%%_*}"
+ for D in $(install_order $P) ; do
+ configured $D && continue
+ mydpkg -E --path-include="$UDEBSDIR/*.deb" --configure $D
+ done
+done
+
+# 3f This is set up for choose-mirror and console-setup
+echo ${DISTNAME} > $INITRD/etc/default-release
+mkdir $INITRD/etc/console-setup
+mkdir -p $INITRD/cdrom
+
+# 3g Install "initrd-init" as the initrd /init
+cp initrd-init $INITRD/init
+
+#==== Step 4. Set up a package pool
+#
+# This will populate $MEDIA/pool/* with deb files and $MEDIA/dists
+# with Packages.xz to describe them. First PKGS is extended with
+# pool_packages,
+
+# Extend PKGS table with pool packages;
+for P in $(pool_packages) ; do
+ F="${DEBURL[$P]}"
+ [ -z "$F" ] || PKGS["$P"]="$F"
+done
+
+# Expand the PKGS table with depends and recommends;
+echo -n "*** computing dependencies" >&2
+for P in ${!PKGS[@]} ; do expand_depends "$P" ; done
+echo
+
+# Download the PKGS table packages into their pool sections;
+for P in ${!PKGS[@]} ; do download "$P" ; done
+
+# Prepare the distribution indexes for the various sections.
+for S in $SECTIONS ; do
+ P="dists/$DISTNAME/$S/binary-$TARGET_ARCH/Packages"
+ mkdir -p "$MEDIA/${P%/*}"
+ ( cd $MEDIA ; dpkg-scanpackages -t '*deb' "pool/$S" > $P )
+done
+
+apt-ftparchive \
+ -o APT::FTPArchive::Release::Date="$(date -R)" \
+ -o APT::FTPArchive::Release::Label="DEVUANMEDIA" \
+ -o APT::FTPArchive::Release::Origin="Devuan" \
+ -o APT::FTPArchive::Release::Suite="stable" \
+ -o APT::FTPArchive::Release::Version="$DISTVERS" \
+ -o APT::FTPArchive::Release::Codename="$DISTNAME" \
+ -o APT::FTPArchive::Release::Architectures="$TARGET_ARCH" \
+ -o APT::FTPArchive::Release::Components="$SECTIONS" \
+ release $MEDIA > $MEDIA/dists/$DISTNAME/Release
+ln -s $DISTNAME $MEDIA/dists/stable
+
+# Finally prepare a squashfs of $MEDIA into $INITRD
+fakeroot mksquashfs $MEDIA $INITRD/pool.squashfs
+
+#==== Step 5: Pack up the initrd
+
+# Pack up as initrd.gz and grab vmlinuz
+find $INITRD -not -name udebs -printf '%P\n' | \
+ fakeroot cpio -o -H newc -D $INITRD | gzip > initrd.gz
+cp $INITRD/boot/vmlinuz .
+
+exit 0
+
--- /dev/null
+# This is a list of packages that should be unpacked for the installer
+# Lines with # are ignored. Use envsubst with KERNELVERSION defined
+#
+kernel-image-${KERNELVERSION}-di
+acpi-modules-${KERNELVERSION}-di
+ata-modules-${KERNELVERSION}-di
+btrfs-modules-${KERNELVERSION}-di
+cdrom-core-modules-${KERNELVERSION}-di
+compress-modules-${KERNELVERSION}-di
+crc-modules-${KERNELVERSION}-di
+crypto-dm-modules-${KERNELVERSION}-di
+crypto-modules-${KERNELVERSION}-di
+efi-modules-${KERNELVERSION}-di
+event-modules-${KERNELVERSION}-di
+ext4-modules-${KERNELVERSION}-di
+f2fs-modules-${KERNELVERSION}-di
+fat-modules-${KERNELVERSION}-di
+fb-modules-${KERNELVERSION}-di
+firewire-core-modules-${KERNELVERSION}-di
+fuse-modules-${KERNELVERSION}-di
+i2c-modules-${KERNELVERSION}-di
+input-modules-${KERNELVERSION}-di
+isofs-modules-${KERNELVERSION}-di
+jfs-modules-${KERNELVERSION}-di
+loop-modules-${KERNELVERSION}-di
+md-modules-${KERNELVERSION}-di
+mmc-core-modules-${KERNELVERSION}-di
+mmc-modules-${KERNELVERSION}-di
+mouse-modules-${KERNELVERSION}-di
+mtd-core-modules-${KERNELVERSION}-di
+multipath-modules-${KERNELVERSION}-di
+nbd-modules-${KERNELVERSION}-di
+nic-modules-${KERNELVERSION}-di
+nic-pcmcia-modules-${KERNELVERSION}-di
+nic-shared-modules-${KERNELVERSION}-di
+nic-usb-modules-${KERNELVERSION}-di
+nic-wireless-modules-${KERNELVERSION}-di
+pata-modules-${KERNELVERSION}-di
+pcmcia-modules-${KERNELVERSION}-di
+pcmcia-storage-modules-${KERNELVERSION}-di
+ppp-modules-${KERNELVERSION}-di
+sata-modules-${KERNELVERSION}-di
+scsi-core-modules-${KERNELVERSION}-di
+scsi-modules-${KERNELVERSION}-di
+scsi-nic-modules-${KERNELVERSION}-di
+serial-modules-${KERNELVERSION}-di
+sound-modules-${KERNELVERSION}-di
+speakup-modules-${KERNELVERSION}-di
+squashfs-modules-${KERNELVERSION}-di
+udf-modules-${KERNELVERSION}-di
+uinput-modules-${KERNELVERSION}-di
+usb-modules-${KERNELVERSION}-di
+usb-serial-modules-${KERNELVERSION}-di
+usb-storage-modules-${KERNELVERSION}-di
+xfs-modules-${KERNELVERSION}-di
+apt-cdrom-setup
+apt-mirror-setup
+apt-setup-udeb
+base-installer
+bogl-bterm-udeb
+bootstrap-base
+brltty-udeb
+ca-certificates-udeb
+cdebconf
+cdebconf-newt-entropy
+cdebconf-newt-terminal
+cdebconf-newt-udeb
+cdebconf-priority
+cdebconf-text-entropy
+cdebconf-text-udeb
+cdrom-checker
+cdrom-detect
+choose-init
+console-setup-pc-ekmap
+console-setup-udeb
+console-setup-linux-fonts-udeb
+di-utils-exit-installer
+di-utils-reboot
+di-utils-shell
+eject-udeb
+env-preseed
+espeakup-udeb
+f2fs-tools-udeb
+fdisk-udeb
+file-preseed
+finish-install
+gpgv-udeb
+haveged-udeb
+initrd-preseed
+installation-locale
+kbd-chooser
+kmod-udeb
+libc-bin
+libc6-udeb
+libfribidi0-udeb
+libkmod2
+libpcre3-udeb
+libelogind0
+libtinfo6-udeb
+lilo-installer
+load-cdrom
+localechooser
+lowmemcheck
+main-menu
+mbr-udeb
+mountmedia
+nano-udeb
+netcfg
+nobootloader
+openssh-client-udeb
+open-iscsi-udeb
+pcmciautils-udeb
+pkgsel
+rescue-mode
+rfkill
+save-logs
+screen-udeb
+tzsetup-udeb
+wget-udeb