#!/bin/sh
# Set up a first-party FreeBSD package build server (poudriere) for
# pkg.clawdie.si. Idempotent: safe to re-run. Validates the environment and
# inputs first, then acts. Run on the mother-build host as root.
#
# After this: build with poudriere-build.sh, serve the repo over nginx, and
# hand clients clawdie-repo.conf.in. See README.md.

set -u
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
export PATH

ZPOOL="${POUDRIERE_ZPOOL:-zroot}"
ZROOTFS="${POUDRIERE_ZROOTFS:-/poudriere}"
BASEFS="${POUDRIERE_BASEFS:-/usr/local/poudriere}"
VERSION="${POUDRIERE_VERSION:-15.0-RELEASE}"
ARCH="${POUDRIERE_ARCH:-amd64}"
JAIL="${POUDRIERE_JAIL:-clawdie-${ARCH}}"
PTREE="${POUDRIERE_PTREE:-clawdie}"
PORTS_BRANCH="${POUDRIERE_PORTS_BRANCH:-main}"
KEYDIR="${POUDRIERE_KEYDIR:-/usr/local/etc/ssl}"
KEY="${KEYDIR}/clawdie-pkg.key"
PUB="${KEYDIR}/clawdie-pkg.pub"
CONF="/usr/local/etc/poudriere.conf"

usage() {
    cat <<EOF
Usage: poudriere-setup.sh [options]

Idempotently prepares poudriere on this host: config, signing key, build jail,
and a ports tree. Re-running skips anything already present.

Options (or set the matching POUDRIERE_* env var):
  --zpool NAME      ZFS pool poudriere stores under   (default: ${ZPOOL})
  --version VER     FreeBSD jail version              (default: ${VERSION})
  --arch ARCH       jail architecture                 (default: ${ARCH})
  --jail NAME       build jail name                   (default: ${JAIL})
  --ports NAME      ports tree name                   (default: ${PTREE})
  -h, --help        show this help
EOF
}

die() {
    echo "ERROR: $*" >&2
    exit 1
}

have() {
    command -v "$1" >/dev/null 2>&1
}

info() {
    printf '==> %s\n' "$*"
}

while [ "$#" -gt 0 ]; do
    case "$1" in
        --zpool)   ZPOOL="${2:?--zpool needs a value}"; shift 2 ;;
        --version) VERSION="${2:?--version needs a value}"; shift 2 ;;
        --arch)    ARCH="${2:?--arch needs a value}"; shift 2 ;;
        --jail)    JAIL="${2:?--jail needs a value}"; shift 2 ;;
        --ports)   PTREE="${2:?--ports needs a value}"; shift 2 ;;
        -h|--help) usage; exit 0 ;;
        *)         echo "ERROR: unknown option: $1" >&2; usage >&2; exit 2 ;;
    esac
done

# --- verify, then act -------------------------------------------------------

[ "$(id -u)" -eq 0 ] || die "must run as root (poudriere setup creates jails)."

case "${VERSION}" in
    [0-9]*.[0-9]*-RELEASE|[0-9]*.[0-9]*-STABLE|[0-9]*.[0-9]*-CURRENT) ;;
    *) die "invalid --version '${VERSION}' (expected e.g. 15.0-RELEASE)." ;;
esac

have pkg || die "pkg not found; run on a FreeBSD host."
have openssl || die "openssl not found (needed to generate the repo signing key)."
zpool list "${ZPOOL}" >/dev/null 2>&1 || die "ZFS pool '${ZPOOL}' not found; create it or pass --zpool."

# --- act --------------------------------------------------------------------

if ! have poudriere; then
    info "installing poudriere"
    env ASSUME_ALWAYS_YES=yes pkg install -y poudriere || die "pkg install poudriere failed."
fi

if [ ! -f "${KEY}" ]; then
    info "generating repo signing key: ${KEY}"
    mkdir -p "${KEYDIR}"
    ( umask 077; openssl genrsa -out "${KEY}" 4096 ) || die "failed to generate signing key."
    openssl rsa -in "${KEY}" -pubout -out "${PUB}" || die "failed to derive public key."
    chmod 0400 "${KEY}"
fi

if [ ! -f "${CONF}" ]; then
    info "writing ${CONF}"
    cat > "${CONF}" <<EOF
# Clawdie first-party package build server. Managed by poudriere-setup.sh.
ZPOOL=${ZPOOL}
ZROOTFS=${ZROOTFS}
BASEFS=${BASEFS}
FREEBSD_HOST=download.freebsd.org
RESOLV_CONF=/etc/resolv.conf
DISTFILES_CACHE=${BASEFS}/distfiles
USE_TMPFS=yes
# poudriere signs the repo catalog with this key on every bulk run; clients
# trust the matching public key (${PUB}). See clawdie-repo.conf.in.
PKG_REPO_SIGNING_KEY=${KEY}
EOF
else
    info "${CONF} exists; leaving it untouched"
fi

if poudriere jail -i -j "${JAIL}" >/dev/null 2>&1; then
    info "jail '${JAIL}' exists; skipping"
else
    info "creating jail '${JAIL}' (${VERSION} ${ARCH})"
    poudriere jail -c -j "${JAIL}" -v "${VERSION}" -a "${ARCH}" || die "jail create failed."
fi

if poudriere ports -l | awk 'NR>1 {print $1}' | grep -Fxq "${PTREE}"; then
    info "ports tree '${PTREE}' exists; skipping"
else
    info "creating ports tree '${PTREE}' (git ${PORTS_BRANCH})"
    poudriere ports -c -p "${PTREE}" -m git -B "${PORTS_BRANCH}" || die "ports tree create failed."
fi

cat <<EOF

Poudriere is ready.
  jail:        ${JAIL}
  ports tree:  ${PTREE}
  signing key: ${KEY}
  public key:  ${PUB}   <-- ship this to clients (see clawdie-repo.conf.in)

Next:
  ./poudriere-build.sh --jail ${JAIL} --ports ${PTREE} sysutils/colibri
EOF
