#!/bin/bash
set -eu
echo "$0 $@ [$$] START" >&2


if [ "${1:-}" = --help ]; then
	exec cat <<EOF
Info: Скрипт создания архива для офф-лайн обновления Eva
Usage: make_offline_distr.sh from_version [to_version [dst_dir/]]
    from_version - current installed version: docker exec evateam cat /opt/eva_version
    to_version - default last official version
    dst_dir - default current working dir

Запускать необходимо от root, чтобы при копировании файлов сохранялись их метаданные.

Example:

Create archive:
1) On any linux host:
# skip strongbash034
curl -o - \
	https://updater.evateam.ru/evateam/public/current/devel/eva_opt/bin/make_offline_distr.sh \
	| bash -s from_version [to_version]

2) On updater, for download
/opt/eva_deploy/output/evateam/public/current/devel/eva_opt/bin/make_offline_distr.sh \
	from_version to_version /opt/eva_deploy/output/

Update instruction

    copy dst_dir/evateam...tar.xz distributive archive to container host.

    Run on container host to update Evateam:
	docker exec evateam mkdir -p /var/cache/eva_dist

	# skip strongbash034
	xzcat evateam-02.03.06.0081-02.04.01.0012.tar.xz | docker cp - evateam:/var/cache/eva_dist

	# обновим скрипты обновления:
docker exec evateam rsync -a \
/var/cache/eva_dist/evateam/public/builds/02.04.01.0012/eva_opt/bin/{update.sh,update_scripts} \
/opt/bin/

	# Запуск обновления
	docker exec evateam /opt/bin/update.sh --offline --version=to_version [--restore-on-fail]
EOF
fi


# Если запускать от curl, то $0 == bash, которых в системе много.
# if pidof -csxo %PPID "${0##*/}"; then
# 	echo "Already running" >&2
# 	exit 1
# fi
if [[ $(id -u) != 0 ]]; then
	echo 'Please run script as root! For preserve files permissions.' >&2
	exit 1
fi


declare UPDATE_URL
declare PRODUCT=evateam BRANCH
declare VERSION SCOPE_DIR VERSION_SCOPE=public INSTALLED_VERSION
declare IS_LOCAL_DIST DIST_CACHE_DIR DST_DIR
declare -a PATCHES_LIST


if [[ ! ${UPDATE_URL:-} ]]; then
	if [[ -d /opt/eva_deploy/output ]]; then
		UPDATE_URL=/opt/eva_deploy/output
	else
		UPDATE_URL="rsync://${UPDATE_SERVER:-updater.evateam.ru}/eva"
	fi
fi

if [[ $UPDATE_URL != *:* ]]; then
	IS_LOCAL_DIST=TRUE
	DIST_CACHE_DIR="$UPDATE_URL"
else
	IS_LOCAL_DIST=
	DIST_CACHE_DIR=eva_dist
fi

SCOPE_DIR="$DIST_CACHE_DIR/$PRODUCT/$VERSION_SCOPE"

echo "Use UPDATE_URL=${UPDATE_URL}${IS_LOCAL_DIST:+ as local}, DIST_CACHE_DIR=${DIST_CACHE_DIR}"


init_distr_cache() {
	mkdir -p "$DIST_CACHE_DIR"
	#  sync: product/scope/... --exclude=builds
	#   т.е. все индексы доступных билдов, без самих билдов.
	#   если --exclude=builds/*/**, то будет список билдов для контроля индексов
	# rm -rf /var/cache/eva_dist
	rsync --stats --verbose --progress --checksum --timeout=60 --block-size=40507 \
		--archive --relative \
		--include="/$PRODUCT/$VERSION_SCOPE/builds/*/" \
		--include="/$PRODUCT/$VERSION_SCOPE/builds/*/branch" \
		--exclude="/$PRODUCT/$VERSION_SCOPE/builds/**" \
		"${UPDATE_URL}/./$PRODUCT/$VERSION_SCOPE" \
		"$DIST_CACHE_DIR" >&2
	return 0
}


check_need_update() {
	if [[ ${ARG_VERSION:-} ]]; then
		if [[ ! -h $SCOPE_DIR/test/$ARG_VERSION ]]; then
			echo "Версии $ARG_VERSION нет на сервере обновления" >&2
			echo -e "Доступны версии:\n$(ls -1 "$SCOPE_DIR/test/")" >&2
			exit 1
		elif [[ -h $SCOPE_DIR/deleted/$ARG_VERSION ]]; then
			echo "Версия $ARG_VERSION удалена, обновление не возможно." >&2
			exit 1
		fi
		VERSION="$ARG_VERSION"
	elif [[ -h "$SCOPE_DIR/official/$BRANCH/last" ]]; then
		VERSION=$(readlink "$SCOPE_DIR/official/$BRANCH/last")
		VERSION=${VERSION##*/}
	else
		# На сервере нет последнего офишела
		VERSION=
	fi
	echo "Версия updater: ${VERSION}; локально: ${INSTALLED_VERSION}" >&2
	if [[ "${VERSION}" > "${INSTALLED_VERSION}" ]]; then
		echo "На updater более свежая версия $VERSION, нужно обновление!" >&2
	else
		echo "Обновление не требуется" >&2
		exit 0
	fi
	return 0
}


calc_patches_list() {
	PATCHES_LIST=()
	local version
	# shellcheck disable=SC2045
	for version in $(ls "$SCOPE_DIR/patch/"); do
		if ! [[ $version =~ ^([0-9]{2}\.){3}[0-9]{4}$ ]]; then
			echo "Skip unknown file $version" >&2
			continue
		fi

		if ! [[
			$version > $INSTALLED_VERSION
			&& $version < $VERSION
			]]; then
			continue
		fi

		# if [[ ${ARG_SKIP_PATCHES:-} ]] \
		# 	&& [[
		# 	$ARG_SKIP_PATCHES = TRUE
		# 	| | " $ARG_SKIP_PATCHES " = *" $version "*
		# 	]]; then
		# 	echo "Skip $version due --skip-patches" >&2
		# 	continue
		# fi

		echo "Add intermediate patch version $version" >&2
		PATCHES_LIST+=("$version")
	done
	return 0
}


check_disksize() {
	local avail required
	set -o pipefail
	avail="$(df -P . | awk 'END {print $4/1024/1024}' | cut -d '.' -f 1)"
	set +o pipefail
	# Calc Gb: 4(first) + 1 * n(patch) + 3(tar.xz)
	required=$(( ${#PATCHES_LIST[*]} + 4 + 3 ))

	if [ "$avail" -lt $((required * 9 / 10 )) ]; then
		# Специально пишем больше, чем проверяем
		echo "Нужно минимум $required Гб свободного места для запуска обновления!"
		exit 1
	fi
	return 0
}


download_version() {
	local version="$1" prev="${2:-}"
	if [[ -d "$SCOPE_DIR/builds/${version}/eva_opt" ]]; then
		echo "Version $version already downloaded" >&2
		return 0
	fi
	echo
	echo "Do download $version" >&2
	local dl_dir="$SCOPE_DIR/builds/${version}.dl"
	# --verbose --progress
	# --link-dest= - создаём линк на на уже скачанный файл, если он не изменился (экономия места)
	rsync --stats --checksum --timeout=60 --block-size=40507 \
		--archive --delete \
		${prev:+"--link-dest=$PWD/$SCOPE_DIR/builds/$prev/"} \
		--exclude='/docker_image.tgz' \
		"${UPDATE_URL}/$PRODUCT/$VERSION_SCOPE/builds/$version/" "$dl_dir"
	rm -rf "$SCOPE_DIR/builds/${version}/"
	mv "$dl_dir" "$SCOPE_DIR/builds/${version}"
	return 0
}


run_update_download() {
	# Нужно скачать все промежуточные патчи, и саму версию.
	# Пока качаем всё сразу, но если будут проблемы с местом,
	#   то можно качать и накатывать по-одному.
	# Качаем "поверх" предыдущей версии, чтобы снизить объём.
	echo "Загрузим необходимые для обновления версии"
	local version prev=
	# download patches
	for version in "${PATCHES_LIST[@]}"; do
		download_version "$version" "$prev"
		prev="$version"
	done
	# download target version
	download_version "$VERSION" "$prev"
	return 0
}


create_archive() {
	local version archive_name="$DST_DIR/$PRODUCT-$INSTALLED_VERSION-$VERSION.tar.xz"
	local -a members=("$PRODUCT/$VERSION_SCOPE"/{current,deleted,official,patch,test})

	for version in "${PATCHES_LIST[@]}"; do
		members+=("$PRODUCT/$VERSION_SCOPE/builds/$version")
	done
	members+=("$PRODUCT/$VERSION_SCOPE/builds/$VERSION")

	tar -c --xz -f "$archive_name" -C "$DIST_CACHE_DIR" "${members[@]}"
	return 0
}


main() {
	INSTALLED_VERSION="$1"
	ARG_VERSION="${2:-}"
	DST_DIR="${3:-.}"

	if [[ ! $IS_LOCAL_DIST ]]; then
		init_distr_cache
	fi
	if [[ -f $DIST_CACHE_DIR/$PRODUCT/$VERSION_SCOPE/builds/$INSTALLED_VERSION/branch ]]; then
		BRANCH=$(<"$DIST_CACHE_DIR/$PRODUCT/$VERSION_SCOPE/builds/$INSTALLED_VERSION/branch")
	else
		BRANCH=release
	fi
	check_need_update
	calc_patches_list
	echo
	echo "Обновление до $VERSION, промежуточные версии: ${PATCHES_LIST[*]}" >&2
	if [[ ! $IS_LOCAL_DIST ]]; then
		check_disksize
		run_update_download
	fi
	create_archive
	if [[ ! $IS_LOCAL_DIST ]]; then
		echo "Remove local dist files $DIST_CACHE_DIR"
		rm -rf eva_dist  # == $DIST_CACHE_DIR
	fi

	return 0
}


main "$@"
echo "$0 $@ [$$] SUCCESS" >&2
exit 0
