#!/bin/bash
# minimal config version
MIN_VERSION="3.0"

# Run with tee
if echo "$@" |grep -Eqw '\-v|--verbose' && [ "$1" != 'RESTARTED' ]; then
	# set dir or filename for full log of notamock (with key -v) by environment
	VLOGD=$(realpath ${VLOGD:-./})
	VLOG=${VLOG:-${VLOGD}/$(basename $0)-$(date +%H%M%S).log}
	mkdir -p $(dirname $VLOG)
	date > $VLOG
	echo "==> $(basename $0): log: $VLOG"
	chmod 666 "$VLOG"
	if [ -t 0 ] ; then
		$0 RESTARTED "$@" 2>&1 |tee -a $VLOG | rpmbuild_paint
	else
		$0 RESTARTED "$@" 2>&1 |tee -a $VLOG
	fi
	exit ${PIPESTATUS[0]}
fi

[ "$1" == 'RESTARTED' ] && 	shift

CURPWD=$(pwd)
BUILD_source=$CURPWD
BUILD_dest=$CURPWD
CHANGELOG='no'
PLATFORM=$(rpm --eval %distro_release)
ONLY_ONE=${ONLY_ONE:-"no"}
UPD=${UPD:-"yes"}
VARIABLES=''
BUILD_args=''
REUSABLE=no
BIND=''
DEVNULL='2>/dev/null'
TIME=$(date +%s)
MBINDS=""

# unset variables that checks as [ $var ]
unset CLEARPROJECT
unset CLEARCACHE
unset CLEARTMP
unset STATLOG
unset VERBOSE
unset PGREPWAIT
unset NEED_CREATEREPO

CFG=/etc/notamock.cfg
CFGD=/etc/notamock.d

# special config for project
if [ -f './notamock.cfg' ] ; then
	CFG=$(realpath ./notamock.cfg)
	CFGD=''
fi


HLP(){
echo "$0 - simple mock analogue"
echo "Usage:
	cd /project/dir ; $0 <variables> <parameters>"
echo "Parameters:
	-c | --config        -  specify config file, default is /etc/notamock.cfg
	-h | --help          -  this help
	-r | --release       -  target OS release (ex. 2021.1)
	-v | --verbose       -  verbose mode with a full logging and highlighting
	-u | --update        -  create or recreate new rootfs
	-e | --cmd-pre       -  run command before rpmbuild (default /bin/sh)
	-o | --cmd-post      -  run command after rpmbuild (default /bin/sh)
	-C | --clear-project -  cleanup project dir before build
	-w | --pgrep-wait    -  wait while 'pgrep VAR' before run Package Manager
	-S | --showrc        -  show current configuration and exit
	--clear-cache        -  remove rpms cache
	--clear-tmp          -  remove all from \$TMPD
	--stat               -  statistics log"
echo "Variables:
	all variables specified from config file can be overridden from the cmdline of script in KEY=VALUE format
	also you may set variables from environment, but config file priority is higher
	See /etc/notamock.cfg.
"
echo "Examples:
	notamock -v -r 13        -  build project for rosa13 platform with verbose and colored output
	notamock -v REUSABLE=yes -  build for current platform, retaining the rootfs for future builds of this project
	notamock -v RPMSAVE=yes  -  save BuildRequires packages to notamock's internal RPM repository
	notamock -e              -  pause before rpmbuild to enter interactive shell in the chroot container
"
exit
}

# find and import libmotamock
NOTAMOCKLIB="/usr/share/notamock/libnotamock"
[ -f './libnotamock' ] && NOTAMOCKLIB=./libnotamock
source "$NOTAMOCKLIB"

pre_chroot(){
	vecho "additional actions for rootfs before chroot"
	pushd $1
		for q in $(seq 0 $(( ${#chroot_act[@]} - 1 )) ) ; do
			echo "==> $(basename $0): run: ${chroot_act[$q]}"
			${chroot_act[$q]}
		done
	popd
}

save_to_cache() {
	vecho "Save rpms to cache: $RPMCACHE"
	DIRFROM=$1
	echo ''
	mkdir -p $RPMCACHE
		(find ${DIRFROM}/var/cache/dnf -name "*rpm" -type f
		find /var/cache/dnf -name "*rpm" -type f |grep $PLATFORM ) |while read rpm ; do
		if ! [ -f "${RPMCACHE}/$(basename $rpm)" ] ; then
			mv -f "$rpm" ${RPMCACHE}/ && echo -e "${REWRITE}==> $(basename $0): To cache: $(basename $rpm)"
			: > "${RPMCACHE}/.need_update_metadata"
		fi
	done
	echo -e ${REWRITE}
	if [ -f  "${RPMCACHE}/.need_update_metadata" ] ; then
		rm -f "${RPMCACHE}/.need_update_metadata"
		waitfor "${RPMCACHE}/.repodata" "==> $(basename $0): waiting while another notamock process use $CREATE_REPO"
		vrun "$CREATE_REPO $RPMCACHE"
	fi
}

getdeps() {
	spec=$1 ; shift
	ROOT=$1 ; shift
	args=$@
	vecho "Installing build requires"
	if [ "$PM_install_BR" == "INTERNAL" ] ; then
		vecho "get BuildRequires $spec $args"
		rpms=$(LC_ALL=C chroot $ROOT rpmspec -q  --define="_sourcedir $(dirname $spec |sed 's:'$ROOT':/:')"  \
		--buildrequires $(echo $spec |sed 's:'$ROOT':/:') $args |sed -e "s:^:\':" -e "s:$:\':")
		if [ -n "$rpms" ] ; then
			vecho "$PM_install $PM_opts $PM_root_opt $ROOT"
			vecho "$rpms"
			pgrep_wait $PGREPWAIT
			echo "$rpms" |xargs $PM_install $PM_opts $PM_root_opt $ROOT  && return 0
			return 1
		else
			vecho "BuildRequires resolved. Nothing to do"
			return 0
		fi
	elif command -v $(echo $PM_install_BR $args|cut -f1 -d " ") ; then
		vecho "$PM_install_BR ... $spec"
		pgrep_wait $PGREPWAIT
		vrun $PM_install_BR $PM_root_opt $ROOT $PM_opts $spec || return 1
	else
		echo "Unknown command: $PM_install"
		return 1
	fi
}

echo "==> $(basename $0): find parameters"
sep=''
while [ -n "$1" ] ; do
	case "${1}" in
	"-h" | "--help") HLP ;;
	"-c" | "--config")  shift
						CFG="$1"
						CFGD='' ;;
	"-r" | "--release") shift ; PLATFORM="$1" ;;
	"-u" | "--update") RF_BUILD=yes ;;
	"-v" | "--verbose") DEVNULL=''
						VERBOSE=yes;;
	"-S" | "--showrc" ) SHOWRC=yes
						VERBOSE=yes;;
	"-e" | "--cmd-pre") if [ "$2" ] && [ ${2:0:1} != '-' ] ; then
							CMDPRE="$2" ; shift
						else
							CMDPRE='/bin/sh'
						fi ;;
	"-o" | "--cmd-post") if [ "$2" ] && [ ${2:0:1} != '-' ] ; then
							CMDPOST="$2" ; shift
						else
							CMDPOST='/bin/sh'
						fi ;;
	"-w" | "--pgrep-wait" ) shift ; PGREPWAIT=$1 ;;
	"-C" | "--clear-project" ) CLEARPROJECT=yes ;;
	"--clear-cache" ) CLEARCACHE=yes ;;
	"--clear-tmp" ) CLEARTMP=yes ;;
	"--stat" ) STATLOG=yes ; VERBOSE=yes ;;
	"-"*[A-Za-z]*) 	BUILD_args="$BUILD_args $1" ;;
	*)	if [[ "$1" == *"="* ]] && [[ "$1" != "-"* ]] ; then
			VARIABLES="${VARIABLES}${sep}$(echo $1 |sed 's/ /#/g')"
			sep=' '
		elif echo $1 |grep -qe 'src.rpm$' ;then
			BUILD_source=$(basename $1 |sed 's:-[[:digit:]].*src.rpm::')
			BUILD_dest=$BUILD_source
			SRCRPM=$(realpath $1)
			mkdir -p $BUILD_source
			pushd $BUILD_source || exit ${LINENO}
				rpm2cpio $SRCRPM | cpio -idmv
			popd
		else
			echo_exit "Unknown parameter: $1" ${LINENO}
		fi;;
	esac
shift
done

echo "==> $(basename $0): seting up variables from cmdline"

source "$CFG"
VER=${CFG_VERSION:-0.1}
if [ $(echo -e "$VER\n$MIN_VERSION" |sort -V| tail -n1) != "$VER" ] ; then
	echo "Version of config file is less then minimum required for notamock.
$VER < $MIN_VERSION
Please update $CFG from /usr/share/notamock/default.notamock.cfg"
	exit
fi

if [ -d "$CFGD" ] ; then
	for a in $(ls -1 ${CFGD}/. |sort ) ; do
		vrun source ${CFGD}/$a
	done
fi

for a in $VARIABLES; do
	vecho "processing arg: $a"
	a=$(echo $a |sed 's/#/ /g')
	if echo "$a" |grep -q '[[:alnum:]]*+=.*$' ; then
		eval $(echo $a |sed -e 's/=/=("/' -e 's/$/")/')
	elif echo "$a" |grep -q '[[:alnum:]]*=.*$' ; then
		eval $(echo $a |sed -e 's/=/="/' -e 's/$/"/')
	fi
done

PM_opts="$PM_opts \
$PM_cfg_opt $PM_config \
$PM_addrep_opt $PM_cache \
$PM_adds"

if [ "$VERBOSE" ] ; then
	vecho "Variables current values:"
	for a in $(cat $CFG |grep -E '^[[:alnum:]]+.*=' |grep -Ev '\+=|=\(' |sed 's/\+*=.*//'  |sort -u); do
		eval echo -e "\  \  ${a}: \$$a"
	done
	vecho "Arrays current values:"
	for a in $(cat $CFG |grep -E '^[[:alnum:]]+.*=' |grep -E '\+=|=\(' |sed 's/\+*=.*//'  |sort -u); do
		eval echo -e "\  \  ${a}: \${$a[@]}"
	done
	[ "$SHOWRC" ] && exit ${LINENO}
	# parameters conflicts
	if [ "$CMDPRE" -o "$CMDPOST" ] ; then
		echo "==> $(basename $0):  conflict parameters"
		echo "==> $(basename $0):  verbose mode can not be interactive"
		exit ${LINENO}
	fi
fi

if ! [ -f ${BUILD_source}/*.spec ] ; then
	echo "$BUILD_source is not look like project dir"
	exit ${LINENO}
fi

if [ "$CLEARPROJECT" ] ; then
	echo "Cleanup project: ${BUILD_source}"
	if [ "$VERBOSE" ] ; then
		rm -rf ${BUILD_source}/{*.{log,buildlog},*RPMS,BUILD*}
	else
		rm -rf ${BUILD_source}/{*.buildlog,*RPMS,BUILD*}
	fi
fi

if ! [ "$USERID" ] ; then
	USERID=$(ls -Gdn ${BUILD_source} |cut -f3 -d ' ')
	[ "$USERID" -eq 0 ] && USERID=$(ls -Gn ${BUILD_source}/*.spec |cut -f3 -d ' ')
	[ "$USERID" -eq 0 ] && USERID=500
fi

[ "$ONLY_ONE" == yes ] && waitfor "/run/notamock_$PLATFORM" "==> $(basename $0): waiting another notamock process"
date > "/run/notamock_$PLATFORM"

if [ "$CLEARCACHE" ]  ; then
	vrun rm -rf  ${RPMCACHE}
fi

if ! [ -d ${RPMCACHE} ] ; then
	vrun "mkdir -p ${RPMCACHE}"
	vrun "$CREATE_REPO $RPMCACHE"
fi

# Build rootfs if nonexistent or forced with '-u'
if [ ! -f ${BASE}/${ROOTFSNAME}/bin/bash ] || [ "$RF_BUILD" ] ; then
	pgrep_wait $PGREPWAIT
	build_new_rootfs || echo_exit "Build new rootfs - error!" ${LINENO}
elif ! cat /proc/mounts |grep -q "${BASE}/$ROOTFSNAME" ; then
	# wait direct update of rootfs by another notamock process
	waitfor "${BASE}/${ROOTFSNAME}.lock" "==> $(basename $0): waiting while another notamock process update rootfs"
	pgrep_wait $PGREPWAIT
	update_rootfs || echo_exit "Update rootfs - error!" ${LINENO}
	UPD_direct='yes'
fi

vecho "make temporary dirs"
if [ "$CLEARTMP" ] ; then
	vrun full_clear_tmp
else
	mkdir -p $TMPD
fi

[ $? != 0 ] &&  echo_exit "Cannot create temporary directory" ${LINENO}

SUFFIX=$(basename $(realpath $BUILD_source))
if [ "$REUSABLE" == 'yes' ] ; then
	BUILD_DIR=${TMPD}/$SUFFIX
	mkdir -p $BUILD_DIR
	vecho "Build dir: $BUILD_DIR"
else
	BUILD_DIR=$(mktemp -d -p $TMPD --suffix "_$SUFFIX")
fi

echo "==> $(basename $0): temp dir: $BUILD_DIR"
OVERLAY=${BUILD_DIR}/overlay
WORKDIR=${BUILD_DIR}/work
UPPERDIR=${BUILD_DIR}/upper
mkdir -p $OVERLAY $WORKDIR $UPPERDIR

LOWERDIR="${BASE}/$ROOTFSNAME"

vrun mount -t overlay overlay -o lowerdir="${LOWERDIR}",upperdir="$UPPERDIR",workdir="$WORKDIR" "$OVERLAY"
[ $? != 0 ] &&  echo_exit "Mounting overlay - error!" ${LINENO}
trap 'clear_overlay "${INTERNAL_DIR}"' EXIT

if ! [ "$UPD_direct" ] ; then
	vrun $PM_update $PM_root_opt "$OVERLAY" $PM_opts
	[ $? != 0 ] &&  echo_exit "Update layered rootfs - error!" ${LINENO}
else
	vecho "skip udate in chroot"
fi

[ "$STATLOG" ] && vecho "STAGE 1: Container:  $(du -sh "$UPPERDIR" |cut -f1), \
time: $(echo $(($(date +%s) - $TIME)) | awk '{printf "%d:%02d:%02d", $1/3600, ($1/60)%60, $1%60}')"

touch  ${OVERLAY}/etc/machine-id

mkdir -p "${OVERLAY}/$INTERNAL_DIR"
if  [ "$BIND_SOURCE" != 'yes' ] ; then
vecho "copy project to container"
	find $BUILD_source -maxdepth 1 -mindepth 1 \
	! -name BUILDROOT ! -name BUILD \
	! -name RPMS ! -name SRPMS \
	! -name .git -exec cp -fax {} "${OVERLAY}/${INTERNAL_DIR}/" \;
	if [ -f ${BUILD_source}/.git/config ] && [ "$CHANGELOG" != 'no' ] ; then
		grep -iq '%changelog' ${OVERLAY}/${INTERNAL_DIR}/*.spec || (\
		echo -en "\n\n%changelog\n* $(date +'%a %b %d %Y') built by notamock \n- git branch: "
		git --git-dir="${BUILD_source}/.git" --work-tree="$BUILD_source" branch --show-current
		echo
		git --git-dir="${BUILD_source}/.git" --work-tree="$BUILD_source" log \
		--pretty=format:"* %cd %cn <%ce> - %h:%n- %s%n%b%n" --since=1000.days --date=format:'%a %b %d %Y' | \
		sed -e 's/[ \t]*$//' -e 's/%%*/%%/g' ) >> ${OVERLAY}/${INTERNAL_DIR}/*.spec
	fi
else
	vecho "mount project dir to container"
	mount -o bind "$BUILD_source" "${OVERLAY}/${INTERNAL_DIR}/"
fi

trap 'clear_overlay "${INTERNAL_DIR}" "${RPMCACHEBIND}"' EXIT

if [ "$BIND" ] ; then
	MBINDS=''
	inter_mount
	trap 'clear_overlay ${MBINDS} "${INTERNAL_DIR}" "${RPMCACHEBIND}"' EXIT
fi

[ "$STATLOG" ] && vecho "STAGE 2: Container:  $(du -sh "$UPPERDIR" |cut -f1), \
time: $(echo $(($(date +%s) - $TIME)) | awk '{printf "%d:%02d:%02d", $1/3600, ($1/60)%60, $1%60}')"

echo 666 > "${OVERLAY}/exitcode"

for str in $(seq 0 $(( ${#ENV[@]} - 1 )) ) ; do
	echo "==> $(basename $0): set env: ${ENV[$str]}"
	echo "${ENV[$str]}" >> "${OVERLAY}/ENV.lst"
done

echo ======== ENV =======
cat "${OVERLAY}/ENV.lst" 2>/dev/null
echo ====================

vecho "Prepare script that will be started by $CHROOT command"
cat > "${OVERLAY}/start" << EOF
#!/bin/bash
echo "==> $(basename $0): Starting rpm build process"
pushd $INTERNAL_DIR
	mkdir -p RPMS SRPMS BUILD BUILDROOT
	echo 0 > /exitcode
	chmod 666 /exitcode
	adduser omv -u $USERID -U -M -o
	chown -R omv:omv ../
	$CMDPRE
	echo "==>$BUILD_cmd $BUILD_args"
	su omv -c "(. /ENV.lst
		$BUILD_cmd $BUILD_args
		echo "\$?" > /exitcode)"
	$CMDPOST
popd
	exit \$(cat /exitcode)
EOF

chmod +x "${OVERLAY}/start"
getdeps "${OVERLAY}/$INTERNAL_DIR/*.spec" "$OVERLAY" "$BUILD_args"
[ $? != 0 ] &&  echo_exit "Installing BuildRequires - error" ${LINENO}

[ "$STATLOG" ] && vecho "STAGE 3: Container:  $(du -sh "$UPPERDIR" |cut -f1), \
time: $(echo $(($(date +%s) - $TIME)) | awk '{printf "%d:%02d:%02d", $1/3600, ($1/60)%60, $1%60}')"

pre_chroot ${OVERLAY}

vecho "run main chroot process"
[ "$CHROOT" == 'INTERNAL' ] && CHROOT="chroot_internal"
vrun $CHROOT "$OVERLAY" /start

[ "$STATLOG" ] && vecho "STAGE 4: Container:  $(du -sh "$UPPERDIR" |cut -f1), \
time: $(echo $(($(date +%s) - $TIME)) | awk '{printf "%d:%02d:%02d", $1/3600, ($1/60)%60, $1%60}')"

n=1
while [ -f ${OVERLAY}/$INTERNAL_DIR/SRPMS/*nosrc.rpm ] && [ $n -lt 6 ] ; do
	n=$(($n + 1))
	NOSRCRPM=$(ls -1 ${OVERLAY}/$INTERNAL_DIR/SRPMS/*nosrc.rpm | tail -n1)
	rpm -q --requires $NOSRCRPM |while read a ; do
		pgrep_wait $PGREPWAIT
		echo \"$a\" |xargs $PM_install $PM_opts $PM_root_opt $OVERLAY
	done
	rm -f $NOSRCRPM
	vecho "run chroot process, iteration: $n"
	vrun $CHROOT "$OVERLAY" /start
done

[ "$STATLOG" ] && vecho "STAGE 5: Container:  $(du -sh "$UPPERDIR" |cut -f1), \
time: $(echo $(($(date +%s) - $TIME)) | awk '{printf "%d:%02d:%02d", $1/3600, ($1/60)%60, $1%60}')"

[ "$RPMSAVE" -a "${RPMSAVE}" == 'yes' ] && save_to_cache  ${OVERLAY}

ecode=0
mkdir -p ${BUILD_dest} 2>/dev/null
if [ ! -f  ${OVERLAY}/exitcode ] ; then
	echo "==> $(basename $0): build procces: failed"
	ecode=1
elif [ $(cat ${OVERLAY}/exitcode) != 0 ] ; then
	echo "==> $(basename $0): build procces return: $(cat ${OVERLAY}/exitcode)"
	if [ "$BIND_SOURCE" != 'yes' -o "${BUILD_dest}" != "${BUILD_source}" ] ; then
		cp -fan ${OVERLAY}/${INTERNAL_DIR}/*  ${BUILD_dest}/ 2>/dev/null
	fi
	ecode=2
else
	echo "==> $(basename $0): build procces return: 0"
	if [ "$BIND_SOURCE" != 'yes' -o "${BUILD_dest}" != "${BUILD_source}" ] ; then
		mkdir -p ${BUILD_dest}/SRPMS ${BUILD_dest}/RPMS
		chown --reference=${OVERLAY}/${INTERNAL_DIR}/RPMS ${BUILD_dest}/*RPMS
		ls ${OVERLAY}/${INTERNAL_DIR}/SRPMS/* > /dev/null 2>&1 && \
			cp -fpr ${OVERLAY}/${INTERNAL_DIR}/SRPMS/* ${BUILD_dest}/SRPMS/
		if cp -fpr ${OVERLAY}/${INTERNAL_DIR}/RPMS/* ${BUILD_dest}/RPMS/ 2>/dev/null; then
			cp -pn ${OVERLAY}/${INTERNAL_DIR}/*  ${BUILD_dest}/ 2>/dev/null
		else
			cp -prn ${OVERLAY}/${INTERNAL_DIR}/*  ${BUILD_dest}/ 2>/dev/null # do not rewrite existing files
			echo_exit "Oops, failed to get packages from builder" 3
		fi
		rmdir ${BUILD_dest}/*SRPMS 2>/dev/null
	fi
fi

vecho  "post build actions"
if [ $ecode -eq 0 ] ; then
	pushd ${BUILD_dest} > /dev/null
		for q in $(seq 0 $(( ${#post_act[@]} - 1 )) ) ; do
			vrun ${post_act[$q]}
		done
	popd > /dev/null
fi

echo "==> $(basename $0): done."
exit $ecode
