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

function display_help() {
    uecho "$scriptname: outil pour faciliter l'utilisation de docker

USAGE
    $scriptname CMDs...

COMMANDES
    build
        Construire les images
    push
        Pousser les images vers le serveur
    start, run [SERVICE]
        Démarrer le(s) service(s)
    stop [SERVICE]
        Arrêter le(s) service(s)
    up
        Créer l'environnement, démarrer les services et suivre les logs de façon
        interactive.
    logs [SERVICE]
        Afficher les logs
    down
        Arrêter les services et supprimer l'environnement
    brd
        Construire les images (comme avec build), démarrer les services et
        suivre les logs de façon interactive (comme avec up). Dès que l'on
        arrête l'affichage des logs avec Ctrl+C, arrêter les services et
        supprimer l'environnement (comme avec down)
    bs
        Construire les images (comme avec build) puis démarrer les services
        (comme avec start)
    exec SERVICE COMMAND
        Lancer la commande dans le container spécifié
    systemd-unit
        Générer une unité systemd qui démarre les services. A priori, ce n'est
        nécessaire que si aucune politique de redémarrage n'a été définie.
    ps
        Afficher les containers en cours d'exécution
    ls
        Lister les images actuellement présentes
    rm
        Supprimer une image
    prune
        Supprimer les containers et les images inutilisées

OPTIONS générales
    (ces options sont communes à toutes les commandes)
    -d, --chdir PROJDIR
    -p, --profile PROFILE
    -P, --prod
    -T, --test
    -n, --fake
    -j, --no-cache
    -h, --host HOST

OPTIONS build
    (ces options ne sont valides que pour les commandes build, brd, bs)
    -g, --ug, --no-update-apps
        ne pas mettre à jour les dépôts dépendants. ces dépôts sont
        définis dans le fichier update-apps.conf qui a le format
        suivant:
            DEFAULT_BRANCH=
            APPS=()
            app_URL=
            app_DEST=
            app_ORIGIN=
            app_BRANCH=
            app_TYPE=
            app_AFTER_UPDATE=()
    -u, --uu, --update-apps-only
        Ne faire que la mise à jour depuis les dépôts dépendants.
    --uo, --update-apps-origin ORIGIN
        Spécifier l'origine par défaut pour update-apps
    --ub, --update-apps-branch BRANCH
        Spécifier la branche par défaut pour update-apps"
}

function get_version() {
    local GIT_DIR; unset GIT_DIR
    if git rev-parse --git-dir >/dev/null 2>&1; then
        local head commit tag
        commit="$(git rev-list --tags --max-count=1 2>/dev/null)"
        if [ -n "$commit" ]; then
            tag="$(git describe --tags "$commit" 2>/dev/null)"
            if [ -n "$tag" ]; then
                head="$(git rev-parse HEAD)"
                if [ "$commit" != "$head" ]; then
                    echo "$tag-develop"
                else
                    echo "$tag"
                fi
                return
            fi
        fi
    elif [ -f VERSION.txt ]; then
        cat VERSION.txt
    fi
    echo develop
}

function docker_add_build_arg() {
    eval "replace_build_args+=(--build-arg $1=\"$2\"); $1=\"$2\""
}
function docker_parse_build_args() {
    cat "$1" |
    grep -v '^#' |
    grep -v '^$' |
    sed -r 's/([^=]+)=(.*)/replace_build_args+=(--build-arg \1="\2"); \1="\2"/'
}
function docker_parse_env_args() {
    [ -f .build.env ] && eval "$(docker_parse_build_args .build.env)"
    [ -f build.env ] && eval "$(docker_parse_build_args build.env)"
    [ -n "$PROFILE" -a -f ".build.$PROFILE.env" ] && eval "$(docker_parse_build_args ".build.$PROFILE.env")"
}
function docker_set_env_args() {
    [ -f .build.env ] && source ./.build.env
    [ -f build.env ] && source ./build.env
    [ -n "$PROFILE" -a -f ".build.$PROFILE.env" ] && source "./.build.$PROFILE.env"
}
function docker_set_run_args() {
    replace_run_args+=(--env-file "$1")
    source "$1"
}
function docker_check_name() {
    [ -n "$NAME" ] || die "Vous devez définir NAME dans .build.env"
    if [ "$1" == set_container_name ]; then
        project_name="$NAME"
        container_name="${project_name//[^a-zA-Z0-9_-]/}"
        [ -n "$PROFILE" ] && container_name="${container_name}_$PROFILE"
    fi
}

function compose_set_env_args() {
    replace_env_args+=(-f docker-compose.yml)
    if [ -f docker-compose.override.yml ]; then
        replace_env_args+=(-f docker-compose.override.yml)
    fi
    local PROJECT_NAME=--none--
    [ -f .compose.env ] && source ./.compose.env
    if [ -n "$PROFILE" ]; then
        if [ -f "docker-compose.$PROFILE.yml" ]; then
            replace_env_args+=(-f "docker-compose.$PROFILE.yml")
        fi
        if [ "$PROJECT_NAME" != --none-- ]; then
            if [ -z "$COMPOSE_PROJECT_NAME" ]; then
                [ -n "$PROJECT_NAME" ] || PROJECT_NAME="$(basename -- "$(pwd)")"
                COMPOSE_PROJECT_NAME="${PROJECT_NAME}_${PROFILE}"
            fi
            export COMPOSE_PROJECT_NAME
        fi
    fi
    if [ "$1" == set_container_name ]; then
        if [ "$PROJECT_NAME" == --none-- ]; then
            project_name="$(basename -- "$(pwd)")"
        else
            project_name="$PROJECT_NAME"
        fi
        container_name="${project_name//[^a-zA-Z0-9_-]/}"
        [ -n "$PROFILE" ] && container_name="${container_name}_$PROFILE"
    fi
}

function host_run() {
    # lancer le script $2..@ sur l'hôte $1
    # si $1 n'est pas défini ou est le nom de l'hôte local ou vaut 'localhost',
    # lancer le script en local avec les droits root
    # sinon, si docker-machine existe, l'hôte doit correspondre à la machine active
    # sinon, lancer inconditionnellement le script sur l'hôte distant
    local host="$1" script="$2"; shift; shift
    if [ -n "$host" -a "$host" != localhost ]; then
        if check_hostname "$host"; then
            estep "Lancement de $script localement"
            runscript_as_root "$script" "$@"
        elif progexists docker-machine; then
            local dm; setx dm=docker-machine active 2>/dev/null
            if [ "${host%%.*}" == "$dm" ]; then
                estep "Copie du script vers root@$host"
                scp "$script" "root@$host:/tmp/tmp-dk-service-script" || die
                estep "Lancement du script à distance"
                local -a args; args=(/tmp/tmp-dk-service-script "$@")
                ssh -qt "root@$host" "$(qvals "${args[@]}"); rm -f /tmp/tmp-dk-service-script"
            else
                ewarn "La machine active ($dm) n'est pas la destination ($host)"
            fi
        else
            estep "Copie du script vers root@$host"
            scp "$script" "root@$host:/tmp/tmp-dk-service-script" || die
            estep "Lancement du script à distance"
            local -a args; args=(/tmp/tmp-dk-service-script "$@")
            ssh -qt "root@$host" "$(qvals "${args[@]}"); rm -f /tmp/tmp-dk-service-script"
        fi
    else
        estep "Lancement de $script localement"
        runscript_as_root "$script" "$@"
    fi
}
function local_run() {
    # lancer le script $2..@ sur l'hôte $1 uniquement si c'est l'hôte courant
    local host="$1" script="$2"; shift; shift
    if [ -n "$host" ]; then
        if ! check_hostname "$host"; then
            eerror "Cette commande doit obligatoirement être lancée depuis l'hôte $host"
            return 1
        fi
    fi
    estep "Lancement de $script localement"
    runscript_as_root "$script" "$@"
}

BUILD_UPDATE_APPS=
BUILD_BUILD=
UPDATE_APPS_ORIGIN=
UPDATE_APPS_BRANCH=
function build_set_options() {
    case "$1" in
    u) BUILD_UPDATE_APPS=1; BUILD_BUILD=;;
    b) BUILD_UPDATE_APPS=; BUILD_BUILD=1;;
    *) BUILD_UPDATE_APPS=1; BUILD_BUILD=1;;
    esac
    UPDATE_APPS_ORIGIN="$2"
    UPDATE_APPS_BRANCH="$3"
}

function update_apps_func_sqlmig() {
    local destdir="$1" srcdir="$2"
    [ -n "$destdir" ] || destdir=db
    [ "${destdir%/config/mariadb/sqlmig}" != "$destdir" ] || destdir="$destdir/config/mariadb/sqlmig"

    [ -n "$srcdir" ] || srcdir="$dest"
    [ "${srcdir%/config/sqlmig}" != "$srcdir" ] || srcdir="$srcdir/config/sqlmig"

    [ -d "$srcdir" ] || return 0

    local -a sqlmigs; local sqlmig name
    array_lsall sqlmigs "$srcdir"
    mkdir -p "$destdir" || return 1
    for sqlmig in "${sqlmigs[@]}"; do
        if [ -d "$sqlmig" ]; then
            setx name=basename -- "$sqlmig"
            rsync -av --delete-after "$sqlmig/" "$destdir/$name"
        else
            rsync -av "$sqlmig" "$destdir"
        fi
    done
    return 0
}

function build_update_apps() {
    [ -n "$BUILD_UPDATE_APPS" ] || return 0
    [ -f update-apps.conf ] || return 0

    # charger le fichier de configuration
    local DEFAULT_ORIGIN DEFAULT_BRANCH APPS
    DEFAULT_ORIGIN="$UPDATE_APPS_ORIGIN"
    [ -z "$DEFAULT_ORIGIN" ] && DEFAULT_ORIGIN=origin
    DEFAULT_BRANCH="$UPDATE_APPS_BRANCH"
    #XXX à terme, on déploiera la branche master en prod
    [ -z "$DEFAULT_BRANCH" -a "$PROFILE" == prod ] && DEFAULT_BRANCH=develop #master
    [ -z "$DEFAULT_BRANCH" ] && DEFAULT_BRANCH=develop
    APPS=()
    [ -f update-apps.conf ] && source ./update-apps.conf
    [ ${#APPS[*]} -gt 0 ] || return 0

    local PRODUCTION DEVELOPMENT
    case "$PROFILE" in
    prod) PRODUCTION=1; DEVELOPMENT=;;
    test) PRODUCTION=1; DEVELOPMENT=1;;
    devel) PRODUCTION=; DEVELOPMENT=;;
    esac

    etitle "Mise à jour des dépendances"
    local app type url dest branch after_update after_updates
    for app in "${APPS[@]}"; do
        etitle "$app"
        url="${app}_URL"; url="${!url}"
        dest="${app}_DEST"; dest="${!dest}"
        origin="${app}_ORIGIN"; origin="${!origin}"
        branch="${app}_BRANCH"; branch="${!branch}"
        type="${app}_TYPE"; type="${!type}"
        after_updates="${app}_AFTER_UPDATE"
        if is_defined "$after_updates"; then
            after_updates="$after_updates[@]"; after_updates="${!after_updates}"
        else
            # script par défaut après update-apps
            after_updates=(sqlmig)
        fi

        [ -n "$url" ] || {
            ewarn "$app: vous devez définir l'url"
            eend; return 1
        }
        [ -n "$dest" ] || dest="$app"
        mkdir -p "$dest" || { eend; return 1; }
        [ -n "$origin" ] || origin="$DEFAULT_ORIGIN"
        [ -n "$branch" ] || branch="$DEFAULT_BRANCH"

        dest="$dest/$app"
        if [ ! -d "$dest" ]; then
            # clonage initial
            estep "Clonage $url:$branch --> $dest"
            git clone -o "$origin" -b "$branch" "$url" "$dest" || { eend; return 1; }
        else
            # mise à jour
            estep "Maj dépôt $url:$branch --> $dest"
            setx cwd=pwd
            cd "$dest"
            git fetch --all -p -f || { eend; return 1; }
            git reset --hard "$origin/$branch" || { eend; return 1; }
            cd "$cwd"
        fi

        if [ -z "$type" ]; then
            if [ -f "$dest/composer.json" ]; then
                type=composer
            else
                type=inconnu
            fi
        fi
        estep "Type de dépôt: $type"
        if [ "$type" == composer ]; then
            composer=/usr/bin/composer
            [ -x "$dest/composer.phar" ] && composer="$dest/composer.phar"

            estep "Installation des dépendances composer"
            "$composer" -d"$dest" install ${PRODUCTION:+--no-dev -o} || { eend; return 1; }
        fi

        for after_update in "${after_updates[@]}"; do
            if [ "${after_update#/}" != "$after_update" ]; then
                # commande absolue, la lancer telle quelle
                estep "$after_update"
                eval "$after_update" || { eend; return 1; }
            elif [ "${after_update#./}" != "$after_update" ]; then
                # commande relative, la lancer telle quelle
                estep "$after_update"
                eval "$after_update" || { eend; return 1; }
            else
                # c'est une fonction update_apps_func_*
                estep "$after_update"
                eval "update_apps_func_$after_update" || { eend; return 1; }
            fi
        done

        eend
    done
    eend
}

function initialize_build_env() {
    CTXDIR=.
    NAME=
    TAGS=(latest)
    VERSION=
}
function default_update_build_env() {
    [ -n "$VERSION" ] || docker_add_build_arg VERSION "$(get_version)"
    [ -n "$VERSION" ] && TAGS+=("$VERSION")
}
function update_build_env() { default_update_build_env; }

function default_compose_build() {
    ${FAKE:+qvals} docker-compose \
        "${replace_env_args[@]}" "${env_args[@]}" \
        build \
        ${NO_CACHE:+--no-cache} \
        "${replace_build_args[@]}" "${build_args[@]}" \
        "$@"
}
function default_docker_build() {
    local tag
    for tag in "${TAGS[@]}"; do
        replace_build_args+=(-t "$NAME:$tag")
    done
    ${FAKE:+qvals} docker build \
        ${NO_CACHE:+--no-cache} \
        "${replace_env_args[@]}" "${env_args[@]}" \
        "${replace_build_args[@]}" "${build_args[@]}" \
        "$@" "$CTXDIR"
}
function compose_build() {
    [ -n "$BUILD_BUILD" ] || return 0
    default_compose_build "$@"
}
function docker_build() {
    [ -n "$BUILD_BUILD" ] || return 0
    default_docker_build "$@"
}
function auto_build() {
    local -a replace_env_args env_args
    local -a replace_build_args build_args
    initialize_build_env
    if [ -f docker-compose.yml ]; then
        compose_set_env_args
        update_build_env
        build_update_apps || return 1
        compose_build
    else
        docker_parse_env_args
        docker_check_name
        docker_add_build_arg build_date "$(date +%y%m%d)"
        update_build_env
        build_update_apps || return 1
        docker_build
    fi
}

function auto_push() {
    local -a replace_env_args env_args
    local -a replace_build_args build_args
    local tag
    initialize_build_env
    if [ -f docker-compose.yml ]; then
        compose_set_env_args
        update_build_env
    else
        docker_parse_env_args
        docker_check_name
        update_build_env
    fi
    for tag in "${TAGS[@]}"; do
        ${FAKE:+qvals} docker push "$NAME:$tag"
    done
}

function default_compose_up() {
    ${FAKE:+qvals} docker-compose \
        "${replace_env_args[@]}" "${env_args[@]}" \
        up "${replace_run_args[@]}" "${run_args[@]}" \
        "${replace_user_args[@]}" "${user_args[@]}" "$@"
}
function default_docker_up() {
    ${FAKE:+qvals} docker run \
           "${replace_env_args[@]}" "${env_args[@]}" \
           "${replace_run_args[@]}" "${run_args[@]}" \
           "$NAME" \
           "${replace_user_args[@]}" "${user_args[@]}" "$@"
}
function compose_up() { default_compose_up "$@"; }
function docker_up() { default_docker_up "$@"; }
function auto_up() {
    local -a replace_env_args env_args
    local -a replace_run_args run_args
    local -a replace_user_args user_args
    local project_name container_name
    if [ -f docker-compose.yml ]; then
        compose_set_env_args
        replace_run_args=(-d)
        compose_up "$@"
    else
        docker_set_env_args
        docker_check_name set_container_name
        replace_run_args=(-d --name "$container_name")
        docker_up "$@"
    fi
}

function default_compose_stop() {
    ${FAKE:+qvals} docker-compose \
        "${replace_env_args[@]}" "${env_args[@]}" \
        stop "${replace_stop_args[@]}" "${stop_args[@]}" \
        "$@"
}
function default_docker_stop() {
    ${FAKE:+qvals} docker container stop \
           "${replace_stop_args[@]}" "${stop_args[@]}" \
           "$container_name" "$@"
}
function compose_stop() { default_compose_stop "$@"; }
function docker_stop() { default_docker_stop "$@"; }
function auto_stop() {
    local -a replace_env_args env_args
    local -a replace_stop_args stop_args
    local project_name container_name
    if [ -f docker-compose.yml ]; then
        compose_set_env_args
        compose_stop "$@"
    else
        docker_set_env_args
        docker_check_name set_container_name
        docker_stop "$@"
    fi
}

function default_compose_logs() {
    ${FAKE:+qvals} docker-compose \
        "${replace_env_args[@]}" "${env_args[@]}" \
        logs "${replace_logs_args[@]}" "${logs_args[@]}" \
        "$@"
}
function default_docker_logs() {
    ${FAKE:+qvals} docker logs \
           "${replace_logs_args[@]}" "${logs_args[@]}" \
           "$container_name" "$@"
}
function compose_logs() { default_compose_logs "$@"; }
function docker_logs() { default_docker_logs "$@"; }
function auto_logs() {
    local -a replace_env_args env_args
    local -a replace_logs_args logs_args
    local project_name container_name
    if [ -f docker-compose.yml ]; then
        compose_set_env_args
        replace_logs_args=(-f)
        compose_logs "$@"
    else
        docker_set_env_args
        docker_check_name set_container_name
        replace_logs_args=(-f)
        docker_logs "$@"
    fi
}

function default_compose_down() {
    ${FAKE:+qvals} docker-compose \
        "${replace_env_args[@]}" "${env_args[@]}" \
        down "${replace_down_args[@]}" "${down_args[@]}" \
        "$@"
}
function default_docker_down() {
    estep "stop"
    ${FAKE:+qvals} docker container stop \
           "${replace_down_args[@]}" "${down_args[@]}" \
           "$container_name" "$@"
    estep "rm"
    ${FAKE:+qvals} docker container rm \
           "${replace_rm_args[@]}" "${rm_args[@]}" \
           "$container_name"
}
function compose_down() { default_compose_down "$@"; }
function docker_down() { default_docker_down "$@"; }
function auto_down() {
    local -a replace_env_args env_args
    local -a replace_down_args down_args
    local -a replace_rm_args rm_args
    local project_name container_name
    if [ -f docker-compose.yml ]; then
        compose_set_env_args
        compose_down "$@"
    else
        docker_set_env_args
        docker_check_name set_container_name
        docker_down "$@"
    fi
}

function default_compose_exec() {
    ${FAKE:+qvals} docker-compose \
        "${replace_env_args[@]}" "${env_args[@]}" \
        exec "${replace_exec_args[@]}" "${exec_args[@]}" \
        "$@"
}
function default_docker_exec() {
    ${FAKE:+qvals} docker container exec \
           "${replace_exec_args[@]}" "${exec_args[@]}" \
           "$container_name" "$@"
}
function compose_exec() { default_compose_exec "$@"; }
function docker_exec() { default_docker_exec "$@"; }
function auto_exec() {
    local -a replace_env_args env_args
    local -a replace_exec_args exec_args
    local project_name container_name
    if [ -f docker-compose.yml ]; then
        compose_set_env_args
        compose_exec "$@"
    else
        docker_set_env_args
        docker_check_name set_container_name
        docker_exec "$@"
    fi
}

function default_compose_systemd_unit() {
    local docker_compose="$(which docker-compose 2>/dev/null)"
    if [ -z "$docker_compose" ]; then
        if [ -x /usr/bin/docker-compose ]; then
            docker_compose=/usr/bin/docker-compose
        elif [ -x /usr/local/bin/docker-compose ]; then
            docker_compose=/usr/local/bin/docker-compose
        else
            die "Impossible de trouver docker-compose"
        fi
    fi
    setx startcmd=qvals "$docker_compose" \
        "${replace_env_args[@]}" "${env_args[@]}" \
        up "${replace_run_args[@]}" "${run_args[@]}" \
        "${replace_user_args[@]}" "${user_args[@]}" "$@"
    setx stopcmd=qvals "$docker_compose" down
}
function default_docker_systemd_unit() {
    local docker="$(which docker 2>/dev/null)"
    if [ -z "$docker" ]; then
        if [ -x /usr/bin/docker ]; then
            docker=/usr/bin/docker
        elif [ -x /usr/local/bin/docker ]; then
            docker=/usr/local/bin/docker
        else
            die "Impossible de trouver docker"
        fi
    fi
    setx startcmd=qvals "$docker" run \
           "${replace_env_args[@]}" "${env_args[@]}" \
           "${replace_run_args[@]}" "${run_args[@]}" \
           "$NAME" \
           "${replace_user_args[@]}" "${user_args[@]}" "$@"
    setx stopcmd=qvals "$docker" stop "$container_name"
}
function compose_systemd_unit() { default_compose_systemd_unit "$@"; }
function docker_systemd_unit() { default_docker_systemd_unit "$@"; }
function auto_systemd_unit() {
    local -a replace_env_args env_args
    local -a replace_run_args run_args
    local -a replace_user_args user_args
    local project_name container_name startcmd stopcmd
    local tmpscript; ac_set_tmpfile tmpscript

    estep "Génération de l'unité systemd"
    export COMPOSE_PROJECT_NAME=
    if [ -f docker-compose.yml ]; then
        compose_set_env_args set_container_name
        replace_run_args=(-d --no-color)
        compose_systemd_unit "$@"
        if [ -z "$HOST" -a -f .env ]; then
            source ./.env
            if [ -n "$PROFILE" ]; then
                HOST="${PROFILE^^}_HOST"; HOST="${!HOST}"
            fi
        fi
    else
        docker_set_env_args
        docker_check_name set_container_name
        replace_run_args=(-d --name "$container_name")
        docker_systemd_unit "$@"
    fi
    [ -n "$COMPOSE_PROJECT_NAME" ] || COMPOSE_PROJECT_NAME="$project_name"
    chmod 755 "$tmpscript"
    cat >"$tmpscript" <<EOF
#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
cat >/etc/systemd/system/$container_name.service <<EOD
[Unit]
Description=$project_name stack ($PROFILE)
Requires=docker.service
After=docker.service

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=$(pwd)
Environment=$(qval "COMPOSE_PROJECT_NAME=$COMPOSE_PROJECT_NAME")
ExecStart=$startcmd
ExecStop=$stopcmd
TimeoutStopSec=300

[Install]
WantedBy=multi-user.target
EOD
systemctl daemon-reload
systemctl enable $container_name.service
EOF

    estep "Installation de l'unité systemd"
    local_run "$HOST" "$tmpscript"
}

DEFAULT_PROFILE=devel
PROFILE=
DM_PROFILES=()
set_defaults dk
export PROFILE

if progexists docker-machine; then
    setx active_dm=docker-machine active 2>/dev/null
    for dm_profile in "${DM_PROFILES[@]}"; do
        splitpair "$dm_profile" dm profile
        if [ "$dm" == "$active_dm" ]; then
            DEFAULT_PROFILE="$profile"
            break
        fi
    done
fi

chdir=
FAKE=
NO_CACHE=
HOST=
update_apps_mode=ub
update_apps_origin=
update_apps_branch=
args=(
    --help '$exit_with display_help'
    -d:,--chdir: chdir=
    -p:,--profile: PROFILE=
    -P,--prod PROFILE=prod
    -T,--test PROFILE=test
    -n,--fake FAKE=1
    -j,--no-cache NO_CACHE=1
    -h:,--host: HOST=
    -g,--ug,--no-update-apps update_apps_mode=b
    -u,--uu,--update-apps-only update_apps_mode=u
    --uo:,--update-apps-origin: update_apps_origin=
    --ub:,--update-apps-branch: update_apps_branch=
)
parse_args "$@"; set -- "${args[@]}"

# construire par défaut
[ $# -eq 0 ] && set -- build
[ -n "$PROFILE" ] || PROFILE="$DEFAULT_PROFILE"

[ -n "$chdir" ] && { cd "$chdir" || die; }

while [ $# -gt 0 ]; do
    [ "$1" == -- ] && { shift; continue; }
    cmd="$1"; shift
    case "$cmd" in
    b|build)
        build_set_options "$update_apps_mode" "$update_apps_origin"  "$update_apps_branch"
        [ -f .build.scripts.sh ] && source ./.build.scripts.sh
        [ -f build.scripts.sh ] && source ./build.scripts.sh
        args=()
        while [ $# -gt 0 -a "$1" != -- ]; do
            args+=("$1"); shift
        done
        enote "Profil $PROFILE"
        auto_build "${args[@]}" || die
        ;;
    push)
        [ -f .build.scripts.sh ] && source ./.build.scripts.sh
        [ -f build.scripts.sh ] && source ./build.scripts.sh
        args=()
        while [ $# -gt 0 -a "$1" != -- ]; do
            args+=("$1"); shift
        done
        enote "Profil $PROFILE"
        auto_push "${args[@]}" || die
        ;;
    s|run|start)
        args=()
        while [ $# -gt 0 -a "$1" != -- ]; do
            args+=("$1"); shift
        done
        enote "Profil $PROFILE"
        auto_up "${args[@]}" || die
        ;;
    k|stop)
        args=()
        while [ $# -gt 0 -a "$1" != -- ]; do
            args+=("$1"); shift
        done
        enote "Profil $PROFILE"
        auto_stop "${args[@]}" || die
        ;;
    1|up)
        args=()
        while [ $# -gt 0 -a "$1" != -- ]; do
            args+=("$1"); shift
        done
        enote "Profil $PROFILE"
        auto_up "${args[@]}" && auto_logs || die
        ;;
    l|logs)
        args=()
        while [ $# -gt 0 -a "$1" != -- ]; do
            args+=("$1"); shift
        done
        enote "Profil $PROFILE"
        auto_logs "${args[@]}" || die
        ;;
    0|down)
        args=()
        while [ $# -gt 0 -a "$1" != -- ]; do
            args+=("$1"); shift
        done
        enote "Profil $PROFILE"
        auto_down "${args[@]}" || die
        ;;
    d|brd)
        do_auto_down=1
        function auto_down_trap() {
            [ -n "$do_auto_down" ] && auto_down
        }
        trap auto_down_trap 1 3 15 EXIT

        build_set_options "$update_apps_mode" "$update_apps_origin"  "$update_apps_branch"
        [ -f .build.scripts.sh ] && source ./.build.scripts.sh
        [ -f build.scripts.sh ] && source ./build.scripts.sh
        args=()
        while [ $# -gt 0 -a "$1" != -- ]; do
            args+=("$1"); shift
        done
        enote "Profil $PROFILE"
        if auto_build; then
            auto_up "${args[@]}" && auto_logs || die
        else
            do_auto_down=
        fi
        ;;
    bs)
        build_set_options "$update_apps_mode" "$update_apps_origin"  "$update_apps_branch"
        [ -f .build.scripts.sh ] && source ./.build.scripts.sh
        [ -f build.scripts.sh ] && source ./build.scripts.sh
        args=()
        while [ $# -gt 0 -a "$1" != -- ]; do
            args+=("$1"); shift
        done
        enote "Profil $PROFILE"
        auto_build && auto_up "${args[@]}"
        ;;
    x|exec)
        args=()
        while [ $# -gt 0 -a "$1" != -- ]; do
            args+=("$1"); shift
        done
        enote "Profil $PROFILE"
        auto_exec "${args[@]}" || die
        ;;
    systemd-unit|systemd)
        args=()
        while [ $# -gt 0 -a "$1" != -- ]; do
            args+=("$1"); shift
        done
        enote "Profil $PROFILE"
        auto_systemd_unit "${args[@]}" || die
        ;;
    ps) docker container ps -a || die;;
    ls) docker image ls || die;;
    rm)
        args=()
        while [ $# -gt 0 -a "$1" != -- ]; do
            args+=("$1"); shift
        done
        docker image rm "${args[@]}" || die
        ;;
    X|prune)
        docker container prune -f || die
        docker image prune -f || die
        ;;
    *) die "$cmd: commande inconnue";;
    esac
done