#!/bin/sh
#
# Copyright (c) 2020, 2021  Peter Pentchev <roam@ringlet.net>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.


set -e

usage()
{
	cat <<'EOUSAGE'
Usage:	repopush [-Nv] local remote
	repopush [-Nv] [-f configfile] -s section
EOUSAGE
}

debug()
{
	[ -z "$v" ] || printf -- '%s\n' "$*" 1>&2
}

taste_dep_repo()
{
	local locpath="$1"
	if [ -d "$locpath/dists" ] && [ -d "$locpath/pool" ]; then
		echo 'deb'
	fi
}

taste_yum_repo()
{
	local locpath="$1"
	local arches valid_arches arch

	if [ ! -d "$locpath/SRPMS/Sources" ]; then
		return
	fi

	arches="$(cd -- "$locpath" && find . -mindepth 2 -maxdepth 2 -type d -path '*/repodata' \! -path './SRPMS/repodata' | cut -d/ -f2 | xargs -r)"
	if [ -z "$arches" ]; then
		return
	fi
	
	valid_arches=''
	for arch in $arches; do
		if [ -d "$locpath/$arch/Packages" ]; then
			valid_arches="$valid_arches $arch"
		fi
	done
	if [ -z "$valid_arches" ]; then
		return
	fi

	echo "yum$valid_arches"
}

taste_repo()
{
	local locpath="$1"
	local repo_params

	repo_params="$(taste_dep_repo "$locpath")"
	if [ -n "$repo_params" ]; then
		printf -- '%s\n' "$repo_params"
		return
	fi

	repo_params="$(taste_yum_repo "$locpath")"
	if [ -n "$repo_params" ]; then
		printf -- '%s\n' "$repo_params"
		return
	fi

	# *crickets*
}

rsync_step()
{
	local n="$1" ntotal="$2" desc="$3" path="$4" delete="$5"
	debug "- $n/$ntotal: $desc"
	# shellcheck disable=SC2086
	rsync $noop $v -az $delete -- "$locpath$path/" "$remote$path/"
}

sync_debrepo()
{
	local locpath="$1" remote="$2"
	echo "Syncing a Debian repository $locpath -> $remote"
	
	rsync_step 1 4 'pool' /pool ''
	rsync_step 2 4 'dists' /dists ''
	rsync_step 3 4 'all without --delete' '' ''
	rsync_step 4 4 'all with --delete' '' '--delete'
}

sync_yumrepo()
{
	local locpath="$1" remote="$2" arches="$3"
	echo "Syncing a Yum repository $locpath -> $remote ($arches)"
	
	rsync_step 1 6 'Sources' /SRPMS/Sources ''
	for arch in $arches; do
		rsync_step 2 6 "Packages $arch" "/$arch/Packages" ''
	done
	rsync_step 3 6 'source repodata' /SRPMS/repodata ''
	for arch in $arches; do
		rsync_step 4 6 "$arch repodata" "/$arch/repodata" ''
	done
	rsync_step 5 6 'all without --delete' '' ''
	rsync_step 6 6 'all with --delete' '' '--delete'
}

cfgfile="${XDG_CONFIG_HOME-$HOME/.config}/repopush.conf"
unset noop section v
while getopts 'f:Ns:v' o; do
	case "$o" in
		f)
			cfgfile="$OPTARG"
			;;

		N)
			noop='-n'
			;;

		s)
			section="$OPTARG"
			;;

		v)
			v='-v'
			;;

		*)
			usage 1>&2
			exit 1
			;;
	esac
done
shift "$((OPTIND - 1))"

if [ -z "$section" ]; then
	if [ "$#" -ne 2 ]; then
		usage 1>&2
		exit 1
	fi
	locpath="$1"
	remote="$2"
else
	if [ "$#" -ne 0 ]; then
		usage 1>&2
		exit 1
	fi
	debug "Looking for the '$section' section in $cfgfile"

	locpath="$(confget -f "$cfgfile" -s "$section" local)"
	if [ -z "$locpath" ]; then
		echo "Could not fetch $section.local from $cfgfile" 1>&2
		exit 1
	fi
	remote="$(confget -f "$cfgfile" -s "$section" remote)"
	if [ -z "$remote" ]; then
		echo "Could not fetch $section.remote from $cfgfile" 1>&2
		exit 1
	fi
fi

if [ "$locpath" != '/' ]; then
	locpath="${locpath%/}"
fi
if [ "$remote" != '/' ]; then
	remote="${remote%/}"
fi

if [ ! -d "$locpath" ]; then
	echo "Not a directory: $locpath" 1>&2
	exit 1
fi

repo_params="$(taste_repo "$locpath")"
if [ -z "$repo_params" ]; then
	echo "Cannot determine the type of the repository at $locpath" 1>&2
	exit 1
fi
repo_type="${repo_params%% *}"
repo_args="${repo_params#* }"
case "$repo_type" in
	deb)
		sync_debrepo "$locpath" "$remote"
		;;

	yum)
		sync_yumrepo "$locpath" "$remote" "$repo_args"
		;;

	*)
		echo "Internal error: repo params '$repo_params'" 1>&2
		exit 1
		;;
esac
