#! /bin/bash

BUILD_SCRIPT_VERSION=1.1

# Target to Photon OS version
VERSION=5

# Keep running container instance alive?
KEEP_SANDBOX_AFTER_FAILURE=1

# Draw spinner while waiting
DRAW_SPINNER=1

# Process %check section
WITH_CHECK=0

# Array of rpmbuild/rpmspec macro definitions
# Example: RPM_MACROS=( --define \"vmxnet3_sw_timestamp 1\" )
RPM_MACROS=()

test "$#" -lt 1 && echo "Usage: $0 <spec-file-to-build.spec> [path-to-output-directory]" && exit 1

CONTAINER=build_spec
SOURCES_BASEURL=https://packages.vmware.com/photon/photon_sources/1.0
SPECPATH=$(readlink -m "$1")
SPECFILE=$(basename "$SPECPATH")
SPECDIR=$(dirname "$SPECPATH")

if [ -z "$2" ]; then
  STAGE="$SPECDIR/stage"
else
  STAGE=$(readlink -m "$2")
fi

mkdir -p "$STAGE/LOGS"
LOGFILE=stage/LOGS/$(basename "$SPECFILE" .spec).log

RPM_MACROS+=( --define \"dist .ph$VERSION\" --define \"with_check $WITH_CHECK\" )

mkdir -p "$STAGE/RPMS"
mkdir -p "$STAGE/SRPMS"

# use &3 for user output
exec 3>&1
# redirect &1 and &2 to the log file
exec &>"$LOGFILE"

# First argument meaning: 1 - exit on fail, 0 - continue on failure.
function wait_for_result() {
  local pid=$!
  if [ "$DRAW_SPINNER" -eq 1 ]; then
    local spin='-\|/'
    local i=0
    echo -n " " >&3
    while [ -d /proc/$pid ]; do
      sleep .25
      echo -ne "\b${spin:i++%4:1}" >&3
    done
    echo -ne "\b" >&3
  fi
  if wait $pid; then
    echo -e "\033[0;32mOK\033[0m" >&3
  elif [ $1 -eq 0 ]; then
    echo -e "\033[0;33mERROR\033[0m" >&3
    return 1
  else
    echo -e "\033[0;31mFAIL\033[0m" >&3
    fail
  fi
  return 0
}

function run() {
  echo -ne "\t$1 " >&3
  shift
  echo "run: $*"
  "$@" &
  wait_for_result 1
}

function tryrun() {
  echo -ne "\t$1 " >&3
  shift
  echo "run: $*"
  "$@" &
  wait_for_result 0
}

function in_sandbox() {
  eval docker exec $CONTAINER $@
}


function create_sandbox() {
  docker ps -f "name=$CONTAINER" && docker rm -f $CONTAINER
  docker inspect --format='{{.Created}}' photon_build_spec:$VERSION.0
  local status=$?
  local cdate
  cdate=$(date --date="$(docker inspect --format='{{.Created}}' photon_build_spec:$VERSION.0)" '+%s')
  # image exists?
  if [ $status -eq 0 ]; then
    local vdate
    vdate=$(($(date '+%s') - 1209600))
    # image is less then 2 weeks
    if [ "$cdate" -gt "$vdate" ]; then
      # use this image
      run "Use local build template image" docker run --ulimit nofile=1024:1024 -v $STAGE/RPMS:/usr/src/photon/RPMS -v $STAGE/SRPMS:/usr/src/photon/SRPMS --privileged -d --name $CONTAINER --network="host" photon_build_spec:$VERSION.0 tail -f /dev/null
      return 0
    else
      # remove old image
      docker image rm photon_build_spec:$VERSION.0
    fi
  fi


  run "Pull photon image" docker run --ulimit nofile=1024:1024 -v $STAGE/RPMS:/usr/src/photon/RPMS -v $STAGE/SRPMS:/usr/src/photon/SRPMS --privileged -d --name $CONTAINER --network="host" photon:$VERSION.0 tail -f /dev/null

  # replace toybox with coreutils and install default build tools
  run "Replace toybox with coreutils" in_sandbox tdnf remove -y toybox
  run "Upgrade Packages" in_sandbox tdnf upgrade -y
  run "Install default build tools" in_sandbox tdnf install -y rpm-build build-essential gmp-devel mpfr-devel tar sed findutils file gzip patch bzip2 createrepo
  run "Create local repo in sandbox" echo -e "[local]\nname=VMWare Photon Linux Local\nbaseurl=file:///usr/src/photon/RPMS\nenabled=1\nskip_if_unavailable=1" | sed 1d | docker exec -i $CONTAINER sh -c 'cat > /etc/yum.repos.d/local.repo'

  run "Create build template image for future use" docker commit "$(docker ps -q -f "name=$CONTAINER")" photon_build_spec:$VERSION.0
}

function prepare_buildenv() {
  mkdir -p "$SPECDIR/SOURCES"
  in_sandbox mkdir -p /usr/src/photon/SOURCES
  run "Create source folder" find "$SPECDIR" -type f -exec cp -u {} "$SPECDIR/SOURCES" \;
  run "Copy sources from $SPECDIR" docker cp "$SPECDIR/SOURCES/." $CONTAINER:/usr/src/photon/SOURCES

  for url in $(in_sandbox rpmspec ${RPM_MACROS[@]} -P /usr/src/photon/SOURCES/"$SPECFILE" | grep "Source[[:digit:]]*:" | grep -o '[^[:space:]]\+$');
  do
    file=$(basename "$url")
    test -f "$SPECDIR/SOURCES/$file" && continue
    tryrun "Download $file" wget "$SOURCES_BASEURL/$file" -O "$SPECDIR/SOURCES/$file" && docker cp "$SPECDIR/SOURCES/$file" $CONTAINER:/usr/src/photon/SOURCES
    # Retry from original URL
    [ $? -eq 0 ] || run "Download $url" wget "$url" -O "$SPECDIR/SOURCES/$file" && docker cp "$SPECDIR/SOURCES/$file" $CONTAINER:/usr/src/photon/SOURCES
  done

  run "createrepo " in_sandbox createrepo /usr/src/photon/RPMS/.
  run "makecache" in_sandbox tdnf makecache

  local br
  br=$(in_sandbox rpmspec ${RPM_MACROS[@]} -P /usr/src/photon/SOURCES/"$SPECFILE" | sed -n 's/BuildRequires://p' | sed 's/ \(<\|\)= /=/g;s/>\(=\|\) [^ ]*//g;s/ \+/ /g' | tr '\n' ' ')
  if [ "$br" != "" ]; then
    run "Install build requirements" in_sandbox tdnf install -y $br
  fi
}

function build() {
  echo -ne "\tRun rpmbuild " >&3
  [ $WITH_CHECK -eq 0 ] && WITH_CHECK_PARAM="--nocheck"
  in_sandbox rpmbuild $WITH_CHECK_PARAM -ba ${RPM_MACROS[@]} /usr/src/photon/SOURCES/"$SPECFILE" &
  wait_for_result 1
  run "Delete SOURCES" rm -rf $SPECDIR/SOURCES
}

function destroy_sandbox() {
  run "Stop container" docker kill $CONTAINER
  run "Remove container" docker rm $CONTAINER
}

function clean_up() {
  echo "Post clean up" >&3
  docker ps -f "name=$CONTAINER" &>/dev/null && destroy_sandbox &>/dev/null
}

function fail() {
  test "$KEEP_SANDBOX_AFTER_FAILURE" -ne 1 && clean_up || \
    echo "Sandbox is preserved for analisys. Use 'docker exec -it $CONTAINER /bin/bash'" >&3
  echo "Build failed. See $LOGFILE for full output" >&3
  echo -e "\033[1;33m" >&3
  tail "$LOGFILE" >&3
  echo -e "\033[0m" >&3
  exit 1
}

trap clean_up SIGINT SIGTERM

echo "0. Build Script Version:" $BUILD_SCRIPT_VERSION >&3

echo "1. Create sandbox" >&3
create_sandbox

echo "2. Prepare build environment" >&3
prepare_buildenv

echo "3. Build Binary and Source Package" >&3
build

echo "4. Destroy sandbox" >&3
destroy_sandbox

echo "Build completed. RPMS are in '$STAGE' folder" >&3