#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
source "$(dirname "$0")/lib/ulib/ulib" || exit 1
urequire DEFAULTS woinst

function display_help() {
    uecho "$scriptname: Déploiement distant avec woinst

USAGE
    $scriptname [-H host] [-T tmproot] <file|archive|dir>... [-- options de woinst]

OPTIONS
    -T, --tmproot TMPROOT
        Spécifier le répertoire temporaire sur l'hôte distant, comme par exemple
        /var/tmp. Cette option est utile pour les vservers, qui ont par défaut
        un /tmp minuscule de 16 Mo.
    -S, --ssh SSH
        Spécifier le programme à utiliser pour la connection par ssh.
    -h, --host hosts
    -h, --host @hostsfile
    -H host
        Spécifier un ou plusieurs hôtes sur lequels faire le déploiement. Pour
        spécifier plusieurs hôtes, il est possible d'utiliser plusieurs fois
        l'option -h, ou spécifier en une seule fois plusieurs hôtes en les
        séparant par un espace ou le caractère ':', e.g. 'host1 host2' ou
        'host1:host2'. Si la spécification contient les caractères { et },
        l'expansion est effectuée, e.g
            -h 'root@{host1,host2}.univ.run'
        Par défaut, la connexion sur l'hôte distant se fait avec l'utilisateur
        root. Il est possible de spécifier un autre utilisateur avec la syntaxe
        user@host, e.g -h user@host
        La forme @hostsfile permet de lire la liste des hôtes depuis le fichier
        hostsfile, à raison d'un hôte par ligne.
    --deploy
    --no-deploy
        Autoriser (respectivement interdire) l'utilisation de la configuration
        locale de déploiement pour identifier la destination si aucun hôte n'est
        spécifié.
        Par défaut, si aucun hôte n'est spécifié, la configuration locale de
        déploiement est interrogée pour avoir cette information.
    -c, --deploy-config CONFNAME
        Cette option permet de spécifier le nom de la configuration à utiliser
        pour effectuer la requête. Par défaut, utiliser le nom 'rwoinst'"
}

__PARSED_HOSTS=()
__PARSED_FILES=()
function parse_hostsfile() {
    # Lire chacun des fichiers $* et initialiser __PARSED_HOSTS avec la liste
    # des hôtes mentionnés dans les fichiers.
    local inputfile basedir inputs input
    for inputfile in "$@"; do
        inputfile="$(abspath "$inputfile")"
        array_contains __PARSED_FILES "$inputfile" && {
            ewarn "$(ppath "$inputfile"): inclusion récursive"
            continue
        }
        array_add __PARSED_FILES "$inputfile"
        basedir="$(dirname "$inputfile")"

        array_from_lines inputs "$(<"$inputfile" filter_conf)" || {
            ewarn "$inputfile: fichier ingnoré"
            continue
        }
        for input in "${inputs[@]}"; do
            if [ "${input#@}" != "$input" ]; then
                # fichier inclus
                parse_hostsfile "$(abspath "${input#@}" "$basedir")"
            else
                array_addu __PARSED_HOSTS "$input"
            fi
        done
    done
}
function __expand_braces() {
    if [[ "$1" == *{* ]] && [[ "$1" == *}* ]]; then
        eval "echo $1"
    else
        echo "$1"
    fi
}
function __dot_is_localhost() { [ "$1" == "." ] && echo "localhost" || echo "$1"; }
function fix_hosts() {
    # Si hosts contient des éléments multiple, comme a:b, séparer ces
    # éléments. i.e (a b:c) --> (a b c)
    # Supporter la syntaxe @hostsfile qui permet de charger la liste des hôtes
    # depuis un fichier.
    # Remplacer aussi les '.' par 'localhost'
    array_map hosts __expand_braces
    array_fix_paths hosts ":"
    array_fix_paths hosts " "

    local -a _hosts _tmphosts host
    for host in "${hosts[@]}"; do
        host="${host%/}"
        if [ "${host#@}" != "$host" ]; then
            __PARSED_HOSTS=()
            parse_hostsfile "${host#@}"
            array_fix_paths __PARSED_HOSTS
            array_extendu _hosts __PARSED_HOSTS
        else
            array_addu _hosts "$host"
        fi
    done
    array_copy hosts _hosts
    array_map hosts __dot_is_localhost
}

action=deploy
tmproot=
SSH=
hosts=()
deploy_enable=1
deploy_confname=rwoinst
parse_opts "${PRETTYOPTS[@]}" \
    --help '$exit_with display_help' \
    -T:,--tmproot: tmproot= \
    -S:,--ssh: SSH= \
    -h:,-H:,--host: hosts \
    --deploy deploy_enable=1 \
    --no-deploy deploy_enable= \
    -c:,--deploy-config deploy_confname= \
    @ args -- "$@" && set -- "${args[@]}" || die "$args"

: "${SSH:=ssh}"

## Bundle à déployer
if [ $# -eq 0 ] || [[ "$1" == -* ]] || [[ "$1" == *=* ]]; then
    # pas d'argument, ou c'est une option (qui fait donc partie des arguments de
    # woinst)
    case "$(dirname "$(pwd)")" in
    *.woa|*.framework) bundle=.;;
    *) bundle=;;
    esac
    read_value "Veuillez entrer le bundle à déployer" bundle "$bundle" O
    set -- "$bundle" "$@"
fi

## Hôtes sur lesquels faire le déploiement
if array_isempty hosts && [ -n "$deploy_enable" ]; then
    urequire deploy
    deploy_setconf "$deploy_confname"
    if deploy_loadconf; then
        setxx wobundle=abspath "$1" // basename --
        if eval "$(deploy_query -v host DEST wobundle rwoinst_bundle "" shell "$wobundle")"; then
            check_interaction -c && einfo "Ce bundle sera déployé vers les hôtes suivants:
$(array_to_lines host "" "    ")"
            ask_any "Voulez-vous continuer?" Oq || die
            array_copy hosts host
        fi
    fi
fi
array_isempty hosts && read_value "Entrez une liste d'hôtes séparés par ':'" hosts "localhost"
fix_hosts

## Création de l'archive
etitle "Création du répertoire de travail"
ac_set_tmpdir workdir
bundles=()
while [ -n "$1" ]; do
    if [ "$1" == "--" ]; then
        # début des options de woinst
        shift
        break
    elif [[ "$1" == -* ]] || [[ "$1" == *=* ]]; then
        # début des options de woinst
        break
    fi

    src="$1"; shift
    if [ -f "$src" ] && is_archive "$src"; then
        estep "$(ppath "$src")"
        cp_R "$src" "$workdir" || die
    elif [ -d "$src" ]; then
        src="$(abspath "$src")"
        if endswith "$src" .framework; then
            is_wofwkdir "$src" || die "Framework invalide: $(ppath "$src")"
        elif endswith "$src" .woa; then
            is_woappdir "$src" || die "Application invalide: $(ppath "$src")"
        else
            die "N'est pas un bundle valide: $(ppath "$src")"
        fi
        estep "$(ppath "$src")"
        cp_R "$src" "$workdir" || die
    else
        die "Fichier ou répertoire introuvable: $src"
    fi

    src="$(abspath "$src")"
    array_add bundles "$(basename "$src")"
done

estep "Copie de l'environnement de déploiement"
ulibsync "$workdir"
echo '#!/bin/sh
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
if . `dirname "$0"`/ulib/ulibsh; then
    urequire DEFAULTS woinst
else
    exit 1
fi
OENC="$UTF8"
woinst "$@"' >"$workdir/woinst"
chmod +x "$workdir/woinst"

eend

ac_set_tmpfile archive
archivename="$(basename "$archive")"

etitle "Création de l'archive pour le déploiement" \
    "$scriptdir/mkusfx" --bare --tmp-archive -o "$archive" "$workdir" -- ./woinst --is-tmpdir "${bundles[@]}" || die

## Déploiement

# sur l'hôte distant, ne rendre interactif qu'à partir de -yy
rinteraction=$__interaction
[ $rinteraction -lt 2 ] && rinteraction=$(($rinteraction + 1))

for host in "${hosts[@]}"; do
    if [ "$host" == "localhost" ]; then
        etitle "Déploiement sur l'hôte local" \
            "$archive" ${tmproot:+--tmproot "$tmproot"} -- "$@"
    else
        if [[ "$host" == *@* ]]; then
            user="${host%%@*}"
            host="${host#*@}"
        else
            user=root
        fi

        etitle "Déploiement sur $user@$host"
        estep "Copie de l'archive"
        scp -S "$SSH" "$archive" "$user@$host:" || die
        estep "Lancement du script de déploiement"
        "$SSH" -qt "$user@$host" "\
__interaction=$rinteraction
__estack=$(qval "$__estack")
__tlevel=$(qval "$__tlevel")
export __interaction __estack __tlevel
${UTOOLS_LANG:+UTOOLS_LANG='$UTOOLS_LANG'; export UTOOLS_LANG
}$(qvals "./$archivename" ${tmproot:+--tmproot "$tmproot"} -- MYHOST="$host" "$@")"
        eend
    fi
done