#!/bin/ash -euf
# -*- mode: Shell-script; tab-width: 8; fill-column: 70; -*- 
# $Id: rebuild,v 0.0.1 2006/04/26 17:52:17 legion Exp $ 

DEBUG=1
TEST=
NOMAILS=

export LC_ALL=C LANG=C LANGUAGE=C

. "${0%/*}/../config"
. "$dist/incominger/functions"

src="$incdir/rebuild"
dst="$incdir/unmets"

cur_arch="i586"
build_archs="i586 x86_64"
supported_archs="i386 i586 i686 x86_64 noarch"

stable_repo="/raid/ALT/Sisyphus"

srclist="$filesdir/$PROG/srclist"
nodesdir="$dist/incominger/nodes"
arch_srcdir="$filesdir/$PROG/arch-src"
sessdir="$filesdir/$PROG/sessions"
lockdir="$filesdir/$PROG/.lock"
logdir="$logsdir/$PROG"
MYGNUPGHOME=

report_done=1
report_failed=1

gensourceslist() {
    local a= alist=
    case "$1" in
	i686) alist="i586 i386" ;;
	i386) alist="i586" ;;
    esac
    for a in noarch $1 $alist; do
	printf %s\\n "rpm file:$2 $a classic"
    done
}

debug() {
    [ -n "$DEBUG" ] || return 0
    printf 'DEBUG: [%s]: %s\n' "$(now)" "$@"
}

writelog() {
    local node="$1" f="$2" arch="$3" status="$4"
    logit "$arch	$node	$f	$status"
    debug "$node: $fn: $status"
}

mklink() {
    [ "$#" -eq 2 ] || return 1
    local f d full_s full_d
    [ -f "$1" -a ! -f "$2/${1##*/}" ] || return 1
    full_s="$(readlink -ev "${1%/*}")"
    full_d="$(readlink -ev "$2")"
    r="$(relative "$full_s" "$full_d/")"
    ln -s "$r/${1##*/}" "$full_d/"
}

getsign() {
    local sign="$(GNUPGHOME="${MYGNUPGHOME:-/usr/lib/alt-gpgkeys}" rpmsign -Kv "$1")" || return 1
    if printf %s "$sign" |grep -qs '^gpg: Good signature'; then
	printf %s "$sign" |sed -ne 's/^gpg:.*[[:space:]]\(from\|aka\)[^<]\+<\([^@]\+\)[^>]\+>.*$/\2/p' |sort -u |head -1
	return 0
    fi
    return 1
}

count() {
    printf %d "$#"
}

merge() {
    [ "$#" -eq 2 ] || return 0
    local e=
    for e in $1; do
	in_list "$e" "$2" && printf '%s ' "$e"
    done
    printf \\n
}

buildarch() {
    local a= f= rpm_exclusivearch= rpm_excludearch= rpm_buildarch= rpm_buildarchs= values=
    while read f; do
	[ -n "$f" -a "$f" != "${f%.src.rpm}" ] || continue
	f="$(readlink -ve "$f")" || continue

	if ! values="$(LC_ALL=C rpmquery -p --qf='
rpm_exclusivearch=\"[%{EXCLUSIVEARCH} ]\";
rpm_excludearch=\"[%{EXCLUDEARCH} ]\"; 
rpm_buildarchs=\"[%{BUILDARCHS} ]\";
	' -- "$f")"; then
	    error "Error: rpmuery failed!"
	    continue
	fi
	rpm_exclusivearch= rpm_excludearch= rpm_buildarchs=
	eval "$values"
	unset values

	printf %s\  "${f##*/}"

	# Only first element will be used by rpm.
	rpm_buildarch="${rpm_buildarchs%% *}"

	# NOARCH package we may build anywhere.
	[ "$rpm_buildarch" = "noarch" ] && printf %s\\n "$build_archs" && continue

	out_validarchs=
	for a in $supported_archs; do
	    [ "$a" != "noarch" ] || continue
	
	    # BUILDARCH equivalent `rpm --target ...`
	    [ -n "$rpm_buildarch" ] && [ "$a" = "$rpm_buildarch" ] && out_validarchs="$a" && break

	    # Exclude ARCH check: we must ignore architecture if current architecture in list of excluded architectures.
	    [ -n "$rpm_excludearch" ] && in_list "$a" $rpm_excludearch && continue

	    # Exclusive ARCH check: ignore this architecture if current architecture not in list of exclusive architectures.
	    [ -n "$rpm_exclusivearch" ] && ! in_list "$a" $rpm_exclusivearch && continue

	    in_list "$a" $build_archs && out_validarchs="$out_validarchs $a"
	done
	[ -n "$out_validarchs" ] && 
		printf %s\\n "$out_validarchs" || 
	    	{ printf \\n; error "The package cannot be build on supported architectures: $fn"; }
    done
}

WORKDIR=
lock_handler() {
    local rc=$?
    trap - EXIT
    find "$sessdir" "$arch_srcdir" -mindepth 1 -maxdepth 1 -type d -exec rm -rf -- \{\} \+
    [ ! -d "$WORKDIR" ] || rm -rf -- "$WORKDIR"
    rm ${DEBUG:+-v} -rf -- "$lockdir"
    exit $rc
}

[ -n "$(find "$src" -mindepth 1 -maxdepth 1 -name '*\.src\.rpm' -print -quit)" ] || exit 0

trap lock_handler HUP PIPE INT QUIT TERM EXIT
if ! mkdir ${DEBUG:+-v} -p -- "$lockdir"; then
    trap - HUP PIPE INT QUIT TERM EXIT
    printf "Process is already running.\n" >&2
    exit 0
fi
WORKDIR="$(mktemp -dt $PROG.XXXXXXXXXX)"
{
    cd "$WORKDIR"
    altgpgkeys="$(find "$stable_repo/files/$cur_arch/RPMS/" -name "alt-gpgkeys-*\.$cur_arch\.rpm" | head -1)"
    if [ -f "$altgpgkeys" ]; then
	debug "auth: $altgpgkeys"
	rpm2cpio "$altgpgkeys" |cpio -id
	MYGNUPGHOME="$(find "$WORKDIR/" -name 'gpg.conf' -printf %h\\n -quit 2>&1)"
	[ -d "$MYGNUPGHOME" ] || { error "alt-gpgkeys not found: sistem installed will be uesd"; MYGNUPGHOME=; }
    else
	error "alt-gpgkeys not found: sistem installed will be used"
    fi
    cd - >/dev/null
}

"$helpers/dups" "$src"
[ -d "${logfile%/*}" ] || mkdir -p "${logfile%/*}"
find "$src" -name '*\.src\.rpm' > "$srclist"

allnode_archs="$(
find "$nodesdir" -name '*\.conf' |
while read conf; do 
	. $conf
	[ -n "$node_serv" ] || continue
	ssh -n "$node_serv" -- "rpm --showrc |sed -ne 's/^compatible build archs[[:space:]]*:[[:space:]]*//p'" ||:
done |tr \  \\n |sort -u
)"
nodes_archs="$(merge "$supported_archs" "$allnode_archs")"
[ $(count "$supported_archs") -eq $(count "$nodes_archs") ] || 
	fatal "Unsupport architectures was found: $supported_archs"

debug "We divide packages into different available architectures"
cat "$srclist" |buildarch |
while read f target_archs; do
    for a in $target_archs; do
	mkdir -p -- "$arch_srcdir/$a"
	mklink "$src/$f" "$arch_srcdir/$a" || error "Unable make symlink: $f -> $arch_srcdir/$a"
	debug "[$a] $f"
    done
done

debug "We divide packages into different mantainers"
for a in $supported_archs; do
    [ -d "$arch_srcdir/$a" ] || continue
    find "$arch_srcdir/$a" -name '*\.src\.rpm' |
    while read f; do
	sess="$(getsign "$f")" && [ -n "$sess" ] || { error "Unable get signature name: $f"; continue; }
	mkdir -p -- \
	    "$sessdir/$a/$sess/repo/$a/RPMS.hasher" \
	    "$sessdir/$a/$sess/repo/SRPMS.hasher" \
	    "$sessdir/$a/$sess/src" \
	    "$sessdir/$a/$sess/log" \
	    #
	rpmquery -p --qf='%{BUILDTIME}\t%{NAME}-%{VERSION}-%{RELEASE}.src.rpm\n' -- "$f" >>"$sessdir/$a/$sess/buildline"
	mklink "$f" "$sessdir/$a/$sess/src" || error "Unable make symlink: $f -> $sessdir/$a/$sess/src"
	debug "[$a] [$sess] $f"
    done
done
find "$sessdir" -type f -name 'buildline' -exec sort -o \{\} -- \{\} \;

for conf in $(find "$nodesdir" -name "*\.conf"); do
    {
	node_serv= node_prefix=
	. "$conf"
	
	debug "$node_serv: up"
	tmpd="$(ssh -n "$node_serv" -- printf %s "\$TMPDIR")"
	[ -n "$tmpd" ] || fatal "Unable get TMPDIR from node: $node_serv"

	node_archs="$(ssh -n "$node_serv" -- "rpm --showrc |sed -ne 's/^compatible build archs[[:space:]]*:[[:space:]]*//p'")"
	node_supportarchs="$(merge "$node_archs" "$supported_archs")"
	[ -n "$node_supportarchs" ] || 
		{ error "Supported archs not found: $node_serv: $node_archs"; continue; }

	for a in $node_supportarchs; do
	    [ -d "$sessdir/$a" ] || continue
	    [ -n "$(find "$sessdir/$a/" -regextype posix-basic -regex '.*/src/[^/]\+\.src\.rpm$' -print -quit)" ] || continue

	    ssh "$node_serv" -- "cat >\"$tmpd/apt.conf\"" <<EOF
Dir::Etc::SourceList "$tmpd/sources.list";
EOF
	    ssh "$node_serv" -- "cat >\"$tmpd/sources.list\"" <<EOF
$(gensourceslist "$a" "$node_prefix$stable_repo")
EOF
	    ssh "$node_serv" -- "cat >\"$tmpd/build\"" <<EOF
#!/bin/sh -euf
rc=0;
hsh -v --nprocs=1 --target="$a" --apt-config="$tmpd/apt.conf" --mountpoints=/proc --no-repackage-source -- "$tmpd" "$tmpd/\$1" || 
rc=\$?; rm -f -- "$tmpd/\$1"; exit \$rc;
EOF
	    ssh -n "$node_serv" -- "chmod +x -- \"$tmpd/build\";
				    hsh --cleanup -- \"$tmpd\";"

	    find "$sessdir/$a" -mindepth 1 -maxdepth 1 -type d |
	    while read sess; do
		mkdir -- "$sess/.lock" >/dev/null 2>&1 || continue

		unbuilt=0
		while :; do
		    prev_unbuilt="$unbuilt"

		    while read buildtime fn; do # buildtime unused variable
			f="$sess/src/$fn"
			[ -f "$f" ] || continue
			rsync \
			    ${DEBUG:+--log-format="DEBUG: [$(now)]: $node_serv:$tmpd/ %i %n%L"} \
			    -ptL "$f" "$node_serv:$tmpd/" || fatal "Unable to send file to build node"

			log="$sess/log/$fn"
			if ssh -n "$node_serv" -- "$tmpd/build \"$fn\"" >"$log" 2>&1; then
			    rsync -a "$node_serv:$tmpd/repo/$a/RPMS.hasher/" "$sess/repo/$a/RPMS.hasher/" ||
			    	{ error "rsync from '$node_serv' failed: $fn"; continue; }
			    rsync -rptL "$f" "$sess/repo/SRPMS.hasher/"
			    
			    # We should remove the old log from previous iteration.
			    [ ! -f "$log.failed" ] || rm -f -- "$log.failed"
			    
			    mv -f -- "$log" "$log.done"
			    writelog "$node_serv" "$fn" "$a" "Success"
			else
			    rc=$?
			    [ "$rc" -ne 255 ] || fatal "ssh into the '$node_serv' exits with the exit status 255, aborting on $fn"
			    
			    mv -f -- "$log" "$log.failed"
			    if grep -iqs '^E: \(Couldn.t find package \|Version .* for .* was not found\)' "$log.failed"; then
				writelog "$node_serv" "$fn" "$a" "Failed: Package or version was not found."
				continue
			    fi
			    writelog "$node_serv" "$fn" "$a" "Failed"
			fi
			rm -f -- "$f"
		    done < "$sess/buildline"
		    
		    unbuilt="$(find "$sess/src" -name '*.src.rpm' |wc -l)"
		    [ "$unbuilt" -gt 0 ] || break
		    [ "$prev_unbuilt" -eq 0 -o "$unbuilt" -lt "$prev_unbuilt" ] || break
		done
	    done
	    ssh -n "$node_serv" -- "hsh --cleanup -- \"$tmpd\";
				    rm -f -- \"$tmpd/apt.conf\" \"$tmpd/sources.list\" \"$tmpd/build\";"
	done
	debug "$node_serv: down"
    } &
done
wait

debug "Final repository building ..."
for a in $supported_archs; do
    [ -d "$sessdir/$a" ] || continue
    find "$sessdir/$a/" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' |
    while read s; do
	mkdir -p -- "$dst/$a/RPMS.$s" "$dst/SRPMS.$s"
	rsync -a --remove-sent-files "$sessdir/$a/$s/repo/$a/RPMS.hasher/" "$dst/$a/RPMS.$s/"
	rsync -a --remove-sent-files "$sessdir/$a/$s/repo/SRPMS.hasher/" "$dst/SRPMS.$s/"
    done
    genbasedir --create --no-oldhashfile --topdir="$dst" "$a" ||:
done

debug "Prepare info ..."
for a in $supported_archs; do
    [ -d "$sessdir/$a" ] || continue

    mkdir -p -- "$logdir/$a"    
    find "$sessdir/$a/" -regextype posix-basic -regex '.*/log/[^/]\+\.src\.rpm\.[^/]\+$' |
    while read log; do
	fn= suffix=

	for suffix in "done" "failed"; do
	    [ "$log" != "${log%.$suffix}" ] || continue
	    fn="${log%.$suffix}"
	    fn="${fn##*/}"
	    break
	done
	[ -n "$fn" ]      || { error "Unknown log file type: $log"; continue; }
	[ -f "$src/$fn" ] || { error "Source rpm not found: $src/$fn"; continue; }

	rpm_name="$(rpmquery -p --qf='%{NAME}' -- "$src/$fn")" || { error "Unable get rpm name: $src/$fn"; continue; }
	mv -f -- "$log" "$logdir/$a/$rpm_name.$suffix.log"

	[ -z "$NOMAILS" ] || continue
	eval "[ -n \"\$report_$suffix\" ]" || continue

	if ! rpmquery -p --qf='
rpm_name=%{NAME:shescape};
rpm_version=%{VERSION:shescape};
rpm_release=%{RELEASE:shescape};
	' -- "$src/$fn" > "$logdir/$a/$rpm_name.$suffix.values"; then
	    error "Unable get rpm values: $src/$fn"
	    rm -f -- "$logdir/$a/$rpm_name.$suffix.values"
	    continue
	fi
	"$helpers/getowner" --gnupghome="$WORKDIR/usr/lib/alt-gpgkeys" "$src/$fn" |
	head -n1 > "$logdir/$a/$rpm_name.$suffix.info"
    done
done

cat "$srclist" |xargs -r ${TEST:+echo} rm ${DEBUG:+-v} -f --

if [ -z "$NOMAILS" ]; then
    debug "Sending doom mails ..."
    for a in $supported_archs; do
	[ -d "$logdir/$a" ] || continue
	for suffix in "done" "failed"; do
	    find "$logdir/$a/" -name "*.$suffix.info" -exec "$helpers/sendmails" ${TEST:+-T} -m "rebuild_$suffix" -t "$a" -- \{\} \;
	done
    done
else
    find "$logdir/" -name '*.log' -exec bzip2 -f -- \{\} \;
    find "$logdir/" \( -name '*.info' -o -name '*.values' \) -delete
fi
