##@cooked comments # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
## Déploiement de bundle WebObjects
##@cooked nocomments
##@require ulib
##@require base
##@require prefixes
##@require apache
##@require webobjects
uprovide woinst
urequire ulib base prefixes apache webobjects

function date2version() {
    local date="$1"
    dd="$(expr "$date" : "\(..\)")"; [ -z "$dd" ] && dd="$(date +%d)"
    mm="$(expr "$date" : "../\(..\)")"; [ -z "$mm" ] && mm="$(date +%m)"
    yyyy="$(expr "$date" : "../../\(....\)")"; [ -z "$yyyy" ] && yyyy="$(date +%Y)"
    yy="$(expr "$yyyy" : "..\(..\)")"
    echo "$yyyy$mm$dd"
}

function woconf() {
    function __woconf_display_help() {
        uecho "woctl configure: configurer une application ou un framework

USAGE
    configure <app.woa|fwk.framework> [cmds...]

cmds est soit un unique argument avec une commande par ligne, soit une suite de commandes séparées par //

COMMANDES
    # comment
        Les commentaires sont ignorés
    v[erify]
        Vérifier le corriger la consistance du bundle.
        Cette action est effectuée automatiquement sauf si l'option --no-verify
        est spécifiée.
    set_config configfile
    c>configfile
        Changer le fichier de configuration par défaut
    set_config configfile srcfile
    c>configfile:srcfile
        Créer le fichier configfile en le copiant depuis srcfile, et en faire le
        nouveau fichier de configuration par défaut
    set_property prop value [configfile]
    c[configfile/]prop=value
        Modifier une propriété dans le fichier de configuration spécifié.
        Utiliser le fichier de configuration par défaut si configfile n'est pas
        spécifié.
    set_eomodel eomodel
    e>eomodel
        Changer l'eomodel par défaut
    set_dbconfig dbConfig [eomodel]
    e[eomodel/]dbConfig
        Activer la configuration dbConfig dans l'eomodel spécifié. Utiliser
        l'eomodel par défaut si eomodel n'est pas spécifié.
    add Fwk.framework
    aFwk.framework
        Ajouter un framework au classpath de l'application
    remove Fwk.framework
    rFwk.framework
        Enlever un framework du classpath de l'application
    replace before after
    sbefore:after
        Faire un recherche/remplace dans les fichiers de classpath de
        l'application.
    f[ix_case]
        Corriger la casse dans les fichiers de classpath de l'application, selon
        les frameworks effectivement installés. En effet, un déploiement qui
        peut fonctionner sur MacOSX, dont le système de fichier n'est pas
        sensible à la casse, peut cesser de fonctionner sur Linux, parce que le
        nom du jar ne correspond pas."
    }

    local noverify bundle cmds cmd
    noverify=
    parse_opts "${PRETTYOPTS[@]}" \
        --help '$exit_with __woconf_display_help' \
        --noverify,--no-verify noverify=1 \
        @ args -- "$@" && set -- "${args[@]}" || die "$args"

    bundle="$1"; shift
    [ -n "$bundle" ] || {
        eerror "Il faut spécifier le bundle à configurer"
        return 1
    }
    if is_wobundle "$bundle" && is_woappdir "$bundle"; then
        :
    elif is_wobundle "$bundle" && is_wofwkdir "$bundle"; then
        :
    else
        eerror "N'est pas un bundle valide: $bundle"
        return 1
    fi
    bundle="$(abspath "$bundle")"
    [ -w "$bundle" ] || die "Bundle non accessible en écriture"

    if [ $# -le 1 ]; then
        array_from_lines cmds "$(filter_comment <<<"$1")"
    else
        cmds=()
        cmd=()
        while [ -n "$1" ]; do
            if [ "$1" == // ]; then
                if [ -n "${cmd[*]}" ]; then
                    cmds=("${cmds[@]}" "$(quoted_args "${cmd[@]}")")
                fi
                cmd=()
            else
                cmd=("${cmd[@]}" "$1")
            fi
            shift
        done
        if [ -n "${cmd[*]}" ]; then
            cmds=("${cmds[@]}" "$(quoted_args "${cmd[@]}")")
        fi
    fi

    etitle "Configuration de $(ppath "$bundle")"
    local verify_done first=1
    [ -z "$noverify" ] && {
        estep verify
        verifix_bundle "$bundle"
        verify_done=1
    }
    for cmd in "${cmds[@]}"; do
        eval "set -- $cmd"
        if [ $# -eq 1 ]; then
            # commandes abrégées sur une lettre
            case "$1" in
            v|verify|verifix)
                # si v est en premier, et que la vérification a déjà été faite,
                # alors on peut l'ignorer
                if [ -z "$first" -o -z "$verify_done" ]; then
                    estep "verify"
                    verifix_bundle "$bundle"
                fi
                ;;
            f|fix_case|fix-case)
                estep "fix-case"
                fix_jars_case "$bundle"
                ;;
            c*)
                eerror "set_config / set_property ne sont pas implémentés"
                ;;
            e*)
                eerror "set_eomodel / set_dbconfig ne sont pas implémentés"
                ;;
            a*)
                local framework="${1#a}"
                estep "add $framework"
                add_framework "$bundle" "$framework"
                ;;
            r*)
                local framework="${1#r}"
                estep "remove $framework"
                remove_framework "$bundle" "$framework"
                ;;
            s*)
                local search replace
                replace="${1#s}"
                search="${replace%%:*}"
                replace="${replace#*:}"
                if [ -n "$replace" ]; then
                    estep "Replace in classpath $search --> $replace"
                else
                    estep "Delete in classpath $search"
                fi
                searchreplace_classpath "$bundle" "$search" "$replace"
                ;;
            *) eerror "Commande invalide: $1";;
            esac
        else
            # commandes "user-friendly"
            case "$1" in
            conf|config|set_conf|set_config)
                eerror "set_config n'est pas implémenté"
                ;;
            prop|property|set_prop|set_property)
                eerror "set_property n'est pas implémenté"
                ;;
            eomodel|set_eomodel)
                eerror "set_eomodel n'est pas implémenté"
                ;;
            dbconf|dbconfig|set_dbconf|set_dbconfig)
                eerror "set_dbconfig n'est pas implémenté"
                ;;
            add|add_fwk|add_framework)
                estep "add $2"
                add_framework "$bundle" "$2"
                ;;
            remove|remove_fwk|remove_framework)
                estep "remove $2"
                remove_framework "$bundle" "$2"
                ;;
            repl|replace)
                if [ -n "$3" ]; then
                    estep "Replace in classpath $2 --> $3"
                else
                    estep "Delete in classpath $2"
                fi
                searchreplace_classpath "$bundle" "$2" "$3"
                ;;
            *) eerror "Commande invalide: $*";;
            esac
        fi
        first=
    done
    eend
}

function wotag() {
    function __wotag_display_help() {
        uecho "woctl tag: attacher une information de version à un bundle

USAGE
    tag [options] <bundle>

OPTIONS
    -c  Vérifier si des informations de versions existent dans le bundle
        spécifié.
    -s  Afficher les informations de versions attachées
    -v VERSION
        Spécifier la version, ou '-' s'il n'y a pas de version
    -d DATE
        Spécifier la date de release
    -m DESC
        Spécifier une description du bundle

Si aucune des options -v, -d, -m n'est spécifiée, alors tenter de prendre les
informations de version et la date de release depuis le fichier VERSION.txt
situé dans le répertoire de resources.
Sinon, il faut saisir manuellement les informations."
    }

    local action newversion newdate newdesc
    local version date desc release
    local bundlename bundle found
    action=tag
    newversion=
    newdate=
    newdesc=
    bundlename=
    parse_opts "${PRETTYOPTS[@]}" \
        --help '$exit_with __wotag_display_help' \
        -c action=check \
        -s action=show \
        -v:,--version: newversion= \
        -d:,--date: newdate= \
        -m:,--desc: newdesc= \
        --message: bundlename= \
        @ args -- "$@" &&
    set -- "${args[@]}" || {
        eerror "$args"
        return 1
    }

    bundle="$1"
    [ -n "$bundle" ] || {
        eerror "Il faut spécifier le bundle à tagger"
        return 1
    }
    if [ ! -d "$bundle" ]; then
        if [ "$action" == "check" ]; then
            return 1
        elif [ "$action" == "show" ]; then
            [ -z "$bundlename" ] && bundlename="$(basename "$bundle")"
            eecho "$bundlename: not found"
            return 0
        else
            eerror "Bundle non trouvé: $bundle"
            return 1
        fi
    elif is_wobundle "$bundle" && is_woappdir "$bundle"; then
        :
    elif is_wobundle "$bundle" && is_wofwkdir "$bundle"; then
        :
    else
        eerror "N'est pas un bundle valide: $bundle"
        return 1
    fi
    bundle="$(abspath "$bundle")"
    [ -z "$bundlename" ] && bundlename="$(basename "$bundle")"

    local infofile="$(get_infofile "$bundle")"
    local jawotoolsfile="$(get_jawotoolsfile "$bundle")"
    local jawotoolsfile2="$bundle/jawotools.properties"
    local versionfile="$(get_versionfile "$bundle")"

    if [ "$action" == "check" ]; then
        [ -f "$jawotoolsfile" ] || return 1
        read_jawotoolsfile "$jawotoolsfile" version date
        [ -n "$version" -o -n "$date" ] && return 0
        return 1

    elif [ "$action" == "show" ]; then
        found=
        if [ -f "$jawotoolsfile" ]; then
            read_jawotoolsfile "$jawotoolsfile" version date desc
            if [ -n "$version" -o -n "$date" ]; then
                eecho "$bundlename: Version${version:+ "$version"}${date:+ du "$date"}${desc:+
("$desc")}"
                found=1
            fi
        elif [ -f "$infofile" ]; then
            read_infofile "$infofile" version release
            if [ -n "$version" ]; then
                eecho "$bundlename: Version $version${release:+ ("$release")}"
                found=1
            elif [ -n "$release" ]; then
                eecho "$bundlename: Release $release"
                found=1
            fi
        fi
        [ -n "$found" ] || eecho "$bundlename: no version found"

    elif [ "$action" == "tag" ]; then
        # lire les valeurs actuelles
        if [ -f "$jawotoolsfile" ]; then
            read_jawotoolsfile "$jawotoolsfile" version date desc
        fi

        # si le fichier de version existe, lire la version et la date de release
        if [ -z "$newversion" -a -z "$newdate" -a -f "$versionfile" ]; then
            newversion="$(awk <"$versionfile" '{
              gsub("-r../../....$", "")
              print
              exit
            }')"
            newdate="$(awk <"$versionfile" '{
              if ($0 ~ /.*-r..\/..\/....$/) {
                gsub(".*-r", "")
                print
              }
              exit
            }')"
        fi
        # si le fichier Info.plist existe, y lire les informations de version
        if [ -z "$newversion" -a -z "$newdate" -a -f "$infofile" ]; then
            read_infofile "$infofile" newversion release
            [ -n "$newversion" ] || newversion="$release"
        fi

        # nous n'avons pas trouvé de version, il faut saisir les valeurs à la main
        if [ -z "$newversion" -o -z "$newdate" ]; then
            [ "$newversion" == "-" ] && newversion=
            [ "$newdate" == "-" ] && newdate=

            defversion="${newversion:-$version}"
            read_value "Entrez la version (vide s'il n'y a que la date de release)" newversion "$defversion" N
            defdate="${newdate:-${date:-$(date +"%d/%m/%Y")}}"
            read_value "Entrez la date de release" newdate "$defdate"
            defdesc="${newdesc:-$desc}"
            read_value -i "Entrez une description du bundle" newdesc "$defdesc" N
        fi
        [ "$newversion" == "-" ] && newversion=
        [ "$newdate" == "-" ] && newdate=

        [ -n "$newversion" -o -n "$newdate" ] || {
            eerror "Il faut spécifier le numéro de version et/ou la date de release"
            return 1
        }

        eecho "$bundlename: Version${newversion:+ "$newversion"}${newdate:+ du "$newdate"}${newdesc:+
("$newdesc")}"
        if [ "$newversion" != "$version" -o "$newdate" != "$date" -o "$newdesc" != "$desc" ]; then
            [ "$jawotoolsfile" == "$jawotoolsfile2" ] || rm -f "$jawotoolsfile"
            save_jawotoolsfile "$jawotoolsfile2" "$newversion" "$newdate" "$newdesc"
            write_infofile "$infofile" "$newversion" "$(date2version "$newdate")"
        else
            einfo "Aucune modification effectuée"
        fi
    fi
}

WOINST_TMPDIR_SET=
function __woinst_set_tmpdir_maybe() {
    if [ -z "$WOINST_TMPDIR_SET" ]; then
        ac_set_tmpdir tmpdir
        WOINST_TMPDIR_SET=1
    fi
}

function __woinst_expand_archive() {
    local archdir contents
    __woinst_set_tmpdir_maybe

    estep "Décompactage de $(ppath "$src") dans $tmpdir..."
    archdir="$tmpdir/$(get_archive_appname "$src")"
    mkdir -p "$archdir"
    extract_archive "$src" "$archdir" || return 1

    # s'il n'y a qu'un répertoire dans l'archive, le considérer comme le
    # répertoire à déployer.
    array_from_lines contents "$(list_all "$archdir")"
    if [ "${#contents[*]}" -eq 1 -a -d "$archdir/$contents" ]; then
        archdir="$archdir/$contents"
    fi

    src="$archdir"
}

function woinst() {
    function __woinst_display_help() {
        uecho "$scriptname: Déployer un bundle (application ou framework) de WebObjects

USAGE
    $scriptname [options] <file|archive|dir>...

OPTIONS
    PREFIX=value
        Spécifier une valeur pour un préfixe, plutôt que de laisser uprefix
        l'autodétecter. Utiliser uprefix -l pour une liste de préfixes valides.
    -b  Redémarrer les instances en mode bounce.
        Par défaut, les instances sont arrêtées avant le déploiement, et
        redémarrées après
    -W  Ne déployer que les resources web. Implique -n
    -n  Ne pas tagger les bundles déployés avec un numéro de version. Par
        défaut, l'utilisateur est invité à compléter des informations telles
        que n° de version et date de release si ces informations ne sont pas
        disponible.
    -x CMD
        Exécuter la commande CMD après avoir effectué le déploiement"
    }

    local bounce webinst notag scripts istmpdir tmpdir
    scripts=()
    parse_opts "${PRETTYOPTS[@]}" \
        --help '$exit_with __woinst_display_help' \
        -b,--bounce bounce=1 \
        -W,--webres webinst=1 \
        -n,--notag notag=1 \
        -x:,--exec: scripts \
        --is-tmpdir '$istmpdir=1; tmpdir=.' \
        @ args -- "$@" &&
    set -- "${args[@]}" || {
        eerror "$args"
        return 1
    }

    # Tout d'abord énumérer les frameworks et applications à installer
    # Lire aussi les définitions de variables
    compute_apache_prefixes
    compute_webobjects_prefixes

    etitle "Calcul de la liste des bundles à déployer"

    if [ -z "$istmpdir" ]; then
        __woinst_set_tmpdir_maybe
        estep "La copie de travail sera faite dans $(ppath "$tmpdir")..."
    fi

    local -a variables frameworks applications
    local src name value valid copied
    variables=("${WEBOBJECTS_PREFIXES[@]}")
    frameworks=()
    applications=()
    for src in "${@:-.}"; do
        if [[ "$src" == *=* ]]; then
            splitvar "$src" name value
            array_add variables "$name"
            set_var "$name" "$value"
        else
            src="$(abspath "$src")"
            valid=
            copied=

            if [ -f "$src" ] && is_archive "$src"; then
                __woinst_expand_archive || return 1
                copied=1
            fi
            if [ -d "$src" ]; then
                if endswith "$src" .framework; then
                    is_wofwkdir "$src" || {
                        eerror "Framework invalide: $(ppath "$src")"
                        return 1
                    }
                    valid=1
                elif endswith "$src" .woa; then
                    is_woappdir "$src" || {
                        eerror "Application invalide: $(ppath "$src")"
                        return 1
                    }
                    valid=1
                fi
            fi
            [ -n "$valid" ] || {
                eerror "N'est pas un bundle valide: $(ppath "$src")"
                return 1
            }
            srcname="$(basename "$src")"

            if [ -z "$copied" -a -z "$istmpdir" ]; then
                estep "$(ppath "$src")"
                cpnovcs "$src" "$tmpdir"
                src="$tmpdir/$srcname"
            fi

            if endswith "$src" .framework; then
                array_add frameworks "$src"
            elif endswith "$src" .woa; then
                array_add applications "$src"
            fi
        fi
    done
    eend

    # Tagger les bundles
    if [ -z "$webinst" -a -z "$notag" ]; then
        etitle "Tagger les bundles"
        for src in "${frameworks[@]}"; do
            srcname="$(basename "$src")"
            etitle "$srcname"
            wotag --message "Version installée" -s "$WOFRAMEWORKS/$srcname"
            wotag --message "Version à installer" "$src"
            eend
        done
        for src in "${applications[@]}"; do
            srcname="$(basename "$src")"
            etitle "$srcname"
            wotag --message "Version installée" -s "$WOAPPLICATIONS/$srcname"
            wotag --message "Version à installer" "$src"
            eend
        done
        eend
    fi

    # Confirmer le déploiement
    etitle "Résumé"
    function __woinst_strip_woa() { basename "$1" .woa; }
    function __woinst_strip_fwk() { basename "$1" .framework; }
    estepn "Déploiement ${applications:+des applications: $(array_mapjoin applications __woinst_strip_woa " ")${frameworks:+
et }}${frameworks:+des frameworks: $(array_mapjoin frameworks __woinst_strip_fwk " ")}
dans NEXT_ROOT  = ${NEXT_ROOT:-/}
     HTDOCS_DIR = ${HTDOCSDIR}"

    if ! ask_yesno -c "Voulez-vous lancer le déploiement?" O; then
        eend
        return 1
    fi
    eend

    # Faire le déploiement
    local configfile variable
    ac_set_tmpfile configfile
    for variable in "${variables[@]}" bounce webinst ULIBDIR __estack __tlevel; do
        set_var_cmd "$variable" "${!variable}" >>"$configfile"
    done
    for array in variables scripts applications frameworks; do
        set_array_cmd "$array" >>"$configfile"
    done
    # pour les serveurs qui ont une vieille version de bash, forcer la valeur de
    # ULIBDIR
    set_var_cmd "FORCED_ULIBDIR" "$ULIBDIR" >>"$configfile"

    runscript_as_root "$ULIBDIR/support/woinst2s" "$configfile"

    ac_clean "$tmpdir" "$configfile"
}
