#!/bin/bash
# Tasks:
# - Build a clean Debian distro (with debootstrap)
# - Install additional packages (using SOURCE/distro/dpkg-selections.txt)
# - Overwrite with user files (from SOURCE/*)
# - Create distro tarball

# 2007 Arnau Sanchez <arnau@ehas.org>

set -e
# Default values
DEBIAN_VERSION=etch
DEBIAN_REPOSITORY=http://ftp.debian.org/debian

error() { echo -e $1 1>&2; }

debug() { error "--- $1"; }

chroot_exec() {
    chroot $1 bash -c "mount | grep '^proc[[:space:]]' || mount /proc" &&
    chroot $1 su -c "set -e; $2" root;
    chroot $1 bash -c "umount /proc"
}

usage() {
    cat <<EOF
synopsis:

  $BASENAME [OPTIONS] SOURCE TARGET

options:
  -r, --debian-repository=URL    URL Address poiting a Debian repository
  -v, --debian-version=CODENAME  Debian codename version (default: etch)
  -s, --apt-sources=PATH         Alternative sources.list for packages install
  --skip-debootstrap             Skip debootstrap process
  -h, --help                     print command syntax summary
EOF
    if [ ! -z "$1" ]; then error "$1\n"; fi
    exit 1
}

### Main

# Set default environment variables for chroot environment
export LANGUAGE=
export LANG=
export LC_ALL=
export LC_MESSAGES=

# Process options
RETCODE=$(getopt -T >/dev/null; echo $?)
if [ $RETCODE -ne 4 ]; then 
    error "enhanced getopt(1) required"
    exit 1
fi
    
short_opts="hr:d:s"
long_opts="help,debian-repository:,debian-version:,apt-sources:,skip-debootstrap"
args=$(getopt -o $short_opts -l $long_opts -n $(basename $0) -- "$@" 2>/dev/null)
if [ $? -ne 0 ]; then usage; fi
eval set -- "$args"
while true; do
    case "$1" in
        -r|--debian-repository)
            DEBIAN_REPOSITORY=$2
            shift 2;;
        -d|--debian-version)
            DEBIAN_VERSION=$2
            shift 2;;
        -s|--apt-sources)
            APT_SOURCES=$2
            shift 2;;
        --skip-debootstrap)
            SKIP_DEBOOTSTRAP=yes
            shift;;
        -h|--help)
            usage;;            
        --)
            shift; break;;        
        *)
            usage "unrecognized option: $1";;
    esac
done

if [ $# -ne 2 ]; then usage "arguments missing"; fi
SOURCE=$1
TARGET=$2

# Definitions
PACKAGES_SELECTION=$SOURCE/distro/dpkg-selections.txt
VERSION=$SOURCE/distro/VERSION

# Sanity checks
if [ ! -d "$SOURCE" ]; then
    usage "Fatal error: SOURCE must be an existing directory"
elif [ "$(readlink -f $TARGET)" = "/" ]; then
    usage "Fatal error: TARGET directory cannot point to /"
elif [ ! -f $PACKAGES_SELECTION ]; then
    error "packages selection file not found: $PACKAGES_SELECTION"
    error "which should be the output of 'dpkg --get-selections'"
    error "of the desired system"
    exit 1
elif [ !  -f $VERSION ]; then
    error "VERSION file missing: $VERSION"
    exit 1
fi

# Create target directory

# Run debootstrap if option enabled or if original directory is not found
if [ "$SKIP_DEBOOTSTRAP" != yes -o ! -d $TARGET ]; then
    debug "creating target directory: $(readlink -f $TARGET)"
    mkdir -p $TARGET
    debug "bootstraping a base Debian system ($DEBIAN_REPOSITORY): $TARGET"
    debootstrap $DEBIAN_VERSION $TARGET $DEBIAN_REPOSITORY
fi
    
# Copy some basic stuff to target (preinst/postinst scripts and sources.list)
if [ -z "$APT_SOURCES" ]; then
    debug "no apt_sources given, using sources from $SOURCE"
    cp $SOURCE/etc/apt/sources.list $TARGET/etc/apt
else
    debug "using file as sources.list: $APT_SOURCES"
    cp $APT_SOURCES $TARGET/etc/apt/sources.list
fi

# Copy the (optional) preinst/postinst scripts
debug "copying preinst/postinst scripts"
cp $SOURCE/distro/{preinst,postinst} $TARGET/tmp || true

# Write sane contents for basic files (fstab, hosts)
echo "proc /proc proc defaults 0 0" > $TARGET/etc/fstab
echo "127.0.0.1 localhost" > $TARGET/etc/hosts

#  Run preinst script and install additional packages
debug "running preinst script"
chroot_exec $TARGET "/tmp/preinst root" || true
debug "updating repositories and installing additional packages"
chroot_exec $TARGET "dpkg --set-selections" < $PACKAGES_SELECTION
chroot_exec $TARGET "export DEBIAN_FRONTEND=noninteractive &&
    export DEBCONF_NOWARNINGS=yes &&
    export DEBIAN_PRIORITY=critical &&
    apt-get update && 
    apt-get -y --force-yes dselect-upgrade &&
    apt-get clean"

# Copy all files from source to target and run postinst script
debug "copying remaining distro files to target: $SOURCE/distro -> $TARGET"
cp -av $SOURCE/* $TARGET
debug "running postinst script"
chroot_exec $TARGET "/tmp/postinst root" || true

# Create TGZ to distribute
NVERSION=$(cat $VERSION)
TGZ=$(basename $TARGET)-$NVERSION.tgz
debug "creating tarball: $TGZ (from $TARGET)"
tar --numeric-owner -czf $TGZ -C $TARGET .

exit 0

