From b2a89970d39396c615c00fbf7f8a3f74e7c806a1 Mon Sep 17 00:00:00 2001 From: "Hahn Axel (hahn)" <axel.hahn@unibe.ch> Date: Mon, 6 Jan 2025 15:03:07 +0100 Subject: [PATCH] update docker dev env --- docker/.env | 2 +- docker/containers/web-server/Dockerfile | 6 +- .../apache/sites-enabled/vhost_app.conf | 2 +- .../web-server/php/extra-php-config.ini | 2 +- docker/docker-compose.yml | 4 +- docker/init.sh | 732 ++++++++++++++---- docker/init.sh.cfg | 7 +- 7 files changed, 612 insertions(+), 143 deletions(-) diff --git a/docker/.env b/docker/.env index 95e9205..0ddeb5f 100644 --- a/docker/.env +++ b/docker/.env @@ -1,6 +1,6 @@ # ====================================================================== # -# GENERATED BY init.sh - template: ./templates/dot_env - e2cde05722688ff85d3a93e9cd55787e +# GENERATED BY init.sh - template: templates/dot_env - e2cde05722688ff85d3a93e9cd55787e # values to be used in docker-composer.yml # # ====================================================================== diff --git a/docker/containers/web-server/Dockerfile b/docker/containers/web-server/Dockerfile index 0c17062..93975a1 100644 --- a/docker/containers/web-server/Dockerfile +++ b/docker/containers/web-server/Dockerfile @@ -1,13 +1,13 @@ # -# GENERATED BY init.sh - template: ./templates/web-server-Dockerfile - 42dce773c83597a7d05af398bdd66d15 +# GENERATED BY init.sh - template: templates/web-server-Dockerfile - 42dce773c83597a7d05af398bdd66d15 # -FROM php:8.2-apache +FROM php:8.4-apache # install packages RUN apt-get update && apt-get install -y git unzip zip libapache2-mod-xsendfile # enable apache modules -RUN a2enmod xsendfile +RUN a2enmod rewrite xsendfile # install php packages COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ diff --git a/docker/containers/web-server/apache/sites-enabled/vhost_app.conf b/docker/containers/web-server/apache/sites-enabled/vhost_app.conf index 6d6e7fb..db98532 100644 --- a/docker/containers/web-server/apache/sites-enabled/vhost_app.conf +++ b/docker/containers/web-server/apache/sites-enabled/vhost_app.conf @@ -1,5 +1,5 @@ # -# GENERATED BY init.sh - template: ./templates/vhost_app.conf - 4dfd63417ad808a5ed00ffaf117464a8 +# GENERATED BY init.sh - template: templates/vhost_app.conf - 4dfd63417ad808a5ed00ffaf117464a8 # <VirtualHost *:80> DocumentRoot /var/www/ci-pkg/public_html diff --git a/docker/containers/web-server/php/extra-php-config.ini b/docker/containers/web-server/php/extra-php-config.ini index aa13bd7..8fc5696 100644 --- a/docker/containers/web-server/php/extra-php-config.ini +++ b/docker/containers/web-server/php/extra-php-config.ini @@ -1,5 +1,5 @@ ; -; GENERATED BY init.sh - template: ./templates/extra-php-config.ini - 9dce36d285d5b21d70e015c074c196c2 +; GENERATED BY init.sh - template: templates/extra-php-config.ini - 9dce36d285d5b21d70e015c074c196c2 ; [PHP] diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 39e932e..85be423 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,5 +1,5 @@ # -# GENERATED BY init.sh - template: ./templates/docker-compose.yml - fc2f1d55926abdb9c54f65afd0571d7b +# GENERATED BY init.sh - template: templates/docker-compose.yml - fc2f1d55926abdb9c54f65afd0571d7b # # ====================================================================== # @@ -19,7 +19,7 @@ services: build: context: . dockerfile: ./containers/web-server/Dockerfile - image: "php:8.2-apache" + image: "php:8.4-apache" container_name: 'ci-pkg-server' ports: - '${APP_PORT}:80' diff --git a/docker/init.sh b/docker/init.sh index 060db24..04fc725 100755 --- a/docker/init.sh +++ b/docker/init.sh @@ -4,53 +4,354 @@ # 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) +# 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 # ====================================================================== -cd $( dirname $0 ) -. $( basename $0 ).cfg +cd "$( dirname "$0" )" || exit 1 + +_version="1.26" + +# init used vars +gittarget= +WEBURL= + +_self=$( basename "$0" ) + +# shellcheck source=/dev/null +. "${_self}.cfg" || exit 1 + # git@git-repo.iml.unibe.ch:iml-open-source/docker-php-starterkit.git selfgitrepo="docker-php-starterkit.git" -_version="1.6" +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 "\e[33m>>>>> $*\e[0m" + echo -e "$fgBrown>>>>> $*$fgReset" } # draw a headline 3 function h3(){ echo - echo -e "\e[34m----- $*\e[0m" + 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 } -# function _gitinstall(){ -# h2 "install/ update app from git repo ${gitrepo} in ${gittarget} ..." -# test -d ${gittarget} && ( cd ${gittarget} && git pull ) -# test -d ${gittarget} || git clone -b ${gitbranch} ${gitrepo} ${gittarget} -# } +# ---------------------------------------------------------------------- +# ACTIONS # set acl on local directory function _setWritepermissions(){ h2 "set write permissions on ${gittarget} ..." - local _user=$( id -gn ) - typeset -i local _user_uid=0 - test -f /etc/subuid && _user_uid=$( grep $_user /etc/subuid 2>/dev/null | cut -f 2 -d ':' )-1 - typeset -i local DOCKER_USER_OUTSIDE=$_user_uid+$DOCKER_USER_UID + 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 @@ -62,10 +363,10 @@ function _setWritepermissions(){ 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}" + 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}" + sudo setfacl -Rm "u:${DOCKER_USER_OUTSIDE}:rwx,${_user}:rwx" "${mywritedir}" done set +vx @@ -76,11 +377,10 @@ function _removeGitdata(){ h2 "Remove git data of starterkit" echo -n "Current git remote url: " git config --get remote.origin.url - git config --get remote.origin.url 2>/dev/null | grep $selfgitrepo >/dev/null - if [ $? -eq 0 ]; then + 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 answer + read -r answer test "$answer" = "y" && ( echo "Deleting ... " && rm -rf ../.git ../.gitignore ) else echo "It was done already - $selfgitrepo was not found." @@ -92,36 +392,64 @@ function _removeGitdata(){ # see _generateFiles() function _fix_no-db(){ local _file=$1 - if [ $DB_ADD = false ]; then - typeset -i local iStart=$( cat ${_file} | grep -Fn "$CUTTER_NO_DATABASE" | cut -f 1 -d ':' )-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 -ni "1,${iStart}p" ${_file} + 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 + grep "^[a-zA-Z]" "$_self.cfg" | 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 +} + # 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(){ - # re-read config vars - . $( basename $0 ).cfg + 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 - h2 "generate files from templates..." - for mytpl in $( ls -1 ./templates/* ) + + 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 }' ) + target=$( head -1 "$mytpl" | grep "^# TARGET:" | cut -f 2- -d ":" | awk '{ print $1 }' ) if [ -z "$target" ]; then - echo SKIP: $mytpl - target was not found in 1st line + if [ "$_dryrun" != "dryrun" ]; then + echo "SKIP: $mytpl - target was not found in 1st line" + fi _doReplace=0 fi @@ -129,39 +457,38 @@ function _generateFiles(){ if [ $_doReplace -eq 1 ]; then # write file from line 2 to a tmp file - sed -n '2,$p' $mytpl >$_tmpfile + 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=$( md5sum $_tmpfile | awk '{ print $1 }' ) - sed -i "s#{{generator}}#GENERATED BY $( basename $0 ) - template: $mytpl - $_md5#g" $_tmpfile - - # loop over vars to make the replacement - grep "^[a-zA-Z]" $( basename $0 ).cfg | while read line - do - # echo replacement: $line - mykey=$( echo $line | cut -f 1 -d '=' ) - myvalue="$( eval echo \"\${$mykey}\" )" - # grep "{{$mykey}}" $_tmpfile - - # TODO: multiline values fail here in replacement with sed - sed -i "s#{{$mykey}}#${myvalue}#g" $_tmpfile - done + 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:" - diff "../$target" "$_tmpfile" | grep -v "$_md5" | grep -v "^---" | grep . - if [ $? -eq 0 -o ! -f "../$target" ]; then - echo -n "$mytpl - changes detected - writing [$target] ... " - mkdir -p $( dirname "../$target" ) || exit 2 - mv "$_tmpfile" "../$target" || exit 2 - echo OK + 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 - echo "SKIP: $mytpl - Nothing to do." + if [ "$_dryrun" != "dryrun" ]; then + echo "SKIP: $mytpl - Nothing to do." + fi fi fi - echo done } @@ -170,104 +497,200 @@ function _generateFiles(){ # a traget file. function _removeGeneratedFiles(){ h2 "remove generated files..." - for mytpl in $( ls -1 ./templates/* ) + for mytpl in templates/* do - h3 $mytpl + h3 "$mytpl" # fetch traget file from first line - target=$( head -1 $mytpl | grep "^# TARGET:" | cut -f 2- -d ":" | awk '{ print $1 }' ) + target=$( head -1 "$mytpl" | grep "^# TARGET:" | cut -f 2- -d ":" | awk '{ print $1 }' ) - if [ ! -z "$target" -a -f "../$target" ]; then + if [ -n "$target" ] && [ -f "../$target" ]; then echo -n "REMOVING " ls -l "../$target" || exit 2 rm -f "../$target" || exit 2 - echo OK + echo -e "${fgGreen}OK${fgReset}" else - echo SKIP: $target + echo "SKIP: $target" fi done } + +# show running containers function _showContainers(){ local bLong=$1 - h2 CONTAINERS - if [ -z "$bLong" ]; then - docker-compose -p "$APP_NAME" ps - else - docker ps | grep $APP_NAME + + 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 -# a bit stupid ... i think I need to delete it. -function _showInfos(){ - _showContainers long - h2 INFO + h2 CONTAINERS - h3 "processes" - docker-compose top + 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 - h3 "Check app port" - >/dev/tcp/localhost/${APP_PORT} 2>/dev/null && ( - echo "OK, app port ${APP_PORT} is reachable" + if [ -n "$Status" ]; then + echo " $Status" echo - echo "In a web browser open:" - echo " $frontendurl" - ) - h3 "Check database port" - >/dev/tcp/localhost/${DB_PORT} 2>/dev/null && ( - echo "OK, db port ${DB_PORT} is reachable" + fi + + if [ -n "$bLong" ]; then + echo "$_out" + + h2 STATS + docker stats --no-stream echo - echo "In a local DB admin tool:" - echo " host : localhost" - echo " port : ${DB_PORT}" - echo " user : root" - echo " password: ${MYSQL_ROOT_PASS}" - ) - echo -} + fi -# helper for menu: print an inverted key -function _key(){ - printf "\e[4;7m ${1} \e[0m" } # helper: wait for a return key function _wait(){ - echo -n "... press RETURN > "; read -r + 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 # ---------------------------------------------------------------------- -action=$1 +_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 - echo - echo -e "\e[32m===== INITIALIZER FOR DOCKER APP [$APP_NAME] v$_version ===== \e[0m\n\r" - if [ -z "$action" ]; then + _getStatus_repo + _getStatus_docker + _getStatus_template + _getWebUrl - _showContainers + if [ -z "$action" ]; then - h2 MENU - echo " $( _key g ) - remove git data of starterkit" - echo - echo " $( _key i ) - init application: set permissions" - echo " $( _key t ) - generate files from templates" - echo " $( _key T ) - remove generated files" + echo "_______________________________________________________________________________" echo - echo " $( _key u ) - startup containers docker-compose ... up -d" - echo " $( _key U ) - startup containers docker-compose ... up -d --build" - echo " $( _key s ) - shutdown containers docker-compose stop" - echo " $( _key r ) - remove containers docker-compose rm -f" + printf " %-70s ______\n" "${APP_NAME^^} :: Initializer for docker" + echo "________________________________________________________________________/ $_version" echo - echo " $( _key m ) - more infos" - echo " $( _key c ) - console (bash)" - echo - echo " $( _key q ) - quit" + + _showContainers + + h2 MENU + showMenu echo echo -n " select >" read -rn 1 action @@ -275,6 +698,8 @@ while true; do fi case "$action" in + "-h") showHelp; exit 0 ;; + "-v") echo "$_self $_version"; exit 0 ;; g) _removeGitdata ;; @@ -289,51 +714,96 @@ while true; do _removeGeneratedFiles rm -rf containers ;; - # not in the menu - # f) - # _removeGeneratedFiles - # _generateFiles - # _wait - # ;; m) _showInfos _wait ;; u|U) - dockerUp="docker-compose -p "$APP_NAME" --verbose up -d --remove-orphans" + 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 - echo "In a web browser:" - echo " $frontendurl" + _showBrowserurl else echo "ERROR: docker-compose up failed :-/" docker-compose -p "$APP_NAME" logs | tail fi echo - _wait ;; s) + h2 "Stopping..." docker-compose -p "$APP_NAME" stop ;; r) + h2 "Removing..." docker-compose -p "$APP_NAME" rm -f ;; c) - docker ps - echo -n "id or name >" - read dockerid - test -z "$dockerid" || docker exec -it $dockerid /bin/bash + 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= + action=$1; shift 1 done diff --git a/docker/init.sh.cfg b/docker/init.sh.cfg index b96727e..7b4f3fc 100644 --- a/docker/init.sh.cfg +++ b/docker/init.sh.cfg @@ -15,9 +15,9 @@ APP_PORT=8001 APP_APT_PACKAGES="git unzip zip libapache2-mod-xsendfile" #APP_APACHE_MODULES="rewrite" -APP_APACHE_MODULES="xsendfile" +APP_APACHE_MODULES="rewrite xsendfile" -APP_PHP_VERSION=8.2 +APP_PHP_VERSION=8.4 # APP_PHP_MODULES="curl pdo_mysql mbstring xml zip xdebug" APP_PHP_MODULES="" @@ -60,9 +60,8 @@ DOCKER_USER_UID=33 # document root inside web-server container WEBROOT=/var/www/${APP_NAME}/public_html +WEBURL="/" CUTTER_NO_DATABASE="CUT-HERE-FOR-NO-DATABASE" -frontendurl=http://localhost:${APP_PORT}/ - # ---------------------------------------------------------------------- -- GitLab