#!/usr/bin/env bash
set -Eeuo pipefail

thisDir="$(dirname "$(readlink -vf "$BASH_SOURCE")")"
source "$thisDir/.constants.sh" \
	'<target-dir>' \
	'rootfs'

eval "$dgetopt"
while true; do
	flag="$1"; shift
	dgetopt-case "$flag"
	case "$flag" in
		--) break ;;
		*) eusage "unknown flag '$flag'" ;;
	esac
done

targetDir="${1:-}"; shift || eusage 'missing target-dir'
[ -n "$targetDir" ]

aptVersion="$("$thisDir/.apt-version.sh" "$targetDir")"
dpkgVersion="$("$thisDir/.apt-version.sh" "$targetDir" 'dpkg')"

# https://github.com/docker/docker/blob/d6f4fe9e38b60f63e429fff7ffced9c26cbf8236/contrib/mkimage/debootstrap#L63-L177

# prevent init scripts from running during install/update
cat > "$targetDir/usr/sbin/policy-rc.d" <<-'EOF'
	#!/bin/sh

	# For most Docker users, "apt-get install" only happens during "docker build",
	# where starting services doesn't work and often fails in humorous ways. This
	# prevents those failures by stopping the services from attempting to start.

	exit 101
EOF
chmod 0755 "$targetDir/usr/sbin/policy-rc.d"

# prevent upstart scripts from running during install/update (but only if the "upstart" package exists)
if "$thisDir/debuerreotype-chroot" "$targetDir" apt-get install -qq -s upstart &> /dev/null; then
	"$thisDir/debuerreotype-chroot" "$targetDir" dpkg-divert --local --rename --add /sbin/initctl > /dev/null
	cp -a "$targetDir/usr/sbin/policy-rc.d" "$targetDir/sbin/initctl"
	sed -i 's/^exit.*/exit 0/' "$targetDir/sbin/initctl"
fi

# force dpkg not to call sync() after package extraction (speeding up installs)
if [ -d "$targetDir/etc/dpkg/dpkg.cfg.d" ] && dpkg --compare-versions "$dpkgVersion" '>=' '1.15.8.6~'; then
	# --debian-eol lenny and older do not include /etc/dpkg/dpkg.cfg.d
	# force-unsafe-io was added in dpkg 1.15.8.6: https://salsa.debian.org/dpkg-team/dpkg/-/commit/929a9c4808c79781469987585f78f07df7f1d484
	cat > "$targetDir/etc/dpkg/dpkg.cfg.d/docker-apt-speedup" <<-'EOF'
		# For most Docker users, package installs happen during "docker build", which
		# doesn't survive power loss and gets restarted clean afterwards anyhow, so
		# this minor tweak gives us a nice speedup (much nicer on spinning disks,
		# obviously).

		force-unsafe-io
	EOF
	chmod 0644 "$targetDir/etc/dpkg/dpkg.cfg.d/docker-apt-speedup"
fi

if [ -d "$targetDir/etc/apt/apt.conf.d" ]; then
	# TODO make some (all?) of these conditional based on the version of APT that added the feature
	# (perhaps it's finally time for an "apt-version-cmp.sh" helper script to test whether APT is X or newer one version component at a time? "dpkg --compare-versions"!!!)

	# update "autoremove" configuration to be aggressive about removing suggests deps that weren't manually installed
	cat > "$targetDir/etc/apt/apt.conf.d/docker-autoremove-suggests" <<-'EOF'
		# Since Docker users are looking for the smallest possible final images, the
		# following emerges as a very common pattern:

		#   RUN apt-get update \
		#       && apt-get install -y <packages> \
		#       && <do some compilation work> \
		#       && apt-get purge -y --auto-remove <packages>

		# By default, APT will actually _keep_ packages installed via Recommends or
		# Depends if another package Suggests them, even and including if the package
		# that originally caused them to be installed is removed.  Setting this to
		# "false" ensures that APT is appropriately aggressive about removing the
		# packages it added.

		# https://www.debian.org/doc/manuals/aptitude/ch02s05s05.en.html#configApt-AutoRemove-SuggestsImportant
		Apt::AutoRemove::SuggestsImportant "false";
	EOF
	chmod 0644 "$targetDir/etc/apt/apt.conf.d/docker-autoremove-suggests"
	"$thisDir/.fix-apt-comments.sh" "$aptVersion" "$targetDir/etc/apt/apt.conf.d/docker-autoremove-suggests"

	# keep us lean by effectively running "apt-get clean" after every install
	aptGetClean='"rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true";'
	cat > "$targetDir/etc/apt/apt.conf.d/docker-clean" <<-EOF
		# Since for most Docker users, package installs happen in "docker build" steps,
		# they essentially become individual layers due to the way Docker handles
		# layering, especially using CoW filesystems.  What this means for us is that
		# the caches that APT keeps end up just wasting space in those layers, making
		# our layers unnecessarily large (especially since we'll normally never use
		# these caches again and will instead just "docker build" again and make a brand
		# new image).

		# Ideally, these would just be invoking "apt-get clean", but in our testing,
		# that ended up being cyclic and we got stuck on APT's lock, so we get this fun
		# creation that's essentially just "apt-get clean".
		DPkg::Post-Invoke { $aptGetClean };
		APT::Update::Post-Invoke { $aptGetClean };

		Dir::Cache::pkgcache "";
		Dir::Cache::srcpkgcache "";

		# Note that we do realize this isn't the ideal way to do this, and are always
		# open to better suggestions (https://github.com/debuerreotype/debuerreotype/issues).
	EOF
	chmod 0644 "$targetDir/etc/apt/apt.conf.d/docker-clean"
	"$thisDir/.fix-apt-comments.sh" "$aptVersion" "$targetDir/etc/apt/apt.conf.d/docker-clean"

	cat > "$targetDir/etc/apt/apt.conf.d/docker-gzip-indexes" <<-'EOF'
		# Since Docker users using "RUN apt-get update && apt-get install -y ..." in
		# their Dockerfiles don't go delete the lists files afterwards, we want them to
		# be as small as possible on-disk, so we explicitly request that Apt keep them
		# compressed on-disk too instead of decompressing them.

		# For comparison, an "apt-get update" layer without this on a pristine
		# "debian:wheezy" base image was "29.88 MB", where with this it was only
		# "8.273 MB".

		Acquire::GzipIndexes "true";
	EOF
	# https://github.com/debuerreotype/debuerreotype/issues/41
	isDebianJessie="$([ -f "$targetDir/etc/os-release" ] && source "$targetDir/etc/os-release" && [ "${ID:-}" = 'debian' ] && [ "${VERSION_ID:-}" = '8' ] && echo '1')" || :
	if [ -n "$isDebianJessie" ] || [[ "$aptVersion" == 0.* ]] || dpkg --compare-versions "$aptVersion" '<<' '1.0.9.2~'; then
		cat >> "$targetDir/etc/apt/apt.conf.d/docker-gzip-indexes" <<-'EOF'

			# https://salsa.debian.org/apt-team/apt/commit/b0f4b486e6850c5f98520ccf19da71d0ed748ae4; released in src:apt 1.0.9.2, 2014-10-02
			# prior to src:apt 1.0.9.2, "Acquire::GzipIndexes" _only_ applied to gzip-compressed list files, so we need to prefer those on older releases
			Acquire::CompressionTypes::Order:: "gz";
		EOF
		if [ -n "$isDebianJessie" ]; then
			cat >> "$targetDir/etc/apt/apt.conf.d/docker-gzip-indexes" <<-'EOF'
				# see also https://github.com/debuerreotype/debuerreotype/issues/41 (details of a bug that's apparently specific to Debian Jessie)
			EOF
		fi
	fi
	chmod 0644 "$targetDir/etc/apt/apt.conf.d/docker-gzip-indexes"
	"$thisDir/.fix-apt-comments.sh" "$aptVersion" "$targetDir/etc/apt/apt.conf.d/docker-gzip-indexes"

	# remove apt-cache translations for faster "apt-get update"
	cat > "$targetDir/etc/apt/apt.conf.d/docker-no-languages" <<-'EOF'
		# In Docker, we don't often need the "Translations" files, so we're just wasting
		# time and space by downloading them, and this inhibits that.  For users that do
		# need them, it's a simple matter to delete this file and "apt-get update". :)

		Acquire::Languages "none";
	EOF
	chmod 0644 "$targetDir/etc/apt/apt.conf.d/docker-no-languages"
	"$thisDir/.fix-apt-comments.sh" "$aptVersion" "$targetDir/etc/apt/apt.conf.d/docker-no-languages"
fi
