Skip to content
Snippets Groups Projects
Select Git revision
  • f87831aa861da3e8370fca3dafd0744bab84a1c0
  • master default protected
  • Legacy_Php7
3 results

inc_functions.php

Blame
    • hahn's avatar
      f87831aa
      build: · f87831aa
      hahn authored
      - use RVMSCRIPT env variable an update check-config + template
      - fix message on end of build
      f87831aa
      History
      build:
      hahn authored
      - use RVMSCRIPT env variable an update check-config + template
      - fix message on end of build
    init.sh 24.50 KiB
    #!/bin/bash
    # ======================================================================
    #
    # DOCKER PHP DEV ENVIRONMENT :: INIT
    #
    # ----------------------------------------------------------------------
    # 2021-11-nn  v1.0  <axel.hahn@iml.unibe.ch>
    # 2022-07-19  v1.1  <axel.hahn@iml.unibe.ch>  support multiple dirs for setfacl
    # 2022-11-16  v1.2  <www.axel-hahn.de>        use docker-compose -p "$APP_NAME"
    # 2022-12-18  v1.3  <www.axel-hahn.de>        add -p "$APP_NAME" in other docker commands
    # 2022-12-20  v1.4  <axel.hahn@unibe.ch>      replace fgrep with grep -F
    # 2023-03-06  v1.5  <www.axel-hahn.de>        up with and without --build
    # 2023-08-17  v1.6  <www.axel-hahn.de>        menu selection with single key (without return)
    # 2023-11-10  v1.7  <axel.hahn@unibe.ch>      replace docker-compose with "docker compose"
    # 2023-11-13  v1.8  <axel.hahn@unibe.ch>      UNDO "docker compose"; update infos
    # 2023-11-15  v1.9  <axel.hahn@unibe.ch>      add help; execute multiple actions by params; new menu item: open app
    # 2023-12-07  v1.10 <www.axel-hahn.de>        simplyfy console command; add php linter
    # 2024-07-01  v1.11 <www.axel-hahn.de>        diff with colored output; suppress errors on port check
    # 2024-07-19  v1.12 <axel.hahn@unibe.ch>      apply shell fixes
    # 2024-07-22  v1.13 <axel.hahn@unibe.ch>      show info if there is no database container; speedup replacements
    # 2024-07-22  v1.14 <axel.hahn@unibe.ch>      show colored boxes with container status
    # 2024-07-24  v1.15 <axel.hahn@unibe.ch>      update menu output
    # 2024-07-26  v1.16 <axel.hahn@unibe.ch>      hide unnecessary menu items (WIP)
    # 2024-07-29  v1.17 <www.axel-hahn.de>        hide unnecessary menu items; reorder functions
    # 2024-08-14  v1.18 <www.axel-hahn.de>        update container view
    # 2024-09-20  v1.19 <www.axel-hahn.de>        detect dockerd-rootless (hides menu item to set permissions)
    # 2024-10-16  v1.20 <axel.hahn@unibe.ch>      add db import and export
    # 2024-10-25  v1.21 <axel.hahn@unibe.ch>      create missing subdir dbdumps
    # 2024-10-30  v1.22 <axel.hahn@unibe.ch>      added: Open Mysql client in container
    # 2024-10-30  v1.23 <axel.hahn@unibe.ch>      added: show menu hints why some menu items are visible
    # 2024-11-20  v1.24 <axel.hahn@unibe.ch>      fix menu with started database less app; apply template permissions on target file; add $WEBURL; remove $frontendurl
    # 2024-11-20  v1.25 <axel.hahn@unibe.ch>      fix menu startup containers
    # 2024-11-21  v1.26 <axel.hahn@unibe.ch>      Reset colors in _checkConfig 
    # 2025-05-13  v1.27 <axel.hahn@unibe.ch>      handle addition variables config file for non sharable values 
    # ======================================================================
    
    cd "$( dirname "$0" )" || exit 1
    
    _version="1.27"
    
    # init used vars
    gittarget=
    WEBURL=
    
    _self=$( basename "$0" )
    
    # shellcheck source=/dev/null
    . "${_self}.cfg" || exit 1
    . "${_self}_not_shared.cfg" 2>/dev/null
    
    # git@git-repo.iml.unibe.ch:iml-open-source/docker-php-starterkit.git
    selfgitrepo="docker-php-starterkit.git"
    
    fgGray="\e[1;30m"
    fgRed="\e[31m"
    fgGreen="\e[32m"
    fgBrown="\e[33m"
    fgBlue="\e[34m"
    
    fgInvert="\e[7m"
    fgReset="\e[0m"
    
    # ----- status varsiables
    # running containers
    DC_WEB_UP=0
    DC_DB_UP=0
    DC_ALL_UP=0
    
    # repo of docker-php-starterkit is here?
    DC_REPO=1
    
    DC_CONFIG_CHANGED=0
    
    # absolute urls for web app
    DC_WEB_URL=""
    
    DC_DUMP_DIR=dbdumps
    DC_SHOW_MENUHINTS=0
    
    isDockerRootless=0
    ps -ef | grep  dockerd-rootless | grep -q $USER && isDockerRootless=1
    
    # ----------------------------------------------------------------------
    # FUNCTIONS
    # ----------------------------------------------------------------------
    
    # check config for changes in newer versions
    function _checkConfig(){
    
        # --- v1.24
        if [ -z "$WEBURL" ]; then
            echo -e "${fgBrown}INFO: add 'WEBURL=\"/\"' in your ${_self}.cfg. It is a new var since v1.24${fgReset}"
            WEBURL="/"
        fi
        if [ -n "$frontendurl" ]; then
            echo -e "${fgBrown}INFO: Remove frontendurl=$frontendurl in your ${_self}.cfg. It is obsolete since v1.24${fgReset}"
        fi
    
    }
    # ----------------------------------------------------------------------
    # STATUS FUNCTIONS
    
    # get container status and set global variable DC_REPO
    # DC_REPO = 0 nothing to do - repo was changed to project
    # DC_REPO = 1 if repo is in selfgitrepo (must be deleted)
    function _getStatus_repo(){
        DC_REPO=0
        git config --get remote.origin.url 2>/dev/null | grep -q $selfgitrepo && DC_REPO=1
    }
    
    # check if any of the templates has a change that must be applied
    function _getStatus_template(){
        _generateFiles "dryrun"
    }
    
    # get container status and set global variables
    # DC_WEB_UP - web container 
    # DC_DB_UP  - database container
    #   0 = down
    #   1 = up
    function _getStatus_docker(){
        local _out
        _out=$( docker-compose -p "$APP_NAME" ps)    
    
        DC_WEB_UP=0
        DC_DB_UP=0
        DC_ALL_UP=0
    
        grep -q "${APP_NAME}-server" <<< "$_out" && DC_WEB_UP=1
        grep -q "${APP_NAME}-db"     <<< "$_out"  && DC_DB_UP=1
    
        if [ "$DB_ADD" != "false" ] && [ ! -d "${DC_DUMP_DIR}" ]; then
            echo "INFO: creating subdir ${DC_DUMP_DIR} to import/ export databases ..."
            mkdir "${DC_DUMP_DIR}" || exit 1
            return
        fi
    
        if [ "${DC_WEB_UP}" = "1" ] && [ "${DC_DB_UP}" = "1" ]; then
            DC_ALL_UP=1
        fi
    
        if [ "$DB_ADD" = "false" ] && [ "${DC_WEB_UP}" = "1" ]; then
            DC_ALL_UP=1
        fi
    
    }
    
    # Get web url of the application
    # It is for support of Nginx Docker Proxy
    # https://github.com/axelhahn/nginx-docker-proxy
    # It returns http://localhost:<port> or a https://<appname> plus $WEBURL
    function _getWebUrl(){
        if grep -q "^[0-9\.]* ${APP_NAME}-server" /etc/hosts; then
            DC_WEB_URL="https://${APP_NAME}-server$WEBURL"
        else
            DC_WEB_URL=http://localhost:${APP_PORT}$WEBURL
        fi
        set +vx
    }
    
    # ----------------------------------------------------------------------
    # OUTPUT
    
    # draw a headline 2
    function h2(){
        echo
        echo -e "$fgBrown>>>>> $*$fgReset"
    }
    
    # draw a headline 3
    function h3(){
        echo
        echo -e "$fgBlue----- $*$fgReset"
    }
    
    # helper for menu: print an inverted key
    function  _key(){
        echo -en "$fgInvert ${1} $fgReset"
    }
    
    # helper for menu: show hint text
    # param  int      FLag _bAll (i true the txt will be hidden)
    # param  string   message to show
    function menuhint(){
        local _bAll="$1"
        shift 1
        test $DC_SHOW_MENUHINTS -ne 0 && test "$_bAll" -eq "0" && ( echo -e "$fgBlue  $*$fgReset" )
    }
    
    # show menu in interactive mode and list keys in help with param -h
    # param  string  optional: set to "all" to show all menu items
    function showMenu(){
    
        local _bAll=0
        test -n "$1" && _bAll=1
    
        local _spacer="    "
    
        echo
        if [ $DC_REPO -eq 1 ] || [ $_bAll -eq 1 ]; then
            menuhint $_bAll "Git data of starterkit were found"
            echo "${_spacer}$( _key g ) - remove git data of starterkit"
            echo
        fi
    
        if [ $isDockerRootless -eq 1 ] || [ $_bAll -eq 1 ]; then
            menuhint $_bAll "Because rootless docker was found"
            echo "${_spacer}$( _key i ) - init application: set permissions"
            echo
        fi
    
        if [ $DC_CONFIG_CHANGED -eq 1 ] || [ $_bAll -eq 1 ]; then
            menuhint $_bAll "Config was changed"
            echo "${_spacer}$( _key t ) - generate files from templates"
            echo
        fi
        if [ $DC_CONFIG_CHANGED -eq 0 ] || [ $_bAll -eq 1 ]; then
            menuhint $_bAll "Config is unchanged"
            echo "${_spacer}$( _key T ) - remove generated files"
            echo
        fi
        if [ $DC_ALL_UP -eq 0 ] || [ $_bAll -eq 1 \
        ]; then
            if [ $DC_CONFIG_CHANGED -eq 0 ] || [ $_bAll -eq 1 ]; then
                menuhint $_bAll "A container is down and config is unchanged"
                echo "${_spacer}$( _key u ) - startup containers    docker-compose ... up -d"
                echo "${_spacer}$( _key U ) - startup containers    docker-compose ... up -d --build"
                echo
                echo "${_spacer}$( _key r ) - remove containers     docker-compose rm -f"
                echo
            fi
        fi
        if [ $DC_WEB_UP -eq 1 ] || [ $DC_DB_UP -eq 1 ] || [ $_bAll -eq 1 ]; then
            menuhint $_bAll "A container is up"
            echo "${_spacer}$( _key s ) - shutdown containers   docker-compose stop"
            echo
            echo "${_spacer}$( _key m ) - more infos"
            echo "${_spacer}$( _key o ) - open app [${APP_NAME}] $DC_WEB_URL"
            echo "${_spacer}$( _key c ) - console (bash)"
            echo
        fi
        if [ $DC_WEB_UP -eq 1 ] || [ $_bAll -eq 1 ]; then
            menuhint $_bAll "Web container is up"
            echo "${_spacer}$( _key p ) - console check with php linter"
            echo
        fi
        if [ $DC_DB_UP -eq 1 ] || [ $_bAll -eq 1 ]; then
            echo
            menuhint $_bAll "Database container is up"
            echo "${_spacer}$( _key d ) - Dump container database"
            echo "${_spacer}$( _key D ) - Import Dump into container database"
            echo "${_spacer}$( _key M ) - Open Mysql client in database container"
            echo
        fi
        menuhint $_bAll "Always available"
        echo "${_spacer}$( _key q ) - quit"
    
    }
    function showHelp(){
        cat <<EOH
    
    INITIALIZER FOR DOCKER APP v$_version
    
    A helper script written in Bash to bring up a PHP+Mysql application in docker.
    
    📄 Source : https://git-repo.iml.unibe.ch/iml-open-source/docker-php-starterkit
    📗 Docs   : https://os-docs.iml.unibe.ch/docker-php-starterkit/
    📜 License: GNU GPL 3.0
    (c) Institute for Medical Education; University of Bern
    
    
    SYNTAX:
      $_self [-h|-v]
      $_self [menu key [.. menu key N]]
    
    OPTIONS:
      -h   show this help and exit
      -v   show version exit
    
    MENU KEYS:
      In the interactive menu are some keys to init an action.
      The same keys can be put as parameter to start this action.
      You can add multiples keys to apply multiple actions.
    
    $( showMenu "all" )
    
    EXAMPLES:
    
      $_self           starts interactive mode
      $_self u         bring up docker container(s) and stay in interactive mode
      $_self i q       set write permissions and quit
      $_self p q       start php linter and exit
    
    EOH
    }
    
    
    # show urls for app container
    function _showBrowserurl(){
        echo "In a web browser open:"
        echo "  $DC_WEB_URL"
    }
    
    # detect + show ports and urls for app container and db container
    function _showInfos(){
        _showContainers long
        h2 INFO
    
        h3 "processes webserver"
        # docker-compose top
        docker top "${APP_NAME}-server"
        if [ ! "$DB_ADD" = "false" ]; then
            h3 "processes database"
            docker top "${APP_NAME}-db"
        fi
    
        h3 "What to open in browser"
        if echo >"/dev/tcp/localhost/${APP_PORT}"; then
            # echo "OK, app port ${APP_PORT} is reachable"
            # echo
            _showBrowserurl
        else
            echo "ERROR: app port ${APP_PORT} is not available"
        fi 2>/dev/null
    
        if [ "$DB_ADD" != "false" ]; then
            h3 "Check database port"
            if echo >"/dev/tcp/localhost/${DB_PORT}"; then
                echo "OK, db port ${DB_PORT} is reachable"
                echo
                echo "In a local DB admin tool you can connect it:"
                echo "  host    : localhost"
                echo "  port    : ${DB_PORT}"
                echo "  user    : root"
                echo "  password: ${MYSQL_ROOT_PASS}"
            else
                echo "NO, db port ${DB_PORT} is not available"
            fi 2>/dev/null
    
        fi
        echo
    }
    
    # ----------------------------------------------------------------------
    # ACTIONS
    
    # set acl on local directory
    function _setWritepermissions(){
        h2 "set write permissions on ${gittarget} ..."
    
        local _user; _user=$( id -gn )
        local _user_uid; typeset -i _user_uid=0
    
        test -f /etc/subuid && _user_uid=$( grep "$_user" /etc/subuid 2>/dev/null | cut -f 2 -d ':' )-1
        local DOCKER_USER_OUTSIDE; typeset -i DOCKER_USER_OUTSIDE=$_user_uid+$DOCKER_USER_UID
    
        set -vx
    
        for mywritedir in ${WRITABLEDIR}
        do 
    
            echo "--- ${mywritedir}"
            # remove current acl
            sudo setfacl -bR "${mywritedir}"
    
            # default permissions: both the host user and the user with UID 33 (www-data on many systems) are owners with rwx perms
            sudo setfacl -dRm "u:${DOCKER_USER_OUTSIDE}:rwx,${_user}:rwx" "${mywritedir}"
    
            # permissions: make both the host user and the user with UID 33 owner with rwx perms for all existing files/directories
            sudo setfacl -Rm "u:${DOCKER_USER_OUTSIDE}:rwx,${_user}:rwx" "${mywritedir}"
        done
    
        set +vx
    }
    
    # cleanup starterkit git data
    function _removeGitdata(){
        h2 "Remove git data of starterkit"
        echo -n "Current git remote url: "
        git config --get remote.origin.url
        if git config --get remote.origin.url 2>/dev/null | grep -q $selfgitrepo; then
            echo
            echo -n "Delete local .git and .gitignore? [y/N] > "
            read -r answer
            test "$answer" = "y" && ( echo "Deleting ... " && rm -rf ../.git ../.gitignore )
        else
            echo "It was done already - $selfgitrepo was not found."
        fi
    
    }
    
    # helper function: cut a text file starting from database start marker
    # see _generateFiles()
    function _fix_no-db(){
        local _file=$1
        if [ "$DB_ADD" = "false" ]; then
            local iStart; typeset -i iStart
            iStart=$( grep -Fn "$CUTTER_NO_DATABASE" "${_file}" | cut -f 1 -d ':' )-1
            if [ $iStart -gt 0 ]; then
                sed -n "$sed_no_backup" "1,${iStart}p" "${_file}"
            fi
        fi
    }
    
    # helper function to generate replacements using sed
    # it loops over all vars in the config file
    # used in _generateFiles
    function _getreplaces(){
        # loop over vars to make the replacement
        for myfile in "${_self}.cfg" "${_self}_not_shared.cfg"
        do
            grep "^[a-zA-Z]" "${myfile}" | while read -r line
            do
                # echo replacement: $line
                mykey=$( echo "$line" | cut -f 1 -d '=' )
                myvalue="$( eval echo \"\$"$mykey"\" )"
    
                # TODO: multiline values fail here in replacement with sed 
                echo -e "s#{{$mykey}}#${myvalue}#g"
    
            done
        done
    }
    
    # loop over all files in templates subdir make replacements and generate
    # a target file.
    # It skips if 
    #   - 1st line is not starting with "# TARGET: filename"
    #   - target file has no updated lines
    # If the 1st parameter is set to "dryrun" it will not generate files.
    # param string dryrun optional: set to "dryrun" to not generate files
    function _generateFiles(){
    
        local _dryrun="$1"
        DC_CONFIG_CHANGED=0
    
        # shellcheck source=/dev/null
        . "${_self}.cfg" || exit 1    
    
        params=$( _getreplaces | while read -r line; do echo -n "-e '$line' ";  done )
    
        local _tmpfile=/tmp/newfilecontent$$.tmp
        
        test "$_dryrun" = "dryrun" || h2 "generate files from templates..."
        for mytpl in templates/*
        do
            # h3 $mytpl
            local _doReplace=1
    
            # fetch traget file from first line
            target=$( head -1 "$mytpl" | grep "^# TARGET:" | cut -f 2- -d ":" | awk '{ print $1 }' )
    
            if [ -z "$target" ]; then
                if [ "$_dryrun" != "dryrun" ]; then
                    echo "SKIP: $mytpl - target was not found in 1st line"
                fi
                _doReplace=0
            fi
    
            # write generated files to target
            if [ $_doReplace -eq 1 ]; then
    
                # write file from line 2 to a tmp file
                sed -n '2,$p' "$mytpl" >"$_tmpfile"
                chmod "$( stat -c %a "$mytpl" )" "$_tmpfile"
    
                # add generator
                # sed -i "s#{{generator}}#generated by $0 - template: $mytpl - $( date )#g" $_tmpfile
                local _md5; _md5=$( md5sum $_tmpfile | awk '{ print $1 }' )
                sed -i "$sed_no_backup" "s#{{generator}}#GENERATED BY $_self - template: $mytpl - $_md5#g" $_tmpfile
    
                # apply all replacements to the tmp file
                eval sed "$sed_no_backup" "$params" "$_tmpfile" || exit
    
                _fix_no-db $_tmpfile
    
                # echo "changes for $target:"
                if diff --color=always "../$target"  "$_tmpfile" 2>/dev/null | grep -v "$_md5" | grep -v "^---" | grep . || [ ! -f "../$target" ]; then
                    if [ "$_dryrun" = "dryrun" ]
                    then
                        DC_CONFIG_CHANGED=1
                    else
                        echo -n "$mytpl - changes detected - writing [$target] ... "
                        mkdir -p "$( dirname  ../"$target" )" || exit 2
                        mv "$_tmpfile" "../$target" || exit 2
                        echo -e "${fgGreen}OK${fgReset}"
                        echo
                    fi
                else
                    rm -f $_tmpfile
                    if [ "$_dryrun" != "dryrun" ]; then
                        echo "SKIP: $mytpl - Nothing to do."
                    fi
                fi
            fi
        done
    
    }
    
    # loop over all files in templates subdir make replacements and generate
    # a traget file.
    function _removeGeneratedFiles(){
        h2 "remove generated files..."
        for mytpl in templates/*
        do
            h3 "$mytpl"
    
            # fetch traget file from first line
            target=$( head -1 "$mytpl" | grep "^# TARGET:" | cut -f 2- -d ":" | awk '{ print $1 }' )
    
            if [ -n "$target" ] && [ -f "../$target" ]; then
                echo -n "REMOVING "
                ls -l "../$target" || exit 2
                rm -f "../$target" || exit 2
                echo -e "${fgGreen}OK${fgReset}"
            else
                echo "SKIP: $target"
            fi
            
        done
    }
    
    
    # show running containers
    function _showContainers(){
        local bLong=$1
    
        local _out
    
        local sUp=".. UP"
        local sDown=".. down"
    
        local Status=
        local StatusWeb="$sDown"
        local StatusDb="$sDown"
        local colWeb=
        local colDb=
    
        colDb="$fgRed"
        colWeb="$fgRed"
    
        if [ $DC_WEB_UP -eq 1 ]; then
            colWeb="$fgGreen"
            StatusWeb="$sUp"
        fi
        
        if [ $DC_DB_UP -eq 1 ]; then
            colDb="$fgGreen"
            StatusDb="$sUp"
        fi
    
        if [ "$DB_ADD" = "false" ]; then
            colDb="$fgGray"
            local StatusDb=".. N/A"
            Status="This app has no database container."
        fi
    
        h2 CONTAINERS
    
        echo
        printf "  $colWeb$fgInvert  %-32s  $fgReset   $colDb$fgInvert  %-32s  $fgReset\n"      "WEB ${StatusWeb}"  "DB ${StatusDb}"
        printf "    %-32s  $fgReset     %-32s  $fgReset\n"      "PHP ${APP_PHP_VERSION}"      "${MYSQL_IMAGE}"
        printf "    %-32s  $fgReset     %-32s  $fgReset\n"      ":${APP_PORT}"                ":${DB_PORT}"
    
        echo
    
        if [ -n "$Status" ]; then
            echo "  $Status"
            echo
        fi
    
        if [ -n "$bLong" ]; then
            echo "$_out"
    
            h2 STATS
            docker stats --no-stream
            echo
        fi
    
    }
    
    # helper: wait for a return key
    function _wait(){
        local _wait=15
        echo -n "... press RETURN ... or wait $_wait sec > "; read -r -t $_wait
        echo
    }
    
    # DB TOOL - dump db from container
    function _dbDump(){
        local _iKeepDumps;
        typeset -i _iKeepDumps=5
        local _iStart;
        typeset -i _iStart=$_iKeepDumps+1;
    
        if [ $DC_DB_UP -eq 0 ]; then
            echo "Database container is not running. Aborting."
            return
        fi
        outfile=${DC_DUMP_DIR}/${MYSQL_DB}_$( date +%Y%m%d_%H%M%S ).sql
        echo -n "dumping ${MYSQL_DB} ... "
        if docker exec -i "${APP_NAME}-db" mysqldump -uroot -p${MYSQL_ROOT_PASS} ${MYSQL_DB} > "$outfile"; then
            echo -n "OK ... Gzip ... "
            if gzip "${outfile}"; then
                echo "OK"
                ls -l "$outfile.gz"
    
                # CLEANUP
                echo
                echo "--- Cleanup: keep $_iKeepDumps files."
                ls -1t ${DC_DUMP_DIR}/* | sed -n "$_iStart,\$p" | while read -r delfile
                do 
                    echo "CLEANUP: Deleting $delfile ... "
                    rm -f "$delfile"
                done
                echo
                echo -n "Size of dump directory: "
                du -hs ${DC_DUMP_DIR} | awk '{ print $1 }'
    
            else
                echo "ERROR"
                rm -f "$outfile"
            fi
        else
            echo "ERROR"
            rm -f "$outfile"
        fi
    }
    
    # DB TOOL - import local database dump into container
    function _dbImport(){
        echo "--- Available dumps:"
        ls -ltr ${DC_DUMP_DIR}/*.gz | sed "s#^#    #g"
        if [ $DC_DB_UP -eq 0 ]; then
            echo "Database container is not running. Aborting."
            return
        fi
        echo -n "Dump file to import into ${MYSQL_DB} > "
        read -r dumpfile
        if [ -z "$dumpfile" ]; then
            echo "Abort - no value was given."
            return
        fi
        if [ ! -f "$dumpfile" ]; then
            echo "Abort - wrong filename."
            return
        fi
    
        echo -n "Importing $dumpfile ... "
    
        # Mac OS compatibility
        # if zcat "$dumpfile" | docker exec -i "${APP_NAME}-db" mysql -uroot -p${MYSQL_ROOT_PASS} "${MYSQL_DB}"
        if cat "$dumpfile" | zcat | docker exec -i "${APP_NAME}-db" mysql -uroot -p${MYSQL_ROOT_PASS} "${MYSQL_DB}"
        then
            echo "OK"
        else
            echo "ERROR"
        fi
    }
    
    # ----------------------------------------------------------------------
    # MAIN
    # ----------------------------------------------------------------------
    
    _checkConfig
    
    # Mac OS compatibility
    case "$OSTYPE" in
      darwin*|bsd*)
        sed_no_backup=" -i '' "
        ;; 
      *)
        sed_no_backup="-i"
        ;;
    esac
    
    action=$1; shift 1
    
    while true; do
    
        _getStatus_repo
        _getStatus_docker
        _getStatus_template
        _getWebUrl
    
        if [ -z "$action" ]; then
    
            echo "_______________________________________________________________________________"
            echo
            printf "  %-70s ______\n" "${APP_NAME^^}  ::  Initializer for docker"
            echo "________________________________________________________________________/ $_version"
            echo
    
            _showContainers
    
            h2 MENU       
            showMenu
            echo
            echo -n "  select >"
            read -rn 1 action 
            echo
        fi
    
        case "$action" in
            "-h") showHelp; exit 0 ;;
            "-v") echo "$_self $_version"; exit 0 ;;
            g)
                _removeGitdata
                ;;
            i)
                # _gitinstall
                _setWritepermissions
                ;;
            t)
                _generateFiles
                ;;
            T)
                _removeGeneratedFiles
                rm -rf containers
                ;;
            m)
                _showInfos
                _wait
                ;;
            u|U)
                h2 "Bring up..."
                dockerUp="docker-compose -p $APP_NAME --verbose up -d --remove-orphans"
                if [ "$action" = "U" ]; then
                    dockerUp+=" --build"
                fi
                echo "$dockerUp"
                if $dockerUp; then
                    _showBrowserurl
                else
                    echo "ERROR: docker-compose up failed :-/"
                    docker-compose -p "$APP_NAME" logs | tail
                fi
                echo
    
                ;;
            s)
                h2 "Stopping..."
                docker-compose -p "$APP_NAME" stop
                ;;
            r)
                h2 "Removing..."
                docker-compose -p "$APP_NAME" rm -f
                ;;
            c)
                h2 "Console"
                _containers=$( docker-compose -p "$APP_NAME" ps | sed -n "2,\$p" | awk '{ print $1}' )
                if [ "$DB_ADD" = "false" ]; then
                    dockerid=$_containers
                else
                    echo "Select a container:"
                    sed "s#^#    #g" <<< "$_containers"
                    echo -n "id or name >"
                    read -r dockerid
                fi
                test -z "$dockerid" || (
                    echo
                    echo "> docker exec -it $dockerid /bin/bash     (type 'exit' + Return when finished)"
                    docker exec -it "$dockerid" /bin/bash
                )
                ;;
            p)
                h2 "PHP $APP_PHP_VERSION linter"
    
                dockerid="${APP_NAME}-server"
                echo -n "Scanning ... "
                typeset -i _iFiles
                _iFiles=$( docker exec -it "$dockerid" /bin/bash -c "find . -name '*.php' " | wc -l )
    
                if [ $_iFiles -gt 0 ]; then
                    echo "found $_iFiles [*.php] files ... errors from PHP $APP_PHP_VERSION linter:"
                    time if echo "$APP_PHP_VERSION" | grep -E "([567]\.|8\.[012])" >/dev/null ; then
                        docker exec -it "$dockerid" /bin/bash -c "find . -name '*.php' -exec php -l {} \; | grep -v '^No syntax errors detected'"
                    else
                        docker exec -it "$dockerid" /bin/bash -c "php -l \$( find . -name '*.php' ) | grep -v '^No syntax errors detected' "
                    fi
                    echo
                    _wait
                else
                    echo "Start your docker container first."
                fi
                ;;
            d) 
                h2 "DB tools :: dump"
                _dbDump
                ;;
            D) 
                h2 "DB tools :: import"
                _dbImport
                ;;
            M)
                h2 "DB tools :: mysql client"
                docker exec -it "${APP_NAME}-db" mysql -uroot -p${MYSQL_ROOT_PASS} "${MYSQL_DB}"
                ;;
            o) 
                h2 "Open app ..."
                xdg-open "$DC_WEB_URL"
                ;;
            q)
                h2 "Bye!"
                exit 0;
                ;;
            *) 
                test -n "$action" && ( echo "  ACTION FOR [$action] NOT IMPLEMENTED."; sleep 1 )
        esac
        action=$1; shift 1
    done
    
    
    # ----------------------------------------------------------------------