#!/usr/bin/env bash # set -euo pipefail IFS=$'\n\t' DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" : ${DATA_DIRECTORY:="/usr/local/share/freebsdupdate"} : ${STAGE_FILE:="${DATA_DIRECTORY}/stage"} : ${RELEASE_DIRECTORY:="${DATA_DIRECTORY}/release"} : ${LOG_DIRECTORY:="${DATA_DIRECTORY}/logs"} : ${PORTS_TREE:="/usr/ports"} : ${PORTS_REPO:="https://git.FreeBSD.org/ports.git"} ############## Setup ######################### function die { local status_code="$1" shift (>&2 echo "${@}") exit "$status_code" } function log { (>&2 echo "${@}") } ############## Program ######################### function main { assert_directories local stage="" if [ -e "$STAGE_FILE" ]; then local stage=$(cat "$STAGE_FILE") fi if [ "$stage" = "selfbuild" ]; then log_cmd stage_selfbuild elif [ "$stage" = "selfinstallworld" ]; then log_cmd stage_selfinstallworld elif [ "$stage" = "selfconflictcheck" ]; then log_cmd stage_selfconflictcheck elif [ "$stage" = "releasebuild" ]; then log_cmd stage_releasebuild elif [ "$stage" = "done" ]; then log_cmd stage_done else die 1 "Unhandled stage: \"$stage\"." fi } function log_cmd { "${@}" |& tee "$LOG_DIRECTORY/$(date +%Y%m%d-%s).log" } function self_conflict_check { if etcupdate status | grep -qE '^ C '; then die 1 'Conflicts remain in etcupdate. Run `etcupdate resolve` to fix them first.' fi } function assert_directories { for d in "$DATA_DIRECTORY" "$RELEASE_DIRECTORY" "$LOG_DIRECTORY"; do if [ ! -e "$d" ]; then mkdir -p "$d" fi done } function update_ports_tree { if [ ! -e "$PORTS_TREE" ]; then mkdir -p $PORTS_TREE git -C $PORTS_TREE init --initial-branch=main git -C $PORTS_TREE remote add origin $PORTS_REPO fi git -C $PORTS_TREE fetch origin main # 'refs/heads/main' git -C $PORTS_TREE checkout FETCH_HEAD } function set_stage { echo "${@}" > "$STAGE_FILE" } function stage_selfbuild { self_conflict_check assert_directories update_ports_tree SRCCONF=/dev/null __MAKE_CONF=/dev/null make -C /usr/src clean SRCCONF=/dev/null __MAKE_CONF=/dev/null make -C /usr/src buildworld buildkernel SRCCONF=/dev/null __MAKE_CONF=/dev/null make -C /usr/src installkernel set_stage "selfinstallworld" /sbin/shutdown -r now } function stage_selfinstallworld { etcupdate -p SRCCONF=/dev/null __MAKE_CONF=/dev/null make -C /usr/src installworld etcupdate -B set_stage "selfconflictcheck" stage_selfconflictcheck } function stage_selfconflictcheck { self_conflict_check set_stage "releasebuild" /sbin/shutdown -r now } function stage_releasebuild { local today=$(date +%Y%m%d) local target_directory="${RELEASE_DIRECTORY}/${today}" if [ -e "$target_directory" ]; then die 1 "The release directory $target_directory already exists. Exiting." fi SRCCONF=/dev/null __MAKE_CONF=/dev/null make -C /usr/src clean make -C /usr/src buildworld buildkernel make -C /usr/src/release obj make -C /usr/src/release release mkdir -p "$target_directory" make -C /usr/src/release install DESTDIR="$target_directory" set_stage "done" } function stage_done { log "Everything is done." } main "${@}"