From d93bf99276ba530c426b7698570a0f8f8add05f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Mon, 26 Aug 2024 17:44:25 +0200 Subject: [PATCH 01/24] compiled installRequirements using go compiler --- .bash-compiler | 2 + .pre-commit-config-github.yaml | 9 +- .pre-commit-config.yaml | 9 +- README.md | 9 + bin/installRequirements | 2184 +++++++++-------- .../build/installRequirements.options.tpl | 18 - src/_binaries/build/installRequirements.sh | 24 - .../binary-installRequirements.yaml | 26 + .../installRequirements-main.sh | 11 + .../installRequirements-options.sh | 13 + 10 files changed, 1207 insertions(+), 1098 deletions(-) create mode 100644 .bash-compiler delete mode 100644 src/_binaries/build/installRequirements.options.tpl delete mode 100755 src/_binaries/build/installRequirements.sh create mode 100644 src/_binaries/build/installRequirements/binary-installRequirements.yaml create mode 100755 src/_binaries/build/installRequirements/installRequirements-main.sh create mode 100755 src/_binaries/build/installRequirements/installRequirements-options.sh diff --git a/.bash-compiler b/.bash-compiler new file mode 100644 index 00000000..1124bd26 --- /dev/null +++ b/.bash-compiler @@ -0,0 +1,2 @@ +FRAMEWORK_ROOT_DIR=${FRAMEWORK_ROOT_DIR:-vendor/bash-tools-framework} +BASH_TOOLS_ROOT_DIR=${BASH_TOOLS_ROOT_DIR:-.} diff --git a/.pre-commit-config-github.yaml b/.pre-commit-config-github.yaml index 6fa9aca4..0dbcb0df 100644 --- a/.pre-commit-config-github.yaml +++ b/.pre-commit-config-github.yaml @@ -161,12 +161,10 @@ repos: exclude: /testsData/ - repo: https://github.com/fchastanet/bash-tools-framework - rev: 4.0.0 + rev: 5.0.0 hooks: - id: fixShebangExecutionBit - id: awkLint - - id: buildShFiles - - id: buildShFilesGithubAction - id: shellcheckLint - id: shellcheckLintGithubAction - id: frameworkLint @@ -205,3 +203,8 @@ repos: pass_filenames: false require_serial: true stages: [manual] # GITHUB + + - repo: https://github.com/fchastanet/bash-compiler + rev: 0.2.9 + hooks: + - id: buildBashBinaries diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index aa20b251..aa8d97d4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -156,12 +156,10 @@ repos: exclude: /testsData/ - repo: https://github.com/fchastanet/bash-tools-framework - rev: 4.0.0 + rev: 5.0.0 hooks: - id: fixShebangExecutionBit - id: awkLint - - id: buildShFiles - - id: buildShFilesGithubAction - id: shellcheckLint - id: shellcheckLintGithubAction - id: frameworkLint @@ -200,3 +198,8 @@ repos: pass_filenames: false require_serial: true stages: [] # GITHUB + + - repo: https://github.com/fchastanet/bash-compiler + rev: 0.2.9 + hooks: + - id: buildBashBinaries diff --git a/README.md b/README.md index aeca78ad..8a4bcbd9 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ - [3. Development Environment](#3-development-environment) - [3.1. build dependencies](#31-build-dependencies) - [3.2. Precommit hook](#32-precommit-hook) + - [Compile binaries](#compile-binaries) - [3.3. UT](#33-ut) - [3.4. auto generated bash doc](#34-auto-generated-bash-doc) - [3.5. github page](#35-github-page) @@ -150,6 +151,14 @@ You also have to execute the following command to enable it: pre-commit install --hook-type pre-commit --hook-type pre-push ``` +### Compile binaries + +```bash +export FRAMEWORK_ROOT_DIR=/home/wsl/fchastanet/bash-tools/vendor/bash-tools-framework +export BASH_TOOLS_ROOT_DIR=/home/wsl/fchastanet/bash-tools +go run ./cmd/bash-compiler $(find "${BASH_TOOLS_ROOT_DIR}/src/_binaries" -name 'binary-*.yaml' -print) +``` + ### 3.3. UT All the commands are unit tested, you can run the unit tests using the following diff --git a/bin/installRequirements b/bin/installRequirements index 7475530f..669836f9 100755 --- a/bin/installRequirements +++ b/bin/installRequirements @@ -1,13 +1,12 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FACADE FROM https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/build/installRequirements.sh +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/build/installRequirements/binary-installRequirements.yaml # DO NOT EDIT IT # @generated ############################################################################### # shellcheck disable=SC2288,SC2034 -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/installRequirements -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE + + # ensure that no user aliases could interfere with # commands used in this script @@ -62,13 +61,6 @@ interruptManagement() { kill -s INT "$$" } trap interruptManagement INT -SCRIPT_NAME=${0##*/} -REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" -if [[ -n "${EMBED_CURRENT_DIR}" ]]; then - CURRENT_DIR="${EMBED_CURRENT_DIR}" -else - CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" -fi ################################################ # Temp dir management @@ -102,6 +94,20 @@ cleanOnExit() { } trap cleanOnExit EXIT HUP QUIT ABRT TERM + +SCRIPT_NAME=${0##*/} +REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" +if [[ -n "${EMBED_CURRENT_DIR}" ]]; then + CURRENT_DIR="${EMBED_CURRENT_DIR}" +else + CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" +fi +FRAMEWORK_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" +FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" +FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + # @description Log namespace provides 2 kind of functions # - Log::display* allows to display given message with # given display level @@ -132,375 +138,218 @@ export __VERBOSE_LEVEL_DEBUG=2 # @description verbose level info export __VERBOSE_LEVEL_TRACE=3 -# @description Display message using info color (bg light blue/fg white) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayInfo() { - local type="${2:-INFO}" - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description concatenate each element of an array with a separator +# but wrapping text when line length is more than provided argument +# The algorithm will try not to cut the array element if it can. +# - if an arg can be placed on current line it will be, +# otherwise current line is printed and arg is added to the new +# current line +# - Empty arg is interpreted as a new line. +# - Add \r to arg in order to force break line and avoid following +# arg to be concatenated with current arg. +# +# @arg $1 glue:String +# @arg $2 maxLineLength:int +# @arg $3 indentNextLine:int +# @arg $@ array:String[] +Array::wrap2() { + local glue="${1-}" + local -i glueLength="${#glue}" + shift || true + local -i maxLineLength=$1 + shift || true + local -i indentNextLine=$1 + shift || true + local indentStr="" + if ((indentNextLine > 0)); then + indentStr="$(head -c "${indentNextLine}" 0)); do + arg="$1" + shift || true + + # replace tab by 2 spaces + arg="${arg//$'\t'/ }" + # remove trailing spaces + arg="${arg%[[:blank:]]}" + if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then + printCurrentLine + ((previousLineEmpty = 1)) + continue + else + if ((previousLineEmpty == 1)); then + printCurrentLine + fi + ((previousLineEmpty = 0)) || true + fi + # convert eol to args + mapfile -t additionalLines <<<"${arg}" + if ((${#additionalLines[@]} > 1)); then + set -- "${additionalLines[@]}" "$@" + continue + fi + + ((argLength = ${#arg})) || true + + # empty arg + if ((argLength == 0)); then + if ((isNewline == 0)); then + # isNewline = 0 means currentLine is not empty + printCurrentLine + fi + continue + fi + + if ((isNewline == 0)); then + glueLength="${#glue}" + else + glueLength="0" + fi + if ((currentLineLength + argLength + glueLength > maxLineLength)); then + if ((argLength + glueLength > maxLineLength)); then + # arg is too long to even fit on one line + # we have to split the arg on current and next line + local -i remainingLineLength + ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) + appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" + printCurrentLine + arg="${arg:${remainingLineLength}}" + # remove leading spaces + arg="${arg##[[:blank:]]}" + + set -- "${arg}" "$@" + else + # the arg can fit on next line + printCurrentLine + appendToCurrentLine "${arg}" "${argLength}" + fi + else + appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" + fi + done + if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then + printCurrentLine + fi + ) | sed -E -e 's/[[:blank:]]+$//' } -# @description Display message using debug color (gray) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayDebug() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then - Log::computeDuration - echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logDebug "$1" + +# @description check if command specified exists or return 1 +# with error and message if not +# +# @arg $1 commandName:String on which existence must be checked +# @arg $2 helpIfNotExists:String a help command to display if the command does not exist +# +# @exitcode 1 if the command specified does not exist +# @stderr diagnostic information + help if second argument is provided +Assert::commandExists() { + local commandName="$1" + local helpIfNotExists="$2" + + "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { + Log::displayError "${commandName} is not installed, please install it" + if [[ -n "${helpIfNotExists}" ]]; then + Log::displayInfo "${helpIfNotExists}" + fi + return 1 + } + return 0 } -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description check if tty (interactive mode) is active +# @noargs +# @exitcode 1 if tty not active +# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive +# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive +Assert::tty() { + if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then + return 1 fi - Log::logWarning "$1" + if [[ "${INTERACTIVE:-0}" = "1" ]]; then + return 0 + fi + tty -s } -# @description Display message using error color (red) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayError() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then - Log::computeDuration - echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description ensure env files are loaded +# @arg $@ list of default files to load at the end +# @exitcode 1 if one of env files fails to load +# @stderr diagnostics information is displayed +# shellcheck disable=SC2120 +Env::requireLoad() { + REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 + + local -a defaultFiles=("$@") + # get list of possible config files + local -a configFiles=() + if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then + # BASH_FRAMEWORK_ENV_FILES is an array + configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") fi - Log::logError "$1" + local localFrameworkConfigFile + localFrameworkConfigFile="$(pwd)/.framework-config" + if [[ -f "${localFrameworkConfigFile}" ]]; then + configFiles+=("${localFrameworkConfigFile}") + fi + if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then + configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") + fi + configFiles+=("${optionEnvFiles[@]}") + configFiles+=("${defaultFiles[@]}") + + for file in "${configFiles[@]}"; do + # shellcheck source=/.framework-config + CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { + Log::displayError "while loading config file: ${file}" + return 1 + } + done } -# @description load colors theme constants -# @warning if tty not opened, noColor theme will be chosen -# @arg $1 theme:String the theme to use (default, noColor) -# @arg $@ args:String[] -# @set __ERROR_COLOR String indicate error status -# @set __INFO_COLOR String indicate info status -# @set __SUCCESS_COLOR String indicate success status -# @set __WARNING_COLOR String indicate warning status -# @set __SKIPPED_COLOR String indicate skipped status -# @set __DEBUG_COLOR String indicate debug status -# @set __HELP_COLOR String indicate help status -# @set __TEST_COLOR String not used -# @set __TEST_ERROR_COLOR String not used -# @set __HELP_TITLE_COLOR String used to display help title in help strings -# @set __HELP_OPTION_COLOR String used to display highlight options in help strings -# -# @set __RESET_COLOR String reset default color -# -# @set __HELP_EXAMPLE String to remove -# @set __HELP_TITLE String to remove -# @set __HELP_NORMAL String to remove -# shellcheck disable=SC2034 -UI::theme() { - local theme="${1-default}" - if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then - theme="noColor" - fi - case "${theme}" in - default | default-force) - theme="default" - ;; - noColor) ;; - *) - Log::fatal "invalid theme provided" - ;; - esac - if [[ "${theme}" = "default" ]]; then - BASH_FRAMEWORK_THEME="default" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='\e[31m' # Red - __INFO_COLOR='\e[44m' # white on lightBlue - __SUCCESS_COLOR='\e[32m' # Green - __WARNING_COLOR='\e[33m' # Yellow - __SKIPPED_COLOR='\e[33m' # Yellow - __DEBUG_COLOR='\e[37m' # Gray - __HELP_COLOR='\e[7;49;33m' # Black on Gold - __TEST_COLOR='\e[100m' # Light magenta - __TEST_ERROR_COLOR='\e[41m' # white on red - __HELP_TITLE_COLOR="\e[1;37m" # Bold - __HELP_OPTION_COLOR="\e[1;34m" # Blue - # Internal: reset color - __RESET_COLOR='\e[0m' # Reset Color - # shellcheck disable=SC2155,SC2034 - __HELP_EXAMPLE="$(echo -e "\e[2;97m")" - # shellcheck disable=SC2155,SC2034 - __HELP_TITLE="$(echo -e "\e[1;37m")" - # shellcheck disable=SC2155,SC2034 - __HELP_NORMAL="$(echo -e "\033[0m")" - else - BASH_FRAMEWORK_THEME="noColor" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='' - __INFO_COLOR='' - __SUCCESS_COLOR='' - __WARNING_COLOR='' - __SKIPPED_COLOR='' - __DEBUG_COLOR='' - __HELP_COLOR='' - __TEST_COLOR='' - __TEST_ERROR_COLOR='' - __HELP_TITLE_COLOR='' - __HELP_OPTION_COLOR='' - # Internal: reset color - __RESET_COLOR='' - __HELP_EXAMPLE='' - __HELP_TITLE='' - __HELP_NORMAL='' - fi -} - -# @description draw a line with the character passed in parameter repeated depending on terminal width -# @arg $1 character:String character to use as separator (default value #) -UI::drawLine() { - local character="${1:-#}" - local -i width=${COLUMNS:-0} - if ((width == 0)) && [[ -t 1 ]]; then - width=$(tput cols) - fi - if ((width == 0)); then - width=80 - fi - printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") - echo -} - -# @description Display message using error color (red) and exit immediately with error status 1 -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::fatal() { - Log::computeDuration - echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - Log::logFatal "$1" - exit 1 -} # @description create a temp file using default TMPDIR variable -# initialized in _includes/_commonHeader.sh # @env TMPDIR String (default value /tmp) # @arg $1 templateName:String template name to use(optional) Framework::createTempFile() { mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" } -# @description ensure env files are loaded -# @arg $@ list of default files to load at the end -# @exitcode 1 if one of env files fails to load -# @stderr diagnostics information is displayed -# shellcheck disable=SC2120 -Env::requireLoad() { - local -a defaultFiles=("$@") - # get list of possible config files - local -a configFiles=() - if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then - # BASH_FRAMEWORK_ENV_FILES is an array - configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") - fi - local localFrameworkConfigFile - localFrameworkConfigFile="$(pwd)/.framework-config" - if [[ -f "${localFrameworkConfigFile}" ]]; then - configFiles+=("${localFrameworkConfigFile}") - fi - if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then - configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") - fi - configFiles+=("${optionEnvFiles[@]}") - configFiles+=("${defaultFiles[@]}") - - for file in "${configFiles[@]}"; do - # shellcheck source=/.framework-config - CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { - Log::displayError "while loading config file: ${file}" - return 1 - } - done -} - -# @description activate or not Log::display* and Log::log* functions -# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL -# environment variables loaded by Env::requireLoad -# try to create log file and rotate it if necessary -# @noargs -# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable -# @env BASH_FRAMEWORK_DISPLAY_LEVEL int -# @env BASH_FRAMEWORK_LOG_LEVEL int -# @env BASH_FRAMEWORK_LOG_FILE String -# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 -# @exitcode 0 always successful -# @stderr diagnostics information about log file is displayed -# @require Env::requireLoad -# @require UI::requireTheme -Log::requireLoad() { - if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - export BASH_FRAMEWORK_LOG_LEVEL - fi - - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then - if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 - fi - elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - fi - - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - # will always be created even if not in info level - Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" - if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then - Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" - fi - fi -} - -# @description concatenate each element of an array with a separator -# but wrapping text when line length is more than provided argument -# The algorithm will try not to cut the array element if it can. -# - if an arg can be placed on current line it will be, -# otherwise current line is printed and arg is added to the new -# current line -# - Empty arg is interpreted as a new line. -# - Add \r to arg in order to force break line and avoid following -# arg to be concatenated with current arg. -# -# @arg $1 glue:String -# @arg $2 maxLineLength:int -# @arg $3 indentNextLine:int -# @arg $@ array:String[] -Array::wrap2() { - local glue="${1-}" - local -i glueLength="${#glue}" - shift || true - local -i maxLineLength=$1 - shift || true - local -i indentNextLine=$1 - shift || true - local indentStr="" - if ((indentNextLine > 0)); then - indentStr="$(head -c "${indentNextLine}" 0)); do - arg="$1" - shift || true - - # replace tab by 2 spaces - arg="${arg//$'\t'/ }" - # remove trailing spaces - arg="${arg%[[:blank:]]}" - if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then - printCurrentLine - ((previousLineEmpty = 1)) - continue - else - if ((previousLineEmpty == 1)); then - printCurrentLine - fi - ((previousLineEmpty = 0)) || true - fi - # convert eol to args - mapfile -t additionalLines <<<"${arg}" - if ((${#additionalLines[@]} > 1)); then - set -- "${additionalLines[@]}" "$@" - continue - fi - - ((argLength = ${#arg})) || true - - # empty arg - if ((argLength == 0)); then - if ((isNewline == 0)); then - # isNewline = 0 means currentLine is not empty - printCurrentLine - fi - continue - fi - - if ((isNewline == 0)); then - glueLength="${#glue}" - else - glueLength="0" - fi - if ((currentLineLength + argLength + glueLength > maxLineLength)); then - if ((argLength + glueLength > maxLineLength)); then - # arg is too long to even fit on one line - # we have to split the arg on current and next line - local -i remainingLineLength - ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) - appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" - printCurrentLine - arg="${arg:${remainingLineLength}}" - # remove leading spaces - arg="${arg##[[:blank:]]}" - - set -- "${arg}" "$@" - else - # the arg can fit on next line - printCurrentLine - appendToCurrentLine "${arg}" "${argLength}" - fi - else - appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" - fi - done - if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then - printCurrentLine - fi - ) | sed -E -e 's/[[:blank:]]+$//' -} # @description clone the repository if not done yet, else pull it if no change in it # @arg $1 dir:String directory in which repository is installed or will be cloned @@ -523,11 +372,10 @@ Git::cloneOrPullIfNoChanges() { if [[ -d "${dir}/.git" ]]; then local exitCode=0 Git::pullIfNoChanges "${dir}" || exitCode=$? - if Array::contains "${exitCode}" "2" "4"; then - # changes detected + if [[ "${exitCode}" =~ ^[245]$ ]]; then + # changes detected or pull not applicable return 0 - fi - if [[ "${exitCode}" != "0" ]]; then + elif [[ "${exitCode}" != "0" ]]; then return "${exitCode}" fi # shellcheck disable=SC2086 @@ -552,115 +400,58 @@ Git::cloneOrPullIfNoChanges() { fi } -bashToolsDefaultConfigTemplate="${bashToolsDefaultConfigTemplate:-$( - cat <<'EOF' -# shellcheck disable=SC2034 -# Default settings -# you can override these settings by creating ${HOME}/.bash-tools/.env file - -### -### DISPLAY Level -### minimum level of the messages that will be displayed on screen -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} - -### -### DISPLAY duration -### 0: no duration is displayed on the messages -### 1: duration between previous message and current is displayed -### with the message -### -DISPLAY_DURATION=${DISPLAY_DURATION:0} - -### -### Log to file -### -### all log messages will be redirected to log file specified -### this same path will be used inside and outside of the container -### -BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} - -### -### LOG Level -### minimum level of the messages that will be logged into LOG_FILE -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} - -# absolute directory containing db import sql dumps -DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} - -# garbage collect all files for which modification is greater than eg: 30 days (+30) -# each time an existing file is used by dbImport/dbImportTable -# the file modification time is set to now -DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} - -# absolute directory containing dbScripts used by dbScriptAllDatabases -SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} - -# ----------------------------------------------------- -# AWS Parameters -# ----------------------------------------------------- -S3_BASE_URL=${S3_BASE_URL:-} - -# ----------------------------------------------------- -# Postman Parameters -# ----------------------------------------------------- -POSTMAN_API_KEY= -EOF -)}" - -# @description loads ~/.bash-tools/.env if available -# if not creates it from a default template -# else check if new options need to be added -BashTools::Conf::requireLoad() { - local envFile="${HOME}/.bash-tools/.env" - if [[ ! -f "${envFile}" ]]; then - mkdir -p "${HOME}/.bash-tools" - ( - echo "#!/usr/bin/env bash" - echo "${bashToolsDefaultConfigTemplate}" - ) >"${envFile}" - Log::displayInfo "Configuration file '${envFile}' created" - else - if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then - ( - echo '# -----------------------------------------------------' - echo '# Postman Parameters' - echo '# -----------------------------------------------------' - echo 'POSTMAN_API_KEY=' - ) >>"${envFile}" - fi - fi - # shellcheck source=/conf/.env - source "${envFile}" || { - Log::displayError "impossible to load '${envFile}'" +# @description pull git directory only if no change has been detected +# @arg $1 dir:String the git directory to pull +# @exitcode 0 on successful pulling +# @exitcode 1 on any other failure +# @exitcode 2 changes detected, pull avoided +# @exitcode 3 not a git directory +# @exitcode 4 not able to update index +# @exitcode 5 not a branch, pull avoided +# @stderr diagnostics information is displayed +# @env SUDO String allows to use custom sudo prefix command +Git::pullIfNoChanges() { + + if [[ "${REQUIRE_FUNCTION_GIT_REQUIRE_GIT_COMMAND_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Git::requireGitCommand has not been loaded" exit 1 - } + fi + + local dir="$1" + if [[ ! -d "${dir}/.git" ]]; then + return 3 + fi + ( + cd "${dir}" || return 3 + if ! ${SUDO:-} git update-index --refresh &>/dev/null; then + Log::displayWarning "Impossible to update git index of '${dir}' - check if you have modified file" + return 4 + fi + if ! ${SUDO:-} git diff-index --quiet HEAD --; then + Log::displayWarning "Pulling git repository '${dir}' avoided as changes detected" + return 2 + fi + if ! ${SUDO:-} git symbolic-ref -q HEAD; then + Log::displayWarning "Pulling git repository '${dir}' avoided as you are not on a branch" + return 5 + fi + Log::displayInfo "Pull git repository '${dir}' as no changes detected" + ${SUDO:-} git pull --progress + ) } -# @description ensure COMMAND_BIN_DIR env var is set -# and PATH correctly prepared -# @noargs -# @set COMMAND_BIN_DIR string the directory where to find this command -# @set PATH string add directory where to find this command binary -Compiler::Facade::requireCommandBinDir() { - COMMAND_BIN_DIR="${CURRENT_DIR}" - Env::pathPrepend "${COMMAND_BIN_DIR}" + +# @description ensure command git is available +# @exitcode 1 if git command not available +# @stderr diagnostics information is displayed +Git::requireGitCommand() { + REQUIRE_FUNCTION_GIT_REQUIRE_GIT_COMMAND_LOADED=1 + + Assert::commandExists git } + # @description ensure running user is not root # @exitcode 1 if current user is root # @stderr diagnostics information is displayed @@ -670,52 +461,111 @@ Linux::requireExecutedAsUser() { fi } -declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR -FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" -LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" -LOG_LAST_LOG_DATE_INIT=1 -LOG_LAST_DURATION_STR="" -# @description compute duration since last call to this function -# the result is set in following env variables. -# in ss.sss (seconds followed by milliseconds precision 3 decimals) -# @noargs +declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR +FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" +LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" +LOG_LAST_LOG_DATE_INIT=1 +LOG_LAST_DURATION_STR="" + +# @description compute duration since last call to this function +# the result is set in following env variables. +# in ss.sss (seconds followed by milliseconds precision 3 decimals) +# @noargs +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @set LOG_LAST_LOG_DATE_INIT int (default 1) set to 0 at first call, allows to detect reference log +# @set LOG_LAST_DURATION_STR String the last duration displayed +# @set LOG_LAST_LOG_DATE String the last log date that will be used to compute next diff +Log::computeDuration() { + if ((${DISPLAY_DURATION:-0} == 1)); then + local -i duration=0 + local -i delta=0 + local -i currentLogDate + currentLogDate="${EPOCHREALTIME/[^0-9]/}" + if ((LOG_LAST_LOG_DATE_INIT == 1)); then + LOG_LAST_LOG_DATE_INIT=0 + LOG_LAST_DURATION_STR="Ref" + else + duration=$(((currentLogDate - FIRST_LOG_DATE) / 1000000)) + delta=$(((currentLogDate - LOG_LAST_LOG_DATE) / 1000000)) + LOG_LAST_DURATION_STR="${duration}s/+${delta}s" + fi + LOG_LAST_LOG_DATE="${currentLogDate}" + # shellcheck disable=SC2034 + local microSeconds="${EPOCHREALTIME#*.}" + LOG_LAST_DURATION_STR="$(printf '%(%T)T.%03.0f\n' "${EPOCHSECONDS}" "${microSeconds:0:3}")(${LOG_LAST_DURATION_STR}) - " + else + # shellcheck disable=SC2034 + LOG_LAST_DURATION_STR="" + fi +} + + +# @description Display message using debug color (gray) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayDebug() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then + Log::computeDuration + echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logDebug "$1" +} + + +# @description Display message using error color (red) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayError() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then + Log::computeDuration + echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logError "$1" +} + + +# @description Display message using info color (bg light blue/fg white) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayInfo() { + local type="${2:-INFO}" + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logInfo "$1" "${type}" +} + + +# @description Display message using warning color (yellow) +# @arg $1 message:String the message to display # @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @set LOG_LAST_LOG_DATE_INIT int (default 1) set to 0 at first call, allows to detect reference log -# @set LOG_LAST_DURATION_STR String the last duration displayed -# @set LOG_LAST_LOG_DATE String the last log date that will be used to compute next diff -Log::computeDuration() { - if ((${DISPLAY_DURATION:-0} == 1)); then - local -i duration=0 - local -i delta=0 - local -i currentLogDate - currentLogDate="${EPOCHREALTIME/[^0-9]/}" - if ((LOG_LAST_LOG_DATE_INIT == 1)); then - LOG_LAST_LOG_DATE_INIT=0 - LOG_LAST_DURATION_STR="Ref" - else - duration=$(((currentLogDate - FIRST_LOG_DATE) / 1000000)) - delta=$(((currentLogDate - LOG_LAST_LOG_DATE) / 1000000)) - LOG_LAST_DURATION_STR="${duration}s/+${delta}s" - fi - LOG_LAST_LOG_DATE="${currentLogDate}" - # shellcheck disable=SC2034 - local microSeconds="${EPOCHREALTIME#*.}" - LOG_LAST_DURATION_STR="$(printf '%(%T)T.%03.0f\n' "${EPOCHSECONDS}" "${microSeconds:0:3}")(${LOG_LAST_DURATION_STR}) - " - else - # shellcheck disable=SC2034 - LOG_LAST_DURATION_STR="" +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayWarning() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then + Log::computeDuration + echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logWarning "$1" } -# @description log message to file + +# @description Display message using error color (red) and exit immediately with error status 1 # @arg $1 message:String the message to display -Log::logInfo() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then - Log::logMessage "${2:-INFO}" "$1" - fi +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::fatal() { + Log::computeDuration + echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + Log::logFatal "$1" + exit 1 } + # @description log message to file # @arg $1 message:String the message to display Log::logDebug() { @@ -724,13 +574,6 @@ Log::logDebug() { fi } -# @description log message to file -# @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" - fi -} # @description log message to file # @arg $1 message:String the message to display @@ -740,20 +583,6 @@ Log::logError() { fi } -# @description check if tty (interactive mode) is active -# @noargs -# @exitcode 1 if tty not active -# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive -# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive -Assert::tty() { - if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then - return 1 - fi - if [[ "${INTERACTIVE:-0}" = "1" ]]; then - return 0 - fi - tty -s -} # @description log message to file # @arg $1 message:String the message to display @@ -761,6 +590,16 @@ Log::logFatal() { Log::logMessage "${2:-FATAL}" "$1" } + +# @description log message to file +# @arg $1 message:String the message to display +Log::logInfo() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then + Log::logMessage "${2:-INFO}" "$1" + fi +} + + # @description Internal: common log message # @example text # [date]|[levelMsg]|message @@ -774,9 +613,18 @@ Log::logFatal() { # @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty # @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages # @stderr diagnostics information is displayed -# @require Env::requireLoad -# @require Log::requireLoad Log::logMessage() { + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Log::requireLoad has not been loaded" + exit 1 + fi + local levelMsg="$1" local msg="$2" local date @@ -788,6 +636,74 @@ Log::logMessage() { fi } + +# @description log message to file +# @arg $1 message:String the message to display +Log::logWarning() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then + Log::logMessage "${2:-WARNING}" "$1" + fi +} + + +# @description activate or not Log::display* and Log::log* functions +# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL +# environment variables loaded by Env::requireLoad +# try to create log file and rotate it if necessary +# @noargs +# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable +# @env BASH_FRAMEWORK_DISPLAY_LEVEL int +# @env BASH_FRAMEWORK_LOG_LEVEL int +# @env BASH_FRAMEWORK_LOG_FILE String +# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 +# @exitcode 0 always successful +# @stderr diagnostics information about log file is displayed +Log::requireLoad() { + REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement UI::requireTheme has not been loaded" + exit 1 + fi + + if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + export BASH_FRAMEWORK_LOG_LEVEL + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then + if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 + fi + elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + # will always be created even if not in info level + Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" + if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then + Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + fi + fi +} + + # @description To be called before logging in the log file # @arg $1 file:string log file name # @arg $2 maxLogFilesCount:int maximum number of log files @@ -810,63 +726,22 @@ Log::rotate() { fi } -# @description pull git directory only if no change has been detected -# @arg $1 dir:String the git directory to pull -# @exitcode 0 on successful pulling -# @exitcode 1 on any other failure -# @exitcode 2 changes detected, pull avoided -# @exitcode 3 not a git directory -# @exitcode 4 not able to update index -# @stderr diagnostics information is displayed -# @env SUDO String allows to use custom sudo prefix command -# @require Git::requireGitCommand -Git::pullIfNoChanges() { - local dir="$1" - if [[ ! -d "${dir}/.git" ]]; then - return 3 - fi - ( - cd "${dir}" || return 3 - if ! ${SUDO:-} git update-index --refresh &>/dev/null; then - Log::displayWarning "Impossible to update git index of '${dir}' - check if you have modified file" - return 4 - fi - if ! ${SUDO:-} git diff-index --quiet HEAD --; then - Log::displayWarning "Pulling git repository '${dir}' avoided as changes detected" - return 2 - fi - Log::displayInfo "Pull git repository '${dir}' as no changes detected" - ${SUDO:-} git pull --progress - ) -} -# @description check if an element is contained in an array -# -# @arg $1 needle:String -# @arg $@ array:String[] -# @exitcode 0 if found -# @exitcode 1 otherwise -# @example -# Array::contains "${libPath}" "${__BASH_FRAMEWORK_IMPORTED_FILES[@]}" -Array::contains() { - local element - for element in "${@:2}"; do - [[ "${element}" = "$1" ]] && return 0 - done - return 1 +# @description draw a line with the character passed in parameter repeated depending on terminal width +# @arg $1 character:String character to use as separator (default value #) +UI::drawLine() { + local character="${1:-#}" + local -i width=${COLUMNS:-0} + if ((width == 0)) && [[ -t 1 ]]; then + width=$(tput cols) + fi + if ((width == 0)); then + width=80 + fi + printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") + echo } -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" - fi - done -} # @description load color theme # @noargs @@ -874,76 +749,104 @@ Env::pathPrepend() { # @env LOAD_THEME int 0 to avoid loading theme # @exitcode 0 always successful UI::requireTheme() { + REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + if [[ "${LOAD_THEME:-1}" = "1" ]]; then UI::theme "${BASH_FRAMEWORK_THEME-default}" fi } - -# @description ensure command git is available -# @exitcode 1 if git command not available -# @stderr diagnostics information is displayed -Git::requireGitCommand() { - Assert::commandExists git -} - -# @description check if command specified exists or return 1 -# with error and message if not -# -# @arg $1 commandName:String on which existence must be checked -# @arg $2 helpIfNotExists:String a help command to display if the command does not exist -# -# @exitcode 1 if the command specified does not exist -# @stderr diagnostic information + help if second argument is provided -Assert::commandExists() { - local commandName="$1" - local helpIfNotExists="$2" - - "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { - Log::displayError "${commandName} is not installed, please install it" - if [[ -n "${helpIfNotExists}" ]]; then - Log::displayInfo "${helpIfNotExists}" - fi - return 1 - } - return 0 -} - + + +# @description load colors theme constants +# @warning if tty not opened, noColor theme will be chosen +# @arg $1 theme:String the theme to use (default, noColor) +# @arg $@ args:String[] +# @set __ERROR_COLOR String indicate error status +# @set __INFO_COLOR String indicate info status +# @set __SUCCESS_COLOR String indicate success status +# @set __WARNING_COLOR String indicate warning status +# @set __SKIPPED_COLOR String indicate skipped status +# @set __DEBUG_COLOR String indicate debug status +# @set __HELP_COLOR String indicate help status +# @set __TEST_COLOR String not used +# @set __TEST_ERROR_COLOR String not used +# @set __HELP_TITLE_COLOR String used to display help title in help strings +# @set __HELP_OPTION_COLOR String used to display highlight options in help strings +# +# @set __RESET_COLOR String reset default color +# +# @set __HELP_EXAMPLE String to remove +# @set __HELP_TITLE String to remove +# @set __HELP_NORMAL String to remove +# shellcheck disable=SC2034 +UI::theme() { + local theme="${1-default}" + if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then + theme="noColor" + fi + case "${theme}" in + default | default-force) + theme="default" + ;; + noColor) ;; + *) + Log::fatal "invalid theme provided" + ;; + esac + if [[ "${theme}" = "default" ]]; then + BASH_FRAMEWORK_THEME="default" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='\e[31m' # Red + __INFO_COLOR='\e[44m' # white on lightBlue + __SUCCESS_COLOR='\e[32m' # Green + __WARNING_COLOR='\e[33m' # Yellow + __SKIPPED_COLOR='\e[33m' # Yellow + __DEBUG_COLOR='\e[37m' # Gray + __HELP_COLOR='\e[7;49;33m' # Black on Gold + __TEST_COLOR='\e[100m' # Light magenta + __TEST_ERROR_COLOR='\e[41m' # white on red + __HELP_TITLE_COLOR="\e[1;37m" # Bold + __HELP_OPTION_COLOR="\e[1;34m" # Blue + # Internal: reset color + __RESET_COLOR='\e[0m' # Reset Color + # shellcheck disable=SC2155,SC2034 + __HELP_EXAMPLE="$(echo -e "\e[2;97m")" + # shellcheck disable=SC2155,SC2034 + __HELP_TITLE="$(echo -e "\e[1;37m")" + # shellcheck disable=SC2155,SC2034 + __HELP_NORMAL="$(echo -e "\033[0m")" + else + BASH_FRAMEWORK_THEME="noColor" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='' + __INFO_COLOR='' + __SUCCESS_COLOR='' + __WARNING_COLOR='' + __SKIPPED_COLOR='' + __DEBUG_COLOR='' + __HELP_COLOR='' + __TEST_COLOR='' + __TEST_ERROR_COLOR='' + __HELP_TITLE_COLOR='' + __HELP_OPTION_COLOR='' + # Internal: reset color + __RESET_COLOR='' + __HELP_EXAMPLE='' + __HELP_TITLE='' + __HELP_NORMAL='' + fi +} # FUNCTIONS -facade_main_installRequirementssh() { -BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" -if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then - FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" -else - # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR - FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" -fi -FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" -FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" - -# @require BashTools::Conf::requireLoad -if [[ -f "${HOME}/.bash-tools/.env" ]]; then - export BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") -fi -# REQUIRES -Env::requireLoad -UI::requireTheme -Log::requireLoad -Git::requireGitCommand -BashTools::Conf::requireLoad -Compiler::Facade::requireCommandBinDir -Linux::requireExecutedAsUser - -# @require Compiler::Facade::requireCommandBinDir -# shellcheck disable=SC2034,SC2154 - -declare copyrightBeginYear="2020" -declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" declare -a BASH_FRAMEWORK_ARGV_FILTERED=() +beforeParseCallback() { + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + copyrightCallback() { if [[ -z "${copyrightBeginYear}" ]]; then copyrightBeginYear="$(date +%Y)" @@ -980,13 +883,14 @@ updateArgListQuietCallback() { :; } # shellcheck disable=SC2317 # if function is overridden optionHelpCallback() { - installRequirementsCommand help + Log::displayError "optionHelpCallback needs to be overridden" exit 0 } # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { - echo "${SCRIPT_NAME} version 1.0" + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version ${versionNumber}" exit 0 } @@ -1004,21 +908,22 @@ optionEnvFileCallback() { optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_INFO} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionDebugVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_DEBUG} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionTraceVerboseCallback() { + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vvv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_TRACE} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } getLevel() { @@ -1042,6 +947,7 @@ getLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -1063,6 +969,7 @@ getVerboseLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -1073,7 +980,7 @@ optionDisplayLevelCallback() { logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1082,19 +989,20 @@ optionLogLevelCallback() { local logLevel verboseLevel logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionLogFileCallback() { local logFile="$2" - echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionQuietCallback() { - echo "BASH_FRAMEWORK_QUIET_MODE=1" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_QUIET_MODE=1" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1125,6 +1033,7 @@ optionBashFrameworkConfigCallback() { defaultFrameworkConfig="$( cat <<'EOF' + # copied from src/_includes/.framework-config.default # shellcheck disable=SC2034 @@ -1142,7 +1051,7 @@ NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-conf # describe the files that are allowed to not have an associated bats file BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" # describe the files that are allowed to not have a function matching the filename -FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|\.tpl$|/testsData/|^manualTests/|\.bats$}" +FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|/testsData/|^manualTests/|\.bats$}" # Source directories if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then FRAMEWORK_SRC_DIRS=( @@ -1158,6 +1067,7 @@ BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" + EOF )" @@ -1166,7 +1076,7 @@ overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" commandOptionParseFinished() { # load default template framework config defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" - echo "${defaultFrameworkConfig}" > "${defaultEnvFile}" + echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") if [[ -f "${envFile}" ]]; then files+=("${envFile}") @@ -1184,407 +1094,581 @@ commandOptionParseFinished() { fi } -installRequirementsCommand() { - local options_parse_cmd="$1" - shift || true - if [[ "${options_parse_cmd}" = "parse" ]]; then - local -i options_parse_optionParsedCountOptionBashFrameworkConfig - ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true - optionConfig="0" - local -i options_parse_optionParsedCountOptionConfig - ((options_parse_optionParsedCountOptionConfig = 0)) || true - optionInfoVerbose="0" - local -i options_parse_optionParsedCountOptionInfoVerbose - ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true - optionDebugVerbose="0" - local -i options_parse_optionParsedCountOptionDebugVerbose - ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true - optionTraceVerbose="0" - local -i options_parse_optionParsedCountOptionTraceVerbose - ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionNoColor="0" - local -i options_parse_optionParsedCountOptionNoColor - ((options_parse_optionParsedCountOptionNoColor = 0)) || true - optionTheme="default" - local -i options_parse_optionParsedCountOptionTheme - ((options_parse_optionParsedCountOptionTheme = 0)) || true - optionHelp="0" - local -i options_parse_optionParsedCountOptionHelp - ((options_parse_optionParsedCountOptionHelp = 0)) || true - optionVersion="0" - local -i options_parse_optionParsedCountOptionVersion - ((options_parse_optionParsedCountOptionVersion = 0)) || true - optionQuiet="0" - local -i options_parse_optionParsedCountOptionQuiet - ((options_parse_optionParsedCountOptionQuiet = 0)) || true - local -i options_parse_optionParsedCountOptionLogLevel - ((options_parse_optionParsedCountOptionLogLevel = 0)) || true - local -i options_parse_optionParsedCountOptionLogFile - ((options_parse_optionParsedCountOptionLogFile = 0)) || true - local -i options_parse_optionParsedCountOptionDisplayLevel - ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true - # shellcheck disable=SC2034 - local -i options_parse_parsedArgIndex=0 - while (($# > 0)); do - local options_parse_arg="$1" - local argOptDefaultBehavior=0 - case "${options_parse_arg}" in - # Option 1/14 - # Option optionBashFrameworkConfig --bash-framework-config variableType String min 0 max 1 authorizedValues '' regexp '' - --bash-framework-config) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) - # shellcheck disable=SC2034 - optionBashFrameworkConfig="$1" - optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" - ;; - # Option 2/14 - # Option optionConfig --config variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --config) - # shellcheck disable=SC2034 - optionConfig="1" - if ((options_parse_optionParsedCountOptionConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionConfig)) - ;; - # Option 3/14 - # Option optionInfoVerbose --verbose|-v variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --verbose | -v) - # shellcheck disable=SC2034 - optionInfoVerbose="1" - if ((options_parse_optionParsedCountOptionInfoVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionInfoVerbose)) - optionInfoVerboseCallback "${options_parse_arg}" - updateArgListInfoVerboseCallback "${options_parse_arg}" - ;; - # Option 4/14 - # Option optionDebugVerbose -vv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vv) - # shellcheck disable=SC2034 - optionDebugVerbose="1" - if ((options_parse_optionParsedCountOptionDebugVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDebugVerbose)) - optionDebugVerboseCallback "${options_parse_arg}" - updateArgListDebugVerboseCallback "${options_parse_arg}" - ;; - # Option 5/14 - # Option optionTraceVerbose -vvv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vvv) - # shellcheck disable=SC2034 - optionTraceVerbose="1" - if ((options_parse_optionParsedCountOptionTraceVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTraceVerbose)) - optionTraceVerboseCallback "${options_parse_arg}" - updateArgListTraceVerboseCallback "${options_parse_arg}" - ;; - # Option 6/14 - # Option optionEnvFiles --env-file variableType StringArray min 0 max -1 authorizedValues '' regexp '' - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - ;; - # Option 7/14 - # Option optionNoColor --no-color variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --no-color) - # shellcheck disable=SC2034 - optionNoColor="1" - if ((options_parse_optionParsedCountOptionNoColor >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionNoColor)) - optionNoColorCallback "${options_parse_arg}" - updateArgListNoColorCallback "${options_parse_arg}" - ;; - # Option 8/14 - # Option optionTheme --theme variableType String min 0 max 1 authorizedValues 'default|default-force|noColor' regexp '' - --theme) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default|default-force|noColor)" - return 1 - fi - if ((options_parse_optionParsedCountOptionTheme >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTheme)) - # shellcheck disable=SC2034 - optionTheme="$1" - optionThemeCallback "${options_parse_arg}" "${optionTheme}" - updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" - ;; - # Option 9/14 - # Option optionHelp --help|-h variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --help | -h) - # shellcheck disable=SC2034 - optionHelp="1" - if ((options_parse_optionParsedCountOptionHelp >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionHelp)) - optionHelpCallback "${options_parse_arg}" - ;; - # Option 10/14 - # Option optionVersion --version variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --version) - # shellcheck disable=SC2034 - optionVersion="1" - if ((options_parse_optionParsedCountOptionVersion >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionVersion)) - optionVersionCallback "${options_parse_arg}" - ;; - # Option 11/14 - # Option optionQuiet --quiet|-q variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --quiet | -q) - # shellcheck disable=SC2034 - optionQuiet="1" - if ((options_parse_optionParsedCountOptionQuiet >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionQuiet)) - optionQuietCallback "${options_parse_arg}" - updateArgListQuietCallback "${options_parse_arg}" - ;; - # Option 12/14 - # Option optionLogLevel --log-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --log-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogLevel)) - # shellcheck disable=SC2034 - optionLogLevel="$1" - optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - ;; - # Option 13/14 - # Option optionLogFile --log-file variableType String min 0 max 1 authorizedValues '' regexp '' - --log-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogFile >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogFile)) - # shellcheck disable=SC2034 - optionLogFile="$1" - optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" - updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" - ;; - # Option 14/14 - # Option optionDisplayLevel --display-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --display-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionDisplayLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDisplayLevel)) - # shellcheck disable=SC2034 - optionDisplayLevel="$1" - optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - ;; - -*) - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" - return 1 - fi - ;; - *) - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided" - return 1 - fi - ;; - esac - shift || true - done - commandOptionParseFinished - Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" - Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" - elif [[ "${options_parse_cmd}" = "help" ]]; then - Array::wrap2 " " 80 0 "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" "installs requirements" - echo - - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "${SCRIPT_NAME}" "[OPTIONS]")" - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ - "${SCRIPT_NAME}" \ - "[--bash-framework-config ]" "[--config]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--no-color]" "[--theme ]" "[--help|-h]" "[--version]" "[--quiet|-q]" "[--log-level ]" "[--log-file ]" "[--display-level ]")" - echo - echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(use\ alternate\ bash\ framework\ configuration.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ configuration) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(info\ level\ verbose\ mode\ \(alias\ of\ --display-level\ INFO\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(debug\ level\ verbose\ mode\ \(alias\ of\ --display-level\ DEBUG\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(trace\ level\ verbose\ mode\ \(alias\ of\ --display-level\ TRACE\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Load\ the\ specified\ env\ file\ \(deprecated\,\ please\ use\ --bash-framework-config\ option\ instead\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Produce\ monochrome\ output.\ alias\ of\ --theme\ noColor.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' - echo ' Possible values: default|default-force|noColor' - echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ this\ command\ help) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Print\ version\ information\ and\ quit) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(quiet\ mode\,\ doesn\'t\ display\ any\ output) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ file) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(set\ display\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e """ -${__HELP_TITLE}INSTALLS REQUIREMENTS:${__HELP_NORMAL} -- fchastanet/bash-tools-framework""" - echo - echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo '1.0' - echo - echo -e "${__HELP_TITLE_COLOR}AUTHOR:${__RESET_COLOR}" - echo '[François Chastanet](https://github.com/fchastanet)' - echo - echo -e "${__HELP_TITLE_COLOR}SOURCE FILE:${__RESET_COLOR}" - echo 'https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/build/installRequirements.sh' - echo - echo -e "${__HELP_TITLE_COLOR}LICENSE:${__RESET_COLOR}" - echo 'MIT License' - echo - Array::wrap2 ' ' 76 4 "$(copyrightCallback)" - else - Log::displayError "Command ${SCRIPT_NAME} - Option command invalid: '${options_parse_cmd}'" - return 1 - fi + +# shellcheck disable=SC2034 +declare copyrightBeginYear="2020" +# shellcheck disable=SC2034 +declare versionNumber="1.0" + +declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" + +optionHelpCallback() { + installRequirementsCommandHelp + exit 0 +} + + +# ------------------------------------------ +# Command installRequirementsCommand +# ------------------------------------------ + +# options variables initialization +declare optionHelp="0" +declare optionConfig="0" +declare optionBashFrameworkConfig="" +declare optionInfoVerbose="0" +declare optionDebugVerbose="0" +declare optionTraceVerbose="0" +declare -a optionEnvFiles=() +declare optionLogLevel="" +declare optionLogFile="" +declare optionDisplayLevel="" +declare optionNoColor="0" +declare optionTheme="default" +declare optionVersion="0" +declare optionQuiet="0" +# arguments variables initialization +# @description parse command options and arguments for installRequirementsCommand +installRequirementsCommandParse() { + Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" + Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" + optionHelp="0" + local -i options_parse_optionParsedCountOptionHelp + ((options_parse_optionParsedCountOptionHelp = 0)) || true + optionConfig="0" + local -i options_parse_optionParsedCountOptionConfig + ((options_parse_optionParsedCountOptionConfig = 0)) || true + optionBashFrameworkConfig="" + local -i options_parse_optionParsedCountOptionBashFrameworkConfig + ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true + optionInfoVerbose="0" + local -i options_parse_optionParsedCountOptionInfoVerbose + ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true + optionDebugVerbose="0" + local -i options_parse_optionParsedCountOptionDebugVerbose + ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true + optionTraceVerbose="0" + local -i options_parse_optionParsedCountOptionTraceVerbose + ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true + + optionLogLevel="" + local -i options_parse_optionParsedCountOptionLogLevel + ((options_parse_optionParsedCountOptionLogLevel = 0)) || true + optionLogFile="" + local -i options_parse_optionParsedCountOptionLogFile + ((options_parse_optionParsedCountOptionLogFile = 0)) || true + optionDisplayLevel="" + local -i options_parse_optionParsedCountOptionDisplayLevel + ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true + optionNoColor="0" + local -i options_parse_optionParsedCountOptionNoColor + ((options_parse_optionParsedCountOptionNoColor = 0)) || true + optionTheme="default" + local -i options_parse_optionParsedCountOptionTheme + ((options_parse_optionParsedCountOptionTheme = 0)) || true + optionVersion="0" + local -i options_parse_optionParsedCountOptionVersion + ((options_parse_optionParsedCountOptionVersion = 0)) || true + optionQuiet="0" + local -i options_parse_optionParsedCountOptionQuiet + ((options_parse_optionParsedCountOptionQuiet = 0)) || true + + + # shellcheck disable=SC2034 + local -i options_parse_parsedArgIndex=0 + while (($# > 0)); do + local options_parse_arg="$1" + local argOptDefaultBehavior=0 + case "${options_parse_arg}" in + # Option 1/14 + # optionHelp alts --help|-h + # type: Boolean min 0 max 1 + --help | -h) + # shellcheck disable=SC2034 + optionHelp="1" + + if ((options_parse_optionParsedCountOptionHelp >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionHelp)) + optionHelpCallback "${options_parse_arg}" "${optionHelp}" + + ;; + + # Option 2/14 + # optionConfig alts --config + # type: Boolean min 0 max 1 + --config) + # shellcheck disable=SC2034 + optionConfig="1" + + if ((options_parse_optionParsedCountOptionConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionConfig)) + ;; + + # Option 3/14 + # optionBashFrameworkConfig alts --bash-framework-config + # type: String min 0 max 1 + --bash-framework-config) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) + # shellcheck disable=SC2034 + optionBashFrameworkConfig="$1" + optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" + + ;; + + # Option 4/14 + # optionInfoVerbose alts --verbose|-v + # type: Boolean min 0 max 1 + --verbose | -v) + # shellcheck disable=SC2034 + optionInfoVerbose="1" + + if ((options_parse_optionParsedCountOptionInfoVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionInfoVerbose)) + optionInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + updateArgListInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + ;; + + # Option 5/14 + # optionDebugVerbose alts -vv + # type: Boolean min 0 max 1 + -vv) + # shellcheck disable=SC2034 + optionDebugVerbose="1" + + if ((options_parse_optionParsedCountOptionDebugVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDebugVerbose)) + optionDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + updateArgListDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + ;; + + # Option 6/14 + # optionTraceVerbose alts -vvv + # type: Boolean min 0 max 1 + -vvv) + # shellcheck disable=SC2034 + optionTraceVerbose="1" + + if ((options_parse_optionParsedCountOptionTraceVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTraceVerbose)) + optionTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + updateArgListTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + ;; + + # Option 7/14 + # optionEnvFiles alts --env-file + # type: StringArray min 0 max -1 + --env-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + ((++options_parse_optionParsedCountOptionEnvFiles)) + optionEnvFiles+=("$1") + optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + ;; + + # Option 8/14 + # optionLogLevel alts --log-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --log-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogLevel)) + # shellcheck disable=SC2034 + optionLogLevel="$1" + optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + ;; + + # Option 9/14 + # optionLogFile alts --log-file + # type: String min 0 max 1 + --log-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogFile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogFile)) + # shellcheck disable=SC2034 + optionLogFile="$1" + optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + ;; + + # Option 10/14 + # optionDisplayLevel alts --display-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --display-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionDisplayLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDisplayLevel)) + # shellcheck disable=SC2034 + optionDisplayLevel="$1" + optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + ;; + + # Option 11/14 + # optionNoColor alts --no-color + # type: Boolean min 0 max 1 + --no-color) + # shellcheck disable=SC2034 + optionNoColor="1" + + if ((options_parse_optionParsedCountOptionNoColor >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionNoColor)) + optionNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + updateArgListNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + ;; + + # Option 12/14 + # optionTheme alts --theme + # type: String min 0 max 1 + # authorizedValues: default|default-force|noColor + --theme) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ default|default-force|noColor ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionTheme >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTheme)) + # shellcheck disable=SC2034 + optionTheme="$1" + optionThemeCallback "${options_parse_arg}" "${optionTheme}" + + updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" + + ;; + + # Option 13/14 + # optionVersion alts --version + # type: Boolean min 0 max 1 + --version) + # shellcheck disable=SC2034 + optionVersion="1" + + if ((options_parse_optionParsedCountOptionVersion >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionVersion)) + optionVersionCallback "${options_parse_arg}" "${optionVersion}" + + ;; + + # Option 14/14 + # optionQuiet alts --quiet|-q + # type: Boolean min 0 max 1 + --quiet | -q) + # shellcheck disable=SC2034 + optionQuiet="1" + + if ((options_parse_optionParsedCountOptionQuiet >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionQuiet)) + optionQuietCallback "${options_parse_arg}" "${optionQuiet}" + + updateArgListQuietCallback "${options_parse_arg}" "${optionQuiet}" + + ;; + + -*) + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" + return 1 + fi + ;; + *) + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + # too much args and no unknownArgumentCallbacks configured + Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" + return 1 + fi + + ((++options_parse_parsedArgIndex)) + ;; + esac + shift || true + done || return $? + commandOptionParseFinished + } -installRequirementsCommand parse "${BASH_FRAMEWORK_ARGV[@]}" +# @description display command options and arguments help for installRequirementsCommand +installRequirementsCommandHelp() { + Array::wrap2 ' ' 80 0 "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" \ + "Installs requirements." + echo + echo + + # ------------------------------------------ + # usage section + # ------------------------------------------ + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "installRequirements [OPTIONS] " + echo + # ------------------------------------------ + # usage/options section + # ------------------------------------------ + optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" + ) + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ + "installRequirements" "${optionsAltList[@]}" + echo + + # ------------------------------------------ + # options section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays this command help" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays configuration" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config bash-framework-config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--env-file env-file${__HELP_NORMAL} {list} (optional)" + Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--log-level log-level${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--log-file log-file${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log file" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--display-level display-level${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set display level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--theme theme${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." + echo + Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" + echo + + Array::wrap2 ' ' 76 6 " Default value: " "default" + echo + + echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Print version information and quit." + echo + + -# @require Linux::requireExecutedAsUser -run() { - mkdir -p "${BASH_TOOLS_ROOT_DIR}/vendor" || true - Git::cloneOrPullIfNoChanges \ - "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" \ - "https://github.com/fchastanet/bash-tools-framework.git" + echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." + echo + + + # ------------------------------------------ + # longDescription section + # ------------------------------------------ + echo + echo + echo -e "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" + declare -a installRequirementsCommandLongDescription=( + "Installs requirements:" + "" + "- fchastanet/bash-tools-framework" + ) + Array::wrap2 ' ' 76 0 "${installRequirementsCommandLongDescription[@]}" + echo + # ------------------------------------------ + # version section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" + echo "1.0" + # ------------------------------------------ + # author section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}AUTHOR: ${__RESET_COLOR}" + echo "[François Chastanet](https://github.com/fchastanet)" + # ------------------------------------------ + # sourceFile section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/build/installRequirements/binary-installRequirements.yaml" + # ------------------------------------------ + # license section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}LICENSE: ${__RESET_COLOR}" + echo "MIT License" + # ------------------------------------------ + # copyright section + # ------------------------------------------ + Array::wrap2 ' ' 76 0 "$(copyrightCallback)" } -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run -fi + +beforeParseCallback + +installRequirementsCommandParse "$@" +MAIN_FUNCTION_NAME="main" +main() { + +# shellcheck disable=SC2034,SC2154 + +Linux::requireExecutedAsUser +Git::requireGitCommand + +mkdir -p "${BASH_TOOLS_ROOT_DIR}/vendor" || true +Git::cloneOrPullIfNoChanges \ + "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" \ + "https://github.com/fchastanet/bash-tools-framework.git" } -facade_main_installRequirementssh "$@" +# if file is sourced avoid calling main function +# shellcheck disable=SC2178 +BASH_SOURCE=".$0" # cannot be changed in bash +# shellcheck disable=SC2128 +if test ".$0" == ".${BASH_SOURCE}"; then + if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then + main "$@" &>/dev/null + else + main "$@" + fi +fi diff --git a/src/_binaries/build/installRequirements.options.tpl b/src/_binaries/build/installRequirements.options.tpl deleted file mode 100644 index 6b29a3b4..00000000 --- a/src/_binaries/build/installRequirements.options.tpl +++ /dev/null @@ -1,18 +0,0 @@ -% -declare versionNumber="1.0" -declare commandFunctionName="installRequirementsCommand" -declare help="installs requirements" -# shellcheck disable=SC2016 -declare longDescription=''' -${__HELP_TITLE}INSTALLS REQUIREMENTS:${__HELP_NORMAL} -- fchastanet/bash-tools-framework -''' -% - -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.base.tpl)" - -% -Options::generateCommand "${options[@]}" -% - -<% ${commandFunctionName} %> parse "${BASH_FRAMEWORK_ARGV[@]}" diff --git a/src/_binaries/build/installRequirements.sh b/src/_binaries/build/installRequirements.sh deleted file mode 100755 index e72fb25e..00000000 --- a/src/_binaries/build/installRequirements.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/installRequirements -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE -# shellcheck disable=SC2034,SC2154 - -declare copyrightBeginYear="2020" -declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" - -.INCLUDE "$(dynamicTemplateDir _binaries/build/installRequirements.options.tpl)" - -# @require Linux::requireExecutedAsUser -run() { - mkdir -p "${BASH_TOOLS_ROOT_DIR}/vendor" || true - Git::cloneOrPullIfNoChanges \ - "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" \ - "https://github.com/fchastanet/bash-tools-framework.git" -} - -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run -fi diff --git a/src/_binaries/build/installRequirements/binary-installRequirements.yaml b/src/_binaries/build/installRequirements/binary-installRequirements.yaml new file mode 100644 index 00000000..d975fbb7 --- /dev/null +++ b/src/_binaries/build/installRequirements/binary-installRequirements.yaml @@ -0,0 +1,26 @@ +extends: + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" + +vars: + SRC_FILE_PATH: src/_binaries/build/installRequirements/binary-installRequirements.yaml + +compilerConfig: + targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/installRequirements" + relativeRootDirBasedOnTargetDir: .. +binData: + commands: + default: + functionName: installRequirementsCommand + version: "1.0" + commandName: installRequirements + beforeParseCallbacks: + - beforeParseCallback + definitionFiles: + 20: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/build/installRequirements/installRequirements-options.sh + mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/build/installRequirements/installRequirements-main.sh + help: Installs requirements. + longDescription: | + Installs requirements: + + - fchastanet/bash-tools-framework diff --git a/src/_binaries/build/installRequirements/installRequirements-main.sh b/src/_binaries/build/installRequirements/installRequirements-main.sh new file mode 100755 index 00000000..4d7cce68 --- /dev/null +++ b/src/_binaries/build/installRequirements/installRequirements-main.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +# shellcheck disable=SC2034,SC2154 + +Linux::requireExecutedAsUser +Git::requireGitCommand + +mkdir -p "${BASH_TOOLS_ROOT_DIR}/vendor" || true +Git::cloneOrPullIfNoChanges \ + "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" \ + "https://github.com/fchastanet/bash-tools-framework.git" diff --git a/src/_binaries/build/installRequirements/installRequirements-options.sh b/src/_binaries/build/installRequirements/installRequirements-options.sh new file mode 100755 index 00000000..600f3470 --- /dev/null +++ b/src/_binaries/build/installRequirements/installRequirements-options.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +# shellcheck disable=SC2034 +declare copyrightBeginYear="2020" +# shellcheck disable=SC2034 +declare versionNumber="1.0" + +declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" + +optionHelpCallback() { + installRequirementsCommandHelp + exit 0 +} From 723175d74830c5fc99581db6b203473dbf2057d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Mon, 26 Aug 2024 20:51:02 +0200 Subject: [PATCH 02/24] compiled doc using go compiler --- bin/doc | 2571 +++++++++-------- src/_binaries/build/doc.options.tpl | 14 - src/_binaries/build/doc/binary-doc.yaml | 23 + .../build/{doc.sh => doc/doc-main.sh} | 41 +- src/_binaries/build/doc/doc-options.sh | 13 + 5 files changed, 1409 insertions(+), 1253 deletions(-) delete mode 100644 src/_binaries/build/doc.options.tpl create mode 100644 src/_binaries/build/doc/binary-doc.yaml rename src/_binaries/build/{doc.sh => doc/doc-main.sh} (79%) create mode 100755 src/_binaries/build/doc/doc-options.sh diff --git a/bin/doc b/bin/doc index 1608095c..3893c38b 100755 --- a/bin/doc +++ b/bin/doc @@ -1,13 +1,12 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FACADE FROM https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/build/doc.sh +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/build/doc/binary-doc.yaml # DO NOT EDIT IT # @generated ############################################################################### # shellcheck disable=SC2288,SC2034 -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/doc -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE + + # ensure that no user aliases could interfere with # commands used in this script @@ -62,13 +61,6 @@ interruptManagement() { kill -s INT "$$" } trap interruptManagement INT -SCRIPT_NAME=${0##*/} -REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" -if [[ -n "${EMBED_CURRENT_DIR}" ]]; then - CURRENT_DIR="${EMBED_CURRENT_DIR}" -else - CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" -fi ################################################ # Temp dir management @@ -102,6 +94,20 @@ cleanOnExit() { } trap cleanOnExit EXIT HUP QUIT ABRT TERM + +SCRIPT_NAME=${0##*/} +REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" +if [[ -n "${EMBED_CURRENT_DIR}" ]]; then + CURRENT_DIR="${EMBED_CURRENT_DIR}" +else + CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" +fi +FRAMEWORK_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" +FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" +FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + # @description Log namespace provides 2 kind of functions # - Log::display* allows to display given message with # given display level @@ -132,175 +138,246 @@ export __VERBOSE_LEVEL_DEBUG=2 # @description verbose level info export __VERBOSE_LEVEL_TRACE=3 -# @description Display message using info color (bg light blue/fg white) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayInfo() { - local type="${2:-INFO}" - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description concatenate each element of an array with a separator +# but wrapping text when line length is more than provided argument +# The algorithm will try not to cut the array element if it can. +# - if an arg can be placed on current line it will be, +# otherwise current line is printed and arg is added to the new +# current line +# - Empty arg is interpreted as a new line. +# - Add \r to arg in order to force break line and avoid following +# arg to be concatenated with current arg. +# +# @arg $1 glue:String +# @arg $2 maxLineLength:int +# @arg $3 indentNextLine:int +# @arg $@ array:String[] +Array::wrap2() { + local glue="${1-}" + local -i glueLength="${#glue}" + shift || true + local -i maxLineLength=$1 + shift || true + local -i indentNextLine=$1 + shift || true + local indentStr="" + if ((indentNextLine > 0)); then + indentStr="$(head -c "${indentNextLine}" 0)); do + arg="$1" + shift || true + + # replace tab by 2 spaces + arg="${arg//$'\t'/ }" + # remove trailing spaces + arg="${arg%[[:blank:]]}" + if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then + printCurrentLine + ((previousLineEmpty = 1)) + continue + else + if ((previousLineEmpty == 1)); then + printCurrentLine + fi + ((previousLineEmpty = 0)) || true + fi + # convert eol to args + mapfile -t additionalLines <<<"${arg}" + if ((${#additionalLines[@]} > 1)); then + set -- "${additionalLines[@]}" "$@" + continue + fi + + ((argLength = ${#arg})) || true + + # empty arg + if ((argLength == 0)); then + if ((isNewline == 0)); then + # isNewline = 0 means currentLine is not empty + printCurrentLine + fi + continue + fi + + if ((isNewline == 0)); then + glueLength="${#glue}" + else + glueLength="0" + fi + if ((currentLineLength + argLength + glueLength > maxLineLength)); then + if ((argLength + glueLength > maxLineLength)); then + # arg is too long to even fit on one line + # we have to split the arg on current and next line + local -i remainingLineLength + ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) + appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" + printCurrentLine + arg="${arg:${remainingLineLength}}" + # remove leading spaces + arg="${arg##[[:blank:]]}" + + set -- "${arg}" "$@" + else + # the arg can fit on next line + printCurrentLine + appendToCurrentLine "${arg}" "${argLength}" + fi + else + appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" + fi + done + if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then + printCurrentLine + fi + ) | sed -E -e 's/[[:blank:]]+$//' } -# @description Display message using debug color (gray) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayDebug() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then - Log::computeDuration - echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logDebug "$1" + +# @description check if command specified exists or return 1 +# with error and message if not +# +# @arg $1 commandName:String on which existence must be checked +# @arg $2 helpIfNotExists:String a help command to display if the command does not exist +# +# @exitcode 1 if the command specified does not exist +# @stderr diagnostic information + help if second argument is provided +Assert::commandExists() { + local commandName="$1" + local helpIfNotExists="$2" + + "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { + Log::displayError "${commandName} is not installed, please install it" + if [[ -n "${helpIfNotExists}" ]]; then + Log::displayInfo "${helpIfNotExists}" + fi + return 1 + } + return 0 } -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description check if tty (interactive mode) is active +# @noargs +# @exitcode 1 if tty not active +# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive +# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive +Assert::tty() { + if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then + return 1 fi - Log::logWarning "$1" + if [[ "${INTERACTIVE:-0}" = "1" ]]; then + return 0 + fi + tty -s } -# @description Display message using error color (red) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayError() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then - Log::computeDuration - echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description get file content if file not expired +# @arg $1 file:String the file to get content from +# @arg $2 maxDuration:int number of seconds after which the file is considered expired +# @stdout {String} the file content if not expired +# @exitcode 1 if file does not exists +# @exitcode 2 if file expired +Cache::getFileContentIfNotExpired() { + local file="$1" + local maxDuration="$2" + + if [[ ! -f "${file}" ]]; then + return 1 fi - Log::logError "$1" + if (($(File::elapsedTimeSinceLastModification "${file}") > maxDuration)); then + return 2 + fi + cat "${file}" } -# @description load colors theme constants -# @warning if tty not opened, noColor theme will be chosen -# @arg $1 theme:String the theme to use (default, noColor) -# @arg $@ args:String[] -# @set __ERROR_COLOR String indicate error status -# @set __INFO_COLOR String indicate info status -# @set __SUCCESS_COLOR String indicate success status -# @set __WARNING_COLOR String indicate warning status -# @set __SKIPPED_COLOR String indicate skipped status -# @set __DEBUG_COLOR String indicate debug status -# @set __HELP_COLOR String indicate help status -# @set __TEST_COLOR String not used -# @set __TEST_ERROR_COLOR String not used -# @set __HELP_TITLE_COLOR String used to display help title in help strings -# @set __HELP_OPTION_COLOR String used to display highlight options in help strings -# -# @set __RESET_COLOR String reset default color + +# @description convert base64 encoded back to target file +# if target file is executable prepend dir of target +# file to PATH to make binary available everywhere +# it is advised to include in the path of the target file +# the md5sum of the binFile # -# @set __HELP_EXAMPLE String to remove -# @set __HELP_TITLE String to remove -# @set __HELP_NORMAL String to remove -# shellcheck disable=SC2034 -UI::theme() { - local theme="${1-default}" - if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then - theme="noColor" - fi - case "${theme}" in - default | default-force) - theme="default" - ;; - noColor) ;; - *) - Log::fatal "invalid theme provided" - ;; - esac - if [[ "${theme}" = "default" ]]; then - BASH_FRAMEWORK_THEME="default" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='\e[31m' # Red - __INFO_COLOR='\e[44m' # white on lightBlue - __SUCCESS_COLOR='\e[32m' # Green - __WARNING_COLOR='\e[33m' # Yellow - __SKIPPED_COLOR='\e[33m' # Yellow - __DEBUG_COLOR='\e[37m' # Gray - __HELP_COLOR='\e[7;49;33m' # Black on Gold - __TEST_COLOR='\e[100m' # Light magenta - __TEST_ERROR_COLOR='\e[41m' # white on red - __HELP_TITLE_COLOR="\e[1;37m" # Bold - __HELP_OPTION_COLOR="\e[1;34m" # Blue - # Internal: reset color - __RESET_COLOR='\e[0m' # Reset Color - # shellcheck disable=SC2155,SC2034 - __HELP_EXAMPLE="$(echo -e "\e[2;97m")" - # shellcheck disable=SC2155,SC2034 - __HELP_TITLE="$(echo -e "\e[1;37m")" - # shellcheck disable=SC2155,SC2034 - __HELP_NORMAL="$(echo -e "\033[0m")" - else - BASH_FRAMEWORK_THEME="noColor" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='' - __INFO_COLOR='' - __SUCCESS_COLOR='' - __WARNING_COLOR='' - __SKIPPED_COLOR='' - __DEBUG_COLOR='' - __HELP_COLOR='' - __TEST_COLOR='' - __TEST_ERROR_COLOR='' - __HELP_TITLE_COLOR='' - __HELP_OPTION_COLOR='' - # Internal: reset color - __RESET_COLOR='' - __HELP_EXAMPLE='' - __HELP_TITLE='' - __HELP_NORMAL='' +# @arg $1 targetFile:String the file to write +# @arg $2 binFileBase64:String the base64 encoded file +# @arg $3 fileMode:String the chmod to set on the file +# @set PATH String prepend target embedded file binary directory to PATH variable if binary executable +Compiler::Embed::extractFileFromBase64() { + local targetFile="$1" + local binFileBase64="$2" + local fileMode="${3:-+x}" + local targetDir="${targetFile%/*}" + + if [[ ! -f "${targetFile}" ]]; then + if [[ ! -d "${targetDir}" ]]; then + mkdir -p "${targetDir}" + fi + base64 -d >"${targetFile}" <<<"${binFileBase64}" + chmod "${fileMode}" "${targetFile}" fi -} -# @description draw a line with the character passed in parameter repeated depending on terminal width -# @arg $1 character:String character to use as separator (default value #) -UI::drawLine() { - local character="${1:-#}" - local -i width=${COLUMNS:-0} - if ((width == 0)) && [[ -t 1 ]]; then - width=$(tput cols) - fi - if ((width == 0)); then - width=80 + if [[ -x "${targetFile}" ]]; then + Env::pathPrepend "${targetDir}" fi - printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") - echo } -# @description Display message using error color (red) and exit immediately with error status 1 -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::fatal() { - Log::computeDuration - echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - Log::logFatal "$1" - exit 1 -} -# @description create a temp file using default TMPDIR variable -# initialized in _includes/_commonHeader.sh -# @env TMPDIR String (default value /tmp) -# @arg $1 templateName:String template name to use(optional) -Framework::createTempFile() { - mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" +# @description prepend directories to the PATH environment variable +# @arg $@ args:String[] list of directories to prepend +# @set PATH update PATH with the directories prepended +Env::pathPrepend() { + local arg + for arg in "$@"; do + if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then + PATH="$(realpath "${arg}"):${PATH}" + fi + done } + # @description ensure env files are loaded # @arg $@ list of default files to load at the end # @exitcode 1 if one of env files fails to load # @stderr diagnostics information is displayed # shellcheck disable=SC2120 Env::requireLoad() { + REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 + local -a defaultFiles=("$@") # get list of possible config files local -a configFiles=() @@ -328,416 +405,190 @@ Env::requireLoad() { done } -# @description activate or not Log::display* and Log::log* functions -# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL -# environment variables loaded by Env::requireLoad -# try to create log file and rotate it if necessary -# @noargs -# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable -# @env BASH_FRAMEWORK_DISPLAY_LEVEL int -# @env BASH_FRAMEWORK_LOG_LEVEL int -# @env BASH_FRAMEWORK_LOG_FILE String -# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 -# @exitcode 0 always successful -# @stderr diagnostics information about log file is displayed -# @require Env::requireLoad -# @require UI::requireTheme -Log::requireLoad() { - if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - export BASH_FRAMEWORK_LOG_LEVEL - fi - - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then - if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 - fi - elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - fi - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - # will always be created even if not in info level - Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" - if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then - Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" - fi +# @description get number of seconds since last modification of the file +# @arg $1 file:String file path +# @exitcode 1 if file does not exist +# @stdout number of seconds since last modification of the file +File::elapsedTimeSinceLastModification() { + local file="$1" + if [[ ! -f "${file}" ]]; then + return 1 fi + local lastModificationTimeSeconds diff + lastModificationTimeSeconds="$(stat -c %Y "${file}")" + ((diff = $(date +%s) - lastModificationTimeSeconds)) + echo -n "${diff}" } -# @description concatenate each element of an array with a separator -# but wrapping text when line length is more than provided argument -# The algorithm will try not to cut the array element if it can. -# - if an arg can be placed on current line it will be, -# otherwise current line is printed and arg is added to the new -# current line -# - Empty arg is interpreted as a new line. -# - Add \r to arg in order to force break line and avoid following -# arg to be concatenated with current arg. -# -# @arg $1 glue:String -# @arg $2 maxLineLength:int -# @arg $3 indentNextLine:int -# @arg $@ array:String[] -Array::wrap2() { - local glue="${1-}" - local -i glueLength="${#glue}" - shift || true - local -i maxLineLength=$1 - shift || true - local -i indentNextLine=$1 - shift || true - local indentStr="" - if ((indentNextLine > 0)); then - indentStr="$(head -c "${indentNextLine}" 0)); do - arg="$1" - shift || true + cat - | Filters::removeAnsiCodes >"${tokenFile}" + # ensure blank final line + echo >>"${tokenFile}" - # replace tab by 2 spaces - arg="${arg//$'\t'/ }" - # remove trailing spaces - arg="${arg%[[:blank:]]}" - if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then - printCurrentLine - ((previousLineEmpty = 1)) - continue - else - if ((previousLineEmpty == 1)); then - printCurrentLine - fi - ((previousLineEmpty = 0)) || true - fi - # convert eol to args - mapfile -t additionalLines <<<"${arg}" - if ((${#additionalLines[@]} > 1)); then - set -- "${additionalLines[@]}" "$@" - continue - fi + sed -E -i \ + -e "/${token}/r ${tokenFile}" \ + -e "/${token}/d" \ + "${targetFile}" + ) +} - ((argLength = ${#arg})) || true - # empty arg - if ((argLength == 0)); then - if ((isNewline == 0)); then - # isNewline = 0 means currentLine is not empty - printCurrentLine - fi - continue - fi +# @description remove ansi codes from input or files given as argument +# @arg $@ files:String[] the files to filter +# @exitcode * if one of the filter command fails +# @stdin you can use stdin as alternative to files argument +# @stdout the filtered content +# @see https://en.wikipedia.org/wiki/ANSI_escape_code +# shellcheck disable=SC2120 +Filters::removeAnsiCodes() { + # cspell:disable + sed -E 's/\x1b\[[0-9;]*[mGKHF]//g' "$@" + # cspell:enable +} - if ((isNewline == 0)); then - glueLength="${#glue}" - else - glueLength="0" - fi - if ((currentLineLength + argLength + glueLength > maxLineLength)); then - if ((argLength + glueLength > maxLineLength)); then - # arg is too long to even fit on one line - # we have to split the arg on current and next line - local -i remainingLineLength - ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) - appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" - printCurrentLine - arg="${arg:${remainingLineLength}}" - # remove leading spaces - arg="${arg##[[:blank:]]}" - set -- "${arg}" "$@" - else - # the arg can fit on next line - printCurrentLine - appendToCurrentLine "${arg}" "${argLength}" - fi - else - appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" - fi - done - if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then - printCurrentLine - fi - ) | sed -E -e 's/[[:blank:]]+$//' +# @description create a temp file using default TMPDIR variable +# @env TMPDIR String (default value /tmp) +# @arg $1 templateName:String template name to use(optional) +Framework::createTempFile() { + mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" } -BASH_FRAMEWORK_SHDOC_INSTALLED_PATH="vendor/.shDocInstalled" -BASH_FRAMEWORK_SHDOC_CHECK_TIMEOUT=86400 # 1 day -# @description install requirements to execute shdoc -# @warning cloning is skipped if vendor/.shDocInstalled file exists -# @warning a new check is done everyday -# @warning repository is not updated if a change is detected -# @env BASH_FRAMEWORK_SHDOC_CHECK_TIMEOUT int default value 86400 (86400 seconds = 1 day) -# @set BASH_FRAMEWORK_SHDOC_INSTALLED String the file created when git clone succeeded -# @see https://github.com/fchastanet/shdoc -# @stderr diagnostics information is displayed -# @feature Git::cloneOrPullIfNoChanges -ShellDoc::installRequirementsIfNeeded() { - local BASH_FRAMEWORK_SHDOC_INSTALLED="${FRAMEWORK_ROOT_DIR}/${BASH_FRAMEWORK_SHDOC_INSTALLED_PATH}" - if [[ -d "${FRAMEWORK_ROOT_DIR}/vendor" ]]; then - mkdir -p "${FRAMEWORK_ROOT_DIR}/vendor" || return 1 - fi - if [[ "$( - Cache::getFileContentIfNotExpired \ - "${BASH_FRAMEWORK_SHDOC_INSTALLED}" \ - "${BASH_FRAMEWORK_SHDOC_CHECK_TIMEOUT}" - )" != "1" ]]; then - Log::displayInfo "Check if shdoc is up to date" - if GIT_CLONE_OPTIONS="--recursive" Git::cloneOrPullIfNoChanges \ - "${FRAMEWORK_VENDOR_DIR:-${FRAMEWORK_ROOT_DIR}/vendor}/shdoc" \ - "https://github.com/fchastanet/shdoc.git"; then - echo "1" >"${BASH_FRAMEWORK_SHDOC_INSTALLED}" +# @description clone the repository if not done yet, else pull it if no change in it +# @arg $1 dir:String directory in which repository is installed or will be cloned +# @arg $2 repo:String repository url +# @arg $3 cloneCallback:Function callback on successful clone +# @arg $4 pullCallback:Function callback on successful pull +# @env GIT_CLONE_OPTIONS:String additional options to pass to git clone command +# @env SUDO String allows to use custom sudo prefix command +# @exitcode 0 on successful pulling/cloning, 1 on failure +Git::cloneOrPullIfNoChanges() { + local dir="$1" + shift || true + local repo="$1" + shift || true + local cloneCallback=${1:-} + shift || true + local pullCallback=${1:-} + shift || true + + if [[ -d "${dir}/.git" ]]; then + local exitCode=0 + Git::pullIfNoChanges "${dir}" || exitCode=$? + if [[ "${exitCode}" =~ ^[245]$ ]]; then + # changes detected or pull not applicable + return 0 + elif [[ "${exitCode}" != "0" ]]; then + return "${exitCode}" + fi + # shellcheck disable=SC2086 + if [[ "$(type -t ${pullCallback})" = "function" ]]; then + ${pullCallback} "${dir}" + fi + else + Log::displayInfo "cloning ${repo} ..." + if ! ${SUDO:-} test -d "${dir%/*}"; then + ${SUDO:-} mkdir -p "${dir%/*}" + fi + # shellcheck disable=SC2086,SC2248 + if ${SUDO:-} git clone ${GIT_CLONE_OPTIONS} --progress "$@" "${repo}" "${dir}"; then + # shellcheck disable=SC2086 + if [[ "$(type -t ${cloneCallback})" = "function" ]]; then + ${cloneCallback} "${dir}" + fi else - Log::fatal "unable to install shdoc library" + Log::displayError "Cloning '${repo}' on '${dir}' failed" + return 1 fi fi } -# @description generates markdown file from template by -# replacing @@@command_help@@@ by the help of the command -# eg: @@@test_help@@@ will be replaced by the output -# of the command `test --help` in the directory provided -# -# @arg $1 templateFile:String the file to use as template -# @arg $2 targetFile:String the target file -# @arg $3 fromDir:String the directory from which commands will be searched -# @arg $4 tokenNotFoundCount:&int (passed by reference) number of tokens @@@command_help@@@ not found in the template file -# @arg $5 excludeFilesPattern:String grep exclude pattern (eg: '^(bash-tpl)$') (default value: "") -# @stderr diagnostics logs -# @stdout the generated markdown with help of the matching command -ShellDoc::generateMdFileFromTemplate() { - local templateFile="$1" - local targetFile="$2" - local fromDir="$3" - local -n tokenNotFoundCount=$4 - local excludeFilesPattern="${5:-}" - local -a grepExclude - local startTime endTime +# @description pull git directory only if no change has been detected +# @arg $1 dir:String the git directory to pull +# @exitcode 0 on successful pulling +# @exitcode 1 on any other failure +# @exitcode 2 changes detected, pull avoided +# @exitcode 3 not a git directory +# @exitcode 4 not able to update index +# @exitcode 5 not a branch, pull avoided +# @stderr diagnostics information is displayed +# @env SUDO String allows to use custom sudo prefix command +Git::pullIfNoChanges() { - if [[ -z "${excludeFilesPattern}" ]]; then - grepExclude=(cat) - else - grepExclude=(grep -E -v "${excludeFilesPattern}") + if [[ "${REQUIRE_FUNCTION_GIT_REQUIRE_GIT_COMMAND_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Git::requireGitCommand has not been loaded" + exit 1 fi - cp "${templateFile}" "${targetFile}" - - startTime=$(date +%s.%3N) - ((nbTokensGenerated = 0)) || true - while IFS= read -r relativeFile; do - local token="${relativeFile#./}" - token="${token////_}" - if grep -q "@@@${token}_help@@@" "${targetFile}"; then - Log::displayInfo "generate help for ${token}" - ( # - (cd "${fromDir}" && "${relativeFile}" --help) | - File::replaceTokenByInput "@@@${token}_help@@@" "${targetFile}" - ) || Log::displayError "$(realpath "${fromDir}/${relativeFile}" --relative-to="${FRAMEWORK_ROOT_DIR}") --help error caught" - else - ((++tokenNotFoundCount)) - Log::displayError "token ${token} not found in ${targetFile}" + local dir="$1" + if [[ ! -d "${dir}/.git" ]]; then + return 3 + fi + ( + cd "${dir}" || return 3 + if ! ${SUDO:-} git update-index --refresh &>/dev/null; then + Log::displayWarning "Impossible to update git index of '${dir}' - check if you have modified file" + return 4 fi - ((nbTokensGenerated++)) || true - done < <(cd "${fromDir}" && find . -type f -executable | "${grepExclude[@]}") - endTime=$(date +%s.%3N) - Log::displayInfo "${nbTokensGenerated} commands' help replaced in $(echo "scale=3; ${endTime} - ${startTime}" | bc)seconds" + if ! ${SUDO:-} git diff-index --quiet HEAD --; then + Log::displayWarning "Pulling git repository '${dir}' avoided as changes detected" + return 2 + fi + if ! ${SUDO:-} git symbolic-ref -q HEAD; then + Log::displayWarning "Pulling git repository '${dir}' avoided as you are not on a branch" + return 5 + fi + Log::displayInfo "Pull git repository '${dir}' as no changes detected" + ${SUDO:-} git pull --progress + ) } -# @description fix markdown TOC generated by Markdown all in one vscode extension -# to make TOC compatible with docsify -# @arg $1 file:String file to fix -# @exitcode 1 if awk fails -# @see https://regex101.com/r/DJJf2I/1 -ShellDoc::fixMarkdownToc() { - local file="$1" - local fixMarkdownToc - - fixMarkdownToc="$( - cat <<'EOF' -{ - line=$0 - if (match(line, /^(\s*- \[([0-9]+\.)+ [^]]+\]\(#)([^)]+)\)/, arr)) { - print arr[1] "_" rewrite(arr[3]) ")" - } else { - print line - } -} -function rewrite(str) -{ - gsub(/-(-)+/, "-", str) - return str -} -EOF - )" - awk -i inplace "${fixMarkdownToc}" "${file}" -} +# @description ensure command git is available +# @exitcode 1 if git command not available +# @stderr diagnostics information is displayed +Git::requireGitCommand() { + REQUIRE_FUNCTION_GIT_REQUIRE_GIT_COMMAND_LOADED=1 -# @description Display message using info color (blue) but warning level -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayStatus() { - local type="${2:-STATUS}" - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logStatus "$1" "${type}" + Assert::commandExists git } -bashToolsDefaultConfigTemplate="${bashToolsDefaultConfigTemplate:-$( - cat <<'EOF' -# shellcheck disable=SC2034 -# Default settings -# you can override these settings by creating ${HOME}/.bash-tools/.env file - -### -### DISPLAY Level -### minimum level of the messages that will be displayed on screen -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} - -### -### DISPLAY duration -### 0: no duration is displayed on the messages -### 1: duration between previous message and current is displayed -### with the message -### -DISPLAY_DURATION=${DISPLAY_DURATION:0} - -### -### Log to file -### -### all log messages will be redirected to log file specified -### this same path will be used inside and outside of the container -### -BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} - -### -### LOG Level -### minimum level of the messages that will be logged into LOG_FILE -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} - -# absolute directory containing db import sql dumps -DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} - -# garbage collect all files for which modification is greater than eg: 30 days (+30) -# each time an existing file is used by dbImport/dbImportTable -# the file modification time is set to now -DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} - -# absolute directory containing dbScripts used by dbScriptAllDatabases -SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} - -# ----------------------------------------------------- -# AWS Parameters -# ----------------------------------------------------- -S3_BASE_URL=${S3_BASE_URL:-} - -# ----------------------------------------------------- -# Postman Parameters -# ----------------------------------------------------- -POSTMAN_API_KEY= -EOF -)}" - -# @description loads ~/.bash-tools/.env if available -# if not creates it from a default template -# else check if new options need to be added -BashTools::Conf::requireLoad() { - local envFile="${HOME}/.bash-tools/.env" - if [[ ! -f "${envFile}" ]]; then - mkdir -p "${HOME}/.bash-tools" - ( - echo "#!/usr/bin/env bash" - echo "${bashToolsDefaultConfigTemplate}" - ) >"${envFile}" - Log::displayInfo "Configuration file '${envFile}' created" - else - if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then - ( - echo '# -----------------------------------------------------' - echo '# Postman Parameters' - echo '# -----------------------------------------------------' - echo 'POSTMAN_API_KEY=' - ) >>"${envFile}" - fi +# @description ensure running user is not root +# @exitcode 1 if current user is root +# @stderr diagnostics information is displayed +Linux::requireExecutedAsUser() { + if [[ "$(id -u)" = "0" ]]; then + Log::fatal "this script should be executed as normal user" fi - # shellcheck source=/conf/.env - source "${envFile}" || { - Log::displayError "impossible to load '${envFile}'" - exit 1 - } } -# @description ensure COMMAND_BIN_DIR env var is set -# and PATH correctly prepared -# @noargs -# @set COMMAND_BIN_DIR string the directory where to find this command -# @set PATH string add directory where to find this command binary -Compiler::Facade::requireCommandBinDir() { - COMMAND_BIN_DIR="${CURRENT_DIR}" - Env::pathPrepend "${COMMAND_BIN_DIR}" + +# @description ensure command tar is available +# @exitcode 1 if tar command not available +# @stderr diagnostics information is displayed +Linux::requireTarCommand() { + Assert::commandExists tar } + declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" @@ -776,30 +627,95 @@ Log::computeDuration() { fi } -# @description log message to file + +# @description Display message using debug color (gray) # @arg $1 message:String the message to display -Log::logInfo() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then - Log::logMessage "${2:-INFO}" "$1" +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayDebug() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then + Log::computeDuration + echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logDebug "$1" } -# @description log message to file + +# @description Display message using error color (red) # @arg $1 message:String the message to display -Log::logDebug() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then - Log::logMessage "${2:-DEBUG}" "$1" +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayError() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then + Log::computeDuration + echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logError "$1" } -# @description log message to file -# @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" + +# @description Display message using info color (bg light blue/fg white) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayInfo() { + local type="${2:-INFO}" + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logInfo "$1" "${type}" +} + + +# @description Display message using info color (blue) but warning level +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayStatus() { + local type="${2:-STATUS}" + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then + Log::computeDuration + echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logStatus "$1" "${type}" +} + + +# @description Display message using warning color (yellow) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayWarning() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then + Log::computeDuration + echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logWarning "$1" +} + + +# @description Display message using error color (red) and exit immediately with error status 1 +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::fatal() { + Log::computeDuration + echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + Log::logFatal "$1" + exit 1 +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logDebug() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then + Log::logMessage "${2:-DEBUG}" "$1" fi } + # @description log message to file # @arg $1 message:String the message to display Log::logError() { @@ -808,20 +724,6 @@ Log::logError() { fi } -# @description check if tty (interactive mode) is active -# @noargs -# @exitcode 1 if tty not active -# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive -# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive -Assert::tty() { - if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then - return 1 - fi - if [[ "${INTERACTIVE:-0}" = "1" ]]; then - return 0 - fi - tty -s -} # @description log message to file # @arg $1 message:String the message to display @@ -829,6 +731,16 @@ Log::logFatal() { Log::logMessage "${2:-FATAL}" "$1" } + +# @description log message to file +# @arg $1 message:String the message to display +Log::logInfo() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then + Log::logMessage "${2:-INFO}" "$1" + fi +} + + # @description Internal: common log message # @example text # [date]|[levelMsg]|message @@ -842,9 +754,18 @@ Log::logFatal() { # @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty # @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages # @stderr diagnostics information is displayed -# @require Env::requireLoad -# @require Log::requireLoad Log::logMessage() { + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Log::requireLoad has not been loaded" + exit 1 + fi + local levelMsg="$1" local msg="$2" local date @@ -856,6 +777,83 @@ Log::logMessage() { fi } + +# @description log message to file +# @arg $1 message:String the message to display +Log::logStatus() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then + Log::logMessage "${2:-STATUS}" "$1" + fi +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logWarning() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then + Log::logMessage "${2:-WARNING}" "$1" + fi +} + + +# @description activate or not Log::display* and Log::log* functions +# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL +# environment variables loaded by Env::requireLoad +# try to create log file and rotate it if necessary +# @noargs +# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable +# @env BASH_FRAMEWORK_DISPLAY_LEVEL int +# @env BASH_FRAMEWORK_LOG_LEVEL int +# @env BASH_FRAMEWORK_LOG_FILE String +# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 +# @exitcode 0 always successful +# @stderr diagnostics information about log file is displayed +Log::requireLoad() { + REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement UI::requireTheme has not been loaded" + exit 1 + fi + + if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + export BASH_FRAMEWORK_LOG_LEVEL + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then + if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 + fi + elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + # will always be created even if not in info level + Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" + if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then + Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + fi + fi +} + + # @description To be called before logging in the log file # @arg $1 file:string log file name # @arg $2 maxLogFilesCount:int maximum number of log files @@ -878,119 +876,129 @@ Log::rotate() { fi } -# @description get file content if file not expired -# @arg $1 file:String the file to get content from -# @arg $2 maxDuration:int number of seconds after which the file is considered expired -# @stdout {String} the file content if not expired -# @exitcode 1 if file does not exists -# @exitcode 2 if file expired -Cache::getFileContentIfNotExpired() { + +# @description fix markdown TOC generated by Markdown all in one vscode extension +# to make TOC compatible with docsify +# @arg $1 file:String file to fix +# @exitcode 1 if awk fails +# @see https://regex101.com/r/DJJf2I/1 +ShellDoc::fixMarkdownToc() { local file="$1" - local maxDuration="$2" - if [[ ! -f "${file}" ]]; then - return 1 - fi - if (($(File::elapsedTimeSinceLastModification "${file}") > maxDuration)); then - return 2 - fi - cat "${file}" +Linux::requireTarCommand +Compiler::Embed::extractFileFromBase64 \ + "${PERSISTENT_TMPDIR:-/tmp}/647e7865a1d35cf1435bb0aa74a5cdbe/fixMarkdownTocScript" \ + "ewogIGxpbmU9JDAKICBpZiAobWF0Y2gobGluZSwgL14oXHMqLSBcWyhbMC05XStcLikrIFteXV0rXF1cKCMpKFteKV0rKVwpLywgYXJyKSkgewogICAgcHJpbnQgYXJyWzFdICJfIiByZXdyaXRlKGFyclszXSkgIikiCiAgfSBlbHNlIHsKICAgIHByaW50IGxpbmUKICB9Cn0KZnVuY3Rpb24gcmV3cml0ZShzdHIpCnsKICAgIGdzdWIoLy0oLSkrLywgIi0iLCBzdHIpCiAgICByZXR1cm4gc3RyCn0K" \ + "644" + +declare -gx embed_file_fixMarkdownTocScript="${PERSISTENT_TMPDIR:-/tmp}/647e7865a1d35cf1435bb0aa74a5cdbe/fixMarkdownTocScript" + + # shellcheck disable=SC2154 + awk -i inplace -f "${embed_file_fixMarkdownTocScript}" "${file}" } -# @description clone the repository if not done yet, else pull it if no change in it -# @arg $1 dir:String directory in which repository is installed or will be cloned -# @arg $2 repo:String repository url -# @arg $3 cloneCallback:Function callback on successful clone -# @arg $4 pullCallback:Function callback on successful pull -# @env GIT_CLONE_OPTIONS:String additional options to pass to git clone command -# @env SUDO String allows to use custom sudo prefix command -# @exitcode 0 on successful pulling/cloning, 1 on failure -Git::cloneOrPullIfNoChanges() { - local dir="$1" - shift || true - local repo="$1" - shift || true - local cloneCallback=${1:-} - shift || true - local pullCallback=${1:-} - shift || true - if [[ -d "${dir}/.git" ]]; then - local exitCode=0 - Git::pullIfNoChanges "${dir}" || exitCode=$? - if Array::contains "${exitCode}" "2" "4"; then - # changes detected - return 0 - fi - if [[ "${exitCode}" != "0" ]]; then - return "${exitCode}" - fi - # shellcheck disable=SC2086 - if [[ "$(type -t ${pullCallback})" = "function" ]]; then - ${pullCallback} "${dir}" - fi +# @description generates markdown file from template by +# replacing @@@command_help@@@ by the help of the command +# eg: @@@test_help@@@ will be replaced by the output +# of the command `test --help` in the directory provided +# +# @arg $1 templateFile:String the file to use as template +# @arg $2 targetFile:String the target file +# @arg $3 fromDir:String the directory from which commands will be searched +# @arg $4 tokenNotFoundCount:&int (passed by reference) number of tokens @@@command_help@@@ not found in the template file +# @arg $5 excludeFilesPattern:String grep exclude pattern (eg: '^(compile)$') (default value: "") +# @stderr diagnostics logs +# @stdout the generated markdown with help of the matching command +ShellDoc::generateMdFileFromTemplate() { + local templateFile="$1" + local targetFile="$2" + local fromDir="$3" + local -n tokenNotFoundCount=$4 + local excludeFilesPattern="${5:-}" + + local -a grepExclude + local startTime endTime + + if [[ -z "${excludeFilesPattern}" ]]; then + grepExclude=(cat) else - Log::displayInfo "cloning ${repo} ..." - if ! ${SUDO:-} test -d "${dir%/*}"; then - ${SUDO:-} mkdir -p "${dir%/*}" - fi - # shellcheck disable=SC2086,SC2248 - if ${SUDO:-} git clone ${GIT_CLONE_OPTIONS} --progress "$@" "${repo}" "${dir}"; then - # shellcheck disable=SC2086 - if [[ "$(type -t ${cloneCallback})" = "function" ]]; then - ${cloneCallback} "${dir}" - fi + grepExclude=(grep -E -v "${excludeFilesPattern}") + fi + + cp "${templateFile}" "${targetFile}" + + startTime=$(date +%s.%3N) + ((nbTokensGenerated = 0)) || true + while IFS= read -r relativeFile; do + local token="${relativeFile#./}" + token="${token////_}" + if grep -q "@@@${token}_help@@@" "${targetFile}"; then + Log::displayInfo "generate help for ${token}" + ( # + (cd "${fromDir}" && "${relativeFile}" --help) | + File::replaceTokenByInput "@@@${token}_help@@@" "${targetFile}" + ) || Log::displayError "$(realpath "${fromDir}/${relativeFile}" --relative-to="${FRAMEWORK_ROOT_DIR}") --help error caught" else - Log::displayError "Cloning '${repo}' on '${dir}' failed" - return 1 + ((++tokenNotFoundCount)) + Log::displayError "token ${token} not found in ${targetFile}" fi - fi + ((nbTokensGenerated++)) || true + done < <(cd "${fromDir}" && find . -type f -executable | "${grepExclude[@]}") + endTime=$(date +%s.%3N) + Log::displayInfo "${nbTokensGenerated} commands' help replaced in $(echo "scale=3; ${endTime} - ${startTime}" | bc)seconds" } -# @description replace token by input(stdin) in given targetFile -# @warning special ansi codes will be removed from stdin -# @arg $1 token:String the token to replace by stdin -# @arg $2 targetFile:String the file in which token will be replaced by stdin -# @exitcode 1 if error -# @stdin the file content that will be injected in targetFile -File::replaceTokenByInput() { - local token="$1" - local targetFile="$2" - ( - local tokenFile - tokenFile="$(Framework::createTempFile "replaceTokenByInput")" - - cat - | Filters::removeAnsiCodes >"${tokenFile}" - # ensure blank final line - echo >>"${tokenFile}" +BASH_FRAMEWORK_SHDOC_INSTALLED_PATH="vendor/.shDocInstalled" +BASH_FRAMEWORK_SHDOC_CHECK_TIMEOUT=86400 # 1 day - sed -E -i \ - -e "/${token}/r ${tokenFile}" \ - -e "/${token}/d" \ - "${targetFile}" - ) +# @description install requirements to execute shdoc +# @warning cloning is skipped if vendor/.shDocInstalled file exists +# @warning a new check is done everyday +# @warning repository is not updated if a change is detected +# @env BASH_FRAMEWORK_SHDOC_CHECK_TIMEOUT int default value 86400 (86400 seconds = 1 day) +# @set BASH_FRAMEWORK_SHDOC_INSTALLED String the file created when git clone succeeded +# @see https://github.com/fchastanet/shdoc +# @stderr diagnostics information is displayed +# @feature Git::cloneOrPullIfNoChanges +ShellDoc::installRequirementsIfNeeded() { + local BASH_FRAMEWORK_SHDOC_INSTALLED="${FRAMEWORK_ROOT_DIR}/${BASH_FRAMEWORK_SHDOC_INSTALLED_PATH}" + if [[ -d "${FRAMEWORK_ROOT_DIR}/vendor" ]]; then + mkdir -p "${FRAMEWORK_ROOT_DIR}/vendor" || return 1 + fi + if [[ "$( + Cache::getFileContentIfNotExpired \ + "${BASH_FRAMEWORK_SHDOC_INSTALLED}" \ + "${BASH_FRAMEWORK_SHDOC_CHECK_TIMEOUT}" + )" != "1" ]]; then + Log::displayInfo "Check if shdoc is up to date" + if GIT_CLONE_OPTIONS="--recursive" Git::cloneOrPullIfNoChanges \ + "${FRAMEWORK_VENDOR_DIR:-${FRAMEWORK_ROOT_DIR}/vendor}/shdoc" \ + "https://github.com/fchastanet/shdoc.git"; then + echo "1" >"${BASH_FRAMEWORK_SHDOC_INSTALLED}" + else + Log::fatal "unable to install shdoc library" + fi + fi } -# @description log message to file -# @arg $1 message:String the message to display -Log::logStatus() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-STATUS}" "$1" + +# @description draw a line with the character passed in parameter repeated depending on terminal width +# @arg $1 character:String character to use as separator (default value #) +UI::drawLine() { + local character="${1:-#}" + local -i width=${COLUMNS:-0} + if ((width == 0)) && [[ -t 1 ]]; then + width=$(tput cols) fi + if ((width == 0)); then + width=80 + fi + printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") + echo } -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" - fi - done -} # @description load color theme # @noargs @@ -998,148 +1006,104 @@ Env::pathPrepend() { # @env LOAD_THEME int 0 to avoid loading theme # @exitcode 0 always successful UI::requireTheme() { + REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + if [[ "${LOAD_THEME:-1}" = "1" ]]; then UI::theme "${BASH_FRAMEWORK_THEME-default}" fi } -# @description get number of seconds since last modification of the file -# @arg $1 file:String file path -# @exitcode 1 if file does not exist -# @stdout number of seconds since last modification of the file -File::elapsedTimeSinceLastModification() { - local file="$1" - if [[ ! -f "${file}" ]]; then - return 1 - fi - local lastModificationTimeSeconds diff - lastModificationTimeSeconds="$(stat -c %Y "${file}")" - ((diff = $(date +%s) - lastModificationTimeSeconds)) - echo -n "${diff}" -} - -# @description pull git directory only if no change has been detected -# @arg $1 dir:String the git directory to pull -# @exitcode 0 on successful pulling -# @exitcode 1 on any other failure -# @exitcode 2 changes detected, pull avoided -# @exitcode 3 not a git directory -# @exitcode 4 not able to update index -# @stderr diagnostics information is displayed -# @env SUDO String allows to use custom sudo prefix command -# @require Git::requireGitCommand -Git::pullIfNoChanges() { - local dir="$1" - if [[ ! -d "${dir}/.git" ]]; then - return 3 - fi - ( - cd "${dir}" || return 3 - if ! ${SUDO:-} git update-index --refresh &>/dev/null; then - Log::displayWarning "Impossible to update git index of '${dir}' - check if you have modified file" - return 4 - fi - if ! ${SUDO:-} git diff-index --quiet HEAD --; then - Log::displayWarning "Pulling git repository '${dir}' avoided as changes detected" - return 2 - fi - Log::displayInfo "Pull git repository '${dir}' as no changes detected" - ${SUDO:-} git pull --progress - ) -} -# @description check if an element is contained in an array -# -# @arg $1 needle:String -# @arg $@ array:String[] -# @exitcode 0 if found -# @exitcode 1 otherwise -# @example -# Array::contains "${libPath}" "${__BASH_FRAMEWORK_IMPORTED_FILES[@]}" -Array::contains() { - local element - for element in "${@:2}"; do - [[ "${element}" = "$1" ]] && return 0 - done - return 1 -} - -# @description remove ansi codes from input or files given as argument -# @arg $@ files:String[] the files to filter -# @exitcode * if one of the filter command fails -# @stdin you can use stdin as alternative to files argument -# @stdout the filtered content -# @see https://en.wikipedia.org/wiki/ANSI_escape_code -# shellcheck disable=SC2120 -Filters::removeAnsiCodes() { - # cspell:disable - sed -E 's/\x1b\[[0-9;]*[mGKHF]//g' "$@" - # cspell:enable -} - -# @description ensure command git is available -# @exitcode 1 if git command not available -# @stderr diagnostics information is displayed -Git::requireGitCommand() { - Assert::commandExists git -} - -# @description check if command specified exists or return 1 -# with error and message if not +# @description load colors theme constants +# @warning if tty not opened, noColor theme will be chosen +# @arg $1 theme:String the theme to use (default, noColor) +# @arg $@ args:String[] +# @set __ERROR_COLOR String indicate error status +# @set __INFO_COLOR String indicate info status +# @set __SUCCESS_COLOR String indicate success status +# @set __WARNING_COLOR String indicate warning status +# @set __SKIPPED_COLOR String indicate skipped status +# @set __DEBUG_COLOR String indicate debug status +# @set __HELP_COLOR String indicate help status +# @set __TEST_COLOR String not used +# @set __TEST_ERROR_COLOR String not used +# @set __HELP_TITLE_COLOR String used to display help title in help strings +# @set __HELP_OPTION_COLOR String used to display highlight options in help strings # -# @arg $1 commandName:String on which existence must be checked -# @arg $2 helpIfNotExists:String a help command to display if the command does not exist +# @set __RESET_COLOR String reset default color # -# @exitcode 1 if the command specified does not exist -# @stderr diagnostic information + help if second argument is provided -Assert::commandExists() { - local commandName="$1" - local helpIfNotExists="$2" - - "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { - Log::displayError "${commandName} is not installed, please install it" - if [[ -n "${helpIfNotExists}" ]]; then - Log::displayInfo "${helpIfNotExists}" - fi - return 1 - } - return 0 +# @set __HELP_EXAMPLE String to remove +# @set __HELP_TITLE String to remove +# @set __HELP_NORMAL String to remove +# shellcheck disable=SC2034 +UI::theme() { + local theme="${1-default}" + if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then + theme="noColor" + fi + case "${theme}" in + default | default-force) + theme="default" + ;; + noColor) ;; + *) + Log::fatal "invalid theme provided" + ;; + esac + if [[ "${theme}" = "default" ]]; then + BASH_FRAMEWORK_THEME="default" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='\e[31m' # Red + __INFO_COLOR='\e[44m' # white on lightBlue + __SUCCESS_COLOR='\e[32m' # Green + __WARNING_COLOR='\e[33m' # Yellow + __SKIPPED_COLOR='\e[33m' # Yellow + __DEBUG_COLOR='\e[37m' # Gray + __HELP_COLOR='\e[7;49;33m' # Black on Gold + __TEST_COLOR='\e[100m' # Light magenta + __TEST_ERROR_COLOR='\e[41m' # white on red + __HELP_TITLE_COLOR="\e[1;37m" # Bold + __HELP_OPTION_COLOR="\e[1;34m" # Blue + # Internal: reset color + __RESET_COLOR='\e[0m' # Reset Color + # shellcheck disable=SC2155,SC2034 + __HELP_EXAMPLE="$(echo -e "\e[2;97m")" + # shellcheck disable=SC2155,SC2034 + __HELP_TITLE="$(echo -e "\e[1;37m")" + # shellcheck disable=SC2155,SC2034 + __HELP_NORMAL="$(echo -e "\033[0m")" + else + BASH_FRAMEWORK_THEME="noColor" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='' + __INFO_COLOR='' + __SUCCESS_COLOR='' + __WARNING_COLOR='' + __SKIPPED_COLOR='' + __DEBUG_COLOR='' + __HELP_COLOR='' + __TEST_COLOR='' + __TEST_ERROR_COLOR='' + __HELP_TITLE_COLOR='' + __HELP_OPTION_COLOR='' + # Internal: reset color + __RESET_COLOR='' + __HELP_EXAMPLE='' + __HELP_TITLE='' + __HELP_NORMAL='' + fi } - # FUNCTIONS -facade_main_docsh() { -BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" -if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then - FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" -else - # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR - FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" -fi -FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" -FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" - -# @require BashTools::Conf::requireLoad -if [[ -f "${HOME}/.bash-tools/.env" ]]; then - export BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") -fi -# REQUIRES -Env::requireLoad -UI::requireTheme -Log::requireLoad -Git::requireGitCommand -BashTools::Conf::requireLoad -Compiler::Facade::requireCommandBinDir - -# @require Compiler::Facade::requireCommandBinDir -# shellcheck disable=SC2034 - -declare copyrightBeginYear="2020" declare -a BASH_FRAMEWORK_ARGV_FILTERED=() +beforeParseCallback() { + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + copyrightCallback() { if [[ -z "${copyrightBeginYear}" ]]; then copyrightBeginYear="$(date +%Y)" @@ -1176,13 +1140,14 @@ updateArgListQuietCallback() { :; } # shellcheck disable=SC2317 # if function is overridden optionHelpCallback() { - docCommand help + Log::displayError "optionHelpCallback needs to be overridden" exit 0 } # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { - echo "${SCRIPT_NAME} version 1.1" + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version ${versionNumber}" exit 0 } @@ -1200,21 +1165,22 @@ optionEnvFileCallback() { optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_INFO} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionDebugVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_DEBUG} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionTraceVerboseCallback() { + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vvv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_TRACE} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } getLevel() { @@ -1238,6 +1204,7 @@ getLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -1259,6 +1226,7 @@ getVerboseLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -1269,7 +1237,7 @@ optionDisplayLevelCallback() { logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1278,19 +1246,20 @@ optionLogLevelCallback() { local logLevel verboseLevel logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionLogFileCallback() { local logFile="$2" - echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionQuietCallback() { - echo "BASH_FRAMEWORK_QUIET_MODE=1" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_QUIET_MODE=1" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1321,6 +1290,7 @@ optionBashFrameworkConfigCallback() { defaultFrameworkConfig="$( cat <<'EOF' + # copied from src/_includes/.framework-config.default # shellcheck disable=SC2034 @@ -1338,7 +1308,7 @@ NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-conf # describe the files that are allowed to not have an associated bats file BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" # describe the files that are allowed to not have a function matching the filename -FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|\.tpl$|/testsData/|^manualTests/|\.bats$}" +FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|/testsData/|^manualTests/|\.bats$}" # Source directories if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then FRAMEWORK_SRC_DIRS=( @@ -1354,6 +1324,7 @@ BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" + EOF )" @@ -1362,7 +1333,7 @@ overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" commandOptionParseFinished() { # load default template framework config defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" - echo "${defaultFrameworkConfig}" > "${defaultEnvFile}" + echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") if [[ -f "${envFile}" ]]; then files+=("${envFile}") @@ -1379,6 +1350,10 @@ commandOptionParseFinished() { displayConfig fi } + + + +# shellcheck disable=SC2034 declare optionContinuousIntegrationMode=0 # shellcheck disable=SC2317 # if function is overridden @@ -1386,411 +1361,578 @@ updateOptionContinuousIntegrationMode() { BASH_FRAMEWORK_ARGV_FILTERED+=("$1") } -docCommand() { - local options_parse_cmd="$1" - shift || true - if [[ "${options_parse_cmd}" = "parse" ]]; then - optionContinuousIntegrationMode="0" - local -i options_parse_optionParsedCountOptionContinuousIntegrationMode - ((options_parse_optionParsedCountOptionContinuousIntegrationMode = 0)) || true - local -i options_parse_optionParsedCountOptionBashFrameworkConfig - ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true - optionConfig="0" - local -i options_parse_optionParsedCountOptionConfig - ((options_parse_optionParsedCountOptionConfig = 0)) || true - optionInfoVerbose="0" - local -i options_parse_optionParsedCountOptionInfoVerbose - ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true - optionDebugVerbose="0" - local -i options_parse_optionParsedCountOptionDebugVerbose - ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true - optionTraceVerbose="0" - local -i options_parse_optionParsedCountOptionTraceVerbose - ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionNoColor="0" - local -i options_parse_optionParsedCountOptionNoColor - ((options_parse_optionParsedCountOptionNoColor = 0)) || true - optionTheme="default" - local -i options_parse_optionParsedCountOptionTheme - ((options_parse_optionParsedCountOptionTheme = 0)) || true - optionHelp="0" - local -i options_parse_optionParsedCountOptionHelp - ((options_parse_optionParsedCountOptionHelp = 0)) || true - optionVersion="0" - local -i options_parse_optionParsedCountOptionVersion - ((options_parse_optionParsedCountOptionVersion = 0)) || true - optionQuiet="0" - local -i options_parse_optionParsedCountOptionQuiet - ((options_parse_optionParsedCountOptionQuiet = 0)) || true - local -i options_parse_optionParsedCountOptionLogLevel - ((options_parse_optionParsedCountOptionLogLevel = 0)) || true - local -i options_parse_optionParsedCountOptionLogFile - ((options_parse_optionParsedCountOptionLogFile = 0)) || true - local -i options_parse_optionParsedCountOptionDisplayLevel - ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true - # shellcheck disable=SC2034 - local -i options_parse_parsedArgIndex=0 - while (($# > 0)); do - local options_parse_arg="$1" - local argOptDefaultBehavior=0 - case "${options_parse_arg}" in - # Option 1/15 - # Option optionContinuousIntegrationMode --ci variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --ci) - # shellcheck disable=SC2034 - optionContinuousIntegrationMode="1" - if ((options_parse_optionParsedCountOptionContinuousIntegrationMode >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionContinuousIntegrationMode)) - updateOptionContinuousIntegrationMode "${options_parse_arg}" - ;; - # Option 2/15 - # Option optionBashFrameworkConfig --bash-framework-config variableType String min 0 max 1 authorizedValues '' regexp '' - --bash-framework-config) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) - # shellcheck disable=SC2034 - optionBashFrameworkConfig="$1" - optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" - ;; - # Option 3/15 - # Option optionConfig --config variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --config) - # shellcheck disable=SC2034 - optionConfig="1" - if ((options_parse_optionParsedCountOptionConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionConfig)) - ;; - # Option 4/15 - # Option optionInfoVerbose --verbose|-v variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --verbose | -v) - # shellcheck disable=SC2034 - optionInfoVerbose="1" - if ((options_parse_optionParsedCountOptionInfoVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionInfoVerbose)) - optionInfoVerboseCallback "${options_parse_arg}" - updateArgListInfoVerboseCallback "${options_parse_arg}" - ;; - # Option 5/15 - # Option optionDebugVerbose -vv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vv) - # shellcheck disable=SC2034 - optionDebugVerbose="1" - if ((options_parse_optionParsedCountOptionDebugVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDebugVerbose)) - optionDebugVerboseCallback "${options_parse_arg}" - updateArgListDebugVerboseCallback "${options_parse_arg}" - ;; - # Option 6/15 - # Option optionTraceVerbose -vvv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vvv) - # shellcheck disable=SC2034 - optionTraceVerbose="1" - if ((options_parse_optionParsedCountOptionTraceVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTraceVerbose)) - optionTraceVerboseCallback "${options_parse_arg}" - updateArgListTraceVerboseCallback "${options_parse_arg}" - ;; - # Option 7/15 - # Option optionEnvFiles --env-file variableType StringArray min 0 max -1 authorizedValues '' regexp '' - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - ;; - # Option 8/15 - # Option optionNoColor --no-color variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --no-color) - # shellcheck disable=SC2034 - optionNoColor="1" - if ((options_parse_optionParsedCountOptionNoColor >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionNoColor)) - optionNoColorCallback "${options_parse_arg}" - updateArgListNoColorCallback "${options_parse_arg}" - ;; - # Option 9/15 - # Option optionTheme --theme variableType String min 0 max 1 authorizedValues 'default|default-force|noColor' regexp '' - --theme) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default|default-force|noColor)" - return 1 - fi - if ((options_parse_optionParsedCountOptionTheme >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTheme)) - # shellcheck disable=SC2034 - optionTheme="$1" - optionThemeCallback "${options_parse_arg}" "${optionTheme}" - updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" - ;; - # Option 10/15 - # Option optionHelp --help|-h variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --help | -h) - # shellcheck disable=SC2034 - optionHelp="1" - if ((options_parse_optionParsedCountOptionHelp >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionHelp)) - optionHelpCallback "${options_parse_arg}" - ;; - # Option 11/15 - # Option optionVersion --version variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --version) - # shellcheck disable=SC2034 - optionVersion="1" - if ((options_parse_optionParsedCountOptionVersion >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionVersion)) - optionVersionCallback "${options_parse_arg}" - ;; - # Option 12/15 - # Option optionQuiet --quiet|-q variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --quiet | -q) - # shellcheck disable=SC2034 - optionQuiet="1" - if ((options_parse_optionParsedCountOptionQuiet >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionQuiet)) - optionQuietCallback "${options_parse_arg}" - updateArgListQuietCallback "${options_parse_arg}" - ;; - # Option 13/15 - # Option optionLogLevel --log-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --log-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogLevel)) - # shellcheck disable=SC2034 - optionLogLevel="$1" - optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - ;; - # Option 14/15 - # Option optionLogFile --log-file variableType String min 0 max 1 authorizedValues '' regexp '' - --log-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogFile >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogFile)) - # shellcheck disable=SC2034 - optionLogFile="$1" - optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" - updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" - ;; - # Option 15/15 - # Option optionDisplayLevel --display-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --display-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionDisplayLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDisplayLevel)) - # shellcheck disable=SC2034 - optionDisplayLevel="$1" - optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - ;; - -*) - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" - return 1 - fi - ;; - *) - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided" - return 1 - fi - ;; - esac - shift || true - done - commandOptionParseFinished - Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" - Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" - elif [[ "${options_parse_cmd}" = "help" ]]; then - Array::wrap2 " " 80 0 "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" "generate markdown documentation" - echo - - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "${SCRIPT_NAME}" "[OPTIONS]")" - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ - "${SCRIPT_NAME}" \ - "[--ci]" "[--bash-framework-config ]" "[--config]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--no-color]" "[--theme ]" "[--help|-h]" "[--version]" "[--quiet|-q]" "[--log-level ]" "[--log-file ]" "[--display-level ]")" - echo - echo -e "${__HELP_TITLE_COLOR}OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--ci${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(activate\ continuous\ integration\ mode\ \(tmp\ folder\ not\ shared\ with\ host\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(use\ alternate\ bash\ framework\ configuration.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ configuration) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(info\ level\ verbose\ mode\ \(alias\ of\ --display-level\ INFO\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(debug\ level\ verbose\ mode\ \(alias\ of\ --display-level\ DEBUG\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(trace\ level\ verbose\ mode\ \(alias\ of\ --display-level\ TRACE\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Load\ the\ specified\ env\ file\ \(deprecated\,\ please\ use\ --bash-framework-config\ option\ instead\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Produce\ monochrome\ output.\ alias\ of\ --theme\ noColor.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' - echo ' Possible values: default|default-force|noColor' - echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ this\ command\ help) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Print\ version\ information\ and\ quit) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(quiet\ mode\,\ doesn\'t\ display\ any\ output) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ file) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(set\ display\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo - echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo '1.1' - echo - echo -e "${__HELP_TITLE_COLOR}AUTHOR:${__RESET_COLOR}" - echo '[François Chastanet](https://github.com/fchastanet)' - echo - echo -e "${__HELP_TITLE_COLOR}SOURCE FILE:${__RESET_COLOR}" - echo 'https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/build/doc.sh' - echo - echo -e "${__HELP_TITLE_COLOR}LICENSE:${__RESET_COLOR}" - echo 'MIT License' - echo - Array::wrap2 ' ' 76 4 "$(copyrightCallback)" - else - Log::displayError "Command ${SCRIPT_NAME} - Option command invalid: '${options_parse_cmd}'" - return 1 - fi + +# shellcheck disable=SC2034 +declare copyrightBeginYear="2020" +# shellcheck disable=SC2034 +declare versionNumber="1.1" + +declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" + +optionHelpCallback() { + docCommandHelp + exit 0 +} + + +# ------------------------------------------ +# Command docCommand +# ------------------------------------------ + +# options variables initialization +declare optionContinuousIntegrationMode="0" +declare optionHelp="0" +declare optionConfig="0" +declare optionBashFrameworkConfig="" +declare optionInfoVerbose="0" +declare optionDebugVerbose="0" +declare optionTraceVerbose="0" +declare -a optionEnvFiles=() +declare optionLogLevel="" +declare optionLogFile="" +declare optionDisplayLevel="" +declare optionNoColor="0" +declare optionTheme="default" +declare optionVersion="0" +declare optionQuiet="0" +# arguments variables initialization +# @description parse command options and arguments for docCommand +docCommandParse() { + Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" + Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" + optionContinuousIntegrationMode="0" + local -i options_parse_optionParsedCountOptionContinuousIntegrationMode + ((options_parse_optionParsedCountOptionContinuousIntegrationMode = 0)) || true + optionHelp="0" + local -i options_parse_optionParsedCountOptionHelp + ((options_parse_optionParsedCountOptionHelp = 0)) || true + optionConfig="0" + local -i options_parse_optionParsedCountOptionConfig + ((options_parse_optionParsedCountOptionConfig = 0)) || true + optionBashFrameworkConfig="" + local -i options_parse_optionParsedCountOptionBashFrameworkConfig + ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true + optionInfoVerbose="0" + local -i options_parse_optionParsedCountOptionInfoVerbose + ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true + optionDebugVerbose="0" + local -i options_parse_optionParsedCountOptionDebugVerbose + ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true + optionTraceVerbose="0" + local -i options_parse_optionParsedCountOptionTraceVerbose + ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true + + optionLogLevel="" + local -i options_parse_optionParsedCountOptionLogLevel + ((options_parse_optionParsedCountOptionLogLevel = 0)) || true + optionLogFile="" + local -i options_parse_optionParsedCountOptionLogFile + ((options_parse_optionParsedCountOptionLogFile = 0)) || true + optionDisplayLevel="" + local -i options_parse_optionParsedCountOptionDisplayLevel + ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true + optionNoColor="0" + local -i options_parse_optionParsedCountOptionNoColor + ((options_parse_optionParsedCountOptionNoColor = 0)) || true + optionTheme="default" + local -i options_parse_optionParsedCountOptionTheme + ((options_parse_optionParsedCountOptionTheme = 0)) || true + optionVersion="0" + local -i options_parse_optionParsedCountOptionVersion + ((options_parse_optionParsedCountOptionVersion = 0)) || true + optionQuiet="0" + local -i options_parse_optionParsedCountOptionQuiet + ((options_parse_optionParsedCountOptionQuiet = 0)) || true + + + # shellcheck disable=SC2034 + local -i options_parse_parsedArgIndex=0 + while (($# > 0)); do + local options_parse_arg="$1" + local argOptDefaultBehavior=0 + case "${options_parse_arg}" in + # Option 1/15 + # optionContinuousIntegrationMode alts --ci + # type: Boolean min 0 max 1 + --ci) + # shellcheck disable=SC2034 + optionContinuousIntegrationMode="1" + + if ((options_parse_optionParsedCountOptionContinuousIntegrationMode >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionContinuousIntegrationMode)) + updateOptionContinuousIntegrationMode "${options_parse_arg}" "${optionContinuousIntegrationMode}" + + ;; + + # Option 2/15 + # optionHelp alts --help|-h + # type: Boolean min 0 max 1 + --help | -h) + # shellcheck disable=SC2034 + optionHelp="1" + + if ((options_parse_optionParsedCountOptionHelp >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionHelp)) + optionHelpCallback "${options_parse_arg}" "${optionHelp}" + + ;; + + # Option 3/15 + # optionConfig alts --config + # type: Boolean min 0 max 1 + --config) + # shellcheck disable=SC2034 + optionConfig="1" + + if ((options_parse_optionParsedCountOptionConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionConfig)) + ;; + + # Option 4/15 + # optionBashFrameworkConfig alts --bash-framework-config + # type: String min 0 max 1 + --bash-framework-config) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) + # shellcheck disable=SC2034 + optionBashFrameworkConfig="$1" + optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" + + ;; + + # Option 5/15 + # optionInfoVerbose alts --verbose|-v + # type: Boolean min 0 max 1 + --verbose | -v) + # shellcheck disable=SC2034 + optionInfoVerbose="1" + + if ((options_parse_optionParsedCountOptionInfoVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionInfoVerbose)) + optionInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + updateArgListInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + ;; + + # Option 6/15 + # optionDebugVerbose alts -vv + # type: Boolean min 0 max 1 + -vv) + # shellcheck disable=SC2034 + optionDebugVerbose="1" + + if ((options_parse_optionParsedCountOptionDebugVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDebugVerbose)) + optionDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + updateArgListDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + ;; + + # Option 7/15 + # optionTraceVerbose alts -vvv + # type: Boolean min 0 max 1 + -vvv) + # shellcheck disable=SC2034 + optionTraceVerbose="1" + + if ((options_parse_optionParsedCountOptionTraceVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTraceVerbose)) + optionTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + updateArgListTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + ;; + + # Option 8/15 + # optionEnvFiles alts --env-file + # type: StringArray min 0 max -1 + --env-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + ((++options_parse_optionParsedCountOptionEnvFiles)) + optionEnvFiles+=("$1") + optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + ;; + + # Option 9/15 + # optionLogLevel alts --log-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --log-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogLevel)) + # shellcheck disable=SC2034 + optionLogLevel="$1" + optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + ;; + + # Option 10/15 + # optionLogFile alts --log-file + # type: String min 0 max 1 + --log-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogFile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogFile)) + # shellcheck disable=SC2034 + optionLogFile="$1" + optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + ;; + + # Option 11/15 + # optionDisplayLevel alts --display-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --display-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionDisplayLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDisplayLevel)) + # shellcheck disable=SC2034 + optionDisplayLevel="$1" + optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + ;; + + # Option 12/15 + # optionNoColor alts --no-color + # type: Boolean min 0 max 1 + --no-color) + # shellcheck disable=SC2034 + optionNoColor="1" + + if ((options_parse_optionParsedCountOptionNoColor >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionNoColor)) + optionNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + updateArgListNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + ;; + + # Option 13/15 + # optionTheme alts --theme + # type: String min 0 max 1 + # authorizedValues: default|default-force|noColor + --theme) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ default|default-force|noColor ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionTheme >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTheme)) + # shellcheck disable=SC2034 + optionTheme="$1" + optionThemeCallback "${options_parse_arg}" "${optionTheme}" + + updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" + + ;; + + # Option 14/15 + # optionVersion alts --version + # type: Boolean min 0 max 1 + --version) + # shellcheck disable=SC2034 + optionVersion="1" + + if ((options_parse_optionParsedCountOptionVersion >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionVersion)) + optionVersionCallback "${options_parse_arg}" "${optionVersion}" + + ;; + + # Option 15/15 + # optionQuiet alts --quiet|-q + # type: Boolean min 0 max 1 + --quiet | -q) + # shellcheck disable=SC2034 + optionQuiet="1" + + if ((options_parse_optionParsedCountOptionQuiet >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionQuiet)) + optionQuietCallback "${options_parse_arg}" "${optionQuiet}" + + updateArgListQuietCallback "${options_parse_arg}" "${optionQuiet}" + + ;; + + -*) + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" + return 1 + fi + ;; + *) + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + # too much args and no unknownArgumentCallbacks configured + Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" + return 1 + fi + + ((++options_parse_parsedArgIndex)) + ;; + esac + shift || true + done || return $? + commandOptionParseFinished + +} + +# @description display command options and arguments help for docCommand +docCommandHelp() { + Array::wrap2 ' ' 80 0 "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" \ + "Generate markdown documentation." + echo + echo + + # ------------------------------------------ + # usage section + # ------------------------------------------ + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "doc [OPTIONS] " + echo + # ------------------------------------------ + # usage/options section + # ------------------------------------------ + optionsAltList=("[--ci]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" + ) + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ + "doc" "${optionsAltList[@]}" + echo + + # ------------------------------------------ + # options section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--ci${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Activate continuous integration mode (tmp folder not shared with host)" "" + echo + + + echo + echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays this command help" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays configuration" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config bash-framework-config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--env-file env-file${__HELP_NORMAL} {list} (optional)" + Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--log-level log-level${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--log-file log-file${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log file" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--display-level display-level${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set display level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--theme theme${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." + echo + Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" + echo + + Array::wrap2 ' ' 76 6 " Default value: " "default" + echo + + echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Print version information and quit." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." + echo + + + # ------------------------------------------ + # version section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" + echo "1.1" + # ------------------------------------------ + # author section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}AUTHOR: ${__RESET_COLOR}" + echo "[François Chastanet](https://github.com/fchastanet)" + # ------------------------------------------ + # sourceFile section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/build/doc/binary-doc.yaml" + # ------------------------------------------ + # license section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}LICENSE: ${__RESET_COLOR}" + echo "MIT License" + # ------------------------------------------ + # copyright section + # ------------------------------------------ + Array::wrap2 ' ' 76 0 "$(copyrightCallback)" } -docCommand parse "${BASH_FRAMEWORK_ARGV[@]}" + +beforeParseCallback + +docCommandParse "$@" +MAIN_FUNCTION_NAME="main" +main() { + +# shellcheck disable=SC2034,SC2154 + +Linux::requireExecutedAsUser installRequirements() { ShellDoc::installRequirementsIfNeeded @@ -1898,30 +2040,33 @@ generateDoc() { Log::displayStatus "Doc generated in ${ORIGINAL_DOC_DIR} folder" } -run() { - if [[ "${IN_BASH_DOCKER:-}" != "You're in docker" ]]; then - installRequirements - if [[ "${optionContinuousIntegrationMode}" = "1" ]]; then - chmod -R 777 pages - fi - runContainer - if [[ "${optionContinuousIntegrationMode}" = "1" ]]; then - # restore previous rights - find pages -type d -exec chmod 755 {} ';' - find pages -type f -exec chmod 644 {} ';' - fi - else - configureContainer - generateDoc +if [[ "${IN_BASH_DOCKER:-}" != "You're in docker" ]]; then + Git::requireGitCommand + installRequirements + if [[ "${optionContinuousIntegrationMode}" = "1" ]]; then + chmod -R 777 pages + fi + runContainer + if [[ "${optionContinuousIntegrationMode}" = "1" ]]; then + # restore previous rights + find pages -type d -exec chmod 755 {} ';' + find pages -type f -exec chmod 644 {} ';' fi -} - -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null else - run + configureContainer + generateDoc fi } -facade_main_docsh "$@" +# if file is sourced avoid calling main function +# shellcheck disable=SC2178 +BASH_SOURCE=".$0" # cannot be changed in bash +# shellcheck disable=SC2128 +if test ".$0" == ".${BASH_SOURCE}"; then + if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then + main "$@" &>/dev/null + else + main "$@" + fi +fi diff --git a/src/_binaries/build/doc.options.tpl b/src/_binaries/build/doc.options.tpl deleted file mode 100644 index 5321ea12..00000000 --- a/src/_binaries/build/doc.options.tpl +++ /dev/null @@ -1,14 +0,0 @@ -% -declare versionNumber="1.1" -declare commandFunctionName="docCommand" -declare help="generate markdown documentation" -% - -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.base.tpl)" -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.ci.tpl)" - -% -Options::generateCommand "${options[@]}" -% - -<% ${commandFunctionName} %> parse "${BASH_FRAMEWORK_ARGV[@]}" diff --git a/src/_binaries/build/doc/binary-doc.yaml b/src/_binaries/build/doc/binary-doc.yaml new file mode 100644 index 00000000..ee23fe40 --- /dev/null +++ b/src/_binaries/build/doc/binary-doc.yaml @@ -0,0 +1,23 @@ +extends: + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/optionsCi.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" + +vars: + SRC_FILE_PATH: src/_binaries/build/doc/binary-doc.yaml + +compilerConfig: + targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/doc" + relativeRootDirBasedOnTargetDir: .. +binData: + commands: + default: + functionName: docCommand + version: "1.1" + commandName: doc + beforeParseCallbacks: + - beforeParseCallback + definitionFiles: + 20: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/build/doc/doc-options.sh + mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/build/doc/doc-main.sh + help: Generate markdown documentation. diff --git a/src/_binaries/build/doc.sh b/src/_binaries/build/doc/doc-main.sh similarity index 79% rename from src/_binaries/build/doc.sh rename to src/_binaries/build/doc/doc-main.sh index 1b6bbf6f..ea5fb2ea 100755 --- a/src/_binaries/build/doc.sh +++ b/src/_binaries/build/doc/doc-main.sh @@ -1,12 +1,8 @@ #!/usr/bin/env bash -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/doc -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE -# shellcheck disable=SC2034 -declare copyrightBeginYear="2020" +# shellcheck disable=SC2034,SC2154 -.INCLUDE "$(dynamicTemplateDir _binaries/build/doc.options.tpl)" +Linux::requireExecutedAsUser installRequirements() { ShellDoc::installRequirementsIfNeeded @@ -114,26 +110,19 @@ generateDoc() { Log::displayStatus "Doc generated in ${ORIGINAL_DOC_DIR} folder" } -run() { - if [[ "${IN_BASH_DOCKER:-}" != "You're in docker" ]]; then - installRequirements - if [[ "${optionContinuousIntegrationMode}" = "1" ]]; then - chmod -R 777 pages - fi - runContainer - if [[ "${optionContinuousIntegrationMode}" = "1" ]]; then - # restore previous rights - find pages -type d -exec chmod 755 {} ';' - find pages -type f -exec chmod 644 {} ';' - fi - else - configureContainer - generateDoc +if [[ "${IN_BASH_DOCKER:-}" != "You're in docker" ]]; then + Git::requireGitCommand + installRequirements + if [[ "${optionContinuousIntegrationMode}" = "1" ]]; then + chmod -R 777 pages + fi + runContainer + if [[ "${optionContinuousIntegrationMode}" = "1" ]]; then + # restore previous rights + find pages -type d -exec chmod 755 {} ';' + find pages -type f -exec chmod 644 {} ';' fi -} - -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null else - run + configureContainer + generateDoc fi diff --git a/src/_binaries/build/doc/doc-options.sh b/src/_binaries/build/doc/doc-options.sh new file mode 100755 index 00000000..39895d2b --- /dev/null +++ b/src/_binaries/build/doc/doc-options.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +# shellcheck disable=SC2034 +declare copyrightBeginYear="2020" +# shellcheck disable=SC2034 +declare versionNumber="1.1" + +declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" + +optionHelpCallback() { + docCommandHelp + exit 0 +} From 67c279dc86007dc77c834c532a0673bf84c02d78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Mon, 26 Aug 2024 21:15:42 +0200 Subject: [PATCH 03/24] compiled install using go compiler --- bin/install | 1708 +++++++++++++++++ src/_binaries/build/install.options.tpl | 13 - src/_binaries/build/install.sh | 47 - .../build/install/binary-install.yaml | 34 + src/_binaries/build/install/install-main.sh | 40 + .../build/install/install-options.sh | 13 + 6 files changed, 1795 insertions(+), 60 deletions(-) create mode 100755 bin/install delete mode 100644 src/_binaries/build/install.options.tpl delete mode 100755 src/_binaries/build/install.sh create mode 100644 src/_binaries/build/install/binary-install.yaml create mode 100755 src/_binaries/build/install/install-main.sh create mode 100755 src/_binaries/build/install/install-options.sh diff --git a/bin/install b/bin/install new file mode 100755 index 00000000..9942a834 --- /dev/null +++ b/bin/install @@ -0,0 +1,1708 @@ +#!/usr/bin/env bash +############################################################################### +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/build/install/binary-install.yaml +# DO NOT EDIT IT +# @generated +############################################################################### +# shellcheck disable=SC2288,SC2034 + + + +# ensure that no user aliases could interfere with +# commands used in this script +unalias -a || true +shopt -u expand_aliases + +# shellcheck disable=SC2034 +((failures = 0)) || true + +# Bash will remember & return the highest exit code in a chain of pipes. +# This way you can catch the error inside pipes, e.g. mysqldump | gzip +set -o pipefail +set -o errexit + +# Command Substitution can inherit errexit option since bash v4.4 +shopt -s inherit_errexit || true + +# if set, and job control is not active, the shell runs the last command +# of a pipeline not executed in the background in the current shell +# environment. +shopt -s lastpipe + +# a log is generated when a command fails +set -o errtrace + +# use nullglob so that (file*.php) will return an empty array if no file +# matches the wildcard +shopt -s nullglob + +# ensure regexp are interpreted without accentuated characters +export LC_ALL=POSIX + +export TERM=xterm-256color + +# avoid interactive install +export DEBIAN_FRONTEND=noninteractive +export DEBCONF_NONINTERACTIVE_SEEN=true + +# store command arguments for later usage +# shellcheck disable=SC2034 +declare -a BASH_FRAMEWORK_ARGV=("$@") +# shellcheck disable=SC2034 +declare -a ORIGINAL_BASH_FRAMEWORK_ARGV=("$@") + +# @see https://unix.stackexchange.com/a/386856 +# shellcheck disable=SC2317 +interruptManagement() { + # restore SIGINT handler + trap - INT + # ensure that Ctrl-C is trapped by this script and not by sub process + # report to the parent that we have indeed been interrupted + kill -s INT "$$" +} +trap interruptManagement INT + +################################################ +# Temp dir management +################################################ + +KEEP_TEMP_FILES="${KEEP_TEMP_FILES:-0}" +export KEEP_TEMP_FILES + +# PERSISTENT_TMPDIR is not deleted by traps +PERSISTENT_TMPDIR="${TMPDIR:-/tmp}/bash-framework" +export PERSISTENT_TMPDIR +if [[ ! -d "${PERSISTENT_TMPDIR}" ]]; then + mkdir -p "${PERSISTENT_TMPDIR}" +fi + +# shellcheck disable=SC2034 +TMPDIR="$(mktemp -d -p "${PERSISTENT_TMPDIR:-/tmp}" -t bash-framework-$$-XXXXXX)" +export TMPDIR + +# temp dir cleaning +# shellcheck disable=SC2317 +cleanOnExit() { + local rc=$? + if [[ "${KEEP_TEMP_FILES:-0}" = "1" ]]; then + Log::displayInfo "KEEP_TEMP_FILES=1 temp files kept here '${TMPDIR}'" + elif [[ -n "${TMPDIR+xxx}" ]]; then + Log::displayDebug "KEEP_TEMP_FILES=0 removing temp files '${TMPDIR}'" + rm -Rf "${TMPDIR:-/tmp/fake}" >/dev/null 2>&1 + fi + exit "${rc}" +} +trap cleanOnExit EXIT HUP QUIT ABRT TERM + + +SCRIPT_NAME=${0##*/} +REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" +if [[ -n "${EMBED_CURRENT_DIR}" ]]; then + CURRENT_DIR="${EMBED_CURRENT_DIR}" +else + CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" +fi +FRAMEWORK_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" +FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" +FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + +# @description Log namespace provides 2 kind of functions +# - Log::display* allows to display given message with +# given display level +# - Log::log* allows to log given message with +# given log level +# Log::display* functions automatically log the message too +# @see Env::requireLoad to load the display and log level from .env file + +# @description log level off +export __LEVEL_OFF=0 +# @description log level error +export __LEVEL_ERROR=1 +# @description log level warning +export __LEVEL_WARNING=2 +# @description log level info +export __LEVEL_INFO=3 +# @description log level success +export __LEVEL_SUCCESS=3 +# @description log level debug +export __LEVEL_DEBUG=4 + +# @description verbose level off +export __VERBOSE_LEVEL_OFF=0 +# @description verbose level info +export __VERBOSE_LEVEL_INFO=1 +# @description verbose level info +export __VERBOSE_LEVEL_DEBUG=2 +# @description verbose level info +export __VERBOSE_LEVEL_TRACE=3 + + +# @description concatenate each element of an array with a separator +# but wrapping text when line length is more than provided argument +# The algorithm will try not to cut the array element if it can. +# - if an arg can be placed on current line it will be, +# otherwise current line is printed and arg is added to the new +# current line +# - Empty arg is interpreted as a new line. +# - Add \r to arg in order to force break line and avoid following +# arg to be concatenated with current arg. +# +# @arg $1 glue:String +# @arg $2 maxLineLength:int +# @arg $3 indentNextLine:int +# @arg $@ array:String[] +Array::wrap2() { + local glue="${1-}" + local -i glueLength="${#glue}" + shift || true + local -i maxLineLength=$1 + shift || true + local -i indentNextLine=$1 + shift || true + local indentStr="" + if ((indentNextLine > 0)); then + indentStr="$(head -c "${indentNextLine}" 0)); do + arg="$1" + shift || true + + # replace tab by 2 spaces + arg="${arg//$'\t'/ }" + # remove trailing spaces + arg="${arg%[[:blank:]]}" + if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then + printCurrentLine + ((previousLineEmpty = 1)) + continue + else + if ((previousLineEmpty == 1)); then + printCurrentLine + fi + ((previousLineEmpty = 0)) || true + fi + # convert eol to args + mapfile -t additionalLines <<<"${arg}" + if ((${#additionalLines[@]} > 1)); then + set -- "${additionalLines[@]}" "$@" + continue + fi + + ((argLength = ${#arg})) || true + + # empty arg + if ((argLength == 0)); then + if ((isNewline == 0)); then + # isNewline = 0 means currentLine is not empty + printCurrentLine + fi + continue + fi + + if ((isNewline == 0)); then + glueLength="${#glue}" + else + glueLength="0" + fi + if ((currentLineLength + argLength + glueLength > maxLineLength)); then + if ((argLength + glueLength > maxLineLength)); then + # arg is too long to even fit on one line + # we have to split the arg on current and next line + local -i remainingLineLength + ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) + appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" + printCurrentLine + arg="${arg:${remainingLineLength}}" + # remove leading spaces + arg="${arg##[[:blank:]]}" + + set -- "${arg}" "$@" + else + # the arg can fit on next line + printCurrentLine + appendToCurrentLine "${arg}" "${argLength}" + fi + else + appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" + fi + done + if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then + printCurrentLine + fi + ) | sed -E -e 's/[[:blank:]]+$//' +} + + +# @description check if command specified exists or return 1 +# with error and message if not +# +# @arg $1 commandName:String on which existence must be checked +# @arg $2 helpIfNotExists:String a help command to display if the command does not exist +# +# @exitcode 1 if the command specified does not exist +# @stderr diagnostic information + help if second argument is provided +Assert::commandExists() { + local commandName="$1" + local helpIfNotExists="$2" + + "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { + Log::displayError "${commandName} is not installed, please install it" + if [[ -n "${helpIfNotExists}" ]]; then + Log::displayInfo "${helpIfNotExists}" + fi + return 1 + } + return 0 +} + + +# @description check if tty (interactive mode) is active +# @noargs +# @exitcode 1 if tty not active +# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive +# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive +Assert::tty() { + if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then + return 1 + fi + if [[ "${INTERACTIVE:-0}" = "1" ]]; then + return 0 + fi + tty -s +} + + +# @description Backup given directory in the base directory or in BACKUP_DIR directory +# backup directory name is composed by following fields separated by _: +# - if BACKUP_DIR is not empty then escaped full dir name separated by @ +# - date with format %Y%m%d_%H:%M:%S (Eg: 20240326_14:45:08) +# - .tgz extension +# +# @arg $1 string the base directory +# @arg $2 string the directory to backup from base directory +# +# @env BACKUP_DIR String variable referencing backup directory +# @env FRAMEWORK_ROOT_DIR String used to remove this project directory from displayed backup path +# @env SUDO String allows to use custom sudo prefix command +# +# @exitcode 1 if directory to backup does not exist, 0 if everything is OK +# @exitcode 2 on backup failure +# +# @stderr message about where the directory is backed up +Backup::dir() { + + if [[ "${REQUIRE_FUNCTION_LINUX_REQUIRE_TAR_COMMAND_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Linux::requireTarCommand has not been loaded" + exit 1 + fi + + local escapedDirname backupFile + local fromDir="$1" + local dirname="$2" + + if [[ ! -d "${fromDir}/${dirname}" ]]; then + Log::displayError "Backup::dir - directory '${fromDir}/${dirname}' doesn't exist" + return 1 + fi + if [[ -z "${BACKUP_DIR:-}" ]]; then + escapedDirname="$(sed -e 's#^/##; s#/#@#g' <<<"${dirname}")" + backupFile="${fromDir}/${escapedDirname}-$(date +"%Y%m%d_%H:%M:%S").tgz" + else + escapedDirname="$(sed -e 's#^/##; s#/#@#g' <<<"${fromDir}/${dirname}")" + backupFile="${BACKUP_DIR}/${escapedDirname}-$(date +"%Y%m%d_%H:%M:%S").tgz" + fi + + Log::displayInfo "Backup directory '${fromDir}/${dirname}' to ${backupFile}" + if ! ${SUDO:-} tar --same-owner -czf "${backupFile}" "${fromDir}/${dirname}" 2>/dev/null; then + Log::displayError "cannot backup '${fromDir}/${dirname}'" + return 2 + fi +} + + +# @description ensure env files are loaded +# @arg $@ list of default files to load at the end +# @exitcode 1 if one of env files fails to load +# @stderr diagnostics information is displayed +# shellcheck disable=SC2120 +Env::requireLoad() { + REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 + + local -a defaultFiles=("$@") + # get list of possible config files + local -a configFiles=() + if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then + # BASH_FRAMEWORK_ENV_FILES is an array + configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") + fi + local localFrameworkConfigFile + localFrameworkConfigFile="$(pwd)/.framework-config" + if [[ -f "${localFrameworkConfigFile}" ]]; then + configFiles+=("${localFrameworkConfigFile}") + fi + if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then + configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") + fi + configFiles+=("${optionEnvFiles[@]}") + configFiles+=("${defaultFiles[@]}") + + for file in "${configFiles[@]}"; do + # shellcheck source=/.framework-config + CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { + Log::displayError "while loading config file: ${file}" + return 1 + } + done +} + + +# @description create a temp file using default TMPDIR variable +# @env TMPDIR String (default value /tmp) +# @arg $1 templateName:String template name to use(optional) +Framework::createTempFile() { + mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" +} + + +# @description ensure running user is not root +# @exitcode 1 if current user is root +# @stderr diagnostics information is displayed +Linux::requireExecutedAsUser() { + if [[ "$(id -u)" = "0" ]]; then + Log::fatal "this script should be executed as normal user" + fi +} + + +# @description ensure command tar is available +# @exitcode 1 if tar command not available +# @stderr diagnostics information is displayed +Linux::requireTarCommand() { + REQUIRE_FUNCTION_LINUX_REQUIRE_TAR_COMMAND_LOADED=1 + + Assert::commandExists tar +} + + +declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR +FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" +LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" +LOG_LAST_LOG_DATE_INIT=1 +LOG_LAST_DURATION_STR="" + +# @description compute duration since last call to this function +# the result is set in following env variables. +# in ss.sss (seconds followed by milliseconds precision 3 decimals) +# @noargs +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @set LOG_LAST_LOG_DATE_INIT int (default 1) set to 0 at first call, allows to detect reference log +# @set LOG_LAST_DURATION_STR String the last duration displayed +# @set LOG_LAST_LOG_DATE String the last log date that will be used to compute next diff +Log::computeDuration() { + if ((${DISPLAY_DURATION:-0} == 1)); then + local -i duration=0 + local -i delta=0 + local -i currentLogDate + currentLogDate="${EPOCHREALTIME/[^0-9]/}" + if ((LOG_LAST_LOG_DATE_INIT == 1)); then + LOG_LAST_LOG_DATE_INIT=0 + LOG_LAST_DURATION_STR="Ref" + else + duration=$(((currentLogDate - FIRST_LOG_DATE) / 1000000)) + delta=$(((currentLogDate - LOG_LAST_LOG_DATE) / 1000000)) + LOG_LAST_DURATION_STR="${duration}s/+${delta}s" + fi + LOG_LAST_LOG_DATE="${currentLogDate}" + # shellcheck disable=SC2034 + local microSeconds="${EPOCHREALTIME#*.}" + LOG_LAST_DURATION_STR="$(printf '%(%T)T.%03.0f\n' "${EPOCHSECONDS}" "${microSeconds:0:3}")(${LOG_LAST_DURATION_STR}) - " + else + # shellcheck disable=SC2034 + LOG_LAST_DURATION_STR="" + fi +} + + +# @description Display message using debug color (gray) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayDebug() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then + Log::computeDuration + echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logDebug "$1" +} + + +# @description Display message using error color (red) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayError() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then + Log::computeDuration + echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logError "$1" +} + + +# @description Display message using info color (bg light blue/fg white) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayInfo() { + local type="${2:-INFO}" + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logInfo "$1" "${type}" +} + + +# @description Display message using skip color (yellow) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displaySkipped() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__SKIPPED_COLOR}SKIPPED - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logSkipped "$1" +} + + +# @description Display message using warning color (yellow) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayWarning() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then + Log::computeDuration + echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logWarning "$1" +} + + +# @description Display message using error color (red) and exit immediately with error status 1 +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::fatal() { + Log::computeDuration + echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + Log::logFatal "$1" + exit 1 +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logDebug() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then + Log::logMessage "${2:-DEBUG}" "$1" + fi +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logError() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_ERROR)); then + Log::logMessage "${2:-ERROR}" "$1" + fi +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logFatal() { + Log::logMessage "${2:-FATAL}" "$1" +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logInfo() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then + Log::logMessage "${2:-INFO}" "$1" + fi +} + + +# @description Internal: common log message +# @example text +# [date]|[levelMsg]|message +# +# @example text +# 2020-01-19 19:20:21|ERROR |log error +# 2020-01-19 19:20:21|SKIPPED|log skipped +# +# @arg $1 levelMsg:String message's level description (eg: STATUS, ERROR, ...) +# @arg $2 msg:String the message to display +# @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty +# @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages +# @stderr diagnostics information is displayed +Log::logMessage() { + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Log::requireLoad has not been loaded" + exit 1 + fi + + local levelMsg="$1" + local msg="$2" + local date + + if [[ -n "${BASH_FRAMEWORK_LOG_FILE}" ]] && ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + date="$(date '+%Y-%m-%d %H:%M:%S')" + touch "${BASH_FRAMEWORK_LOG_FILE}" + printf "%s|%7s|%s\n" "${date}" "${levelMsg}" "${msg}" >>"${BASH_FRAMEWORK_LOG_FILE}" + fi +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logSkipped() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then + Log::logMessage "${2:-SKIPPED}" "$1" + fi +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logWarning() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then + Log::logMessage "${2:-WARNING}" "$1" + fi +} + + +# @description activate or not Log::display* and Log::log* functions +# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL +# environment variables loaded by Env::requireLoad +# try to create log file and rotate it if necessary +# @noargs +# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable +# @env BASH_FRAMEWORK_DISPLAY_LEVEL int +# @env BASH_FRAMEWORK_LOG_LEVEL int +# @env BASH_FRAMEWORK_LOG_FILE String +# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 +# @exitcode 0 always successful +# @stderr diagnostics information about log file is displayed +Log::requireLoad() { + REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement UI::requireTheme has not been loaded" + exit 1 + fi + + if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + export BASH_FRAMEWORK_LOG_LEVEL + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then + if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 + fi + elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + # will always be created even if not in info level + Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" + if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then + Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + fi + fi +} + + +# @description To be called before logging in the log file +# @arg $1 file:string log file name +# @arg $2 maxLogFilesCount:int maximum number of log files +Log::rotate() { + local file="$1" + local maxLogFilesCount="${2:-5}" + + if [[ ! -f "${file}" ]]; then + Log::displayDebug "Log file ${file} doesn't exist yet" + return 0 + fi + local i + for ((i = maxLogFilesCount - 1; i > 0; i--)); do + Log::displayInfo "Log rotation ${file}.${i} to ${file}.$((i + 1))" + mv "${file}."{"${i}","$((i + 1))"} &>/dev/null || true + done + if cp "${file}" "${file}.1" &>/dev/null; then + echo >"${file}" # reset log file + Log::displayInfo "Log rotation ${file} to ${file}.1" + fi +} + + +# @description draw a line with the character passed in parameter repeated depending on terminal width +# @arg $1 character:String character to use as separator (default value #) +UI::drawLine() { + local character="${1:-#}" + local -i width=${COLUMNS:-0} + if ((width == 0)) && [[ -t 1 ]]; then + width=$(tput cols) + fi + if ((width == 0)); then + width=80 + fi + printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") + echo +} + + +# @description load color theme +# @noargs +# @env BASH_FRAMEWORK_THEME String theme to use +# @env LOAD_THEME int 0 to avoid loading theme +# @exitcode 0 always successful +UI::requireTheme() { + REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + + if [[ "${LOAD_THEME:-1}" = "1" ]]; then + UI::theme "${BASH_FRAMEWORK_THEME-default}" + fi +} + + +# @description load colors theme constants +# @warning if tty not opened, noColor theme will be chosen +# @arg $1 theme:String the theme to use (default, noColor) +# @arg $@ args:String[] +# @set __ERROR_COLOR String indicate error status +# @set __INFO_COLOR String indicate info status +# @set __SUCCESS_COLOR String indicate success status +# @set __WARNING_COLOR String indicate warning status +# @set __SKIPPED_COLOR String indicate skipped status +# @set __DEBUG_COLOR String indicate debug status +# @set __HELP_COLOR String indicate help status +# @set __TEST_COLOR String not used +# @set __TEST_ERROR_COLOR String not used +# @set __HELP_TITLE_COLOR String used to display help title in help strings +# @set __HELP_OPTION_COLOR String used to display highlight options in help strings +# +# @set __RESET_COLOR String reset default color +# +# @set __HELP_EXAMPLE String to remove +# @set __HELP_TITLE String to remove +# @set __HELP_NORMAL String to remove +# shellcheck disable=SC2034 +UI::theme() { + local theme="${1-default}" + if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then + theme="noColor" + fi + case "${theme}" in + default | default-force) + theme="default" + ;; + noColor) ;; + *) + Log::fatal "invalid theme provided" + ;; + esac + if [[ "${theme}" = "default" ]]; then + BASH_FRAMEWORK_THEME="default" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='\e[31m' # Red + __INFO_COLOR='\e[44m' # white on lightBlue + __SUCCESS_COLOR='\e[32m' # Green + __WARNING_COLOR='\e[33m' # Yellow + __SKIPPED_COLOR='\e[33m' # Yellow + __DEBUG_COLOR='\e[37m' # Gray + __HELP_COLOR='\e[7;49;33m' # Black on Gold + __TEST_COLOR='\e[100m' # Light magenta + __TEST_ERROR_COLOR='\e[41m' # white on red + __HELP_TITLE_COLOR="\e[1;37m" # Bold + __HELP_OPTION_COLOR="\e[1;34m" # Blue + # Internal: reset color + __RESET_COLOR='\e[0m' # Reset Color + # shellcheck disable=SC2155,SC2034 + __HELP_EXAMPLE="$(echo -e "\e[2;97m")" + # shellcheck disable=SC2155,SC2034 + __HELP_TITLE="$(echo -e "\e[1;37m")" + # shellcheck disable=SC2155,SC2034 + __HELP_NORMAL="$(echo -e "\033[0m")" + else + BASH_FRAMEWORK_THEME="noColor" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='' + __INFO_COLOR='' + __SUCCESS_COLOR='' + __WARNING_COLOR='' + __SKIPPED_COLOR='' + __DEBUG_COLOR='' + __HELP_COLOR='' + __TEST_COLOR='' + __TEST_ERROR_COLOR='' + __HELP_TITLE_COLOR='' + __HELP_OPTION_COLOR='' + # Internal: reset color + __RESET_COLOR='' + __HELP_EXAMPLE='' + __HELP_TITLE='' + __HELP_NORMAL='' + fi +} +# FUNCTIONS + + +declare -a BASH_FRAMEWORK_ARGV_FILTERED=() + +beforeParseCallback() { + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + +copyrightCallback() { + if [[ -z "${copyrightBeginYear}" ]]; then + copyrightBeginYear="$(date +%Y)" + fi + echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" +} + +# shellcheck disable=SC2317 # if function is overridden +updateArgListInfoVerboseCallback() { + BASH_FRAMEWORK_ARGV_FILTERED+=(--verbose) +} +# shellcheck disable=SC2317 # if function is overridden +updateArgListDebugVerboseCallback() { + BASH_FRAMEWORK_ARGV_FILTERED+=(-vv) +} +# shellcheck disable=SC2317 # if function is overridden +updateArgListTraceVerboseCallback() { + BASH_FRAMEWORK_ARGV_FILTERED+=(-vvv) +} +# shellcheck disable=SC2317 # if function is overridden +updateArgListEnvFileCallback() { :; } +# shellcheck disable=SC2317 # if function is overridden +updateArgListLogLevelCallback() { :; } +# shellcheck disable=SC2317 # if function is overridden +updateArgListDisplayLevelCallback() { :; } +# shellcheck disable=SC2317 # if function is overridden +updateArgListNoColorCallback() { + BASH_FRAMEWORK_ARGV_FILTERED+=(--no-color) +} +# shellcheck disable=SC2317 # if function is overridden +updateArgListThemeCallback() { :; } +# shellcheck disable=SC2317 # if function is overridden +updateArgListQuietCallback() { :; } + +# shellcheck disable=SC2317 # if function is overridden +optionHelpCallback() { + Log::displayError "optionHelpCallback needs to be overridden" + exit 0 +} + +# shellcheck disable=SC2317 # if function is overridden +optionVersionCallback() { + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version ${versionNumber}" + exit 0 +} + +# shellcheck disable=SC2317 # if function is overridden +optionEnvFileCallback() { + local envFile="$2" + Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" + if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" + exit 1 + fi +} + +# shellcheck disable=SC2317 # if function is overridden +optionInfoVerboseCallback() { + BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' + BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_INFO} + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionDebugVerboseCallback() { + BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vv' + BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_DEBUG} + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionTraceVerboseCallback() { + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vvv' + BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_TRACE} + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" +} + +getLevel() { + local levelName="$1" + case "${levelName^^}" in + OFF) + echo "${__LEVEL_OFF}" + ;; + ERR | ERROR) + echo "${__LEVEL_ERROR}" + ;; + WARN | WARNING) + echo "${__LEVEL_WARNING}" + ;; + INFO) + echo "${__LEVEL_INFO}" + ;; + DEBUG | TRACE) + echo "${__LEVEL_DEBUG}" + ;; + *) + Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" + return 1 + ;; + esac +} + +getVerboseLevel() { + local levelName="$1" + case "${levelName^^}" in + OFF) + echo "${__VERBOSE_LEVEL_OFF}" + ;; + ERR | ERROR | WARN | WARNING | INFO) + echo "${__VERBOSE_LEVEL_INFO}" + ;; + DEBUG) + echo "${__VERBOSE_LEVEL_DEBUG}" + ;; + TRACE) + echo "${__VERBOSE_LEVEL_TRACE}" + ;; + *) + Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" + return 1 + ;; + esac +} + +# shellcheck disable=SC2317 # if function is overridden +optionDisplayLevelCallback() { + local level="$2" + local logLevel verboseLevel + logLevel="$(getLevel "${level}")" + verboseLevel="$(getVerboseLevel "${level}")" + BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionLogLevelCallback() { + local level="$2" + local logLevel verboseLevel + logLevel="$(getLevel "${level}")" + verboseLevel="$(getVerboseLevel "${level}")" + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} + echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionLogFileCallback() { + local logFile="$2" + echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionQuietCallback() { + echo "BASH_FRAMEWORK_QUIET_MODE=1" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionNoColorCallback() { + UI::theme "noColor" +} + +# shellcheck disable=SC2317 # if function is overridden +optionThemeCallback() { + UI::theme "$2" +} + +displayConfig() { + echo "Config" + UI::drawLine "-" + local var + while read -r var; do + printf '%-40s = %s\n' "${var}" "$(declare -p "${var}" | sed -E -e 's/^[^=]+=(.*)/\1/')" + done < <(typeset -p | awk 'match($3, "^(BASH_FRAMEWORK_[^=]+)=", m) { print m[1] }' | sort) + exit 0 +} + +optionBashFrameworkConfigCallback() { + if [[ ! -f "$2" ]]; then + Log::fatal "Command ${SCRIPT_NAME} - Bash framework config file '$2' does not exists" + fi +} + +defaultFrameworkConfig="$( + cat <<'EOF' + +# copied from src/_includes/.framework-config.default +# shellcheck disable=SC2034 + +REAL_SCRIPT_FILE="${REAL_SCRIPT_FILE:-$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")}" +FRAMEWORK_ROOT_DIR="${FRAMEWORK_ROOT_DIR:-${REAL_SCRIPT_FILE%/*/*}}" +FRAMEWORK_SRC_DIR="${FRAMEWORK_SRC_DIR:-${FRAMEWORK_ROOT_DIR}/src}" +FRAMEWORK_BIN_DIR="${FRAMEWORK_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/bin}" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_VENDOR_DIR:-${FRAMEWORK_ROOT_DIR}/vendor}" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_VENDOR_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/vendor/bin}" + +# describe the functions that will be skipped from being imported +FRAMEWORK_FUNCTIONS_IGNORE_REGEXP="${FRAMEWORK_FUNCTIONS_IGNORE_REGEXP:-^(Namespace::functions|Functions::myFunction|Namespace::requireSomething|Acquire::ForceIPv4)$}" +# describe the files that do not contain function to be imported +NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/_binaries|^src/_includes|^src/batsHeaders.sh$|^src/_standalone)}" +# describe the files that are allowed to not have an associated bats file +BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" +# describe the files that are allowed to not have a function matching the filename +FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|/testsData/|^manualTests/|\.bats$}" +# Source directories +if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then + FRAMEWORK_SRC_DIRS=( + "${FRAMEWORK_ROOT_DIR}/src" + ) +fi + +# export here all the variables that will be used in your templates +export REPOSITORY_URL="${REPOSITORY_URL:-https://github.com/fchastanet/bash-tools-framework}" + +BASH_FRAMEWORK_THEME="${BASH_FRAMEWORK_THEME:-default}" +BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" +BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" +BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" +BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" + +EOF +)" + +overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" + +commandOptionParseFinished() { + # load default template framework config + defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" + echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" + local -a files=("${defaultEnvFile}") + if [[ -f "${envFile}" ]]; then + files+=("${envFile}") + fi + # shellcheck disable=SC2154 + if [[ -f "${optionBashFrameworkConfig}" ]]; then + files+=("${optionBashFrameworkConfig}") + fi + files+=("${overrideEnvFile}") + Env::requireLoad "${files[@]}" + Log::requireLoad + # shellcheck disable=SC2154 + if [[ "${optionConfig}" = "1" ]]; then + displayConfig + fi +} + + + +# shellcheck disable=SC2034 +declare copyrightBeginYear="2020" +# shellcheck disable=SC2034 +declare versionNumber="1.1" + +declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" + +optionHelpCallback() { + installCommandHelp + exit 0 +} + + +# ------------------------------------------ +# Command installCommand +# ------------------------------------------ + +# options variables initialization +declare optionHelp="0" +declare optionConfig="0" +declare optionBashFrameworkConfig="" +declare optionInfoVerbose="0" +declare optionDebugVerbose="0" +declare optionTraceVerbose="0" +declare -a optionEnvFiles=() +declare optionLogLevel="" +declare optionLogFile="" +declare optionDisplayLevel="" +declare optionNoColor="0" +declare optionTheme="default" +declare optionVersion="0" +declare optionQuiet="0" +declare optionSkipBackup="0" +# arguments variables initialization +# @description parse command options and arguments for installCommand +installCommandParse() { + Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" + Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" + optionHelp="0" + local -i options_parse_optionParsedCountOptionHelp + ((options_parse_optionParsedCountOptionHelp = 0)) || true + optionConfig="0" + local -i options_parse_optionParsedCountOptionConfig + ((options_parse_optionParsedCountOptionConfig = 0)) || true + optionBashFrameworkConfig="" + local -i options_parse_optionParsedCountOptionBashFrameworkConfig + ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true + optionInfoVerbose="0" + local -i options_parse_optionParsedCountOptionInfoVerbose + ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true + optionDebugVerbose="0" + local -i options_parse_optionParsedCountOptionDebugVerbose + ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true + optionTraceVerbose="0" + local -i options_parse_optionParsedCountOptionTraceVerbose + ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true + + optionLogLevel="" + local -i options_parse_optionParsedCountOptionLogLevel + ((options_parse_optionParsedCountOptionLogLevel = 0)) || true + optionLogFile="" + local -i options_parse_optionParsedCountOptionLogFile + ((options_parse_optionParsedCountOptionLogFile = 0)) || true + optionDisplayLevel="" + local -i options_parse_optionParsedCountOptionDisplayLevel + ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true + optionNoColor="0" + local -i options_parse_optionParsedCountOptionNoColor + ((options_parse_optionParsedCountOptionNoColor = 0)) || true + optionTheme="default" + local -i options_parse_optionParsedCountOptionTheme + ((options_parse_optionParsedCountOptionTheme = 0)) || true + optionVersion="0" + local -i options_parse_optionParsedCountOptionVersion + ((options_parse_optionParsedCountOptionVersion = 0)) || true + optionQuiet="0" + local -i options_parse_optionParsedCountOptionQuiet + ((options_parse_optionParsedCountOptionQuiet = 0)) || true + optionSkipBackup="0" + local -i options_parse_optionParsedCountOptionSkipBackup + ((options_parse_optionParsedCountOptionSkipBackup = 0)) || true + + + # shellcheck disable=SC2034 + local -i options_parse_parsedArgIndex=0 + while (($# > 0)); do + local options_parse_arg="$1" + local argOptDefaultBehavior=0 + case "${options_parse_arg}" in + # Option 1/15 + # optionHelp alts --help|-h + # type: Boolean min 0 max 1 + --help | -h) + # shellcheck disable=SC2034 + optionHelp="1" + + if ((options_parse_optionParsedCountOptionHelp >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionHelp)) + optionHelpCallback "${options_parse_arg}" "${optionHelp}" + + ;; + + # Option 2/15 + # optionConfig alts --config + # type: Boolean min 0 max 1 + --config) + # shellcheck disable=SC2034 + optionConfig="1" + + if ((options_parse_optionParsedCountOptionConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionConfig)) + ;; + + # Option 3/15 + # optionBashFrameworkConfig alts --bash-framework-config + # type: String min 0 max 1 + --bash-framework-config) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) + # shellcheck disable=SC2034 + optionBashFrameworkConfig="$1" + optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" + + ;; + + # Option 4/15 + # optionInfoVerbose alts --verbose|-v + # type: Boolean min 0 max 1 + --verbose | -v) + # shellcheck disable=SC2034 + optionInfoVerbose="1" + + if ((options_parse_optionParsedCountOptionInfoVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionInfoVerbose)) + optionInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + updateArgListInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + ;; + + # Option 5/15 + # optionDebugVerbose alts -vv + # type: Boolean min 0 max 1 + -vv) + # shellcheck disable=SC2034 + optionDebugVerbose="1" + + if ((options_parse_optionParsedCountOptionDebugVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDebugVerbose)) + optionDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + updateArgListDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + ;; + + # Option 6/15 + # optionTraceVerbose alts -vvv + # type: Boolean min 0 max 1 + -vvv) + # shellcheck disable=SC2034 + optionTraceVerbose="1" + + if ((options_parse_optionParsedCountOptionTraceVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTraceVerbose)) + optionTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + updateArgListTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + ;; + + # Option 7/15 + # optionEnvFiles alts --env-file + # type: StringArray min 0 max -1 + --env-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + ((++options_parse_optionParsedCountOptionEnvFiles)) + optionEnvFiles+=("$1") + optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + ;; + + # Option 8/15 + # optionLogLevel alts --log-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --log-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogLevel)) + # shellcheck disable=SC2034 + optionLogLevel="$1" + optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + ;; + + # Option 9/15 + # optionLogFile alts --log-file + # type: String min 0 max 1 + --log-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogFile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogFile)) + # shellcheck disable=SC2034 + optionLogFile="$1" + optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + ;; + + # Option 10/15 + # optionDisplayLevel alts --display-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --display-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionDisplayLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDisplayLevel)) + # shellcheck disable=SC2034 + optionDisplayLevel="$1" + optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + ;; + + # Option 11/15 + # optionNoColor alts --no-color + # type: Boolean min 0 max 1 + --no-color) + # shellcheck disable=SC2034 + optionNoColor="1" + + if ((options_parse_optionParsedCountOptionNoColor >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionNoColor)) + optionNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + updateArgListNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + ;; + + # Option 12/15 + # optionTheme alts --theme + # type: String min 0 max 1 + # authorizedValues: default|default-force|noColor + --theme) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ default|default-force|noColor ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionTheme >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTheme)) + # shellcheck disable=SC2034 + optionTheme="$1" + optionThemeCallback "${options_parse_arg}" "${optionTheme}" + + updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" + + ;; + + # Option 13/15 + # optionVersion alts --version + # type: Boolean min 0 max 1 + --version) + # shellcheck disable=SC2034 + optionVersion="1" + + if ((options_parse_optionParsedCountOptionVersion >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionVersion)) + optionVersionCallback "${options_parse_arg}" "${optionVersion}" + + ;; + + # Option 14/15 + # optionQuiet alts --quiet|-q + # type: Boolean min 0 max 1 + --quiet | -q) + # shellcheck disable=SC2034 + optionQuiet="1" + + if ((options_parse_optionParsedCountOptionQuiet >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionQuiet)) + optionQuietCallback "${options_parse_arg}" "${optionQuiet}" + + updateArgListQuietCallback "${options_parse_arg}" "${optionQuiet}" + + ;; + + # Option 15/15 + # optionSkipBackup alts --skip-backup + # type: Boolean min 0 max 1 + --skip-backup) + # shellcheck disable=SC2034 + optionSkipBackup="1" + + if ((options_parse_optionParsedCountOptionSkipBackup >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionSkipBackup)) + ;; + + -*) + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" + return 1 + fi + ;; + *) + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + # too much args and no unknownArgumentCallbacks configured + Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" + return 1 + fi + + ((++options_parse_parsedArgIndex)) + ;; + esac + shift || true + done || return $? + commandOptionParseFinished + +} + +# @description display command options and arguments help for installCommand +installCommandHelp() { + Array::wrap2 ' ' 80 0 "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" \ + "Installs bash-tools." + echo + echo + + # ------------------------------------------ + # usage section + # ------------------------------------------ + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "install [OPTIONS] " + echo + # ------------------------------------------ + # usage/options section + # ------------------------------------------ + optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--skip-backup]" + ) + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ + "install" "${optionsAltList[@]}" + echo + + # ------------------------------------------ + # options section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays this command help" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays configuration" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config bash-framework-config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--env-file env-file${__HELP_NORMAL} {list} (optional)" + Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--log-level log-level${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--log-file log-file${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log file" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--display-level display-level${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set display level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--theme theme${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." + echo + Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" + echo + + Array::wrap2 ' ' 76 6 " Default value: " "default" + echo + + echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Print version information and quit." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." + echo + + + echo + echo -e "${__HELP_TITLE_COLOR}OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--skip-backup${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "skip backup of ~/bash-tools." + echo + + + # ------------------------------------------ + # longDescription section + # ------------------------------------------ + echo + echo + echo -e "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" + declare -a installCommandLongDescription=( + "Installs dependent softwares and configuration needed to use bash-tools:" + "- GNU parallel" + "- Installs or updates default configuration files" + " If ~/.bash-tools directory already exists, a backup will be done before" + ) + Array::wrap2 ' ' 76 0 "${installCommandLongDescription[@]}" + echo + # ------------------------------------------ + # version section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" + echo "1.1" + # ------------------------------------------ + # author section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}AUTHOR: ${__RESET_COLOR}" + echo "[François Chastanet](https://github.com/fchastanet)" + # ------------------------------------------ + # sourceFile section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/build/install/binary-install.yaml" + # ------------------------------------------ + # license section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}LICENSE: ${__RESET_COLOR}" + echo "MIT License" + # ------------------------------------------ + # copyright section + # ------------------------------------------ + Array::wrap2 ' ' 76 0 "$(copyrightCallback)" +} + + +beforeParseCallback + +installCommandParse "$@" +MAIN_FUNCTION_NAME="main" +main() { + +Linux::requireExecutedAsUser +Linux::requireTarCommand + +if ! command -v parallel &>/dev/null; then + Log::displayInfo "We will install GNU parallel software, please enter you sudo password" + sudo apt-get update || true + if sudo apt-get install -y parallel; then + # remove parallel nagware + mkdir -p "${HOME}/.parallel" + touch "${HOME}/.parallel/will-cite" + else + Log::displayWarning "Impossible to install GNU parallel, please install it manually" + fi +else + Log::displaySkipped "parallel is already installed" +fi + +if [[ -d "${HOME}/.bash-tools" ]]; then + # shellcheck disable=SC2154 + if [[ "${optionSkipBackup}" = "1" ]]; then + Log::displayInfo "Backup of ~/.bash-tools is set to be skipped" + else + BACKUP_DIR="${FRAMEWORK_ROOT_DIR}/logs" \ + Backup::dir "${HOME}" ".bash-tools" + fi + + Log::displayInfo "Updating configuration" + cp -R --no-clobber "${BASH_TOOLS_ROOT_DIR}/conf/." "${HOME}/.bash-tools" + if [[ "${FRAMEWORK_ROOT_DIR}/conf/.env" -nt "${HOME}/.bash-tools/.env" ]]; then + Log::displayWarning "${FRAMEWORK_ROOT_DIR}/conf/.env is newer than ${HOME}/.bash-tools/.env, compare the files to check if some updates need to be applied" + else + Log::displaySkipped "${HOME}/.bash-tools/.env is up to date" + fi +else + Log::displayInfo "Installing configuration in ~/.bash-tools" + mkdir -p "${HOME}/.bash-tools" + cp -R "${BASE_DIR}/conf/." "${HOME}/.bash-tools" +fi + +} + +# if file is sourced avoid calling main function +# shellcheck disable=SC2178 +BASH_SOURCE=".$0" # cannot be changed in bash +# shellcheck disable=SC2128 +if test ".$0" == ".${BASH_SOURCE}"; then + if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then + main "$@" &>/dev/null + else + main "$@" + fi +fi diff --git a/src/_binaries/build/install.options.tpl b/src/_binaries/build/install.options.tpl deleted file mode 100644 index cc9b5d66..00000000 --- a/src/_binaries/build/install.options.tpl +++ /dev/null @@ -1,13 +0,0 @@ -% -declare versionNumber="1.0" -declare commandFunctionName="installCommand" -declare help="Install dependent softwares and configuration needed to use bash-tools -- GNU parallel -- Install default configuration files" -% - -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.base.tpl)" - -% -Options::generateCommand "${options[@]}" -% diff --git a/src/_binaries/build/install.sh b/src/_binaries/build/install.sh deleted file mode 100755 index dd75f55d..00000000 --- a/src/_binaries/build/install.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/install -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=. -# FACADE -# shellcheck disable=SC2034 - -declare copyrightBeginYear="2020" -declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" - -.INCLUDE "$(dynamicTemplateDir _binaries/build/install.options.tpl)" - -# @require Linux::requireExecutedAsUser -run() { - if ! command -v parallel &>/dev/null; then - Log::displayInfo "We will install GNU parallel software, please enter you sudo password" - sudo apt update || true - if sudo apt install -y parallel; then - # remove parallel nagware - mkdir -p ~/.parallel - touch ~/.parallel/will-cite - else - Log::displayWarning "Impossible to install GNU parallel, please install it manually" - fi - else - Log::displaySkipped "parallel is already installed" - fi - - if [[ -d "${HOME}/.bash-tools" ]]; then - Log::displayInfo "Updating configuration" - cp -R --no-clobber "${BASH_TOOLS_ROOT_DIR}/conf/." "${HOME}/.bash-tools" - if [[ "${BASE_DIR}/conf/.env" -nt "${HOME}/.bash-tools/.env" ]]; then - Log::displayWarning "${BASE_DIR}/conf/.env is newer than ${HOME}/.bash-tools/.env, compare the files to check if some updates need to be applied" - else - Log::displaySkipped "${HOME}/.bash-tools/.env is up to date" - fi - else - Log::displayInfo "Installing configuration in ~/.bash-tools" - mkdir -p ~/.bash-tools - cp -R conf/. ~/.bash-tools - fi -} - -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run -fi diff --git a/src/_binaries/build/install/binary-install.yaml b/src/_binaries/build/install/binary-install.yaml new file mode 100644 index 00000000..81a6c09f --- /dev/null +++ b/src/_binaries/build/install/binary-install.yaml @@ -0,0 +1,34 @@ +extends: + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" + +vars: + SRC_FILE_PATH: src/_binaries/build/install/binary-install.yaml + +compilerConfig: + targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/install" + relativeRootDirBasedOnTargetDir: .. +binData: + commands: + default: + functionName: installCommand + version: "1.1" + commandName: install + beforeParseCallbacks: + - beforeParseCallback + definitionFiles: + 20: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/build/install/install-options.sh + mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/build/install/install-main.sh + help: Installs bash-tools. + longDescription: | + Installs dependent softwares and configuration needed to use bash-tools: + - GNU parallel + - Installs or updates default configuration files + If ~/.bash-tools directory already exists, a backup will be done before + options: + - alts: + - "--skip-backup" + group: OptionsGroup + type: Boolean + help: skip backup of ~/bash-tools. + variableName: optionSkipBackup diff --git a/src/_binaries/build/install/install-main.sh b/src/_binaries/build/install/install-main.sh new file mode 100755 index 00000000..fe7a4391 --- /dev/null +++ b/src/_binaries/build/install/install-main.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +Linux::requireExecutedAsUser +Linux::requireTarCommand + +if ! command -v parallel &>/dev/null; then + Log::displayInfo "We will install GNU parallel software, please enter you sudo password" + sudo apt-get update || true + if sudo apt-get install -y parallel; then + # remove parallel nagware + mkdir -p "${HOME}/.parallel" + touch "${HOME}/.parallel/will-cite" + else + Log::displayWarning "Impossible to install GNU parallel, please install it manually" + fi +else + Log::displaySkipped "parallel is already installed" +fi + +if [[ -d "${HOME}/.bash-tools" ]]; then + # shellcheck disable=SC2154 + if [[ "${optionSkipBackup}" = "1" ]]; then + Log::displayInfo "Backup of ~/.bash-tools is set to be skipped" + else + BACKUP_DIR="${FRAMEWORK_ROOT_DIR}/logs" \ + Backup::dir "${HOME}" ".bash-tools" + fi + + Log::displayInfo "Updating configuration" + cp -R --no-clobber "${BASH_TOOLS_ROOT_DIR}/conf/." "${HOME}/.bash-tools" + if [[ "${FRAMEWORK_ROOT_DIR}/conf/.env" -nt "${HOME}/.bash-tools/.env" ]]; then + Log::displayWarning "${FRAMEWORK_ROOT_DIR}/conf/.env is newer than ${HOME}/.bash-tools/.env, compare the files to check if some updates need to be applied" + else + Log::displaySkipped "${HOME}/.bash-tools/.env is up to date" + fi +else + Log::displayInfo "Installing configuration in ~/.bash-tools" + mkdir -p "${HOME}/.bash-tools" + cp -R "${BASE_DIR}/conf/." "${HOME}/.bash-tools" +fi diff --git a/src/_binaries/build/install/install-options.sh b/src/_binaries/build/install/install-options.sh new file mode 100755 index 00000000..a1439db0 --- /dev/null +++ b/src/_binaries/build/install/install-options.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +# shellcheck disable=SC2034 +declare copyrightBeginYear="2020" +# shellcheck disable=SC2034 +declare versionNumber="1.1" + +declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" + +optionHelpCallback() { + installCommandHelp + exit 0 +} From 092bcb34c5a6768a1b2202b85dbce790d6a16a88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Mon, 26 Aug 2024 22:25:59 +0200 Subject: [PATCH 04/24] compiled mysql2puml using go compiler --- .pre-commit-config-github.yaml | 4 +- .pre-commit-config.yaml | 4 +- bin/mysql2puml | 2648 ++++++++--------- .../Converters/mysql2puml.options.tpl | 84 - src/_binaries/Converters/mysql2puml.sh | 36 - .../mysql2puml/binary-mysql2puml.yaml | 44 + .../Converters/mysql2puml/mysql2puml-main.sh | 17 + .../mysql2puml/mysql2puml-options.sh | 48 + .../{ => mysql2puml}/mysql2puml.awk | 0 .../{ => mysql2puml}/mysql2puml.bats | 0 .../{ => mysql2puml}/testsData/.env | 0 .../testsData/mysql2puml-model.png | Bin .../testsData/mysql2puml.dump.sql | 0 .../testsData/mysql2puml.help.txt | 0 .../{ => mysql2puml}/testsData/mysql2puml.png | Bin .../testsData/mysql2puml.puml | 0 .../{ => mysql2puml}/testsData/mysql2puml.svg | 0 .../testsData/mysql2pumlSkins/default.png | Bin .../testsData/mysql2pumlSkins/default.puml | 0 .../testsData/mysql2pumlSkins/default.svg | 0 20 files changed, 1408 insertions(+), 1477 deletions(-) delete mode 100644 src/_binaries/Converters/mysql2puml.options.tpl delete mode 100755 src/_binaries/Converters/mysql2puml.sh create mode 100644 src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml create mode 100755 src/_binaries/Converters/mysql2puml/mysql2puml-main.sh create mode 100755 src/_binaries/Converters/mysql2puml/mysql2puml-options.sh rename src/_binaries/Converters/{ => mysql2puml}/mysql2puml.awk (100%) rename src/_binaries/Converters/{ => mysql2puml}/mysql2puml.bats (100%) rename src/_binaries/Converters/{ => mysql2puml}/testsData/.env (100%) rename src/_binaries/Converters/{ => mysql2puml}/testsData/mysql2puml-model.png (100%) rename src/_binaries/Converters/{ => mysql2puml}/testsData/mysql2puml.dump.sql (100%) rename src/_binaries/Converters/{ => mysql2puml}/testsData/mysql2puml.help.txt (100%) rename src/_binaries/Converters/{ => mysql2puml}/testsData/mysql2puml.png (100%) rename src/_binaries/Converters/{ => mysql2puml}/testsData/mysql2puml.puml (100%) rename src/_binaries/Converters/{ => mysql2puml}/testsData/mysql2puml.svg (100%) rename src/_binaries/Converters/{ => mysql2puml}/testsData/mysql2pumlSkins/default.png (100%) rename src/_binaries/Converters/{ => mysql2puml}/testsData/mysql2pumlSkins/default.puml (100%) rename src/_binaries/Converters/{ => mysql2puml}/testsData/mysql2pumlSkins/default.svg (100%) diff --git a/.pre-commit-config-github.yaml b/.pre-commit-config-github.yaml index 0dbcb0df..593450e8 100644 --- a/.pre-commit-config-github.yaml +++ b/.pre-commit-config-github.yaml @@ -127,8 +127,8 @@ repos: ^.cspell/codespellrc-.*.txt$ ) - - repo: https://github.com/pre-commit/mirrors-prettier - rev: v4.0.0-alpha.8 + - repo: https://github.com/rbubley/mirrors-prettier + rev: v3.3.3 hooks: - id: prettier exclude: | diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index aa8d97d4..2cdda081 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -122,8 +122,8 @@ repos: ^.cspell/codespellrc-.*.txt$ ) - - repo: https://github.com/pre-commit/mirrors-prettier - rev: v4.0.0-alpha.8 + - repo: https://github.com/rbubley/mirrors-prettier + rev: v3.3.3 hooks: - id: prettier exclude: | diff --git a/bin/mysql2puml b/bin/mysql2puml index a91470cb..535deff1 100755 --- a/bin/mysql2puml +++ b/bin/mysql2puml @@ -1,13 +1,12 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FACADE FROM https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Converters/mysql2puml.sh +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml # DO NOT EDIT IT # @generated ############################################################################### # shellcheck disable=SC2288,SC2034 -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/mysql2puml -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE + + # ensure that no user aliases could interfere with # commands used in this script @@ -62,13 +61,6 @@ interruptManagement() { kill -s INT "$$" } trap interruptManagement INT -SCRIPT_NAME=${0##*/} -REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" -if [[ -n "${EMBED_CURRENT_DIR}" ]]; then - CURRENT_DIR="${EMBED_CURRENT_DIR}" -else - CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" -fi ################################################ # Temp dir management @@ -102,6 +94,20 @@ cleanOnExit() { } trap cleanOnExit EXIT HUP QUIT ABRT TERM + +SCRIPT_NAME=${0##*/} +REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" +if [[ -n "${EMBED_CURRENT_DIR}" ]]; then + CURRENT_DIR="${EMBED_CURRENT_DIR}" +else + CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" +fi +FRAMEWORK_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" +FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" +FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + # @description Log namespace provides 2 kind of functions # - Log::display* allows to display given message with # given display level @@ -132,310 +138,86 @@ export __VERBOSE_LEVEL_DEBUG=2 # @description verbose level info export __VERBOSE_LEVEL_TRACE=3 -# @description Display message using info color (bg light blue/fg white) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayInfo() { - local type="${2:-INFO}" - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logInfo "$1" "${type}" -} - -# @description Display message using debug color (gray) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayDebug() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then - Log::computeDuration - echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logDebug "$1" -} -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logWarning "$1" +# @description check if an element is contained in an array +# +# @arg $1 needle:String +# @arg $@ array:String[] +# @exitcode 0 if found +# @exitcode 1 otherwise +# @example +# Array::contains "${libPath}" "${__BASH_FRAMEWORK_IMPORTED_FILES[@]}" +Array::contains() { + local element + for element in "${@:2}"; do + [[ "${element}" = "$1" ]] && return 0 + done + return 1 } -# @description Display message using error color (red) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayError() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then - Log::computeDuration - echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logError "$1" -} -# @description load colors theme constants -# @warning if tty not opened, noColor theme will be chosen -# @arg $1 theme:String the theme to use (default, noColor) -# @arg $@ args:String[] -# @set __ERROR_COLOR String indicate error status -# @set __INFO_COLOR String indicate info status -# @set __SUCCESS_COLOR String indicate success status -# @set __WARNING_COLOR String indicate warning status -# @set __SKIPPED_COLOR String indicate skipped status -# @set __DEBUG_COLOR String indicate debug status -# @set __HELP_COLOR String indicate help status -# @set __TEST_COLOR String not used -# @set __TEST_ERROR_COLOR String not used -# @set __HELP_TITLE_COLOR String used to display help title in help strings -# @set __HELP_OPTION_COLOR String used to display highlight options in help strings -# -# @set __RESET_COLOR String reset default color +# @description concatenate each element of an array with a separator +# but wrapping text when line length is more than provided argument +# The algorithm will try not to cut the array element if it can. +# - if an arg can be placed on current line it will be, +# otherwise current line is printed and arg is added to the new +# current line +# - Empty arg is interpreted as a new line. +# - Add \r to arg in order to force break line and avoid following +# arg to be concatenated with current arg. # -# @set __HELP_EXAMPLE String to remove -# @set __HELP_TITLE String to remove -# @set __HELP_NORMAL String to remove -# shellcheck disable=SC2034 -UI::theme() { - local theme="${1-default}" - if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then - theme="noColor" +# @arg $1 glue:String +# @arg $2 maxLineLength:int +# @arg $3 indentNextLine:int +# @arg $@ array:String[] +Array::wrap2() { + local glue="${1-}" + local -i glueLength="${#glue}" + shift || true + local -i maxLineLength=$1 + shift || true + local -i indentNextLine=$1 + shift || true + local indentStr="" + if ((indentNextLine > 0)); then + indentStr="$(head -c "${indentNextLine}" &2 - Log::logFatal "$1" - exit 1 -} + ( + local currentLine + local -i currentLineLength=0 isNewline=1 argLength=0 + local -a additionalLines + local -i previousLineEmpty=0 + local arg="" -# @description create a temp file using default TMPDIR variable -# initialized in _includes/_commonHeader.sh -# @env TMPDIR String (default value /tmp) -# @arg $1 templateName:String template name to use(optional) -Framework::createTempFile() { - mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" -} - -# @description ensure env files are loaded -# @arg $@ list of default files to load at the end -# @exitcode 1 if one of env files fails to load -# @stderr diagnostics information is displayed -# shellcheck disable=SC2120 -Env::requireLoad() { - local -a defaultFiles=("$@") - # get list of possible config files - local -a configFiles=() - if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then - # BASH_FRAMEWORK_ENV_FILES is an array - configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") - fi - local localFrameworkConfigFile - localFrameworkConfigFile="$(pwd)/.framework-config" - if [[ -f "${localFrameworkConfigFile}" ]]; then - configFiles+=("${localFrameworkConfigFile}") - fi - if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then - configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") - fi - configFiles+=("${optionEnvFiles[@]}") - configFiles+=("${defaultFiles[@]}") - - for file in "${configFiles[@]}"; do - # shellcheck source=/.framework-config - CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { - Log::displayError "while loading config file: ${file}" - return 1 - } - done -} - -# @description activate or not Log::display* and Log::log* functions -# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL -# environment variables loaded by Env::requireLoad -# try to create log file and rotate it if necessary -# @noargs -# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable -# @env BASH_FRAMEWORK_DISPLAY_LEVEL int -# @env BASH_FRAMEWORK_LOG_LEVEL int -# @env BASH_FRAMEWORK_LOG_FILE String -# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 -# @exitcode 0 always successful -# @stderr diagnostics information about log file is displayed -# @require Env::requireLoad -# @require UI::requireTheme -Log::requireLoad() { - if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - export BASH_FRAMEWORK_LOG_LEVEL - fi - - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then - if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 - fi - elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - fi - - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - # will always be created even if not in info level - Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" - if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then - Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" - fi - fi -} - -# @description concatenate each element of an array with a separator -# but wrapping text when line length is more than provided argument -# The algorithm will try not to cut the array element if it can. -# - if an arg can be placed on current line it will be, -# otherwise current line is printed and arg is added to the new -# current line -# - Empty arg is interpreted as a new line. -# - Add \r to arg in order to force break line and avoid following -# arg to be concatenated with current arg. -# -# @arg $1 glue:String -# @arg $2 maxLineLength:int -# @arg $3 indentNextLine:int -# @arg $@ array:String[] -Array::wrap2() { - local glue="${1-}" - local -i glueLength="${#glue}" - shift || true - local -i maxLineLength=$1 - shift || true - local -i indentNextLine=$1 - shift || true - local indentStr="" - if ((indentNextLine > 0)); then - indentStr="$(head -c "${indentNextLine}" 0)); do - arg="$1" - shift || true + while (($# > 0)); do + arg="$1" + shift || true # replace tab by 2 spaces arg="${arg//$'\t'/ }" @@ -502,52 +284,76 @@ Array::wrap2() { ) | sed -E -e 's/[[:blank:]]+$//' } -# @description list the conf files list available in bash-tools/conf/ folder -# and those overridden in ${HOME}/.bash-tools/ folder + +# @description check if command specified exists or return 1 +# with error and message if not # -# @arg $1 confFolder:String the directory name (not the path) to list -# @arg $2 extension:String the extension (.sh by default) -# @arg $3 indentStr:String the indentation (' - ' by default) can be any string compatible with sed not containing any / +# @arg $1 commandName:String on which existence must be checked +# @arg $2 helpIfNotExists:String a help command to display if the command does not exist # -# @stdout list of files without extension/directory -# @example text -# - default.local -# - default.remote -# - localhost-root -Conf::getMergedList() { - local confFolder="$1" - local extension="${2-sh}" - local indentStr="${3- - }" - - local DEFAULT_CONF_DIR="${FRAMEWORK_ROOT_DIR}/conf/${confFolder}" - local HOME_CONF_DIR="${HOME}/.bash-tools/${confFolder}" +# @exitcode 1 if the command specified does not exist +# @stderr diagnostic information + help if second argument is provided +Assert::commandExists() { + local commandName="$1" + local helpIfNotExists="$2" - ( - if [[ -d "${DEFAULT_CONF_DIR}" ]]; then - Conf::list "${DEFAULT_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" - fi - if [[ -d "${HOME_CONF_DIR}" ]]; then - Conf::list "${HOME_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" + "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { + Log::displayError "${commandName} is not installed, please install it" + if [[ -n "${helpIfNotExists}" ]]; then + Log::displayInfo "${helpIfNotExists}" fi - ) | sort | uniq + return 1 + } + return 0 } -# @description check if an element is contained in an array + +# @description check if tty (interactive mode) is active +# @noargs +# @exitcode 1 if tty not active +# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive +# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive +Assert::tty() { + if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then + return 1 + fi + if [[ "${INTERACTIVE:-0}" = "1" ]]; then + return 0 + fi + tty -s +} + + +# @description convert base64 encoded back to target file +# if target file is executable prepend dir of target +# file to PATH to make binary available everywhere +# it is advised to include in the path of the target file +# the md5sum of the binFile # -# @arg $1 needle:String -# @arg $@ array:String[] -# @exitcode 0 if found -# @exitcode 1 otherwise -# @example -# Array::contains "${libPath}" "${__BASH_FRAMEWORK_IMPORTED_FILES[@]}" -Array::contains() { - local element - for element in "${@:2}"; do - [[ "${element}" = "$1" ]] && return 0 - done - return 1 +# @arg $1 targetFile:String the file to write +# @arg $2 binFileBase64:String the base64 encoded file +# @arg $3 fileMode:String the chmod to set on the file +# @set PATH String prepend target embedded file binary directory to PATH variable if binary executable +Compiler::Embed::extractFileFromBase64() { + local targetFile="$1" + local binFileBase64="$2" + local fileMode="${3:-+x}" + local targetDir="${targetFile%/*}" + + if [[ ! -f "${targetFile}" ]]; then + if [[ ! -d "${targetDir}" ]]; then + mkdir -p "${targetDir}" + fi + base64 -d >"${targetFile}" <<<"${binFileBase64}" + chmod "${fileMode}" "${targetFile}" + fi + + if [[ -x "${targetFile}" ]]; then + Env::pathPrepend "${targetDir}" + fi } + # @description get absolute conf file from specified conf folder deduced using these rules # * from absolute file (ignores and ) # * relative to where script is executed (ignores and ) @@ -609,134 +415,187 @@ Conf::getAbsoluteFile() { return 1 } -# @description remove all empty lines -# - at the beginning of the file before non empty line -# - at the end of the file after last non empty line -# @arg $@ files:String[] the files to filter -# @exitcode * if one of the filter command fails -# @stdin you can use stdin as alternative to files argument -# @stdout the filtered content -# shellcheck disable=SC2120 -# @see https://unix.stackexchange.com/a/653883 -Filters::trimEmptyLines() { - awk ' - NF {print saved $0; saved = ""; started = 1; next} - started {saved = saved $0 ORS} - ' "$@" -} -bashToolsDefaultConfigTemplate="${bashToolsDefaultConfigTemplate:-$( - cat <<'EOF' -# shellcheck disable=SC2034 +# @description list the conf files list available in bash-tools/conf/ folder +# and those overridden in ${HOME}/.bash-tools/ folder +# +# @arg $1 confFolder:String the directory name (not the path) to list +# @arg $2 extension:String the extension (.sh by default) +# @arg $3 indentStr:String the indentation (' - ' by default) can be any string compatible with sed not containing any / +# +# @stdout list of files without extension/directory +# @example text +# - default.local +# - default.remote +# - localhost-root +Conf::getMergedList() { + local confFolder="$1" + local extension="${2-sh}" + local indentStr="${3- - }" -# Default settings -# you can override these settings by creating ${HOME}/.bash-tools/.env file - -### -### DISPLAY Level -### minimum level of the messages that will be displayed on screen -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} - -### -### DISPLAY duration -### 0: no duration is displayed on the messages -### 1: duration between previous message and current is displayed -### with the message -### -DISPLAY_DURATION=${DISPLAY_DURATION:0} - -### -### Log to file -### -### all log messages will be redirected to log file specified -### this same path will be used inside and outside of the container -### -BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} - -### -### LOG Level -### minimum level of the messages that will be logged into LOG_FILE -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} - -# absolute directory containing db import sql dumps -DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} - -# garbage collect all files for which modification is greater than eg: 30 days (+30) -# each time an existing file is used by dbImport/dbImportTable -# the file modification time is set to now -DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} - -# absolute directory containing dbScripts used by dbScriptAllDatabases -SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} - -# ----------------------------------------------------- -# AWS Parameters -# ----------------------------------------------------- -S3_BASE_URL=${S3_BASE_URL:-} - -# ----------------------------------------------------- -# Postman Parameters -# ----------------------------------------------------- -POSTMAN_API_KEY= -EOF -)}" - -# @description loads ~/.bash-tools/.env if available -# if not creates it from a default template -# else check if new options need to be added -BashTools::Conf::requireLoad() { - local envFile="${HOME}/.bash-tools/.env" - if [[ ! -f "${envFile}" ]]; then - mkdir -p "${HOME}/.bash-tools" - ( - echo "#!/usr/bin/env bash" - echo "${bashToolsDefaultConfigTemplate}" - ) >"${envFile}" - Log::displayInfo "Configuration file '${envFile}' created" - else - if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then - ( - echo '# -----------------------------------------------------' - echo '# Postman Parameters' - echo '# -----------------------------------------------------' - echo 'POSTMAN_API_KEY=' - ) >>"${envFile}" + local DEFAULT_CONF_DIR="${FRAMEWORK_ROOT_DIR}/conf/${confFolder}" + local HOME_CONF_DIR="${HOME}/.bash-tools/${confFolder}" + + ( + if [[ -d "${DEFAULT_CONF_DIR}" ]]; then + Conf::list "${DEFAULT_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" fi - fi - # shellcheck source=/conf/.env - source "${envFile}" || { - Log::displayError "impossible to load '${envFile}'" - exit 1 - } + if [[ -d "${HOME_CONF_DIR}" ]]; then + Conf::list "${HOME_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" + fi + ) | sort | uniq } -# @description ensure COMMAND_BIN_DIR env var is set -# and PATH correctly prepared -# @noargs -# @set COMMAND_BIN_DIR string the directory where to find this command -# @set PATH string add directory where to find this command binary -Compiler::Facade::requireCommandBinDir() { - COMMAND_BIN_DIR="${CURRENT_DIR}" - Env::pathPrepend "${COMMAND_BIN_DIR}" -} -declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR -FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" -LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" +# @description list files of dir with given extension and display it as a list one by line +# +# @arg $1 dir:String the directory to list +# @arg $2 prefix:String the profile file prefix (default: "") +# @arg $3 ext:String the extension +# @arg $4 findOptions:String find options, eg: -type d (Default value: '-type f') +# @arg $5 indentStr:String the indentation can be any string compatible with sed not containing any / (Default value: ' - ') +# @stdout list of files without extension/directory +# @example text +# - default.local +# - default.remote +# - localhost-root +# @exitcode 1 if directory does not exists +Conf::list() { + local dir="$1" + local prefix="${2:-}" + local ext="${3}" + local findOptions="${4--type f}" + local indentStr="${5- - }" + + if [[ ! -d "${dir}" ]]; then + Log::displayError "Directory ${dir} does not exist" + fi + if [[ -n "${ext}" && "${ext:0:1}" != "." ]]; then + ext=".${ext}" + fi + ( + # shellcheck disable=SC2086 + cd "${dir}" && + find . -maxdepth 1 ${findOptions} -name "${prefix}*${ext}" | + sed -E "s#^\./${prefix}##g" | + sed -E "s#${ext}\$##g" | sort | sed -E "s#^#${indentStr}#" + ) +} + + +# @description prepend directories to the PATH environment variable +# @arg $@ args:String[] list of directories to prepend +# @set PATH update PATH with the directories prepended +Env::pathPrepend() { + local arg + for arg in "$@"; do + if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then + PATH="$(realpath "${arg}"):${PATH}" + fi + done +} + + +# @description ensure env files are loaded +# @arg $@ list of default files to load at the end +# @exitcode 1 if one of env files fails to load +# @stderr diagnostics information is displayed +# shellcheck disable=SC2120 +Env::requireLoad() { + REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 + + local -a defaultFiles=("$@") + # get list of possible config files + local -a configFiles=() + if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then + # BASH_FRAMEWORK_ENV_FILES is an array + configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") + fi + local localFrameworkConfigFile + localFrameworkConfigFile="$(pwd)/.framework-config" + if [[ -f "${localFrameworkConfigFile}" ]]; then + configFiles+=("${localFrameworkConfigFile}") + fi + if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then + configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") + fi + configFiles+=("${optionEnvFiles[@]}") + configFiles+=("${defaultFiles[@]}") + + for file in "${configFiles[@]}"; do + # shellcheck source=/.framework-config + CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { + Log::displayError "while loading config file: ${file}" + return 1 + } + done +} + + +# @description concatenate 2 paths and ensure the path is correct using realpath -m +# @arg $1 basePath:String +# @arg $2 subPath:String +File::concatenatePath() { + + if [[ "${REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Linux::requireRealpathCommand has not been loaded" + exit 1 + fi + + local basePath="$1" + local subPath="$2" + local fullPath="${basePath:+${basePath}/}${subPath}" + + realpath -m "${fullPath}" 2>/dev/null +} + + +# @description remove all empty lines +# - at the beginning of the file before non empty line +# - at the end of the file after last non empty line +# @arg $@ files:String[] the files to filter +# @exitcode * if one of the filter command fails +# @stdin you can use stdin as alternative to files argument +# @stdout the filtered content +# shellcheck disable=SC2120 +# @see https://unix.stackexchange.com/a/653883 +Filters::trimEmptyLines() { + awk ' + NF {print saved $0; saved = ""; started = 1; next} + started {saved = saved $0 ORS} + ' "$@" +} + + +# @description create a temp file using default TMPDIR variable +# @env TMPDIR String (default value /tmp) +# @arg $1 templateName:String template name to use(optional) +Framework::createTempFile() { + mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" +} + + +# @description ensure command realpath is available +# @exitcode 1 if realpath command not available +# @stderr diagnostics information is displayed +Linux::requireRealpathCommand() { + REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED=1 + + Assert::commandExists realpath +} + + +# @description ensure command tar is available +# @exitcode 1 if tar command not available +# @stderr diagnostics information is displayed +Linux::requireTarCommand() { + Assert::commandExists tar +} + + +declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR +FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" +LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" LOG_LAST_LOG_DATE_INIT=1 LOG_LAST_DURATION_STR="" @@ -772,30 +631,81 @@ Log::computeDuration() { fi } -# @description log message to file + +# @description Display message using debug color (gray) # @arg $1 message:String the message to display -Log::logInfo() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then - Log::logMessage "${2:-INFO}" "$1" +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayDebug() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then + Log::computeDuration + echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logDebug "$1" } -# @description log message to file + +# @description Display message using error color (red) # @arg $1 message:String the message to display -Log::logDebug() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then - Log::logMessage "${2:-DEBUG}" "$1" +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayError() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then + Log::computeDuration + echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logError "$1" +} + + +# @description Display message using info color (bg light blue/fg white) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayInfo() { + local type="${2:-INFO}" + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logInfo "$1" "${type}" +} + + +# @description Display message using warning color (yellow) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayWarning() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then + Log::computeDuration + echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logWarning "$1" +} + + +# @description Display message using error color (red) and exit immediately with error status 1 +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::fatal() { + Log::computeDuration + echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + Log::logFatal "$1" + exit 1 } + # @description log message to file # @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" +Log::logDebug() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then + Log::logMessage "${2:-DEBUG}" "$1" fi } + # @description log message to file # @arg $1 message:String the message to display Log::logError() { @@ -804,20 +714,6 @@ Log::logError() { fi } -# @description check if tty (interactive mode) is active -# @noargs -# @exitcode 1 if tty not active -# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive -# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive -Assert::tty() { - if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then - return 1 - fi - if [[ "${INTERACTIVE:-0}" = "1" ]]; then - return 0 - fi - tty -s -} # @description log message to file # @arg $1 message:String the message to display @@ -825,6 +721,16 @@ Log::logFatal() { Log::logMessage "${2:-FATAL}" "$1" } + +# @description log message to file +# @arg $1 message:String the message to display +Log::logInfo() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then + Log::logMessage "${2:-INFO}" "$1" + fi +} + + # @description Internal: common log message # @example text # [date]|[levelMsg]|message @@ -838,9 +744,18 @@ Log::logFatal() { # @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty # @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages # @stderr diagnostics information is displayed -# @require Env::requireLoad -# @require Log::requireLoad Log::logMessage() { + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Log::requireLoad has not been loaded" + exit 1 + fi + local levelMsg="$1" local msg="$2" local date @@ -852,18 +767,86 @@ Log::logMessage() { fi } -# @description To be called before logging in the log file -# @arg $1 file:string log file name -# @arg $2 maxLogFilesCount:int maximum number of log files -Log::rotate() { - local file="$1" - local maxLogFilesCount="${2:-5}" - if [[ ! -f "${file}" ]]; then - Log::displayDebug "Log file ${file} doesn't exist yet" - return 0 - fi - local i +# @description log message to file +# @arg $1 message:String the message to display +Log::logWarning() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then + Log::logMessage "${2:-WARNING}" "$1" + fi +} + + +# @description activate or not Log::display* and Log::log* functions +# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL +# environment variables loaded by Env::requireLoad +# try to create log file and rotate it if necessary +# @noargs +# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable +# @env BASH_FRAMEWORK_DISPLAY_LEVEL int +# @env BASH_FRAMEWORK_LOG_LEVEL int +# @env BASH_FRAMEWORK_LOG_FILE String +# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 +# @exitcode 0 always successful +# @stderr diagnostics information about log file is displayed +Log::requireLoad() { + REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement UI::requireTheme has not been loaded" + exit 1 + fi + + if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + export BASH_FRAMEWORK_LOG_LEVEL + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then + if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 + fi + elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + # will always be created even if not in info level + Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" + if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then + Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + fi + fi +} + + +# @description To be called before logging in the log file +# @arg $1 file:string log file name +# @arg $2 maxLogFilesCount:int maximum number of log files +Log::rotate() { + local file="$1" + local maxLogFilesCount="${2:-5}" + + if [[ ! -f "${file}" ]]; then + Log::displayDebug "Log file ${file} doesn't exist yet" + return 0 + fi + local i for ((i = maxLogFilesCount - 1; i > 0; i--)); do Log::displayInfo "Log rotation ${file}.${i} to ${file}.$((i + 1))" mv "${file}."{"${i}","$((i + 1))"} &>/dev/null || true @@ -874,64 +857,22 @@ Log::rotate() { fi } -# @description list files of dir with given extension and display it as a list one by line -# -# @arg $1 dir:String the directory to list -# @arg $2 prefix:String the profile file prefix (default: "") -# @arg $3 ext:String the extension -# @arg $4 findOptions:String find options, eg: -type d (Default value: '-type f') -# @arg $5 indentStr:String the indentation can be any string compatible with sed not containing any / (Default value: ' - ') -# @stdout list of files without extension/directory -# @example text -# - default.local -# - default.remote -# - localhost-root -# @exitcode 1 if directory does not exists -Conf::list() { - local dir="$1" - local prefix="${2:-}" - local ext="${3}" - local findOptions="${4--type f}" - local indentStr="${5- - }" - if [[ ! -d "${dir}" ]]; then - Log::displayError "Directory ${dir} does not exist" +# @description draw a line with the character passed in parameter repeated depending on terminal width +# @arg $1 character:String character to use as separator (default value #) +UI::drawLine() { + local character="${1:-#}" + local -i width=${COLUMNS:-0} + if ((width == 0)) && [[ -t 1 ]]; then + width=$(tput cols) fi - if [[ -n "${ext}" && "${ext:0:1}" != "." ]]; then - ext=".${ext}" + if ((width == 0)); then + width=80 fi - ( - # shellcheck disable=SC2086 - cd "${dir}" && - find . -maxdepth 1 ${findOptions} -name "${prefix}*${ext}" | - sed -E "s#^\./${prefix}##g" | - sed -E "s#${ext}\$##g" | sort | sed -E "s#^#${indentStr}#" - ) -} - -# @description concatenate 2 paths and ensure the path is correct using realpath -m -# @arg $1 basePath:String -# @arg $2 subPath:String -# @require Linux::requireRealpathCommand -File::concatenatePath() { - local basePath="$1" - local subPath="$2" - local fullPath="${basePath:+${basePath}/}${subPath}" - - realpath -m "${fullPath}" 2>/dev/null + printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") + echo } -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" - fi - done -} # @description load color theme # @noargs @@ -939,76 +880,104 @@ Env::pathPrepend() { # @env LOAD_THEME int 0 to avoid loading theme # @exitcode 0 always successful UI::requireTheme() { + REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + if [[ "${LOAD_THEME:-1}" = "1" ]]; then UI::theme "${BASH_FRAMEWORK_THEME-default}" fi } -# @description ensure command realpath is available -# @exitcode 1 if realpath command not available -# @stderr diagnostics information is displayed -Linux::requireRealpathCommand() { - Assert::commandExists realpath -} -# @description check if command specified exists or return 1 -# with error and message if not +# @description load colors theme constants +# @warning if tty not opened, noColor theme will be chosen +# @arg $1 theme:String the theme to use (default, noColor) +# @arg $@ args:String[] +# @set __ERROR_COLOR String indicate error status +# @set __INFO_COLOR String indicate info status +# @set __SUCCESS_COLOR String indicate success status +# @set __WARNING_COLOR String indicate warning status +# @set __SKIPPED_COLOR String indicate skipped status +# @set __DEBUG_COLOR String indicate debug status +# @set __HELP_COLOR String indicate help status +# @set __TEST_COLOR String not used +# @set __TEST_ERROR_COLOR String not used +# @set __HELP_TITLE_COLOR String used to display help title in help strings +# @set __HELP_OPTION_COLOR String used to display highlight options in help strings # -# @arg $1 commandName:String on which existence must be checked -# @arg $2 helpIfNotExists:String a help command to display if the command does not exist +# @set __RESET_COLOR String reset default color # -# @exitcode 1 if the command specified does not exist -# @stderr diagnostic information + help if second argument is provided -Assert::commandExists() { - local commandName="$1" - local helpIfNotExists="$2" - - "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { - Log::displayError "${commandName} is not installed, please install it" - if [[ -n "${helpIfNotExists}" ]]; then - Log::displayInfo "${helpIfNotExists}" - fi - return 1 - } - return 0 +# @set __HELP_EXAMPLE String to remove +# @set __HELP_TITLE String to remove +# @set __HELP_NORMAL String to remove +# shellcheck disable=SC2034 +UI::theme() { + local theme="${1-default}" + if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then + theme="noColor" + fi + case "${theme}" in + default | default-force) + theme="default" + ;; + noColor) ;; + *) + Log::fatal "invalid theme provided" + ;; + esac + if [[ "${theme}" = "default" ]]; then + BASH_FRAMEWORK_THEME="default" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='\e[31m' # Red + __INFO_COLOR='\e[44m' # white on lightBlue + __SUCCESS_COLOR='\e[32m' # Green + __WARNING_COLOR='\e[33m' # Yellow + __SKIPPED_COLOR='\e[33m' # Yellow + __DEBUG_COLOR='\e[37m' # Gray + __HELP_COLOR='\e[7;49;33m' # Black on Gold + __TEST_COLOR='\e[100m' # Light magenta + __TEST_ERROR_COLOR='\e[41m' # white on red + __HELP_TITLE_COLOR="\e[1;37m" # Bold + __HELP_OPTION_COLOR="\e[1;34m" # Blue + # Internal: reset color + __RESET_COLOR='\e[0m' # Reset Color + # shellcheck disable=SC2155,SC2034 + __HELP_EXAMPLE="$(echo -e "\e[2;97m")" + # shellcheck disable=SC2155,SC2034 + __HELP_TITLE="$(echo -e "\e[1;37m")" + # shellcheck disable=SC2155,SC2034 + __HELP_NORMAL="$(echo -e "\033[0m")" + else + BASH_FRAMEWORK_THEME="noColor" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='' + __INFO_COLOR='' + __SUCCESS_COLOR='' + __WARNING_COLOR='' + __SKIPPED_COLOR='' + __DEBUG_COLOR='' + __HELP_COLOR='' + __TEST_COLOR='' + __TEST_ERROR_COLOR='' + __HELP_TITLE_COLOR='' + __HELP_OPTION_COLOR='' + # Internal: reset color + __RESET_COLOR='' + __HELP_EXAMPLE='' + __HELP_TITLE='' + __HELP_NORMAL='' + fi } - # FUNCTIONS -facade_main_mysql2pumlsh() { -BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" -if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then - FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" -else - # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR - FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" -fi -FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" -FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" - -# @require BashTools::Conf::requireLoad -if [[ -f "${HOME}/.bash-tools/.env" ]]; then - export BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") -fi -# REQUIRES -Env::requireLoad -UI::requireTheme -Log::requireLoad -Linux::requireRealpathCommand -BashTools::Conf::requireLoad -Compiler::Facade::requireCommandBinDir - -# @require Compiler::Facade::requireCommandBinDir -# shellcheck disable=SC2034 - -declare copyrightBeginYear="2020" -declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" -declare optionSkin="default" declare -a BASH_FRAMEWORK_ARGV_FILTERED=() +beforeParseCallback() { + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + copyrightCallback() { if [[ -z "${copyrightBeginYear}" ]]; then copyrightBeginYear="$(date +%Y)" @@ -1045,13 +1014,14 @@ updateArgListQuietCallback() { :; } # shellcheck disable=SC2317 # if function is overridden optionHelpCallback() { - mysql2pumlCommand help + Log::displayError "optionHelpCallback needs to be overridden" exit 0 } # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { - echo "${SCRIPT_NAME} version 1.0" + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version ${versionNumber}" exit 0 } @@ -1069,21 +1039,22 @@ optionEnvFileCallback() { optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_INFO} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionDebugVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_DEBUG} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionTraceVerboseCallback() { + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vvv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_TRACE} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } getLevel() { @@ -1107,6 +1078,7 @@ getLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -1128,6 +1100,7 @@ getVerboseLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -1138,7 +1111,7 @@ optionDisplayLevelCallback() { logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1147,19 +1120,20 @@ optionLogLevelCallback() { local logLevel verboseLevel logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionLogFileCallback() { local logFile="$2" - echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionQuietCallback() { - echo "BASH_FRAMEWORK_QUIET_MODE=1" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_QUIET_MODE=1" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1190,534 +1164,105 @@ optionBashFrameworkConfigCallback() { defaultFrameworkConfig="$( cat <<'EOF' -# copied from src/_includes/.framework-config.default -# shellcheck disable=SC2034 - -REAL_SCRIPT_FILE="${REAL_SCRIPT_FILE:-$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")}" -FRAMEWORK_ROOT_DIR="${FRAMEWORK_ROOT_DIR:-${REAL_SCRIPT_FILE%/*/*}}" -FRAMEWORK_SRC_DIR="${FRAMEWORK_SRC_DIR:-${FRAMEWORK_ROOT_DIR}/src}" -FRAMEWORK_BIN_DIR="${FRAMEWORK_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/bin}" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_VENDOR_DIR:-${FRAMEWORK_ROOT_DIR}/vendor}" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_VENDOR_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/vendor/bin}" - -# describe the functions that will be skipped from being imported -FRAMEWORK_FUNCTIONS_IGNORE_REGEXP="${FRAMEWORK_FUNCTIONS_IGNORE_REGEXP:-^(Namespace::functions|Functions::myFunction|Namespace::requireSomething|Acquire::ForceIPv4)$}" -# describe the files that do not contain function to be imported -NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/_binaries|^src/_includes|^src/batsHeaders.sh$|^src/_standalone)}" -# describe the files that are allowed to not have an associated bats file -BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" -# describe the files that are allowed to not have a function matching the filename -FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|\.tpl$|/testsData/|^manualTests/|\.bats$}" -# Source directories -if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then - FRAMEWORK_SRC_DIRS=( - "${FRAMEWORK_ROOT_DIR}/src" - ) -fi - -# export here all the variables that will be used in your templates -export REPOSITORY_URL="${REPOSITORY_URL:-https://github.com/fchastanet/bash-tools-framework}" - -BASH_FRAMEWORK_THEME="${BASH_FRAMEWORK_THEME:-default}" -BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" -BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" -BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" -BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" -EOF -)" - -overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" - -commandOptionParseFinished() { - # load default template framework config - defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" - echo "${defaultFrameworkConfig}" > "${defaultEnvFile}" - local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi - # shellcheck disable=SC2154 - if [[ -f "${optionBashFrameworkConfig}" ]]; then - files+=("${optionBashFrameworkConfig}") - fi - files+=("${overrideEnvFile}") - Env::requireLoad "${files[@]}" - Log::requireLoad - # shellcheck disable=SC2154 - if [[ "${optionConfig}" = "1" ]]; then - displayConfig - fi -} - -mysql2pumlCommand() { - local options_parse_cmd="$1" - shift || true - - if [[ "${options_parse_cmd}" = "parse" ]]; then - optionSkin="default" - local -i options_parse_optionParsedCountOptionSkin - ((options_parse_optionParsedCountOptionSkin = 0)) || true - local -i options_parse_optionParsedCountOptionBashFrameworkConfig - ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true - optionConfig="0" - local -i options_parse_optionParsedCountOptionConfig - ((options_parse_optionParsedCountOptionConfig = 0)) || true - optionInfoVerbose="0" - local -i options_parse_optionParsedCountOptionInfoVerbose - ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true - optionDebugVerbose="0" - local -i options_parse_optionParsedCountOptionDebugVerbose - ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true - optionTraceVerbose="0" - local -i options_parse_optionParsedCountOptionTraceVerbose - ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionNoColor="0" - local -i options_parse_optionParsedCountOptionNoColor - ((options_parse_optionParsedCountOptionNoColor = 0)) || true - optionTheme="default" - local -i options_parse_optionParsedCountOptionTheme - ((options_parse_optionParsedCountOptionTheme = 0)) || true - optionHelp="0" - local -i options_parse_optionParsedCountOptionHelp - ((options_parse_optionParsedCountOptionHelp = 0)) || true - optionVersion="0" - local -i options_parse_optionParsedCountOptionVersion - ((options_parse_optionParsedCountOptionVersion = 0)) || true - optionQuiet="0" - local -i options_parse_optionParsedCountOptionQuiet - ((options_parse_optionParsedCountOptionQuiet = 0)) || true - local -i options_parse_optionParsedCountOptionLogLevel - ((options_parse_optionParsedCountOptionLogLevel = 0)) || true - local -i options_parse_optionParsedCountOptionLogFile - ((options_parse_optionParsedCountOptionLogFile = 0)) || true - local -i options_parse_optionParsedCountOptionDisplayLevel - ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true - local -i options_parse_argParsedCountInputSqlFile - ((options_parse_argParsedCountInputSqlFile = 0)) || true - # shellcheck disable=SC2034 - local -i options_parse_parsedArgIndex=0 - while (($# > 0)); do - local options_parse_arg="$1" - local argOptDefaultBehavior=0 - case "${options_parse_arg}" in - # Option 1/15 - # Option optionSkin --skin variableType String min 0 max 1 authorizedValues '' regexp '' - --skin) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionSkin >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionSkin)) - # shellcheck disable=SC2034 - optionSkin="$1" - optionSkinCallback "${options_parse_arg}" "${optionSkin}" - ;; - # Option 2/15 - # Option optionBashFrameworkConfig --bash-framework-config variableType String min 0 max 1 authorizedValues '' regexp '' - --bash-framework-config) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) - # shellcheck disable=SC2034 - optionBashFrameworkConfig="$1" - optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" - ;; - # Option 3/15 - # Option optionConfig --config variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --config) - # shellcheck disable=SC2034 - optionConfig="1" - if ((options_parse_optionParsedCountOptionConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionConfig)) - ;; - # Option 4/15 - # Option optionInfoVerbose --verbose|-v variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --verbose | -v) - # shellcheck disable=SC2034 - optionInfoVerbose="1" - if ((options_parse_optionParsedCountOptionInfoVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionInfoVerbose)) - optionInfoVerboseCallback "${options_parse_arg}" - updateArgListInfoVerboseCallback "${options_parse_arg}" - ;; - # Option 5/15 - # Option optionDebugVerbose -vv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vv) - # shellcheck disable=SC2034 - optionDebugVerbose="1" - if ((options_parse_optionParsedCountOptionDebugVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDebugVerbose)) - optionDebugVerboseCallback "${options_parse_arg}" - updateArgListDebugVerboseCallback "${options_parse_arg}" - ;; - # Option 6/15 - # Option optionTraceVerbose -vvv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vvv) - # shellcheck disable=SC2034 - optionTraceVerbose="1" - if ((options_parse_optionParsedCountOptionTraceVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTraceVerbose)) - optionTraceVerboseCallback "${options_parse_arg}" - updateArgListTraceVerboseCallback "${options_parse_arg}" - ;; - # Option 7/15 - # Option optionEnvFiles --env-file variableType StringArray min 0 max -1 authorizedValues '' regexp '' - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - ;; - # Option 8/15 - # Option optionNoColor --no-color variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --no-color) - # shellcheck disable=SC2034 - optionNoColor="1" - if ((options_parse_optionParsedCountOptionNoColor >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionNoColor)) - optionNoColorCallback "${options_parse_arg}" - updateArgListNoColorCallback "${options_parse_arg}" - ;; - # Option 9/15 - # Option optionTheme --theme variableType String min 0 max 1 authorizedValues 'default|default-force|noColor' regexp '' - --theme) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default|default-force|noColor)" - return 1 - fi - if ((options_parse_optionParsedCountOptionTheme >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTheme)) - # shellcheck disable=SC2034 - optionTheme="$1" - optionThemeCallback "${options_parse_arg}" "${optionTheme}" - updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" - ;; - # Option 10/15 - # Option optionHelp --help|-h variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --help | -h) - # shellcheck disable=SC2034 - optionHelp="1" - if ((options_parse_optionParsedCountOptionHelp >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionHelp)) - optionHelpCallback "${options_parse_arg}" - ;; - # Option 11/15 - # Option optionVersion --version variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --version) - # shellcheck disable=SC2034 - optionVersion="1" - if ((options_parse_optionParsedCountOptionVersion >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionVersion)) - optionVersionCallback "${options_parse_arg}" - ;; - # Option 12/15 - # Option optionQuiet --quiet|-q variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --quiet | -q) - # shellcheck disable=SC2034 - optionQuiet="1" - if ((options_parse_optionParsedCountOptionQuiet >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionQuiet)) - optionQuietCallback "${options_parse_arg}" - updateArgListQuietCallback "${options_parse_arg}" - ;; - # Option 13/15 - # Option optionLogLevel --log-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --log-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogLevel)) - # shellcheck disable=SC2034 - optionLogLevel="$1" - optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - ;; - # Option 14/15 - # Option optionLogFile --log-file variableType String min 0 max 1 authorizedValues '' regexp '' - --log-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogFile >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogFile)) - # shellcheck disable=SC2034 - optionLogFile="$1" - optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" - updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" - ;; - # Option 15/15 - # Option optionDisplayLevel --display-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --display-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionDisplayLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDisplayLevel)) - # shellcheck disable=SC2034 - optionDisplayLevel="$1" - optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - ;; - -*) - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" - return 1 - fi - ;; - *) - if ((0)); then - # Technical if - never reached - : - # Argument 1/1 - # Argument inputSqlFile min 0 max 1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 0 && options_parse_parsedArgIndex < 1)); then - if ((options_parse_argParsedCountInputSqlFile >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument inputSqlFile - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountInputSqlFile)) - # shellcheck disable=SC2034 - inputSqlFile="${options_parse_arg}" - inputSqlFileCallback "${inputSqlFile}" -- "${@:2}" - else - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" - return 1 - fi - fi - ((++options_parse_parsedArgIndex)) - ;; - esac - shift || true - done - commandOptionParseFinished - Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" - Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" - elif [[ "${options_parse_cmd}" = "help" ]]; then - Array::wrap2 " " 80 0 "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" "convert mysql dump sql schema to plantuml format" - echo - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "${SCRIPT_NAME}" "[OPTIONS]" "[ARGUMENTS]")" - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ - "${SCRIPT_NAME}" \ - "[--skin ]" "[--bash-framework-config ]" "[--config]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--no-color]" "[--theme ]" "[--help|-h]" "[--version]" "[--quiet|-q]" "[--log-level ]" "[--log-file ]" "[--display-level ]")" - echo - echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - echo -e " [${__HELP_OPTION_COLOR}inputSqlFile${__HELP_NORMAL} {single}]" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(sql\ filepath\ to\ parse\ \(read\ from\ stdin\ if\ not\ provided\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--skin ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(header\ configuration\ of\ the\ plant\ uml\ file) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' - echo - echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(use\ alternate\ bash\ framework\ configuration.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ configuration) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(info\ level\ verbose\ mode\ \(alias\ of\ --display-level\ INFO\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(debug\ level\ verbose\ mode\ \(alias\ of\ --display-level\ DEBUG\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(trace\ level\ verbose\ mode\ \(alias\ of\ --display-level\ TRACE\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Load\ the\ specified\ env\ file\ \(deprecated\,\ please\ use\ --bash-framework-config\ option\ instead\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Produce\ monochrome\ output.\ alias\ of\ --theme\ noColor.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' - echo ' Possible values: default|default-force|noColor' - echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ this\ command\ help) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Print\ version\ information\ and\ quit) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(quiet\ mode\,\ doesn\'t\ display\ any\ output) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ file) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(set\ display\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e """ -${__HELP_TITLE}EXAMPLE 1:${__HELP_NORMAL} -${__HELP_EXAMPLE}mysql2puml dump.dql${__HELP_NORMAL} - -${__HELP_TITLE}EXAMPLE 2:${__HELP_NORMAL} -${__HELP_EXAMPLE}mysqldump --skip-add-drop-table \ - --skip-add-locks \ - --skip-disable-keys \ - --skip-set-charset \ - --user=root \ - --password=root \ - --no-data skills | mysql2puml -${__HELP_NORMAL} -${__HELP_TITLE}LIST OF AVAILABLE SKINS:${__HELP_NORMAL} -@@@SKINS_LIST@@@""" - echo - echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo '1.0' - echo - echo -e "${__HELP_TITLE_COLOR}AUTHOR:${__RESET_COLOR}" - echo '[François Chastanet](https://github.com/fchastanet)' - echo - echo -e "${__HELP_TITLE_COLOR}SOURCE FILE:${__RESET_COLOR}" - echo 'https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Converters/mysql2puml.sh' - echo - echo -e "${__HELP_TITLE_COLOR}LICENSE:${__RESET_COLOR}" - echo 'MIT License' - echo - Array::wrap2 ' ' 76 4 "$(copyrightCallback)" - else - Log::displayError "Command ${SCRIPT_NAME} - Option command invalid: '${options_parse_cmd}'" - return 1 +# copied from src/_includes/.framework-config.default +# shellcheck disable=SC2034 + +REAL_SCRIPT_FILE="${REAL_SCRIPT_FILE:-$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")}" +FRAMEWORK_ROOT_DIR="${FRAMEWORK_ROOT_DIR:-${REAL_SCRIPT_FILE%/*/*}}" +FRAMEWORK_SRC_DIR="${FRAMEWORK_SRC_DIR:-${FRAMEWORK_ROOT_DIR}/src}" +FRAMEWORK_BIN_DIR="${FRAMEWORK_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/bin}" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_VENDOR_DIR:-${FRAMEWORK_ROOT_DIR}/vendor}" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_VENDOR_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/vendor/bin}" + +# describe the functions that will be skipped from being imported +FRAMEWORK_FUNCTIONS_IGNORE_REGEXP="${FRAMEWORK_FUNCTIONS_IGNORE_REGEXP:-^(Namespace::functions|Functions::myFunction|Namespace::requireSomething|Acquire::ForceIPv4)$}" +# describe the files that do not contain function to be imported +NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/_binaries|^src/_includes|^src/batsHeaders.sh$|^src/_standalone)}" +# describe the files that are allowed to not have an associated bats file +BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" +# describe the files that are allowed to not have a function matching the filename +FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|/testsData/|^manualTests/|\.bats$}" +# Source directories +if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then + FRAMEWORK_SRC_DIRS=( + "${FRAMEWORK_ROOT_DIR}/src" + ) +fi + +# export here all the variables that will be used in your templates +export REPOSITORY_URL="${REPOSITORY_URL:-https://github.com/fchastanet/bash-tools-framework}" + +BASH_FRAMEWORK_THEME="${BASH_FRAMEWORK_THEME:-default}" +BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" +BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" +BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" +BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" + +EOF +)" + +overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" + +commandOptionParseFinished() { + # load default template framework config + defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" + echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" + local -a files=("${defaultEnvFile}") + if [[ -f "${envFile}" ]]; then + files+=("${envFile}") + fi + # shellcheck disable=SC2154 + if [[ -f "${optionBashFrameworkConfig}" ]]; then + files+=("${optionBashFrameworkConfig}") + fi + files+=("${overrideEnvFile}") + Env::requireLoad "${files[@]}" + Log::requireLoad + # shellcheck disable=SC2154 + if [[ "${optionConfig}" = "1" ]]; then + displayConfig fi } + + +# shellcheck disable=SC2034 +declare copyrightBeginYear="2020" +# shellcheck disable=SC2034 +declare versionNumber="1.0" + +declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" + optionHelpCallback() { - local skinListHelpFile - skinListHelpFile="$(Framework::createTempFile "shellcheckHelp")" - Conf::getMergedList "mysql2pumlSkins" ".puml" " - " >"${skinListHelpFile}" - - mysql2pumlCommand help | - sed -E \ - -e "/@@@SKINS_LIST@@@/r ${skinListHelpFile}" \ - -e "/@@@SKINS_LIST@@@/d" + mysql2pumlCommandHelp exit 0 } +longDescriptionFunction() { + echo -e "${__HELP_TITLE}EXAMPLE 1:${__HELP_NORMAL}" + echo -e "${__HELP_EXAMPLE}mysql2puml dump.dql${__HELP_NORMAL}" + echo + echo -e "${__HELP_TITLE}EXAMPLE 2:${__HELP_NORMAL}" + echo -e "${__HELP_EXAMPLE}mysqldump --skip-add-drop-table \\" + echo -e " --skip-add-locks \\" + echo -e " --skip-disable-keys \\" + echo -e " --skip-set-charset \\" + echo -e " --user=root \\" + echo -e " --password=root \\" + echo -e " --no-data skills | mysql2puml" + echo -e "${__HELP_NORMAL}" + echo -e "${__HELP_TITLE}LIST OF AVAILABLE SKINS:${__HELP_NORMAL}" + Conf::getMergedList "mysql2pumlSkins" ".puml" " - " +} + optionSkinCallback() { declare -a skinList readarray -t skinList < <(Conf::getMergedList "mysql2pumlSkins" ".puml" "") - if ! Array::contains "$2" "${skinList[@]}"; then - Log::displayError "${SCRIPT_NAME} - invalid skin '$2' provided" - return 1 + # shellcheck disable=SC2154 + if ! Array::contains "${optionSkin}" "${skinList[@]}"; then + Log::displayError "${SCRIPT_NAME} - invalid skin '${optionSkin}' provided" + exit 1 fi } @@ -1725,253 +1270,650 @@ inputSqlFileCallback() { # shellcheck disable=SC2154 if [[ ! -f "${inputSqlFile}" ]]; then Log::displayError "${SCRIPT_NAME} - File '${inputSqlFile}' does not exists" - return 1 + exit 1 fi } -mysql2pumlCommand parse "${BASH_FRAMEWORK_ARGV[@]}" -declare awkScript -awkScript="$( - cat <<'EOF' -# ========================================================================= -# -# mysql2puml - SQL DDL to markdown converter -# -# Invocation/Execution: -# awk -f mysql2puml.awk skinFile < inputFile > outputFile -# DEBUG=1 awk -f mysql2puml.awk skinFile < inputFile > outputFile -# -# Supports -# PlantUML format -# ========================================================================= -function debug(first, second, third, fourth, fifth, sixth, seventh, eighth, ninth, tenth, eleventh, twelfth) { - if (DEBUG == 1) print("DEBUG - ", first, second, third, fourth, fifth, sixth, seventh, eighth, ninth, tenth, eleventh, twelfth) > "/dev/stderr" -} +# ------------------------------------------ +# Command mysql2pumlCommand +# ------------------------------------------ + +# options variables initialization +declare optionHelp="0" +declare optionConfig="0" +declare optionBashFrameworkConfig="" +declare optionInfoVerbose="0" +declare optionDebugVerbose="0" +declare optionTraceVerbose="0" +declare -a optionEnvFiles=() +declare optionLogLevel="" +declare optionLogFile="" +declare optionDisplayLevel="" +declare optionNoColor="0" +declare optionTheme="default" +declare optionVersion="0" +declare optionQuiet="0" +declare optionSkin="default" +# arguments variables initialization +declare inputSqlFile="" +# @description parse command options and arguments for mysql2pumlCommand +mysql2pumlCommandParse() { + Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" + Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" + optionHelp="0" + local -i options_parse_optionParsedCountOptionHelp + ((options_parse_optionParsedCountOptionHelp = 0)) || true + optionConfig="0" + local -i options_parse_optionParsedCountOptionConfig + ((options_parse_optionParsedCountOptionConfig = 0)) || true + optionBashFrameworkConfig="" + local -i options_parse_optionParsedCountOptionBashFrameworkConfig + ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true + optionInfoVerbose="0" + local -i options_parse_optionParsedCountOptionInfoVerbose + ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true + optionDebugVerbose="0" + local -i options_parse_optionParsedCountOptionDebugVerbose + ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true + optionTraceVerbose="0" + local -i options_parse_optionParsedCountOptionTraceVerbose + ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true + + optionLogLevel="" + local -i options_parse_optionParsedCountOptionLogLevel + ((options_parse_optionParsedCountOptionLogLevel = 0)) || true + optionLogFile="" + local -i options_parse_optionParsedCountOptionLogFile + ((options_parse_optionParsedCountOptionLogFile = 0)) || true + optionDisplayLevel="" + local -i options_parse_optionParsedCountOptionDisplayLevel + ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true + optionNoColor="0" + local -i options_parse_optionParsedCountOptionNoColor + ((options_parse_optionParsedCountOptionNoColor = 0)) || true + optionTheme="default" + local -i options_parse_optionParsedCountOptionTheme + ((options_parse_optionParsedCountOptionTheme = 0)) || true + optionVersion="0" + local -i options_parse_optionParsedCountOptionVersion + ((options_parse_optionParsedCountOptionVersion = 0)) || true + optionQuiet="0" + local -i options_parse_optionParsedCountOptionQuiet + ((options_parse_optionParsedCountOptionQuiet = 0)) || true + optionSkin="default" + local -i options_parse_optionParsedCountOptionSkin + ((options_parse_optionParsedCountOptionSkin = 0)) || true + + inputSqlFile="" + local -i options_parse_argParsedCountInputSqlFile + ((options_parse_argParsedCountInputSqlFile = 0)) || true + + + # shellcheck disable=SC2034 + local -i options_parse_parsedArgIndex=0 + while (($# > 0)); do + local options_parse_arg="$1" + local argOptDefaultBehavior=0 + case "${options_parse_arg}" in + # Option 1/15 + # optionHelp alts --help|-h + # type: Boolean min 0 max 1 + --help | -h) + # shellcheck disable=SC2034 + optionHelp="1" + + if ((options_parse_optionParsedCountOptionHelp >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionHelp)) + optionHelpCallback "${options_parse_arg}" "${optionHelp}" -# ========================================================================= + ;; -function uml_start() -{ - if (!skinFile) { - print("skinFile parameter missing") > "/dev/stderr" - exit 1 - } - while ((getline tmp < skinFile) > 0) { - if (match(tmp, /@enduml/)) { - break - } - print(tmp) - } - print "' entities" -} + # Option 2/15 + # optionConfig alts --config + # type: Boolean min 0 max 1 + --config) + # shellcheck disable=SC2034 + optionConfig="1" -# ========================================================================= + if ((options_parse_optionParsedCountOptionConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionConfig)) + ;; + + # Option 3/15 + # optionBashFrameworkConfig alts --bash-framework-config + # type: String min 0 max 1 + --bash-framework-config) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi -function uml_end() -{ - print "@enduml" -} + if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) + # shellcheck disable=SC2034 + optionBashFrameworkConfig="$1" + optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" + + ;; + + # Option 4/15 + # optionInfoVerbose alts --verbose|-v + # type: Boolean min 0 max 1 + --verbose | -v) + # shellcheck disable=SC2034 + optionInfoVerbose="1" + + if ((options_parse_optionParsedCountOptionInfoVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionInfoVerbose)) + optionInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" -# ========================================================================= + updateArgListInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" -function ltrim(s) { sub(/^[ \t\r\n]+/, "", s); return s } -function rtrim(s) { sub(/[ \t\r\n]+$/, "", s); return s } -function trim(s) { return rtrim(ltrim(s)); } + ;; -# ========================================================================= + # Option 5/15 + # optionDebugVerbose alts -vv + # type: Boolean min 0 max 1 + -vv) + # shellcheck disable=SC2034 + optionDebugVerbose="1" -function column_weight(column) -{ - return (columnsDetails[column ",pk"] == "1" ? 8 : 0) \ - + (columnsDetails[column ",fk"] == "1" ? 4 : 0) \ - + (columnsDetails[column ",unique"] == "1" ? 2 : 0) \ - + (columnsDetails[column ",null"] == "0" ? 1 : 0) -} + if ((options_parse_optionParsedCountOptionDebugVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDebugVerbose)) + optionDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + updateArgListDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + ;; + + # Option 6/15 + # optionTraceVerbose alts -vvv + # type: Boolean min 0 max 1 + -vvv) + # shellcheck disable=SC2034 + optionTraceVerbose="1" + + if ((options_parse_optionParsedCountOptionTraceVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTraceVerbose)) + optionTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + updateArgListTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + ;; + + # Option 7/15 + # optionEnvFiles alts --env-file + # type: StringArray min 0 max -1 + --env-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + ((++options_parse_optionParsedCountOptionEnvFiles)) + optionEnvFiles+=("$1") + optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + ;; + + # Option 8/15 + # optionLogLevel alts --log-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --log-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogLevel)) + # shellcheck disable=SC2034 + optionLogLevel="$1" + optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + ;; + + # Option 9/15 + # optionLogFile alts --log-file + # type: String min 0 max 1 + --log-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogFile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogFile)) + # shellcheck disable=SC2034 + optionLogFile="$1" + optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + ;; + + # Option 10/15 + # optionDisplayLevel alts --display-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --display-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionDisplayLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDisplayLevel)) + # shellcheck disable=SC2034 + optionDisplayLevel="$1" + optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + ;; + + # Option 11/15 + # optionNoColor alts --no-color + # type: Boolean min 0 max 1 + --no-color) + # shellcheck disable=SC2034 + optionNoColor="1" + + if ((options_parse_optionParsedCountOptionNoColor >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionNoColor)) + optionNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + updateArgListNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + ;; + + # Option 12/15 + # optionTheme alts --theme + # type: String min 0 max 1 + # authorizedValues: default|default-force|noColor + --theme) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ default|default-force|noColor ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionTheme >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTheme)) + # shellcheck disable=SC2034 + optionTheme="$1" + optionThemeCallback "${options_parse_arg}" "${optionTheme}" + + updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" + + ;; + + # Option 13/15 + # optionVersion alts --version + # type: Boolean min 0 max 1 + --version) + # shellcheck disable=SC2034 + optionVersion="1" + + if ((options_parse_optionParsedCountOptionVersion >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionVersion)) + optionVersionCallback "${options_parse_arg}" "${optionVersion}" + + ;; + + # Option 14/15 + # optionQuiet alts --quiet|-q + # type: Boolean min 0 max 1 + --quiet | -q) + # shellcheck disable=SC2034 + optionQuiet="1" + + if ((options_parse_optionParsedCountOptionQuiet >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionQuiet)) + optionQuietCallback "${options_parse_arg}" "${optionQuiet}" + + updateArgListQuietCallback "${options_parse_arg}" "${optionQuiet}" + + ;; + + # Option 15/15 + # optionSkin alts --skin + # type: String min 0 max 1 + --skin) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi -function column_sort(i1, column1, i2, column2) -{ - weight1 = column_weight(column1) - weight2 = column_weight(column2) - if (weight1 == weight2) { - l = tolower(column1) - r = tolower(column2) + if ((options_parse_optionParsedCountOptionSkin >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionSkin)) + # shellcheck disable=SC2034 + optionSkin="$1" + optionSkinCallback "${options_parse_arg}" "${optionSkin}" + + ;; - if (l < r) { - return -1 - } else if (l == r) { - return 0 - } else { + -*) + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" + return 1 + fi + ;; + *) + if ((0)); then + # Technical if - never reached + : + + # Argument 1/1 + # inputSqlFile min 0 max 1 + # authorizedValues: + elif (( options_parse_parsedArgIndex >= 0 && + options_parse_parsedArgIndex < 1 )); then + + if ((options_parse_argParsedCountInputSqlFile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument sqlFile - Maximum number of argument occurrences reached(1)" return 1 - } - } else { - return weight2 - weight1 - } -} + fi + ((++options_parse_argParsedCountInputSqlFile)) + # shellcheck disable=SC2034 + inputSqlFile="${options_parse_arg}" + optionSkinCallback "${inputSqlFile}" -- "${@:2}" -# ========================================================================= - -function uml_table(createTable) -# DDL to plantuml -# CREATE TABLE `core_customer` (`id` int(11) NOT NULL AUTO_INCREMENT, `instance_name` varchar(128) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `instance_name` (`instance_name`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; -# CREATE TABLE `core_learner_skill` (`id` int(11) NOT NULL AUTO_INCREMENT, `customer_id` int(10) unsigned NOT NULL, PRIMARY KEY (`id`), KEY `customer_id_684f904f_fk_core_learner_id` (`customer_id`), CONSTRAINT `customer_id_684f904f_fk_core_customer_id` FOREIGN KEY (`customer_id`) REFERENCES `core_customer` (`id`)) ENGINE=InnoDB AUTO_INCREMENT=415 DEFAULT CHARSET=utf8; -#table( user ) { -# primary_key( id ): UUID -# column( isActive ): BOOLEAN -#} -#LearnerSkill "0..*" --> "1" Learner : "learner_id" -{ - debug("uml_table", createTable) - split(createTable,lines,"\n") - columnIdx=0 - for (line in lines) { - debug(lines[line]) - if (match(lines[line], /CREATE TABLE `([^`]+)` \(/, arr)) { - debug("TABLE ", lines[line]) - tableName = arr[1] - } else if (match(lines[line], /PRIMARY KEY \(([^)]+)\)/, arr)) { - # PRIMARY KEY (`id`), - debug("PK ", lines[line]) - split(arr[1],primaryKeys,",") - for (i in primaryKeys) { - if(match(primaryKeys[i], /[ ]?`([^`]+)+`[ ]?/, arr)) { - columnsDetails[arr[1] ",pk"] = "1" - } - } - } else if (match(lines[line], /CONSTRAINT `[^`]+` FOREIGN KEY \(`([^`]+)`\) REFERENCES `([^`]+)` \(`([^`]+)`\)/, arr)) { - # CONSTRAINT `core_learner_customer_id_6d356da8_fk_core_customer_id` FOREIGN KEY (`customer_id`) REFERENCES `core_customer` (`id`) - debug("Constraint ", lines[line]) - columnName=arr[1] - columnsDetails[columnName ",fk"]="1" - columnsDetails[columnName ",fkTable"]=arr[2] - columnsDetails[columnName ",fkColumn"]=arr[3] - } else if (match(lines[line], /UNIQUE KEY `[^`]+` \(([^)]+)\)/, arr)) { - # UNIQUE KEY `unique_learner_by_customer` (`external_id`,`customer_id`), - debug("Unique Key ", lines[line]) - split(arr[1],uniqueKeys,",") - for (i in uniqueKeys) { - if(match(uniqueKeys[i], /[ ]?`([^`]+)+`[ ]?/, arr)) { - columnsDetails[arr[1] ",unique"] = "1" - } - } - } else if (match(lines[line], /`([^`]+)` (([A-Za-z]+)((\([^)]+\)|)[ ]?(unsigned|)))[ ]?(NOT NULL|NULL|)[ ]?(AUTO_INCREMENT|)/, arr)) { - # `id` int(11) NOT NULL AUTO_INCREMENT - # `test` int(11) unsigned NULL - # `instance_name` varchar(128) NOT NULL - debug("Column ", lines[line]) - columnName = arr[1] - columns[columnIdx++] = columnName - columnType=arr[2] - gsub(/\(|\)/, "", columnType) - columnsDetails[columnName ",type"] = columnType # eg: int(11) unsigned => int11 unsigned - columnsDetails[columnName ",null"] = arr[7]=="NOT NULL" ? "0" : "1" # eg: NOT NULL - columnsDetails[columnName ",autoIncrement"] = arr[8] # eg: AUTO_INCREMENT - } - } + # else too much args + else - if (DEBUG == 1) { - debug("Table ", tableName) - for (i in columns) debug("column ", i, " ", columns[i]) - for (i in columnsDetails) debug("match ", i, " ", columnsDetails[i]) - } - asort(columns, columns, "column_sort") - if (DEBUG == 1) { - debug("***************************************************************") - debug("Columns order after sort") - for (i in columns) debug("column ", i, " ", columns[i], column_weight(columns[i])) - } - printf("Table(%s) {\n", tableName ) - for(i in columns) { - columnName=columns[i] - # column($name, $type, $null="", $pk="", $fk="", $unique="") - printf( \ - " column(\"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\")\n", \ - columnName, \ - trim(columnsDetails[columnName ",type"]), \ - (columnsDetails[columnName ",null"] == "1") ? "NULL" : "NOT NULL", \ - (columnsDetails[columnName ",pk"] == "1") ? "PK" : "", \ - (columnsDetails[columnName ",fkTable"] == "1") ? "FK" : "", \ - (columnsDetails[columnName ",unique"] == "1") ? "UNIQUE" : "" \ - ) - } - print("}") - - for(i in columns) { - columnName=columns[i] - if (columnsDetails[columnName ",fk"] == "1") { - #LearnerSkill "0..*" --> "1" Learner : "learner_id" - printf("%s \"0..*\" --> \"1\" %s : \"%s\"\n", tableName, columnsDetails[columnName ",fkTable"], columnsDetails[columnName ",fkColumn"] ) - } - } - print("") + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + # too much args and no unknownArgumentCallbacks configured + Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" + return 1 + fi + + fi + ;; + esac + shift || true + done || return $? + commandOptionParseFinished - delete columnsDetails - delete columns } -# ========================================================================= -function uml_parse_line(currentLine) -{ - if (length(currentLine) < 2 || match(currentLine, "^--") > 0) { - return - } +# @description display command options and arguments help for mysql2pumlCommand +mysql2pumlCommandHelp() { + Array::wrap2 ' ' 80 0 "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" \ + "Converts mysql dump sql schema to plantuml format." + echo + echo - if (match(currentLine,";")>0) { - sqlLine = sqlLine "\n" currentLine - debug(sqlLine) - if (match(sqlLine,"CREATE TABLE") > 0) { - uml_table(sqlLine) - } - sqlLine="" - } - else { - sqlLine = sqlLine "\n" currentLine - } -} + # ------------------------------------------ + # usage section + # ------------------------------------------ + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "mysql2puml [OPTIONS] [ARGUMENTS]" + echo + # ------------------------------------------ + # usage/options section + # ------------------------------------------ + optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--skin <>]" + ) + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ + "mysql2puml" "${optionsAltList[@]}" + echo -# ========================================================================= + # ------------------------------------------ + # usage/arguments section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" -BEGIN { - DEBUG=ENVIRON["DEBUG"] - skinFile=ARGV[1] - debug("Skin file", skinFile) - uml_start() -} + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}sqlFile${__HELP_NORMAL}{single}] + " + Array::wrap2 ' ' 76 4 " " "Sql filepath to parse (read from stdin if not provided)." + echo + # ------------------------------------------ + # options section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays this command help" + echo -{ - line=$0 - uml_parse_line(line) -} -END { - uml_end() - exit 0 - # fake call occurring after exit to remove warning about unused function - column_sort() -} -# ========================================================================= -EOF -)" -run() { - absSkinFile="$(Conf::getAbsoluteFile "mysql2pumlSkins" "${optionSkin}" "puml")" || - Log::fatal "the skin ${optionSkin} does not exist" + echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays configuration" + echo - if [[ -n "${inputSqlFile}" ]]; then - exec 3<"${inputSqlFile}" - elif [[ ! -t 0 ]]; then - exec 3<&0 - fi - awk --source "${awkScript}" "${absSkinFile}" - <&3 | Filters::trimEmptyLines + + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config bash-framework-config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--env-file env-file${__HELP_NORMAL} {list} (optional)" + Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--log-level log-level${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--log-file log-file${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log file" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--display-level display-level${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set display level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--theme theme${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." + echo + Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" + echo + + Array::wrap2 ' ' 76 6 " Default value: " "default" + echo + + echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Print version information and quit." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." + echo + + + echo + echo -e "${__HELP_TITLE_COLOR}OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--skin ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Header configuration of the plantuml file." + echo + + + Array::wrap2 ' ' 76 6 " Default value: " "default" + echo + # ------------------------------------------ + # longDescription section + # ------------------------------------------ + echo + echo + echo -e "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" + longDescriptionFunction + # ------------------------------------------ + # version section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" + echo "1.0" + # ------------------------------------------ + # author section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}AUTHOR: ${__RESET_COLOR}" + echo "[François Chastanet](https://github.com/fchastanet)" + # ------------------------------------------ + # sourceFile section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml" + # ------------------------------------------ + # license section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}LICENSE: ${__RESET_COLOR}" + echo "MIT License" + # ------------------------------------------ + # copyright section + # ------------------------------------------ + Array::wrap2 ' ' 76 0 "$(copyrightCallback)" } -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run + +beforeParseCallback + +mysql2pumlCommandParse "$@" +MAIN_FUNCTION_NAME="main" +main() { + +Linux::requireTarCommand +Compiler::Embed::extractFileFromBase64 \ + "${PERSISTENT_TMPDIR:-/tmp}/abbc557ccf07f1917f739b75af2fba89/mysql2pumlScript" \ + "IyEvYmluL2F3ayAtZgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIwojIG15c3FsMnB1bWwgLSBTUUwgRERMIHRvIG1hcmtkb3duIGNvbnZlcnRlcgojCiMgSW52b2NhdGlvbi9FeGVjdXRpb246CiMgICBhd2sgLWYgbXlzcWwycHVtbC5hd2sgc2tpbkZpbGUgPCBpbnB1dEZpbGUgPiBvdXRwdXRGaWxlCiMgICBERUJVRz0xIGF3ayAtZiBteXNxbDJwdW1sLmF3ayBza2luRmlsZSA8IGlucHV0RmlsZSA+IG91dHB1dEZpbGUKIwojIFN1cHBvcnRzCiMgICBQbGFudFVNTCBmb3JtYXQKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CgpmdW5jdGlvbiBkZWJ1ZyhmaXJzdCwgc2Vjb25kLCB0aGlyZCwgZm91cnRoLCBmaWZ0aCwgc2l4dGgsIHNldmVudGgsIGVpZ2h0aCwgbmludGgsIHRlbnRoLCBlbGV2ZW50aCwgdHdlbGZ0aCkgewogICAgaWYgKERFQlVHID09IDEpIHByaW50KCJERUJVRyAtICIsIGZpcnN0LCBzZWNvbmQsIHRoaXJkLCBmb3VydGgsIGZpZnRoLCBzaXh0aCwgc2V2ZW50aCwgZWlnaHRoLCBuaW50aCwgdGVudGgsIGVsZXZlbnRoLCB0d2VsZnRoKSA+ICIvZGV2L3N0ZGVyciIKfQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CgpmdW5jdGlvbiB1bWxfc3RhcnQoKQp7CiAgICBpZiAoIXNraW5GaWxlKSB7CiAgICAgICAgcHJpbnQoInNraW5GaWxlIHBhcmFtZXRlciBtaXNzaW5nIikgPiAiL2Rldi9zdGRlcnIiCiAgICAgICAgZXhpdCAxCiAgICB9CiAgICB3aGlsZSAoKGdldGxpbmUgdG1wIDwgc2tpbkZpbGUpID4gMCkgewogICAgICAgIGlmIChtYXRjaCh0bXAsIC9AZW5kdW1sLykpIHsKICAgICAgICAgICAgYnJlYWsKICAgICAgICB9CiAgICAgICAgcHJpbnQodG1wKQogICAgfQogICAgcHJpbnQgIicgZW50aXRpZXMiCn0KCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKZnVuY3Rpb24gdW1sX2VuZCgpCnsKICAgIHByaW50ICJAZW5kdW1sIgp9CgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KCmZ1bmN0aW9uIGx0cmltKHMpIHsgc3ViKC9eWyBcdFxyXG5dKy8sICIiLCBzKTsgcmV0dXJuIHMgfQpmdW5jdGlvbiBydHJpbShzKSB7IHN1YigvWyBcdFxyXG5dKyQvLCAiIiwgcyk7IHJldHVybiBzIH0KZnVuY3Rpb24gdHJpbShzKSB7IHJldHVybiBydHJpbShsdHJpbShzKSk7IH0KCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKZnVuY3Rpb24gY29sdW1uX3dlaWdodChjb2x1bW4pCnsKICAgIHJldHVybiAoY29sdW1uc0RldGFpbHNbY29sdW1uICIscGsiXSA9PSAiMSIgPyA4IDogMCkgXAogICAgICAgICsgKGNvbHVtbnNEZXRhaWxzW2NvbHVtbiAiLGZrIl0gPT0gIjEiID8gNCA6IDApIFwKICAgICAgICArIChjb2x1bW5zRGV0YWlsc1tjb2x1bW4gIix1bmlxdWUiXSA9PSAiMSIgPyAyIDogMCkgXAogICAgICAgICsgKGNvbHVtbnNEZXRhaWxzW2NvbHVtbiAiLG51bGwiXSA9PSAiMCIgPyAxIDogMCkKfQoKZnVuY3Rpb24gY29sdW1uX3NvcnQoaTEsIGNvbHVtbjEsIGkyLCBjb2x1bW4yKQp7CiAgICB3ZWlnaHQxID0gY29sdW1uX3dlaWdodChjb2x1bW4xKQogICAgd2VpZ2h0MiA9IGNvbHVtbl93ZWlnaHQoY29sdW1uMikKICAgIGlmICh3ZWlnaHQxID09IHdlaWdodDIpIHsKICAgICAgICBsID0gdG9sb3dlcihjb2x1bW4xKQogICAgICAgIHIgPSB0b2xvd2VyKGNvbHVtbjIpCgogICAgICAgIGlmIChsIDwgcikgewogICAgICAgICAgICByZXR1cm4gLTEKICAgICAgICB9IGVsc2UgaWYgKGwgPT0gcikgewogICAgICAgICAgICByZXR1cm4gMAogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIHJldHVybiAxCiAgICAgICAgfQogICAgfSBlbHNlIHsKICAgICAgICByZXR1cm4gd2VpZ2h0MiAtIHdlaWdodDEKICAgIH0KfQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CgpmdW5jdGlvbiB1bWxfdGFibGUoY3JlYXRlVGFibGUpCiMgRERMIHRvIHBsYW50dW1sCiMgQ1JFQVRFIFRBQkxFIGBjb3JlX2N1c3RvbWVyYCAoYGlkYCBpbnQoMTEpIE5PVCBOVUxMIEFVVE9fSU5DUkVNRU5ULCBgaW5zdGFuY2VfbmFtZWAgdmFyY2hhcigxMjgpIE5PVCBOVUxMLCBQUklNQVJZIEtFWSAoYGlkYCksIFVOSVFVRSBLRVkgYGluc3RhbmNlX25hbWVgIChgaW5zdGFuY2VfbmFtZWApICkgRU5HSU5FPUlubm9EQiBBVVRPX0lOQ1JFTUVOVD01IERFRkFVTFQgQ0hBUlNFVD11dGY4OwojIENSRUFURSBUQUJMRSBgY29yZV9sZWFybmVyX3NraWxsYCAoYGlkYCBpbnQoMTEpIE5PVCBOVUxMIEFVVE9fSU5DUkVNRU5ULCBgY3VzdG9tZXJfaWRgIGludCgxMCkgdW5zaWduZWQgTk9UIE5VTEwsIFBSSU1BUlkgS0VZIChgaWRgKSwgS0VZIGBjdXN0b21lcl9pZF82ODRmOTA0Zl9ma19jb3JlX2xlYXJuZXJfaWRgIChgY3VzdG9tZXJfaWRgKSwgQ09OU1RSQUlOVCBgY3VzdG9tZXJfaWRfNjg0ZjkwNGZfZmtfY29yZV9jdXN0b21lcl9pZGAgRk9SRUlHTiBLRVkgKGBjdXN0b21lcl9pZGApIFJFRkVSRU5DRVMgYGNvcmVfY3VzdG9tZXJgIChgaWRgKSkgRU5HSU5FPUlubm9EQiBBVVRPX0lOQ1JFTUVOVD00MTUgREVGQVVMVCBDSEFSU0VUPXV0Zjg7CiN0YWJsZSggdXNlciApIHsKIyAgcHJpbWFyeV9rZXkoIGlkICk6IFVVSUQKIyAgY29sdW1uKCBpc0FjdGl2ZSApOiBCT09MRUFOCiN9CiNMZWFybmVyU2tpbGwgIjAuLioiIC0tPiAiMSIgTGVhcm5lciA6ICJsZWFybmVyX2lkIgp7CiAgICBkZWJ1ZygidW1sX3RhYmxlIiwgY3JlYXRlVGFibGUpCiAgICBzcGxpdChjcmVhdGVUYWJsZSxsaW5lcywiXG4iKQogICAgY29sdW1uSWR4PTAKICAgIGZvciAobGluZSBpbiBsaW5lcykgewogICAgICAgIGRlYnVnKGxpbmVzW2xpbmVdKQogICAgICAgIGlmIChtYXRjaChsaW5lc1tsaW5lXSwgL0NSRUFURSBUQUJMRSBgKFteYF0rKWAgXCgvLCBhcnIpKSB7CiAgICAgICAgICAgIGRlYnVnKCJUQUJMRSAiLCBsaW5lc1tsaW5lXSkKICAgICAgICAgICAgdGFibGVOYW1lID0gYXJyWzFdCiAgICAgICAgfSBlbHNlIGlmIChtYXRjaChsaW5lc1tsaW5lXSwgL1BSSU1BUlkgS0VZIFwoKFteKV0rKVwpLywgYXJyKSkgewogICAgICAgICAgICAjIFBSSU1BUlkgS0VZIChgaWRgKSwKICAgICAgICAgICAgZGVidWcoIlBLICIsIGxpbmVzW2xpbmVdKQogICAgICAgICAgICBzcGxpdChhcnJbMV0scHJpbWFyeUtleXMsIiwiKQogICAgICAgICAgICBmb3IgKGkgaW4gcHJpbWFyeUtleXMpIHsKICAgICAgICAgICAgICAgIGlmKG1hdGNoKHByaW1hcnlLZXlzW2ldLCAvWyBdP2AoW15gXSspK2BbIF0/LywgYXJyKSkgewogICAgICAgICAgICAgICAgICAgIGNvbHVtbnNEZXRhaWxzW2FyclsxXSAiLHBrIl0gPSAiMSIKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0gZWxzZSBpZiAobWF0Y2gobGluZXNbbGluZV0sIC9DT05TVFJBSU5UIGBbXmBdK2AgRk9SRUlHTiBLRVkgXChgKFteYF0rKWBcKSBSRUZFUkVOQ0VTIGAoW15gXSspYCBcKGAoW15gXSspYFwpLywgYXJyKSkgewogICAgICAgICAgICAjIENPTlNUUkFJTlQgYGNvcmVfbGVhcm5lcl9jdXN0b21lcl9pZF82ZDM1NmRhOF9ma19jb3JlX2N1c3RvbWVyX2lkYCBGT1JFSUdOIEtFWSAoYGN1c3RvbWVyX2lkYCkgUkVGRVJFTkNFUyBgY29yZV9jdXN0b21lcmAgKGBpZGApCiAgICAgICAgICAgIGRlYnVnKCJDb25zdHJhaW50ICIsIGxpbmVzW2xpbmVdKQogICAgICAgICAgICBjb2x1bW5OYW1lPWFyclsxXQogICAgICAgICAgICBjb2x1bW5zRGV0YWlsc1tjb2x1bW5OYW1lICIsZmsiXT0iMSIKICAgICAgICAgICAgY29sdW1uc0RldGFpbHNbY29sdW1uTmFtZSAiLGZrVGFibGUiXT1hcnJbMl0KICAgICAgICAgICAgY29sdW1uc0RldGFpbHNbY29sdW1uTmFtZSAiLGZrQ29sdW1uIl09YXJyWzNdCiAgICAgICAgfSBlbHNlIGlmIChtYXRjaChsaW5lc1tsaW5lXSwgL1VOSVFVRSBLRVkgYFteYF0rYCBcKChbXildKylcKS8sIGFycikpIHsKICAgICAgICAgICAgIyBVTklRVUUgS0VZIGB1bmlxdWVfbGVhcm5lcl9ieV9jdXN0b21lcmAgKGBleHRlcm5hbF9pZGAsYGN1c3RvbWVyX2lkYCksCiAgICAgICAgICAgIGRlYnVnKCJVbmlxdWUgS2V5ICIsIGxpbmVzW2xpbmVdKQogICAgICAgICAgICBzcGxpdChhcnJbMV0sdW5pcXVlS2V5cywiLCIpCiAgICAgICAgICAgIGZvciAoaSBpbiB1bmlxdWVLZXlzKSB7CiAgICAgICAgICAgICAgICBpZihtYXRjaCh1bmlxdWVLZXlzW2ldLCAvWyBdP2AoW15gXSspK2BbIF0/LywgYXJyKSkgewogICAgICAgICAgICAgICAgICAgIGNvbHVtbnNEZXRhaWxzW2FyclsxXSAiLHVuaXF1ZSJdID0gIjEiCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9IGVsc2UgaWYgKG1hdGNoKGxpbmVzW2xpbmVdLCAvYChbXmBdKylgICgoW0EtWmEtel0rKSgoXChbXildK1wpfClbIF0/KHVuc2lnbmVkfCkpKVsgXT8oTk9UIE5VTEx8TlVMTHwpWyBdPyhBVVRPX0lOQ1JFTUVOVHwpLywgYXJyKSkgewogICAgICAgICAgICAjIGBpZGAgaW50KDExKSBOT1QgTlVMTCBBVVRPX0lOQ1JFTUVOVAogICAgICAgICAgICAjIGB0ZXN0YCBpbnQoMTEpIHVuc2lnbmVkIE5VTEwKICAgICAgICAgICAgIyBgaW5zdGFuY2VfbmFtZWAgdmFyY2hhcigxMjgpIE5PVCBOVUxMCiAgICAgICAgICAgIGRlYnVnKCJDb2x1bW4gIiwgbGluZXNbbGluZV0pCiAgICAgICAgICAgIGNvbHVtbk5hbWUgPSBhcnJbMV0KICAgICAgICAgICAgY29sdW1uc1tjb2x1bW5JZHgrK10gPSBjb2x1bW5OYW1lCiAgICAgICAgICAgIGNvbHVtblR5cGU9YXJyWzJdCiAgICAgICAgICAgIGdzdWIoL1wofFwpLywgIiIsIGNvbHVtblR5cGUpCiAgICAgICAgICAgIGNvbHVtbnNEZXRhaWxzW2NvbHVtbk5hbWUgIix0eXBlIl0gPSBjb2x1bW5UeXBlICAjIGVnOiBpbnQoMTEpIHVuc2lnbmVkID0+IGludDExIHVuc2lnbmVkCiAgICAgICAgICAgIGNvbHVtbnNEZXRhaWxzW2NvbHVtbk5hbWUgIixudWxsIl0gPSBhcnJbN109PSJOT1QgTlVMTCIgPyAiMCIgOiAiMSIgIyBlZzogTk9UIE5VTEwKICAgICAgICAgICAgY29sdW1uc0RldGFpbHNbY29sdW1uTmFtZSAiLGF1dG9JbmNyZW1lbnQiXSA9IGFycls4XSAjIGVnOiBBVVRPX0lOQ1JFTUVOVAogICAgICAgIH0KICAgIH0KCiAgICBpZiAoREVCVUcgPT0gMSkgewogICAgICAgIGRlYnVnKCJUYWJsZSAiLCB0YWJsZU5hbWUpCiAgICAgICAgZm9yIChpIGluIGNvbHVtbnMpIGRlYnVnKCJjb2x1bW4gIiwgaSwgIiAiLCBjb2x1bW5zW2ldKQogICAgICAgIGZvciAoaSBpbiBjb2x1bW5zRGV0YWlscykgZGVidWcoIm1hdGNoICIsIGksICIgIiwgY29sdW1uc0RldGFpbHNbaV0pCiAgICB9CiAgICBhc29ydChjb2x1bW5zLCBjb2x1bW5zLCAiY29sdW1uX3NvcnQiKQogICAgaWYgKERFQlVHID09IDEpIHsKICAgICAgICBkZWJ1ZygiKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqIikKICAgICAgICBkZWJ1ZygiQ29sdW1ucyBvcmRlciBhZnRlciBzb3J0IikKICAgICAgICBmb3IgKGkgaW4gY29sdW1ucykgZGVidWcoImNvbHVtbiAiLCBpLCAiICIsIGNvbHVtbnNbaV0sIGNvbHVtbl93ZWlnaHQoY29sdW1uc1tpXSkpCiAgICB9CiAgICBwcmludGYoIlRhYmxlKCVzKSB7XG4iLCB0YWJsZU5hbWUgKQogICAgZm9yKGkgaW4gY29sdW1ucykgewogICAgICAgIGNvbHVtbk5hbWU9Y29sdW1uc1tpXQogICAgICAgICMgY29sdW1uKCRuYW1lLCAkdHlwZSwgJG51bGw9IiIsICRwaz0iIiwgJGZrPSIiLCAkdW5pcXVlPSIiKQogICAgICAgIHByaW50ZiggXAogICAgICAgICAgICAiICBjb2x1bW4oXCIlc1wiLCBcIiVzXCIsIFwiJXNcIiwgXCIlc1wiLCBcIiVzXCIsIFwiJXNcIilcbiIsIFwKICAgICAgICAgICAgY29sdW1uTmFtZSwgXAogICAgICAgICAgICB0cmltKGNvbHVtbnNEZXRhaWxzW2NvbHVtbk5hbWUgIix0eXBlIl0pLCBcCiAgICAgICAgICAgIChjb2x1bW5zRGV0YWlsc1tjb2x1bW5OYW1lICIsbnVsbCJdID09ICIxIikgPyAiTlVMTCIgOiAiTk9UIE5VTEwiLCBcCiAgICAgICAgICAgIChjb2x1bW5zRGV0YWlsc1tjb2x1bW5OYW1lICIscGsiXSA9PSAiMSIpID8gIlBLIiA6ICIiLCBcCiAgICAgICAgICAgIChjb2x1bW5zRGV0YWlsc1tjb2x1bW5OYW1lICIsZmtUYWJsZSJdID09ICIxIikgPyAiRksiIDogIiIsIFwKICAgICAgICAgICAgKGNvbHVtbnNEZXRhaWxzW2NvbHVtbk5hbWUgIix1bmlxdWUiXSA9PSAiMSIpID8gIlVOSVFVRSIgOiAiIiBcCiAgICAgICAgKQogICAgfQogICAgcHJpbnQoIn0iKQoKICAgIGZvcihpIGluIGNvbHVtbnMpIHsKICAgICAgICBjb2x1bW5OYW1lPWNvbHVtbnNbaV0KICAgICAgICBpZiAoY29sdW1uc0RldGFpbHNbY29sdW1uTmFtZSAiLGZrIl0gPT0gIjEiKSB7CiAgICAgICAgICAgICNMZWFybmVyU2tpbGwgIjAuLioiIC0tPiAiMSIgTGVhcm5lciA6ICJsZWFybmVyX2lkIgogICAgICAgICAgICBwcmludGYoIiVzIFwiMC4uKlwiIC0tPiBcIjFcIiAlcyA6IFwiJXNcIlxuIiwgdGFibGVOYW1lLCBjb2x1bW5zRGV0YWlsc1tjb2x1bW5OYW1lICIsZmtUYWJsZSJdLCBjb2x1bW5zRGV0YWlsc1tjb2x1bW5OYW1lICIsZmtDb2x1bW4iXSApCiAgICAgICAgfQogICAgfQogICAgcHJpbnQoIiIpCgogICAgZGVsZXRlIGNvbHVtbnNEZXRhaWxzCiAgICBkZWxldGUgY29sdW1ucwp9CgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KZnVuY3Rpb24gdW1sX3BhcnNlX2xpbmUoY3VycmVudExpbmUpCnsKICAgIGlmIChsZW5ndGgoY3VycmVudExpbmUpIDwgMiB8fCBtYXRjaChjdXJyZW50TGluZSwgIl4tLSIpID4gMCkgewogICAgICAgIHJldHVybgogICAgfQoKICAgIGlmIChtYXRjaChjdXJyZW50TGluZSwiOyIpPjApIHsKICAgICAgICBzcWxMaW5lID0gc3FsTGluZSAiXG4iIGN1cnJlbnRMaW5lCiAgICAgICAgZGVidWcoc3FsTGluZSkKICAgICAgICBpZiAobWF0Y2goc3FsTGluZSwiQ1JFQVRFIFRBQkxFIikgPiAwKSB7CiAgICAgICAgICAgIHVtbF90YWJsZShzcWxMaW5lKQogICAgICAgIH0KICAgICAgICBzcWxMaW5lPSIiCiAgICB9CiAgICBlbHNlIHsKICAgICAgICBzcWxMaW5lID0gc3FsTGluZSAiXG4iIGN1cnJlbnRMaW5lCiAgICB9Cn0KCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKQkVHSU4gewogICAgREVCVUc9RU5WSVJPTlsiREVCVUciXQogICAgc2tpbkZpbGU9QVJHVlsxXQogICAgZGVidWcoIlNraW4gZmlsZSIsIHNraW5GaWxlKQogICAgdW1sX3N0YXJ0KCkKfQoKewogICAgbGluZT0kMAogICAgdW1sX3BhcnNlX2xpbmUobGluZSkKfQoKRU5EIHsKICAgIHVtbF9lbmQoKQogICAgZXhpdCAwCiAgICAjIGZha2UgY2FsbCBvY2N1cnJpbmcgYWZ0ZXIgZXhpdCB0byByZW1vdmUgd2FybmluZyBhYm91dCB1bnVzZWQgZnVuY3Rpb24KICAgIGNvbHVtbl9zb3J0KCkKfQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0K" \ + "755" + +declare -gx embed_file_mysql2pumlScript="${PERSISTENT_TMPDIR:-/tmp}/abbc557ccf07f1917f739b75af2fba89/mysql2pumlScript" + + +Linux::requireRealpathCommand + +# shellcheck disable=SC2154 +absSkinFile="$(Conf::getAbsoluteFile "mysql2pumlSkins" "${optionSkin}" "puml")" || + Log::fatal "the skin ${optionSkin} does not exist" + +if [[ -n "${inputSqlFile}" ]]; then + exec 3<"${inputSqlFile}" +elif [[ ! -t 0 ]]; then + exec 3<&0 fi +# shellcheck disable=SC2154 +awk -f "${embed_file_mysql2pumlScript}" "${absSkinFile}" - <&3 | Filters::trimEmptyLines + } -facade_main_mysql2pumlsh "$@" +# if file is sourced avoid calling main function +# shellcheck disable=SC2178 +BASH_SOURCE=".$0" # cannot be changed in bash +# shellcheck disable=SC2128 +if test ".$0" == ".${BASH_SOURCE}"; then + if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then + main "$@" &>/dev/null + else + main "$@" + fi +fi diff --git a/src/_binaries/Converters/mysql2puml.options.tpl b/src/_binaries/Converters/mysql2puml.options.tpl deleted file mode 100644 index 36da7671..00000000 --- a/src/_binaries/Converters/mysql2puml.options.tpl +++ /dev/null @@ -1,84 +0,0 @@ -% -declare versionNumber="1.0" -declare commandFunctionName="mysql2pumlCommand" -declare optionSkinDefault="default" -declare help="convert mysql dump sql schema to plantuml format" -# shellcheck disable=SC2016 -# kics-scan disable=487f4be7-3fd9-4506-a07a-eae252180c08 -declare longDescription=''' -${__HELP_TITLE}EXAMPLE 1:${__HELP_NORMAL} -${__HELP_EXAMPLE}mysql2puml dump.dql${__HELP_NORMAL} - -${__HELP_TITLE}EXAMPLE 2:${__HELP_NORMAL} -${__HELP_EXAMPLE}mysqldump --skip-add-drop-table \ - --skip-add-locks \ - --skip-disable-keys \ - --skip-set-charset \ - --user=root \ - --password=root \ - --no-data skills | mysql2puml -${__HELP_NORMAL} -${__HELP_TITLE}LIST OF AVAILABLE SKINS:${__HELP_NORMAL} -@@@SKINS_LIST@@@''' -% - -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.base.tpl)" - -% -# shellcheck source=/dev/null -source <( - Options::generateOption \ - --variable-type String \ - --help "header configuration of the plant uml file" \ - --default-value "${optionSkinDefault}" \ - --alt "--skin" \ - --callback "optionSkinCallback" \ - --variable-name "optionSkin" \ - --function-name optionSkinFunction - inputSqlFileCallback() { :; } - Options::generateArg \ - --variable-name "inputSqlFile" \ - --min 0 \ - --max 1 \ - --name "inputSqlFile" \ - --callback inputSqlFileCallback \ - --help "sql filepath to parse (read from stdin if not provided)" \ - --function-name argumentInputSqlFileFunction -) -options+=( - optionSkinFunction - argumentInputSqlFileFunction -) -Options::generateCommand "${options[@]}" -% - -optionHelpCallback() { - local skinListHelpFile - skinListHelpFile="$(Framework::createTempFile "shellcheckHelp")" - Conf::getMergedList "mysql2pumlSkins" ".puml" " - " >"${skinListHelpFile}" - - <% ${commandFunctionName} %> help | - sed -E \ - -e "/@@@SKINS_LIST@@@/r ${skinListHelpFile}" \ - -e "/@@@SKINS_LIST@@@/d" - exit 0 -} - -optionSkinCallback() { - declare -a skinList - readarray -t skinList < <(Conf::getMergedList "mysql2pumlSkins" ".puml" "") - if ! Array::contains "$2" "${skinList[@]}"; then - Log::displayError "${SCRIPT_NAME} - invalid skin '$2' provided" - return 1 - fi -} - -inputSqlFileCallback() { - # shellcheck disable=SC2154 - if [[ ! -f "${inputSqlFile}" ]]; then - Log::displayError "${SCRIPT_NAME} - File '${inputSqlFile}' does not exists" - return 1 - fi -} - -<% ${commandFunctionName} %> parse "${BASH_FRAMEWORK_ARGV[@]}" diff --git a/src/_binaries/Converters/mysql2puml.sh b/src/_binaries/Converters/mysql2puml.sh deleted file mode 100755 index eceb16bf..00000000 --- a/src/_binaries/Converters/mysql2puml.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env bash -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/mysql2puml -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE -# shellcheck disable=SC2034 - -declare copyrightBeginYear="2020" -declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" -declare optionSkin="default" - -.INCLUDE "$(dynamicTemplateDir _binaries/Converters/mysql2puml.options.tpl)" -declare awkScript -awkScript="$( - cat <<'EOF' -.INCLUDE "$(dynamicSrcFile _binaries/Converters/mysql2puml.awk)" -EOF -)" - -run() { - absSkinFile="$(Conf::getAbsoluteFile "mysql2pumlSkins" "${optionSkin}" "puml")" || - Log::fatal "the skin ${optionSkin} does not exist" - - if [[ -n "${inputSqlFile}" ]]; then - exec 3<"${inputSqlFile}" - elif [[ ! -t 0 ]]; then - exec 3<&0 - fi - - awk --source "${awkScript}" "${absSkinFile}" - <&3 | Filters::trimEmptyLines -} - -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run -fi diff --git a/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml b/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml new file mode 100644 index 00000000..476014cb --- /dev/null +++ b/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml @@ -0,0 +1,44 @@ +extends: + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" + +vars: + SRC_FILE_PATH: src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml + +compilerConfig: + targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/mysql2puml" + relativeRootDirBasedOnTargetDir: .. +binData: + commands: + default: + functionName: mysql2pumlCommand + version: "1.0" + commandName: mysql2puml + beforeParseCallbacks: + - beforeParseCallback + definitionFiles: + 20: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Converters/mysql2puml/mysql2puml-options.sh + mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Converters/mysql2puml/mysql2puml-main.sh + help: Converts mysql dump sql schema to plantuml format. + longDescription: longDescriptionFunction + options: + - alts: + - "--skin" + group: OptionsGroup + type: String + defaultValue: default + help: Header configuration of the plantuml file. + helpValueName: + callbacks: + - optionSkinCallback + variableName: optionSkin + + args: + - type: String + min: 0 + max: 1 + help: Sql filepath to parse (read from stdin if not provided). + callbacks: + - optionSkinCallback + name: sqlFile + variableName: inputSqlFile diff --git a/src/_binaries/Converters/mysql2puml/mysql2puml-main.sh b/src/_binaries/Converters/mysql2puml/mysql2puml-main.sh new file mode 100755 index 00000000..911b711a --- /dev/null +++ b/src/_binaries/Converters/mysql2puml/mysql2puml-main.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# @embed "${BASH_TOOLS_ROOT_DIR}/src/_binaries/Converters/mysql2puml/mysql2puml.awk" AS mysql2pumlScript + +Linux::requireRealpathCommand + +# shellcheck disable=SC2154 +absSkinFile="$(Conf::getAbsoluteFile "mysql2pumlSkins" "${optionSkin}" "puml")" || + Log::fatal "the skin ${optionSkin} does not exist" + +if [[ -n "${inputSqlFile}" ]]; then + exec 3<"${inputSqlFile}" +elif [[ ! -t 0 ]]; then + exec 3<&0 +fi + +# shellcheck disable=SC2154 +awk -f "${embed_file_mysql2pumlScript}" "${absSkinFile}" - <&3 | Filters::trimEmptyLines diff --git a/src/_binaries/Converters/mysql2puml/mysql2puml-options.sh b/src/_binaries/Converters/mysql2puml/mysql2puml-options.sh new file mode 100755 index 00000000..1948c8ea --- /dev/null +++ b/src/_binaries/Converters/mysql2puml/mysql2puml-options.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +# shellcheck disable=SC2034 +declare copyrightBeginYear="2020" +# shellcheck disable=SC2034 +declare versionNumber="1.0" + +declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" + +optionHelpCallback() { + mysql2pumlCommandHelp + exit 0 +} + +longDescriptionFunction() { + echo -e "${__HELP_TITLE}EXAMPLE 1:${__HELP_NORMAL}" + echo -e "${__HELP_EXAMPLE}mysql2puml dump.dql${__HELP_NORMAL}" + echo + echo -e "${__HELP_TITLE}EXAMPLE 2:${__HELP_NORMAL}" + echo -e "${__HELP_EXAMPLE}mysqldump --skip-add-drop-table \\" + echo -e " --skip-add-locks \\" + echo -e " --skip-disable-keys \\" + echo -e " --skip-set-charset \\" + echo -e " --user=root \\" + echo -e " --password=root \\" + echo -e " --no-data skills | mysql2puml" + echo -e "${__HELP_NORMAL}" + echo -e "${__HELP_TITLE}LIST OF AVAILABLE SKINS:${__HELP_NORMAL}" + Conf::getMergedList "mysql2pumlSkins" ".puml" " - " +} + +optionSkinCallback() { + declare -a skinList + readarray -t skinList < <(Conf::getMergedList "mysql2pumlSkins" ".puml" "") + # shellcheck disable=SC2154 + if ! Array::contains "${optionSkin}" "${skinList[@]}"; then + Log::displayError "${SCRIPT_NAME} - invalid skin '${optionSkin}' provided" + exit 1 + fi +} + +inputSqlFileCallback() { + # shellcheck disable=SC2154 + if [[ ! -f "${inputSqlFile}" ]]; then + Log::displayError "${SCRIPT_NAME} - File '${inputSqlFile}' does not exists" + exit 1 + fi +} diff --git a/src/_binaries/Converters/mysql2puml.awk b/src/_binaries/Converters/mysql2puml/mysql2puml.awk similarity index 100% rename from src/_binaries/Converters/mysql2puml.awk rename to src/_binaries/Converters/mysql2puml/mysql2puml.awk diff --git a/src/_binaries/Converters/mysql2puml.bats b/src/_binaries/Converters/mysql2puml/mysql2puml.bats similarity index 100% rename from src/_binaries/Converters/mysql2puml.bats rename to src/_binaries/Converters/mysql2puml/mysql2puml.bats diff --git a/src/_binaries/Converters/testsData/.env b/src/_binaries/Converters/mysql2puml/testsData/.env similarity index 100% rename from src/_binaries/Converters/testsData/.env rename to src/_binaries/Converters/mysql2puml/testsData/.env diff --git a/src/_binaries/Converters/testsData/mysql2puml-model.png b/src/_binaries/Converters/mysql2puml/testsData/mysql2puml-model.png similarity index 100% rename from src/_binaries/Converters/testsData/mysql2puml-model.png rename to src/_binaries/Converters/mysql2puml/testsData/mysql2puml-model.png diff --git a/src/_binaries/Converters/testsData/mysql2puml.dump.sql b/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.dump.sql similarity index 100% rename from src/_binaries/Converters/testsData/mysql2puml.dump.sql rename to src/_binaries/Converters/mysql2puml/testsData/mysql2puml.dump.sql diff --git a/src/_binaries/Converters/testsData/mysql2puml.help.txt b/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt similarity index 100% rename from src/_binaries/Converters/testsData/mysql2puml.help.txt rename to src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt diff --git a/src/_binaries/Converters/testsData/mysql2puml.png b/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.png similarity index 100% rename from src/_binaries/Converters/testsData/mysql2puml.png rename to src/_binaries/Converters/mysql2puml/testsData/mysql2puml.png diff --git a/src/_binaries/Converters/testsData/mysql2puml.puml b/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.puml similarity index 100% rename from src/_binaries/Converters/testsData/mysql2puml.puml rename to src/_binaries/Converters/mysql2puml/testsData/mysql2puml.puml diff --git a/src/_binaries/Converters/testsData/mysql2puml.svg b/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.svg similarity index 100% rename from src/_binaries/Converters/testsData/mysql2puml.svg rename to src/_binaries/Converters/mysql2puml/testsData/mysql2puml.svg diff --git a/src/_binaries/Converters/testsData/mysql2pumlSkins/default.png b/src/_binaries/Converters/mysql2puml/testsData/mysql2pumlSkins/default.png similarity index 100% rename from src/_binaries/Converters/testsData/mysql2pumlSkins/default.png rename to src/_binaries/Converters/mysql2puml/testsData/mysql2pumlSkins/default.png diff --git a/src/_binaries/Converters/testsData/mysql2pumlSkins/default.puml b/src/_binaries/Converters/mysql2puml/testsData/mysql2pumlSkins/default.puml similarity index 100% rename from src/_binaries/Converters/testsData/mysql2pumlSkins/default.puml rename to src/_binaries/Converters/mysql2puml/testsData/mysql2pumlSkins/default.puml diff --git a/src/_binaries/Converters/testsData/mysql2pumlSkins/default.svg b/src/_binaries/Converters/mysql2puml/testsData/mysql2pumlSkins/default.svg similarity index 100% rename from src/_binaries/Converters/testsData/mysql2pumlSkins/default.svg rename to src/_binaries/Converters/mysql2puml/testsData/mysql2pumlSkins/default.svg From b038877ab78ce3e33de6a59523b6ce8dad174a2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Sat, 31 Aug 2024 13:17:14 +0200 Subject: [PATCH 05/24] compiled dbImport using go compiler --- .pre-commit-config-github.yaml | 2 +- .pre-commit-config.yaml | 2 +- bin/dbImport | 3885 +++++++++-------- bin/doc | 7 +- bin/install | 7 +- bin/installRequirements | 7 +- bin/mysql2puml | 33 +- conf/dsn/default.local.env | 2 +- conf/dsn/default.remote.env | 2 +- conf/dsn/localhost-root.env | 2 +- src/BashTools/Conf/requireLoad.sh | 31 +- .../checkRequirements.sh} | 12 +- .../mysql2puml/binary-mysql2puml.yaml | 2 +- .../mysql2puml/mysql2puml-options.sh | 9 + .../Converters/mysql2puml/mysql2puml.bats | 5 +- .../mysql2puml/testsData/mysql2puml.help.txt | 97 +- .../Database/dbImport/binary-dbImport.yaml | 48 + .../Database/dbImport/dbImport-main.sh | 192 + .../Database/dbImport/dbImport-options.sh | 91 + .../dbImport}/dbImport.bats | 8 +- .../Database/dbImport/testsData/.env | 52 + .../testsData/auto_default.local_fromDb_20.sh | 8 + .../testsData/auto_default.local_fromDb_70.sh | 8 + .../dbImport/testsData/dbImport.help.txt | 95 + .../testsData/dbImportProfile.help.txt | 89 + .../testsData/dbImportProfile.tableList1 | 3 + .../testsData/dbImportProfiles/all.sh | 4 + .../testsData/dbImportProfiles/default.sh | 15 + .../testsData/dbImportProfiles/none.sh | 6 + .../testsData/dbImportStream.help.txt | 92 + .../dbImport/testsData/dbImportTableDump.sql | 35 + .../dbImport/testsData/dsn/default.local.env | 13 + .../dbImport/testsData/dsn/default.remote.env | 12 + .../dbImport/testsData/dsn/localhost-root.env | 12 + .../Database/dbImport/testsData/dump.sql | 84 + .../dbImport/testsData/dumpMissingSchema.sql | 61 + .../dbImport/testsData/dumpSchema.sql | 64 + .../dbImport/testsData/empty-dump.sql | 1 + .../expectedDbImportProfileTableListQuery.sql | 1 + .../testsData/expectedDbImportTableDump.sql | 42 + .../expectedDbImportTableDumpRenamed.sql | 42 + .../Database/dbImport/testsData/gawk | 6 + src/_binaries/Database/dbImport/testsData/pv | 6 + .../dbImport/testsData/tableSizeQuery.sql | 1 + src/_binaries/DbImport/dbImport.sh | 13 +- .../commandDefinitions/optionsDefault.sh | 8 + .../optionsMysqlCollationName.sh | 1 + .../optionsMysqlCollationName.yaml | 18 + .../commandDefinitions/optionsMysqlSource.sh | 5 + .../optionsMysqlSource.yaml | 39 + .../commandDefinitions/optionsMysqlTarget.sh | 22 + .../optionsMysqlTarget.yaml | 27 + .../optionsProfile.sh} | 81 +- .../commandDefinitions/optionsProfile.yaml | 35 + .../commandDefinitions/optionsVersion.sh | 8 + .../commandDefinitions/optionsVersion.yaml | 6 + src/_binaries/options/options.dsn.tpl | 21 - .../options/options.mysql.collationName.tpl | 27 - .../options/options.mysql.target.tpl | 61 - src/_includes/_initFrameworkVariables.tpl | 20 - src/batsHeaders.sh | 19 +- 61 files changed, 3524 insertions(+), 2083 deletions(-) rename src/{_includes/dbTools.requirements.tpl => Db/checkRequirements.sh} (83%) mode change 100644 => 100755 create mode 100644 src/_binaries/Database/dbImport/binary-dbImport.yaml create mode 100755 src/_binaries/Database/dbImport/dbImport-main.sh create mode 100755 src/_binaries/Database/dbImport/dbImport-options.sh rename src/_binaries/{DbImport => Database/dbImport}/dbImport.bats (96%) create mode 100755 src/_binaries/Database/dbImport/testsData/.env create mode 100755 src/_binaries/Database/dbImport/testsData/auto_default.local_fromDb_20.sh create mode 100755 src/_binaries/Database/dbImport/testsData/auto_default.local_fromDb_70.sh create mode 100644 src/_binaries/Database/dbImport/testsData/dbImport.help.txt create mode 100644 src/_binaries/Database/dbImport/testsData/dbImportProfile.help.txt create mode 100644 src/_binaries/Database/dbImport/testsData/dbImportProfile.tableList1 create mode 100755 src/_binaries/Database/dbImport/testsData/dbImportProfiles/all.sh create mode 100755 src/_binaries/Database/dbImport/testsData/dbImportProfiles/default.sh create mode 100755 src/_binaries/Database/dbImport/testsData/dbImportProfiles/none.sh create mode 100644 src/_binaries/Database/dbImport/testsData/dbImportStream.help.txt create mode 100644 src/_binaries/Database/dbImport/testsData/dbImportTableDump.sql create mode 100644 src/_binaries/Database/dbImport/testsData/dsn/default.local.env create mode 100644 src/_binaries/Database/dbImport/testsData/dsn/default.remote.env create mode 100644 src/_binaries/Database/dbImport/testsData/dsn/localhost-root.env create mode 100644 src/_binaries/Database/dbImport/testsData/dump.sql create mode 100644 src/_binaries/Database/dbImport/testsData/dumpMissingSchema.sql create mode 100644 src/_binaries/Database/dbImport/testsData/dumpSchema.sql create mode 100644 src/_binaries/Database/dbImport/testsData/empty-dump.sql create mode 100644 src/_binaries/Database/dbImport/testsData/expectedDbImportProfileTableListQuery.sql create mode 100644 src/_binaries/Database/dbImport/testsData/expectedDbImportTableDump.sql create mode 100644 src/_binaries/Database/dbImport/testsData/expectedDbImportTableDumpRenamed.sql create mode 100755 src/_binaries/Database/dbImport/testsData/gawk create mode 100755 src/_binaries/Database/dbImport/testsData/pv create mode 100644 src/_binaries/Database/dbImport/testsData/tableSizeQuery.sql create mode 100755 src/_binaries/commandDefinitions/optionsDefault.sh create mode 100755 src/_binaries/commandDefinitions/optionsMysqlCollationName.sh create mode 100644 src/_binaries/commandDefinitions/optionsMysqlCollationName.yaml create mode 100755 src/_binaries/commandDefinitions/optionsMysqlSource.sh create mode 100644 src/_binaries/commandDefinitions/optionsMysqlSource.yaml create mode 100755 src/_binaries/commandDefinitions/optionsMysqlTarget.sh create mode 100644 src/_binaries/commandDefinitions/optionsMysqlTarget.yaml rename src/_binaries/{options/options.profile.tpl => commandDefinitions/optionsProfile.sh} (52%) mode change 100644 => 100755 create mode 100644 src/_binaries/commandDefinitions/optionsProfile.yaml create mode 100755 src/_binaries/commandDefinitions/optionsVersion.sh create mode 100644 src/_binaries/commandDefinitions/optionsVersion.yaml delete mode 100644 src/_binaries/options/options.dsn.tpl delete mode 100644 src/_binaries/options/options.mysql.collationName.tpl delete mode 100644 src/_binaries/options/options.mysql.target.tpl delete mode 100644 src/_includes/_initFrameworkVariables.tpl diff --git a/.pre-commit-config-github.yaml b/.pre-commit-config-github.yaml index 593450e8..200b46a0 100644 --- a/.pre-commit-config-github.yaml +++ b/.pre-commit-config-github.yaml @@ -205,6 +205,6 @@ repos: stages: [manual] # GITHUB - repo: https://github.com/fchastanet/bash-compiler - rev: 0.2.9 + rev: 0.3.0 hooks: - id: buildBashBinaries diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2cdda081..f8a4606e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -200,6 +200,6 @@ repos: stages: [] # GITHUB - repo: https://github.com/fchastanet/bash-compiler - rev: 0.2.9 + rev: 0.3.0 hooks: - id: buildBashBinaries diff --git a/bin/dbImport b/bin/dbImport index 7803aa0d..03f36544 100755 --- a/bin/dbImport +++ b/bin/dbImport @@ -1,13 +1,12 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FACADE FROM https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/DbImport/dbImport.sh +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImport/binary-dbImport.yaml # DO NOT EDIT IT # @generated ############################################################################### # shellcheck disable=SC2288,SC2034 -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/dbImport -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE + + # ensure that no user aliases could interfere with # commands used in this script @@ -62,13 +61,6 @@ interruptManagement() { kill -s INT "$$" } trap interruptManagement INT -SCRIPT_NAME=${0##*/} -REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" -if [[ -n "${EMBED_CURRENT_DIR}" ]]; then - CURRENT_DIR="${EMBED_CURRENT_DIR}" -else - CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" -fi ################################################ # Temp dir management @@ -102,6 +94,20 @@ cleanOnExit() { } trap cleanOnExit EXIT HUP QUIT ABRT TERM + +SCRIPT_NAME=${0##*/} +REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" +if [[ -n "${EMBED_CURRENT_DIR}" ]]; then + CURRENT_DIR="${EMBED_CURRENT_DIR}" +else + CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" +fi +FRAMEWORK_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" +FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" +FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + # @description Log namespace provides 2 kind of functions # - Log::display* allows to display given message with # given display level @@ -132,294 +138,313 @@ export __VERBOSE_LEVEL_DEBUG=2 # @description verbose level info export __VERBOSE_LEVEL_TRACE=3 -# @description Display message using info color (bg light blue/fg white) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayInfo() { - local type="${2:-INFO}" - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logInfo "$1" "${type}" -} - -# @description Display message using debug color (gray) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayDebug() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then - Log::computeDuration - echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logDebug "$1" -} -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logWarning "$1" +# @description check if an element is contained in an array +# +# @arg $1 needle:String +# @arg $@ array:String[] +# @exitcode 0 if found +# @exitcode 1 otherwise +# @example +# Array::contains "${libPath}" "${__BASH_FRAMEWORK_IMPORTED_FILES[@]}" +Array::contains() { + local element + for element in "${@:2}"; do + [[ "${element}" = "$1" ]] && return 0 + done + return 1 } -# @description Display message using error color (red) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayError() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then - Log::computeDuration - echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logError "$1" -} -# @description load colors theme constants -# @warning if tty not opened, noColor theme will be chosen -# @arg $1 theme:String the theme to use (default, noColor) -# @arg $@ args:String[] -# @set __ERROR_COLOR String indicate error status -# @set __INFO_COLOR String indicate info status -# @set __SUCCESS_COLOR String indicate success status -# @set __WARNING_COLOR String indicate warning status -# @set __SKIPPED_COLOR String indicate skipped status -# @set __DEBUG_COLOR String indicate debug status -# @set __HELP_COLOR String indicate help status -# @set __TEST_COLOR String not used -# @set __TEST_ERROR_COLOR String not used -# @set __HELP_TITLE_COLOR String used to display help title in help strings -# @set __HELP_OPTION_COLOR String used to display highlight options in help strings -# -# @set __RESET_COLOR String reset default color +# @description concatenate each element of an array with a separator +# but wrapping text when line length is more than provided argument +# The algorithm will try not to cut the array element if it can. +# - if an arg can be placed on current line it will be, +# otherwise current line is printed and arg is added to the new +# current line +# - Empty arg is interpreted as a new line. +# - Add \r to arg in order to force break line and avoid following +# arg to be concatenated with current arg. # -# @set __HELP_EXAMPLE String to remove -# @set __HELP_TITLE String to remove -# @set __HELP_NORMAL String to remove -# shellcheck disable=SC2034 -UI::theme() { - local theme="${1-default}" - if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then - theme="noColor" +# @arg $1 glue:String +# @arg $2 maxLineLength:int +# @arg $3 indentNextLine:int +# @arg $@ array:String[] +Array::wrap2() { + local glue="${1-}" + local -i glueLength="${#glue}" + shift || true + local -i maxLineLength=$1 + shift || true + local -i indentNextLine=$1 + shift || true + local indentStr="" + if ((indentNextLine > 0)); then + indentStr="$(head -c "${indentNextLine}" &2 - Log::logFatal "$1" - exit 1 -} + ( + local currentLine + local -i currentLineLength=0 isNewline=1 argLength=0 + local -a additionalLines + local -i previousLineEmpty=0 + local arg="" -# @description create a temp file using default TMPDIR variable -# initialized in _includes/_commonHeader.sh -# @env TMPDIR String (default value /tmp) -# @arg $1 templateName:String template name to use(optional) -Framework::createTempFile() { - mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" -} + while (($# > 0)); do + arg="$1" + shift || true -# @description ensure env files are loaded -# @arg $@ list of default files to load at the end -# @exitcode 1 if one of env files fails to load -# @stderr diagnostics information is displayed -# shellcheck disable=SC2120 -Env::requireLoad() { - local -a defaultFiles=("$@") - # get list of possible config files - local -a configFiles=() - if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then - # BASH_FRAMEWORK_ENV_FILES is an array - configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") - fi - local localFrameworkConfigFile - localFrameworkConfigFile="$(pwd)/.framework-config" - if [[ -f "${localFrameworkConfigFile}" ]]; then - configFiles+=("${localFrameworkConfigFile}") - fi - if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then - configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") - fi - configFiles+=("${optionEnvFiles[@]}") - configFiles+=("${defaultFiles[@]}") - - for file in "${configFiles[@]}"; do - # shellcheck source=/.framework-config - CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { - Log::displayError "while loading config file: ${file}" - return 1 - } - done -} + # replace tab by 2 spaces + arg="${arg//$'\t'/ }" + # remove trailing spaces + arg="${arg%[[:blank:]]}" + if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then + printCurrentLine + ((previousLineEmpty = 1)) + continue + else + if ((previousLineEmpty == 1)); then + printCurrentLine + fi + ((previousLineEmpty = 0)) || true + fi + # convert eol to args + mapfile -t additionalLines <<<"${arg}" + if ((${#additionalLines[@]} > 1)); then + set -- "${additionalLines[@]}" "$@" + continue + fi -# @description activate or not Log::display* and Log::log* functions -# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL -# environment variables loaded by Env::requireLoad -# try to create log file and rotate it if necessary -# @noargs -# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable -# @env BASH_FRAMEWORK_DISPLAY_LEVEL int -# @env BASH_FRAMEWORK_LOG_LEVEL int -# @env BASH_FRAMEWORK_LOG_FILE String -# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 -# @exitcode 0 always successful -# @stderr diagnostics information about log file is displayed -# @require Env::requireLoad -# @require UI::requireTheme -Log::requireLoad() { - if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - export BASH_FRAMEWORK_LOG_LEVEL - fi + ((argLength = ${#arg})) || true - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then - if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 + # empty arg + if ((argLength == 0)); then + if ((isNewline == 0)); then + # isNewline = 0 means currentLine is not empty + printCurrentLine fi - elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + continue fi - elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - fi - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - # will always be created even if not in info level - Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" - if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then - Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + if ((isNewline == 0)); then + glueLength="${#glue}" + else + glueLength="0" + fi + if ((currentLineLength + argLength + glueLength > maxLineLength)); then + if ((argLength + glueLength > maxLineLength)); then + # arg is too long to even fit on one line + # we have to split the arg on current and next line + local -i remainingLineLength + ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) + appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" + printCurrentLine + arg="${arg:${remainingLineLength}}" + # remove leading spaces + arg="${arg##[[:blank:]]}" + + set -- "${arg}" "$@" + else + # the arg can fit on next line + printCurrentLine + appendToCurrentLine "${arg}" "${argLength}" + fi + else + appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" + fi + done + if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then + printCurrentLine fi - fi + ) | sed -E -e 's/[[:blank:]]+$//' } -# @description list the conf files list available in bash-tools/conf/ folder -# and those overridden in ${HOME}/.bash-tools/ folder + +# @description check if command specified exists or return 1 +# with error and message if not # -# @arg $1 confFolder:String the directory name (not the path) to list -# @arg $2 extension:String the extension (.sh by default) -# @arg $3 indentStr:String the indentation (' - ' by default) can be any string compatible with sed not containing any / +# @arg $1 commandName:String on which existence must be checked +# @arg $2 helpIfNotExists:String a help command to display if the command does not exist # -# @stdout list of files without extension/directory -# @example text -# - default.local -# - default.remote -# - localhost-root -Conf::getMergedList() { - local confFolder="$1" - local extension="${2-sh}" - local indentStr="${3- - }" +# @exitcode 1 if the command specified does not exist +# @stderr diagnostic information + help if second argument is provided +Assert::commandExists() { + local commandName="$1" + local helpIfNotExists="$2" - local DEFAULT_CONF_DIR="${FRAMEWORK_ROOT_DIR}/conf/${confFolder}" - local HOME_CONF_DIR="${HOME}/.bash-tools/${confFolder}" + "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { + Log::displayError "${commandName} is not installed, please install it" + if [[ -n "${helpIfNotExists}" ]]; then + Log::displayInfo "${helpIfNotExists}" + fi + return 1 + } + return 0 +} - ( - if [[ -d "${DEFAULT_CONF_DIR}" ]]; then - Conf::list "${DEFAULT_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" + +# @description check if tty (interactive mode) is active +# @noargs +# @exitcode 1 if tty not active +# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive +# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive +Assert::tty() { + if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then + return 1 + fi + if [[ "${INTERACTIVE:-0}" = "1" ]]; then + return 0 + fi + tty -s +} + + +# @description ignore exit code 141 from simple command pipes +# @example use with: +# local resultingStatus=0 +# local -a originalPipeStatus=() +# cmd1 | cmd2 || Bash::handlePipelineFailure resultingStatus originalPipeStatus || true +# [[ "${resultingStatus}" = "0" ]] +# @arg $1 resultingStatusCode:&int (passed by reference) (optional) resulting status code +# @arg $2 originalStatus:int[] (passed by reference) (optional) copy of original PIPESTATUS array +# @env PIPESTATUS assuming that this function is called like in the example provided +# @see https://unix.stackexchange.com/a/709880/582856 +Bash::handlePipelineFailure() { + local -a pipeStatusBackup=("${PIPESTATUS[@]}") + local -n handlePipelineFailure_resultingStatusCode=$1 + local -n handlePipelineFailure_originalStatus=$2 + # shellcheck disable=SC2034 + handlePipelineFailure_originalStatus=("${pipeStatusBackup[@]}") + handlePipelineFailure_resultingStatusCode=0 + local statusCode + for statusCode in "${pipeStatusBackup[@]}"; do + if ((statusCode == 141)); then + return 0 + elif ((statusCode > 0)); then + # shellcheck disable=SC2034 + handlePipelineFailure_resultingStatusCode="${statusCode}" + break fi - if [[ -d "${HOME_CONF_DIR}" ]]; then - Conf::list "${HOME_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" + done + return "${handlePipelineFailure_resultingStatusCode}" +} + + +# @description loads ~/.bash-tools/.env if available +# if not creates it from a default template +# else check if new options need to be added +BashTools::Conf::requireLoad() { +Linux::requireTarCommand +Compiler::Embed::extractFileFromBase64 \ + "${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" \ + "IyEvdXNyL2Jpbi9lbnYgYmFzaAojIHNoZWxsY2hlY2sgZGlzYWJsZT1TQzIwMzQKCiMgRGVmYXVsdCBzZXR0aW5ncwojIHlvdSBjYW4gb3ZlcnJpZGUgdGhlc2Ugc2V0dGluZ3MgYnkgY3JlYXRpbmcgJHtIT01FfS8uYmFzaC10b29scy8uZW52IGZpbGUKCiMjIwojIyMgRElTUExBWSBMZXZlbAojIyMgbWluaW11bSBsZXZlbCBvZiB0aGUgbWVzc2FnZXMgdGhhdCB3aWxsIGJlIGRpc3BsYXllZCBvbiBzY3JlZW4KIyMjCiMjIyAwOiBOTyBMT0cKIyMjIDE6IEVSUk9SCiMjIyAyOiBXQVJOSU5HCiMjIyAzOiBJTkZPCiMjIyA0OiBERUJVRwojIyMKQkFTSF9GUkFNRVdPUktfRElTUExBWV9MRVZFTD0ke0JBU0hfRlJBTUVXT1JLX0RJU1BMQVlfTEVWRUw6LTN9CgojIyMKIyMjIERJU1BMQVkgZHVyYXRpb24KIyMjIDA6IG5vIGR1cmF0aW9uIGlzIGRpc3BsYXllZCBvbiB0aGUgbWVzc2FnZXMKIyMjIDE6IGR1cmF0aW9uIGJldHdlZW4gcHJldmlvdXMgbWVzc2FnZSBhbmQgY3VycmVudCBpcyBkaXNwbGF5ZWQKIyMjIHdpdGggdGhlIG1lc3NhZ2UKIyMjCkRJU1BMQVlfRFVSQVRJT049JHtESVNQTEFZX0RVUkFUSU9OOjB9CgojIyMKIyMjIExvZyB0byBmaWxlCiMjIwojIyMgYWxsIGxvZyBtZXNzYWdlcyB3aWxsIGJlIHJlZGlyZWN0ZWQgdG8gbG9nIGZpbGUgc3BlY2lmaWVkCiMjIyB0aGlzIHNhbWUgcGF0aCB3aWxsIGJlIHVzZWQgaW5zaWRlIGFuZCBvdXRzaWRlIG9mIHRoZSBjb250YWluZXIKIyMjCkJBU0hfRlJBTUVXT1JLX0xPR19GSUxFPSR7QkFTSF9GUkFNRVdPUktfTE9HX0ZJTEU6LSR7RlJBTUVXT1JLX1JPT1RfRElSfS9sb2dzL2Jhc2gubG9nfQoKIyMjCiMjIyBMT0cgTGV2ZWwKIyMjIG1pbmltdW0gbGV2ZWwgb2YgdGhlIG1lc3NhZ2VzIHRoYXQgd2lsbCBiZSBsb2dnZWQgaW50byBMT0dfRklMRQojIyMKIyMjIDA6IE5PIExPRwojIyMgMTogRVJST1IKIyMjIDI6IFdBUk5JTkcKIyMjIDM6IElORk8KIyMjIDQ6IERFQlVHCiMjIwpCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw9JHtCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw6LTB9CgojIGFic29sdXRlIGRpcmVjdG9yeSBjb250YWluaW5nIGRiIGltcG9ydCBzcWwgZHVtcHMKREJfSU1QT1JUX0RVTVBfRElSPSR7REJfSU1QT1JUX0RVTVBfRElSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2RiSW1wb3J0RHVtcHN9CgojIGdhcmJhZ2UgY29sbGVjdCBhbGwgZmlsZXMgZm9yIHdoaWNoIG1vZGlmaWNhdGlvbiBpcyBncmVhdGVyIHRoYW4gZWc6IDMwIGRheXMgKCszMCkKIyBlYWNoIHRpbWUgYW4gZXhpc3RpbmcgZmlsZSBpcyB1c2VkIGJ5IGRiSW1wb3J0L2RiSW1wb3J0VGFibGUKIyB0aGUgZmlsZSBtb2RpZmljYXRpb24gdGltZSBpcyBzZXQgdG8gbm93CkRCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUz0ke0RCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUzotKzMwfQoKIyBhYnNvbHV0ZSBkaXJlY3RvcnkgY29udGFpbmluZyBkYlNjcmlwdHMgdXNlZCBieSBkYlNjcmlwdEFsbERhdGFiYXNlcwpTQ1JJUFRTX0ZPTERFUj0ke1NDUklQVFNfRk9MREVSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2NvbmYvZGJTY3JpcHRzfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEFXUyBQYXJhbWV0ZXJzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KUzNfQkFTRV9VUkw9JHtTM19CQVNFX1VSTDotfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvc3RtYW4gUGFyYW1ldGVycwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClBPU1RNQU5fQVBJX0tFWT0K" \ + "755" + +declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" + + BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" + if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then + FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" + else + # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR + FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" + fi + # shellcheck disable=SC2034 + FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" + # shellcheck disable=SC2034 + FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + + if [[ -f "${HOME}/.bash-tools/.env" ]]; then + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") + fi + + local envFile="${HOME}/.bash-tools/.env" + if [[ ! -f "${envFile}" ]]; then + mkdir -p "${HOME}/.bash-tools" + ( + echo "#!/usr/bin/env bash" + # shellcheck disable=SC2154 + echo "${embed_file_bashToolsDefaultConfigTemplate}" + ) >"${envFile}" + Log::displayInfo "Configuration file '${envFile}' created" + else + if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then + ( + echo '# -----------------------------------------------------' + echo '# Postman Parameters' + echo '# -----------------------------------------------------' + echo 'POSTMAN_API_KEY=' + ) >>"${envFile}" fi - ) | sort | uniq + fi + # shellcheck source=/conf/.env + source "${envFile}" || { + Log::displayError "impossible to load '${envFile}'" + exit 1 + } } -# @description check if an element is contained in an array + +# @description convert base64 encoded back to target file +# if target file is executable prepend dir of target +# file to PATH to make binary available everywhere +# it is advised to include in the path of the target file +# the md5sum of the binFile # -# @arg $1 needle:String -# @arg $@ array:String[] -# @exitcode 0 if found -# @exitcode 1 otherwise -# @example -# Array::contains "${libPath}" "${__BASH_FRAMEWORK_IMPORTED_FILES[@]}" -Array::contains() { - local element - for element in "${@:2}"; do - [[ "${element}" = "$1" ]] && return 0 - done - return 1 +# @arg $1 targetFile:String the file to write +# @arg $2 binFileBase64:String the base64 encoded file +# @arg $3 fileMode:String the chmod to set on the file +# @set PATH String prepend target embedded file binary directory to PATH variable if binary executable +Compiler::Embed::extractFileFromBase64() { + local targetFile="$1" + local binFileBase64="$2" + local fileMode="${3:-+x}" + local targetDir="${targetFile%/*}" + + if [[ ! -f "${targetFile}" ]]; then + if [[ ! -d "${targetDir}" ]]; then + mkdir -p "${targetDir}" + fi + base64 -d >"${targetFile}" <<<"${binFileBase64}" + chmod "${fileMode}" "${targetFile}" + fi + + if [[ -x "${targetFile}" ]]; then + Env::pathPrepend "${targetDir}" + fi } + # @description get absolute conf file from specified conf folder deduced using these rules # * from absolute file (ignores and ) # * relative to where script is executed (ignores and ) @@ -481,237 +506,191 @@ Conf::getAbsoluteFile() { return 1 } -# @description mysql query on a given db -# @warning could use QUERY_OPTIONS variable from dsn if defined -# @example -# cat file.sql | Database::query ... -# @arg $1 instanceQuery:&Map (passed by reference) database instance to use -# @arg $2 sqlQuery:String (optional) sql query or sql file to execute. if not provided or empty, the command can be piped -# @arg $3 dbName:String (optional) the db name -# -# @exitcode mysql command status code -Database::query() { - local -n instanceQuery=$1 - local -a mysqlCommand=() - local -a queryOptions - - mysqlCommand+=(mysql) - mysqlCommand+=("--defaults-extra-file=${instanceQuery['AUTH_FILE']}") - IFS=' ' read -r -a queryOptions <<<"${instanceQuery['QUERY_OPTIONS']}" - mysqlCommand+=("${queryOptions[@]}") - if [[ "${instanceQuery['SKIP_COLUMN_NAMES']}" = "1" ]]; then - mysqlCommand+=("-s" "--skip-column-names") - fi - # add optional db name - if [[ -n "${3+x}" ]]; then - mysqlCommand+=("$3") - fi - # add optional sql query - if [[ -n "${2+x}" && -n "$2" && ! -f "$2" ]]; then - mysqlCommand+=("-e") - mysqlCommand+=("$2") - fi - Log::displayDebug "$(printf "execute command: '%s'" "${mysqlCommand[*]}")" - if [[ -f "$2" ]]; then - "${mysqlCommand[@]}" <"$2" - else - "${mysqlCommand[@]}" - fi -} - -# @description concatenate each element of an array with a separator -# but wrapping text when line length is more than provided argument -# The algorithm will try not to cut the array element if it can. -# - if an arg can be placed on current line it will be, -# otherwise current line is printed and arg is added to the new -# current line -# - Empty arg is interpreted as a new line. -# - Add \r to arg in order to force break line and avoid following -# arg to be concatenated with current arg. +# @description list the conf files list available in bash-tools/conf/ folder +# and those overridden in ${HOME}/.bash-tools/ folder # -# @arg $1 glue:String -# @arg $2 maxLineLength:int -# @arg $3 indentNextLine:int -# @arg $@ array:String[] -Array::wrap2() { - local glue="${1-}" - local -i glueLength="${#glue}" - shift || true - local -i maxLineLength=$1 - shift || true - local -i indentNextLine=$1 - shift || true - local indentStr="" - if ((indentNextLine > 0)); then - indentStr="$(head -c "${indentNextLine}" 0)); do - arg="$1" - shift || true - - # replace tab by 2 spaces - arg="${arg//$'\t'/ }" - # remove trailing spaces - arg="${arg%[[:blank:]]}" - if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then - printCurrentLine - ((previousLineEmpty = 1)) - continue - else - if ((previousLineEmpty == 1)); then - printCurrentLine - fi - ((previousLineEmpty = 0)) || true - fi - # convert eol to args - mapfile -t additionalLines <<<"${arg}" - if ((${#additionalLines[@]} > 1)); then - set -- "${additionalLines[@]}" "$@" - continue - fi - - ((argLength = ${#arg})) || true - - # empty arg - if ((argLength == 0)); then - if ((isNewline == 0)); then - # isNewline = 0 means currentLine is not empty - printCurrentLine - fi - continue - fi - - if ((isNewline == 0)); then - glueLength="${#glue}" - else - glueLength="0" - fi - if ((currentLineLength + argLength + glueLength > maxLineLength)); then - if ((argLength + glueLength > maxLineLength)); then - # arg is too long to even fit on one line - # we have to split the arg on current and next line - local -i remainingLineLength - ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) - appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" - printCurrentLine - arg="${arg:${remainingLineLength}}" - # remove leading spaces - arg="${arg##[[:blank:]]}" +# @arg $1 confFolder:String the directory name (not the path) to list +# @arg $2 extension:String the extension (.sh by default) +# @arg $3 indentStr:String the indentation (' - ' by default) can be any string compatible with sed not containing any / +# +# @stdout list of files without extension/directory +# @example text +# - default.local +# - default.remote +# - localhost-root +Conf::getMergedList() { + local confFolder="$1" + local extension="${2-sh}" + local indentStr="${3- - }" - set -- "${arg}" "$@" - else - # the arg can fit on next line - printCurrentLine - appendToCurrentLine "${arg}" "${argLength}" - fi - else - appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" - fi - done - if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then - printCurrentLine + local DEFAULT_CONF_DIR="${FRAMEWORK_ROOT_DIR}/conf/${confFolder}" + local HOME_CONF_DIR="${HOME}/.bash-tools/${confFolder}" + + ( + if [[ -d "${DEFAULT_CONF_DIR}" ]]; then + Conf::list "${DEFAULT_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" fi - ) | sed -E -e 's/[[:blank:]]+$//' + if [[ -d "${HOME_CONF_DIR}" ]]; then + Conf::list "${HOME_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" + fi + ) | sort | uniq } -# @description check if command specified exists or return 1 -# with error and message if not + +# @description list files of dir with given extension and display it as a list one by line # -# @arg $1 commandName:String on which existence must be checked -# @arg $2 helpIfNotExists:String a help command to display if the command does not exist +# @arg $1 dir:String the directory to list +# @arg $2 prefix:String the profile file prefix (default: "") +# @arg $3 ext:String the extension +# @arg $4 findOptions:String find options, eg: -type d (Default value: '-type f') +# @arg $5 indentStr:String the indentation can be any string compatible with sed not containing any / (Default value: ' - ') +# @stdout list of files without extension/directory +# @example text +# - default.local +# - default.remote +# - localhost-root +# @exitcode 1 if directory does not exists +Conf::list() { + local dir="$1" + local prefix="${2:-}" + local ext="${3}" + local findOptions="${4--type f}" + local indentStr="${5- - }" + + if [[ ! -d "${dir}" ]]; then + Log::displayError "Directory ${dir} does not exist" + fi + if [[ -n "${ext}" && "${ext:0:1}" != "." ]]; then + ext=".${ext}" + fi + ( + # shellcheck disable=SC2086 + cd "${dir}" && + find . -maxdepth 1 ${findOptions} -name "${prefix}*${ext}" | + sed -E "s#^\./${prefix}##g" | + sed -E "s#${ext}\$##g" | sort | sed -E "s#^#${indentStr}#" + ) +} + + +# @description check if dsn file has all the mandatory variables set +# Mandatory variables are: HOSTNAME, USER, PASSWORD, PORT # -# @exitcode 1 if the command specified does not exist -# @stderr diagnostic information + help if second argument is provided -Assert::commandExists() { - local commandName="$1" - local helpIfNotExists="$2" +# @arg $1 dsnFileName:String dsn absolute filename +# @set HOSTNAME loaded from dsn file +# @set PORT loaded from dsn file +# @set USER loaded from dsn file +# @set PASSWORD loaded from dsn file +# @exitcode 0 on valid file +# @exitcode 1 if one of the properties of the conf file is invalid or if file not found +# @stderr log output if error found in conf file +Database::checkDsnFile() { + local dsnFileName="$1" + if [[ ! -f "${dsnFileName}" ]]; then + Log::displayError "dsn file ${dsnFileName} not found" + return 1 + fi - "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { - Log::displayError "${commandName} is not installed, please install it" - if [[ -n "${helpIfNotExists}" ]]; then - Log::displayInfo "${helpIfNotExists}" + ( + unset HOSTNAME PORT PASSWORD USER + # shellcheck source=/src/Database/testsData/dsn_valid.env + source "${dsnFileName}" + if [[ -z ${HOSTNAME+x} ]]; then + Log::displayError "dsn file ${dsnFileName} : HOSTNAME not provided" + return 1 fi - return 1 - } - return 0 + if [[ -z "${HOSTNAME}" ]]; then + Log::displayWarning "dsn file ${dsnFileName} : HOSTNAME value not provided" + fi + if [[ "${HOSTNAME}" = "localhost" ]]; then + Log::displayWarning "dsn file ${dsnFileName} : check that HOSTNAME should not be 127.0.0.1 instead of localhost" + fi + if [[ -z "${PORT+x}" ]]; then + Log::displayError "dsn file ${dsnFileName} : PORT not provided" + return 1 + fi + if ! [[ ${PORT} =~ ^[0-9]+$ ]]; then + Log::displayError "dsn file ${dsnFileName} : PORT invalid" + return 1 + fi + if [[ -z "${USER+x}" ]]; then + Log::displayError "dsn file ${dsnFileName} : USER not provided" + return 1 + fi + if [[ -z "${PASSWORD+x}" ]]; then + Log::displayError "dsn file ${dsnFileName} : PASSWORD not provided" + return 1 + fi + ) } -# @description Check that command version is greater than expected minimal version -# display warning if command version greater than expected minimal version -# display error if command version less than expected minimal version and exit 1 -# @arg $1 commandName:String command path -# @arg $2 argVersion:String command line parameters to launch to get command version -# @arg $3 minimalVersion:String expected minimal command version -# @arg $4 parseVersionCallback:Function -# @arg $5 help:String optional help message to display if command does not exist -# @exitcode 0 if command version greater or equal to expected minimal version -# @exitcode 1 if command version less than expected minimal version -# @exitcode 2 if command does not exist -Version::checkMinimal() { - local commandName="$1" - local argVersion="$2" - local minimalVersion="$3" - local parseVersionCallback=${4:-Version::parse} - local help="${5:-}" - Assert::commandExists "${commandName}" "${help}" || return 2 +# @description dump db limited to optional table list +# +# @arg $1 instanceDump:&Map (passed by reference) database instance to use +# @arg $2 db:String the db to dump +# @arg $3 optionalTableList:String (optional) string containing tables list (can be empty string in order to specify additional options) +# @arg $4 dumpAdditionalOptions:String[] (optional)_ ... additional dump options +# @stderr display db sql debug +# @exitcode * mysqldump command status code +Database::dump() { + # shellcheck disable=SC2178 + local -n instanceDump=$1 + shift || true + local db="$1" + shift || true + # optional table list + local optionalTableList="" + if [[ -n "${1+x}" ]]; then + optionalTableList="$1" + shift || true + fi + local -a dumpAdditionalOptions=() + local -a mysqlCommand=() - # shellcheck disable=SC2034 - local status=0 - # shellcheck disable=SC2034 - local -a pipeStatus=() - local version - version="$("${commandName}" "${argVersion}" 2>&1 | ${parseVersionCallback} || Bash::handlePipelineFailure status pipeStatus)" + # additional options + if [[ -n "${1+x}" ]]; then + dumpAdditionalOptions=("$@") + fi - Log::displayDebug "check ${commandName} version ${version} against minimal ${minimalVersion}" + mysqlCommand+=(mysqldump) + mysqlCommand+=("--defaults-extra-file=${instanceDump['AUTH_FILE']}") + IFS=' ' read -r -a dumpOptions <<<"${instanceDump['DUMP_OPTIONS']}" + mysqlCommand+=("${dumpOptions[@]}") + mysqlCommand+=("${dumpAdditionalOptions[@]}") + mysqlCommand+=("${db}") + # shellcheck disable=SC2206 + mysqlCommand+=(${optionalTableList}) - Version::compare "${version}" "${minimalVersion}" || { - local result=$? - if [[ "${result}" = "1" ]]; then - Log::displayInfo "${commandName} version is ${version} greater than ${minimalVersion}" - elif [[ "${result}" = "2" ]]; then - Log::displayError "${commandName} minimal version is ${minimalVersion}, your version is ${version}" - return 1 - fi - return 0 - } + Log::displayDebug "execute command: '${mysqlCommand[*]}'" + "${mysqlCommand[@]}" +} + + +# @description check if given database exists +# +# @arg $1 instanceIfDbExists:&Map (passed by reference) database instance to use +# @arg $2 dbName:String database name +# @exitcode 1 if db doesn't exist +# @stderr debug command +Database::ifDbExists() { + local -n instanceIfDbExists=$1 + local dbName="$2" + local result + local -a mysqlCommand=() + mysqlCommand+=(mysqlshow) + mysqlCommand+=("--defaults-extra-file=${instanceIfDbExists['AUTH_FILE']}") + # shellcheck disable=SC2206 + mysqlCommand+=(${instanceIfDbExists['SSL_OPTIONS']}) + mysqlCommand+=("${dbName}") + Log::displayDebug "execute command: '${mysqlCommand[*]}'" + result="$(MSYS_NO_PATHCONV=1 "${mysqlCommand[@]}" 2>/dev/null | grep '^Database: ' | grep -o "${dbName}")" + [[ "${result}" = "${dbName}" ]] } + # @description create a new db instance # Returns immediately if the instance is already initialized # @@ -764,12 +743,53 @@ Database::newInstance() { instanceNewInstance['SKIP_COLUMN_NAMES']="${SKIP_COLUMN_NAMES:-1}" instanceNewInstance['SSL_OPTIONS']="${MYSQL_SSL_OPTIONS:---ssl-mode=DISABLED}" instanceNewInstance['QUERY_OPTIONS']="${MYSQL_QUERY_OPTIONS:---batch --raw --default-character-set=utf8}" - instanceNewInstance['DUMP_OPTIONS']="${MYSQL_DUMP_OPTIONS:---default-character-set=utf8 --compress --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 ${instanceNewInstance['SSL_OPTIONS']}}" + instanceNewInstance['DUMP_OPTIONS']="${MYSQL_DUMP_OPTIONS:---default-character-set=utf8 --compression-algorithms --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 ${instanceNewInstance['SSL_OPTIONS']}}" instanceNewInstance['DB_IMPORT_OPTIONS']="${DB_IMPORT_OPTIONS:---connect-timeout=5 --batch --raw --default-character-set=utf8}" instanceNewInstance['INITIALIZED']=1 } + +# @description mysql query on a given db +# @warning could use QUERY_OPTIONS variable from dsn if defined +# @example +# cat file.sql | Database::query ... +# @arg $1 instanceQuery:&Map (passed by reference) database instance to use +# @arg $2 sqlQuery:String (optional) sql query or sql file to execute. if not provided or empty, the command can be piped +# @arg $3 dbName:String (optional) the db name +# +# @exitcode mysql command status code +Database::query() { + local -n instanceQuery=$1 + local -a mysqlCommand=() + local -a queryOptions + + mysqlCommand+=(mysql) + mysqlCommand+=("--defaults-extra-file=${instanceQuery['AUTH_FILE']}") + IFS=' ' read -r -a queryOptions <<<"${instanceQuery['QUERY_OPTIONS']}" + mysqlCommand+=("${queryOptions[@]}") + if [[ "${instanceQuery['SKIP_COLUMN_NAMES']}" = "1" ]]; then + mysqlCommand+=("-s" "--skip-column-names") + fi + # add optional db name + if [[ -n "${3+x}" ]]; then + mysqlCommand+=("$3") + fi + # add optional sql query + if [[ -n "${2+x}" && -n "$2" && ! -f "$2" ]]; then + mysqlCommand+=("-e") + mysqlCommand+=("$2") + fi + Log::displayDebug "$(printf "execute command: '%s'" "${mysqlCommand[*]}")" + + if [[ -f "$2" ]]; then + "${mysqlCommand[@]}" <"$2" + else + "${mysqlCommand[@]}" + fi +} + + # @description set the general options to use on mysql command to query the database # Differs than setOptions in the way that these options could change each time # @@ -781,98 +801,92 @@ Database::setQueryOptions() { instanceSetQueryOptions['QUERY_OPTIONS']="$2" } -# @description check if given database exists -# -# @arg $1 instanceIfDbExists:&Map (passed by reference) database instance to use -# @arg $2 dbName:String database name -# @exitcode 1 if db doesn't exist -# @stderr debug command -Database::ifDbExists() { - local -n instanceIfDbExists=$1 - local dbName="$2" - local result - local -a mysqlCommand=() - mysqlCommand+=(mysqlshow) - mysqlCommand+=("--defaults-extra-file=${instanceIfDbExists['AUTH_FILE']}") - # shellcheck disable=SC2206 - mysqlCommand+=(${instanceIfDbExists['SSL_OPTIONS']}) - mysqlCommand+=("${dbName}") - Log::displayDebug "execute command: '${mysqlCommand[*]}'" - result="$(MSYS_NO_PATHCONV=1 "${mysqlCommand[@]}" 2>/dev/null | grep '^Database: ' | grep -o "${dbName}")" - [[ "${result}" = "${dbName}" ]] +# @description check if all requirements are satisfied +# to execute dbImport commands +Db::checkRequirements() { + if [[ "${SKIP_REQUIREMENTS_CHECKS:-0}" = "1" ]]; then + return 0 + fi + local -i failures=0 + echo + Assert::commandExists mysql "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqldump "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists pv "sudo apt-get install -y pv" || ((++failures)) + Assert::commandExists gawk "sudo apt-get install -y gawk" || ((++failures)) + Assert::commandExists awk "sudo apt-get install -y gawk" || ((++failures)) + Version::checkMinimal "gawk" "--version" "5.0.1" || ((++failures)) + return "${failures}" +} + + +# @description prepend directories to the PATH environment variable +# @arg $@ args:String[] list of directories to prepend +# @set PATH update PATH with the directories prepended +Env::pathPrepend() { + local arg + for arg in "$@"; do + if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then + PATH="$(realpath "${arg}"):${PATH}" + fi + done } -# @description dump db limited to optional table list -# -# @arg $1 instanceDump:&Map (passed by reference) database instance to use -# @arg $2 db:String the db to dump -# @arg $3 optionalTableList:String (optional) string containing tables list (can be empty string in order to specify additional options) -# @arg $4 dumpAdditionalOptions:String[] (optional)_ ... additional dump options -# @stderr display db sql debug -# @exitcode * mysqldump command status code -Database::dump() { - # shellcheck disable=SC2178 - local -n instanceDump=$1 - shift || true - local db="$1" - shift || true - # optional table list - local optionalTableList="" - if [[ -n "${1+x}" ]]; then - optionalTableList="$1" - shift || true + +# @description ensure env files are loaded +# @arg $@ list of default files to load at the end +# @exitcode 1 if one of env files fails to load +# @stderr diagnostics information is displayed +# shellcheck disable=SC2120 +Env::requireLoad() { + REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 + + local -a defaultFiles=("$@") + # get list of possible config files + local -a configFiles=() + if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then + # BASH_FRAMEWORK_ENV_FILES is an array + configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") fi - local -a dumpAdditionalOptions=() - local -a mysqlCommand=() + local localFrameworkConfigFile + localFrameworkConfigFile="$(pwd)/.framework-config" + if [[ -f "${localFrameworkConfigFile}" ]]; then + configFiles+=("${localFrameworkConfigFile}") + fi + if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then + configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") + fi + configFiles+=("${optionEnvFiles[@]}") + configFiles+=("${defaultFiles[@]}") - # additional options - if [[ -n "${1+x}" ]]; then - dumpAdditionalOptions=("$@") + for file in "${configFiles[@]}"; do + # shellcheck source=/.framework-config + CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { + Log::displayError "while loading config file: ${file}" + return 1 + } + done +} + + +# @description concatenate 2 paths and ensure the path is correct using realpath -m +# @arg $1 basePath:String +# @arg $2 subPath:String +File::concatenatePath() { + + if [[ "${REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Linux::requireRealpathCommand has not been loaded" + exit 1 fi - mysqlCommand+=(mysqldump) - mysqlCommand+=("--defaults-extra-file=${instanceDump['AUTH_FILE']}") - IFS=' ' read -r -a dumpOptions <<<"${instanceDump['DUMP_OPTIONS']}" - mysqlCommand+=("${dumpOptions[@]}") - mysqlCommand+=("${dumpAdditionalOptions[@]}") - mysqlCommand+=("${db}") - # shellcheck disable=SC2206 - mysqlCommand+=(${optionalTableList}) + local basePath="$1" + local subPath="$2" + local fullPath="${basePath:+${basePath}/}${subPath}" - Log::displayDebug "execute command: '${mysqlCommand[*]}'" - "${mysqlCommand[@]}" + realpath -m "${fullPath}" 2>/dev/null } -# @description ignore exit code 141 from simple command pipes -# @example use with: -# local resultingStatus=0 -# local -a originalPipeStatus=() -# cmd1 | cmd2 || Bash::handlePipelineFailure resultingStatus originalPipeStatus || true -# [[ "${resultingStatus}" = "0" ]] -# @arg $1 resultingStatusCode:&int (passed by reference) (optional) resulting status code -# @arg $2 originalStatus:int[] (passed by reference) (optional) copy of original PIPESTATUS array -# @env PIPESTATUS assuming that this function is called like in the example provided -# @see https://unix.stackexchange.com/a/709880/582856 -Bash::handlePipelineFailure() { - local -a pipeStatusBackup=("${PIPESTATUS[@]}") - local -n handlePipelineFailure_resultingStatusCode=$1 - local -n handlePipelineFailure_originalStatus=$2 - # shellcheck disable=SC2034 - handlePipelineFailure_originalStatus=("${pipeStatusBackup[@]}") - handlePipelineFailure_resultingStatusCode=0 - local statusCode - for statusCode in "${pipeStatusBackup[@]}"; do - if ((statusCode == 141)); then - return 0 - elif ((statusCode > 0)); then - # shellcheck disable=SC2034 - handlePipelineFailure_resultingStatusCode="${statusCode}" - break - fi - done - return "${handlePipelineFailure_resultingStatusCode}" -} # @description delete files older than n days in given path # @warning use this function with caution as it will delete all files in given path without any prompt @@ -901,114 +915,14 @@ File::garbageCollect() { find "${path}" -depth -maxdepth "${maxdepth}" -type f -mtime "${mtime}" -print -delete } -bashToolsDefaultConfigTemplate="${bashToolsDefaultConfigTemplate:-$( - cat <<'EOF' -# shellcheck disable=SC2034 - -# Default settings -# you can override these settings by creating ${HOME}/.bash-tools/.env file - -### -### DISPLAY Level -### minimum level of the messages that will be displayed on screen -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} - -### -### DISPLAY duration -### 0: no duration is displayed on the messages -### 1: duration between previous message and current is displayed -### with the message -### -DISPLAY_DURATION=${DISPLAY_DURATION:0} - -### -### Log to file -### -### all log messages will be redirected to log file specified -### this same path will be used inside and outside of the container -### -BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} - -### -### LOG Level -### minimum level of the messages that will be logged into LOG_FILE -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} - -# absolute directory containing db import sql dumps -DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} - -# garbage collect all files for which modification is greater than eg: 30 days (+30) -# each time an existing file is used by dbImport/dbImportTable -# the file modification time is set to now -DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} - -# absolute directory containing dbScripts used by dbScriptAllDatabases -SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} - -# ----------------------------------------------------- -# AWS Parameters -# ----------------------------------------------------- -S3_BASE_URL=${S3_BASE_URL:-} - -# ----------------------------------------------------- -# Postman Parameters -# ----------------------------------------------------- -POSTMAN_API_KEY= -EOF -)}" -# @description loads ~/.bash-tools/.env if available -# if not creates it from a default template -# else check if new options need to be added -BashTools::Conf::requireLoad() { - local envFile="${HOME}/.bash-tools/.env" - if [[ ! -f "${envFile}" ]]; then - mkdir -p "${HOME}/.bash-tools" - ( - echo "#!/usr/bin/env bash" - echo "${bashToolsDefaultConfigTemplate}" - ) >"${envFile}" - Log::displayInfo "Configuration file '${envFile}' created" - else - if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then - ( - echo '# -----------------------------------------------------' - echo '# Postman Parameters' - echo '# -----------------------------------------------------' - echo 'POSTMAN_API_KEY=' - ) >>"${envFile}" - fi - fi - # shellcheck source=/conf/.env - source "${envFile}" || { - Log::displayError "impossible to load '${envFile}'" - exit 1 - } +# @description create a temp file using default TMPDIR variable +# @env TMPDIR String (default value /tmp) +# @arg $1 templateName:String template name to use(optional) +Framework::createTempFile() { + mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" } -# @description ensure COMMAND_BIN_DIR env var is set -# and PATH correctly prepared -# @noargs -# @set COMMAND_BIN_DIR string the directory where to find this command -# @set PATH string add directory where to find this command binary -Compiler::Facade::requireCommandBinDir() { - COMMAND_BIN_DIR="${CURRENT_DIR}" - Env::pathPrepend "${COMMAND_BIN_DIR}" -} # @description ensure running user is not root # @exitcode 1 if current user is root @@ -1019,6 +933,25 @@ Linux::requireExecutedAsUser() { fi } + +# @description ensure command realpath is available +# @exitcode 1 if realpath command not available +# @stderr diagnostics information is displayed +Linux::requireRealpathCommand() { + REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED=1 + + Assert::commandExists realpath +} + + +# @description ensure command tar is available +# @exitcode 1 if tar command not available +# @stderr diagnostics information is displayed +Linux::requireTarCommand() { + Assert::commandExists tar +} + + declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" @@ -1057,14 +990,72 @@ Log::computeDuration() { fi } -# @description log message to file + +# @description Display message using debug color (gray) # @arg $1 message:String the message to display -Log::logInfo() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then - Log::logMessage "${2:-INFO}" "$1" +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayDebug() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then + Log::computeDuration + echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logDebug "$1" +} + + +# @description Display message using error color (red) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayError() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then + Log::computeDuration + echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logError "$1" +} + + +# @description Display message using info color (bg light blue/fg white) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayInfo() { + local type="${2:-INFO}" + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logInfo "$1" "${type}" +} + + +# @description Display message using warning color (yellow) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayWarning() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then + Log::computeDuration + echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logWarning "$1" +} + + +# @description Display message using error color (red) and exit immediately with error status 1 +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::fatal() { + Log::computeDuration + echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + Log::logFatal "$1" + exit 1 } + # @description log message to file # @arg $1 message:String the message to display Log::logDebug() { @@ -1073,13 +1064,6 @@ Log::logDebug() { fi } -# @description log message to file -# @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" - fi -} # @description log message to file # @arg $1 message:String the message to display @@ -1089,20 +1073,6 @@ Log::logError() { fi } -# @description check if tty (interactive mode) is active -# @noargs -# @exitcode 1 if tty not active -# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive -# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive -Assert::tty() { - if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then - return 1 - fi - if [[ "${INTERACTIVE:-0}" = "1" ]]; then - return 0 - fi - tty -s -} # @description log message to file # @arg $1 message:String the message to display @@ -1110,6 +1080,16 @@ Log::logFatal() { Log::logMessage "${2:-FATAL}" "$1" } + +# @description log message to file +# @arg $1 message:String the message to display +Log::logInfo() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then + Log::logMessage "${2:-INFO}" "$1" + fi +} + + # @description Internal: common log message # @example text # [date]|[levelMsg]|message @@ -1123,9 +1103,18 @@ Log::logFatal() { # @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty # @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages # @stderr diagnostics information is displayed -# @require Env::requireLoad -# @require Log::requireLoad Log::logMessage() { + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Log::requireLoad has not been loaded" + exit 1 + fi + local levelMsg="$1" local msg="$2" local date @@ -1137,6 +1126,74 @@ Log::logMessage() { fi } + +# @description log message to file +# @arg $1 message:String the message to display +Log::logWarning() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then + Log::logMessage "${2:-WARNING}" "$1" + fi +} + + +# @description activate or not Log::display* and Log::log* functions +# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL +# environment variables loaded by Env::requireLoad +# try to create log file and rotate it if necessary +# @noargs +# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable +# @env BASH_FRAMEWORK_DISPLAY_LEVEL int +# @env BASH_FRAMEWORK_LOG_LEVEL int +# @env BASH_FRAMEWORK_LOG_FILE String +# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 +# @exitcode 0 always successful +# @stderr diagnostics information about log file is displayed +Log::requireLoad() { + REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement UI::requireTheme has not been loaded" + exit 1 + fi + + if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + export BASH_FRAMEWORK_LOG_LEVEL + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then + if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 + fi + elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + # will always be created even if not in info level + Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" + if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then + Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + fi + fi +} + + # @description To be called before logging in the log file # @arg $1 file:string log file name # @arg $2 maxLogFilesCount:int maximum number of log files @@ -1159,67 +1216,160 @@ Log::rotate() { fi } -# @description list files of dir with given extension and display it as a list one by line + +# @description draw a line with the character passed in parameter repeated depending on terminal width +# @arg $1 character:String character to use as separator (default value #) +UI::drawLine() { + local character="${1:-#}" + local -i width=${COLUMNS:-0} + if ((width == 0)) && [[ -t 1 ]]; then + width=$(tput cols) + fi + if ((width == 0)); then + width=80 + fi + printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") + echo +} + + +# @description load color theme +# @noargs +# @env BASH_FRAMEWORK_THEME String theme to use +# @env LOAD_THEME int 0 to avoid loading theme +# @exitcode 0 always successful +UI::requireTheme() { + REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + + if [[ "${LOAD_THEME:-1}" = "1" ]]; then + UI::theme "${BASH_FRAMEWORK_THEME-default}" + fi +} + + +# @description load colors theme constants +# @warning if tty not opened, noColor theme will be chosen +# @arg $1 theme:String the theme to use (default, noColor) +# @arg $@ args:String[] +# @set __ERROR_COLOR String indicate error status +# @set __INFO_COLOR String indicate info status +# @set __SUCCESS_COLOR String indicate success status +# @set __WARNING_COLOR String indicate warning status +# @set __SKIPPED_COLOR String indicate skipped status +# @set __DEBUG_COLOR String indicate debug status +# @set __HELP_COLOR String indicate help status +# @set __TEST_COLOR String not used +# @set __TEST_ERROR_COLOR String not used +# @set __HELP_TITLE_COLOR String used to display help title in help strings +# @set __HELP_OPTION_COLOR String used to display highlight options in help strings # -# @arg $1 dir:String the directory to list -# @arg $2 prefix:String the profile file prefix (default: "") -# @arg $3 ext:String the extension -# @arg $4 findOptions:String find options, eg: -type d (Default value: '-type f') -# @arg $5 indentStr:String the indentation can be any string compatible with sed not containing any / (Default value: ' - ') -# @stdout list of files without extension/directory -# @example text -# - default.local -# - default.remote -# - localhost-root -# @exitcode 1 if directory does not exists -Conf::list() { - local dir="$1" - local prefix="${2:-}" - local ext="${3}" - local findOptions="${4--type f}" - local indentStr="${5- - }" +# @set __RESET_COLOR String reset default color +# +# @set __HELP_EXAMPLE String to remove +# @set __HELP_TITLE String to remove +# @set __HELP_NORMAL String to remove +# shellcheck disable=SC2034 +UI::theme() { + local theme="${1-default}" + if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then + theme="noColor" + fi + case "${theme}" in + default | default-force) + theme="default" + ;; + noColor) ;; + *) + Log::fatal "invalid theme provided" + ;; + esac + if [[ "${theme}" = "default" ]]; then + BASH_FRAMEWORK_THEME="default" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='\e[31m' # Red + __INFO_COLOR='\e[44m' # white on lightBlue + __SUCCESS_COLOR='\e[32m' # Green + __WARNING_COLOR='\e[33m' # Yellow + __SKIPPED_COLOR='\e[33m' # Yellow + __DEBUG_COLOR='\e[37m' # Gray + __HELP_COLOR='\e[7;49;33m' # Black on Gold + __TEST_COLOR='\e[100m' # Light magenta + __TEST_ERROR_COLOR='\e[41m' # white on red + __HELP_TITLE_COLOR="\e[1;37m" # Bold + __HELP_OPTION_COLOR="\e[1;34m" # Blue + # Internal: reset color + __RESET_COLOR='\e[0m' # Reset Color + # shellcheck disable=SC2155,SC2034 + __HELP_EXAMPLE="$(echo -e "\e[2;97m")" + # shellcheck disable=SC2155,SC2034 + __HELP_TITLE="$(echo -e "\e[1;37m")" + # shellcheck disable=SC2155,SC2034 + __HELP_NORMAL="$(echo -e "\033[0m")" + else + BASH_FRAMEWORK_THEME="noColor" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='' + __INFO_COLOR='' + __SUCCESS_COLOR='' + __WARNING_COLOR='' + __SKIPPED_COLOR='' + __DEBUG_COLOR='' + __HELP_COLOR='' + __TEST_COLOR='' + __TEST_ERROR_COLOR='' + __HELP_TITLE_COLOR='' + __HELP_OPTION_COLOR='' + # Internal: reset color + __RESET_COLOR='' + __HELP_EXAMPLE='' + __HELP_TITLE='' + __HELP_NORMAL='' + fi +} + + +# @description Check that command version is greater than expected minimal version +# display warning if command version greater than expected minimal version +# display error if command version less than expected minimal version and exit 1 +# @arg $1 commandName:String command path +# @arg $2 argVersion:String command line parameters to launch to get command version +# @arg $3 minimalVersion:String expected minimal command version +# @arg $4 parseVersionCallback:Function +# @arg $5 help:String optional help message to display if command does not exist +# @exitcode 0 if command version greater or equal to expected minimal version +# @exitcode 1 if command version less than expected minimal version +# @exitcode 2 if command does not exist +Version::checkMinimal() { + local commandName="$1" + local argVersion="$2" + local minimalVersion="$3" + local parseVersionCallback=${4:-Version::parse} + local help="${5:-}" + + Assert::commandExists "${commandName}" "${help}" || return 2 + + # shellcheck disable=SC2034 + local status=0 + # shellcheck disable=SC2034 + local -a pipeStatus=() + local version + version="$("${commandName}" "${argVersion}" 2>&1 | ${parseVersionCallback} || Bash::handlePipelineFailure status pipeStatus)" - if [[ ! -d "${dir}" ]]; then - Log::displayError "Directory ${dir} does not exist" - fi - if [[ -n "${ext}" && "${ext:0:1}" != "." ]]; then - ext=".${ext}" - fi - ( - # shellcheck disable=SC2086 - cd "${dir}" && - find . -maxdepth 1 ${findOptions} -name "${prefix}*${ext}" | - sed -E "s#^\./${prefix}##g" | - sed -E "s#${ext}\$##g" | sort | sed -E "s#^#${indentStr}#" - ) -} + Log::displayDebug "check ${commandName} version ${version} against minimal ${minimalVersion}" -# @description concatenate 2 paths and ensure the path is correct using realpath -m -# @arg $1 basePath:String -# @arg $2 subPath:String -# @require Linux::requireRealpathCommand -File::concatenatePath() { - local basePath="$1" - local subPath="$2" - local fullPath="${basePath:+${basePath}/}${subPath}" + Version::compare "${version}" "${minimalVersion}" || { + local result=$? + if [[ "${result}" = "1" ]]; then + Log::displayInfo "${commandName} version is ${version} greater than ${minimalVersion}" + elif [[ "${result}" = "2" ]]; then + Log::displayError "${commandName} minimal version is ${minimalVersion}, your version is ${version}" + return 1 + fi + return 0 + } - realpath -m "${fullPath}" 2>/dev/null } -# @description filter to keep only version number from a string -# @arg $@ files:String[] the files to filter -# @exitcode * if one of the filter command fails -# @stdin you can use stdin as alternative to files argument -# @stdout the filtered content -# shellcheck disable=SC2120 -Version::parse() { - # match anything, print(p), exit on first match(Q) - sed -En \ - -e 's/\x1b\[[0-9;]*[mGKHF]//g' \ - -e 's/[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/' \ - -e '//{p;Q}' \ - "$@" -} # @description compare 2 version numbers # @arg $1 version1:String version 1 @@ -1253,140 +1403,32 @@ Version::compare() { return 0 } -# @description check if dsn file has all the mandatory variables set -# Mandatory variables are: HOSTNAME, USER, PASSWORD, PORT -# -# @arg $1 dsnFileName:String dsn absolute filename -# @set HOSTNAME loaded from dsn file -# @set PORT loaded from dsn file -# @set USER loaded from dsn file -# @set PASSWORD loaded from dsn file -# @exitcode 0 on valid file -# @exitcode 1 if one of the properties of the conf file is invalid or if file not found -# @stderr log output if error found in conf file -Database::checkDsnFile() { - local dsnFileName="$1" - if [[ ! -f "${dsnFileName}" ]]; then - Log::displayError "dsn file ${dsnFileName} not found" - return 1 - fi - - ( - unset HOSTNAME PORT PASSWORD USER - # shellcheck source=/src/Database/testsData/dsn_valid.env - source "${dsnFileName}" - if [[ -z ${HOSTNAME+x} ]]; then - Log::displayError "dsn file ${dsnFileName} : HOSTNAME not provided" - return 1 - fi - if [[ -z "${HOSTNAME}" ]]; then - Log::displayWarning "dsn file ${dsnFileName} : HOSTNAME value not provided" - fi - if [[ "${HOSTNAME}" = "localhost" ]]; then - Log::displayWarning "dsn file ${dsnFileName} : check that HOSTNAME should not be 127.0.0.1 instead of localhost" - fi - if [[ -z "${PORT+x}" ]]; then - Log::displayError "dsn file ${dsnFileName} : PORT not provided" - return 1 - fi - if ! [[ ${PORT} =~ ^[0-9]+$ ]]; then - Log::displayError "dsn file ${dsnFileName} : PORT invalid" - return 1 - fi - if [[ -z "${USER+x}" ]]; then - Log::displayError "dsn file ${dsnFileName} : USER not provided" - return 1 - fi - if [[ -z "${PASSWORD+x}" ]]; then - Log::displayError "dsn file ${dsnFileName} : PASSWORD not provided" - return 1 - fi - ) -} - -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" - fi - done -} - -# @description load color theme -# @noargs -# @env BASH_FRAMEWORK_THEME String theme to use -# @env LOAD_THEME int 0 to avoid loading theme -# @exitcode 0 always successful -UI::requireTheme() { - if [[ "${LOAD_THEME:-1}" = "1" ]]; then - UI::theme "${BASH_FRAMEWORK_THEME-default}" - fi -} -# @description ensure command realpath is available -# @exitcode 1 if realpath command not available -# @stderr diagnostics information is displayed -Linux::requireRealpathCommand() { - Assert::commandExists realpath +# @description filter to keep only version number from a string +# @arg $@ files:String[] the files to filter +# @exitcode * if one of the filter command fails +# @stdin you can use stdin as alternative to files argument +# @stdout the filtered content +# shellcheck disable=SC2120 +Version::parse() { + # match anything, print(p), exit on first match(Q) + sed -En \ + -e 's/\x1b\[[0-9;]*[mGKHF]//g' \ + -e 's/[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/' \ + -e '//{p;Q}' \ + "$@" } - # FUNCTIONS -facade_main_dbImportsh() { -BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" -if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then - FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" -else - # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR - FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" -fi -FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" -FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" - -# @require BashTools::Conf::requireLoad -if [[ -f "${HOME}/.bash-tools/.env" ]]; then - export BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") -fi -# REQUIRES -Env::requireLoad -UI::requireTheme -Log::requireLoad -Linux::requireRealpathCommand -BashTools::Conf::requireLoad -Compiler::Facade::requireCommandBinDir -Linux::requireExecutedAsUser - -# @require Compiler::Facade::requireCommandBinDir -# shellcheck disable=SC2034 - -# default values -declare optionFromAws="" -declare optionSkipSchema="0" -declare targetDbName="" -declare fromDbName="" -declare optionProfile="default" -declare optionTables="" -declare profileCommandFile="" -declare optionTargetDsn="" -declare optionCharacterSet="" -declare defaultTargetCharacterSet="" - -# other configuration -declare copyrightBeginYear="2020" -declare TIMEFORMAT='time spent : %3R' -declare DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR%/} -declare PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbImportProfiles" -declare HOME_PROFILES_DIR="${HOME}/.bash-tools/dbImportProfiles" -declare DOWNLOAD_DUMP=0 declare -a BASH_FRAMEWORK_ARGV_FILTERED=() +beforeParseCallback() { + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + copyrightCallback() { if [[ -z "${copyrightBeginYear}" ]]; then copyrightBeginYear="$(date +%Y)" @@ -1423,13 +1465,14 @@ updateArgListQuietCallback() { :; } # shellcheck disable=SC2317 # if function is overridden optionHelpCallback() { - dbImportCommand help + Log::displayError "optionHelpCallback needs to be overridden" exit 0 } # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { - echo "${SCRIPT_NAME} version 2.0" + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version ${versionNumber}" exit 0 } @@ -1447,21 +1490,22 @@ optionEnvFileCallback() { optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_INFO} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionDebugVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_DEBUG} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionTraceVerboseCallback() { + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vvv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_TRACE} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } getLevel() { @@ -1485,6 +1529,7 @@ getLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -1506,6 +1551,7 @@ getVerboseLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -1516,7 +1562,7 @@ optionDisplayLevelCallback() { logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1525,19 +1571,20 @@ optionLogLevelCallback() { local logLevel verboseLevel logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionLogFileCallback() { local logFile="$2" - echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionQuietCallback() { - echo "BASH_FRAMEWORK_QUIET_MODE=1" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_QUIET_MODE=1" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1568,6 +1615,7 @@ optionBashFrameworkConfigCallback() { defaultFrameworkConfig="$( cat <<'EOF' + # copied from src/_includes/.framework-config.default # shellcheck disable=SC2034 @@ -1585,7 +1633,7 @@ NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-conf # describe the files that are allowed to not have an associated bats file BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" # describe the files that are allowed to not have a function matching the filename -FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|\.tpl$|/testsData/|^manualTests/|\.bats$}" +FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|/testsData/|^manualTests/|\.bats$}" # Source directories if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then FRAMEWORK_SRC_DIRS=( @@ -1601,6 +1649,7 @@ BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" + EOF )" @@ -1609,7 +1658,7 @@ overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" commandOptionParseFinished() { # load default template framework config defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" - echo "${defaultFrameworkConfig}" > "${defaultEnvFile}" + echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") if [[ -f "${envFile}" ]]; then files+=("${envFile}") @@ -1618,42 +1667,138 @@ commandOptionParseFinished() { if [[ -f "${optionBashFrameworkConfig}" ]]; then files+=("${optionBashFrameworkConfig}") fi - files+=("${overrideEnvFile}") - Env::requireLoad "${files[@]}" - Log::requireLoad - # shellcheck disable=SC2154 - if [[ "${optionConfig}" = "1" ]]; then - displayConfig + files+=("${overrideEnvFile}") + Env::requireLoad "${files[@]}" + Log::requireLoad + # shellcheck disable=SC2154 + if [[ "${optionConfig}" = "1" ]]; then + displayConfig + fi +} + + + +# shellcheck disable=SC2034 +declare copyrightBeginYear="2020" +# shellcheck disable=SC2034 +declare versionNumber="2.0" + +declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" +declare defaultTargetCharacterSet="" + +beforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad + Linux::requireRealpathCommand + Linux::requireExecutedAsUser +} + +optionHelpCallback() { + dbImportCommandHelp + exit 0 +} + +longDescriptionFunction() { + local profilesList="" + local dsnList="" + dsnList="$(Conf::getMergedList "dsn" "env")" + profilesList="$(Conf::getMergedList "dbImportProfiles" "sh" || true)" + + echo -e "${__HELP_TITLE}Default profiles directory:${__HELP_NORMAL}" + echo -e "${PROFILES_DIR-configuration error}" + + echo -e "${__HELP_TITLE}User profiles directory:${__HELP_NORMAL}" + echo -e "${HOME_PROFILES_DIR-configuration error}" + echo -e "Allows to override profiles defined in 'Default profiles directory'" + + echo -e "${__HELP_TITLE}List of available profiles:${__HELP_NORMAL}" + echo -e "${profilesList}" + + echo -e "${__HELP_TITLE}List of available dsn:${__HELP_NORMAL}" + echo -e "${dsnList}" + + echo -e "${__HELP_TITLE}Aws s3 location:${__HELP_NORMAL}" + echo -e "${S3_BASE_URL}" + echo + echo -e "${__HELP_TITLE}Example 1: from one database to another one${__HELP_NORMAL}" + echo -e "${__HELP_EXAMPLE}TODO${__HELP_NORMAL}" + echo + echo -e "${__HELP_TITLE}Example 2: import from S3${__HELP_NORMAL}" + echo -e "${__HELP_EXAMPLE}TODO${__HELP_NORMAL}" + Db::checkRequirements +} + +dsnHelpFunction() { + echo 'dsn to use for source database' + echo 'this option is incompatible with -a|--from-aws option' +} + +dbImportCommandCallback() { + if [[ -z "${targetDbName}" ]]; then + # shellcheck disable=SC2154 + targetDbName="${fromDbName}" + fi + + if [[ -n "${optionFromAws}" ]]; then + Assert::commandExists aws \ + "Command ${SCRIPT_NAME} - missing aws, please check https://docs.aws.amazon.com/fr_fr/cli/latest/userguide/install-cliv2.html" || exit 1 + + if [[ -n "${optionFromDsn}" ]]; then + Log::fatal "Command ${SCRIPT_NAME} - you cannot use from-dsn and from-aws at the same time" + fi + + if [[ -z "${S3_BASE_URL}" ]]; then + Log::fatal "Command ${SCRIPT_NAME} - missing S3_BASE_URL, please provide a value in .env file" + fi + elif [[ -z "${optionFromDsn}" ]]; then + # default value for FROM_DSN if from-aws not set + # shellcheck disable=SC2154 + optionFromDsn="default.remote" + fi + + if [[ -z "${DB_IMPORT_DUMP_DIR}" ]]; then + Log::fatal "Command ${SCRIPT_NAME} - you have to specify a value for DB_IMPORT_DUMP_DIR env variable" + fi + + if [[ ! -d "${DB_IMPORT_DUMP_DIR}" ]]; then + mkdir -p "${DB_IMPORT_DUMP_DIR}" || + Log::fatal "Command ${SCRIPT_NAME} - impossible to create directory ${DB_IMPORT_DUMP_DIR} specified by DB_IMPORT_DUMP_DIR env variable" + fi +} + + + +# shellcheck disable=SC2034 +declare defaultTargetCollationName="utf8_general_ci" +declare defaultTargetCharacterSet="utf8" + +initializeDefaultTargetMysqlOptions() { + local -n dbFromInstanceTargetMysql=$1 + local fromDbName="$2" + + # get remote db collation name + if [[ -n ${optionCollationName+x} && -z "${optionCollationName}" ]]; then + optionCollationName=$(Database::query dbFromInstanceTargetMysql \ + "SELECT default_collation_name FROM information_schema.SCHEMATA WHERE schema_name = \"${fromDbName}\";" "information_schema") + fi + + # get remote db character set + if [[ -z "${optionCharacterSet}" ]]; then + optionCharacterSet=$(Database::query dbFromInstanceTargetMysql \ + "SELECT default_character_set_name FROM information_schema.SCHEMATA WHERE schema_name = \"${fromDbName}\";" "information_schema") fi } -# default values -declare optionFromDsn="" - -# default values -declare optionProfile="default" -declare optionTables="" -declare profileCommandFile="" -profileOptionHelpCallback() { - echo "the name of the profile to use in order to include or exclude tables" - echo "(if not specified in default.sh from 'User profiles directory' if exists or 'Default profiles directory')" -} -optionTablesCallback() { - if [[ ! ${optionTables} =~ ^[A-Za-z0-9_]+(,[A-Za-z0-9_]+)*$ ]]; then - Log::fatal "Command ${SCRIPT_NAME} - Table list is not valid : ${optionTables}" - fi +profileOptionHelpFunction() { + Array::wrap2 " " 80 4 \ + " The name of the profile to use in order to include or exclude tables." + echo } -profileOptionCallback() { - local -a profilesArray - readarray -t profilesArray < <(Conf::getMergedList "dbImportProfiles" "sh" "" || true) - if ! Array::contains "$2" "${profilesArray[@]}"; then - Log::displayError "${SCRIPT_NAME} - invalid profile '$2' provided" - return 1 - fi -} initProfileCommandCallback() { if [[ "${optionProfile}" != "default" && -n "${optionTables}" ]]; then Log::fatal "Command ${SCRIPT_NAME} - you cannot use table and profile options at the same time" @@ -1682,735 +1827,919 @@ initProfileCommandCallback() { Log::displayInfo "${profileMsgInfo}" } -declare optionTargetDsn="default.local" -declare optionCharacterSet="" -declare defaultTargetCharacterSet="utf8" - -initializeDefaultTargetMysqlOptions() { - local -n dbFromInstanceTargetMysql=$1 - local fromDbName="$2" - - # get remote db collation name - if [[ -n ${optionCollationName+x} && -z "${optionCollationName}" ]]; then - optionCollationName=$(Database::query dbFromInstanceTargetMysql \ - "SELECT default_collation_name FROM information_schema.SCHEMATA WHERE schema_name = \"${fromDbName}\";" "information_schema") +profileOptionCallback() { + local -a profilesArray + readarray -t profilesArray < <(Conf::getMergedList "dbImportProfiles" "sh" "" || true) + if ! Array::contains "$2" "${profilesArray[@]}"; then + Log::displayError "${SCRIPT_NAME} - invalid profile '$2' provided" + return 1 fi +} - # get remote db character set - if [[ -z "${optionCharacterSet}" ]]; then - optionCharacterSet=$(Database::query dbFromInstanceTargetMysql \ - "SELECT default_character_set_name FROM information_schema.SCHEMATA WHERE schema_name = \"${fromDbName}\";" "information_schema") +optionTablesCallback() { + if [[ ! ${optionTables} =~ ^[A-Za-z0-9_]+(,[A-Za-z0-9_]+)*$ ]]; then + Log::fatal "Command ${SCRIPT_NAME} - Table list is not valid : ${optionTables}" fi } + + + + +optionVersionCallback() { + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version ${versionNumber}" + Db::checkRequirements + exit 0 +} + + + +dsnHelpFunction() { + echo " target mysql server" +} + + +# ------------------------------------------ +# Command dbImportCommand +# ------------------------------------------ + +# options variables initialization declare optionCollationName="" -declare defaultTargetCollationName="utf8_general_ci" +declare optionTargetDsn="default.local" +declare optionCharacterSet="" +declare optionProfile="default" +declare optionTables="" +declare optionSkipSchema="0" +declare optionFromDsn="" +declare optionFromAws="" +declare optionHelp="0" +declare optionConfig="0" +declare optionBashFrameworkConfig="" +declare optionInfoVerbose="0" +declare optionDebugVerbose="0" +declare optionTraceVerbose="0" +declare -a optionEnvFiles=() +declare optionLogLevel="" +declare optionLogFile="" +declare optionDisplayLevel="" +declare optionNoColor="0" +declare optionTheme="default" +declare optionVersion="0" +declare optionQuiet="0" +# arguments variables initialization +declare fromDbName="" +declare targetDbName="" +# @description parse command options and arguments for dbImportCommand +dbImportCommandParse() { + Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" + Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" + optionCollationName="" + local -i options_parse_optionParsedCountOptionCollationName + ((options_parse_optionParsedCountOptionCollationName = 0)) || true + optionTargetDsn="default.local" + local -i options_parse_optionParsedCountOptionTargetDsn + ((options_parse_optionParsedCountOptionTargetDsn = 0)) || true + optionCharacterSet="" + local -i options_parse_optionParsedCountOptionCharacterSet + ((options_parse_optionParsedCountOptionCharacterSet = 0)) || true + optionProfile="default" + local -i options_parse_optionParsedCountOptionProfile + ((options_parse_optionParsedCountOptionProfile = 0)) || true + optionTables="" + local -i options_parse_optionParsedCountOptionTables + ((options_parse_optionParsedCountOptionTables = 0)) || true + optionSkipSchema="0" + local -i options_parse_optionParsedCountOptionSkipSchema + ((options_parse_optionParsedCountOptionSkipSchema = 0)) || true + optionFromDsn="" + local -i options_parse_optionParsedCountOptionFromDsn + ((options_parse_optionParsedCountOptionFromDsn = 0)) || true + optionFromAws="" + local -i options_parse_optionParsedCountOptionFromAws + ((options_parse_optionParsedCountOptionFromAws = 0)) || true + optionHelp="0" + local -i options_parse_optionParsedCountOptionHelp + ((options_parse_optionParsedCountOptionHelp = 0)) || true + optionConfig="0" + local -i options_parse_optionParsedCountOptionConfig + ((options_parse_optionParsedCountOptionConfig = 0)) || true + optionBashFrameworkConfig="" + local -i options_parse_optionParsedCountOptionBashFrameworkConfig + ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true + optionInfoVerbose="0" + local -i options_parse_optionParsedCountOptionInfoVerbose + ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true + optionDebugVerbose="0" + local -i options_parse_optionParsedCountOptionDebugVerbose + ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true + optionTraceVerbose="0" + local -i options_parse_optionParsedCountOptionTraceVerbose + ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true + + optionLogLevel="" + local -i options_parse_optionParsedCountOptionLogLevel + ((options_parse_optionParsedCountOptionLogLevel = 0)) || true + optionLogFile="" + local -i options_parse_optionParsedCountOptionLogFile + ((options_parse_optionParsedCountOptionLogFile = 0)) || true + optionDisplayLevel="" + local -i options_parse_optionParsedCountOptionDisplayLevel + ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true + optionNoColor="0" + local -i options_parse_optionParsedCountOptionNoColor + ((options_parse_optionParsedCountOptionNoColor = 0)) || true + optionTheme="default" + local -i options_parse_optionParsedCountOptionTheme + ((options_parse_optionParsedCountOptionTheme = 0)) || true + optionVersion="0" + local -i options_parse_optionParsedCountOptionVersion + ((options_parse_optionParsedCountOptionVersion = 0)) || true + optionQuiet="0" + local -i options_parse_optionParsedCountOptionQuiet + ((options_parse_optionParsedCountOptionQuiet = 0)) || true + + fromDbName="" + local -i options_parse_argParsedCountFromDbName + ((options_parse_argParsedCountFromDbName = 0)) || true + + targetDbName="" + local -i options_parse_argParsedCountTargetDbName + ((options_parse_argParsedCountTargetDbName = 0)) || true -dbImportCommand() { - local options_parse_cmd="$1" - shift || true - if [[ "${options_parse_cmd}" = "parse" ]]; then - local -i options_parse_optionParsedCountOptionProfile - ((options_parse_optionParsedCountOptionProfile = 0)) || true - local -i options_parse_optionParsedCountOptionTables - ((options_parse_optionParsedCountOptionTables = 0)) || true - local -i options_parse_optionParsedCountOptionFromDsn - ((options_parse_optionParsedCountOptionFromDsn = 0)) || true - optionSkipSchema="0" - local -i options_parse_optionParsedCountOptionSkipSchema - ((options_parse_optionParsedCountOptionSkipSchema = 0)) || true - local -i options_parse_optionParsedCountOptionFromAws - ((options_parse_optionParsedCountOptionFromAws = 0)) || true - local -i options_parse_optionParsedCountOptionTargetDsn - ((options_parse_optionParsedCountOptionTargetDsn = 0)) || true - local -i options_parse_optionParsedCountOptionCharacterSet - ((options_parse_optionParsedCountOptionCharacterSet = 0)) || true - local -i options_parse_optionParsedCountOptionCollationName - ((options_parse_optionParsedCountOptionCollationName = 0)) || true - local -i options_parse_optionParsedCountOptionBashFrameworkConfig - ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true - optionConfig="0" - local -i options_parse_optionParsedCountOptionConfig - ((options_parse_optionParsedCountOptionConfig = 0)) || true - optionInfoVerbose="0" - local -i options_parse_optionParsedCountOptionInfoVerbose - ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true - optionDebugVerbose="0" - local -i options_parse_optionParsedCountOptionDebugVerbose - ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true - optionTraceVerbose="0" - local -i options_parse_optionParsedCountOptionTraceVerbose - ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionNoColor="0" - local -i options_parse_optionParsedCountOptionNoColor - ((options_parse_optionParsedCountOptionNoColor = 0)) || true - optionTheme="default" - local -i options_parse_optionParsedCountOptionTheme - ((options_parse_optionParsedCountOptionTheme = 0)) || true - optionHelp="0" - local -i options_parse_optionParsedCountOptionHelp - ((options_parse_optionParsedCountOptionHelp = 0)) || true - optionVersion="0" - local -i options_parse_optionParsedCountOptionVersion - ((options_parse_optionParsedCountOptionVersion = 0)) || true - optionQuiet="0" - local -i options_parse_optionParsedCountOptionQuiet - ((options_parse_optionParsedCountOptionQuiet = 0)) || true - local -i options_parse_optionParsedCountOptionLogLevel - ((options_parse_optionParsedCountOptionLogLevel = 0)) || true - local -i options_parse_optionParsedCountOptionLogFile - ((options_parse_optionParsedCountOptionLogFile = 0)) || true - local -i options_parse_optionParsedCountOptionDisplayLevel - ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true - local -i options_parse_argParsedCountFromDbName - ((options_parse_argParsedCountFromDbName = 0)) || true - local -i options_parse_argParsedCountTargetDbName - ((options_parse_argParsedCountTargetDbName = 0)) || true - # shellcheck disable=SC2034 - local -i options_parse_parsedArgIndex=0 - while (($# > 0)); do - local options_parse_arg="$1" - local argOptDefaultBehavior=0 - case "${options_parse_arg}" in - # Option 1/22 - # Option optionProfile --profile|-p variableType String min 0 max 1 authorizedValues '' regexp '' - --profile | -p) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionProfile >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionProfile)) - # shellcheck disable=SC2034 - optionProfile="$1" - profileOptionCallback "${options_parse_arg}" "${optionProfile}" - ;; - # Option 2/22 - # Option optionTables --tables variableType String min 0 max 1 authorizedValues '' regexp '' - --tables) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionTables >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTables)) - # shellcheck disable=SC2034 - optionTables="$1" - optionTablesCallback "${options_parse_arg}" "${optionTables}" - ;; - # Option 3/22 - # Option optionFromDsn --from-dsn|-f variableType String min 0 max 1 authorizedValues '' regexp '' - --from-dsn | -f) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionFromDsn >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionFromDsn)) - # shellcheck disable=SC2034 - optionFromDsn="$1" - ;; - # Option 4/22 - # Option optionSkipSchema --skip-schema|-s variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --skip-schema | -s) - # shellcheck disable=SC2034 - optionSkipSchema="1" - if ((options_parse_optionParsedCountOptionSkipSchema >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionSkipSchema)) - ;; - # Option 5/22 - # Option optionFromAws --from-aws|-a variableType String min 0 max 1 authorizedValues '' regexp '' - --from-aws | -a) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionFromAws >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionFromAws)) - # shellcheck disable=SC2034 - optionFromAws="$1" - ;; - # Option 6/22 - # Option optionTargetDsn --target-dsn|-t variableType String min 0 max 1 authorizedValues '' regexp '' - --target-dsn | -t) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionTargetDsn >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTargetDsn)) - # shellcheck disable=SC2034 - optionTargetDsn="$1" - ;; - # Option 7/22 - # Option optionCharacterSet --character-set|-c variableType String min 0 max 1 authorizedValues '' regexp '' - --character-set | -c) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionCharacterSet >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionCharacterSet)) - # shellcheck disable=SC2034 - optionCharacterSet="$1" - ;; - # Option 8/22 - # Option optionCollationName --collation-name|-o variableType String min 0 max 1 authorizedValues '' regexp '' - --collation-name | -o) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionCollationName >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionCollationName)) - # shellcheck disable=SC2034 - optionCollationName="$1" - ;; - # Option 9/22 - # Option optionBashFrameworkConfig --bash-framework-config variableType String min 0 max 1 authorizedValues '' regexp '' - --bash-framework-config) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) - # shellcheck disable=SC2034 - optionBashFrameworkConfig="$1" - optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" - ;; - # Option 10/22 - # Option optionConfig --config variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --config) - # shellcheck disable=SC2034 - optionConfig="1" - if ((options_parse_optionParsedCountOptionConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionConfig)) - ;; - # Option 11/22 - # Option optionInfoVerbose --verbose|-v variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --verbose | -v) - # shellcheck disable=SC2034 - optionInfoVerbose="1" - if ((options_parse_optionParsedCountOptionInfoVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionInfoVerbose)) - optionInfoVerboseCallback "${options_parse_arg}" - updateArgListInfoVerboseCallback "${options_parse_arg}" - ;; - # Option 12/22 - # Option optionDebugVerbose -vv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vv) - # shellcheck disable=SC2034 - optionDebugVerbose="1" - if ((options_parse_optionParsedCountOptionDebugVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDebugVerbose)) - optionDebugVerboseCallback "${options_parse_arg}" - updateArgListDebugVerboseCallback "${options_parse_arg}" - ;; - # Option 13/22 - # Option optionTraceVerbose -vvv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vvv) - # shellcheck disable=SC2034 - optionTraceVerbose="1" - if ((options_parse_optionParsedCountOptionTraceVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTraceVerbose)) - optionTraceVerboseCallback "${options_parse_arg}" - updateArgListTraceVerboseCallback "${options_parse_arg}" - ;; - # Option 14/22 - # Option optionEnvFiles --env-file variableType StringArray min 0 max -1 authorizedValues '' regexp '' - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - ;; - # Option 15/22 - # Option optionNoColor --no-color variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --no-color) - # shellcheck disable=SC2034 - optionNoColor="1" - if ((options_parse_optionParsedCountOptionNoColor >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionNoColor)) - optionNoColorCallback "${options_parse_arg}" - updateArgListNoColorCallback "${options_parse_arg}" - ;; - # Option 16/22 - # Option optionTheme --theme variableType String min 0 max 1 authorizedValues 'default|default-force|noColor' regexp '' - --theme) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default|default-force|noColor)" - return 1 - fi - if ((options_parse_optionParsedCountOptionTheme >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTheme)) - # shellcheck disable=SC2034 - optionTheme="$1" - optionThemeCallback "${options_parse_arg}" "${optionTheme}" - updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" - ;; - # Option 17/22 - # Option optionHelp --help|-h variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --help | -h) - # shellcheck disable=SC2034 - optionHelp="1" - if ((options_parse_optionParsedCountOptionHelp >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionHelp)) - optionHelpCallback "${options_parse_arg}" - ;; - # Option 18/22 - # Option optionVersion --version variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --version) - # shellcheck disable=SC2034 - optionVersion="1" - if ((options_parse_optionParsedCountOptionVersion >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionVersion)) - optionVersionCallback "${options_parse_arg}" - ;; - # Option 19/22 - # Option optionQuiet --quiet|-q variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --quiet | -q) - # shellcheck disable=SC2034 - optionQuiet="1" - if ((options_parse_optionParsedCountOptionQuiet >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionQuiet)) - optionQuietCallback "${options_parse_arg}" - updateArgListQuietCallback "${options_parse_arg}" - ;; - # Option 20/22 - # Option optionLogLevel --log-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --log-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogLevel)) - # shellcheck disable=SC2034 - optionLogLevel="$1" - optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - ;; - # Option 21/22 - # Option optionLogFile --log-file variableType String min 0 max 1 authorizedValues '' regexp '' - --log-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogFile >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogFile)) - # shellcheck disable=SC2034 - optionLogFile="$1" - optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" - updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" - ;; - # Option 22/22 - # Option optionDisplayLevel --display-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --display-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" + # shellcheck disable=SC2034 + local -i options_parse_parsedArgIndex=0 + while (($# > 0)); do + local options_parse_arg="$1" + local argOptDefaultBehavior=0 + case "${options_parse_arg}" in + # Option 1/22 + # optionCollationName alts --collation-name|-o + # type: String min 0 max 1 + --collation-name | -o) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionCollationName >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionCollationName)) + # shellcheck disable=SC2034 + optionCollationName="$1" + ;; + + # Option 2/22 + # optionTargetDsn alts --target-dsn|-t + # type: String min 0 max 1 + --target-dsn | -t) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionTargetDsn >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTargetDsn)) + # shellcheck disable=SC2034 + optionTargetDsn="$1" + ;; + + # Option 3/22 + # optionCharacterSet alts --character-set|-c + # type: String min 0 max 1 + --character-set | -c) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionCharacterSet >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionCharacterSet)) + # shellcheck disable=SC2034 + optionCharacterSet="$1" + ;; + + # Option 4/22 + # optionProfile alts --profile|-p + # type: String min 0 max 1 + --profile | -p) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionProfile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionProfile)) + # shellcheck disable=SC2034 + optionProfile="$1" + profileOptionCallback "${options_parse_arg}" "${optionProfile}" + + ;; + + # Option 5/22 + # optionTables alts --tables + # type: String min 0 max 1 + --tables) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionTables >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTables)) + # shellcheck disable=SC2034 + optionTables="$1" + optionTablesCallback "${options_parse_arg}" "${optionTables}" + + ;; + + # Option 6/22 + # optionSkipSchema alts --skip-schema|-s + # type: Boolean min 0 max 1 + --skip-schema | -s) + # shellcheck disable=SC2034 + optionSkipSchema="1" + + if ((options_parse_optionParsedCountOptionSkipSchema >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionSkipSchema)) + ;; + + # Option 7/22 + # optionFromDsn alts --from-dsn|-f + # type: String min 0 max 1 + --from-dsn | -f) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionFromDsn >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionFromDsn)) + # shellcheck disable=SC2034 + optionFromDsn="$1" + ;; + + # Option 8/22 + # optionFromAws alts --from-aws|-a + # type: String min 0 max 1 + --from-aws | -a) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionFromAws >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionFromAws)) + # shellcheck disable=SC2034 + optionFromAws="$1" + ;; + + # Option 9/22 + # optionHelp alts --help|-h + # type: Boolean min 0 max 1 + --help | -h) + # shellcheck disable=SC2034 + optionHelp="1" + + if ((options_parse_optionParsedCountOptionHelp >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionHelp)) + optionHelpCallback "${options_parse_arg}" "${optionHelp}" + + ;; + + # Option 10/22 + # optionConfig alts --config + # type: Boolean min 0 max 1 + --config) + # shellcheck disable=SC2034 + optionConfig="1" + + if ((options_parse_optionParsedCountOptionConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionConfig)) + ;; + + # Option 11/22 + # optionBashFrameworkConfig alts --bash-framework-config + # type: String min 0 max 1 + --bash-framework-config) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) + # shellcheck disable=SC2034 + optionBashFrameworkConfig="$1" + optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" + + ;; + + # Option 12/22 + # optionInfoVerbose alts --verbose|-v + # type: Boolean min 0 max 1 + --verbose | -v) + # shellcheck disable=SC2034 + optionInfoVerbose="1" + + if ((options_parse_optionParsedCountOptionInfoVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionInfoVerbose)) + optionInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + updateArgListInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + ;; + + # Option 13/22 + # optionDebugVerbose alts -vv + # type: Boolean min 0 max 1 + -vv) + # shellcheck disable=SC2034 + optionDebugVerbose="1" + + if ((options_parse_optionParsedCountOptionDebugVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDebugVerbose)) + optionDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + updateArgListDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + ;; + + # Option 14/22 + # optionTraceVerbose alts -vvv + # type: Boolean min 0 max 1 + -vvv) + # shellcheck disable=SC2034 + optionTraceVerbose="1" + + if ((options_parse_optionParsedCountOptionTraceVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTraceVerbose)) + optionTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + updateArgListTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + ;; + + # Option 15/22 + # optionEnvFiles alts --env-file + # type: StringArray min 0 max -1 + --env-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + ((++options_parse_optionParsedCountOptionEnvFiles)) + optionEnvFiles+=("$1") + optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + ;; + + # Option 16/22 + # optionLogLevel alts --log-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --log-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogLevel)) + # shellcheck disable=SC2034 + optionLogLevel="$1" + optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + ;; + + # Option 17/22 + # optionLogFile alts --log-file + # type: String min 0 max 1 + --log-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogFile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogFile)) + # shellcheck disable=SC2034 + optionLogFile="$1" + optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + ;; + + # Option 18/22 + # optionDisplayLevel alts --display-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --display-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionDisplayLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDisplayLevel)) + # shellcheck disable=SC2034 + optionDisplayLevel="$1" + optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + ;; + + # Option 19/22 + # optionNoColor alts --no-color + # type: Boolean min 0 max 1 + --no-color) + # shellcheck disable=SC2034 + optionNoColor="1" + + if ((options_parse_optionParsedCountOptionNoColor >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionNoColor)) + optionNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + updateArgListNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + ;; + + # Option 20/22 + # optionTheme alts --theme + # type: String min 0 max 1 + # authorizedValues: default|default-force|noColor + --theme) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ default|default-force|noColor ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionTheme >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTheme)) + # shellcheck disable=SC2034 + optionTheme="$1" + optionThemeCallback "${options_parse_arg}" "${optionTheme}" + + updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" + + ;; + + # Option 21/22 + # optionVersion alts --version + # type: Boolean min 0 max 1 + --version) + # shellcheck disable=SC2034 + optionVersion="1" + + if ((options_parse_optionParsedCountOptionVersion >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionVersion)) + optionVersionCallback "${options_parse_arg}" "${optionVersion}" + + ;; + + # Option 22/22 + # optionQuiet alts --quiet|-q + # type: Boolean min 0 max 1 + --quiet | -q) + # shellcheck disable=SC2034 + optionQuiet="1" + + if ((options_parse_optionParsedCountOptionQuiet >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionQuiet)) + optionQuietCallback "${options_parse_arg}" "${optionQuiet}" + + updateArgListQuietCallback "${options_parse_arg}" "${optionQuiet}" + + ;; + + -*) + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" + return 1 + fi + ;; + *) + ((minParsedArgIndex0 = 0)) || true + ((maxParsedArgIndex0 = 0)) || true + ((minParsedArgIndex1 = minParsedArgIndex0 + 1)) || true + ((maxParsedArgIndex1 = maxParsedArgIndex0 + 1)) || true + ((minParsedArgIndex2 = minParsedArgIndex1 + 0)) || true + ((maxParsedArgIndex2 = maxParsedArgIndex1 + 1)) || true + ((incrementArg = 1 )) + if ((0)); then + # Technical if - never reached + : + + # Argument 1/2 - fromDbName + # Argument fromDbName min 1 max 1 + # Argument fromDbName authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && + options_parse_parsedArgIndex < maxParsedArgIndex1 )); then + if ((options_parse_argParsedCountFromDbName >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument - Maximum number of argument occurrences reached(1)" return 1 fi - if ((options_parse_optionParsedCountOptionDisplayLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + ((++options_parse_argParsedCountFromDbName)) + # shellcheck disable=SC2034 + fromDbName="${options_parse_arg}" + + + # Argument 2/2 - targetDbName + # Argument targetDbName min 0 max 1 + # Argument targetDbName authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex1 && + options_parse_parsedArgIndex < maxParsedArgIndex2 )); then + if ((options_parse_argParsedCountTargetDbName >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument - Maximum number of argument occurrences reached(1)" return 1 fi - ((++options_parse_optionParsedCountOptionDisplayLevel)) + ((++options_parse_argParsedCountTargetDbName)) # shellcheck disable=SC2034 - optionDisplayLevel="$1" - optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - ;; - -*) + targetDbName="${options_parse_arg}" + + + # else too much args + else + + if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" + # too much args and no unknownArgumentCallbacks configured + Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" return 1 fi - ;; - *) - if ((0)); then - # Technical if - never reached - : - # Argument 1/2 - # Argument fromDbName min 1 max 1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 0 && options_parse_parsedArgIndex < 1)); then - if ((options_parse_argParsedCountFromDbName >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument fromDbName - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountFromDbName)) - # shellcheck disable=SC2034 - fromDbName="${options_parse_arg}" - # Argument 2/2 - # Argument targetDbName min 0 max 1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 1 && options_parse_parsedArgIndex < 2)); then - if ((options_parse_argParsedCountTargetDbName >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument targetDbName - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountTargetDbName)) - # shellcheck disable=SC2034 - targetDbName="${options_parse_arg}" - else - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" - return 1 - fi - fi + + fi + if ((incrementArg == 1)); then ((++options_parse_parsedArgIndex)) - ;; - esac - shift || true - done - if ((options_parse_argParsedCountFromDbName < 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument 'fromDbName' should be provided at least 1 time(s)" - return 1 - fi - commandOptionParseFinished - initProfileCommandCallback - dbImportCommandCallback - Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" - Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" - elif [[ "${options_parse_cmd}" = "help" ]]; then - Array::wrap2 " " 80 0 "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" "Import source db into target db using eventual table filter" - echo + fi + ;; + esac + shift || true + done - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "${SCRIPT_NAME}" "[OPTIONS]" "[ARGUMENTS]")" - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ - "${SCRIPT_NAME}" \ - "[--profile|-p ]" "[--tables ]" "[--from-dsn|-f ]" "[--skip-schema|-s]" "[--from-aws|-a ]" "[--target-dsn|-t ]" "[--character-set|-c ]" "[--collation-name|-o ]" "[--bash-framework-config ]" "[--config]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--no-color]" "[--theme ]" "[--help|-h]" "[--version]" "[--quiet|-q]" "[--log-level ]" "[--log-file ]" "[--display-level ]")" - echo - echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}fromDbName${__HELP_NORMAL} {single} (mandatory)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(the\ name\ of\ the\ source/remote\ database) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " [${__HELP_OPTION_COLOR}targetDbName${__HELP_NORMAL} {single}]" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(the\ name\ of\ the\ target\ database\,\ use\ fromDbName\(without\ extension\)\ if\ not\ provided) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}PROFILE OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--profile${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-p ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054,SC2206 - mapfile -t helpArray < <(profileOptionHelpCallback) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--tables ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(import\ only\ table\ specified\ in\ the\ list.\ \ If\ aws\ mode\,\ ignore\ profile\ option) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}FROM OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--from-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-f ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=($'dsn to use for source database\n\n this option is incompatible with -a|--from-aws option') - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--skip-schema${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-s${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(avoid\ to\ import\ the\ schema) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--from-aws${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-a ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(db\ dump\ will\ be\ downloaded\ from\ s3\ instead\ of\ using\ remote\ db.\ The\ value\ \\ is\ the\ name\ of\ the\ file\ without\ s3\ location\ \(Only\ .gz\ or\ tar.gz\ file\ are\ supported\).\ This\ option\ is\ incompatible\ with\ -f\|--from-dsn\ option) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}TARGET OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--target-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-t ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(dsn\ to\ use\ for\ target\ database\ \(Default:\ default.local\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--character-set${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-c ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(change\ the\ character\ set\ used\ during\ database\ creation\ \(default\ value:\ utf8\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--collation-name${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-o ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(change\ the\ collation\ name\ used\ during\ database\ creation\ \(default\ value:\ utf8_general_ci\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(use\ alternate\ bash\ framework\ configuration.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ configuration) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(info\ level\ verbose\ mode\ \(alias\ of\ --display-level\ INFO\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(debug\ level\ verbose\ mode\ \(alias\ of\ --display-level\ DEBUG\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(trace\ level\ verbose\ mode\ \(alias\ of\ --display-level\ TRACE\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Load\ the\ specified\ env\ file\ \(deprecated\,\ please\ use\ --bash-framework-config\ option\ instead\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Produce\ monochrome\ output.\ alias\ of\ --theme\ noColor.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' - echo ' Possible values: default|default-force|noColor' - echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ this\ command\ help) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Print\ version\ information\ and\ quit) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(quiet\ mode\,\ doesn\'t\ display\ any\ output) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ file) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(set\ display\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e """ -${__HELP_TITLE}Default profiles directory:${__HELP_NORMAL} -${PROFILES_DIR-configuration error} - -${__HELP_TITLE}User profiles directory:${__HELP_NORMAL} -${HOME_PROFILES_DIR-configuration error} -Allows to override profiles defined in "Default profiles directory" - -${__HELP_TITLE}List of available profiles:${__HELP_NORMAL} -${profilesList} - -${__HELP_TITLE}List of available dsn:${__HELP_NORMAL} -${dsnList} - -${__HELP_TITLE}Aws s3 location:${__HELP_NORMAL} -${S3_BASE_URL} - -${__HELP_TITLE}Example 1: from one database to another one${__HELP_NORMAL} -${__HELP_EXAMPLE}TODO${__HELP_NORMAL} - -${__HELP_TITLE}Example 2: import from S3${__HELP_NORMAL} -${__HELP_EXAMPLE}TODO${__HELP_NORMAL}""" - echo - echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo '2.0' - echo - echo -e "${__HELP_TITLE_COLOR}AUTHOR:${__RESET_COLOR}" - echo '[François Chastanet](https://github.com/fchastanet)' - echo - echo -e "${__HELP_TITLE_COLOR}SOURCE FILE:${__RESET_COLOR}" - echo 'https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/DbImport/dbImport.sh' - echo - echo -e "${__HELP_TITLE_COLOR}LICENSE:${__RESET_COLOR}" - echo 'MIT License' - echo - Array::wrap2 ' ' 76 4 "$(copyrightCallback)" - else - Log::displayError "Command ${SCRIPT_NAME} - Option command invalid: '${options_parse_cmd}'" + + + + + + + + + + + + + + + + + + + + + + if ((options_parse_argParsedCountFromDbName < 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument '' should be provided at least 1 time(s)" return 1 - fi + fi || return $? + + + + commandOptionParseFinished + initProfileCommandCallback + dbImportCommandCallback + } -checkRequirements() { - if [[ "${SKIP_REQUIREMENTS_CHECKS:-0}" = "1" ]]; then - return 0 - fi - local -i failures=0 +# @description display command options and arguments help for dbImportCommand +dbImportCommandHelp() { + Array::wrap2 ' ' 80 0 "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" \ + "Import source db into target db using eventual table filter." + echo echo - Assert::commandExists mysql "sudo apt-get install -y mysql-client" || ((++failures)) - Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" || ((++failures)) - Assert::commandExists mysqldump "sudo apt-get install -y mysql-client" || ((++failures)) - Assert::commandExists pv "sudo apt-get install -y pv" || ((++failures)) - Assert::commandExists gawk "sudo apt-get install -y gawk" || ((++failures)) - Assert::commandExists awk "sudo apt-get install -y gawk" || ((++failures)) - Version::checkMinimal "gawk" "--version" "5.0.1" || ((++failures)) - return "${failures}" -} -optionVersionCallback() { - echo "${SCRIPT_NAME} version 2.0" - checkRequirements - exit 0 -} + # ------------------------------------------ + # usage section + # ------------------------------------------ + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "dbImport [OPTIONS] [ARGUMENTS]" + echo + # ------------------------------------------ + # usage/options section + # ------------------------------------------ + optionsAltList=("[--collation-name|-o <>]" "[--target-dsn|-t <>]" "[--character-set|-c <>]" "[--profile|-p <>]" "[--tables <>]" "[--skip-schema|-s]" "[--from-dsn|-f <>]" "[--from-aws|-a <>]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" + ) + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ + "dbImport" "${optionsAltList[@]}" + echo -optionHelpCallback() { - local profilesList="" - local dsnList="" - dsnList="$(Conf::getMergedList "dsn" "env")" - profilesList="$(Conf::getMergedList "dbImportProfiles" "sh" || true)" + # ------------------------------------------ + # usage/arguments section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - dbImportCommand help | envsubst - checkRequirements - exit 0 -} + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}${__HELP_NORMAL}{single} (mandatory) + " + Array::wrap2 ' ' 76 4 " " "The name of the source/remote database." + echo + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}${__HELP_NORMAL}{single}] + " + Array::wrap2 ' ' 76 4 " " "The name of the target database" "Default value: (without extension)" "" + echo + # ------------------------------------------ + # options section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}TARGET OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--collation-name${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-o ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Change the collation name used during database creation." + echo -dbImportCommandCallback() { - if [[ -z "${targetDbName}" ]]; then - targetDbName="${fromDbName}" - fi - if [[ -n "${optionFromAws}" ]]; then - Assert::commandExists aws \ - "Command ${SCRIPT_NAME} - missing aws, please check https://docs.aws.amazon.com/fr_fr/cli/latest/userguide/install-cliv2.html" || exit 1 - if [[ -n "${optionFromDsn}" ]]; then - Log::fatal "Command ${SCRIPT_NAME} - you cannot use from-dsn and from-aws at the same time" - fi + echo -e " ${__HELP_OPTION_COLOR}--target-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-t ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Dsn to use for target database." + echo - if [[ -z "${S3_BASE_URL}" ]]; then - Log::fatal "Command ${SCRIPT_NAME} - missing S3_BASE_URL, please provide a value in .env file" - fi - elif [[ -z "${optionFromDsn}" ]]; then - # default value for FROM_DSN if from-aws not set - optionFromDsn="" - fi - if [[ -z "${DB_IMPORT_DUMP_DIR}" ]]; then - Log::fatal "Command ${SCRIPT_NAME} -you have to specify a value for DB_IMPORT_DUMP_DIR env variable" - fi + Array::wrap2 ' ' 76 6 " Default value: " "default.local" + echo - if [[ ! -d "${DB_IMPORT_DUMP_DIR}" ]]; then - mkdir -p "${DB_IMPORT_DUMP_DIR}" || - Log::fatal "Command ${SCRIPT_NAME} -impossible to create directory ${DB_IMPORT_DUMP_DIR} specified by DB_IMPORT_DUMP_DIR env variable" - fi + echo -e " ${__HELP_OPTION_COLOR}--character-set${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-c ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Change the character set used during database creation." + echo + + + echo + echo -e "${__HELP_TITLE_COLOR}PROFILE OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--profile${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-p ${__HELP_NORMAL} {single}" + profileOptionHelpFunction + + + + Array::wrap2 ' ' 76 6 " Default value: " "default" + echo + + echo -e " ${__HELP_OPTION_COLOR}--tables ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Import only table specified in the list." "If aws mode, ignore profile option." "" + echo + + + echo + echo -e "${__HELP_TITLE_COLOR}SOURCE OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--skip-schema${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-s${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Avoids to import the schema." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--from-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-f ${__HELP_NORMAL} {single}" + dsnHelpFunction + + + + + echo -e " ${__HELP_OPTION_COLOR}--from-aws${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-a ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "db dump will be downloaded from s3 instead of using remote db." "The value is the name of the file without s3 location" "(Only .gz or tar.gz file are supported)." "This option is incompatible with -f|--from-dsn option." "" + echo + + + echo + echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays this command help" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays configuration" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config bash-framework-config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--env-file env-file${__HELP_NORMAL} {list} (optional)" + Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--log-level log-level${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--log-file log-file${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log file" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--display-level display-level${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set display level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--theme theme${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." + echo + Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" + echo + + Array::wrap2 ' ' 76 6 " Default value: " "default" + echo + + echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Print version information and quit." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." + echo + + + # ------------------------------------------ + # version section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" + echo "2.0" + # ------------------------------------------ + # author section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}AUTHOR: ${__RESET_COLOR}" + echo "[François Chastanet](https://github.com/fchastanet)" + # ------------------------------------------ + # sourceFile section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImport/binary-dbImport.yaml" + # ------------------------------------------ + # license section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}LICENSE: ${__RESET_COLOR}" + echo "MIT License" + # ------------------------------------------ + # copyright section + # ------------------------------------------ + Array::wrap2 ' ' 76 0 "$(copyrightCallback)" } -dbImportCommand parse "${BASH_FRAMEWORK_ARGV[@]}" + +beforeParseCallback + +dbImportCommandParse "$@" +MAIN_FUNCTION_NAME="main" +main() { + +Linux::requireTarCommand +Compiler::Embed::extractFileFromBase64 \ + "${PERSISTENT_TMPDIR:-/tmp}/95e2e89e049fcfcd74a8b75390f067bf/dumpSizeQuery" \ + "U0VMRUNUIFJPVU5EKFNVTShkYXRhX2xlbmd0aCArIGluZGV4X2xlbmd0aCkgLyAxMDI0IC8gMTAyNCwgMCkgQVMgc2l6ZQpGUk9NIGluZm9ybWF0aW9uX3NjaGVtYS5UQUJMRVMgV0hFUkUgdGFibGVfc2NoZW1hPScke2Zyb21EYk5hbWV9JwpBTkQgdGFibGVfbmFtZSBJTigke2xpc3RUYWJsZXNEdW1wU2l6ZX0sICdkdW1teScpCkdST1VQIEJZIHRhYmxlX3NjaGVtYQo=" \ + "644" + +declare -gx embed_file_dumpSizeQuery="${PERSISTENT_TMPDIR:-/tmp}/95e2e89e049fcfcd74a8b75390f067bf/dumpSizeQuery" + + +declare TIMEFORMAT='time spent : %3R' +declare DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR%/} +declare PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbImportProfiles" +declare HOME_PROFILES_DIR="${HOME}/.bash-tools/dbImportProfiles" +declare DOWNLOAD_DUMP=0 # dump header/footer read -r -d '\0' DUMP_HEADER <<-EOM @@ -2426,190 +2755,186 @@ read -r -d '\0' DUMP_FOOTER <<-EOM2 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;\0 EOM2 -declare DUMP_SIZE_QUERY -DUMP_SIZE_QUERY="$( - cat <<'EOF' -SELECT ROUND(SUM(data_length + index_length) / 1024 / 1024, 0) AS size -FROM information_schema.TABLES WHERE table_schema='${fromDbName}' -AND table_name IN(${listTablesDumpSize}, 'dummy') -GROUP BY table_schema -EOF -)" - -# @require Linux::requireExecutedAsUser -run() { - # create db instances - declare -Agx dbFromInstance dbTargetDatabase +# create db instances +declare -Agx dbFromInstance dbTargetDatabase +# shellcheck disable=SC2154 +Database::newInstance dbTargetDatabase "${optionTargetDsn}" +# shellcheck disable=SC2154 +Database::setQueryOptions dbTargetDatabase "${dbTargetDatabase[QUERY_OPTIONS]} --connect-timeout=5" +Log::displayInfo "Using target dsn ${dbTargetDatabase['DSN_FILE']}" +if [[ -z "${optionFromAws}" ]]; then # shellcheck disable=SC2154 - Database::newInstance dbTargetDatabase "${optionTargetDsn}" + Database::newInstance dbFromInstance "${optionFromDsn}" + Database::setQueryOptions dbFromInstance "${dbFromInstance[QUERY_OPTIONS]} --connect-timeout=5" + Log::displayInfo "Using from dsn ${dbFromInstance['DSN_FILE']}" +fi + +declare remoteDbDumpTempFile +declare remoteDbStructureDumpTempFile +if [[ -n "${optionFromAws}" ]]; then + remoteDbDumpTempFile="${DB_IMPORT_DUMP_DIR}/${optionFromAws}" +else # shellcheck disable=SC2154 - Database::setQueryOptions dbTargetDatabase "${dbTargetDatabase[QUERY_OPTIONS]} --connect-timeout=5" - Log::displayInfo "Using target dsn ${dbTargetDatabase['DSN_FILE']}" - if [[ -z "${optionFromAws}" ]]; then - # shellcheck disable=SC2154 - Database::newInstance dbFromInstance "${optionFromDsn}" - Database::setQueryOptions dbFromInstance "${dbFromInstance[QUERY_OPTIONS]} --connect-timeout=5" - Log::displayInfo "Using from dsn ${dbFromInstance['DSN_FILE']}" - fi + remoteDbDumpTempFile="${DB_IMPORT_DUMP_DIR}/${fromDbName}_${optionProfile}.sql.gz" + remoteDbStructureDumpTempFile="${DB_IMPORT_DUMP_DIR}/${fromDbName}_${optionProfile}_structure.sql.gz" +fi + +# check if local dump exists +declare downloadDump=0 +if [[ ! -f "${remoteDbDumpTempFile}" ]]; then + Log::displayInfo "local dump does not exist" + downloadDump=1 +fi +if [[ -z "${optionFromAws}" && ! -f "${remoteDbStructureDumpTempFile}" ]]; then + Log::displayInfo "local structure dump does not exist" + downloadDump=1 +fi +if [[ "${downloadDump}" = "0" ]]; then + Log::displayInfo "local dump ${remoteDbDumpTempFile} already exists, avoid download" +fi + +# shellcheck disable=SC2154 +Log::displayInfo "tables list will calculated using profile ${optionProfile} => ${profileCommandFile}" +SECONDS=0 +if [[ "${downloadDump}" = "1" ]]; then + Log::displayInfo "Download dump" - local remoteDbDumpTempFile - local remoteDbStructureDumpTempFile if [[ -n "${optionFromAws}" ]]; then - remoteDbDumpTempFile="${DB_IMPORT_DUMP_DIR}/${optionFromAws}" + # download dump from s3 + declare s3Url="${S3_BASE_URL%/}/${optionFromAws}" + aws s3 ls --human-readable "${s3Url}" || { + Log::fatal "Command ${SCRIPT_NAME} - unable to get information on S3 object : ${s3Url}" + } + Log::displayInfo "Download dump from ${s3Url} ..." + TMPDIR="${TMDIR:-/tmp}" aws s3 cp "${s3Url}" "${remoteDbDumpTempFile}" || { + Log::fatal "Command ${SCRIPT_NAME} - unable to download dump from S3 : ${s3Url}" + } else - # shellcheck disable=SC2154 - remoteDbDumpTempFile="${DB_IMPORT_DUMP_DIR}/${fromDbName}_${optionProfile}.sql.gz" - remoteDbStructureDumpTempFile="${DB_IMPORT_DUMP_DIR}/${fromDbName}_${optionProfile}_structure.sql.gz" - fi - - # check if local dump exists - local downloadDump=0 - if [[ ! -f "${remoteDbDumpTempFile}" ]]; then - Log::displayInfo "local dump does not exist" - downloadDump=1 - fi - if [[ -z "${optionFromAws}" && ! -f "${remoteDbStructureDumpTempFile}" ]]; then - Log::displayInfo "local structure dump does not exist" - downloadDump=1 - fi - if [[ "${downloadDump}" = "0" ]]; then - Log::displayInfo "local dump ${remoteDbDumpTempFile} already exists, avoid download" - fi - - Log::displayInfo "tables list will calculated using profile ${optionProfile} => ${profileCommandFile}" - SECONDS=0 - if [[ "${downloadDump}" = "1" ]]; then - Log::displayInfo "Download dump" - - if [[ -n "${optionFromAws}" ]]; then - # download dump from s3 - local s3Url="${S3_BASE_URL%/}/${optionFromAws}" - aws s3 ls --human-readable "${s3Url}" || { - Log::fatal "Command ${SCRIPT_NAME} - unable to get information on S3 object : ${s3Url}" - } - Log::displayInfo "Download dump from ${s3Url} ..." - TMPDIR="${TMDIR:-/tmp}" aws s3 cp "${s3Url}" "${remoteDbDumpTempFile}" || { - Log::fatal "Command ${SCRIPT_NAME} - unable to download dump from S3 : ${s3Url}" - } - else - # check if remote db exists - Database::ifDbExists dbFromInstance "${fromDbName}" || { - Log::fatal "Command ${SCRIPT_NAME} - Remote Database ${fromDbName} does not exist" - } - - initializeDefaultTargetMysqlOptions dbFromInstance "${fromDbName}" - - local dumpHeader - dumpHeader=$(printf "%s\nSET names '%s';\n" "${DUMP_HEADER}" "${optionCharacterSet}") - - # calculate remote db dump size - local listTables - local listTablesDumpSize - local listTablesDump - listTables="$(Database::query dbFromInstance "show tables" "${fromDbName}" | ${profileCommandFile} | sort)" - # shellcheck disable=SC2034 # used by DUMP_SIZE_QUERY - listTablesDumpSize="$(echo "${listTables}" | awk -v d="," -v q="'" '{s=(NR==1?s:s d)q $0 q}END{print s }')" - listTablesDump=$(echo "${listTables}" | awk -v d=" " -v q="" '{s=(NR==1?s:s d)q $0 q}END{print s }') - - Log::displayInfo "Calculate dump size for tables ${listTablesDump}" - local remoteDbDumpSize - remoteDbDumpSize=$(echo "${DUMP_SIZE_QUERY}" | envsubst | Database::query dbFromInstance) - if [[ -z "${remoteDbDumpSize}" ]]; then - # could occur with the none profile - remoteDbDumpSize="0" - fi + # check if remote db exists + Database::ifDbExists dbFromInstance "${fromDbName}" || { + Log::fatal "Command ${SCRIPT_NAME} - Remote Database ${fromDbName} does not exist" + } - # dump db - Log::displayInfo "Dump the database ${fromDbName} (Size:${remoteDbDumpSize}MB) ..." - local dumpSizePvEstimation - dumpSizePvEstimation=$(awk "BEGIN {printf \"%.0f\",${remoteDbDumpSize}/1.5}") - time ( - echo "${dumpHeader}" - Database::dump dbFromInstance "${fromDbName}" "${listTablesDump}" \ - --no-create-info --skip-add-drop-table --single-transaction=TRUE | - pv --progress --size "${dumpSizePvEstimation}m" - echo "${DUMP_FOOTER}" - ) | gzip >"${remoteDbDumpTempFile}" - - Log::displayInfo "Dump structure of the database ${fromDbName} ..." - time ( - echo "${dumpHeader}" - #shellcheck disable=SC2016 - Database::dump dbFromInstance "${fromDbName}" "" \ - --no-data --skip-add-drop-table --single-transaction=TRUE | - sed 's/^CREATE TABLE `/CREATE TABLE IF NOT EXISTS `/g' - echo "${DUMP_FOOTER}" - ) | gzip >"${remoteDbStructureDumpTempFile}" - fi - Log::displayInfo "Dump done." - fi + initializeDefaultTargetMysqlOptions dbFromInstance "${fromDbName}" - # mark dumps as modified now to avoid them to be garbage collected - touch -c -m "${remoteDbDumpTempFile}" || true - touch -c -m "${remoteDbStructureDumpTempFile}" || true + declare dumpHeader + dumpHeader=$(printf "%s\nSET names '%s';\n" "${DUMP_HEADER}" "${optionCharacterSet}") - # TODO Collation and character set should be retrieved from dump files if possible - # shellcheck disable=SC2154 - local targetCollationName="${optionCollationName:-${defaultTargetCollationName}}" - # shellcheck disable=SC2154 - local targetCharacterSet="${optionCharacterSet:-${defaultTargetCharacterSet}}" + # calculate remote db dump size + declare listTables + declare listTablesDumpSize + declare listTablesDump + listTables="$(Database::query dbFromInstance "show tables" "${fromDbName}" | ${profileCommandFile} | sort)" + # shellcheck disable=SC2034 # used by embed_file_dumpSizeQuery + listTablesDumpSize="$(echo "${listTables}" | awk -v d="," -v q="'" '{s=(NR==1?s:s d)q $0 q}END{print s }')" + listTablesDump=$(echo "${listTables}" | awk -v d=" " -v q="" '{s=(NR==1?s:s d)q $0 q}END{print s }') - # shellcheck disable=SC2154 - Log::displayInfo "create target database ${targetDbName} if needed" - #shellcheck disable=SC2016 - Database::query dbTargetDatabase \ - "$(printf 'CREATE DATABASE IF NOT EXISTS `%s` CHARACTER SET "%s" COLLATE "%s"' "${targetDbName}" "${targetCharacterSet}" "${targetCollationName}")" - - if [[ -z "${optionFromAws}" ]]; then - Database::setQueryOptions dbTargetDatabase "${dbTargetDatabase['DB_IMPORT_OPTIONS']}" - Log::displayInfo "Importing remote db '${fromDbName}' to local db '${targetDbName}'" + Log::displayInfo "Calculate dump size for tables ${listTablesDump}" + declare remoteDbDumpSize # shellcheck disable=SC2154 - if [[ "${optionSkipSchema}" = "1" ]]; then - Log::displayInfo "avoid to create db structure" - else - Log::displayInfo "create db structure from ${remoteDbStructureDumpTempFile}" - # shellcheck disable=SC2034 - local status=0 - # shellcheck disable=SC2034 - local -a pipeStatus=() - time ( - pv "${remoteDbStructureDumpTempFile}" | zcat | - Database::query dbTargetDatabase "" "${targetDbName}" || Bash::handlePipelineFailure status pipeStatus - ) + remoteDbDumpSize="$( + export listTablesDumpSize fromDbName + envsubst <"${embed_file_dumpSizeQuery}" | Database::query dbFromInstance + )" + if [[ -z "${remoteDbDumpSize}" ]]; then + # could occur with the none profile + remoteDbDumpSize="0" fi - fi - Log::displayInfo "import remote to local from file ${remoteDbDumpTempFile}" - local -a dbImportStreamOptions=( - --profile "${optionProfile}" - --target-dsn "${optionTargetDsn}" - --character-set "${targetCharacterSet}" - ) - if [[ -n "${optionTables:-}" ]]; then - dbImportStreamOptions+=( - --tables "${optionTables}" + + # dump db + Log::displayInfo "Dump the database ${fromDbName} (Size:${remoteDbDumpSize}MB) ..." + declare dumpSizePvEstimation + dumpSizePvEstimation=$(awk "BEGIN {printf \"%.0f\",${remoteDbDumpSize}/1.5}") + time ( + echo "${dumpHeader}" + Database::dump dbFromInstance "${fromDbName}" "${listTablesDump}" \ + --no-create-info --skip-add-drop-table --single-transaction=TRUE | + pv --progress --size "${dumpSizePvEstimation}m" + echo "${DUMP_FOOTER}" + ) | gzip >"${remoteDbDumpTempFile}" + + Log::displayInfo "Dump structure of the database ${fromDbName} ..." + time ( + echo "${dumpHeader}" + #shellcheck disable=SC2016 + Database::dump dbFromInstance "${fromDbName}" "" \ + --no-data --skip-add-drop-table --single-transaction=TRUE | + sed 's/^CREATE TABLE `/CREATE TABLE IF NOT EXISTS `/g' + echo "${DUMP_FOOTER}" + ) | gzip >"${remoteDbStructureDumpTempFile}" + fi + Log::displayInfo "Dump done." +fi + +# mark dumps as modified now to avoid them to be garbage collected +touch -c -m "${remoteDbDumpTempFile}" || true +touch -c -m "${remoteDbStructureDumpTempFile}" || true + +# TODO Collation and character set should be retrieved from dump files if possible +# shellcheck disable=SC2154 +declare targetCollationName="${optionCollationName:-${defaultTargetCollationName}}" +# shellcheck disable=SC2154 +declare targetCharacterSet="${optionCharacterSet:-${defaultTargetCharacterSet}}" + +# shellcheck disable=SC2154 +Log::displayInfo "create target database ${targetDbName} if needed" +#shellcheck disable=SC2016 +Database::query dbTargetDatabase \ + "$(printf 'CREATE DATABASE IF NOT EXISTS `%s` CHARACTER SET "%s" COLLATE "%s"' "${targetDbName}" "${targetCharacterSet}" "${targetCollationName}")" + +if [[ -z "${optionFromAws}" ]]; then + Database::setQueryOptions dbTargetDatabase "${dbTargetDatabase['DB_IMPORT_OPTIONS']}" + Log::displayInfo "Importing remote db '${fromDbName}' to local db '${targetDbName}'" + # shellcheck disable=SC2154 + if [[ "${optionSkipSchema}" = "1" ]]; then + Log::displayInfo "avoid to create db structure" + else + Log::displayInfo "create db structure from ${remoteDbStructureDumpTempFile}" + # shellcheck disable=SC2034 + declare status=0 + # shellcheck disable=SC2034 + declare -a pipeStatus=() + time ( + pv "${remoteDbStructureDumpTempFile}" | zcat | + Database::query dbTargetDatabase "" "${targetDbName}" || Bash::handlePipelineFailure status pipeStatus ) fi - time ( - "${CURRENT_DIR}/dbImportStream" \ - "${dbImportStreamOptions[@]}" \ - "${remoteDbDumpTempFile}" \ - "${targetDbName}" - +fi +Log::displayInfo "import remote to local from file ${remoteDbDumpTempFile}" +declare -a dbImportStreamOptions=( + --profile "${optionProfile}" + --target-dsn "${optionTargetDsn}" + --character-set "${targetCharacterSet}" +) +if [[ -n "${optionTables:-}" ]]; then + dbImportStreamOptions+=( + --tables "${optionTables}" ) +fi +time ( + "${CURRENT_DIR}/dbImportStream" \ + "${dbImportStreamOptions[@]}" \ + "${remoteDbDumpTempFile}" \ + "${targetDbName}" - # garbage collect db import dumps - File::garbageCollect "${DB_IMPORT_DUMP_DIR}" "${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30}" || true +) - Log::displayInfo "Import database duration : $(date -u -d "@${SECONDS}" +"%T")" -} +# garbage collect db import dumps +File::garbageCollect "${DB_IMPORT_DUMP_DIR}" "${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30}" || true -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run -fi +Log::displayInfo "Import database duration : $(date -u -d "@${SECONDS}" +"%T")" } -facade_main_dbImportsh "$@" +# if file is sourced avoid calling main function +# shellcheck disable=SC2178 +BASH_SOURCE=".$0" # cannot be changed in bash +# shellcheck disable=SC2128 +if test ".$0" == ".${BASH_SOURCE}"; then + if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then + main "$@" &>/dev/null + else + main "$@" + fi +fi diff --git a/bin/doc b/bin/doc index 3893c38b..06146676 100755 --- a/bin/doc +++ b/bin/doc @@ -1762,12 +1762,15 @@ docCommandParse() { Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" return 1 fi - - ((++options_parse_parsedArgIndex)) + ((incrementArg = 0)) + if ((incrementArg == 1)); then + ((++options_parse_parsedArgIndex)) + fi ;; esac shift || true done || return $? + commandOptionParseFinished } diff --git a/bin/install b/bin/install index 9942a834..b8ff79bf 100755 --- a/bin/install +++ b/bin/install @@ -1472,12 +1472,15 @@ installCommandParse() { Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" return 1 fi - - ((++options_parse_parsedArgIndex)) + ((incrementArg = 0)) + if ((incrementArg == 1)); then + ((++options_parse_parsedArgIndex)) + fi ;; esac shift || true done || return $? + commandOptionParseFinished } diff --git a/bin/installRequirements b/bin/installRequirements index 669836f9..8f8be3c8 100755 --- a/bin/installRequirements +++ b/bin/installRequirements @@ -1475,12 +1475,15 @@ installRequirementsCommandParse() { Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" return 1 fi - - ((++options_parse_parsedArgIndex)) + ((incrementArg = 0)) + if ((incrementArg == 1)); then + ((++options_parse_parsedArgIndex)) + fi ;; esac shift || true done || return $? + commandOptionParseFinished } diff --git a/bin/mysql2puml b/bin/mysql2puml index 535deff1..037f77bd 100755 --- a/bin/mysql2puml +++ b/bin/mysql2puml @@ -1256,6 +1256,15 @@ longDescriptionFunction() { Conf::getMergedList "mysql2pumlSkins" ".puml" " - " } +# shellcheck disable=SC2317 +inputSqlFileCallback() { + # shellcheck disable=SC2154 + if [[ ! -f "${inputSqlFile}" ]]; then + Log::displayError "${SCRIPT_NAME} - File '${inputSqlFile}' does not exists" + return 1 + fi +} + optionSkinCallback() { declare -a skinList readarray -t skinList < <(Conf::getMergedList "mysql2pumlSkins" ".puml" "") @@ -1666,16 +1675,20 @@ mysql2pumlCommandParse() { fi ;; *) + ((minParsedArgIndex0 = 0)) || true + ((maxParsedArgIndex0 = 0)) || true + ((minParsedArgIndex1 = minParsedArgIndex0 + 0)) || true + ((maxParsedArgIndex1 = maxParsedArgIndex0 + 1)) || true + ((incrementArg = 1 )) if ((0)); then # Technical if - never reached : - # Argument 1/1 - # inputSqlFile min 0 max 1 - # authorizedValues: - elif (( options_parse_parsedArgIndex >= 0 && - options_parse_parsedArgIndex < 1 )); then - + # Argument 1/1 - inputSqlFile + # Argument inputSqlFile min 0 max 1 + # Argument inputSqlFile authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && + options_parse_parsedArgIndex < maxParsedArgIndex1 )); then if ((options_parse_argParsedCountInputSqlFile >= 1 )); then Log::displayError "Command ${SCRIPT_NAME} - Argument sqlFile - Maximum number of argument occurrences reached(1)" return 1 @@ -1683,11 +1696,13 @@ mysql2pumlCommandParse() { ((++options_parse_argParsedCountInputSqlFile)) # shellcheck disable=SC2034 inputSqlFile="${options_parse_arg}" - optionSkinCallback "${inputSqlFile}" -- "${@:2}" + inputSqlFileCallback "${inputSqlFile}" -- "${@:2}" + # else too much args else + if [[ "${argOptDefaultBehavior}" = "0" ]]; then # too much args and no unknownArgumentCallbacks configured Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" @@ -1695,10 +1710,14 @@ mysql2pumlCommandParse() { fi fi + if ((incrementArg == 1)); then + ((++options_parse_parsedArgIndex)) + fi ;; esac shift || true done || return $? + commandOptionParseFinished } diff --git a/conf/dsn/default.local.env b/conf/dsn/default.local.env index 4436c373..ccb2e265 100644 --- a/conf/dsn/default.local.env +++ b/conf/dsn/default.local.env @@ -7,7 +7,7 @@ USER=root # you can override default MYSQL options if you wish # SSL_OPTIONS=--ssl-mode=DISABLED # QUERY_OPTIONS=--batch --raw --default-character-set=utf8 -# DUMP_OPTIONS=--default-character-set=utf8 --compress --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 +# DUMP_OPTIONS=--default-character-set=utf8 --compression-algorithms --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 # specific options during import DB_IMPORT_OPTIONS= diff --git a/conf/dsn/default.remote.env b/conf/dsn/default.remote.env index f00fa8a4..1958d45e 100644 --- a/conf/dsn/default.remote.env +++ b/conf/dsn/default.remote.env @@ -6,7 +6,7 @@ USER=root # you can override default MYSQL options if you wish # SSL_OPTIONS=--ssl-mode=DISABLED # QUERY_OPTIONS=--batch --raw --default-character-set=utf8 -# DUMP_OPTIONS=--default-character-set=utf8 --compress --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 +# DUMP_OPTIONS=--default-character-set=utf8 --compression-algorithms --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 # specific options during import DB_IMPORT_OPTIONS= diff --git a/conf/dsn/localhost-root.env b/conf/dsn/localhost-root.env index f00fa8a4..1958d45e 100644 --- a/conf/dsn/localhost-root.env +++ b/conf/dsn/localhost-root.env @@ -6,7 +6,7 @@ USER=root # you can override default MYSQL options if you wish # SSL_OPTIONS=--ssl-mode=DISABLED # QUERY_OPTIONS=--batch --raw --default-character-set=utf8 -# DUMP_OPTIONS=--default-character-set=utf8 --compress --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 +# DUMP_OPTIONS=--default-character-set=utf8 --compression-algorithms --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 # specific options during import DB_IMPORT_OPTIONS= diff --git a/src/BashTools/Conf/requireLoad.sh b/src/BashTools/Conf/requireLoad.sh index 9493ee08..0a34b868 100755 --- a/src/BashTools/Conf/requireLoad.sh +++ b/src/BashTools/Conf/requireLoad.sh @@ -1,21 +1,38 @@ #!/bin/bash -bashToolsDefaultConfigTemplate="${bashToolsDefaultConfigTemplate:-$( - cat <<'EOF' -.INCLUDE "${BASH_TOOLS_ROOT_DIR}/conf/.env" -EOF -)}" - # @description loads ~/.bash-tools/.env if available # if not creates it from a default template # else check if new options need to be added BashTools::Conf::requireLoad() { + # @embed "${BASH_TOOLS_ROOT_DIR}/conf/.env" as bashToolsDefaultConfigTemplate + BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" + if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then + FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" + else + # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR + FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" + fi + # shellcheck disable=SC2034 + FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" + # shellcheck disable=SC2034 + FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + + if [[ -f "${HOME}/.bash-tools/.env" ]]; then + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") + fi + local envFile="${HOME}/.bash-tools/.env" if [[ ! -f "${envFile}" ]]; then mkdir -p "${HOME}/.bash-tools" ( echo "#!/usr/bin/env bash" - echo "${bashToolsDefaultConfigTemplate}" + # shellcheck disable=SC2154 + echo "${embed_file_bashToolsDefaultConfigTemplate}" ) >"${envFile}" Log::displayInfo "Configuration file '${envFile}' created" else diff --git a/src/_includes/dbTools.requirements.tpl b/src/Db/checkRequirements.sh old mode 100644 new mode 100755 similarity index 83% rename from src/_includes/dbTools.requirements.tpl rename to src/Db/checkRequirements.sh index f44ce488..e15f70c0 --- a/src/_includes/dbTools.requirements.tpl +++ b/src/Db/checkRequirements.sh @@ -1,4 +1,8 @@ -checkRequirements() { +#!/usr/bin/env bash + +# @description check if all requirements are satisfied +# to execute dbImport commands +Db::checkRequirements() { if [[ "${SKIP_REQUIREMENTS_CHECKS:-0}" = "1" ]]; then return 0 fi @@ -13,9 +17,3 @@ checkRequirements() { Version::checkMinimal "gawk" "--version" "5.0.1" || ((++failures)) return "${failures}" } - -optionVersionCallback() { - echo "${SCRIPT_NAME} version <% ${versionNumber} %>" - checkRequirements - exit 0 -} diff --git a/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml b/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml index 476014cb..1c3cfd98 100644 --- a/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml +++ b/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml @@ -39,6 +39,6 @@ binData: max: 1 help: Sql filepath to parse (read from stdin if not provided). callbacks: - - optionSkinCallback + - inputSqlFileCallback name: sqlFile variableName: inputSqlFile diff --git a/src/_binaries/Converters/mysql2puml/mysql2puml-options.sh b/src/_binaries/Converters/mysql2puml/mysql2puml-options.sh index 1948c8ea..f5d4da23 100755 --- a/src/_binaries/Converters/mysql2puml/mysql2puml-options.sh +++ b/src/_binaries/Converters/mysql2puml/mysql2puml-options.sh @@ -29,6 +29,15 @@ longDescriptionFunction() { Conf::getMergedList "mysql2pumlSkins" ".puml" " - " } +# shellcheck disable=SC2317 +inputSqlFileCallback() { + # shellcheck disable=SC2154 + if [[ ! -f "${inputSqlFile}" ]]; then + Log::displayError "${SCRIPT_NAME} - File '${inputSqlFile}' does not exists" + return 1 + fi +} + optionSkinCallback() { declare -a skinList readarray -t skinList < <(Conf::getMergedList "mysql2pumlSkins" ".puml" "") diff --git a/src/_binaries/Converters/mysql2puml/mysql2puml.bats b/src/_binaries/Converters/mysql2puml/mysql2puml.bats index 697edab2..8a3151b9 100755 --- a/src/_binaries/Converters/mysql2puml/mysql2puml.bats +++ b/src/_binaries/Converters/mysql2puml/mysql2puml.bats @@ -1,14 +1,13 @@ #!/usr/bin/env bash # shellcheck source=src/batsHeaders.sh -source "$(cd "${BATS_TEST_DIRNAME}/../.." && pwd)/batsHeaders.sh" +source "$(cd "${BATS_TEST_DIRNAME}/../../.." && pwd -P)/batsHeaders.sh" setup() { export TMPDIR="${BATS_TEST_TMPDIR}" export HOME="${BATS_TEST_TMPDIR}/home" - mkdir -p \ - "${HOME}/.bash-tools/mysql2pumlSkins" + mkdir -p "${HOME}/.bash-tools/mysql2pumlSkins" cp "${BATS_TEST_DIRNAME}/testsData/mysql2pumlSkins/"* "${HOME}/.bash-tools/mysql2pumlSkins/" cp "${BATS_TEST_DIRNAME}/testsData/.env" "${HOME}/.bash-tools/.env" diff --git a/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt b/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt index fff715e4..dd7e5764 100644 --- a/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt +++ b/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt @@ -1,74 +1,79 @@ -DESCRIPTION: convert mysql dump sql schema to plantuml format +SYNOPSIS: Converts mysql dump sql schema to plantuml format. + USAGE: mysql2puml [OPTIONS] [ARGUMENTS] -USAGE: mysql2puml [--skin ] - [--bash-framework-config ] [--config] [--verbose|-v] [-vv] [-vvv] - [--env-file ] [--no-color] [--theme ] [--help|-h] [--version] - [--quiet|-q] [--log-level ] [--log-file ] - [--display-level ] +USAGE: mysql2puml [--help|-h] [--config] + [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] + [--env-file ] [--log-level ] [--log-file ] + [--display-level ] [--no-color] [--theme ] [--version] + [--quiet|-q] [--skin <>] ARGUMENTS: - [inputSqlFile {single}] - sql filepath to parse (read from stdin if not provided) - -OPTIONS: - --skin  {single} - header configuration of the plant uml file - Default value: default + [sqlFile{single}] + Sql filepath to parse (read from stdin if not provided). GLOBAL OPTIONS: - --bash-framework-config  {single} - use alternate bash framework configuration. + --help, -h {single} + Displays this command help --config {single} - Display configuration + Displays configuration + --bash-framework-config bash-framework-config {single} + Use alternate bash framework configuration. --verbose, -v {single} - info level verbose mode (alias of --display-level INFO) + Info level verbose mode (alias of --display-level INFO) -vv {single} - debug level verbose mode (alias of --display-level DEBUG) + Debug level verbose mode (alias of --display-level DEBUG) -vvv {single} - trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-config - option instead) + Trace level verbose mode (alias of --display-level TRACE) + --env-file env-file {list} (optional) + Load the specified env file (deprecated, please use --bash-framework-con + fig option instead) + --log-level log-level {single} + Set log level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --log-file log-file {single} + Set log file + --display-level display-level {single} + Set display level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE --no-color {single} Produce monochrome output. alias of --theme noColor. - --theme  {single} - choose color theme - default-force means colors will be produced even if com - mand is piped + --theme theme {single} + Choose color theme - default-force means colors will be produced even if + command is piped. + Possible values: default, default-force, noColor Default value: default - Possible values: default|default-force|noColor - --help, -h {single} - Display this command help --version {single} - Print version information and quit + Print version information and quit. --quiet, -q {single} - quiet mode, doesn't display any output - --log-level  {single} - Set log level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - --log-file  {single} - Set log file - --display-level  {single} - set display level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + Quiet mode, doesn't display any output. +OPTIONS: + --skin  {single} + Header configuration of the plantuml file. + Default value: default + + +DESCRIPTION: EXAMPLE 1: mysql2puml dump.dql EXAMPLE 2: -mysqldump --skip-add-drop-table --skip-add-locks --skip-disable-keys --skip-set-charset --user=root --password=root --no-data skills | mysql2puml +mysqldump --skip-add-drop-table \ + --skip-add-locks \ + --skip-disable-keys \ + --skip-set-charset \ + --user=root \ + --password=root \ + --no-data skills | mysql2puml  LIST OF AVAILABLE SKINS: - default VERSION: 1.0 -AUTHOR: -[François Chastanet](https://github.com/fchastanet) - -SOURCE FILE: -https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Converters/mysql2puml.sh +AUTHOR: [François Chastanet](https://github.com/fchastanet) -LICENSE: -MIT License +SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml +LICENSE: MIT License Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Database/dbImport/binary-dbImport.yaml b/src/_binaries/Database/dbImport/binary-dbImport.yaml new file mode 100644 index 00000000..ca17643a --- /dev/null +++ b/src/_binaries/Database/dbImport/binary-dbImport.yaml @@ -0,0 +1,48 @@ +extends: + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsMysqlCollationName.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsMysqlTarget.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsProfile.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsMysqlSource.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" + +vars: + SRC_FILE_PATH: src/_binaries/Database/dbImport/binary-dbImport.yaml + +compilerConfig: + targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/dbImport" + relativeRootDirBasedOnTargetDir: .. + srcDirs: + - ${BASH_TOOLS_ROOT_DIR}/src +binData: + commands: + default: + functionName: dbImportCommand + version: "2.0" + commandName: dbImport + beforeParseCallbacks: + - beforeParseCallback + callbacks: + - dbImportCommandCallback@100 + definitionFiles: + 20: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbImport/dbImport-options.sh + #99: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.sh + mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbImport/dbImport-main.sh + help: Import source db into target db using eventual table filter. + args: + - help: The name of the source/remote database. + type: String + min: 1 + max: 1 + name: + variableName: fromDbName + + - help: | + The name of the target database + Default value: (without extension) + type: String + min: 0 + max: 1 + name: + variableName: targetDbName diff --git a/src/_binaries/Database/dbImport/dbImport-main.sh b/src/_binaries/Database/dbImport/dbImport-main.sh new file mode 100755 index 00000000..08b69b22 --- /dev/null +++ b/src/_binaries/Database/dbImport/dbImport-main.sh @@ -0,0 +1,192 @@ +#!/usr/bin/env bash +# @embed "${BASH_TOOLS_ROOT_DIR}/src/_binaries/DbImport/dumpSizeQuery.sql" AS dumpSizeQuery + +declare TIMEFORMAT='time spent : %3R' +declare DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR%/} +declare PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbImportProfiles" +declare HOME_PROFILES_DIR="${HOME}/.bash-tools/dbImportProfiles" +declare DOWNLOAD_DUMP=0 + +# dump header/footer +read -r -d '\0' DUMP_HEADER <<-EOM + SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS = 0; + SET @OLD_AUTOCOMMIT=@@AUTOCOMMIT, AUTOCOMMIT = 0; + SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS = 0;\0 +EOM + +read -r -d '\0' DUMP_FOOTER <<-EOM2 + COMMIT; + SET AUTOCOMMIT=@OLD_AUTOCOMMIT; + SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; + SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;\0 +EOM2 + +# create db instances +declare -Agx dbFromInstance dbTargetDatabase + +# shellcheck disable=SC2154 +Database::newInstance dbTargetDatabase "${optionTargetDsn}" +# shellcheck disable=SC2154 +Database::setQueryOptions dbTargetDatabase "${dbTargetDatabase[QUERY_OPTIONS]} --connect-timeout=5" +Log::displayInfo "Using target dsn ${dbTargetDatabase['DSN_FILE']}" +if [[ -z "${optionFromAws}" ]]; then + # shellcheck disable=SC2154 + Database::newInstance dbFromInstance "${optionFromDsn}" + Database::setQueryOptions dbFromInstance "${dbFromInstance[QUERY_OPTIONS]} --connect-timeout=5" + Log::displayInfo "Using from dsn ${dbFromInstance['DSN_FILE']}" +fi + +declare remoteDbDumpTempFile +declare remoteDbStructureDumpTempFile +if [[ -n "${optionFromAws}" ]]; then + remoteDbDumpTempFile="${DB_IMPORT_DUMP_DIR}/${optionFromAws}" +else + # shellcheck disable=SC2154 + remoteDbDumpTempFile="${DB_IMPORT_DUMP_DIR}/${fromDbName}_${optionProfile}.sql.gz" + remoteDbStructureDumpTempFile="${DB_IMPORT_DUMP_DIR}/${fromDbName}_${optionProfile}_structure.sql.gz" +fi + +# check if local dump exists +declare downloadDump=0 +if [[ ! -f "${remoteDbDumpTempFile}" ]]; then + Log::displayInfo "local dump does not exist" + downloadDump=1 +fi +if [[ -z "${optionFromAws}" && ! -f "${remoteDbStructureDumpTempFile}" ]]; then + Log::displayInfo "local structure dump does not exist" + downloadDump=1 +fi +if [[ "${downloadDump}" = "0" ]]; then + Log::displayInfo "local dump ${remoteDbDumpTempFile} already exists, avoid download" +fi + +# shellcheck disable=SC2154 +Log::displayInfo "tables list will calculated using profile ${optionProfile} => ${profileCommandFile}" +SECONDS=0 +if [[ "${downloadDump}" = "1" ]]; then + Log::displayInfo "Download dump" + + if [[ -n "${optionFromAws}" ]]; then + # download dump from s3 + declare s3Url="${S3_BASE_URL%/}/${optionFromAws}" + aws s3 ls --human-readable "${s3Url}" || { + Log::fatal "Command ${SCRIPT_NAME} - unable to get information on S3 object : ${s3Url}" + } + Log::displayInfo "Download dump from ${s3Url} ..." + TMPDIR="${TMDIR:-/tmp}" aws s3 cp "${s3Url}" "${remoteDbDumpTempFile}" || { + Log::fatal "Command ${SCRIPT_NAME} - unable to download dump from S3 : ${s3Url}" + } + else + # check if remote db exists + Database::ifDbExists dbFromInstance "${fromDbName}" || { + Log::fatal "Command ${SCRIPT_NAME} - Remote Database ${fromDbName} does not exist" + } + + initializeDefaultTargetMysqlOptions dbFromInstance "${fromDbName}" + + declare dumpHeader + dumpHeader=$(printf "%s\nSET names '%s';\n" "${DUMP_HEADER}" "${optionCharacterSet}") + + # calculate remote db dump size + declare listTables + declare listTablesDumpSize + declare listTablesDump + listTables="$(Database::query dbFromInstance "show tables" "${fromDbName}" | ${profileCommandFile} | sort)" + # shellcheck disable=SC2034 # used by embed_file_dumpSizeQuery + listTablesDumpSize="$(echo "${listTables}" | awk -v d="," -v q="'" '{s=(NR==1?s:s d)q $0 q}END{print s }')" + listTablesDump=$(echo "${listTables}" | awk -v d=" " -v q="" '{s=(NR==1?s:s d)q $0 q}END{print s }') + + Log::displayInfo "Calculate dump size for tables ${listTablesDump}" + declare remoteDbDumpSize + # shellcheck disable=SC2154 + remoteDbDumpSize="$( + export listTablesDumpSize fromDbName + envsubst <"${embed_file_dumpSizeQuery}" | Database::query dbFromInstance + )" + if [[ -z "${remoteDbDumpSize}" ]]; then + # could occur with the none profile + remoteDbDumpSize="0" + fi + + # dump db + Log::displayInfo "Dump the database ${fromDbName} (Size:${remoteDbDumpSize}MB) ..." + declare dumpSizePvEstimation + dumpSizePvEstimation=$(awk "BEGIN {printf \"%.0f\",${remoteDbDumpSize}/1.5}") + time ( + echo "${dumpHeader}" + Database::dump dbFromInstance "${fromDbName}" "${listTablesDump}" \ + --no-create-info --skip-add-drop-table --single-transaction=TRUE | + pv --progress --size "${dumpSizePvEstimation}m" + echo "${DUMP_FOOTER}" + ) | gzip >"${remoteDbDumpTempFile}" + + Log::displayInfo "Dump structure of the database ${fromDbName} ..." + time ( + echo "${dumpHeader}" + #shellcheck disable=SC2016 + Database::dump dbFromInstance "${fromDbName}" "" \ + --no-data --skip-add-drop-table --single-transaction=TRUE | + sed 's/^CREATE TABLE `/CREATE TABLE IF NOT EXISTS `/g' + echo "${DUMP_FOOTER}" + ) | gzip >"${remoteDbStructureDumpTempFile}" + fi + Log::displayInfo "Dump done." +fi + +# mark dumps as modified now to avoid them to be garbage collected +touch -c -m "${remoteDbDumpTempFile}" || true +touch -c -m "${remoteDbStructureDumpTempFile}" || true + +# TODO Collation and character set should be retrieved from dump files if possible +# shellcheck disable=SC2154 +declare targetCollationName="${optionCollationName:-${defaultTargetCollationName}}" +# shellcheck disable=SC2154 +declare targetCharacterSet="${optionCharacterSet:-${defaultTargetCharacterSet}}" + +# shellcheck disable=SC2154 +Log::displayInfo "create target database ${targetDbName} if needed" +#shellcheck disable=SC2016 +Database::query dbTargetDatabase \ + "$(printf 'CREATE DATABASE IF NOT EXISTS `%s` CHARACTER SET "%s" COLLATE "%s"' "${targetDbName}" "${targetCharacterSet}" "${targetCollationName}")" + +if [[ -z "${optionFromAws}" ]]; then + Database::setQueryOptions dbTargetDatabase "${dbTargetDatabase['DB_IMPORT_OPTIONS']}" + Log::displayInfo "Importing remote db '${fromDbName}' to local db '${targetDbName}'" + # shellcheck disable=SC2154 + if [[ "${optionSkipSchema}" = "1" ]]; then + Log::displayInfo "avoid to create db structure" + else + Log::displayInfo "create db structure from ${remoteDbStructureDumpTempFile}" + # shellcheck disable=SC2034 + declare status=0 + # shellcheck disable=SC2034 + declare -a pipeStatus=() + time ( + pv "${remoteDbStructureDumpTempFile}" | zcat | + Database::query dbTargetDatabase "" "${targetDbName}" || Bash::handlePipelineFailure status pipeStatus + ) + fi +fi +Log::displayInfo "import remote to local from file ${remoteDbDumpTempFile}" +declare -a dbImportStreamOptions=( + --profile "${optionProfile}" + --target-dsn "${optionTargetDsn}" + --character-set "${targetCharacterSet}" +) +if [[ -n "${optionTables:-}" ]]; then + dbImportStreamOptions+=( + --tables "${optionTables}" + ) +fi +time ( + "${CURRENT_DIR}/dbImportStream" \ + "${dbImportStreamOptions[@]}" \ + "${remoteDbDumpTempFile}" \ + "${targetDbName}" + +) + +# garbage collect db import dumps +File::garbageCollect "${DB_IMPORT_DUMP_DIR}" "${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30}" || true + +Log::displayInfo "Import database duration : $(date -u -d "@${SECONDS}" +"%T")" diff --git a/src/_binaries/Database/dbImport/dbImport-options.sh b/src/_binaries/Database/dbImport/dbImport-options.sh new file mode 100755 index 00000000..60bb1622 --- /dev/null +++ b/src/_binaries/Database/dbImport/dbImport-options.sh @@ -0,0 +1,91 @@ +#!/usr/bin/env bash + +# shellcheck disable=SC2034 +declare copyrightBeginYear="2020" +# shellcheck disable=SC2034 +declare versionNumber="2.0" + +declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" +declare defaultTargetCharacterSet="" + +beforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad + Linux::requireRealpathCommand + Linux::requireExecutedAsUser +} + +optionHelpCallback() { + dbImportCommandHelp + exit 0 +} + +longDescriptionFunction() { + local profilesList="" + local dsnList="" + dsnList="$(Conf::getMergedList "dsn" "env")" + profilesList="$(Conf::getMergedList "dbImportProfiles" "sh" || true)" + + echo -e "${__HELP_TITLE}Default profiles directory:${__HELP_NORMAL}" + echo -e "${PROFILES_DIR-configuration error}" + + echo -e "${__HELP_TITLE}User profiles directory:${__HELP_NORMAL}" + echo -e "${HOME_PROFILES_DIR-configuration error}" + echo -e "Allows to override profiles defined in 'Default profiles directory'" + + echo -e "${__HELP_TITLE}List of available profiles:${__HELP_NORMAL}" + echo -e "${profilesList}" + + echo -e "${__HELP_TITLE}List of available dsn:${__HELP_NORMAL}" + echo -e "${dsnList}" + + echo -e "${__HELP_TITLE}Aws s3 location:${__HELP_NORMAL}" + echo -e "${S3_BASE_URL}" + echo + echo -e "${__HELP_TITLE}Example 1: from one database to another one${__HELP_NORMAL}" + echo -e "${__HELP_EXAMPLE}TODO${__HELP_NORMAL}" + echo + echo -e "${__HELP_TITLE}Example 2: import from S3${__HELP_NORMAL}" + echo -e "${__HELP_EXAMPLE}TODO${__HELP_NORMAL}" + Db::checkRequirements +} + +dsnHelpFunction() { + echo 'dsn to use for source database' + echo 'this option is incompatible with -a|--from-aws option' +} + +dbImportCommandCallback() { + if [[ -z "${targetDbName}" ]]; then + # shellcheck disable=SC2154 + targetDbName="${fromDbName}" + fi + + if [[ -n "${optionFromAws}" ]]; then + Assert::commandExists aws \ + "Command ${SCRIPT_NAME} - missing aws, please check https://docs.aws.amazon.com/fr_fr/cli/latest/userguide/install-cliv2.html" || exit 1 + + if [[ -n "${optionFromDsn}" ]]; then + Log::fatal "Command ${SCRIPT_NAME} - you cannot use from-dsn and from-aws at the same time" + fi + + if [[ -z "${S3_BASE_URL}" ]]; then + Log::fatal "Command ${SCRIPT_NAME} - missing S3_BASE_URL, please provide a value in .env file" + fi + elif [[ -z "${optionFromDsn}" ]]; then + # default value for FROM_DSN if from-aws not set + # shellcheck disable=SC2154 + optionFromDsn="default.remote" + fi + + if [[ -z "${DB_IMPORT_DUMP_DIR}" ]]; then + Log::fatal "Command ${SCRIPT_NAME} - you have to specify a value for DB_IMPORT_DUMP_DIR env variable" + fi + + if [[ ! -d "${DB_IMPORT_DUMP_DIR}" ]]; then + mkdir -p "${DB_IMPORT_DUMP_DIR}" || + Log::fatal "Command ${SCRIPT_NAME} - impossible to create directory ${DB_IMPORT_DUMP_DIR} specified by DB_IMPORT_DUMP_DIR env variable" + fi +} diff --git a/src/_binaries/DbImport/dbImport.bats b/src/_binaries/Database/dbImport/dbImport.bats similarity index 96% rename from src/_binaries/DbImport/dbImport.bats rename to src/_binaries/Database/dbImport/dbImport.bats index aad66a92..b2a4c0db 100755 --- a/src/_binaries/DbImport/dbImport.bats +++ b/src/_binaries/Database/dbImport/dbImport.bats @@ -1,7 +1,7 @@ #!/usr/bin/env bash # shellcheck source=src/batsHeaders.sh -source "$(cd "${BATS_TEST_DIRNAME}/../.." && pwd)/batsHeaders.sh" +source "$(cd "${BATS_TEST_DIRNAME}/../../.." && pwd)/batsHeaders.sh" setup() { export TMPDIR="${BATS_TEST_TMPDIR}" @@ -41,7 +41,7 @@ function Database::dbImport::remoteDbName_not_provided { #@test run "${binDir}/dbImport" 2>&1 assert_failure 1 assert_lines_count 1 - assert_output --partial "ERROR - Command dbImport - Argument 'fromDbName' should be provided at least 1 time(s)" + assert_output --partial "ERROR - Command dbImport - Argument '' should be provided at least 1 time(s)" } function Database::dbImport::from_aws_and_aws_not_installed { #@test @@ -163,8 +163,8 @@ function Database::dbImport::remote_db_fully_functional_from_mysql { #@test # call 1 (order 7): dump data # call 2 (order 8): dump structure stub mysqldump \ - "\* --default-character-set=utf8 --compress --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 --ssl-mode=DISABLED --no-create-info --skip-add-drop-table --single-transaction=TRUE fromDb table1 : echo '####data####'" \ - "\* --default-character-set=utf8 --compress --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 --ssl-mode=DISABLED --no-data --skip-add-drop-table --single-transaction=TRUE fromDb : echo '####structure####'" + "\* --default-character-set=utf8 --compression-algorithms --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 --ssl-mode=DISABLED --no-create-info --skip-add-drop-table --single-transaction=TRUE fromDb table1 : echo '####data####'" \ + "\* --default-character-set=utf8 --compression-algorithms --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 --ssl-mode=DISABLED --no-data --skip-add-drop-table --single-transaction=TRUE fromDb : echo '####structure####'" stub zcat \ "\* : echo 'structure'" \ diff --git a/src/_binaries/Database/dbImport/testsData/.env b/src/_binaries/Database/dbImport/testsData/.env new file mode 100755 index 00000000..24e891d2 --- /dev/null +++ b/src/_binaries/Database/dbImport/testsData/.env @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2034 + +# Default settings +# you can override these settings by creating ${HOME}/.bash-tools/.env file +### +### LOG Level +### minimum level of the messages that will be logged into LOG_FILE +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_LOG_LEVEL=0 + +### +### DISPLAY Level +### minimum level of the messages that will be displayed on screen +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} + +### +### Log to file +### +### all log messages will be redirected to log file specified +### this same path will be used inside and outside of the container +### +BASH_FRAMEWORK_LOG_FILE= + +# absolute directory containing db import sql dumps +DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} + +# garbage collect all files for which modification is greater than eg: 30 days (+30) +# each time an existing file is used by dbImport/dbImportTable +# the file modification time is set to now +DB_IMPORT_GARBAGE_COLLECT_DAYS=+30 + +# absolute directory containing dbScripts used by dbScriptAllDatabases +SCRIPTS_FOLDER=${HOME}/.bash-tools/conf/dbScripts + +# ----------------------------------------------------- +# AWS Parameters +# ----------------------------------------------------- +S3_BASE_URL=s3://s3server/exports diff --git a/src/_binaries/Database/dbImport/testsData/auto_default.local_fromDb_20.sh b/src/_binaries/Database/dbImport/testsData/auto_default.local_fromDb_20.sh new file mode 100755 index 00000000..a332f1c1 --- /dev/null +++ b/src/_binaries/Database/dbImport/testsData/auto_default.local_fromDb_20.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +# cat represents the whole list of tables +cat | + grep -v '^table1$' | # table size 29MB + grep -v '^table2$' | # table size 10MB + # grep -v '^table3$' | # table size 4MB + cat diff --git a/src/_binaries/Database/dbImport/testsData/auto_default.local_fromDb_70.sh b/src/_binaries/Database/dbImport/testsData/auto_default.local_fromDb_70.sh new file mode 100755 index 00000000..a6919c07 --- /dev/null +++ b/src/_binaries/Database/dbImport/testsData/auto_default.local_fromDb_70.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +# cat represents the whole list of tables +cat | + grep -v '^table1$' | # table size 29MB + # grep -v '^table2$' | # table size 10MB + # grep -v '^table3$' | # table size 4MB + cat diff --git a/src/_binaries/Database/dbImport/testsData/dbImport.help.txt b/src/_binaries/Database/dbImport/testsData/dbImport.help.txt new file mode 100644 index 00000000..8447c2c7 --- /dev/null +++ b/src/_binaries/Database/dbImport/testsData/dbImport.help.txt @@ -0,0 +1,95 @@ +SYNOPSIS: +Import source db into target db using eventual table filter. + +USAGE: dbImport [OPTIONS] [ARGUMENTS] +USAGE: dbImport [--collation-name|-o <>] + [--target-dsn|-t <>] [--character-set|-c <>] + [--profile|-p <>] [--tables <>] + [--skip-schema|-s] [--from-dsn|-f <>] [--from-aws|-a <>] + [--help|-h] [--config] [--bash-framework-config ] + [--verbose|-v] [-vv] [-vvv] [--env-file ] [--log-level ] + [--log-file ] [--display-level ] [--no-color] + [--theme ] [--version] [--quiet|-q] + +ARGUMENTS: + {single} (mandatory) + The name of the source/remote database. + [{single}] + The name of the target database + Default value: (without extension) + + +TARGET OPTIONS: + --collation-name, -o  {single} + Change the collation name used during database creation. + --target-dsn, -t  {single} + Dsn to use for target database. + Default value: default.local + --character-set, -c  {single} + Change the character set used during database creation. + +PROFILE OPTIONS: + --profile, -p  {single} + The name of the profile to use in order to include or exclude tables. + Default value: default + --tables  {single} + Import only table specified in the list. + If aws mode, ignore profile option. + + +SOURCE OPTIONS: + --skip-schema, -s {single} + Avoids to import the schema. + --from-dsn, -f  {single} + target mysql server + --from-aws, -a  {single} + db dump will be downloaded from s3 instead of using remote db. + The value is the name of the file without s3 location + (Only .gz or tar.gz file are supported). + This option is incompatible with -f|--from-dsn option. + + +GLOBAL OPTIONS: + --help, -h {single} + Displays this command help + --config {single} + Displays configuration + --bash-framework-config bash-framework-config {single} + Use alternate bash framework configuration. + --verbose, -v {single} + Info level verbose mode (alias of --display-level INFO) + -vv {single} + Debug level verbose mode (alias of --display-level DEBUG) + -vvv {single} + Trace level verbose mode (alias of --display-level TRACE) + --env-file env-file {list} (optional) + Load the specified env file (deprecated, please use --bash-framework-con + fig option instead) + --log-level log-level {single} + Set log level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --log-file log-file {single} + Set log file + --display-level display-level {single} + Set display level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --no-color {single} + Produce monochrome output. alias of --theme noColor. + --theme theme {single} + Choose color theme - default-force means colors will be produced even if + command is piped. + Possible values: default, default-force, noColor + Default value: default + --version {single} + Print version information and quit. + --quiet, -q {single} + Quiet mode, doesn't display any output. + +VERSION: 2.0 + +AUTHOR: [François Chastanet](https://github.com/fchastanet) + +SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImport/binary-dbImport.yaml + +LICENSE: MIT License +Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Database/dbImport/testsData/dbImportProfile.help.txt b/src/_binaries/Database/dbImport/testsData/dbImportProfile.help.txt new file mode 100644 index 00000000..ee081e13 --- /dev/null +++ b/src/_binaries/Database/dbImport/testsData/dbImportProfile.help.txt @@ -0,0 +1,89 @@ +DESCRIPTION: generate optimized profiles to be used by dbImport +USAGE: dbImportProfile [OPTIONS] [ARGUMENTS] +USAGE: dbImportProfile [--profile|-p ] + [--from-dsn|-f ] [--ratio|-r ] + [--bash-framework-config ] [--config] [--verbose|-v] [-vv] [-vvv] + [--env-file ] [--no-color] [--theme ] [--help|-h] [--version] + [--quiet|-q] [--log-level ] [--log-file ] + [--display-level ] + +ARGUMENTS: + fromDbName {single} (mandatory) + the name of the source/remote database + +OPTIONS: + --profile, -p  {single} + the name of the profile to write in profiles directory. If not provided, th + e file name pattern will be 'auto__.sh' + --from-dsn, -f  {single} + dsn to use for source database (Default: default.remote) if not provided, th + e file name pattern will be 'auto__.sh' + --ratio, -r  {single} + define the ratio to use (0 to 100% - default 70). 0 means profile will filt + er out all the tables. 100 means profile will keep all the tables. Eg: + 70 means that tables with size(table+index) that are greater that 70% of + the max table size will be excluded. + +GLOBAL OPTIONS: + --bash-framework-config  {single} + use alternate bash framework configuration. + --config {single} + Display configuration + --verbose, -v {single} + info level verbose mode (alias of --display-level INFO) + -vv {single} + debug level verbose mode (alias of --display-level DEBUG) + -vvv {single} + trace level verbose mode (alias of --display-level TRACE) + --env-file  {list} (optional) + Load the specified env file (deprecated, please use --bash-framework-config + option instead) + --no-color {single} + Produce monochrome output. alias of --theme noColor. + --theme  {single} + choose color theme - default-force means colors will be produced even if com + mand is piped + Default value: default + Possible values: default|default-force|noColor + --help, -h {single} + Display this command help + --version {single} + Print version information and quit + --quiet, -q {single} + quiet mode, doesn't display any output + --log-level  {single} + Set log level + Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --log-file  {single} + Set log file + --display-level  {single} + set display level + Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + +Default profiles directory: +/bash/conf/dbImportProfiles + +User profiles directory: +home/.bash-tools/dbImportProfiles +Allows to override profiles defined in Default profiles directory + +List of available profiles: + + +List of available dsn: + - default.local + - default.remote + - localhost-root + +VERSION: 2.0 + +AUTHOR: +[François Chastanet](https://github.com/fchastanet) + +SOURCE FILE: +https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/DbImport/dbImportProfile.sh + +LICENSE: +MIT License + +Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Database/dbImport/testsData/dbImportProfile.tableList1 b/src/_binaries/Database/dbImport/testsData/dbImportProfile.tableList1 new file mode 100644 index 00000000..f02c5c5f --- /dev/null +++ b/src/_binaries/Database/dbImport/testsData/dbImportProfile.tableList1 @@ -0,0 +1,3 @@ +table1 29 +table2 10 +table3 4 diff --git a/src/_binaries/Database/dbImport/testsData/dbImportProfiles/all.sh b/src/_binaries/Database/dbImport/testsData/dbImportProfiles/all.sh new file mode 100755 index 00000000..09b79b19 --- /dev/null +++ b/src/_binaries/Database/dbImport/testsData/dbImportProfiles/all.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +# cat represents the whole list of tables +cat diff --git a/src/_binaries/Database/dbImport/testsData/dbImportProfiles/default.sh b/src/_binaries/Database/dbImport/testsData/dbImportProfiles/default.sh new file mode 100755 index 00000000..8cf9b92b --- /dev/null +++ b/src/_binaries/Database/dbImport/testsData/dbImportProfiles/default.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +# cat represents the whole list of tables +cat | + grep -v '.*_log' | + grep -v '.*logs' | + grep -v '.*tracking' | + grep -v '.*stats' | + grep -v '.*history.*' | + # always finish by a cat to be sure the command does not return exit code != 0 + cat || { + # avoid failure on premature process close (check Bash::handlePipelineFailure) + declare exitCode=$?; if (( exitCode == 141 )); then exit 0; fi + exit "${exitCode}" + } diff --git a/src/_binaries/Database/dbImport/testsData/dbImportProfiles/none.sh b/src/_binaries/Database/dbImport/testsData/dbImportProfiles/none.sh new file mode 100755 index 00000000..5604fdca --- /dev/null +++ b/src/_binaries/Database/dbImport/testsData/dbImportProfiles/none.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +# useful if you want to dump only the db structure + +# cat represents the whole list of tables +echo 'propel_migration' diff --git a/src/_binaries/Database/dbImport/testsData/dbImportStream.help.txt b/src/_binaries/Database/dbImport/testsData/dbImportStream.help.txt new file mode 100644 index 00000000..0ff1b343 --- /dev/null +++ b/src/_binaries/Database/dbImport/testsData/dbImportStream.help.txt @@ -0,0 +1,92 @@ +DESCRIPTION: stream tar.gz file or gz file through mysql +USAGE: dbImportStream [OPTIONS] [ARGUMENTS] +USAGE: dbImportStream [--profile|-p ] + [--tables ] [--target-dsn|-t ] + [--character-set|-c ] [--bash-framework-config ] [--config] + [--verbose|-v] [-vv] [-vvv] [--env-file ] [--no-color] + [--theme ] [--help|-h] [--version] [--quiet|-q] [--log-level ] + [--log-file ] [--display-level ] + +ARGUMENTS: + argDumpFile {single} (mandatory) + the of the file that will be streamed through mysql + argTargetDbName {single} (mandatory) + the name of the mysql target database + +PROFILE OPTIONS: + --profile, -p  {single} + the name of the profile to use in order to include or exclude tables (if not + specified in default.sh from 'User profiles directory' if exists or 'Def + ault profiles directory') + --tables  {single} + import only table specified in the list. If aws mode, ignore profile option + +TARGET OPTIONS: + --target-dsn, -t  {single} + dsn to use for target database (Default: default.local) + --character-set, -c  {single} + change the character set used during database creation (default value: utf8) + +GLOBAL OPTIONS: + --bash-framework-config  {single} + use alternate bash framework configuration. + --config {single} + Display configuration + --verbose, -v {single} + info level verbose mode (alias of --display-level INFO) + -vv {single} + debug level verbose mode (alias of --display-level DEBUG) + -vvv {single} + trace level verbose mode (alias of --display-level TRACE) + --env-file  {list} (optional) + Load the specified env file (deprecated, please use --bash-framework-config + option instead) + --no-color {single} + Produce monochrome output. alias of --theme noColor. + --theme  {single} + choose color theme - default-force means colors will be produced even if com + mand is piped + Default value: default + Possible values: default|default-force|noColor + --help, -h {single} + Display this command help + --version {single} + Print version information and quit + --quiet, -q {single} + quiet mode, doesn't display any output + --log-level  {single} + Set log level + Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --log-file  {single} + Set log file + --display-level  {single} + set display level + Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + +Default profiles directory: +/bash/conf/dbImportProfiles + +User profiles directory: +home/.bash-tools/dbImportProfiles +Allows to override profiles defined in Default profiles directory + +List of available profiles: + + +List of available dsn: + - default.local + - default.remote + - localhost-root + +VERSION: 2.0 + +AUTHOR: +[François Chastanet](https://github.com/fchastanet) + +SOURCE FILE: +https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/DbImport/dbImportStream.sh + +LICENSE: +MIT License + +Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Database/dbImport/testsData/dbImportTableDump.sql b/src/_binaries/Database/dbImport/testsData/dbImportTableDump.sql new file mode 100644 index 00000000..e832194e --- /dev/null +++ b/src/_binaries/Database/dbImport/testsData/dbImportTableDump.sql @@ -0,0 +1,35 @@ +-- MySQL dump 10.13 Distrib 8.0.22, for Linux (x86_64) +-- +-- Host: 127.0.0.1 Database: fromDb +-- ------------------------------------------------------ +-- Server version 8.0.21 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!50503 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Dumping data for table `emptyTable` +-- + +DROP TABLE IF EXISTS `dataTable`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `dataTable` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `modification_date` datetime NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2011640 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +LOCK TABLES `dataTable` WRITE; +/*!40000 ALTER TABLE `dataTable` DISABLE KEYS */; +INSERT INTO `dataTable` VALUES (1,1,'Picture:','en-GB'),(2,2,'Name:','en-GB'),(3,3,'First name:','en-GB'),(4,4,'Login:','en-GB'),(5,5,'Password:','en-GB'),(6,6,'E-mail:','en-GB'),(7,7,'Reference number:','en-GB'),(8,8,'Presentation:','en-GB'),(9,9,'Web:','en-GB'),(10,10,'Twitter:','en-GB'),(11,11,'LinkedIn:','en-GB'),(12,12,'Languages:','en-GB'),(13,13,'Time zone:','en-GB'); +/*!40000 ALTER TABLE `dataTable` ENABLE KEYS */; diff --git a/src/_binaries/Database/dbImport/testsData/dsn/default.local.env b/src/_binaries/Database/dbImport/testsData/dsn/default.local.env new file mode 100644 index 00000000..ccb2e265 --- /dev/null +++ b/src/_binaries/Database/dbImport/testsData/dsn/default.local.env @@ -0,0 +1,13 @@ +# shellcheck disable=SC2034 +HOSTNAME=127.0.0.1 +PASSWORD=root +PORT=3306 +USER=root + +# you can override default MYSQL options if you wish +# SSL_OPTIONS=--ssl-mode=DISABLED +# QUERY_OPTIONS=--batch --raw --default-character-set=utf8 +# DUMP_OPTIONS=--default-character-set=utf8 --compression-algorithms --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 + +# specific options during import +DB_IMPORT_OPTIONS= diff --git a/src/_binaries/Database/dbImport/testsData/dsn/default.remote.env b/src/_binaries/Database/dbImport/testsData/dsn/default.remote.env new file mode 100644 index 00000000..1958d45e --- /dev/null +++ b/src/_binaries/Database/dbImport/testsData/dsn/default.remote.env @@ -0,0 +1,12 @@ +HOSTNAME=127.0.0.1 +PASSWORD=root +PORT=3306 +USER=root + +# you can override default MYSQL options if you wish +# SSL_OPTIONS=--ssl-mode=DISABLED +# QUERY_OPTIONS=--batch --raw --default-character-set=utf8 +# DUMP_OPTIONS=--default-character-set=utf8 --compression-algorithms --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 + +# specific options during import +DB_IMPORT_OPTIONS= diff --git a/src/_binaries/Database/dbImport/testsData/dsn/localhost-root.env b/src/_binaries/Database/dbImport/testsData/dsn/localhost-root.env new file mode 100644 index 00000000..1958d45e --- /dev/null +++ b/src/_binaries/Database/dbImport/testsData/dsn/localhost-root.env @@ -0,0 +1,12 @@ +HOSTNAME=127.0.0.1 +PASSWORD=root +PORT=3306 +USER=root + +# you can override default MYSQL options if you wish +# SSL_OPTIONS=--ssl-mode=DISABLED +# QUERY_OPTIONS=--batch --raw --default-character-set=utf8 +# DUMP_OPTIONS=--default-character-set=utf8 --compression-algorithms --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 + +# specific options during import +DB_IMPORT_OPTIONS= diff --git a/src/_binaries/Database/dbImport/testsData/dump.sql b/src/_binaries/Database/dbImport/testsData/dump.sql new file mode 100644 index 00000000..273f7eb0 --- /dev/null +++ b/src/_binaries/Database/dbImport/testsData/dump.sql @@ -0,0 +1,84 @@ +-- MySQL dump 10.13 Distrib 8.0.22, for Linux (x86_64) +-- +-- Host: 127.0.0.1 Database: fromDb +-- ------------------------------------------------------ +-- Server version 8.0.21 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!50503 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Dumping data for table `emptyTable` +-- + +DROP TABLE IF EXISTS `emptyTable`; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `emptyTable` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `modification_date` datetime NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2011640 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +LOCK TABLES `emptyTable` WRITE; +/*!40000 ALTER TABLE `emptyTable` DISABLE KEYS */; +/*!40000 ALTER TABLE `emptyTable` ENABLE KEYS */; +UNLOCK TABLES; + +DROP TABLE IF EXISTS `dataTable`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `dataTable` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `modification_date` datetime NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2011640 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +LOCK TABLES `dataTable` WRITE; +/*!40000 ALTER TABLE `dataTable` DISABLE KEYS */; +INSERT INTO `dataTable` VALUES (1,1,'Picture:','en-GB'),(2,2,'Name:','en-GB'),(3,3,'First name:','en-GB'),(4,4,'Login:','en-GB'),(5,5,'Password:','en-GB'),(6,6,'E-mail:','en-GB'),(7,7,'Reference number:','en-GB'),(8,8,'Presentation:','en-GB'),(9,9,'Web:','en-GB'),(10,10,'Twitter:','en-GB'),(11,11,'LinkedIn:','en-GB'),(12,12,'Languages:','en-GB'),(13,13,'Time zone:','en-GB'); +/*!40000 ALTER TABLE `dataTable` ENABLE KEYS */; +UNLOCK TABLES; + +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `otherTable` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `modification_date` datetime NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2011640 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +LOCK TABLES `otherTable` WRITE; +/*!40000 ALTER TABLE `dataTable` DISABLE KEYS */; +INSERT INTO `otherTable` VALUES (1,1,'Picture:','en-GB'),(2,2,'Name:','en-GB'),(3,3,'First name:','en-GB'),(4,4,'Login:','en-GB'),(5,5,'Password:','en-GB'),(6,6,'E-mail:','en-GB'),(7,7,'Reference number:','en-GB'),(8,8,'Presentation:','en-GB'),(9,9,'Web:','en-GB'),(10,10,'Twitter:','en-GB'),(11,11,'LinkedIn:','en-GB'),(12,12,'Languages:','en-GB'),(13,13,'Time zone:','en-GB'); +/*!40000 ALTER TABLE `dataTable` ENABLE KEYS */; +UNLOCK TABLES; + + +-- +-- Dumping routines for database 'fromDb' +-- +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2021-01-18 22:52:14 +COMMIT; +SET AUTOCOMMIT=@OLD_AUTOCOMMIT; +SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; +SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; diff --git a/src/_binaries/Database/dbImport/testsData/dumpMissingSchema.sql b/src/_binaries/Database/dbImport/testsData/dumpMissingSchema.sql new file mode 100644 index 00000000..de982be2 --- /dev/null +++ b/src/_binaries/Database/dbImport/testsData/dumpMissingSchema.sql @@ -0,0 +1,61 @@ +SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS = 0; +SET @OLD_AUTOCOMMIT=@@AUTOCOMMIT, AUTOCOMMIT = 0; +SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS = 0; +SET names 'utf8'; +-- MySQL dump 10.13 Distrib 8.0.22, for Linux (x86_64) +-- +-- Host: 127.0.0.1 Database: fromDb +-- ------------------------------------------------------ +-- Server version 8.0.21 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!50503 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Dumping data for table `emptyTable` +-- + +LOCK TABLES `emptyTable` WRITE; +/*!40000 ALTER TABLE `emptyTable` DISABLE KEYS */; +/*!40000 ALTER TABLE `emptyTable` ENABLE KEYS */; +UNLOCK TABLES; + +LOCK TABLES `dataTable` WRITE; +/*!40000 ALTER TABLE `dataTable` DISABLE KEYS */; +INSERT INTO `dataTable` VALUES (1,1,'Picture:','en-GB'),(2,2,'Name:','en-GB'),(3,3,'First name:','en-GB'),(4,4,'Login:','en-GB'),(5,5,'Password:','en-GB'),(6,6,'E-mail:','en-GB'),(7,7,'Reference number:','en-GB'),(8,8,'Presentation:','en-GB'),(9,9,'Web:','en-GB'),(10,10,'Twitter:','en-GB'),(11,11,'LinkedIn:','en-GB'),(12,12,'Languages:','en-GB'),(13,13,'Time zone:','en-GB'); +/*!40000 ALTER TABLE `dataTable` ENABLE KEYS */; +UNLOCK TABLES; + +LOCK TABLES `otherTable` WRITE; +/*!40000 ALTER TABLE `dataTable` DISABLE KEYS */; +INSERT INTO `otherTable` VALUES (1,1,'Picture:','en-GB'),(2,2,'Name:','en-GB'),(3,3,'First name:','en-GB'),(4,4,'Login:','en-GB'),(5,5,'Password:','en-GB'),(6,6,'E-mail:','en-GB'),(7,7,'Reference number:','en-GB'),(8,8,'Presentation:','en-GB'),(9,9,'Web:','en-GB'),(10,10,'Twitter:','en-GB'),(11,11,'LinkedIn:','en-GB'),(12,12,'Languages:','en-GB'),(13,13,'Time zone:','en-GB'); +/*!40000 ALTER TABLE `dataTable` ENABLE KEYS */; +UNLOCK TABLES; + + +-- +-- Dumping routines for database 'fromDb' +-- +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2021-01-18 22:52:14 +COMMIT; +SET AUTOCOMMIT=@OLD_AUTOCOMMIT; +SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; +SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; diff --git a/src/_binaries/Database/dbImport/testsData/dumpSchema.sql b/src/_binaries/Database/dbImport/testsData/dumpSchema.sql new file mode 100644 index 00000000..83509a2a --- /dev/null +++ b/src/_binaries/Database/dbImport/testsData/dumpSchema.sql @@ -0,0 +1,64 @@ +-- MySQL dump 10.13 Distrib 8.0.22, for Linux (x86_64) +-- +-- Host: 127.0.0.1 Database: fromDb +-- ------------------------------------------------------ +-- Server version 8.0.21 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!50503 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Dumping data for table `emptyTable` +-- + +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `emptyTable` IF NOT EXISTS ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `modification_date` datetime NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2011640 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `dataTable` IF NOT EXISTS ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `modification_date` datetime NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2011640 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `otherTable` IF NOT EXISTS ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `modification_date` datetime NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2011640 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +-- +-- Dumping routines for database 'fromDb' +-- +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2021-01-18 22:52:14 +COMMIT; +SET AUTOCOMMIT=@OLD_AUTOCOMMIT; +SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; +SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; diff --git a/src/_binaries/Database/dbImport/testsData/empty-dump.sql b/src/_binaries/Database/dbImport/testsData/empty-dump.sql new file mode 100644 index 00000000..c378ac9a --- /dev/null +++ b/src/_binaries/Database/dbImport/testsData/empty-dump.sql @@ -0,0 +1 @@ +-- EMPTY DUMP diff --git a/src/_binaries/Database/dbImport/testsData/expectedDbImportProfileTableListQuery.sql b/src/_binaries/Database/dbImport/testsData/expectedDbImportProfileTableListQuery.sql new file mode 100644 index 00000000..bb2068de --- /dev/null +++ b/src/_binaries/Database/dbImport/testsData/expectedDbImportProfileTableListQuery.sql @@ -0,0 +1 @@ +SELECT TABLE_NAME AS tableName, ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024) as maxSize FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'fromDb' AND TABLE_TYPE NOT IN('VIEW') ORDER BY maxSize DESC diff --git a/src/_binaries/Database/dbImport/testsData/expectedDbImportTableDump.sql b/src/_binaries/Database/dbImport/testsData/expectedDbImportTableDump.sql new file mode 100644 index 00000000..8291932a --- /dev/null +++ b/src/_binaries/Database/dbImport/testsData/expectedDbImportTableDump.sql @@ -0,0 +1,42 @@ +SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS = 0; +SET @OLD_AUTOCOMMIT=@@AUTOCOMMIT, AUTOCOMMIT = 0; +SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS = 0; +-- MySQL dump 10.13 Distrib 8.0.22, for Linux (x86_64) +-- +-- Host: 127.0.0.1 Database: fromDb +-- ------------------------------------------------------ +-- Server version 8.0.21 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!50503 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Dumping data for table `emptyTable` +-- + +DROP TABLE IF EXISTS `dataTable`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `dataTable` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `modification_date` datetime NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2011640 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +LOCK TABLES `dataTable` WRITE; +/*!40000 ALTER TABLE `dataTable` DISABLE KEYS */; +INSERT INTO `dataTable` VALUES (1,1,'Picture:','en-GB'),(2,2,'Name:','en-GB'),(3,3,'First name:','en-GB'),(4,4,'Login:','en-GB'),(5,5,'Password:','en-GB'),(6,6,'E-mail:','en-GB'),(7,7,'Reference number:','en-GB'),(8,8,'Presentation:','en-GB'),(9,9,'Web:','en-GB'),(10,10,'Twitter:','en-GB'),(11,11,'LinkedIn:','en-GB'),(12,12,'Languages:','en-GB'),(13,13,'Time zone:','en-GB'); +/*!40000 ALTER TABLE `dataTable` ENABLE KEYS */; +UNLOCK TABLES; +SET AUTOCOMMIT=@OLD_AUTOCOMMIT; +SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; +SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; diff --git a/src/_binaries/Database/dbImport/testsData/expectedDbImportTableDumpRenamed.sql b/src/_binaries/Database/dbImport/testsData/expectedDbImportTableDumpRenamed.sql new file mode 100644 index 00000000..2818b786 --- /dev/null +++ b/src/_binaries/Database/dbImport/testsData/expectedDbImportTableDumpRenamed.sql @@ -0,0 +1,42 @@ +SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS = 0; +SET @OLD_AUTOCOMMIT=@@AUTOCOMMIT, AUTOCOMMIT = 0; +SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS = 0; +-- MySQL dump 10.13 Distrib 8.0.22, for Linux (x86_64) +-- +-- Host: 127.0.0.1 Database: fromDb +-- ------------------------------------------------------ +-- Server version 8.0.21 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!50503 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Dumping data for table `emptyTable` +-- + +DROP TABLE IF EXISTS `newTableName`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `newTableName` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `modification_date` datetime NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2011640 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +LOCK TABLES `newTableName` WRITE; +/*!40000 ALTER TABLE `newTableName` DISABLE KEYS */; +INSERT INTO `newTableName` VALUES (1,1,'Picture:','en-GB'),(2,2,'Name:','en-GB'),(3,3,'First name:','en-GB'),(4,4,'Login:','en-GB'),(5,5,'Password:','en-GB'),(6,6,'E-mail:','en-GB'),(7,7,'Reference number:','en-GB'),(8,8,'Presentation:','en-GB'),(9,9,'Web:','en-GB'),(10,10,'Twitter:','en-GB'),(11,11,'LinkedIn:','en-GB'),(12,12,'Languages:','en-GB'),(13,13,'Time zone:','en-GB'); +/*!40000 ALTER TABLE `newTableName` ENABLE KEYS */; +UNLOCK TABLES; +SET AUTOCOMMIT=@OLD_AUTOCOMMIT; +SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; +SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; diff --git a/src/_binaries/Database/dbImport/testsData/gawk b/src/_binaries/Database/dbImport/testsData/gawk new file mode 100755 index 00000000..aed42815 --- /dev/null +++ b/src/_binaries/Database/dbImport/testsData/gawk @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +if [[ "$1" = "--version" ]]; then + echo "GNU Awk 5.1.1, API: 2.0 (GNU MPFR 4.1.0, GNU MP 6.2.0)" +else + echo "$( + alts: + - --collation-name + - -o diff --git a/src/_binaries/commandDefinitions/optionsMysqlSource.sh b/src/_binaries/commandDefinitions/optionsMysqlSource.sh new file mode 100755 index 00000000..b4d0d502 --- /dev/null +++ b/src/_binaries/commandDefinitions/optionsMysqlSource.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +dsnHelpFunction() { + echo " target mysql server" +} diff --git a/src/_binaries/commandDefinitions/optionsMysqlSource.yaml b/src/_binaries/commandDefinitions/optionsMysqlSource.yaml new file mode 100644 index 00000000..a7651717 --- /dev/null +++ b/src/_binaries/commandDefinitions/optionsMysqlSource.yaml @@ -0,0 +1,39 @@ +--- +binData: + commands: + default: + definitionFiles: + 40: "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsMysqlSource.sh" + optionGroups: + groupSourceDbOptions: + title: "SOURCE OPTIONS:" + options: + - alts: + - --skip-schema + - -s + group: groupSourceDbOptions + help: Avoids to import the schema. + type: Boolean + variableName: optionSkipSchema + + - variableName: optionFromDsn + group: groupSourceDbOptions + type: String + help: dsnHelpFunction + helpValueName: + alts: + - --from-dsn + - -f + + - alts: + - --from-aws + - -a + group: groupSourceDbOptions + help: | + db dump will be downloaded from s3 instead of using remote db. + The value is the name of the file without s3 location + (Only .gz or tar.gz file are supported). + This option is incompatible with -f|--from-dsn option. + helpValueName: + type: String + variableName: optionFromAws diff --git a/src/_binaries/commandDefinitions/optionsMysqlTarget.sh b/src/_binaries/commandDefinitions/optionsMysqlTarget.sh new file mode 100755 index 00000000..540c1b04 --- /dev/null +++ b/src/_binaries/commandDefinitions/optionsMysqlTarget.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +# shellcheck disable=SC2034 +declare defaultTargetCollationName="utf8_general_ci" +declare defaultTargetCharacterSet="utf8" + +initializeDefaultTargetMysqlOptions() { + local -n dbFromInstanceTargetMysql=$1 + local fromDbName="$2" + + # get remote db collation name + if [[ -n ${optionCollationName+x} && -z "${optionCollationName}" ]]; then + optionCollationName=$(Database::query dbFromInstanceTargetMysql \ + "SELECT default_collation_name FROM information_schema.SCHEMATA WHERE schema_name = \"${fromDbName}\";" "information_schema") + fi + + # get remote db character set + if [[ -z "${optionCharacterSet}" ]]; then + optionCharacterSet=$(Database::query dbFromInstanceTargetMysql \ + "SELECT default_character_set_name FROM information_schema.SCHEMATA WHERE schema_name = \"${fromDbName}\";" "information_schema") + fi +} diff --git a/src/_binaries/commandDefinitions/optionsMysqlTarget.yaml b/src/_binaries/commandDefinitions/optionsMysqlTarget.yaml new file mode 100644 index 00000000..7d898e87 --- /dev/null +++ b/src/_binaries/commandDefinitions/optionsMysqlTarget.yaml @@ -0,0 +1,27 @@ +--- +binData: + commands: + default: + definitionFiles: + 23: "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsMysqlTarget.sh" + optionGroups: + groupTargetOptions: + title: "TARGET OPTIONS:" + options: + - variableName: optionTargetDsn + group: groupTargetOptions + type: String + help: Dsn to use for target database. + helpValueName: + defaultValue: default.local + alts: + - --target-dsn + - -t + - variableName: optionCharacterSet + group: groupTargetOptions + type: String + help: Change the character set used during database creation. + helpValueName: + alts: + - --character-set + - -c diff --git a/src/_binaries/options/options.profile.tpl b/src/_binaries/commandDefinitions/optionsProfile.sh old mode 100644 new mode 100755 similarity index 52% rename from src/_binaries/options/options.profile.tpl rename to src/_binaries/commandDefinitions/optionsProfile.sh index 5e405e06..49db44de --- a/src/_binaries/options/options.profile.tpl +++ b/src/_binaries/commandDefinitions/optionsProfile.sh @@ -1,67 +1,11 @@ -% -# shellcheck source=/dev/null -source <( - Options::generateGroup \ - --title "PROFILE OPTIONS:" \ - --function-name groupProfileOptionsFunction +#!/usr/bin/env bash - profileOptionHelpCallback() { :; } - Options::generateOption \ - --variable-type String \ - --group groupProfileOptionsFunction \ - --help profileOptionHelpCallback \ - --alt "--profile" \ - --alt "-p" \ - --callback "profileOptionCallback" \ - --variable-name "optionProfile" \ - --function-name optionProfileFunction - - # shellcheck disable=SC2116 - Options::generateOption \ - --help-value-name "tablesSeparatedByComma" \ - --help "$(echo \ - "import only table specified in the list. " \ - "If aws mode, ignore profile option" \ - )" \ - --group groupProfileOptionsFunction \ - --alt "--tables" \ - --callback optionTablesCallback \ - --variable-type "String" \ - --variable-name "optionTables" \ - --function-name optionTablesFunction - -) -options+=( - optionProfileFunction - optionTablesFunction - --callback initProfileCommandCallback -) -% - -# default values -declare optionProfile="default" -declare optionTables="" -declare profileCommandFile="" - -profileOptionHelpCallback() { - echo "the name of the profile to use in order to include or exclude tables" - echo "(if not specified in default.sh from 'User profiles directory' if exists or 'Default profiles directory')" +profileOptionHelpFunction() { + Array::wrap2 " " 80 4 \ + " The name of the profile to use in order to include or exclude tables." + echo } -optionTablesCallback() { - if [[ ! ${optionTables} =~ ^[A-Za-z0-9_]+(,[A-Za-z0-9_]+)*$ ]]; then - Log::fatal "Command ${SCRIPT_NAME} - Table list is not valid : ${optionTables}" - fi -} - -profileOptionCallback() { - local -a profilesArray - readarray -t profilesArray < <(Conf::getMergedList "dbImportProfiles" "sh" "" || true) - if ! Array::contains "$2" "${profilesArray[@]}"; then - Log::displayError "${SCRIPT_NAME} - invalid profile '$2' provided" - return 1 - fi -} initProfileCommandCallback() { if [[ "${optionProfile}" != "default" && -n "${optionTables}" ]]; then Log::fatal "Command ${SCRIPT_NAME} - you cannot use table and profile options at the same time" @@ -89,3 +33,18 @@ initProfileCommandCallback() { chmod +x "${profileCommandFile}" Log::displayInfo "${profileMsgInfo}" } + +profileOptionCallback() { + local -a profilesArray + readarray -t profilesArray < <(Conf::getMergedList "dbImportProfiles" "sh" "" || true) + if ! Array::contains "$2" "${profilesArray[@]}"; then + Log::displayError "${SCRIPT_NAME} - invalid profile '$2' provided" + return 1 + fi +} + +optionTablesCallback() { + if [[ ! ${optionTables} =~ ^[A-Za-z0-9_]+(,[A-Za-z0-9_]+)*$ ]]; then + Log::fatal "Command ${SCRIPT_NAME} - Table list is not valid : ${optionTables}" + fi +} diff --git a/src/_binaries/commandDefinitions/optionsProfile.yaml b/src/_binaries/commandDefinitions/optionsProfile.yaml new file mode 100644 index 00000000..cfcdb104 --- /dev/null +++ b/src/_binaries/commandDefinitions/optionsProfile.yaml @@ -0,0 +1,35 @@ +--- +binData: + commands: + default: + definitionFiles: + 24: "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsProfile.sh" + callbacks: + - initProfileCommandCallback + optionGroups: + groupProfileOptions: + title: "PROFILE OPTIONS:" + options: + - variableName: optionProfile + group: groupProfileOptions + type: String + help: profileOptionHelpFunction + callbacks: + - profileOptionCallback + defaultValue: default + helpValueName: + alts: + - --profile + - -p + + - variableName: optionTables + group: groupProfileOptions + type: String + help: | + Import only table specified in the list. + If aws mode, ignore profile option. + callbacks: + - optionTablesCallback + helpValueName: + alts: + - --tables diff --git a/src/_binaries/commandDefinitions/optionsVersion.sh b/src/_binaries/commandDefinitions/optionsVersion.sh new file mode 100755 index 00000000..d1af84b9 --- /dev/null +++ b/src/_binaries/commandDefinitions/optionsVersion.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +optionVersionCallback() { + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version ${versionNumber}" + Db::checkRequirements + exit 0 +} diff --git a/src/_binaries/commandDefinitions/optionsVersion.yaml b/src/_binaries/commandDefinitions/optionsVersion.yaml new file mode 100644 index 00000000..81ee2423 --- /dev/null +++ b/src/_binaries/commandDefinitions/optionsVersion.yaml @@ -0,0 +1,6 @@ +--- +binData: + commands: + default: + definitionFiles: + 30: "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.sh" diff --git a/src/_binaries/options/options.dsn.tpl b/src/_binaries/options/options.dsn.tpl deleted file mode 100644 index 5bc4e11c..00000000 --- a/src/_binaries/options/options.dsn.tpl +++ /dev/null @@ -1,21 +0,0 @@ -% -# shellcheck source=/dev/null -source <( - # shellcheck disable=SC2116 - Options::generateOption \ - --help "${defaultFromDsnHelp:-target mysql server}" \ - --variable-type "String" \ - --group groupSourceDbOptionsFunction \ - --alt "--from-dsn" \ - --alt "-f" \ - --variable-name "optionFromDsn" \ - --function-name optionFromDsnFunction - -) -options+=( - optionFromDsnFunction -) -% - -# default values -declare optionFromDsn="" diff --git a/src/_binaries/options/options.mysql.collationName.tpl b/src/_binaries/options/options.mysql.collationName.tpl deleted file mode 100644 index d5d47ff5..00000000 --- a/src/_binaries/options/options.mysql.collationName.tpl +++ /dev/null @@ -1,27 +0,0 @@ -% -declare defaultTargetCollationName="utf8_general_ci" - -# shellcheck source=/dev/null -source <( - - # shellcheck disable=SC2116 - Options::generateOption \ - --help "$(echo \ - "change the collation name used during database creation" \ - "(default value: ${defaultTargetCollationName})" \ - )" \ - --variable-type "String" \ - --group groupTargetOptionsFunction \ - --alt "--collation-name" \ - --alt "-o" \ - --variable-name "optionCollationName" \ - --function-name optionCollationNameFunction - - ) - options+=( - optionCollationNameFunction - ) -% - -declare optionCollationName="" -declare defaultTargetCollationName="<% ${defaultTargetCollationName} %>" diff --git a/src/_binaries/options/options.mysql.target.tpl b/src/_binaries/options/options.mysql.target.tpl deleted file mode 100644 index a019c53a..00000000 --- a/src/_binaries/options/options.mysql.target.tpl +++ /dev/null @@ -1,61 +0,0 @@ -% -declare defaultTargetDsn="default.local" -declare defaultTargetCharacterSet="utf8" - -# shellcheck source=/dev/null -source <( - Options::generateGroup \ - --title "TARGET OPTIONS:" \ - --function-name groupTargetOptionsFunction - - - Options::generateOption \ - --help "dsn to use for target database (Default: ${defaultTargetDsn})" \ - --help-value-name "targetDsn" \ - --variable-type "String" \ - --group groupTargetOptionsFunction \ - --alt "--target-dsn" \ - --alt "-t" \ - --variable-name "optionTargetDsn" \ - --function-name optionTargetDsnFunction - - # shellcheck disable=SC2116 - Options::generateOption \ - --help "$(echo \ - "change the character set used during database creation" \ - "(default value: ${defaultTargetCharacterSet})" \ - )" \ - --variable-type "String" \ - --group groupTargetOptionsFunction \ - --alt "--character-set" \ - --alt "-c" \ - --variable-name "optionCharacterSet" \ - --function-name optionCharacterSetFunction -) -options+=( - optionTargetDsnFunction - optionCharacterSetFunction -) -% - -declare optionTargetDsn="<% ${defaultTargetDsn} %>" -declare optionCharacterSet="" -declare defaultTargetCharacterSet="<% ${defaultTargetCharacterSet} %>" - - -initializeDefaultTargetMysqlOptions() { - local -n dbFromInstanceTargetMysql=$1 - local fromDbName="$2" - - # get remote db collation name - if [[ -n ${optionCollationName+x} && -z "${optionCollationName}" ]]; then - optionCollationName=$(Database::query dbFromInstanceTargetMysql \ - "SELECT default_collation_name FROM information_schema.SCHEMATA WHERE schema_name = \"${fromDbName}\";" "information_schema") - fi - - # get remote db character set - if [[ -z "${optionCharacterSet}" ]]; then - optionCharacterSet=$(Database::query dbFromInstanceTargetMysql \ - "SELECT default_character_set_name FROM information_schema.SCHEMATA WHERE schema_name = \"${fromDbName}\";" "information_schema") - fi -} diff --git a/src/_includes/_initFrameworkVariables.tpl b/src/_includes/_initFrameworkVariables.tpl deleted file mode 100644 index 69ea1622..00000000 --- a/src/_includes/_initFrameworkVariables.tpl +++ /dev/null @@ -1,20 +0,0 @@ -.DELIMS stmt="%" -% if [[ -n "${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" ]]; then -BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/<% ${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR} %>" && pwd -P)" -if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then - FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" -else - # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR - FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" -fi -FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" -FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" - -# @require BashTools::Conf::requireLoad -if [[ -f "${HOME}/.bash-tools/.env" ]]; then - export BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") -fi -% fi -.RESET-DELIMS diff --git a/src/batsHeaders.sh b/src/batsHeaders.sh index 4bbf92db..d9786497 100755 --- a/src/batsHeaders.sh +++ b/src/batsHeaders.sh @@ -7,23 +7,8 @@ vendorDir="${rootDir}/vendor" FRAMEWORK_ROOT_DIR="$(cd "$(readlink -e "${vendorDir}/bash-tools-framework")" && pwd -P)" export FRAMEWORK_ROOT_DIR -load "${FRAMEWORK_ROOT_DIR}/vendor/bats-support/load.bash" -load "${FRAMEWORK_ROOT_DIR}/vendor/bats-assert/load.bash" -load "${FRAMEWORK_ROOT_DIR}/vendor/bats-mock-Flamefire/load.bash" - -# shellcheck source=vendor/bash-tools-framework/src/_includes/_mandatoryHeader.sh -source "${FRAMEWORK_ROOT_DIR}/src/_includes/_mandatoryHeader.sh" - -# shellcheck source=vendor/bash-tools-framework/src/_standalone/Bats/assert_lines_count.sh -source "${FRAMEWORK_ROOT_DIR}/src/_standalone/Bats/assert_lines_count.sh" -# shellcheck source=vendor/bash-tools-framework/src/Env/__all.sh -source "${FRAMEWORK_ROOT_DIR}/src/Env/__all.sh" -# shellcheck source=vendor/bash-tools-framework/src/Log/__all.sh -source "${FRAMEWORK_ROOT_DIR}/src/Log/__all.sh" -# shellcheck source=vendor/bash-tools-framework/src/UI/theme.sh -source "${FRAMEWORK_ROOT_DIR}/src/UI/theme.sh" -# shellcheck source=vendor/bash-tools-framework/src/Assert/tty.sh -source "${FRAMEWORK_ROOT_DIR}/src/Assert/tty.sh" +# shellcheck source=vendor/bash-tools-framework/src/batsHeaders.sh +source "${FRAMEWORK_ROOT_DIR}/src/batsHeaders.sh" export DISPLAY_DURATION=0 export BASH_FRAMEWORK_LOG_FILE="${BATS_TEST_TMPDIR}/logFile" From b2bbc60529aa021bc6aae018ee44c88fa39dd8fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Sat, 31 Aug 2024 19:53:26 +0200 Subject: [PATCH 06/24] compiled dbImportProfile using go compiler --- .framework-config | 2 +- .pre-commit-config-github.yaml | 6 +- .pre-commit-config.yaml | 6 +- Commands.tmpl.md | 23 +- bin/dbImport | 47 +- bin/dbImportProfile | 3023 ++++++++++------- bin/doc | 14 +- bin/install | 12 +- bin/installRequirements | 12 +- bin/mysql2puml | 18 +- src/BashTools/Conf/requireLoad.bats | 1 - .../mysql2puml/binary-mysql2puml.yaml | 2 +- .../mysql2puml/testsData/mysql2puml.help.txt | 18 +- .../Database/dbImport/binary-dbImport.yaml | 4 +- .../Database/dbImport/dbImport-main.sh | 6 - .../Database/dbImport/dbImport-options.sh | 14 + src/_binaries/Database/dbImport/dbImport.bats | 2 +- .../dbImport/testsData/dbImport.help.txt | 32 +- .../testsData/dbImportProfile.help.txt | 89 - .../testsData/dbImportStream.help.txt | 92 - .../dbImport/testsData/dbImportTableDump.sql | 35 - .../dbImport/testsData/empty-dump.sql | 1 - .../testsData/expectedDbImportTableDump.sql | 42 - .../expectedDbImportTableDumpRenamed.sql | 42 - .../dbImport/testsData/tableSizeQuery.sql | 1 - .../binary-dbImportProfile.yaml | 67 + .../dbImportProfile/dbImportProfile-main.sh | 67 + .../dbImportProfile-options.sh | 84 + .../dbImportProfile}/dbImportProfile.bats | 2 +- .../testsData/auto_default.local_fromDb_20.sh | 0 .../testsData/auto_default.local_fromDb_70.sh | 0 .../testsData/dbImportProfile.help.txt | 91 + .../testsData/dbImportProfile.tableList1 | 0 .../testsData/dbImportProfiles/all.sh | 4 + .../testsData/dbImportProfiles/default.sh | 15 + .../testsData/dbImportProfiles/none.sh | 6 + .../testsData/dsn/default.local.env | 13 + .../testsData/dsn/default.remote.env | 12 + .../testsData/dsn/localhost-root.env | 12 + .../expectedDbImportProfileTableListQuery.sql | 0 src/_binaries/DbImport/dbImport.options.tpl | 141 - src/_binaries/DbImport/dbImport.sh | 215 -- .../DbImport/dbImportProfile.options.tpl | 110 - src/_binaries/DbImport/dbImportProfile.sh | 85 - .../DbImport/dbImportStream.options.tpl | 2 +- .../testsData/dbImportProfile.help.txt | 89 - .../dbQueryAllDatabases.options.tpl | 2 +- src/_binaries/build/doc/doc-main.sh | 2 +- .../optionsMysqlCollationName.yaml | 2 +- .../optionsMysqlSource.yaml | 4 +- .../optionsMysqlTarget.yaml | 4 +- .../commandDefinitions/optionsProfile.yaml | 4 +- 52 files changed, 2243 insertions(+), 2334 deletions(-) delete mode 100644 src/_binaries/Database/dbImport/testsData/dbImportProfile.help.txt delete mode 100644 src/_binaries/Database/dbImport/testsData/dbImportStream.help.txt delete mode 100644 src/_binaries/Database/dbImport/testsData/dbImportTableDump.sql delete mode 100644 src/_binaries/Database/dbImport/testsData/empty-dump.sql delete mode 100644 src/_binaries/Database/dbImport/testsData/expectedDbImportTableDump.sql delete mode 100644 src/_binaries/Database/dbImport/testsData/expectedDbImportTableDumpRenamed.sql delete mode 100644 src/_binaries/Database/dbImport/testsData/tableSizeQuery.sql create mode 100644 src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml create mode 100755 src/_binaries/Database/dbImportProfile/dbImportProfile-main.sh create mode 100755 src/_binaries/Database/dbImportProfile/dbImportProfile-options.sh rename src/_binaries/{DbImport => Database/dbImportProfile}/dbImportProfile.bats (98%) rename src/_binaries/Database/{dbImport => dbImportProfile}/testsData/auto_default.local_fromDb_20.sh (100%) rename src/_binaries/Database/{dbImport => dbImportProfile}/testsData/auto_default.local_fromDb_70.sh (100%) create mode 100644 src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt rename src/_binaries/Database/{dbImport => dbImportProfile}/testsData/dbImportProfile.tableList1 (100%) create mode 100755 src/_binaries/Database/dbImportProfile/testsData/dbImportProfiles/all.sh create mode 100755 src/_binaries/Database/dbImportProfile/testsData/dbImportProfiles/default.sh create mode 100755 src/_binaries/Database/dbImportProfile/testsData/dbImportProfiles/none.sh create mode 100644 src/_binaries/Database/dbImportProfile/testsData/dsn/default.local.env create mode 100644 src/_binaries/Database/dbImportProfile/testsData/dsn/default.remote.env create mode 100644 src/_binaries/Database/dbImportProfile/testsData/dsn/localhost-root.env rename src/_binaries/Database/{dbImport => dbImportProfile}/testsData/expectedDbImportProfileTableListQuery.sql (100%) delete mode 100644 src/_binaries/DbImport/dbImport.options.tpl delete mode 100755 src/_binaries/DbImport/dbImport.sh delete mode 100644 src/_binaries/DbImport/dbImportProfile.options.tpl delete mode 100755 src/_binaries/DbImport/dbImportProfile.sh delete mode 100644 src/_binaries/DbImport/testsData/dbImportProfile.help.txt diff --git a/.framework-config b/.framework-config index c7922347..f89ee4da 100755 --- a/.framework-config +++ b/.framework-config @@ -32,7 +32,7 @@ FRAMEWORK_FUNCTIONS_IGNORE_REGEXP="${FRAMEWORK_FUNCTIONS_IGNORE_REGEXP:-^(Namesp # describe the files that do not contain function to be imported NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|\.framework-config|^test\.sh$|^\.github/preCommitGeneration\.sh$|^install$|\.bats$|/testsData/|^manualTests/|/_\.sh$|/ZZZ\.sh$|/__all\.sh$|^src/_binaries|^src/_includes|^src/batsHeaders\.sh$|^conf/)}" # describe the files that are allowed to not have an associated bats file -BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^conf/|^bin/|^\.github/preCommitGeneration\.sh$|.framework-config|^install$|\.bats$|/testsData/|^manualTests/|/_\.sh$|/ZZZ\.sh$|/__all\.sh$|^src/batsHeaders\.sh$|^src/_includes)}" +BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^conf/|^bin/|^\.github/preCommitGeneration\.sh$|.framework-config|^install$|\.bats$|/testsData/|^manualTests/|/_\.sh$|/ZZZ\.sh$|/__all\.sh$|^src/batsHeaders\.sh$|^src/_includes|^src/_binaries/.*-(options|main)\.sh$|^conf/|^src/_binaries/commandDefinitions/options.*\.sh$|/testsData/|^test\.sh$)}" # describe the files that are allowed to not have a function matching the filename FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^conf/|^bin/|^\.framework-config$|\.tpl$|testsData/binaryFile$}" # Source directories diff --git a/.pre-commit-config-github.yaml b/.pre-commit-config-github.yaml index 200b46a0..0b065dce 100644 --- a/.pre-commit-config-github.yaml +++ b/.pre-commit-config-github.yaml @@ -161,7 +161,7 @@ repos: exclude: /testsData/ - repo: https://github.com/fchastanet/bash-tools-framework - rev: 5.0.0 + rev: 5.1.0 hooks: - id: fixShebangExecutionBit - id: awkLint @@ -171,7 +171,7 @@ repos: args: [ --expected-warnings-count, - "6", + "4", --format, plain, --theme, @@ -205,6 +205,6 @@ repos: stages: [manual] # GITHUB - repo: https://github.com/fchastanet/bash-compiler - rev: 0.3.0 + rev: 0.3.1 hooks: - id: buildBashBinaries diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f8a4606e..2de7aa40 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -156,7 +156,7 @@ repos: exclude: /testsData/ - repo: https://github.com/fchastanet/bash-tools-framework - rev: 5.0.0 + rev: 5.1.0 hooks: - id: fixShebangExecutionBit - id: awkLint @@ -166,7 +166,7 @@ repos: args: [ --expected-warnings-count, - "6", + "4", --format, plain, --theme, @@ -200,6 +200,6 @@ repos: stages: [] # GITHUB - repo: https://github.com/fchastanet/bash-compiler - rev: 0.3.0 + rev: 0.3.1 hooks: - id: buildBashBinaries diff --git a/Commands.tmpl.md b/Commands.tmpl.md index 60f6a14b..05797617 100644 --- a/Commands.tmpl.md +++ b/Commands.tmpl.md @@ -1,10 +1,11 @@ # The commands - [1. Build tools](#1-build-tools) - - [1.1. bin/installRequirements](#11-bininstallrequirements) - - [1.2. bin/waitForIt](#12-binwaitforit) - - [1.3. bin/waitForMysql](#13-binwaitformysql) - - [1.4. bin/doc](#14-bindoc) + - [1.1. bin/install](#11-bininstall) + - [1.2. bin/installRequirements](#12-bininstallrequirements) + - [1.3. bin/waitForIt](#13-binwaitforit) + - [1.4. bin/waitForMysql](#14-binwaitformysql) + - [1.5. bin/doc](#15-bindoc) - [2. Converter and Generator tools](#2-converter-and-generator-tools) - [2.1. bin/mysql2puml](#21-binmysql2puml) - [2.1.1. Help](#211-help) @@ -36,25 +37,31 @@ ## 1. Build tools -### 1.1. bin/installRequirements +### 1.1. bin/install + +```text +@@@install_help@@@ +``` + +### 1.2. bin/installRequirements ```text @@@installRequirements_help@@@ ``` -### 1.2. bin/waitForIt +### 1.3. bin/waitForIt ```text @@@waitForIt_help@@@ ``` -### 1.3. bin/waitForMysql +### 1.4. bin/waitForMysql ```text @@@waitForMysql_help@@@ ``` -### 1.4. bin/doc +### 1.5. bin/doc ```text @@@doc_help@@@ diff --git a/bin/dbImport b/bin/dbImport index 03f36544..e0425b9b 100755 --- a/bin/dbImport +++ b/bin/dbImport @@ -1686,6 +1686,13 @@ declare versionNumber="2.0" declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" declare defaultTargetCharacterSet="" +declare TIMEFORMAT='time spent : %3R' +declare DOWNLOAD_DUMP=0 + +declare DB_IMPORT_DUMP_DIR +declare PROFILES_DIR +declare HOME_PROFILES_DIR + beforeParseCallback() { BashTools::Conf::requireLoad Env::requireLoad @@ -1695,6 +1702,13 @@ beforeParseCallback() { Linux::requireExecutedAsUser } +initConf() { + # shellcheck disable=SC2034 + DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR%/} + PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbImportProfiles" + HOME_PROFILES_DIR="${HOME}/.bash-tools/dbImportProfiles" +} + optionHelpCallback() { dbImportCommandHelp exit 0 @@ -2431,7 +2445,7 @@ dbImportCommandParse() { elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && options_parse_parsedArgIndex < maxParsedArgIndex1 )); then if ((options_parse_argParsedCountFromDbName >= 1 )); then - Log::displayError "Command ${SCRIPT_NAME} - Argument - Maximum number of argument occurrences reached(1)" + Log::displayError "Command ${SCRIPT_NAME} - Argument fromDbName - Maximum number of argument occurrences reached(1)" return 1 fi ((++options_parse_argParsedCountFromDbName)) @@ -2494,7 +2508,7 @@ dbImportCommandParse() { if ((options_parse_argParsedCountFromDbName < 1 )); then - Log::displayError "Command ${SCRIPT_NAME} - Argument '' should be provided at least 1 time(s)" + Log::displayError "Command ${SCRIPT_NAME} - Argument 'fromDbName' should be provided at least 1 time(s)" return 1 fi || return $? @@ -2521,7 +2535,7 @@ dbImportCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--collation-name|-o <>]" "[--target-dsn|-t <>]" "[--character-set|-c <>]" "[--profile|-p <>]" "[--tables <>]" "[--skip-schema|-s]" "[--from-dsn|-f <>]" "[--from-aws|-a <>]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" + optionsAltList=("[--collation-name|-o ]" "[--target-dsn|-t ]" "[--character-set|-c ]" "[--profile|-p ]" "[--tables ]" "[--skip-schema|-s]" "[--from-dsn|-f ]" "[--from-aws|-a ]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "dbImport" "${optionsAltList[@]}" @@ -2533,11 +2547,11 @@ dbImportCommandHelp() { echo echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}${__HELP_NORMAL}{single} (mandatory) + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}fromDbName${__HELP_NORMAL} {single} (mandatory) " Array::wrap2 ' ' 76 4 " " "The name of the source/remote database." echo - Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}${__HELP_NORMAL}{single}] + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}${__HELP_NORMAL} {single}] " Array::wrap2 ' ' 76 4 " " "The name of the target database" "Default value: (without extension)" "" echo @@ -2560,14 +2574,14 @@ dbImportCommandHelp() { Array::wrap2 ' ' 76 6 " Default value: " "default.local" echo - echo -e " ${__HELP_OPTION_COLOR}--character-set${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-c ${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--character-set${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-c ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Change the character set used during database creation." echo echo echo -e "${__HELP_TITLE_COLOR}PROFILE OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--profile${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-p ${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--profile${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-p ${__HELP_NORMAL} {single}" profileOptionHelpFunction @@ -2613,7 +2627,7 @@ dbImportCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config bash-framework-config${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." echo @@ -2637,26 +2651,26 @@ dbImportCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--env-file env-file${__HELP_NORMAL} {list} (optional)" + echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" echo - echo -e " ${__HELP_OPTION_COLOR}--log-level log-level${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" echo - echo -e " ${__HELP_OPTION_COLOR}--log-file log-file${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log file" echo - echo -e " ${__HELP_OPTION_COLOR}--display-level display-level${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set display level" echo Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" @@ -2669,7 +2683,7 @@ dbImportCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--theme theme${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." echo Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" @@ -2721,6 +2735,7 @@ dbImportCommandHelp() { beforeParseCallback +initConf dbImportCommandParse "$@" MAIN_FUNCTION_NAME="main" @@ -2735,12 +2750,6 @@ Compiler::Embed::extractFileFromBase64 \ declare -gx embed_file_dumpSizeQuery="${PERSISTENT_TMPDIR:-/tmp}/95e2e89e049fcfcd74a8b75390f067bf/dumpSizeQuery" -declare TIMEFORMAT='time spent : %3R' -declare DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR%/} -declare PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbImportProfiles" -declare HOME_PROFILES_DIR="${HOME}/.bash-tools/dbImportProfiles" -declare DOWNLOAD_DUMP=0 - # dump header/footer read -r -d '\0' DUMP_HEADER <<-EOM SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS = 0; diff --git a/bin/dbImportProfile b/bin/dbImportProfile index 8be14864..dafd0bca 100755 --- a/bin/dbImportProfile +++ b/bin/dbImportProfile @@ -1,13 +1,12 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FACADE FROM https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/DbImport/dbImportProfile.sh +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml # DO NOT EDIT IT # @generated ############################################################################### # shellcheck disable=SC2288,SC2034 -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/dbImportProfile -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE + + # ensure that no user aliases could interfere with # commands used in this script @@ -62,13 +61,6 @@ interruptManagement() { kill -s INT "$$" } trap interruptManagement INT -SCRIPT_NAME=${0##*/} -REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" -if [[ -n "${EMBED_CURRENT_DIR}" ]]; then - CURRENT_DIR="${EMBED_CURRENT_DIR}" -else - CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" -fi ################################################ # Temp dir management @@ -102,6 +94,20 @@ cleanOnExit() { } trap cleanOnExit EXIT HUP QUIT ABRT TERM + +SCRIPT_NAME=${0##*/} +REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" +if [[ -n "${EMBED_CURRENT_DIR}" ]]; then + CURRENT_DIR="${EMBED_CURRENT_DIR}" +else + CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" +fi +FRAMEWORK_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" +FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" +FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + # @description Log namespace provides 2 kind of functions # - Log::display* allows to display given message with # given display level @@ -132,376 +138,358 @@ export __VERBOSE_LEVEL_DEBUG=2 # @description verbose level info export __VERBOSE_LEVEL_TRACE=3 -# @description Display message using info color (bg light blue/fg white) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayInfo() { - local type="${2:-INFO}" - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description concatenate each element of an array with a separator +# but wrapping text when line length is more than provided argument +# The algorithm will try not to cut the array element if it can. +# - if an arg can be placed on current line it will be, +# otherwise current line is printed and arg is added to the new +# current line +# - Empty arg is interpreted as a new line. +# - Add \r to arg in order to force break line and avoid following +# arg to be concatenated with current arg. +# +# @arg $1 glue:String +# @arg $2 maxLineLength:int +# @arg $3 indentNextLine:int +# @arg $@ array:String[] +Array::wrap2() { + local glue="${1-}" + local -i glueLength="${#glue}" + shift || true + local -i maxLineLength=$1 + shift || true + local -i indentNextLine=$1 + shift || true + local indentStr="" + if ((indentNextLine > 0)); then + indentStr="$(head -c "${indentNextLine}" 0)); do + arg="$1" + shift || true + + # replace tab by 2 spaces + arg="${arg//$'\t'/ }" + # remove trailing spaces + arg="${arg%[[:blank:]]}" + if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then + printCurrentLine + ((previousLineEmpty = 1)) + continue + else + if ((previousLineEmpty == 1)); then + printCurrentLine + fi + ((previousLineEmpty = 0)) || true + fi + # convert eol to args + mapfile -t additionalLines <<<"${arg}" + if ((${#additionalLines[@]} > 1)); then + set -- "${additionalLines[@]}" "$@" + continue + fi + + ((argLength = ${#arg})) || true + + # empty arg + if ((argLength == 0)); then + if ((isNewline == 0)); then + # isNewline = 0 means currentLine is not empty + printCurrentLine + fi + continue + fi + + if ((isNewline == 0)); then + glueLength="${#glue}" + else + glueLength="0" + fi + if ((currentLineLength + argLength + glueLength > maxLineLength)); then + if ((argLength + glueLength > maxLineLength)); then + # arg is too long to even fit on one line + # we have to split the arg on current and next line + local -i remainingLineLength + ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) + appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" + printCurrentLine + arg="${arg:${remainingLineLength}}" + # remove leading spaces + arg="${arg##[[:blank:]]}" + + set -- "${arg}" "$@" + else + # the arg can fit on next line + printCurrentLine + appendToCurrentLine "${arg}" "${argLength}" + fi + else + appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" + fi + done + if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then + printCurrentLine + fi + ) | sed -E -e 's/[[:blank:]]+$//' } -# @description Display message using debug color (gray) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayDebug() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then - Log::computeDuration - echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logDebug "$1" + +# @description check if command specified exists or return 1 +# with error and message if not +# +# @arg $1 commandName:String on which existence must be checked +# @arg $2 helpIfNotExists:String a help command to display if the command does not exist +# +# @exitcode 1 if the command specified does not exist +# @stderr diagnostic information + help if second argument is provided +Assert::commandExists() { + local commandName="$1" + local helpIfNotExists="$2" + + "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { + Log::displayError "${commandName} is not installed, please install it" + if [[ -n "${helpIfNotExists}" ]]; then + Log::displayInfo "${helpIfNotExists}" + fi + return 1 + } + return 0 } -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description check if tty (interactive mode) is active +# @noargs +# @exitcode 1 if tty not active +# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive +# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive +Assert::tty() { + if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then + return 1 fi - Log::logWarning "$1" + if [[ "${INTERACTIVE:-0}" = "1" ]]; then + return 0 + fi + tty -s } -# @description Display message using error color (red) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayError() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then - Log::computeDuration - echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logError "$1" + +# @description ignore exit code 141 from simple command pipes +# @example use with: +# local resultingStatus=0 +# local -a originalPipeStatus=() +# cmd1 | cmd2 || Bash::handlePipelineFailure resultingStatus originalPipeStatus || true +# [[ "${resultingStatus}" = "0" ]] +# @arg $1 resultingStatusCode:&int (passed by reference) (optional) resulting status code +# @arg $2 originalStatus:int[] (passed by reference) (optional) copy of original PIPESTATUS array +# @env PIPESTATUS assuming that this function is called like in the example provided +# @see https://unix.stackexchange.com/a/709880/582856 +Bash::handlePipelineFailure() { + local -a pipeStatusBackup=("${PIPESTATUS[@]}") + local -n handlePipelineFailure_resultingStatusCode=$1 + local -n handlePipelineFailure_originalStatus=$2 + # shellcheck disable=SC2034 + handlePipelineFailure_originalStatus=("${pipeStatusBackup[@]}") + handlePipelineFailure_resultingStatusCode=0 + local statusCode + for statusCode in "${pipeStatusBackup[@]}"; do + if ((statusCode == 141)); then + return 0 + elif ((statusCode > 0)); then + # shellcheck disable=SC2034 + handlePipelineFailure_resultingStatusCode="${statusCode}" + break + fi + done + return "${handlePipelineFailure_resultingStatusCode}" } -# @description load colors theme constants -# @warning if tty not opened, noColor theme will be chosen -# @arg $1 theme:String the theme to use (default, noColor) -# @arg $@ args:String[] -# @set __ERROR_COLOR String indicate error status -# @set __INFO_COLOR String indicate info status -# @set __SUCCESS_COLOR String indicate success status -# @set __WARNING_COLOR String indicate warning status -# @set __SKIPPED_COLOR String indicate skipped status -# @set __DEBUG_COLOR String indicate debug status -# @set __HELP_COLOR String indicate help status -# @set __TEST_COLOR String not used -# @set __TEST_ERROR_COLOR String not used -# @set __HELP_TITLE_COLOR String used to display help title in help strings -# @set __HELP_OPTION_COLOR String used to display highlight options in help strings -# -# @set __RESET_COLOR String reset default color -# -# @set __HELP_EXAMPLE String to remove -# @set __HELP_TITLE String to remove -# @set __HELP_NORMAL String to remove -# shellcheck disable=SC2034 -UI::theme() { - local theme="${1-default}" - if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then - theme="noColor" - fi - case "${theme}" in - default | default-force) - theme="default" - ;; - noColor) ;; - *) - Log::fatal "invalid theme provided" - ;; - esac - if [[ "${theme}" = "default" ]]; then - BASH_FRAMEWORK_THEME="default" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='\e[31m' # Red - __INFO_COLOR='\e[44m' # white on lightBlue - __SUCCESS_COLOR='\e[32m' # Green - __WARNING_COLOR='\e[33m' # Yellow - __SKIPPED_COLOR='\e[33m' # Yellow - __DEBUG_COLOR='\e[37m' # Gray - __HELP_COLOR='\e[7;49;33m' # Black on Gold - __TEST_COLOR='\e[100m' # Light magenta - __TEST_ERROR_COLOR='\e[41m' # white on red - __HELP_TITLE_COLOR="\e[1;37m" # Bold - __HELP_OPTION_COLOR="\e[1;34m" # Blue - # Internal: reset color - __RESET_COLOR='\e[0m' # Reset Color - # shellcheck disable=SC2155,SC2034 - __HELP_EXAMPLE="$(echo -e "\e[2;97m")" - # shellcheck disable=SC2155,SC2034 - __HELP_TITLE="$(echo -e "\e[1;37m")" - # shellcheck disable=SC2155,SC2034 - __HELP_NORMAL="$(echo -e "\033[0m")" - else - BASH_FRAMEWORK_THEME="noColor" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='' - __INFO_COLOR='' - __SUCCESS_COLOR='' - __WARNING_COLOR='' - __SKIPPED_COLOR='' - __DEBUG_COLOR='' - __HELP_COLOR='' - __TEST_COLOR='' - __TEST_ERROR_COLOR='' - __HELP_TITLE_COLOR='' - __HELP_OPTION_COLOR='' - # Internal: reset color - __RESET_COLOR='' - __HELP_EXAMPLE='' - __HELP_TITLE='' - __HELP_NORMAL='' - fi -} - -# @description draw a line with the character passed in parameter repeated depending on terminal width -# @arg $1 character:String character to use as separator (default value #) -UI::drawLine() { - local character="${1:-#}" - local -i width=${COLUMNS:-0} - if ((width == 0)) && [[ -t 1 ]]; then - width=$(tput cols) - fi - if ((width == 0)); then - width=80 - fi - printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") - echo -} -# @description Display message using error color (red) and exit immediately with error status 1 -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::fatal() { - Log::computeDuration - echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - Log::logFatal "$1" - exit 1 -} +# @description loads ~/.bash-tools/.env if available +# if not creates it from a default template +# else check if new options need to be added +BashTools::Conf::requireLoad() { +Linux::requireTarCommand +Compiler::Embed::extractFileFromBase64 \ + "${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" \ + "IyEvdXNyL2Jpbi9lbnYgYmFzaAojIHNoZWxsY2hlY2sgZGlzYWJsZT1TQzIwMzQKCiMgRGVmYXVsdCBzZXR0aW5ncwojIHlvdSBjYW4gb3ZlcnJpZGUgdGhlc2Ugc2V0dGluZ3MgYnkgY3JlYXRpbmcgJHtIT01FfS8uYmFzaC10b29scy8uZW52IGZpbGUKCiMjIwojIyMgRElTUExBWSBMZXZlbAojIyMgbWluaW11bSBsZXZlbCBvZiB0aGUgbWVzc2FnZXMgdGhhdCB3aWxsIGJlIGRpc3BsYXllZCBvbiBzY3JlZW4KIyMjCiMjIyAwOiBOTyBMT0cKIyMjIDE6IEVSUk9SCiMjIyAyOiBXQVJOSU5HCiMjIyAzOiBJTkZPCiMjIyA0OiBERUJVRwojIyMKQkFTSF9GUkFNRVdPUktfRElTUExBWV9MRVZFTD0ke0JBU0hfRlJBTUVXT1JLX0RJU1BMQVlfTEVWRUw6LTN9CgojIyMKIyMjIERJU1BMQVkgZHVyYXRpb24KIyMjIDA6IG5vIGR1cmF0aW9uIGlzIGRpc3BsYXllZCBvbiB0aGUgbWVzc2FnZXMKIyMjIDE6IGR1cmF0aW9uIGJldHdlZW4gcHJldmlvdXMgbWVzc2FnZSBhbmQgY3VycmVudCBpcyBkaXNwbGF5ZWQKIyMjIHdpdGggdGhlIG1lc3NhZ2UKIyMjCkRJU1BMQVlfRFVSQVRJT049JHtESVNQTEFZX0RVUkFUSU9OOjB9CgojIyMKIyMjIExvZyB0byBmaWxlCiMjIwojIyMgYWxsIGxvZyBtZXNzYWdlcyB3aWxsIGJlIHJlZGlyZWN0ZWQgdG8gbG9nIGZpbGUgc3BlY2lmaWVkCiMjIyB0aGlzIHNhbWUgcGF0aCB3aWxsIGJlIHVzZWQgaW5zaWRlIGFuZCBvdXRzaWRlIG9mIHRoZSBjb250YWluZXIKIyMjCkJBU0hfRlJBTUVXT1JLX0xPR19GSUxFPSR7QkFTSF9GUkFNRVdPUktfTE9HX0ZJTEU6LSR7RlJBTUVXT1JLX1JPT1RfRElSfS9sb2dzL2Jhc2gubG9nfQoKIyMjCiMjIyBMT0cgTGV2ZWwKIyMjIG1pbmltdW0gbGV2ZWwgb2YgdGhlIG1lc3NhZ2VzIHRoYXQgd2lsbCBiZSBsb2dnZWQgaW50byBMT0dfRklMRQojIyMKIyMjIDA6IE5PIExPRwojIyMgMTogRVJST1IKIyMjIDI6IFdBUk5JTkcKIyMjIDM6IElORk8KIyMjIDQ6IERFQlVHCiMjIwpCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw9JHtCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw6LTB9CgojIGFic29sdXRlIGRpcmVjdG9yeSBjb250YWluaW5nIGRiIGltcG9ydCBzcWwgZHVtcHMKREJfSU1QT1JUX0RVTVBfRElSPSR7REJfSU1QT1JUX0RVTVBfRElSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2RiSW1wb3J0RHVtcHN9CgojIGdhcmJhZ2UgY29sbGVjdCBhbGwgZmlsZXMgZm9yIHdoaWNoIG1vZGlmaWNhdGlvbiBpcyBncmVhdGVyIHRoYW4gZWc6IDMwIGRheXMgKCszMCkKIyBlYWNoIHRpbWUgYW4gZXhpc3RpbmcgZmlsZSBpcyB1c2VkIGJ5IGRiSW1wb3J0L2RiSW1wb3J0VGFibGUKIyB0aGUgZmlsZSBtb2RpZmljYXRpb24gdGltZSBpcyBzZXQgdG8gbm93CkRCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUz0ke0RCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUzotKzMwfQoKIyBhYnNvbHV0ZSBkaXJlY3RvcnkgY29udGFpbmluZyBkYlNjcmlwdHMgdXNlZCBieSBkYlNjcmlwdEFsbERhdGFiYXNlcwpTQ1JJUFRTX0ZPTERFUj0ke1NDUklQVFNfRk9MREVSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2NvbmYvZGJTY3JpcHRzfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEFXUyBQYXJhbWV0ZXJzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KUzNfQkFTRV9VUkw9JHtTM19CQVNFX1VSTDotfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvc3RtYW4gUGFyYW1ldGVycwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClBPU1RNQU5fQVBJX0tFWT0K" \ + "755" -# @description create a temp file using default TMPDIR variable -# initialized in _includes/_commonHeader.sh -# @env TMPDIR String (default value /tmp) -# @arg $1 templateName:String template name to use(optional) -Framework::createTempFile() { - mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" -} +declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" -# @description ensure env files are loaded -# @arg $@ list of default files to load at the end -# @exitcode 1 if one of env files fails to load -# @stderr diagnostics information is displayed -# shellcheck disable=SC2120 -Env::requireLoad() { - local -a defaultFiles=("$@") - # get list of possible config files - local -a configFiles=() - if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then - # BASH_FRAMEWORK_ENV_FILES is an array - configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") - fi - local localFrameworkConfigFile - localFrameworkConfigFile="$(pwd)/.framework-config" - if [[ -f "${localFrameworkConfigFile}" ]]; then - configFiles+=("${localFrameworkConfigFile}") + BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" + if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then + FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" + else + # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR + FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" fi - if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then - configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") + # shellcheck disable=SC2034 + FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" + # shellcheck disable=SC2034 + FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + + if [[ -f "${HOME}/.bash-tools/.env" ]]; then + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") fi - configFiles+=("${optionEnvFiles[@]}") - configFiles+=("${defaultFiles[@]}") - for file in "${configFiles[@]}"; do - # shellcheck source=/.framework-config - CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { - Log::displayError "while loading config file: ${file}" - return 1 - } - done + local envFile="${HOME}/.bash-tools/.env" + if [[ ! -f "${envFile}" ]]; then + mkdir -p "${HOME}/.bash-tools" + ( + echo "#!/usr/bin/env bash" + # shellcheck disable=SC2154 + echo "${embed_file_bashToolsDefaultConfigTemplate}" + ) >"${envFile}" + Log::displayInfo "Configuration file '${envFile}' created" + else + if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then + ( + echo '# -----------------------------------------------------' + echo '# Postman Parameters' + echo '# -----------------------------------------------------' + echo 'POSTMAN_API_KEY=' + ) >>"${envFile}" + fi + fi + # shellcheck source=/conf/.env + source "${envFile}" || { + Log::displayError "impossible to load '${envFile}'" + exit 1 + } } -# @description activate or not Log::display* and Log::log* functions -# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL -# environment variables loaded by Env::requireLoad -# try to create log file and rotate it if necessary -# @noargs -# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable -# @env BASH_FRAMEWORK_DISPLAY_LEVEL int -# @env BASH_FRAMEWORK_LOG_LEVEL int -# @env BASH_FRAMEWORK_LOG_FILE String -# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 -# @exitcode 0 always successful -# @stderr diagnostics information about log file is displayed -# @require Env::requireLoad -# @require UI::requireTheme -Log::requireLoad() { - if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - export BASH_FRAMEWORK_LOG_LEVEL - fi - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then - if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 - fi - elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 +# @description convert base64 encoded back to target file +# if target file is executable prepend dir of target +# file to PATH to make binary available everywhere +# it is advised to include in the path of the target file +# the md5sum of the binFile +# +# @arg $1 targetFile:String the file to write +# @arg $2 binFileBase64:String the base64 encoded file +# @arg $3 fileMode:String the chmod to set on the file +# @set PATH String prepend target embedded file binary directory to PATH variable if binary executable +Compiler::Embed::extractFileFromBase64() { + local targetFile="$1" + local binFileBase64="$2" + local fileMode="${3:-+x}" + local targetDir="${targetFile%/*}" + + if [[ ! -f "${targetFile}" ]]; then + if [[ ! -d "${targetDir}" ]]; then + mkdir -p "${targetDir}" fi + base64 -d >"${targetFile}" <<<"${binFileBase64}" + chmod "${fileMode}" "${targetFile}" fi - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - # will always be created even if not in info level - Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" - if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then - Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" - fi + if [[ -x "${targetFile}" ]]; then + Env::pathPrepend "${targetDir}" fi } -# @description concatenate each element of an array with a separator -# but wrapping text when line length is more than provided argument -# The algorithm will try not to cut the array element if it can. -# - if an arg can be placed on current line it will be, -# otherwise current line is printed and arg is added to the new -# current line -# - Empty arg is interpreted as a new line. -# - Add \r to arg in order to force break line and avoid following -# arg to be concatenated with current arg. + +# @description get absolute conf file from specified conf folder deduced using these rules +# * from absolute file (ignores and ) +# * relative to where script is executed (ignores and ) +# * from home/.bash-tools/ +# * from framework conf/ # -# @arg $1 glue:String -# @arg $2 maxLineLength:int -# @arg $3 indentNextLine:int -# @arg $@ array:String[] -Array::wrap2() { - local glue="${1-}" - local -i glueLength="${#glue}" - shift || true - local -i maxLineLength=$1 - shift || true - local -i indentNextLine=$1 - shift || true - local indentStr="" - if ((indentNextLine > 0)); then - indentStr="$(head -c "${indentNextLine}" /dev/null)" + # shellcheck disable=SC2181 + if [[ "$?" = "0" && -f "${result}" ]]; then + echo "${result}" + return 0 fi + return 1 } - ( - local currentLine - local -i currentLineLength=0 isNewline=1 argLength=0 - local -a additionalLines - local -i previousLineEmpty=0 - local arg="" - - while (($# > 0)); do - arg="$1" - shift || true + # conf is absolute file (including extension) + testAbs "${confFolder}${extension}" && return 0 + # conf is absolute file + testAbs "${confFolder}" && return 0 + # conf is absolute file (including extension) + testAbs "${conf}${extension}" && return 0 + # conf is absolute file + testAbs "${conf}" && return 0 - # replace tab by 2 spaces - arg="${arg//$'\t'/ }" - # remove trailing spaces - arg="${arg%[[:blank:]]}" - if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then - printCurrentLine - ((previousLineEmpty = 1)) - continue - else - if ((previousLineEmpty == 1)); then - printCurrentLine - fi - ((previousLineEmpty = 0)) || true - fi - # convert eol to args - mapfile -t additionalLines <<<"${arg}" - if ((${#additionalLines[@]} > 1)); then - set -- "${additionalLines[@]}" "$@" - continue - fi + # relative to where script is executed (including extension) + if [[ -n "${CURRENT_DIR+xxx}" ]]; then + testAbs "$(File::concatenatePath "${CURRENT_DIR}" "${confFolder}")/${conf}${extension}" && return 0 + fi + # from home/.bash-tools/ + testAbs "$(File::concatenatePath "${HOME}/.bash-tools" "${confFolder}")/${conf}${extension}" && return 0 - ((argLength = ${#arg})) || true + if [[ -n "${FRAMEWORK_ROOT_DIR+xxx}" ]]; then + # from framework conf/ (including extension) + testAbs "$(File::concatenatePath "${FRAMEWORK_ROOT_DIR}/conf" "${confFolder}")/${conf}${extension}" && return 0 - # empty arg - if ((argLength == 0)); then - if ((isNewline == 0)); then - # isNewline = 0 means currentLine is not empty - printCurrentLine - fi - continue - fi + # from framework conf/ + testAbs "$(File::concatenatePath "${FRAMEWORK_ROOT_DIR}/conf" "${confFolder}")/${conf}" && return 0 + fi - if ((isNewline == 0)); then - glueLength="${#glue}" - else - glueLength="0" - fi - if ((currentLineLength + argLength + glueLength > maxLineLength)); then - if ((argLength + glueLength > maxLineLength)); then - # arg is too long to even fit on one line - # we have to split the arg on current and next line - local -i remainingLineLength - ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) - appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" - printCurrentLine - arg="${arg:${remainingLineLength}}" - # remove leading spaces - arg="${arg##[[:blank:]]}" + # file not found + Log::displayError "conf file '${conf}' not found" - set -- "${arg}" "$@" - else - # the arg can fit on next line - printCurrentLine - appendToCurrentLine "${arg}" "${argLength}" - fi - else - appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" - fi - done - if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then - printCurrentLine - fi - ) | sed -E -e 's/[[:blank:]]+$//' + return 1 } + # @description list the conf files list available in bash-tools/conf/ folder # and those overridden in ${HOME}/.bash-tools/ folder # @@ -532,28 +520,118 @@ Conf::getMergedList() { ) | sort | uniq } -# @description check if command specified exists or return 1 -# with error and message if not -# -# @arg $1 commandName:String on which existence must be checked -# @arg $2 helpIfNotExists:String a help command to display if the command does not exist + +# @description list files of dir with given extension and display it as a list one by line +# +# @arg $1 dir:String the directory to list +# @arg $2 prefix:String the profile file prefix (default: "") +# @arg $3 ext:String the extension +# @arg $4 findOptions:String find options, eg: -type d (Default value: '-type f') +# @arg $5 indentStr:String the indentation can be any string compatible with sed not containing any / (Default value: ' - ') +# @stdout list of files without extension/directory +# @example text +# - default.local +# - default.remote +# - localhost-root +# @exitcode 1 if directory does not exists +Conf::list() { + local dir="$1" + local prefix="${2:-}" + local ext="${3}" + local findOptions="${4--type f}" + local indentStr="${5- - }" + + if [[ ! -d "${dir}" ]]; then + Log::displayError "Directory ${dir} does not exist" + fi + if [[ -n "${ext}" && "${ext:0:1}" != "." ]]; then + ext=".${ext}" + fi + ( + # shellcheck disable=SC2086 + cd "${dir}" && + find . -maxdepth 1 ${findOptions} -name "${prefix}*${ext}" | + sed -E "s#^\./${prefix}##g" | + sed -E "s#${ext}\$##g" | sort | sed -E "s#^#${indentStr}#" + ) +} + + +# @description check if dsn file has all the mandatory variables set +# Mandatory variables are: HOSTNAME, USER, PASSWORD, PORT +# +# @arg $1 dsnFileName:String dsn absolute filename +# @set HOSTNAME loaded from dsn file +# @set PORT loaded from dsn file +# @set USER loaded from dsn file +# @set PASSWORD loaded from dsn file +# @exitcode 0 on valid file +# @exitcode 1 if one of the properties of the conf file is invalid or if file not found +# @stderr log output if error found in conf file +Database::checkDsnFile() { + local dsnFileName="$1" + if [[ ! -f "${dsnFileName}" ]]; then + Log::displayError "dsn file ${dsnFileName} not found" + return 1 + fi + + ( + unset HOSTNAME PORT PASSWORD USER + # shellcheck source=/src/Database/testsData/dsn_valid.env + source "${dsnFileName}" + if [[ -z ${HOSTNAME+x} ]]; then + Log::displayError "dsn file ${dsnFileName} : HOSTNAME not provided" + return 1 + fi + if [[ -z "${HOSTNAME}" ]]; then + Log::displayWarning "dsn file ${dsnFileName} : HOSTNAME value not provided" + fi + if [[ "${HOSTNAME}" = "localhost" ]]; then + Log::displayWarning "dsn file ${dsnFileName} : check that HOSTNAME should not be 127.0.0.1 instead of localhost" + fi + if [[ -z "${PORT+x}" ]]; then + Log::displayError "dsn file ${dsnFileName} : PORT not provided" + return 1 + fi + if ! [[ ${PORT} =~ ^[0-9]+$ ]]; then + Log::displayError "dsn file ${dsnFileName} : PORT invalid" + return 1 + fi + if [[ -z "${USER+x}" ]]; then + Log::displayError "dsn file ${dsnFileName} : USER not provided" + return 1 + fi + if [[ -z "${PASSWORD+x}" ]]; then + Log::displayError "dsn file ${dsnFileName} : PASSWORD not provided" + return 1 + fi + ) +} + + +# @description check if given database exists # -# @exitcode 1 if the command specified does not exist -# @stderr diagnostic information + help if second argument is provided -Assert::commandExists() { - local commandName="$1" - local helpIfNotExists="$2" +# @arg $1 instanceIfDbExists:&Map (passed by reference) database instance to use +# @arg $2 dbName:String database name +# @exitcode 1 if db doesn't exist +# @stderr debug command +Database::ifDbExists() { + local -n instanceIfDbExists=$1 + local dbName="$2" + local result + local -a mysqlCommand=() - "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { - Log::displayError "${commandName} is not installed, please install it" - if [[ -n "${helpIfNotExists}" ]]; then - Log::displayInfo "${helpIfNotExists}" - fi - return 1 - } - return 0 + mysqlCommand+=(mysqlshow) + mysqlCommand+=("--defaults-extra-file=${instanceIfDbExists['AUTH_FILE']}") + # shellcheck disable=SC2206 + mysqlCommand+=(${instanceIfDbExists['SSL_OPTIONS']}) + mysqlCommand+=("${dbName}") + Log::displayDebug "execute command: '${mysqlCommand[*]}'" + result="$(MSYS_NO_PATHCONV=1 "${mysqlCommand[@]}" 2>/dev/null | grep '^Database: ' | grep -o "${dbName}")" + [[ "${result}" = "${dbName}" ]] } + # @description create a new db instance # Returns immediately if the instance is already initialized # @@ -606,44 +684,12 @@ Database::newInstance() { instanceNewInstance['SKIP_COLUMN_NAMES']="${SKIP_COLUMN_NAMES:-1}" instanceNewInstance['SSL_OPTIONS']="${MYSQL_SSL_OPTIONS:---ssl-mode=DISABLED}" instanceNewInstance['QUERY_OPTIONS']="${MYSQL_QUERY_OPTIONS:---batch --raw --default-character-set=utf8}" - instanceNewInstance['DUMP_OPTIONS']="${MYSQL_DUMP_OPTIONS:---default-character-set=utf8 --compress --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 ${instanceNewInstance['SSL_OPTIONS']}}" + instanceNewInstance['DUMP_OPTIONS']="${MYSQL_DUMP_OPTIONS:---default-character-set=utf8 --compression-algorithms --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 ${instanceNewInstance['SSL_OPTIONS']}}" instanceNewInstance['DB_IMPORT_OPTIONS']="${DB_IMPORT_OPTIONS:---connect-timeout=5 --batch --raw --default-character-set=utf8}" instanceNewInstance['INITIALIZED']=1 } -# @description set the general options to use on mysql command to query the database -# Differs than setOptions in the way that these options could change each time -# -# @arg $1 instanceSetQueryOptions:&Map (passed by reference) database instance to use -# @arg $2 optionsList:String query options list -Database::setQueryOptions() { - local -n instanceSetQueryOptions=$1 - # shellcheck disable=SC2034 - instanceSetQueryOptions['QUERY_OPTIONS']="$2" -} - -# @description check if given database exists -# -# @arg $1 instanceIfDbExists:&Map (passed by reference) database instance to use -# @arg $2 dbName:String database name -# @exitcode 1 if db doesn't exist -# @stderr debug command -Database::ifDbExists() { - local -n instanceIfDbExists=$1 - local dbName="$2" - local result - local -a mysqlCommand=() - - mysqlCommand+=(mysqlshow) - mysqlCommand+=("--defaults-extra-file=${instanceIfDbExists['AUTH_FILE']}") - # shellcheck disable=SC2206 - mysqlCommand+=(${instanceIfDbExists['SSL_OPTIONS']}) - mysqlCommand+=("${dbName}") - Log::displayDebug "execute command: '${mysqlCommand[*]}'" - result="$(MSYS_NO_PATHCONV=1 "${mysqlCommand[@]}" 2>/dev/null | grep '^Database: ' | grep -o "${dbName}")" - [[ "${result}" = "${dbName}" ]] -} # @description mysql query on a given db # @warning could use QUERY_OPTIONS variable from dsn if defined @@ -684,115 +730,113 @@ Database::query() { fi } -bashToolsDefaultConfigTemplate="${bashToolsDefaultConfigTemplate:-$( - cat <<'EOF' -# shellcheck disable=SC2034 -# Default settings -# you can override these settings by creating ${HOME}/.bash-tools/.env file - -### -### DISPLAY Level -### minimum level of the messages that will be displayed on screen -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} - -### -### DISPLAY duration -### 0: no duration is displayed on the messages -### 1: duration between previous message and current is displayed -### with the message -### -DISPLAY_DURATION=${DISPLAY_DURATION:0} - -### -### Log to file -### -### all log messages will be redirected to log file specified -### this same path will be used inside and outside of the container -### -BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} - -### -### LOG Level -### minimum level of the messages that will be logged into LOG_FILE -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} - -# absolute directory containing db import sql dumps -DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} - -# garbage collect all files for which modification is greater than eg: 30 days (+30) -# each time an existing file is used by dbImport/dbImportTable -# the file modification time is set to now -DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} - -# absolute directory containing dbScripts used by dbScriptAllDatabases -SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} - -# ----------------------------------------------------- -# AWS Parameters -# ----------------------------------------------------- -S3_BASE_URL=${S3_BASE_URL:-} - -# ----------------------------------------------------- -# Postman Parameters -# ----------------------------------------------------- -POSTMAN_API_KEY= -EOF -)}" +# @description set the general options to use on mysql command to query the database +# Differs than setOptions in the way that these options could change each time +# +# @arg $1 instanceSetQueryOptions:&Map (passed by reference) database instance to use +# @arg $2 optionsList:String query options list +Database::setQueryOptions() { + local -n instanceSetQueryOptions=$1 + # shellcheck disable=SC2034 + instanceSetQueryOptions['QUERY_OPTIONS']="$2" +} -# @description loads ~/.bash-tools/.env if available -# if not creates it from a default template -# else check if new options need to be added -BashTools::Conf::requireLoad() { - local envFile="${HOME}/.bash-tools/.env" - if [[ ! -f "${envFile}" ]]; then - mkdir -p "${HOME}/.bash-tools" - ( - echo "#!/usr/bin/env bash" - echo "${bashToolsDefaultConfigTemplate}" - ) >"${envFile}" - Log::displayInfo "Configuration file '${envFile}' created" - else - if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then - ( - echo '# -----------------------------------------------------' - echo '# Postman Parameters' - echo '# -----------------------------------------------------' - echo 'POSTMAN_API_KEY=' - ) >>"${envFile}" + +# @description check if all requirements are satisfied +# to execute dbImport commands +Db::checkRequirements() { + if [[ "${SKIP_REQUIREMENTS_CHECKS:-0}" = "1" ]]; then + return 0 + fi + local -i failures=0 + echo + Assert::commandExists mysql "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqldump "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists pv "sudo apt-get install -y pv" || ((++failures)) + Assert::commandExists gawk "sudo apt-get install -y gawk" || ((++failures)) + Assert::commandExists awk "sudo apt-get install -y gawk" || ((++failures)) + Version::checkMinimal "gawk" "--version" "5.0.1" || ((++failures)) + return "${failures}" +} + + +# @description prepend directories to the PATH environment variable +# @arg $@ args:String[] list of directories to prepend +# @set PATH update PATH with the directories prepended +Env::pathPrepend() { + local arg + for arg in "$@"; do + if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then + PATH="$(realpath "${arg}"):${PATH}" fi + done +} + + +# @description ensure env files are loaded +# @arg $@ list of default files to load at the end +# @exitcode 1 if one of env files fails to load +# @stderr diagnostics information is displayed +# shellcheck disable=SC2120 +Env::requireLoad() { + REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 + + local -a defaultFiles=("$@") + # get list of possible config files + local -a configFiles=() + if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then + # BASH_FRAMEWORK_ENV_FILES is an array + configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") fi - # shellcheck source=/conf/.env - source "${envFile}" || { - Log::displayError "impossible to load '${envFile}'" + local localFrameworkConfigFile + localFrameworkConfigFile="$(pwd)/.framework-config" + if [[ -f "${localFrameworkConfigFile}" ]]; then + configFiles+=("${localFrameworkConfigFile}") + fi + if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then + configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") + fi + configFiles+=("${optionEnvFiles[@]}") + configFiles+=("${defaultFiles[@]}") + + for file in "${configFiles[@]}"; do + # shellcheck source=/.framework-config + CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { + Log::displayError "while loading config file: ${file}" + return 1 + } + done +} + + +# @description concatenate 2 paths and ensure the path is correct using realpath -m +# @arg $1 basePath:String +# @arg $2 subPath:String +File::concatenatePath() { + + if [[ "${REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Linux::requireRealpathCommand has not been loaded" exit 1 - } + fi + + local basePath="$1" + local subPath="$2" + local fullPath="${basePath:+${basePath}/}${subPath}" + + realpath -m "${fullPath}" 2>/dev/null } -# @description ensure COMMAND_BIN_DIR env var is set -# and PATH correctly prepared -# @noargs -# @set COMMAND_BIN_DIR string the directory where to find this command -# @set PATH string add directory where to find this command binary -Compiler::Facade::requireCommandBinDir() { - COMMAND_BIN_DIR="${CURRENT_DIR}" - Env::pathPrepend "${COMMAND_BIN_DIR}" + +# @description create a temp file using default TMPDIR variable +# @env TMPDIR String (default value /tmp) +# @arg $1 templateName:String template name to use(optional) +Framework::createTempFile() { + mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" } + # @description ensure running user is not root # @exitcode 1 if current user is root # @stderr diagnostics information is displayed @@ -802,6 +846,25 @@ Linux::requireExecutedAsUser() { fi } + +# @description ensure command realpath is available +# @exitcode 1 if realpath command not available +# @stderr diagnostics information is displayed +Linux::requireRealpathCommand() { + REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED=1 + + Assert::commandExists realpath +} + + +# @description ensure command tar is available +# @exitcode 1 if tar command not available +# @stderr diagnostics information is displayed +Linux::requireTarCommand() { + Assert::commandExists tar +} + + declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" @@ -840,14 +903,72 @@ Log::computeDuration() { fi } -# @description log message to file + +# @description Display message using debug color (gray) # @arg $1 message:String the message to display -Log::logInfo() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then - Log::logMessage "${2:-INFO}" "$1" +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayDebug() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then + Log::computeDuration + echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logDebug "$1" +} + + +# @description Display message using error color (red) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayError() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then + Log::computeDuration + echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logError "$1" +} + + +# @description Display message using info color (bg light blue/fg white) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayInfo() { + local type="${2:-INFO}" + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logInfo "$1" "${type}" +} + + +# @description Display message using warning color (yellow) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayWarning() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then + Log::computeDuration + echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logWarning "$1" +} + + +# @description Display message using error color (red) and exit immediately with error status 1 +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::fatal() { + Log::computeDuration + echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + Log::logFatal "$1" + exit 1 } + # @description log message to file # @arg $1 message:String the message to display Log::logDebug() { @@ -856,13 +977,6 @@ Log::logDebug() { fi } -# @description log message to file -# @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" - fi -} # @description log message to file # @arg $1 message:String the message to display @@ -872,20 +986,6 @@ Log::logError() { fi } -# @description check if tty (interactive mode) is active -# @noargs -# @exitcode 1 if tty not active -# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive -# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive -Assert::tty() { - if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then - return 1 - fi - if [[ "${INTERACTIVE:-0}" = "1" ]]; then - return 0 - fi - tty -s -} # @description log message to file # @arg $1 message:String the message to display @@ -893,6 +993,16 @@ Log::logFatal() { Log::logMessage "${2:-FATAL}" "$1" } + +# @description log message to file +# @arg $1 message:String the message to display +Log::logInfo() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then + Log::logMessage "${2:-INFO}" "$1" + fi +} + + # @description Internal: common log message # @example text # [date]|[levelMsg]|message @@ -906,9 +1016,18 @@ Log::logFatal() { # @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty # @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages # @stderr diagnostics information is displayed -# @require Env::requireLoad -# @require Log::requireLoad Log::logMessage() { + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Log::requireLoad has not been loaded" + exit 1 + fi + local levelMsg="$1" local msg="$2" local date @@ -920,6 +1039,74 @@ Log::logMessage() { fi } + +# @description log message to file +# @arg $1 message:String the message to display +Log::logWarning() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then + Log::logMessage "${2:-WARNING}" "$1" + fi +} + + +# @description activate or not Log::display* and Log::log* functions +# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL +# environment variables loaded by Env::requireLoad +# try to create log file and rotate it if necessary +# @noargs +# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable +# @env BASH_FRAMEWORK_DISPLAY_LEVEL int +# @env BASH_FRAMEWORK_LOG_LEVEL int +# @env BASH_FRAMEWORK_LOG_FILE String +# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 +# @exitcode 0 always successful +# @stderr diagnostics information about log file is displayed +Log::requireLoad() { + REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement UI::requireTheme has not been loaded" + exit 1 + fi + + if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + export BASH_FRAMEWORK_LOG_LEVEL + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then + if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 + fi + elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + # will always be created even if not in info level + Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" + if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then + Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + fi + fi +} + + # @description To be called before logging in the log file # @arg $1 file:string log file name # @arg $2 maxLogFilesCount:int maximum number of log files @@ -942,164 +1129,22 @@ Log::rotate() { fi } -# @description list files of dir with given extension and display it as a list one by line -# -# @arg $1 dir:String the directory to list -# @arg $2 prefix:String the profile file prefix (default: "") -# @arg $3 ext:String the extension -# @arg $4 findOptions:String find options, eg: -type d (Default value: '-type f') -# @arg $5 indentStr:String the indentation can be any string compatible with sed not containing any / (Default value: ' - ') -# @stdout list of files without extension/directory -# @example text -# - default.local -# - default.remote -# - localhost-root -# @exitcode 1 if directory does not exists -Conf::list() { - local dir="$1" - local prefix="${2:-}" - local ext="${3}" - local findOptions="${4--type f}" - local indentStr="${5- - }" - - if [[ ! -d "${dir}" ]]; then - Log::displayError "Directory ${dir} does not exist" - fi - if [[ -n "${ext}" && "${ext:0:1}" != "." ]]; then - ext=".${ext}" - fi - ( - # shellcheck disable=SC2086 - cd "${dir}" && - find . -maxdepth 1 ${findOptions} -name "${prefix}*${ext}" | - sed -E "s#^\./${prefix}##g" | - sed -E "s#${ext}\$##g" | sort | sed -E "s#^#${indentStr}#" - ) -} - -# @description get absolute conf file from specified conf folder deduced using these rules -# * from absolute file (ignores and ) -# * relative to where script is executed (ignores and ) -# * from home/.bash-tools/ -# * from framework conf/ -# -# @arg $1 confFolder:String the directory name (not the path) to list -# @arg $2 conf:String file to use without extension -# @arg $3 extension:String the extension (.sh by default) -# -# @stdout absolute conf filename -# @exitcode 1 if file is not found in any location -Conf::getAbsoluteFile() { - local confFolder="$1" - local conf="$2" - local extension="${3-.sh}" - if [[ -n "${extension}" && "${extension:0:1}" != "." ]]; then - extension=".${extension}" - fi - - testAbs() { - local result - result="$(realpath -e "$1" 2>/dev/null)" - # shellcheck disable=SC2181 - if [[ "$?" = "0" && -f "${result}" ]]; then - echo "${result}" - return 0 - fi - return 1 - } - - # conf is absolute file (including extension) - testAbs "${confFolder}${extension}" && return 0 - # conf is absolute file - testAbs "${confFolder}" && return 0 - # conf is absolute file (including extension) - testAbs "${conf}${extension}" && return 0 - # conf is absolute file - testAbs "${conf}" && return 0 - - # relative to where script is executed (including extension) - if [[ -n "${CURRENT_DIR+xxx}" ]]; then - testAbs "$(File::concatenatePath "${CURRENT_DIR}" "${confFolder}")/${conf}${extension}" && return 0 - fi - # from home/.bash-tools/ - testAbs "$(File::concatenatePath "${HOME}/.bash-tools" "${confFolder}")/${conf}${extension}" && return 0 - - if [[ -n "${FRAMEWORK_ROOT_DIR+xxx}" ]]; then - # from framework conf/ (including extension) - testAbs "$(File::concatenatePath "${FRAMEWORK_ROOT_DIR}/conf" "${confFolder}")/${conf}${extension}" && return 0 - # from framework conf/ - testAbs "$(File::concatenatePath "${FRAMEWORK_ROOT_DIR}/conf" "${confFolder}")/${conf}" && return 0 +# @description draw a line with the character passed in parameter repeated depending on terminal width +# @arg $1 character:String character to use as separator (default value #) +UI::drawLine() { + local character="${1:-#}" + local -i width=${COLUMNS:-0} + if ((width == 0)) && [[ -t 1 ]]; then + width=$(tput cols) fi - - # file not found - Log::displayError "conf file '${conf}' not found" - - return 1 -} - -# @description check if dsn file has all the mandatory variables set -# Mandatory variables are: HOSTNAME, USER, PASSWORD, PORT -# -# @arg $1 dsnFileName:String dsn absolute filename -# @set HOSTNAME loaded from dsn file -# @set PORT loaded from dsn file -# @set USER loaded from dsn file -# @set PASSWORD loaded from dsn file -# @exitcode 0 on valid file -# @exitcode 1 if one of the properties of the conf file is invalid or if file not found -# @stderr log output if error found in conf file -Database::checkDsnFile() { - local dsnFileName="$1" - if [[ ! -f "${dsnFileName}" ]]; then - Log::displayError "dsn file ${dsnFileName} not found" - return 1 + if ((width == 0)); then + width=80 fi - - ( - unset HOSTNAME PORT PASSWORD USER - # shellcheck source=/src/Database/testsData/dsn_valid.env - source "${dsnFileName}" - if [[ -z ${HOSTNAME+x} ]]; then - Log::displayError "dsn file ${dsnFileName} : HOSTNAME not provided" - return 1 - fi - if [[ -z "${HOSTNAME}" ]]; then - Log::displayWarning "dsn file ${dsnFileName} : HOSTNAME value not provided" - fi - if [[ "${HOSTNAME}" = "localhost" ]]; then - Log::displayWarning "dsn file ${dsnFileName} : check that HOSTNAME should not be 127.0.0.1 instead of localhost" - fi - if [[ -z "${PORT+x}" ]]; then - Log::displayError "dsn file ${dsnFileName} : PORT not provided" - return 1 - fi - if ! [[ ${PORT} =~ ^[0-9]+$ ]]; then - Log::displayError "dsn file ${dsnFileName} : PORT invalid" - return 1 - fi - if [[ -z "${USER+x}" ]]; then - Log::displayError "dsn file ${dsnFileName} : USER not provided" - return 1 - fi - if [[ -z "${PASSWORD+x}" ]]; then - Log::displayError "dsn file ${dsnFileName} : PASSWORD not provided" - return 1 - fi - ) + printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") + echo } -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" - fi - done -} # @description load color theme # @noargs @@ -1107,74 +1152,196 @@ Env::pathPrepend() { # @env LOAD_THEME int 0 to avoid loading theme # @exitcode 0 always successful UI::requireTheme() { + REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + if [[ "${LOAD_THEME:-1}" = "1" ]]; then UI::theme "${BASH_FRAMEWORK_THEME-default}" fi } -# @description concatenate 2 paths and ensure the path is correct using realpath -m -# @arg $1 basePath:String -# @arg $2 subPath:String -# @require Linux::requireRealpathCommand -File::concatenatePath() { - local basePath="$1" - local subPath="$2" - local fullPath="${basePath:+${basePath}/}${subPath}" - realpath -m "${fullPath}" 2>/dev/null +# @description load colors theme constants +# @warning if tty not opened, noColor theme will be chosen +# @arg $1 theme:String the theme to use (default, noColor) +# @arg $@ args:String[] +# @set __ERROR_COLOR String indicate error status +# @set __INFO_COLOR String indicate info status +# @set __SUCCESS_COLOR String indicate success status +# @set __WARNING_COLOR String indicate warning status +# @set __SKIPPED_COLOR String indicate skipped status +# @set __DEBUG_COLOR String indicate debug status +# @set __HELP_COLOR String indicate help status +# @set __TEST_COLOR String not used +# @set __TEST_ERROR_COLOR String not used +# @set __HELP_TITLE_COLOR String used to display help title in help strings +# @set __HELP_OPTION_COLOR String used to display highlight options in help strings +# +# @set __RESET_COLOR String reset default color +# +# @set __HELP_EXAMPLE String to remove +# @set __HELP_TITLE String to remove +# @set __HELP_NORMAL String to remove +# shellcheck disable=SC2034 +UI::theme() { + local theme="${1-default}" + if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then + theme="noColor" + fi + case "${theme}" in + default | default-force) + theme="default" + ;; + noColor) ;; + *) + Log::fatal "invalid theme provided" + ;; + esac + if [[ "${theme}" = "default" ]]; then + BASH_FRAMEWORK_THEME="default" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='\e[31m' # Red + __INFO_COLOR='\e[44m' # white on lightBlue + __SUCCESS_COLOR='\e[32m' # Green + __WARNING_COLOR='\e[33m' # Yellow + __SKIPPED_COLOR='\e[33m' # Yellow + __DEBUG_COLOR='\e[37m' # Gray + __HELP_COLOR='\e[7;49;33m' # Black on Gold + __TEST_COLOR='\e[100m' # Light magenta + __TEST_ERROR_COLOR='\e[41m' # white on red + __HELP_TITLE_COLOR="\e[1;37m" # Bold + __HELP_OPTION_COLOR="\e[1;34m" # Blue + # Internal: reset color + __RESET_COLOR='\e[0m' # Reset Color + # shellcheck disable=SC2155,SC2034 + __HELP_EXAMPLE="$(echo -e "\e[2;97m")" + # shellcheck disable=SC2155,SC2034 + __HELP_TITLE="$(echo -e "\e[1;37m")" + # shellcheck disable=SC2155,SC2034 + __HELP_NORMAL="$(echo -e "\033[0m")" + else + BASH_FRAMEWORK_THEME="noColor" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='' + __INFO_COLOR='' + __SUCCESS_COLOR='' + __WARNING_COLOR='' + __SKIPPED_COLOR='' + __DEBUG_COLOR='' + __HELP_COLOR='' + __TEST_COLOR='' + __TEST_ERROR_COLOR='' + __HELP_TITLE_COLOR='' + __HELP_OPTION_COLOR='' + # Internal: reset color + __RESET_COLOR='' + __HELP_EXAMPLE='' + __HELP_TITLE='' + __HELP_NORMAL='' + fi } -# @description ensure command realpath is available -# @exitcode 1 if realpath command not available -# @stderr diagnostics information is displayed -Linux::requireRealpathCommand() { - Assert::commandExists realpath -} -# FUNCTIONS +# @description Check that command version is greater than expected minimal version +# display warning if command version greater than expected minimal version +# display error if command version less than expected minimal version and exit 1 +# @arg $1 commandName:String command path +# @arg $2 argVersion:String command line parameters to launch to get command version +# @arg $3 minimalVersion:String expected minimal command version +# @arg $4 parseVersionCallback:Function +# @arg $5 help:String optional help message to display if command does not exist +# @exitcode 0 if command version greater or equal to expected minimal version +# @exitcode 1 if command version less than expected minimal version +# @exitcode 2 if command does not exist +Version::checkMinimal() { + local commandName="$1" + local argVersion="$2" + local minimalVersion="$3" + local parseVersionCallback=${4:-Version::parse} + local help="${5:-}" -facade_main_dbImportProfilesh() { -BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" -if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then - FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" -else - # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR - FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" -fi -FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" -FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + Assert::commandExists "${commandName}" "${help}" || return 2 -# @require BashTools::Conf::requireLoad -if [[ -f "${HOME}/.bash-tools/.env" ]]; then - export BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") -fi -# REQUIRES -Env::requireLoad -UI::requireTheme -Log::requireLoad -Linux::requireRealpathCommand -BashTools::Conf::requireLoad -Compiler::Facade::requireCommandBinDir -Linux::requireExecutedAsUser + # shellcheck disable=SC2034 + local status=0 + # shellcheck disable=SC2034 + local -a pipeStatus=() + local version + version="$("${commandName}" "${argVersion}" 2>&1 | ${parseVersionCallback} || Bash::handlePipelineFailure status pipeStatus)" + + Log::displayDebug "check ${commandName} version ${version} against minimal ${minimalVersion}" + + Version::compare "${version}" "${minimalVersion}" || { + local result=$? + if [[ "${result}" = "1" ]]; then + Log::displayInfo "${commandName} version is ${version} greater than ${minimalVersion}" + elif [[ "${result}" = "2" ]]; then + Log::displayError "${commandName} minimal version is ${minimalVersion}, your version is ${version}" + return 1 + fi + return 0 + } -# @require Compiler::Facade::requireCommandBinDir -# shellcheck disable=SC2034 +} -# default values -declare optionProfile="" -declare fromDbName="" -declare optionFromDsn="default.remote" -declare optionRatio=70 -# other configuration -declare copyrightBeginYear="2020" -declare PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbImportProfiles" -declare HOME_PROFILES_DIR="${HOME}/.bash-tools/dbImportProfiles" +# @description compare 2 version numbers +# @arg $1 version1:String version 1 +# @arg $2 version2:String version 2 +# @exitcode 0 if equal +# @exitcode 1 if version1 > version2 +# @exitcode 2 else +Version::compare() { + if [[ "$1" = "$2" ]]; then + return 0 + fi + local IFS=. + # shellcheck disable=2206 + local i ver1=($1) ver2=($2) + # fill empty fields in ver1 with zeros + for ((i = ${#ver1[@]}; i < ${#ver2[@]}; i++)); do + ver1[i]=0 + done + for ((i = 0; i < ${#ver1[@]}; i++)); do + if [[ -z "${ver2[i]+unset}" ]] || [[ -z ${ver2[i]} ]]; then + # fill empty fields in ver2 with zeros + ver2[i]=0 + fi + if ((10#${ver1[i]} > 10#${ver2[i]})); then + return 1 + fi + if ((10#${ver1[i]} < 10#${ver2[i]})); then + return 2 + fi + done + return 0 +} + + +# @description filter to keep only version number from a string +# @arg $@ files:String[] the files to filter +# @exitcode * if one of the filter command fails +# @stdin you can use stdin as alternative to files argument +# @stdout the filtered content +# shellcheck disable=SC2120 +Version::parse() { + # match anything, print(p), exit on first match(Q) + sed -En \ + -e 's/\x1b\[[0-9;]*[mGKHF]//g' \ + -e 's/[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/' \ + -e '//{p;Q}' \ + "$@" +} +# FUNCTIONS + declare -a BASH_FRAMEWORK_ARGV_FILTERED=() +beforeParseCallback() { + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + copyrightCallback() { if [[ -z "${copyrightBeginYear}" ]]; then copyrightBeginYear="$(date +%Y)" @@ -1211,13 +1378,14 @@ updateArgListQuietCallback() { :; } # shellcheck disable=SC2317 # if function is overridden optionHelpCallback() { - dbImportProfileCommand help + Log::displayError "optionHelpCallback needs to be overridden" exit 0 } # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { - echo "${SCRIPT_NAME} version 2.0" + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version ${versionNumber}" exit 0 } @@ -1235,21 +1403,22 @@ optionEnvFileCallback() { optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_INFO} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionDebugVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_DEBUG} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionTraceVerboseCallback() { + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vvv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_TRACE} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } getLevel() { @@ -1273,6 +1442,7 @@ getLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -1294,6 +1464,7 @@ getVerboseLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -1304,7 +1475,7 @@ optionDisplayLevelCallback() { logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1313,19 +1484,20 @@ optionLogLevelCallback() { local logLevel verboseLevel logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionLogFileCallback() { local logFile="$2" - echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionQuietCallback() { - echo "BASH_FRAMEWORK_QUIET_MODE=1" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_QUIET_MODE=1" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1356,6 +1528,7 @@ optionBashFrameworkConfigCallback() { defaultFrameworkConfig="$( cat <<'EOF' + # copied from src/_includes/.framework-config.default # shellcheck disable=SC2034 @@ -1373,7 +1546,7 @@ NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-conf # describe the files that are allowed to not have an associated bats file BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" # describe the files that are allowed to not have a function matching the filename -FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|\.tpl$|/testsData/|^manualTests/|\.bats$}" +FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|/testsData/|^manualTests/|\.bats$}" # Source directories if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then FRAMEWORK_SRC_DIRS=( @@ -1389,6 +1562,7 @@ BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" + EOF )" @@ -1397,7 +1571,7 @@ overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" commandOptionParseFinished() { # load default template framework config defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" - echo "${defaultFrameworkConfig}" > "${defaultEnvFile}" + echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") if [[ -f "${envFile}" ]]; then files+=("${envFile}") @@ -1415,510 +1589,69 @@ commandOptionParseFinished() { fi } -dbImportProfileCommand() { - local options_parse_cmd="$1" - shift || true - - if [[ "${options_parse_cmd}" = "parse" ]]; then - local -i options_parse_optionParsedCountOptionProfile - ((options_parse_optionParsedCountOptionProfile = 0)) || true - local -i options_parse_optionParsedCountOptionFromDsn - ((options_parse_optionParsedCountOptionFromDsn = 0)) || true - local -i options_parse_optionParsedCountOptionRatio - ((options_parse_optionParsedCountOptionRatio = 0)) || true - local -i options_parse_optionParsedCountOptionBashFrameworkConfig - ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true - optionConfig="0" - local -i options_parse_optionParsedCountOptionConfig - ((options_parse_optionParsedCountOptionConfig = 0)) || true - optionInfoVerbose="0" - local -i options_parse_optionParsedCountOptionInfoVerbose - ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true - optionDebugVerbose="0" - local -i options_parse_optionParsedCountOptionDebugVerbose - ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true - optionTraceVerbose="0" - local -i options_parse_optionParsedCountOptionTraceVerbose - ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionNoColor="0" - local -i options_parse_optionParsedCountOptionNoColor - ((options_parse_optionParsedCountOptionNoColor = 0)) || true - optionTheme="default" - local -i options_parse_optionParsedCountOptionTheme - ((options_parse_optionParsedCountOptionTheme = 0)) || true - optionHelp="0" - local -i options_parse_optionParsedCountOptionHelp - ((options_parse_optionParsedCountOptionHelp = 0)) || true - optionVersion="0" - local -i options_parse_optionParsedCountOptionVersion - ((options_parse_optionParsedCountOptionVersion = 0)) || true - optionQuiet="0" - local -i options_parse_optionParsedCountOptionQuiet - ((options_parse_optionParsedCountOptionQuiet = 0)) || true - local -i options_parse_optionParsedCountOptionLogLevel - ((options_parse_optionParsedCountOptionLogLevel = 0)) || true - local -i options_parse_optionParsedCountOptionLogFile - ((options_parse_optionParsedCountOptionLogFile = 0)) || true - local -i options_parse_optionParsedCountOptionDisplayLevel - ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true - local -i options_parse_argParsedCountFromDbName - ((options_parse_argParsedCountFromDbName = 0)) || true - # shellcheck disable=SC2034 - local -i options_parse_parsedArgIndex=0 - while (($# > 0)); do - local options_parse_arg="$1" - local argOptDefaultBehavior=0 - case "${options_parse_arg}" in - # Option 1/17 - # Option optionProfile --profile|-p variableType String min 0 max 1 authorizedValues '' regexp '' - --profile | -p) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionProfile >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionProfile)) - # shellcheck disable=SC2034 - optionProfile="$1" - ;; - # Option 2/17 - # Option optionFromDsn --from-dsn|-f variableType String min 0 max 1 authorizedValues '' regexp '' - --from-dsn | -f) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionFromDsn >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionFromDsn)) - # shellcheck disable=SC2034 - optionFromDsn="$1" - ;; - # Option 3/17 - # Option optionRatio --ratio|-r variableType String min 0 max 1 authorizedValues '' regexp '' - --ratio | -r) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionRatio >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionRatio)) - # shellcheck disable=SC2034 - optionRatio="$1" - ;; - # Option 4/17 - # Option optionBashFrameworkConfig --bash-framework-config variableType String min 0 max 1 authorizedValues '' regexp '' - --bash-framework-config) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) - # shellcheck disable=SC2034 - optionBashFrameworkConfig="$1" - optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" - ;; - # Option 5/17 - # Option optionConfig --config variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --config) - # shellcheck disable=SC2034 - optionConfig="1" - if ((options_parse_optionParsedCountOptionConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionConfig)) - ;; - # Option 6/17 - # Option optionInfoVerbose --verbose|-v variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --verbose | -v) - # shellcheck disable=SC2034 - optionInfoVerbose="1" - if ((options_parse_optionParsedCountOptionInfoVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionInfoVerbose)) - optionInfoVerboseCallback "${options_parse_arg}" - updateArgListInfoVerboseCallback "${options_parse_arg}" - ;; - # Option 7/17 - # Option optionDebugVerbose -vv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vv) - # shellcheck disable=SC2034 - optionDebugVerbose="1" - if ((options_parse_optionParsedCountOptionDebugVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDebugVerbose)) - optionDebugVerboseCallback "${options_parse_arg}" - updateArgListDebugVerboseCallback "${options_parse_arg}" - ;; - # Option 8/17 - # Option optionTraceVerbose -vvv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vvv) - # shellcheck disable=SC2034 - optionTraceVerbose="1" - if ((options_parse_optionParsedCountOptionTraceVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTraceVerbose)) - optionTraceVerboseCallback "${options_parse_arg}" - updateArgListTraceVerboseCallback "${options_parse_arg}" - ;; - # Option 9/17 - # Option optionEnvFiles --env-file variableType StringArray min 0 max -1 authorizedValues '' regexp '' - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - ;; - # Option 10/17 - # Option optionNoColor --no-color variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --no-color) - # shellcheck disable=SC2034 - optionNoColor="1" - if ((options_parse_optionParsedCountOptionNoColor >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionNoColor)) - optionNoColorCallback "${options_parse_arg}" - updateArgListNoColorCallback "${options_parse_arg}" - ;; - # Option 11/17 - # Option optionTheme --theme variableType String min 0 max 1 authorizedValues 'default|default-force|noColor' regexp '' - --theme) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default|default-force|noColor)" - return 1 - fi - if ((options_parse_optionParsedCountOptionTheme >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTheme)) - # shellcheck disable=SC2034 - optionTheme="$1" - optionThemeCallback "${options_parse_arg}" "${optionTheme}" - updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" - ;; - # Option 12/17 - # Option optionHelp --help|-h variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --help | -h) - # shellcheck disable=SC2034 - optionHelp="1" - if ((options_parse_optionParsedCountOptionHelp >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionHelp)) - optionHelpCallback "${options_parse_arg}" - ;; - # Option 13/17 - # Option optionVersion --version variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --version) - # shellcheck disable=SC2034 - optionVersion="1" - if ((options_parse_optionParsedCountOptionVersion >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionVersion)) - optionVersionCallback "${options_parse_arg}" - ;; - # Option 14/17 - # Option optionQuiet --quiet|-q variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --quiet | -q) - # shellcheck disable=SC2034 - optionQuiet="1" - if ((options_parse_optionParsedCountOptionQuiet >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionQuiet)) - optionQuietCallback "${options_parse_arg}" - updateArgListQuietCallback "${options_parse_arg}" - ;; - # Option 15/17 - # Option optionLogLevel --log-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --log-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogLevel)) - # shellcheck disable=SC2034 - optionLogLevel="$1" - optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - ;; - # Option 16/17 - # Option optionLogFile --log-file variableType String min 0 max 1 authorizedValues '' regexp '' - --log-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogFile >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogFile)) - # shellcheck disable=SC2034 - optionLogFile="$1" - optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" - updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" - ;; - # Option 17/17 - # Option optionDisplayLevel --display-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --display-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionDisplayLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDisplayLevel)) - # shellcheck disable=SC2034 - optionDisplayLevel="$1" - optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - ;; - -*) - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" - return 1 - fi - ;; - *) - if ((0)); then - # Technical if - never reached - : - # Argument 1/1 - # Argument fromDbName min 1 max 1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 0 && options_parse_parsedArgIndex < 1)); then - if ((options_parse_argParsedCountFromDbName >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument fromDbName - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountFromDbName)) - # shellcheck disable=SC2034 - fromDbName="${options_parse_arg}" - else - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" - return 1 - fi - fi - ((++options_parse_parsedArgIndex)) - ;; - esac - shift || true - done - if ((options_parse_argParsedCountFromDbName < 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument 'fromDbName' should be provided at least 1 time(s)" - return 1 - fi - commandOptionParseFinished - dbImportProfileCommandCallback - Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" - Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" - elif [[ "${options_parse_cmd}" = "help" ]]; then - Array::wrap2 " " 80 0 "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" "generate optimized profiles to be used by dbImport" - echo - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "${SCRIPT_NAME}" "[OPTIONS]" "[ARGUMENTS]")" - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ - "${SCRIPT_NAME}" \ - "[--profile|-p ]" "[--from-dsn|-f ]" "[--ratio|-r ]" "[--bash-framework-config ]" "[--config]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--no-color]" "[--theme ]" "[--help|-h]" "[--version]" "[--quiet|-q]" "[--log-level ]" "[--log-file ]" "[--display-level ]")" - echo - echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}fromDbName${__HELP_NORMAL} {single} (mandatory)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(the\ name\ of\ the\ source/remote\ database) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--profile${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-p ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(the\ name\ of\ the\ profile\ to\ write\ in\ profiles\ directory.\ \ If\ not\ provided\,\ the\ file\ name\ pattern\ will\ be\ \'auto_\_\.sh\') - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--from-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-f ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(dsn\ to\ use\ for\ source\ database\ \(Default:\ default.remote\)\ if\ not\ provided\,\ the\ file\ name\ pattern\ will\ be\ \'auto_\_\.sh\') - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--ratio${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-r ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(define\ the\ ratio\ to\ use\ \(0\ to\ 100%\ -\ default\ 70\).\ \ 0\ means\ profile\ will\ filter\ out\ all\ the\ tables.\ \ 100\ means\ profile\ will\ keep\ all\ the\ tables.\ \ Eg:\ 70\ means\ that\ tables\ with\ size\(table+index\)\ that\ are\ greater\ that\ 70%\ of\ the\ max\ table\ size\ will\ be\ excluded.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(use\ alternate\ bash\ framework\ configuration.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ configuration) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(info\ level\ verbose\ mode\ \(alias\ of\ --display-level\ INFO\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(debug\ level\ verbose\ mode\ \(alias\ of\ --display-level\ DEBUG\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(trace\ level\ verbose\ mode\ \(alias\ of\ --display-level\ TRACE\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Load\ the\ specified\ env\ file\ \(deprecated\,\ please\ use\ --bash-framework-config\ option\ instead\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Produce\ monochrome\ output.\ alias\ of\ --theme\ noColor.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' - echo ' Possible values: default|default-force|noColor' - echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ this\ command\ help) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Print\ version\ information\ and\ quit) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(quiet\ mode\,\ doesn\'t\ display\ any\ output) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ file) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(set\ display\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e """ -${__HELP_TITLE}Default profiles directory:${__HELP_NORMAL} -${PROFILES_DIR-configuration error} - -${__HELP_TITLE}User profiles directory:${__HELP_NORMAL} -${HOME_PROFILES_DIR-configuration error} -Allows to override profiles defined in "Default profiles directory" - -${__HELP_TITLE}List of available profiles:${__HELP_NORMAL} -${profilesList} - -${__HELP_TITLE}List of available dsn:${__HELP_NORMAL} -${dsnList}""" - echo - echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo '2.0' - echo - echo -e "${__HELP_TITLE_COLOR}AUTHOR:${__RESET_COLOR}" - echo '[François Chastanet](https://github.com/fchastanet)' - echo - echo -e "${__HELP_TITLE_COLOR}SOURCE FILE:${__RESET_COLOR}" - echo 'https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/DbImport/dbImportProfile.sh' - echo - echo -e "${__HELP_TITLE_COLOR}LICENSE:${__RESET_COLOR}" - echo 'MIT License' - echo - Array::wrap2 ' ' 76 4 "$(copyrightCallback)" - else - Log::displayError "Command ${SCRIPT_NAME} - Option command invalid: '${options_parse_cmd}'" - return 1 - fi +declare defaultFromDsn="default.remote" +# shellcheck disable=SC2034 +declare versionNumber="2.0" +# shellcheck disable=SC2034 +declare copyrightBeginYear="2020" +# shellcheck disable=SC2034 +declare PROFILES_DIR +declare HOME_PROFILES_DIR + +initConf() { + # shellcheck disable=SC2034 + PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbImportProfiles" + HOME_PROFILES_DIR="${HOME}/.bash-tools/dbImportProfiles" } optionHelpCallback() { + dbImportProfileCommandHelp + exit 0 +} + +longDescriptionFunction() { local profilesList="" local dsnList="" dsnList="$(Conf::getMergedList "dsn" "env")" profilesList="$(Conf::getMergedList "dbImportProfiles" "sh" || true)" - dbImportProfileCommand help | envsubst - exit 0 + echo -e "${__HELP_TITLE}Default profiles directory:${__HELP_NORMAL}" + echo -e "${PROFILES_DIR-configuration error}" + echo + echo -e "${__HELP_TITLE}User profiles directory:${__HELP_NORMAL}" + echo -e "${HOME_PROFILES_DIR-configuration error}" + echo -e 'Allows to override profiles defined in "Default profiles directory"' + echo + echo -e "${__HELP_TITLE}List of available profiles:${__HELP_NORMAL}" + echo -e "${profilesList}" + echo + echo -e "${__HELP_TITLE}List of available dsn:${__HELP_NORMAL}" + echo -e "${dsnList}" +} + +optionProfileHelpFunction() { + Array::wrap2 " " 80 4 \ + " The name of the profile to write in profiles directory.\r" \ + "If not provided, the file name pattern will be 'auto__.sh'" + echo +} + +optionFromDsnHelpFunction() { + Array::wrap2 " " 80 4 \ + " dsn to use for source database (Default: ${defaultFromDsn})\r" \ + "if not provided, the file name pattern will be 'auto__.sh'" + echo +} + +optionRatioHelpFunction() { + Array::wrap2 " " 80 4 \ + " define the ratio to use (0 to 100% - default 70).\r" \ + "- 0 means profile will filter out all the tables\r" \ + "- 100 means profile will keep all the tables.\r" \ + "Eg: 70 means that tables with size(table+index)\r" \ + "that are greater than 70% of the max table size will be excluded." + echo } dbImportProfileCommandCallback() { @@ -1927,9 +1660,11 @@ dbImportProfileCommandCallback() { fi if [[ -z "${optionProfile}" ]]; then - optionProfile="auto_${optionFromDsn}_${fromDbName}.sh" + # shellcheck disable=SC2154 + optionProfile="auto_${optionFromDsn}_${fromDbName}" fi + # shellcheck disable=SC2154 if ! [[ "${optionRatio}" =~ ^-?[0-9]+$ ]]; then Log::fatal "Ratio value should be a number" fi @@ -1939,8 +1674,722 @@ dbImportProfileCommandCallback() { fi } -dbImportProfileCommand parse "${BASH_FRAMEWORK_ARGV[@]}" + +optionVersionCallback() { + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version ${versionNumber}" + Db::checkRequirements + exit 0 +} + + + +beforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + + +# ------------------------------------------ +# Command dbImportProfileCommand +# ------------------------------------------ + +# options variables initialization +declare optionHelp="0" +declare optionConfig="0" +declare optionBashFrameworkConfig="" +declare optionInfoVerbose="0" +declare optionDebugVerbose="0" +declare optionTraceVerbose="0" +declare -a optionEnvFiles=() +declare optionLogLevel="" +declare optionLogFile="" +declare optionDisplayLevel="" +declare optionNoColor="0" +declare optionTheme="default" +declare optionVersion="0" +declare optionQuiet="0" +declare optionProfile="" +declare optionFromDsn="default.remote" +declare optionRatio="70" +# arguments variables initialization +declare fromDbName="" +# @description parse command options and arguments for dbImportProfileCommand +dbImportProfileCommandParse() { + Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" + Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" + optionHelp="0" + local -i options_parse_optionParsedCountOptionHelp + ((options_parse_optionParsedCountOptionHelp = 0)) || true + optionConfig="0" + local -i options_parse_optionParsedCountOptionConfig + ((options_parse_optionParsedCountOptionConfig = 0)) || true + optionBashFrameworkConfig="" + local -i options_parse_optionParsedCountOptionBashFrameworkConfig + ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true + optionInfoVerbose="0" + local -i options_parse_optionParsedCountOptionInfoVerbose + ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true + optionDebugVerbose="0" + local -i options_parse_optionParsedCountOptionDebugVerbose + ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true + optionTraceVerbose="0" + local -i options_parse_optionParsedCountOptionTraceVerbose + ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true + + optionLogLevel="" + local -i options_parse_optionParsedCountOptionLogLevel + ((options_parse_optionParsedCountOptionLogLevel = 0)) || true + optionLogFile="" + local -i options_parse_optionParsedCountOptionLogFile + ((options_parse_optionParsedCountOptionLogFile = 0)) || true + optionDisplayLevel="" + local -i options_parse_optionParsedCountOptionDisplayLevel + ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true + optionNoColor="0" + local -i options_parse_optionParsedCountOptionNoColor + ((options_parse_optionParsedCountOptionNoColor = 0)) || true + optionTheme="default" + local -i options_parse_optionParsedCountOptionTheme + ((options_parse_optionParsedCountOptionTheme = 0)) || true + optionVersion="0" + local -i options_parse_optionParsedCountOptionVersion + ((options_parse_optionParsedCountOptionVersion = 0)) || true + optionQuiet="0" + local -i options_parse_optionParsedCountOptionQuiet + ((options_parse_optionParsedCountOptionQuiet = 0)) || true + optionProfile="" + local -i options_parse_optionParsedCountOptionProfile + ((options_parse_optionParsedCountOptionProfile = 0)) || true + optionFromDsn="default.remote" + local -i options_parse_optionParsedCountOptionFromDsn + ((options_parse_optionParsedCountOptionFromDsn = 0)) || true + optionRatio="70" + local -i options_parse_optionParsedCountOptionRatio + ((options_parse_optionParsedCountOptionRatio = 0)) || true + + fromDbName="" + local -i options_parse_argParsedCountFromDbName + ((options_parse_argParsedCountFromDbName = 0)) || true + + + # shellcheck disable=SC2034 + local -i options_parse_parsedArgIndex=0 + while (($# > 0)); do + local options_parse_arg="$1" + local argOptDefaultBehavior=0 + case "${options_parse_arg}" in + # Option 1/17 + # optionHelp alts --help|-h + # type: Boolean min 0 max 1 + --help | -h) + # shellcheck disable=SC2034 + optionHelp="1" + + if ((options_parse_optionParsedCountOptionHelp >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionHelp)) + optionHelpCallback "${options_parse_arg}" "${optionHelp}" + + ;; + + # Option 2/17 + # optionConfig alts --config + # type: Boolean min 0 max 1 + --config) + # shellcheck disable=SC2034 + optionConfig="1" + + if ((options_parse_optionParsedCountOptionConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionConfig)) + ;; + + # Option 3/17 + # optionBashFrameworkConfig alts --bash-framework-config + # type: String min 0 max 1 + --bash-framework-config) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) + # shellcheck disable=SC2034 + optionBashFrameworkConfig="$1" + optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" + + ;; + + # Option 4/17 + # optionInfoVerbose alts --verbose|-v + # type: Boolean min 0 max 1 + --verbose | -v) + # shellcheck disable=SC2034 + optionInfoVerbose="1" + + if ((options_parse_optionParsedCountOptionInfoVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionInfoVerbose)) + optionInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + updateArgListInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + ;; + + # Option 5/17 + # optionDebugVerbose alts -vv + # type: Boolean min 0 max 1 + -vv) + # shellcheck disable=SC2034 + optionDebugVerbose="1" + + if ((options_parse_optionParsedCountOptionDebugVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDebugVerbose)) + optionDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + updateArgListDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + ;; + + # Option 6/17 + # optionTraceVerbose alts -vvv + # type: Boolean min 0 max 1 + -vvv) + # shellcheck disable=SC2034 + optionTraceVerbose="1" + + if ((options_parse_optionParsedCountOptionTraceVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTraceVerbose)) + optionTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + updateArgListTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + ;; + + # Option 7/17 + # optionEnvFiles alts --env-file + # type: StringArray min 0 max -1 + --env-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + ((++options_parse_optionParsedCountOptionEnvFiles)) + optionEnvFiles+=("$1") + optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + ;; + + # Option 8/17 + # optionLogLevel alts --log-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --log-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogLevel)) + # shellcheck disable=SC2034 + optionLogLevel="$1" + optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + ;; + + # Option 9/17 + # optionLogFile alts --log-file + # type: String min 0 max 1 + --log-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogFile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogFile)) + # shellcheck disable=SC2034 + optionLogFile="$1" + optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + ;; + + # Option 10/17 + # optionDisplayLevel alts --display-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --display-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionDisplayLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDisplayLevel)) + # shellcheck disable=SC2034 + optionDisplayLevel="$1" + optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + ;; + + # Option 11/17 + # optionNoColor alts --no-color + # type: Boolean min 0 max 1 + --no-color) + # shellcheck disable=SC2034 + optionNoColor="1" + + if ((options_parse_optionParsedCountOptionNoColor >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionNoColor)) + optionNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + updateArgListNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + ;; + + # Option 12/17 + # optionTheme alts --theme + # type: String min 0 max 1 + # authorizedValues: default|default-force|noColor + --theme) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ default|default-force|noColor ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionTheme >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTheme)) + # shellcheck disable=SC2034 + optionTheme="$1" + optionThemeCallback "${options_parse_arg}" "${optionTheme}" + + updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" + + ;; + + # Option 13/17 + # optionVersion alts --version + # type: Boolean min 0 max 1 + --version) + # shellcheck disable=SC2034 + optionVersion="1" + + if ((options_parse_optionParsedCountOptionVersion >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionVersion)) + optionVersionCallback "${options_parse_arg}" "${optionVersion}" + + ;; + + # Option 14/17 + # optionQuiet alts --quiet|-q + # type: Boolean min 0 max 1 + --quiet | -q) + # shellcheck disable=SC2034 + optionQuiet="1" + + if ((options_parse_optionParsedCountOptionQuiet >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionQuiet)) + optionQuietCallback "${options_parse_arg}" "${optionQuiet}" + + updateArgListQuietCallback "${options_parse_arg}" "${optionQuiet}" + + ;; + + # Option 15/17 + # optionProfile alts --profile|-p + # type: String min 0 max 1 + --profile | -p) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionProfile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionProfile)) + # shellcheck disable=SC2034 + optionProfile="$1" + ;; + + # Option 16/17 + # optionFromDsn alts --from-dsn|-f + # type: String min 0 max 1 + --from-dsn | -f) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionFromDsn >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionFromDsn)) + # shellcheck disable=SC2034 + optionFromDsn="$1" + ;; + + # Option 17/17 + # optionRatio alts --ratio|-r + # type: String min 0 max 1 + --ratio | -r) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionRatio >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionRatio)) + # shellcheck disable=SC2034 + optionRatio="$1" + ;; + + -*) + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" + return 1 + fi + ;; + *) + ((minParsedArgIndex0 = 0)) || true + ((maxParsedArgIndex0 = 0)) || true + ((minParsedArgIndex1 = minParsedArgIndex0 + 1)) || true + ((maxParsedArgIndex1 = maxParsedArgIndex0 + 1)) || true + ((incrementArg = 1 )) + if ((0)); then + # Technical if - never reached + : + + # Argument 1/1 - fromDbName + # Argument fromDbName min 1 max 1 + # Argument fromDbName authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && + options_parse_parsedArgIndex < maxParsedArgIndex1 )); then + if ((options_parse_argParsedCountFromDbName >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument fromDbName - Maximum number of argument occurrences reached(1)" + return 1 + fi + ((++options_parse_argParsedCountFromDbName)) + # shellcheck disable=SC2034 + fromDbName="${options_parse_arg}" + + + # else too much args + else + + + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + # too much args and no unknownArgumentCallbacks configured + Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" + return 1 + fi + + fi + if ((incrementArg == 1)); then + ((++options_parse_parsedArgIndex)) + fi + ;; + esac + shift || true + done + + + + + + + + + + + + + + + + + + if ((options_parse_argParsedCountFromDbName < 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument 'fromDbName' should be provided at least 1 time(s)" + return 1 + fi || return $? + + + commandOptionParseFinished + dbImportProfileCommandCallback + +} + +# @description display command options and arguments help for dbImportProfileCommand +dbImportProfileCommandHelp() { + Array::wrap2 ' ' 80 0 "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" \ + "Generate optimized profiles to be used by dbImport." + echo + echo + + # ------------------------------------------ + # usage section + # ------------------------------------------ + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "dbImportProfile [OPTIONS] [ARGUMENTS]" + echo + # ------------------------------------------ + # usage/options section + # ------------------------------------------ + optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--profile|-p ]" "[--from-dsn|-f ]" "[--ratio|-r ]" + ) + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ + "dbImportProfile" "${optionsAltList[@]}" + echo + + # ------------------------------------------ + # usage/arguments section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" + + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}fromDbName${__HELP_NORMAL} {single} (mandatory) + " + Array::wrap2 ' ' 76 4 " " "The name of the source/remote database." + echo + # ------------------------------------------ + # options section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays this command help" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays configuration" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" + Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log file" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set display level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." + echo + Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" + echo + + Array::wrap2 ' ' 76 6 " Default value: " "default" + echo + + echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Print version information and quit." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." + echo + + + echo + echo -e "${__HELP_TITLE_COLOR}OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--profile${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-p ${__HELP_NORMAL} {single}" + optionProfileHelpFunction + + + + + echo -e " ${__HELP_OPTION_COLOR}--from-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-f ${__HELP_NORMAL} {single}" + optionFromDsnHelpFunction + + + + Array::wrap2 ' ' 76 6 " Default value: " "default.remote" + echo + + echo -e " ${__HELP_OPTION_COLOR}--ratio${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-r ${__HELP_NORMAL} {single}" + optionRatioHelpFunction + + + + Array::wrap2 ' ' 76 6 " Default value: " "70" + echo + # ------------------------------------------ + # longDescription section + # ------------------------------------------ + echo + echo + echo -e "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" + longDescriptionFunction + # ------------------------------------------ + # version section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" + echo "2.0" + # ------------------------------------------ + # author section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}AUTHOR: ${__RESET_COLOR}" + echo "[François Chastanet](https://github.com/fchastanet)" + # ------------------------------------------ + # sourceFile section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml" + # ------------------------------------------ + # license section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}LICENSE: ${__RESET_COLOR}" + echo "MIT License" + # ------------------------------------------ + # copyright section + # ------------------------------------------ + Array::wrap2 ' ' 76 0 "$(copyrightCallback)" +} + + +beforeParseCallback +initConf + +dbImportProfileCommandParse "$@" +MAIN_FUNCTION_NAME="main" +main() { + +# other configuration read -r -d '' QUERY <"${HOME_PROFILES_DIR}/${optionProfile}" +# first table is the biggest one +declare maxTableSize +maxTableSize="$(echo "${tableList}" | head -1 | awk -F ' ' '{print $2}')" - Log::displayInfo "File saved in '${HOME_PROFILES_DIR}/${optionProfile}'" -} +# shellcheck disable=SC2154 +( + echo "#!/usr/bin/env bash" + echo + echo "# cat represents the whole list of tables" + echo "cat |" + declare -i excludedTablesCount + ((excludedTablesCount = 0)) || true + declare tableSize + declare tableName + while IFS="" read -r line || [[ -n "${line}" ]]; do + tableSize="$(echo "${line}" | awk -F ' ' '{print $2}')" + tableName="$(echo "${line}" | awk -F ' ' '{print $1}')" + if ((tableSize < maxTableSize * optionRatio / 100)); then + echo -n ' #' + else + excludedTablesCount=$((excludedTablesCount + 1)) + fi + echo " grep -v '^${tableName}$' | # table size ${tableSize}MB" + done < <(echo "${tableList}") + echo " cat" + tablesCount="$(echo "${tableList}" | wc -l)" + Log::displayInfo "Profile generated - ${excludedTablesCount}/${tablesCount} tables bigger than ${optionRatio}% of max table size (${maxTableSize}MB) automatically excluded" +) >"${HOME_PROFILES_DIR}/${optionProfile}.sh" -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run -fi +Log::displayInfo "File saved in '${HOME_PROFILES_DIR}/${optionProfile}.sh'" } -facade_main_dbImportProfilesh "$@" +# if file is sourced avoid calling main function +# shellcheck disable=SC2178 +BASH_SOURCE=".$0" # cannot be changed in bash +# shellcheck disable=SC2128 +if test ".$0" == ".${BASH_SOURCE}"; then + if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then + main "$@" &>/dev/null + else + main "$@" + fi +fi diff --git a/bin/doc b/bin/doc index 06146676..395b2fa7 100755 --- a/bin/doc +++ b/bin/doc @@ -1820,7 +1820,7 @@ docCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config bash-framework-config${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." echo @@ -1844,26 +1844,26 @@ docCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--env-file env-file${__HELP_NORMAL} {list} (optional)" + echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" echo - echo -e " ${__HELP_OPTION_COLOR}--log-level log-level${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" echo - echo -e " ${__HELP_OPTION_COLOR}--log-file log-file${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log file" echo - echo -e " ${__HELP_OPTION_COLOR}--display-level display-level${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set display level" echo Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" @@ -1876,7 +1876,7 @@ docCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--theme theme${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." echo Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" @@ -2023,7 +2023,7 @@ generateDoc() { "${DOC_DIR}/Commands.md" mkdir -p "${DOC_DIR}/src/_binaries/Converters/testsData" || true - cp "${BASH_TOOLS_ROOT_DIR}/src/_binaries/Converters/testsData/mysql2puml-model.png" "${DOC_DIR}/src/_binaries/Converters/testsData" + cp "${BASH_TOOLS_ROOT_DIR}/src/_binaries/Converters/mysql2puml/testsData/mysql2puml-model.png" "${DOC_DIR}/src/_binaries/Converters/testsData" # copy other files cp "${BASH_TOOLS_ROOT_DIR}/README.md" "${DOC_DIR}/README.md" diff --git a/bin/install b/bin/install index b8ff79bf..d6c2443f 100755 --- a/bin/install +++ b/bin/install @@ -1523,7 +1523,7 @@ installCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config bash-framework-config${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." echo @@ -1547,26 +1547,26 @@ installCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--env-file env-file${__HELP_NORMAL} {list} (optional)" + echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" echo - echo -e " ${__HELP_OPTION_COLOR}--log-level log-level${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" echo - echo -e " ${__HELP_OPTION_COLOR}--log-file log-file${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log file" echo - echo -e " ${__HELP_OPTION_COLOR}--display-level display-level${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set display level" echo Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" @@ -1579,7 +1579,7 @@ installCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--theme theme${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." echo Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" diff --git a/bin/installRequirements b/bin/installRequirements index 8f8be3c8..1df2e575 100755 --- a/bin/installRequirements +++ b/bin/installRequirements @@ -1526,7 +1526,7 @@ installRequirementsCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config bash-framework-config${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." echo @@ -1550,26 +1550,26 @@ installRequirementsCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--env-file env-file${__HELP_NORMAL} {list} (optional)" + echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" echo - echo -e " ${__HELP_OPTION_COLOR}--log-level log-level${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" echo - echo -e " ${__HELP_OPTION_COLOR}--log-file log-file${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log file" echo - echo -e " ${__HELP_OPTION_COLOR}--display-level display-level${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set display level" echo Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" @@ -1582,7 +1582,7 @@ installRequirementsCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--theme theme${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." echo Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" diff --git a/bin/mysql2puml b/bin/mysql2puml index 037f77bd..ca4279ee 100755 --- a/bin/mysql2puml +++ b/bin/mysql2puml @@ -1737,7 +1737,7 @@ mysql2pumlCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--skin <>]" + optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--skin ]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "mysql2puml" "${optionsAltList[@]}" @@ -1749,7 +1749,7 @@ mysql2pumlCommandHelp() { echo echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}sqlFile${__HELP_NORMAL}{single}] + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}sqlFile${__HELP_NORMAL} {single}] " Array::wrap2 ' ' 76 4 " " "Sql filepath to parse (read from stdin if not provided)." echo @@ -1770,7 +1770,7 @@ mysql2pumlCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config bash-framework-config${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." echo @@ -1794,26 +1794,26 @@ mysql2pumlCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--env-file env-file${__HELP_NORMAL} {list} (optional)" + echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" echo - echo -e " ${__HELP_OPTION_COLOR}--log-level log-level${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" echo - echo -e " ${__HELP_OPTION_COLOR}--log-file log-file${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log file" echo - echo -e " ${__HELP_OPTION_COLOR}--display-level display-level${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set display level" echo Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" @@ -1826,7 +1826,7 @@ mysql2pumlCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--theme theme${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." echo Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" @@ -1848,7 +1848,7 @@ mysql2pumlCommandHelp() { echo echo -e "${__HELP_TITLE_COLOR}OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--skin ${__HELP_NORMAL} {single}" + echo -e " ${__HELP_OPTION_COLOR}--skin ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Header configuration of the plantuml file." echo diff --git a/src/BashTools/Conf/requireLoad.bats b/src/BashTools/Conf/requireLoad.bats index a94dcdc5..e0e77315 100755 --- a/src/BashTools/Conf/requireLoad.bats +++ b/src/BashTools/Conf/requireLoad.bats @@ -10,7 +10,6 @@ source "${rootDir}/src/BashTools/Conf/requireLoad.sh" setup() { export TMPDIR="${BATS_TEST_TMPDIR}" export HOME="${BATS_TEST_TMPDIR}/home" - export POSTMAN_API_KEY="fake" export BASH_FRAMEWORK_THEME="noColor" export bashToolsDefaultConfigTemplate="$(cat "${rootDir}/conf/.env")" } diff --git a/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml b/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml index 1c3cfd98..22e7a3b3 100644 --- a/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml +++ b/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml @@ -28,7 +28,7 @@ binData: type: String defaultValue: default help: Header configuration of the plantuml file. - helpValueName: + helpValueName: skin callbacks: - optionSkinCallback variableName: optionSkin diff --git a/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt b/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt index dd7e5764..897faea0 100644 --- a/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt +++ b/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt @@ -5,10 +5,10 @@ [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] [--env-file ] [--log-level ] [--log-file ] [--display-level ] [--no-color] [--theme ] [--version] - [--quiet|-q] [--skin <>] + [--quiet|-q] [--skin ] ARGUMENTS: - [sqlFile{single}] + [sqlFile {single}] Sql filepath to parse (read from stdin if not provided). GLOBAL OPTIONS: @@ -16,7 +16,7 @@ Displays this command help --config {single} Displays configuration - --bash-framework-config bash-framework-config {single} + --bash-framework-config  {single} Use alternate bash framework configuration. --verbose, -v {single} Info level verbose mode (alias of --display-level INFO) @@ -24,20 +24,20 @@ Debug level verbose mode (alias of --display-level DEBUG) -vvv {single} Trace level verbose mode (alias of --display-level TRACE) - --env-file env-file {list} (optional) + --env-file  {list} (optional) Load the specified env file (deprecated, please use --bash-framework-con fig option instead) - --log-level log-level {single} + --log-level  {single} Set log level Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE - --log-file log-file {single} + --log-file  {single} Set log file - --display-level display-level {single} + --display-level  {single} Set display level Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE --no-color {single} Produce monochrome output. alias of --theme noColor. - --theme theme {single} + --theme  {single} Choose color theme - default-force means colors will be produced even if command is piped. Possible values: default, default-force, noColor @@ -48,7 +48,7 @@ Quiet mode, doesn't display any output. OPTIONS: - --skin  {single} + --skin  {single} Header configuration of the plantuml file. Default value: default diff --git a/src/_binaries/Database/dbImport/binary-dbImport.yaml b/src/_binaries/Database/dbImport/binary-dbImport.yaml index ca17643a..0b291789 100644 --- a/src/_binaries/Database/dbImport/binary-dbImport.yaml +++ b/src/_binaries/Database/dbImport/binary-dbImport.yaml @@ -23,11 +23,11 @@ binData: commandName: dbImport beforeParseCallbacks: - beforeParseCallback + - initConf callbacks: - dbImportCommandCallback@100 definitionFiles: 20: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbImport/dbImport-options.sh - #99: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.sh mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbImport/dbImport-main.sh help: Import source db into target db using eventual table filter. args: @@ -35,7 +35,7 @@ binData: type: String min: 1 max: 1 - name: + name: fromDbName variableName: fromDbName - help: | diff --git a/src/_binaries/Database/dbImport/dbImport-main.sh b/src/_binaries/Database/dbImport/dbImport-main.sh index 08b69b22..9b228f6a 100755 --- a/src/_binaries/Database/dbImport/dbImport-main.sh +++ b/src/_binaries/Database/dbImport/dbImport-main.sh @@ -1,12 +1,6 @@ #!/usr/bin/env bash # @embed "${BASH_TOOLS_ROOT_DIR}/src/_binaries/DbImport/dumpSizeQuery.sql" AS dumpSizeQuery -declare TIMEFORMAT='time spent : %3R' -declare DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR%/} -declare PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbImportProfiles" -declare HOME_PROFILES_DIR="${HOME}/.bash-tools/dbImportProfiles" -declare DOWNLOAD_DUMP=0 - # dump header/footer read -r -d '\0' DUMP_HEADER <<-EOM SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS = 0; diff --git a/src/_binaries/Database/dbImport/dbImport-options.sh b/src/_binaries/Database/dbImport/dbImport-options.sh index 60bb1622..4dbceee9 100755 --- a/src/_binaries/Database/dbImport/dbImport-options.sh +++ b/src/_binaries/Database/dbImport/dbImport-options.sh @@ -8,6 +8,13 @@ declare versionNumber="2.0" declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" declare defaultTargetCharacterSet="" +declare TIMEFORMAT='time spent : %3R' +declare DOWNLOAD_DUMP=0 + +declare DB_IMPORT_DUMP_DIR +declare PROFILES_DIR +declare HOME_PROFILES_DIR + beforeParseCallback() { BashTools::Conf::requireLoad Env::requireLoad @@ -17,6 +24,13 @@ beforeParseCallback() { Linux::requireExecutedAsUser } +initConf() { + # shellcheck disable=SC2034 + DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR%/} + PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbImportProfiles" + HOME_PROFILES_DIR="${HOME}/.bash-tools/dbImportProfiles" +} + optionHelpCallback() { dbImportCommandHelp exit 0 diff --git a/src/_binaries/Database/dbImport/dbImport.bats b/src/_binaries/Database/dbImport/dbImport.bats index b2a4c0db..8462a9f2 100755 --- a/src/_binaries/Database/dbImport/dbImport.bats +++ b/src/_binaries/Database/dbImport/dbImport.bats @@ -41,7 +41,7 @@ function Database::dbImport::remoteDbName_not_provided { #@test run "${binDir}/dbImport" 2>&1 assert_failure 1 assert_lines_count 1 - assert_output --partial "ERROR - Command dbImport - Argument '' should be provided at least 1 time(s)" + assert_output --partial "ERROR - Command dbImport - Argument 'fromDbName' should be provided at least 1 time(s)" } function Database::dbImport::from_aws_and_aws_not_installed { #@test diff --git a/src/_binaries/Database/dbImport/testsData/dbImport.help.txt b/src/_binaries/Database/dbImport/testsData/dbImport.help.txt index 8447c2c7..29dc37e7 100644 --- a/src/_binaries/Database/dbImport/testsData/dbImport.help.txt +++ b/src/_binaries/Database/dbImport/testsData/dbImport.help.txt @@ -2,19 +2,19 @@ Import source db into target db using eventual table filter. USAGE: dbImport [OPTIONS] [ARGUMENTS] -USAGE: dbImport [--collation-name|-o <>] - [--target-dsn|-t <>] [--character-set|-c <>] - [--profile|-p <>] [--tables <>] - [--skip-schema|-s] [--from-dsn|-f <>] [--from-aws|-a <>] - [--help|-h] [--config] [--bash-framework-config ] - [--verbose|-v] [-vv] [-vvv] [--env-file ] [--log-level ] +USAGE: dbImport [--collation-name|-o ] + [--target-dsn|-t ] [--character-set|-c ] + [--profile|-p ] [--tables ] + [--skip-schema|-s] [--from-dsn|-f ] [--from-aws|-a ] [--help|-h] + [--config] [--bash-framework-config ] [--verbose|-v] + [-vv] [-vvv] [--env-file ] [--log-level ] [--log-file ] [--display-level ] [--no-color] [--theme ] [--version] [--quiet|-q] ARGUMENTS: - {single} (mandatory) + fromDbName {single} (mandatory) The name of the source/remote database. - [{single}] + [ {single}] The name of the target database Default value: (without extension) @@ -25,11 +25,11 @@ Import source db into target db using eventual table filter. --target-dsn, -t  {single} Dsn to use for target database. Default value: default.local - --character-set, -c  {single} + --character-set, -c  {single} Change the character set used during database creation. PROFILE OPTIONS: - --profile, -p  {single} + --profile, -p  {single} The name of the profile to use in order to include or exclude tables. Default value: default --tables  {single} @@ -54,7 +54,7 @@ Import source db into target db using eventual table filter. Displays this command help --config {single} Displays configuration - --bash-framework-config bash-framework-config {single} + --bash-framework-config  {single} Use alternate bash framework configuration. --verbose, -v {single} Info level verbose mode (alias of --display-level INFO) @@ -62,20 +62,20 @@ Import source db into target db using eventual table filter. Debug level verbose mode (alias of --display-level DEBUG) -vvv {single} Trace level verbose mode (alias of --display-level TRACE) - --env-file env-file {list} (optional) + --env-file  {list} (optional) Load the specified env file (deprecated, please use --bash-framework-con fig option instead) - --log-level log-level {single} + --log-level  {single} Set log level Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE - --log-file log-file {single} + --log-file  {single} Set log file - --display-level display-level {single} + --display-level  {single} Set display level Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE --no-color {single} Produce monochrome output. alias of --theme noColor. - --theme theme {single} + --theme  {single} Choose color theme - default-force means colors will be produced even if command is piped. Possible values: default, default-force, noColor diff --git a/src/_binaries/Database/dbImport/testsData/dbImportProfile.help.txt b/src/_binaries/Database/dbImport/testsData/dbImportProfile.help.txt deleted file mode 100644 index ee081e13..00000000 --- a/src/_binaries/Database/dbImport/testsData/dbImportProfile.help.txt +++ /dev/null @@ -1,89 +0,0 @@ -DESCRIPTION: generate optimized profiles to be used by dbImport -USAGE: dbImportProfile [OPTIONS] [ARGUMENTS] -USAGE: dbImportProfile [--profile|-p ] - [--from-dsn|-f ] [--ratio|-r ] - [--bash-framework-config ] [--config] [--verbose|-v] [-vv] [-vvv] - [--env-file ] [--no-color] [--theme ] [--help|-h] [--version] - [--quiet|-q] [--log-level ] [--log-file ] - [--display-level ] - -ARGUMENTS: - fromDbName {single} (mandatory) - the name of the source/remote database - -OPTIONS: - --profile, -p  {single} - the name of the profile to write in profiles directory. If not provided, th - e file name pattern will be 'auto__.sh' - --from-dsn, -f  {single} - dsn to use for source database (Default: default.remote) if not provided, th - e file name pattern will be 'auto__.sh' - --ratio, -r  {single} - define the ratio to use (0 to 100% - default 70). 0 means profile will filt - er out all the tables. 100 means profile will keep all the tables. Eg: - 70 means that tables with size(table+index) that are greater that 70% of - the max table size will be excluded. - -GLOBAL OPTIONS: - --bash-framework-config  {single} - use alternate bash framework configuration. - --config {single} - Display configuration - --verbose, -v {single} - info level verbose mode (alias of --display-level INFO) - -vv {single} - debug level verbose mode (alias of --display-level DEBUG) - -vvv {single} - trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-config - option instead) - --no-color {single} - Produce monochrome output. alias of --theme noColor. - --theme  {single} - choose color theme - default-force means colors will be produced even if com - mand is piped - Default value: default - Possible values: default|default-force|noColor - --help, -h {single} - Display this command help - --version {single} - Print version information and quit - --quiet, -q {single} - quiet mode, doesn't display any output - --log-level  {single} - Set log level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - --log-file  {single} - Set log file - --display-level  {single} - set display level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - -Default profiles directory: -/bash/conf/dbImportProfiles - -User profiles directory: -home/.bash-tools/dbImportProfiles -Allows to override profiles defined in Default profiles directory - -List of available profiles: - - -List of available dsn: - - default.local - - default.remote - - localhost-root - -VERSION: 2.0 - -AUTHOR: -[François Chastanet](https://github.com/fchastanet) - -SOURCE FILE: -https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/DbImport/dbImportProfile.sh - -LICENSE: -MIT License - -Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Database/dbImport/testsData/dbImportStream.help.txt b/src/_binaries/Database/dbImport/testsData/dbImportStream.help.txt deleted file mode 100644 index 0ff1b343..00000000 --- a/src/_binaries/Database/dbImport/testsData/dbImportStream.help.txt +++ /dev/null @@ -1,92 +0,0 @@ -DESCRIPTION: stream tar.gz file or gz file through mysql -USAGE: dbImportStream [OPTIONS] [ARGUMENTS] -USAGE: dbImportStream [--profile|-p ] - [--tables ] [--target-dsn|-t ] - [--character-set|-c ] [--bash-framework-config ] [--config] - [--verbose|-v] [-vv] [-vvv] [--env-file ] [--no-color] - [--theme ] [--help|-h] [--version] [--quiet|-q] [--log-level ] - [--log-file ] [--display-level ] - -ARGUMENTS: - argDumpFile {single} (mandatory) - the of the file that will be streamed through mysql - argTargetDbName {single} (mandatory) - the name of the mysql target database - -PROFILE OPTIONS: - --profile, -p  {single} - the name of the profile to use in order to include or exclude tables (if not - specified in default.sh from 'User profiles directory' if exists or 'Def - ault profiles directory') - --tables  {single} - import only table specified in the list. If aws mode, ignore profile option - -TARGET OPTIONS: - --target-dsn, -t  {single} - dsn to use for target database (Default: default.local) - --character-set, -c  {single} - change the character set used during database creation (default value: utf8) - -GLOBAL OPTIONS: - --bash-framework-config  {single} - use alternate bash framework configuration. - --config {single} - Display configuration - --verbose, -v {single} - info level verbose mode (alias of --display-level INFO) - -vv {single} - debug level verbose mode (alias of --display-level DEBUG) - -vvv {single} - trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-config - option instead) - --no-color {single} - Produce monochrome output. alias of --theme noColor. - --theme  {single} - choose color theme - default-force means colors will be produced even if com - mand is piped - Default value: default - Possible values: default|default-force|noColor - --help, -h {single} - Display this command help - --version {single} - Print version information and quit - --quiet, -q {single} - quiet mode, doesn't display any output - --log-level  {single} - Set log level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - --log-file  {single} - Set log file - --display-level  {single} - set display level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - -Default profiles directory: -/bash/conf/dbImportProfiles - -User profiles directory: -home/.bash-tools/dbImportProfiles -Allows to override profiles defined in Default profiles directory - -List of available profiles: - - -List of available dsn: - - default.local - - default.remote - - localhost-root - -VERSION: 2.0 - -AUTHOR: -[François Chastanet](https://github.com/fchastanet) - -SOURCE FILE: -https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/DbImport/dbImportStream.sh - -LICENSE: -MIT License - -Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Database/dbImport/testsData/dbImportTableDump.sql b/src/_binaries/Database/dbImport/testsData/dbImportTableDump.sql deleted file mode 100644 index e832194e..00000000 --- a/src/_binaries/Database/dbImport/testsData/dbImportTableDump.sql +++ /dev/null @@ -1,35 +0,0 @@ --- MySQL dump 10.13 Distrib 8.0.22, for Linux (x86_64) --- --- Host: 127.0.0.1 Database: fromDb --- ------------------------------------------------------ --- Server version 8.0.21 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!50503 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Dumping data for table `emptyTable` --- - -DROP TABLE IF EXISTS `dataTable`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `dataTable` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `modification_date` datetime NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2011640 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - -LOCK TABLES `dataTable` WRITE; -/*!40000 ALTER TABLE `dataTable` DISABLE KEYS */; -INSERT INTO `dataTable` VALUES (1,1,'Picture:','en-GB'),(2,2,'Name:','en-GB'),(3,3,'First name:','en-GB'),(4,4,'Login:','en-GB'),(5,5,'Password:','en-GB'),(6,6,'E-mail:','en-GB'),(7,7,'Reference number:','en-GB'),(8,8,'Presentation:','en-GB'),(9,9,'Web:','en-GB'),(10,10,'Twitter:','en-GB'),(11,11,'LinkedIn:','en-GB'),(12,12,'Languages:','en-GB'),(13,13,'Time zone:','en-GB'); -/*!40000 ALTER TABLE `dataTable` ENABLE KEYS */; diff --git a/src/_binaries/Database/dbImport/testsData/empty-dump.sql b/src/_binaries/Database/dbImport/testsData/empty-dump.sql deleted file mode 100644 index c378ac9a..00000000 --- a/src/_binaries/Database/dbImport/testsData/empty-dump.sql +++ /dev/null @@ -1 +0,0 @@ --- EMPTY DUMP diff --git a/src/_binaries/Database/dbImport/testsData/expectedDbImportTableDump.sql b/src/_binaries/Database/dbImport/testsData/expectedDbImportTableDump.sql deleted file mode 100644 index 8291932a..00000000 --- a/src/_binaries/Database/dbImport/testsData/expectedDbImportTableDump.sql +++ /dev/null @@ -1,42 +0,0 @@ -SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS = 0; -SET @OLD_AUTOCOMMIT=@@AUTOCOMMIT, AUTOCOMMIT = 0; -SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS = 0; --- MySQL dump 10.13 Distrib 8.0.22, for Linux (x86_64) --- --- Host: 127.0.0.1 Database: fromDb --- ------------------------------------------------------ --- Server version 8.0.21 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!50503 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Dumping data for table `emptyTable` --- - -DROP TABLE IF EXISTS `dataTable`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `dataTable` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `modification_date` datetime NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2011640 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - -LOCK TABLES `dataTable` WRITE; -/*!40000 ALTER TABLE `dataTable` DISABLE KEYS */; -INSERT INTO `dataTable` VALUES (1,1,'Picture:','en-GB'),(2,2,'Name:','en-GB'),(3,3,'First name:','en-GB'),(4,4,'Login:','en-GB'),(5,5,'Password:','en-GB'),(6,6,'E-mail:','en-GB'),(7,7,'Reference number:','en-GB'),(8,8,'Presentation:','en-GB'),(9,9,'Web:','en-GB'),(10,10,'Twitter:','en-GB'),(11,11,'LinkedIn:','en-GB'),(12,12,'Languages:','en-GB'),(13,13,'Time zone:','en-GB'); -/*!40000 ALTER TABLE `dataTable` ENABLE KEYS */; -UNLOCK TABLES; -SET AUTOCOMMIT=@OLD_AUTOCOMMIT; -SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; -SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; diff --git a/src/_binaries/Database/dbImport/testsData/expectedDbImportTableDumpRenamed.sql b/src/_binaries/Database/dbImport/testsData/expectedDbImportTableDumpRenamed.sql deleted file mode 100644 index 2818b786..00000000 --- a/src/_binaries/Database/dbImport/testsData/expectedDbImportTableDumpRenamed.sql +++ /dev/null @@ -1,42 +0,0 @@ -SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS = 0; -SET @OLD_AUTOCOMMIT=@@AUTOCOMMIT, AUTOCOMMIT = 0; -SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS = 0; --- MySQL dump 10.13 Distrib 8.0.22, for Linux (x86_64) --- --- Host: 127.0.0.1 Database: fromDb --- ------------------------------------------------------ --- Server version 8.0.21 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!50503 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Dumping data for table `emptyTable` --- - -DROP TABLE IF EXISTS `newTableName`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `newTableName` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `modification_date` datetime NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2011640 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - -LOCK TABLES `newTableName` WRITE; -/*!40000 ALTER TABLE `newTableName` DISABLE KEYS */; -INSERT INTO `newTableName` VALUES (1,1,'Picture:','en-GB'),(2,2,'Name:','en-GB'),(3,3,'First name:','en-GB'),(4,4,'Login:','en-GB'),(5,5,'Password:','en-GB'),(6,6,'E-mail:','en-GB'),(7,7,'Reference number:','en-GB'),(8,8,'Presentation:','en-GB'),(9,9,'Web:','en-GB'),(10,10,'Twitter:','en-GB'),(11,11,'LinkedIn:','en-GB'),(12,12,'Languages:','en-GB'),(13,13,'Time zone:','en-GB'); -/*!40000 ALTER TABLE `newTableName` ENABLE KEYS */; -UNLOCK TABLES; -SET AUTOCOMMIT=@OLD_AUTOCOMMIT; -SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; -SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; diff --git a/src/_binaries/Database/dbImport/testsData/tableSizeQuery.sql b/src/_binaries/Database/dbImport/testsData/tableSizeQuery.sql deleted file mode 100644 index e169bb05..00000000 --- a/src/_binaries/Database/dbImport/testsData/tableSizeQuery.sql +++ /dev/null @@ -1 +0,0 @@ -SELECT ROUND(SUM(data_length + index_length) / 1024 / 1024, 0) AS size FROM information_schema.TABLES WHERE table_schema="fromDb" AND table_name='tableName' GROUP BY table_schema diff --git a/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml b/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml new file mode 100644 index 00000000..bbab2601 --- /dev/null +++ b/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml @@ -0,0 +1,67 @@ +extends: + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" + +vars: + SRC_FILE_PATH: src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml + +compilerConfig: + targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/dbImportProfile" + relativeRootDirBasedOnTargetDir: .. + srcDirs: + - ${BASH_TOOLS_ROOT_DIR}/src +binData: + commands: + default: + functionName: dbImportProfileCommand + version: "2.0" + commandName: dbImportProfile + beforeParseCallbacks: + - beforeParseCallback + - initConf + callbacks: + - dbImportProfileCommandCallback@100 + definitionFiles: + 20: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbImportProfile/dbImportProfile-options.sh + 99: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.sh + mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbImportProfile/dbImportProfile-main.sh + help: Generate optimized profiles to be used by dbImport. + longDescription: longDescriptionFunction + options: + - variableName: optionProfile + group: OptionsGroup + type: String + help: optionProfileHelpFunction + helpValueName: profile + alts: + - --profile + - -p + + - variableName: optionFromDsn + group: OptionsGroup + type: String + defaultValue: default.remote + help: optionFromDsnHelpFunction + helpValueName: dsn + alts: + - --from-dsn + - -f + + - variableName: optionRatio + group: OptionsGroup + type: String + help: optionRatioHelpFunction + helpValueName: ratio + defaultValue: 70 + alts: + - --ratio + - -r + + args: + - help: The name of the source/remote database. + type: String + min: 1 + max: 1 + name: fromDbName + variableName: fromDbName diff --git a/src/_binaries/Database/dbImportProfile/dbImportProfile-main.sh b/src/_binaries/Database/dbImportProfile/dbImportProfile-main.sh new file mode 100755 index 00000000..0aecadda --- /dev/null +++ b/src/_binaries/Database/dbImportProfile/dbImportProfile-main.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash + +# other configuration +read -r -d '' QUERY <"${HOME_PROFILES_DIR}/${optionProfile}.sh" + +Log::displayInfo "File saved in '${HOME_PROFILES_DIR}/${optionProfile}.sh'" diff --git a/src/_binaries/Database/dbImportProfile/dbImportProfile-options.sh b/src/_binaries/Database/dbImportProfile/dbImportProfile-options.sh new file mode 100755 index 00000000..c5130972 --- /dev/null +++ b/src/_binaries/Database/dbImportProfile/dbImportProfile-options.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +declare defaultFromDsn="default.remote" +# shellcheck disable=SC2034 +declare versionNumber="2.0" +# shellcheck disable=SC2034 +declare copyrightBeginYear="2020" +# shellcheck disable=SC2034 +declare PROFILES_DIR +declare HOME_PROFILES_DIR + +initConf() { + # shellcheck disable=SC2034 + PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbImportProfiles" + HOME_PROFILES_DIR="${HOME}/.bash-tools/dbImportProfiles" +} + +optionHelpCallback() { + dbImportProfileCommandHelp + exit 0 +} + +longDescriptionFunction() { + local profilesList="" + local dsnList="" + dsnList="$(Conf::getMergedList "dsn" "env")" + profilesList="$(Conf::getMergedList "dbImportProfiles" "sh" || true)" + + echo -e "${__HELP_TITLE}Default profiles directory:${__HELP_NORMAL}" + echo -e "${PROFILES_DIR-configuration error}" + echo + echo -e "${__HELP_TITLE}User profiles directory:${__HELP_NORMAL}" + echo -e "${HOME_PROFILES_DIR-configuration error}" + echo -e 'Allows to override profiles defined in "Default profiles directory"' + echo + echo -e "${__HELP_TITLE}List of available profiles:${__HELP_NORMAL}" + echo -e "${profilesList}" + echo + echo -e "${__HELP_TITLE}List of available dsn:${__HELP_NORMAL}" + echo -e "${dsnList}" +} + +optionProfileHelpFunction() { + Array::wrap2 " " 80 4 \ + " The name of the profile to write in profiles directory.\r" \ + "If not provided, the file name pattern will be 'auto__.sh'" + echo +} + +optionFromDsnHelpFunction() { + Array::wrap2 " " 80 4 \ + " dsn to use for source database (Default: ${defaultFromDsn})\r" \ + "if not provided, the file name pattern will be 'auto__.sh'" + echo +} + +optionRatioHelpFunction() { + Array::wrap2 " " 80 4 \ + " define the ratio to use (0 to 100% - default 70).\r" \ + "- 0 means profile will filter out all the tables\r" \ + "- 100 means profile will keep all the tables.\r" \ + "Eg: 70 means that tables with size(table+index)\r" \ + "that are greater than 70% of the max table size will be excluded." + echo +} + +dbImportProfileCommandCallback() { + if [[ -z "${fromDbName}" ]]; then + Log::fatal "you must provide fromDbName" + fi + + if [[ -z "${optionProfile}" ]]; then + # shellcheck disable=SC2154 + optionProfile="auto_${optionFromDsn}_${fromDbName}" + fi + + # shellcheck disable=SC2154 + if ! [[ "${optionRatio}" =~ ^-?[0-9]+$ ]]; then + Log::fatal "Ratio value should be a number" + fi + + if ((optionRatio < 0 || optionRatio > 100)); then + Log::fatal "Ratio value should be between 0 and 100" + fi +} diff --git a/src/_binaries/DbImport/dbImportProfile.bats b/src/_binaries/Database/dbImportProfile/dbImportProfile.bats similarity index 98% rename from src/_binaries/DbImport/dbImportProfile.bats rename to src/_binaries/Database/dbImportProfile/dbImportProfile.bats index 8e169b5a..57ee26f5 100755 --- a/src/_binaries/DbImport/dbImportProfile.bats +++ b/src/_binaries/Database/dbImportProfile/dbImportProfile.bats @@ -1,7 +1,7 @@ #!/usr/bin/env bash # shellcheck source=src/batsHeaders.sh -source "$(cd "${BATS_TEST_DIRNAME}/../.." && pwd)/batsHeaders.sh" +source "$(cd "${BATS_TEST_DIRNAME}/../../.." && pwd)/batsHeaders.sh" # shellcheck source=vendor/bash-tools-framework/src/Log/_.sh source "${FRAMEWORK_ROOT_DIR}/src/Log/_.sh" || exit 1 diff --git a/src/_binaries/Database/dbImport/testsData/auto_default.local_fromDb_20.sh b/src/_binaries/Database/dbImportProfile/testsData/auto_default.local_fromDb_20.sh similarity index 100% rename from src/_binaries/Database/dbImport/testsData/auto_default.local_fromDb_20.sh rename to src/_binaries/Database/dbImportProfile/testsData/auto_default.local_fromDb_20.sh diff --git a/src/_binaries/Database/dbImport/testsData/auto_default.local_fromDb_70.sh b/src/_binaries/Database/dbImportProfile/testsData/auto_default.local_fromDb_70.sh similarity index 100% rename from src/_binaries/Database/dbImport/testsData/auto_default.local_fromDb_70.sh rename to src/_binaries/Database/dbImportProfile/testsData/auto_default.local_fromDb_70.sh diff --git a/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt b/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt new file mode 100644 index 00000000..18aa4a26 --- /dev/null +++ b/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt @@ -0,0 +1,91 @@ +SYNOPSIS: Generate optimized profiles to be used by dbImport. + +USAGE: dbImportProfile [OPTIONS] [ARGUMENTS] +USAGE: dbImportProfile [--help|-h] [--config] + [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] + [--env-file ] [--log-level ] [--log-file ] + [--display-level ] [--no-color] [--theme ] [--version] + [--quiet|-q] [--profile|-p ] [--from-dsn|-f ] + [--ratio|-r ] + +ARGUMENTS: + fromDbName {single} (mandatory) + The name of the source/remote database. + +GLOBAL OPTIONS: + --help, -h {single} + Displays this command help + --config {single} + Displays configuration + --bash-framework-config  {single} + Use alternate bash framework configuration. + --verbose, -v {single} + Info level verbose mode (alias of --display-level INFO) + -vv {single} + Debug level verbose mode (alias of --display-level DEBUG) + -vvv {single} + Trace level verbose mode (alias of --display-level TRACE) + --env-file  {list} (optional) + Load the specified env file (deprecated, please use --bash-framework-con + fig option instead) + --log-level  {single} + Set log level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --log-file  {single} + Set log file + --display-level  {single} + Set display level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --no-color {single} + Produce monochrome output. alias of --theme noColor. + --theme  {single} + Choose color theme - default-force means colors will be produced even if + command is piped. + Possible values: default, default-force, noColor + Default value: default + --version {single} + Print version information and quit. + --quiet, -q {single} + Quiet mode, doesn't display any output. + +OPTIONS: + --profile, -p  {single} + The name of the profile to write in profiles directory. + If not provided, the file name pattern will be 'auto__.sh' + --from-dsn, -f  {single} + dsn to use for source database (Default: default.remote) + if not provided, the file name pattern will be 'auto__.sh' + Default value: default.remote + --ratio, -r  {single} + define the ratio to use (0 to 100% - default 70). + - 0 means profile will filter out all the tables + - 100 means profile will keep all the tables. + Eg: 70 means that tables with size(table+index) + that are greater than 70% of the max table size will be excluded. + Default value: 70 + + +DESCRIPTION: +Default profiles directory: +/bash/conf/dbImportProfiles + +User profiles directory: +home/.bash-tools/dbImportProfiles +Allows to override profiles defined in "Default profiles directory" + +List of available profiles: + + +List of available dsn: + - default.local + - default.remote + - localhost-root + +VERSION: 2.0 + +AUTHOR: [François Chastanet](https://github.com/fchastanet) + +SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml + +LICENSE: MIT License +Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Database/dbImport/testsData/dbImportProfile.tableList1 b/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.tableList1 similarity index 100% rename from src/_binaries/Database/dbImport/testsData/dbImportProfile.tableList1 rename to src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.tableList1 diff --git a/src/_binaries/Database/dbImportProfile/testsData/dbImportProfiles/all.sh b/src/_binaries/Database/dbImportProfile/testsData/dbImportProfiles/all.sh new file mode 100755 index 00000000..09b79b19 --- /dev/null +++ b/src/_binaries/Database/dbImportProfile/testsData/dbImportProfiles/all.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +# cat represents the whole list of tables +cat diff --git a/src/_binaries/Database/dbImportProfile/testsData/dbImportProfiles/default.sh b/src/_binaries/Database/dbImportProfile/testsData/dbImportProfiles/default.sh new file mode 100755 index 00000000..8cf9b92b --- /dev/null +++ b/src/_binaries/Database/dbImportProfile/testsData/dbImportProfiles/default.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +# cat represents the whole list of tables +cat | + grep -v '.*_log' | + grep -v '.*logs' | + grep -v '.*tracking' | + grep -v '.*stats' | + grep -v '.*history.*' | + # always finish by a cat to be sure the command does not return exit code != 0 + cat || { + # avoid failure on premature process close (check Bash::handlePipelineFailure) + declare exitCode=$?; if (( exitCode == 141 )); then exit 0; fi + exit "${exitCode}" + } diff --git a/src/_binaries/Database/dbImportProfile/testsData/dbImportProfiles/none.sh b/src/_binaries/Database/dbImportProfile/testsData/dbImportProfiles/none.sh new file mode 100755 index 00000000..5604fdca --- /dev/null +++ b/src/_binaries/Database/dbImportProfile/testsData/dbImportProfiles/none.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +# useful if you want to dump only the db structure + +# cat represents the whole list of tables +echo 'propel_migration' diff --git a/src/_binaries/Database/dbImportProfile/testsData/dsn/default.local.env b/src/_binaries/Database/dbImportProfile/testsData/dsn/default.local.env new file mode 100644 index 00000000..ccb2e265 --- /dev/null +++ b/src/_binaries/Database/dbImportProfile/testsData/dsn/default.local.env @@ -0,0 +1,13 @@ +# shellcheck disable=SC2034 +HOSTNAME=127.0.0.1 +PASSWORD=root +PORT=3306 +USER=root + +# you can override default MYSQL options if you wish +# SSL_OPTIONS=--ssl-mode=DISABLED +# QUERY_OPTIONS=--batch --raw --default-character-set=utf8 +# DUMP_OPTIONS=--default-character-set=utf8 --compression-algorithms --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 + +# specific options during import +DB_IMPORT_OPTIONS= diff --git a/src/_binaries/Database/dbImportProfile/testsData/dsn/default.remote.env b/src/_binaries/Database/dbImportProfile/testsData/dsn/default.remote.env new file mode 100644 index 00000000..1958d45e --- /dev/null +++ b/src/_binaries/Database/dbImportProfile/testsData/dsn/default.remote.env @@ -0,0 +1,12 @@ +HOSTNAME=127.0.0.1 +PASSWORD=root +PORT=3306 +USER=root + +# you can override default MYSQL options if you wish +# SSL_OPTIONS=--ssl-mode=DISABLED +# QUERY_OPTIONS=--batch --raw --default-character-set=utf8 +# DUMP_OPTIONS=--default-character-set=utf8 --compression-algorithms --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 + +# specific options during import +DB_IMPORT_OPTIONS= diff --git a/src/_binaries/Database/dbImportProfile/testsData/dsn/localhost-root.env b/src/_binaries/Database/dbImportProfile/testsData/dsn/localhost-root.env new file mode 100644 index 00000000..1958d45e --- /dev/null +++ b/src/_binaries/Database/dbImportProfile/testsData/dsn/localhost-root.env @@ -0,0 +1,12 @@ +HOSTNAME=127.0.0.1 +PASSWORD=root +PORT=3306 +USER=root + +# you can override default MYSQL options if you wish +# SSL_OPTIONS=--ssl-mode=DISABLED +# QUERY_OPTIONS=--batch --raw --default-character-set=utf8 +# DUMP_OPTIONS=--default-character-set=utf8 --compression-algorithms --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 + +# specific options during import +DB_IMPORT_OPTIONS= diff --git a/src/_binaries/Database/dbImport/testsData/expectedDbImportProfileTableListQuery.sql b/src/_binaries/Database/dbImportProfile/testsData/expectedDbImportProfileTableListQuery.sql similarity index 100% rename from src/_binaries/Database/dbImport/testsData/expectedDbImportProfileTableListQuery.sql rename to src/_binaries/Database/dbImportProfile/testsData/expectedDbImportProfileTableListQuery.sql diff --git a/src/_binaries/DbImport/dbImport.options.tpl b/src/_binaries/DbImport/dbImport.options.tpl deleted file mode 100644 index 680c4f31..00000000 --- a/src/_binaries/DbImport/dbImport.options.tpl +++ /dev/null @@ -1,141 +0,0 @@ -% -declare versionNumber="2.0" -declare commandFunctionName="dbImportCommand" -declare help="Import source db into target db using eventual table filter" -# shellcheck disable=SC2016 -declare longDescription=''' -${__HELP_TITLE}Default profiles directory:${__HELP_NORMAL} -${PROFILES_DIR-configuration error} - -${__HELP_TITLE}User profiles directory:${__HELP_NORMAL} -${HOME_PROFILES_DIR-configuration error} -Allows to override profiles defined in "Default profiles directory" - -${__HELP_TITLE}List of available profiles:${__HELP_NORMAL} -${profilesList} - -${__HELP_TITLE}List of available dsn:${__HELP_NORMAL} -${dsnList} - -${__HELP_TITLE}Aws s3 location:${__HELP_NORMAL} -${S3_BASE_URL} - -${__HELP_TITLE}Example 1: from one database to another one${__HELP_NORMAL} -${__HELP_EXAMPLE}TODO${__HELP_NORMAL} - -${__HELP_TITLE}Example 2: import from S3${__HELP_NORMAL} -${__HELP_EXAMPLE}TODO${__HELP_NORMAL}''' -# shellcheck disable=SC2116 -declare defaultFromDsnHelp=$'dsn to use for source database\n - this option is incompatible with -a|--from-aws option' - -% - -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.base.tpl)" -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.dsn.tpl)" -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.profile.tpl)" -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.mysql.target.tpl)" -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.mysql.collationName.tpl)" - -% -# shellcheck source=/dev/null -source <( - Options::generateGroup \ - --title "FROM OPTIONS:" \ - --function-name groupSourceDbOptionsFunction - - Options::generateOption \ - --help "avoid to import the schema" \ - --group groupSourceDbOptionsFunction \ - --alt "--skip-schema" \ - --alt "-s" \ - --variable-name "optionSkipSchema" \ - --function-name optionSkipSchemaFunction - - # shellcheck disable=SC2116 - Options::generateOption \ - --help-value-name "awsFile" \ - --help "$(echo \ - "db dump will be downloaded from s3 instead of using remote db." \ - "The value is the name of the file without s3 location" \ - "(Only .gz or tar.gz file are supported)." \ - "This option is incompatible with -f|--from-dsn option" \ - )" \ - --group groupSourceDbOptionsFunction \ - --alt "--from-aws" \ - --alt "-a" \ - --variable-type "String" \ - --variable-name "optionFromAws" \ - --function-name optionFromAwsFunction - - Options::generateArg \ - --help "the name of the source/remote database" \ - --min 1 \ - --max 1 \ - --name "fromDbName" \ - --variable-name "fromDbName" \ - --function-name argumentFromDbNameFunction - - Options::generateArg \ - --help "the name of the target database, use fromDbName(without extension) if not provided" \ - --variable-name "targetDbName" \ - --min 0 \ - --max 1 \ - --name "targetDbName" \ - --function-name argumentTargetDbNameFunction -) -options+=( - optionSkipSchemaFunction - optionFromAwsFunction - argumentFromDbNameFunction - argumentTargetDbNameFunction - --callback dbImportCommandCallback -) -Options::generateCommand "${options[@]}" -% - -.INCLUDE "$(dynamicTemplateDir _includes/dbTools.requirements.tpl)" - -optionHelpCallback() { - local profilesList="" - local dsnList="" - dsnList="$(Conf::getMergedList "dsn" "env")" - profilesList="$(Conf::getMergedList "dbImportProfiles" "sh" || true)" - - <% ${commandFunctionName} %> help | envsubst - checkRequirements - exit 0 -} - -dbImportCommandCallback() { - if [[ -z "${targetDbName}" ]]; then - targetDbName="${fromDbName}" - fi - - if [[ -n "${optionFromAws}" ]]; then - Assert::commandExists aws \ - "Command ${SCRIPT_NAME} - missing aws, please check https://docs.aws.amazon.com/fr_fr/cli/latest/userguide/install-cliv2.html" || exit 1 - - if [[ -n "${optionFromDsn}" ]]; then - Log::fatal "Command ${SCRIPT_NAME} - you cannot use from-dsn and from-aws at the same time" - fi - - if [[ -z "${S3_BASE_URL}" ]]; then - Log::fatal "Command ${SCRIPT_NAME} - missing S3_BASE_URL, please provide a value in .env file" - fi - elif [[ -z "${optionFromDsn}" ]]; then - # default value for FROM_DSN if from-aws not set - optionFromDsn="<% ${defaultFromDsn} %>" - fi - - if [[ -z "${DB_IMPORT_DUMP_DIR}" ]]; then - Log::fatal "Command ${SCRIPT_NAME} -you have to specify a value for DB_IMPORT_DUMP_DIR env variable" - fi - - if [[ ! -d "${DB_IMPORT_DUMP_DIR}" ]]; then - mkdir -p "${DB_IMPORT_DUMP_DIR}" || - Log::fatal "Command ${SCRIPT_NAME} -impossible to create directory ${DB_IMPORT_DUMP_DIR} specified by DB_IMPORT_DUMP_DIR env variable" - fi -} - -<% ${commandFunctionName} %> parse "${BASH_FRAMEWORK_ARGV[@]}" diff --git a/src/_binaries/DbImport/dbImport.sh b/src/_binaries/DbImport/dbImport.sh deleted file mode 100755 index 93bf1486..00000000 --- a/src/_binaries/DbImport/dbImport.sh +++ /dev/null @@ -1,215 +0,0 @@ -#!/usr/bin/env bash -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/dbImport -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE -# shellcheck disable=SC2034 - -# default values -declare optionFromAws="" -declare optionSkipSchema="0" -declare targetDbName="" -declare fromDbName="" -declare optionProfile="default" -declare optionTables="" -declare profileCommandFile="" -declare optionTargetDsn="" -declare optionCharacterSet="" -declare defaultTargetCharacterSet="" - -# other configuration -declare copyrightBeginYear="2020" -declare TIMEFORMAT='time spent : %3R' -declare DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR%/} -declare PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbImportProfiles" -declare HOME_PROFILES_DIR="${HOME}/.bash-tools/dbImportProfiles" -declare DOWNLOAD_DUMP=0 - -# dump header/footer -read -r -d '\0' DUMP_HEADER <<-EOM - SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS = 0; - SET @OLD_AUTOCOMMIT=@@AUTOCOMMIT, AUTOCOMMIT = 0; - SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS = 0;\0 -EOM - -read -r -d '\0' DUMP_FOOTER <<-EOM2 - COMMIT; - SET AUTOCOMMIT=@OLD_AUTOCOMMIT; - SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; - SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;\0 -EOM2 - -# @embed "${BASH_TOOLS_ROOT_DIR}/src/_binaries/DbImport/dumpSizeQuery.sql" AS dumpSizeQuery -# @require Linux::requireExecutedAsUser -run() { - # create db instances - declare -Agx dbFromInstance dbTargetDatabase - - # shellcheck disable=SC2154 - Database::newInstance dbTargetDatabase "${optionTargetDsn}" - # shellcheck disable=SC2154 - Database::setQueryOptions dbTargetDatabase "${dbTargetDatabase[QUERY_OPTIONS]} --connect-timeout=5" - Log::displayInfo "Using target dsn ${dbTargetDatabase['DSN_FILE']}" - if [[ -z "${optionFromAws}" ]]; then - # shellcheck disable=SC2154 - Database::newInstance dbFromInstance "${optionFromDsn}" - Database::setQueryOptions dbFromInstance "${dbFromInstance[QUERY_OPTIONS]} --connect-timeout=5" - Log::displayInfo "Using from dsn ${dbFromInstance['DSN_FILE']}" - fi - - local remoteDbDumpTempFile - local remoteDbStructureDumpTempFile - if [[ -n "${optionFromAws}" ]]; then - remoteDbDumpTempFile="${DB_IMPORT_DUMP_DIR}/${optionFromAws}" - else - # shellcheck disable=SC2154 - remoteDbDumpTempFile="${DB_IMPORT_DUMP_DIR}/${fromDbName}_${optionProfile}.sql.gz" - remoteDbStructureDumpTempFile="${DB_IMPORT_DUMP_DIR}/${fromDbName}_${optionProfile}_structure.sql.gz" - fi - - # check if local dump exists - local downloadDump=0 - if [[ ! -f "${remoteDbDumpTempFile}" ]]; then - Log::displayInfo "local dump does not exist" - downloadDump=1 - fi - if [[ -z "${optionFromAws}" && ! -f "${remoteDbStructureDumpTempFile}" ]]; then - Log::displayInfo "local structure dump does not exist" - downloadDump=1 - fi - if [[ "${downloadDump}" = "0" ]]; then - Log::displayInfo "local dump ${remoteDbDumpTempFile} already exists, avoid download" - fi - - Log::displayInfo "tables list will calculated using profile ${optionProfile} => ${profileCommandFile}" - SECONDS=0 - if [[ "${downloadDump}" = "1" ]]; then - Log::displayInfo "Download dump" - - if [[ -n "${optionFromAws}" ]]; then - # download dump from s3 - local s3Url="${S3_BASE_URL%/}/${optionFromAws}" - aws s3 ls --human-readable "${s3Url}" || { - Log::fatal "Command ${SCRIPT_NAME} - unable to get information on S3 object : ${s3Url}" - } - Log::displayInfo "Download dump from ${s3Url} ..." - TMPDIR="${TMDIR:-/tmp}" aws s3 cp "${s3Url}" "${remoteDbDumpTempFile}" || { - Log::fatal "Command ${SCRIPT_NAME} - unable to download dump from S3 : ${s3Url}" - } - else - # check if remote db exists - Database::ifDbExists dbFromInstance "${fromDbName}" || { - Log::fatal "Command ${SCRIPT_NAME} - Remote Database ${fromDbName} does not exist" - } - - initializeDefaultTargetMysqlOptions dbFromInstance "${fromDbName}" - - local dumpHeader - dumpHeader=$(printf "%s\nSET names '%s';\n" "${DUMP_HEADER}" "${optionCharacterSet}") - - # calculate remote db dump size - local listTables - local listTablesDumpSize - local listTablesDump - listTables="$(Database::query dbFromInstance "show tables" "${fromDbName}" | ${profileCommandFile} | sort)" - # shellcheck disable=SC2034 # used by DUMP_SIZE_QUERY - listTablesDumpSize="$(echo "${listTables}" | awk -v d="," -v q="'" '{s=(NR==1?s:s d)q $0 q}END{print s }')" - listTablesDump=$(echo "${listTables}" | awk -v d=" " -v q="" '{s=(NR==1?s:s d)q $0 q}END{print s }') - - Log::displayInfo "Calculate dump size for tables ${listTablesDump}" - local remoteDbDumpSize - # shellcheck disable=SC2154 - remoteDbDumpSize=$(envsubst <"${embed_file_dumpSizeQuery}" | Database::query dbFromInstance) - if [[ -z "${remoteDbDumpSize}" ]]; then - # could occur with the none profile - remoteDbDumpSize="0" - fi - - # dump db - Log::displayInfo "Dump the database ${fromDbName} (Size:${remoteDbDumpSize}MB) ..." - local dumpSizePvEstimation - dumpSizePvEstimation=$(awk "BEGIN {printf \"%.0f\",${remoteDbDumpSize}/1.5}") - time ( - echo "${dumpHeader}" - Database::dump dbFromInstance "${fromDbName}" "${listTablesDump}" \ - --no-create-info --skip-add-drop-table --single-transaction=TRUE | - pv --progress --size "${dumpSizePvEstimation}m" - echo "${DUMP_FOOTER}" - ) | gzip >"${remoteDbDumpTempFile}" - - Log::displayInfo "Dump structure of the database ${fromDbName} ..." - time ( - echo "${dumpHeader}" - #shellcheck disable=SC2016 - Database::dump dbFromInstance "${fromDbName}" "" \ - --no-data --skip-add-drop-table --single-transaction=TRUE | - sed 's/^CREATE TABLE `/CREATE TABLE IF NOT EXISTS `/g' - echo "${DUMP_FOOTER}" - ) | gzip >"${remoteDbStructureDumpTempFile}" - fi - Log::displayInfo "Dump done." - fi - - # mark dumps as modified now to avoid them to be garbage collected - touch -c -m "${remoteDbDumpTempFile}" || true - touch -c -m "${remoteDbStructureDumpTempFile}" || true - - # TODO Collation and character set should be retrieved from dump files if possible - # shellcheck disable=SC2154 - local targetCollationName="${optionCollationName:-${defaultTargetCollationName}}" - # shellcheck disable=SC2154 - local targetCharacterSet="${optionCharacterSet:-${defaultTargetCharacterSet}}" - - # shellcheck disable=SC2154 - Log::displayInfo "create target database ${targetDbName} if needed" - #shellcheck disable=SC2016 - Database::query dbTargetDatabase \ - "$(printf 'CREATE DATABASE IF NOT EXISTS `%s` CHARACTER SET "%s" COLLATE "%s"' "${targetDbName}" "${targetCharacterSet}" "${targetCollationName}")" - - if [[ -z "${optionFromAws}" ]]; then - Database::setQueryOptions dbTargetDatabase "${dbTargetDatabase['DB_IMPORT_OPTIONS']}" - Log::displayInfo "Importing remote db '${fromDbName}' to local db '${targetDbName}'" - # shellcheck disable=SC2154 - if [[ "${optionSkipSchema}" = "1" ]]; then - Log::displayInfo "avoid to create db structure" - else - Log::displayInfo "create db structure from ${remoteDbStructureDumpTempFile}" - # shellcheck disable=SC2034 - local status=0 - # shellcheck disable=SC2034 - local -a pipeStatus=() - time ( - pv "${remoteDbStructureDumpTempFile}" | zcat | - Database::query dbTargetDatabase "" "${targetDbName}" || Bash::handlePipelineFailure status pipeStatus - ) - fi - fi - Log::displayInfo "import remote to local from file ${remoteDbDumpTempFile}" - local -a dbImportStreamOptions=( - --profile "${optionProfile}" - --target-dsn "${optionTargetDsn}" - --character-set "${targetCharacterSet}" - ) - if [[ -n "${optionTables:-}" ]]; then - dbImportStreamOptions+=( - --tables "${optionTables}" - ) - fi - time ( - "${CURRENT_DIR}/dbImportStream" \ - "${dbImportStreamOptions[@]}" \ - "${remoteDbDumpTempFile}" \ - "${targetDbName}" - - ) - - # garbage collect db import dumps - File::garbageCollect "${DB_IMPORT_DUMP_DIR}" "${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30}" || true - - Log::displayInfo "Import database duration : $(date -u -d "@${SECONDS}" +"%T")" -} - -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run -fi diff --git a/src/_binaries/DbImport/dbImportProfile.options.tpl b/src/_binaries/DbImport/dbImportProfile.options.tpl deleted file mode 100644 index a89a6ca6..00000000 --- a/src/_binaries/DbImport/dbImportProfile.options.tpl +++ /dev/null @@ -1,110 +0,0 @@ -% -declare versionNumber="2.0" -declare commandFunctionName="dbImportProfileCommand" -declare help="generate optimized profiles to be used by dbImport" -# shellcheck disable=SC2016 -declare longDescription=''' -${__HELP_TITLE}Default profiles directory:${__HELP_NORMAL} -${PROFILES_DIR-configuration error} - -${__HELP_TITLE}User profiles directory:${__HELP_NORMAL} -${HOME_PROFILES_DIR-configuration error} -Allows to override profiles defined in "Default profiles directory" - -${__HELP_TITLE}List of available profiles:${__HELP_NORMAL} -${profilesList} - -${__HELP_TITLE}List of available dsn:${__HELP_NORMAL} -${dsnList}''' -declare defaultFromDsn="default.remote" -% - -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.base.tpl)" - -% -# shellcheck source=/dev/null -source <( - # shellcheck disable=SC2116 - Options::generateOption \ - --help "$(echo \ - "the name of the profile to write in profiles directory. " \ - "If not provided, the file name pattern will be 'auto__.sh'" \ - )" \ - --variable-type "String" \ - --alt "--profile" \ - --alt "-p" \ - --variable-name "optionProfile" \ - --function-name optionProfileFunction - - # shellcheck disable=SC2116 - Options::generateOption \ - --help "$(echo \ - "dsn to use for source database (Default: ${defaultFromDsn})" \ - "if not provided, the file name pattern will be 'auto__.sh'" \ - )" \ - --variable-type "String" \ - --alt "--from-dsn" \ - --alt "-f" \ - --variable-name "optionFromDsn" \ - --function-name optionFromDsnFunction - - # shellcheck disable=SC2116 - Options::generateOption \ - --help "$(echo -e "define the ratio to use (0 to 100% - default 70). " \ - "0 means profile will filter out all the tables. " \ - "100 means profile will keep all the tables. " \ - "Eg: 70 means that tables with size(table+index) that are greater that 70% of the max table size will be excluded." \ - )" \ - --variable-type "String" \ - --alt "--ratio" \ - --alt "-r" \ - --variable-name "optionRatio" \ - --function-name optionRatioFunction - - Options::generateArg \ - --help "the name of the source/remote database" \ - --min 1 \ - --max 1 \ - --name "fromDbName" \ - --variable-name "fromDbName" \ - --function-name argumentFromDbNameFunction -) -options+=( - optionProfileFunction - optionFromDsnFunction - optionRatioFunction - argumentFromDbNameFunction - --callback dbImportProfileCommandCallback -) -Options::generateCommand "${options[@]}" -% - -optionHelpCallback() { - local profilesList="" - local dsnList="" - dsnList="$(Conf::getMergedList "dsn" "env")" - profilesList="$(Conf::getMergedList "dbImportProfiles" "sh" || true)" - - <% ${commandFunctionName} %> help | envsubst - exit 0 -} - -dbImportProfileCommandCallback() { - if [[ -z "${fromDbName}" ]]; then - Log::fatal "you must provide fromDbName" - fi - - if [[ -z "${optionProfile}" ]]; then - optionProfile="auto_${optionFromDsn}_${fromDbName}.sh" - fi - - if ! [[ "${optionRatio}" =~ ^-?[0-9]+$ ]]; then - Log::fatal "Ratio value should be a number" - fi - - if ((optionRatio < 0 || optionRatio > 100)); then - Log::fatal "Ratio value should be between 0 and 100" - fi -} - -<% ${commandFunctionName} %> parse "${BASH_FRAMEWORK_ARGV[@]}" diff --git a/src/_binaries/DbImport/dbImportProfile.sh b/src/_binaries/DbImport/dbImportProfile.sh deleted file mode 100755 index 8f8b2a60..00000000 --- a/src/_binaries/DbImport/dbImportProfile.sh +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env bash -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/dbImportProfile -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE -# shellcheck disable=SC2034 - -# default values -declare optionProfile="" -declare fromDbName="" -declare optionFromDsn="default.remote" -declare optionRatio=70 - -# other configuration -declare copyrightBeginYear="2020" -declare PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbImportProfiles" -declare HOME_PROFILES_DIR="${HOME}/.bash-tools/dbImportProfiles" - -.INCLUDE "$(dynamicTemplateDir _binaries/DbImport/dbImportProfile.options.tpl)" - -read -r -d '' QUERY <"${HOME_PROFILES_DIR}/${optionProfile}" - - Log::displayInfo "File saved in '${HOME_PROFILES_DIR}/${optionProfile}'" -} - -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run -fi diff --git a/src/_binaries/DbImport/dbImportStream.options.tpl b/src/_binaries/DbImport/dbImportStream.options.tpl index 90a5a648..10ea9b5e 100644 --- a/src/_binaries/DbImport/dbImportStream.options.tpl +++ b/src/_binaries/DbImport/dbImportStream.options.tpl @@ -61,7 +61,7 @@ optionHelpCallback() { profilesList="$(Conf::getMergedList "dbImportProfiles" "sh" || true)" <% ${commandFunctionName} %> help | envsubst - checkRequirements + Db::checkRequirements exit 0 } diff --git a/src/_binaries/DbImport/testsData/dbImportProfile.help.txt b/src/_binaries/DbImport/testsData/dbImportProfile.help.txt deleted file mode 100644 index ee081e13..00000000 --- a/src/_binaries/DbImport/testsData/dbImportProfile.help.txt +++ /dev/null @@ -1,89 +0,0 @@ -DESCRIPTION: generate optimized profiles to be used by dbImport -USAGE: dbImportProfile [OPTIONS] [ARGUMENTS] -USAGE: dbImportProfile [--profile|-p ] - [--from-dsn|-f ] [--ratio|-r ] - [--bash-framework-config ] [--config] [--verbose|-v] [-vv] [-vvv] - [--env-file ] [--no-color] [--theme ] [--help|-h] [--version] - [--quiet|-q] [--log-level ] [--log-file ] - [--display-level ] - -ARGUMENTS: - fromDbName {single} (mandatory) - the name of the source/remote database - -OPTIONS: - --profile, -p  {single} - the name of the profile to write in profiles directory. If not provided, th - e file name pattern will be 'auto__.sh' - --from-dsn, -f  {single} - dsn to use for source database (Default: default.remote) if not provided, th - e file name pattern will be 'auto__.sh' - --ratio, -r  {single} - define the ratio to use (0 to 100% - default 70). 0 means profile will filt - er out all the tables. 100 means profile will keep all the tables. Eg: - 70 means that tables with size(table+index) that are greater that 70% of - the max table size will be excluded. - -GLOBAL OPTIONS: - --bash-framework-config  {single} - use alternate bash framework configuration. - --config {single} - Display configuration - --verbose, -v {single} - info level verbose mode (alias of --display-level INFO) - -vv {single} - debug level verbose mode (alias of --display-level DEBUG) - -vvv {single} - trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-config - option instead) - --no-color {single} - Produce monochrome output. alias of --theme noColor. - --theme  {single} - choose color theme - default-force means colors will be produced even if com - mand is piped - Default value: default - Possible values: default|default-force|noColor - --help, -h {single} - Display this command help - --version {single} - Print version information and quit - --quiet, -q {single} - quiet mode, doesn't display any output - --log-level  {single} - Set log level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - --log-file  {single} - Set log file - --display-level  {single} - set display level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - -Default profiles directory: -/bash/conf/dbImportProfiles - -User profiles directory: -home/.bash-tools/dbImportProfiles -Allows to override profiles defined in Default profiles directory - -List of available profiles: - - -List of available dsn: - - default.local - - default.remote - - localhost-root - -VERSION: 2.0 - -AUTHOR: -[François Chastanet](https://github.com/fchastanet) - -SOURCE FILE: -https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/DbImport/dbImportProfile.sh - -LICENSE: -MIT License - -Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/DbQueryAllDatabases/dbQueryAllDatabases.options.tpl b/src/_binaries/DbQueryAllDatabases/dbQueryAllDatabases.options.tpl index 596be118..4cba7255 100644 --- a/src/_binaries/DbQueryAllDatabases/dbQueryAllDatabases.options.tpl +++ b/src/_binaries/DbQueryAllDatabases/dbQueryAllDatabases.options.tpl @@ -80,7 +80,7 @@ optionHelpCallback() { queriesList="$(Conf::getMergedList "dbQueries" "sql" || true)" <% ${commandFunctionName} %> help | envsubst - checkRequirements + Db::checkRequirements exit 0 } diff --git a/src/_binaries/build/doc/doc-main.sh b/src/_binaries/build/doc/doc-main.sh index ea5fb2ea..9bfb7a76 100755 --- a/src/_binaries/build/doc/doc-main.sh +++ b/src/_binaries/build/doc/doc-main.sh @@ -90,7 +90,7 @@ generateDoc() { "${DOC_DIR}/Commands.md" mkdir -p "${DOC_DIR}/src/_binaries/Converters/testsData" || true - cp "${BASH_TOOLS_ROOT_DIR}/src/_binaries/Converters/testsData/mysql2puml-model.png" "${DOC_DIR}/src/_binaries/Converters/testsData" + cp "${BASH_TOOLS_ROOT_DIR}/src/_binaries/Converters/mysql2puml/testsData/mysql2puml-model.png" "${DOC_DIR}/src/_binaries/Converters/testsData" # copy other files cp "${BASH_TOOLS_ROOT_DIR}/README.md" "${DOC_DIR}/README.md" diff --git a/src/_binaries/commandDefinitions/optionsMysqlCollationName.yaml b/src/_binaries/commandDefinitions/optionsMysqlCollationName.yaml index 310bbe0c..4f449028 100644 --- a/src/_binaries/commandDefinitions/optionsMysqlCollationName.yaml +++ b/src/_binaries/commandDefinitions/optionsMysqlCollationName.yaml @@ -12,7 +12,7 @@ binData: group: groupTargetOptions type: String help: Change the collation name used during database creation. - helpValueName: + helpValueName: targetDsn alts: - --collation-name - -o diff --git a/src/_binaries/commandDefinitions/optionsMysqlSource.yaml b/src/_binaries/commandDefinitions/optionsMysqlSource.yaml index a7651717..255d4ab2 100644 --- a/src/_binaries/commandDefinitions/optionsMysqlSource.yaml +++ b/src/_binaries/commandDefinitions/optionsMysqlSource.yaml @@ -20,7 +20,7 @@ binData: group: groupSourceDbOptions type: String help: dsnHelpFunction - helpValueName: + helpValueName: dsn alts: - --from-dsn - -f @@ -34,6 +34,6 @@ binData: The value is the name of the file without s3 location (Only .gz or tar.gz file are supported). This option is incompatible with -f|--from-dsn option. - helpValueName: + helpValueName: awsFile type: String variableName: optionFromAws diff --git a/src/_binaries/commandDefinitions/optionsMysqlTarget.yaml b/src/_binaries/commandDefinitions/optionsMysqlTarget.yaml index 7d898e87..c8313238 100644 --- a/src/_binaries/commandDefinitions/optionsMysqlTarget.yaml +++ b/src/_binaries/commandDefinitions/optionsMysqlTarget.yaml @@ -12,7 +12,7 @@ binData: group: groupTargetOptions type: String help: Dsn to use for target database. - helpValueName: + helpValueName: targetDsn defaultValue: default.local alts: - --target-dsn @@ -21,7 +21,7 @@ binData: group: groupTargetOptions type: String help: Change the character set used during database creation. - helpValueName: + helpValueName: characterSet alts: - --character-set - -c diff --git a/src/_binaries/commandDefinitions/optionsProfile.yaml b/src/_binaries/commandDefinitions/optionsProfile.yaml index cfcdb104..df305689 100644 --- a/src/_binaries/commandDefinitions/optionsProfile.yaml +++ b/src/_binaries/commandDefinitions/optionsProfile.yaml @@ -17,7 +17,7 @@ binData: callbacks: - profileOptionCallback defaultValue: default - helpValueName: + helpValueName: profile alts: - --profile - -p @@ -30,6 +30,6 @@ binData: If aws mode, ignore profile option. callbacks: - optionTablesCallback - helpValueName: + helpValueName: tablesSeparatedByComma alts: - --tables From f2874ddb835465c97d27ac8f351ff6151018dfac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Sat, 31 Aug 2024 22:07:42 +0200 Subject: [PATCH 07/24] compiled dbImportStream using go compiler --- .pre-commit-config-github.yaml | 2 +- .pre-commit-config.yaml | 2 +- bin/dbImport | 8 +- bin/dbImportProfile | 8 +- bin/dbImportStream | 3338 +++++++++-------- bin/doc | 8 +- bin/install | 8 +- bin/installRequirements | 8 +- bin/mysql2puml | 8 +- .../Database/dbImport/dbImport-main.sh | 2 +- .../dbImport}/dumpSizeQuery.sql | 0 .../dbImportStream/binary-dbImportStream.yaml | 46 + .../dbImportStream/dbImportStream-main.sh | 45 + .../dbImportStream/dbImportStream-options.sh | 61 + .../dbImportStream}/dbImportStream.awk | 0 .../dbImportStream}/dbImportStream.bats | 2 +- .../testsData/dbImportStream.help.txt | 94 + .../testsData/dsn/default.local.env | 0 .../testsData/dsn/default.remote.env | 0 .../testsData/dsn/localhost-root.env | 0 .../DbImport/dbImportStream.options.tpl | 77 - src/_binaries/DbImport/dbImportStream.sh | 75 - .../DbImport/dbImportTableRename.awk | 20 - src/_binaries/DbImport/testsData/.env | 52 - .../testsData/auto_default.local_fromDb_20.sh | 8 - .../testsData/auto_default.local_fromDb_70.sh | 8 - .../DbImport/testsData/dbImport.help.txt | 122 - .../testsData/dbImportProfile.tableList1 | 3 - .../testsData/dbImportProfiles/all.sh | 4 - .../testsData/dbImportProfiles/default.sh | 15 - .../testsData/dbImportProfiles/none.sh | 6 - .../testsData/dbImportStream.help.txt | 92 - .../DbImport/testsData/dbImportTableDump.sql | 35 - src/_binaries/DbImport/testsData/dump.sql | 84 - .../DbImport/testsData/dumpMissingSchema.sql | 61 - .../DbImport/testsData/dumpSchema.sql | 64 - .../DbImport/testsData/empty-dump.sql | 1 - .../expectedDbImportProfileTableListQuery.sql | 1 - .../testsData/expectedDbImportTableDump.sql | 42 - .../expectedDbImportTableDumpRenamed.sql | 42 - src/_binaries/DbImport/testsData/gawk | 6 - src/_binaries/DbImport/testsData/pv | 6 - .../DbImport/testsData/tableSizeQuery.sql | 1 - 43 files changed, 2082 insertions(+), 2383 deletions(-) rename src/_binaries/{DbImport => Database/dbImport}/dumpSizeQuery.sql (100%) create mode 100644 src/_binaries/Database/dbImportStream/binary-dbImportStream.yaml create mode 100755 src/_binaries/Database/dbImportStream/dbImportStream-main.sh create mode 100755 src/_binaries/Database/dbImportStream/dbImportStream-options.sh rename src/_binaries/{DbImport => Database/dbImportStream}/dbImportStream.awk (100%) rename src/_binaries/{DbImport => Database/dbImportStream}/dbImportStream.bats (93%) create mode 100644 src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt rename src/_binaries/{DbImport => Database/dbImportStream}/testsData/dsn/default.local.env (100%) rename src/_binaries/{DbImport => Database/dbImportStream}/testsData/dsn/default.remote.env (100%) rename src/_binaries/{DbImport => Database/dbImportStream}/testsData/dsn/localhost-root.env (100%) delete mode 100644 src/_binaries/DbImport/dbImportStream.options.tpl delete mode 100755 src/_binaries/DbImport/dbImportStream.sh delete mode 100644 src/_binaries/DbImport/dbImportTableRename.awk delete mode 100755 src/_binaries/DbImport/testsData/.env delete mode 100755 src/_binaries/DbImport/testsData/auto_default.local_fromDb_20.sh delete mode 100755 src/_binaries/DbImport/testsData/auto_default.local_fromDb_70.sh delete mode 100644 src/_binaries/DbImport/testsData/dbImport.help.txt delete mode 100644 src/_binaries/DbImport/testsData/dbImportProfile.tableList1 delete mode 100755 src/_binaries/DbImport/testsData/dbImportProfiles/all.sh delete mode 100755 src/_binaries/DbImport/testsData/dbImportProfiles/default.sh delete mode 100755 src/_binaries/DbImport/testsData/dbImportProfiles/none.sh delete mode 100644 src/_binaries/DbImport/testsData/dbImportStream.help.txt delete mode 100644 src/_binaries/DbImport/testsData/dbImportTableDump.sql delete mode 100644 src/_binaries/DbImport/testsData/dump.sql delete mode 100644 src/_binaries/DbImport/testsData/dumpMissingSchema.sql delete mode 100644 src/_binaries/DbImport/testsData/dumpSchema.sql delete mode 100644 src/_binaries/DbImport/testsData/empty-dump.sql delete mode 100644 src/_binaries/DbImport/testsData/expectedDbImportProfileTableListQuery.sql delete mode 100644 src/_binaries/DbImport/testsData/expectedDbImportTableDump.sql delete mode 100644 src/_binaries/DbImport/testsData/expectedDbImportTableDumpRenamed.sql delete mode 100755 src/_binaries/DbImport/testsData/gawk delete mode 100755 src/_binaries/DbImport/testsData/pv delete mode 100644 src/_binaries/DbImport/testsData/tableSizeQuery.sql diff --git a/.pre-commit-config-github.yaml b/.pre-commit-config-github.yaml index 0b065dce..84b5c69e 100644 --- a/.pre-commit-config-github.yaml +++ b/.pre-commit-config-github.yaml @@ -205,6 +205,6 @@ repos: stages: [manual] # GITHUB - repo: https://github.com/fchastanet/bash-compiler - rev: 0.3.1 + rev: 0.3.2 hooks: - id: buildBashBinaries diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2de7aa40..76771297 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -200,6 +200,6 @@ repos: stages: [] # GITHUB - repo: https://github.com/fchastanet/bash-compiler - rev: 0.3.1 + rev: 0.3.2 hooks: - id: buildBashBinaries diff --git a/bin/dbImport b/bin/dbImport index e0425b9b..34e5a8fa 100755 --- a/bin/dbImport +++ b/bin/dbImport @@ -840,7 +840,7 @@ Env::pathPrepend() { # @stderr diagnostics information is displayed # shellcheck disable=SC2120 Env::requireLoad() { - REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 + export REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 local -a defaultFiles=("$@") # get list of possible config files @@ -938,7 +938,7 @@ Linux::requireExecutedAsUser() { # @exitcode 1 if realpath command not available # @stderr diagnostics information is displayed Linux::requireRealpathCommand() { - REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED=1 + export REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED=1 Assert::commandExists realpath } @@ -1149,7 +1149,7 @@ Log::logWarning() { # @exitcode 0 always successful # @stderr diagnostics information about log file is displayed Log::requireLoad() { - REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + export REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then @@ -1239,7 +1239,7 @@ UI::drawLine() { # @env LOAD_THEME int 0 to avoid loading theme # @exitcode 0 always successful UI::requireTheme() { - REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + export REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 if [[ "${LOAD_THEME:-1}" = "1" ]]; then UI::theme "${BASH_FRAMEWORK_THEME-default}" diff --git a/bin/dbImportProfile b/bin/dbImportProfile index dafd0bca..3977c09c 100755 --- a/bin/dbImportProfile +++ b/bin/dbImportProfile @@ -781,7 +781,7 @@ Env::pathPrepend() { # @stderr diagnostics information is displayed # shellcheck disable=SC2120 Env::requireLoad() { - REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 + export REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 local -a defaultFiles=("$@") # get list of possible config files @@ -851,7 +851,7 @@ Linux::requireExecutedAsUser() { # @exitcode 1 if realpath command not available # @stderr diagnostics information is displayed Linux::requireRealpathCommand() { - REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED=1 + export REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED=1 Assert::commandExists realpath } @@ -1062,7 +1062,7 @@ Log::logWarning() { # @exitcode 0 always successful # @stderr diagnostics information about log file is displayed Log::requireLoad() { - REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + export REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then @@ -1152,7 +1152,7 @@ UI::drawLine() { # @env LOAD_THEME int 0 to avoid loading theme # @exitcode 0 always successful UI::requireTheme() { - REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + export REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 if [[ "${LOAD_THEME:-1}" = "1" ]]; then UI::theme "${BASH_FRAMEWORK_THEME-default}" diff --git a/bin/dbImportStream b/bin/dbImportStream index c86019d9..61577f2e 100755 --- a/bin/dbImportStream +++ b/bin/dbImportStream @@ -1,13 +1,12 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FACADE FROM https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/DbImport/dbImportStream.sh +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImportStream/binary-dbImportStream.yaml # DO NOT EDIT IT # @generated ############################################################################### # shellcheck disable=SC2288,SC2034 -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/dbImportStream -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE + + # ensure that no user aliases could interfere with # commands used in this script @@ -62,13 +61,6 @@ interruptManagement() { kill -s INT "$$" } trap interruptManagement INT -SCRIPT_NAME=${0##*/} -REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" -if [[ -n "${EMBED_CURRENT_DIR}" ]]; then - CURRENT_DIR="${EMBED_CURRENT_DIR}" -else - CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" -fi ################################################ # Temp dir management @@ -102,6 +94,20 @@ cleanOnExit() { } trap cleanOnExit EXIT HUP QUIT ABRT TERM + +SCRIPT_NAME=${0##*/} +REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" +if [[ -n "${EMBED_CURRENT_DIR}" ]]; then + CURRENT_DIR="${EMBED_CURRENT_DIR}" +else + CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" +fi +FRAMEWORK_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" +FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" +FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + # @description Log namespace provides 2 kind of functions # - Log::display* allows to display given message with # given display level @@ -132,294 +138,313 @@ export __VERBOSE_LEVEL_DEBUG=2 # @description verbose level info export __VERBOSE_LEVEL_TRACE=3 -# @description Display message using info color (bg light blue/fg white) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayInfo() { - local type="${2:-INFO}" - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logInfo "$1" "${type}" -} - -# @description Display message using debug color (gray) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayDebug() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then - Log::computeDuration - echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logDebug "$1" -} -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logWarning "$1" +# @description check if an element is contained in an array +# +# @arg $1 needle:String +# @arg $@ array:String[] +# @exitcode 0 if found +# @exitcode 1 otherwise +# @example +# Array::contains "${libPath}" "${__BASH_FRAMEWORK_IMPORTED_FILES[@]}" +Array::contains() { + local element + for element in "${@:2}"; do + [[ "${element}" = "$1" ]] && return 0 + done + return 1 } -# @description Display message using error color (red) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayError() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then - Log::computeDuration - echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logError "$1" -} -# @description load colors theme constants -# @warning if tty not opened, noColor theme will be chosen -# @arg $1 theme:String the theme to use (default, noColor) -# @arg $@ args:String[] -# @set __ERROR_COLOR String indicate error status -# @set __INFO_COLOR String indicate info status -# @set __SUCCESS_COLOR String indicate success status -# @set __WARNING_COLOR String indicate warning status -# @set __SKIPPED_COLOR String indicate skipped status -# @set __DEBUG_COLOR String indicate debug status -# @set __HELP_COLOR String indicate help status -# @set __TEST_COLOR String not used -# @set __TEST_ERROR_COLOR String not used -# @set __HELP_TITLE_COLOR String used to display help title in help strings -# @set __HELP_OPTION_COLOR String used to display highlight options in help strings -# -# @set __RESET_COLOR String reset default color +# @description concatenate each element of an array with a separator +# but wrapping text when line length is more than provided argument +# The algorithm will try not to cut the array element if it can. +# - if an arg can be placed on current line it will be, +# otherwise current line is printed and arg is added to the new +# current line +# - Empty arg is interpreted as a new line. +# - Add \r to arg in order to force break line and avoid following +# arg to be concatenated with current arg. # -# @set __HELP_EXAMPLE String to remove -# @set __HELP_TITLE String to remove -# @set __HELP_NORMAL String to remove -# shellcheck disable=SC2034 -UI::theme() { - local theme="${1-default}" - if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then - theme="noColor" +# @arg $1 glue:String +# @arg $2 maxLineLength:int +# @arg $3 indentNextLine:int +# @arg $@ array:String[] +Array::wrap2() { + local glue="${1-}" + local -i glueLength="${#glue}" + shift || true + local -i maxLineLength=$1 + shift || true + local -i indentNextLine=$1 + shift || true + local indentStr="" + if ((indentNextLine > 0)); then + indentStr="$(head -c "${indentNextLine}" &2 - Log::logFatal "$1" - exit 1 -} + ( + local currentLine + local -i currentLineLength=0 isNewline=1 argLength=0 + local -a additionalLines + local -i previousLineEmpty=0 + local arg="" -# @description create a temp file using default TMPDIR variable -# initialized in _includes/_commonHeader.sh -# @env TMPDIR String (default value /tmp) -# @arg $1 templateName:String template name to use(optional) -Framework::createTempFile() { - mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" -} + while (($# > 0)); do + arg="$1" + shift || true -# @description ensure env files are loaded -# @arg $@ list of default files to load at the end -# @exitcode 1 if one of env files fails to load -# @stderr diagnostics information is displayed -# shellcheck disable=SC2120 -Env::requireLoad() { - local -a defaultFiles=("$@") - # get list of possible config files - local -a configFiles=() - if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then - # BASH_FRAMEWORK_ENV_FILES is an array - configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") - fi - local localFrameworkConfigFile - localFrameworkConfigFile="$(pwd)/.framework-config" - if [[ -f "${localFrameworkConfigFile}" ]]; then - configFiles+=("${localFrameworkConfigFile}") - fi - if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then - configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") - fi - configFiles+=("${optionEnvFiles[@]}") - configFiles+=("${defaultFiles[@]}") - - for file in "${configFiles[@]}"; do - # shellcheck source=/.framework-config - CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { - Log::displayError "while loading config file: ${file}" - return 1 - } - done -} + # replace tab by 2 spaces + arg="${arg//$'\t'/ }" + # remove trailing spaces + arg="${arg%[[:blank:]]}" + if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then + printCurrentLine + ((previousLineEmpty = 1)) + continue + else + if ((previousLineEmpty == 1)); then + printCurrentLine + fi + ((previousLineEmpty = 0)) || true + fi + # convert eol to args + mapfile -t additionalLines <<<"${arg}" + if ((${#additionalLines[@]} > 1)); then + set -- "${additionalLines[@]}" "$@" + continue + fi -# @description activate or not Log::display* and Log::log* functions -# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL -# environment variables loaded by Env::requireLoad -# try to create log file and rotate it if necessary -# @noargs -# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable -# @env BASH_FRAMEWORK_DISPLAY_LEVEL int -# @env BASH_FRAMEWORK_LOG_LEVEL int -# @env BASH_FRAMEWORK_LOG_FILE String -# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 -# @exitcode 0 always successful -# @stderr diagnostics information about log file is displayed -# @require Env::requireLoad -# @require UI::requireTheme -Log::requireLoad() { - if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - export BASH_FRAMEWORK_LOG_LEVEL - fi + ((argLength = ${#arg})) || true - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then - if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 + # empty arg + if ((argLength == 0)); then + if ((isNewline == 0)); then + # isNewline = 0 means currentLine is not empty + printCurrentLine fi - elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + continue fi - elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - fi - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - # will always be created even if not in info level - Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" - if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then - Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + if ((isNewline == 0)); then + glueLength="${#glue}" + else + glueLength="0" + fi + if ((currentLineLength + argLength + glueLength > maxLineLength)); then + if ((argLength + glueLength > maxLineLength)); then + # arg is too long to even fit on one line + # we have to split the arg on current and next line + local -i remainingLineLength + ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) + appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" + printCurrentLine + arg="${arg:${remainingLineLength}}" + # remove leading spaces + arg="${arg##[[:blank:]]}" + + set -- "${arg}" "$@" + else + # the arg can fit on next line + printCurrentLine + appendToCurrentLine "${arg}" "${argLength}" + fi + else + appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" + fi + done + if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then + printCurrentLine fi - fi + ) | sed -E -e 's/[[:blank:]]+$//' } -# @description list the conf files list available in bash-tools/conf/ folder -# and those overridden in ${HOME}/.bash-tools/ folder + +# @description check if command specified exists or return 1 +# with error and message if not # -# @arg $1 confFolder:String the directory name (not the path) to list -# @arg $2 extension:String the extension (.sh by default) -# @arg $3 indentStr:String the indentation (' - ' by default) can be any string compatible with sed not containing any / +# @arg $1 commandName:String on which existence must be checked +# @arg $2 helpIfNotExists:String a help command to display if the command does not exist # -# @stdout list of files without extension/directory -# @example text -# - default.local -# - default.remote -# - localhost-root -Conf::getMergedList() { - local confFolder="$1" - local extension="${2-sh}" - local indentStr="${3- - }" +# @exitcode 1 if the command specified does not exist +# @stderr diagnostic information + help if second argument is provided +Assert::commandExists() { + local commandName="$1" + local helpIfNotExists="$2" - local DEFAULT_CONF_DIR="${FRAMEWORK_ROOT_DIR}/conf/${confFolder}" - local HOME_CONF_DIR="${HOME}/.bash-tools/${confFolder}" + "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { + Log::displayError "${commandName} is not installed, please install it" + if [[ -n "${helpIfNotExists}" ]]; then + Log::displayInfo "${helpIfNotExists}" + fi + return 1 + } + return 0 +} - ( - if [[ -d "${DEFAULT_CONF_DIR}" ]]; then - Conf::list "${DEFAULT_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" + +# @description check if tty (interactive mode) is active +# @noargs +# @exitcode 1 if tty not active +# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive +# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive +Assert::tty() { + if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then + return 1 + fi + if [[ "${INTERACTIVE:-0}" = "1" ]]; then + return 0 + fi + tty -s +} + + +# @description ignore exit code 141 from simple command pipes +# @example use with: +# local resultingStatus=0 +# local -a originalPipeStatus=() +# cmd1 | cmd2 || Bash::handlePipelineFailure resultingStatus originalPipeStatus || true +# [[ "${resultingStatus}" = "0" ]] +# @arg $1 resultingStatusCode:&int (passed by reference) (optional) resulting status code +# @arg $2 originalStatus:int[] (passed by reference) (optional) copy of original PIPESTATUS array +# @env PIPESTATUS assuming that this function is called like in the example provided +# @see https://unix.stackexchange.com/a/709880/582856 +Bash::handlePipelineFailure() { + local -a pipeStatusBackup=("${PIPESTATUS[@]}") + local -n handlePipelineFailure_resultingStatusCode=$1 + local -n handlePipelineFailure_originalStatus=$2 + # shellcheck disable=SC2034 + handlePipelineFailure_originalStatus=("${pipeStatusBackup[@]}") + handlePipelineFailure_resultingStatusCode=0 + local statusCode + for statusCode in "${pipeStatusBackup[@]}"; do + if ((statusCode == 141)); then + return 0 + elif ((statusCode > 0)); then + # shellcheck disable=SC2034 + handlePipelineFailure_resultingStatusCode="${statusCode}" + break fi - if [[ -d "${HOME_CONF_DIR}" ]]; then - Conf::list "${HOME_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" + done + return "${handlePipelineFailure_resultingStatusCode}" +} + + +# @description loads ~/.bash-tools/.env if available +# if not creates it from a default template +# else check if new options need to be added +BashTools::Conf::requireLoad() { +Linux::requireTarCommand +Compiler::Embed::extractFileFromBase64 \ + "${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" \ + "IyEvdXNyL2Jpbi9lbnYgYmFzaAojIHNoZWxsY2hlY2sgZGlzYWJsZT1TQzIwMzQKCiMgRGVmYXVsdCBzZXR0aW5ncwojIHlvdSBjYW4gb3ZlcnJpZGUgdGhlc2Ugc2V0dGluZ3MgYnkgY3JlYXRpbmcgJHtIT01FfS8uYmFzaC10b29scy8uZW52IGZpbGUKCiMjIwojIyMgRElTUExBWSBMZXZlbAojIyMgbWluaW11bSBsZXZlbCBvZiB0aGUgbWVzc2FnZXMgdGhhdCB3aWxsIGJlIGRpc3BsYXllZCBvbiBzY3JlZW4KIyMjCiMjIyAwOiBOTyBMT0cKIyMjIDE6IEVSUk9SCiMjIyAyOiBXQVJOSU5HCiMjIyAzOiBJTkZPCiMjIyA0OiBERUJVRwojIyMKQkFTSF9GUkFNRVdPUktfRElTUExBWV9MRVZFTD0ke0JBU0hfRlJBTUVXT1JLX0RJU1BMQVlfTEVWRUw6LTN9CgojIyMKIyMjIERJU1BMQVkgZHVyYXRpb24KIyMjIDA6IG5vIGR1cmF0aW9uIGlzIGRpc3BsYXllZCBvbiB0aGUgbWVzc2FnZXMKIyMjIDE6IGR1cmF0aW9uIGJldHdlZW4gcHJldmlvdXMgbWVzc2FnZSBhbmQgY3VycmVudCBpcyBkaXNwbGF5ZWQKIyMjIHdpdGggdGhlIG1lc3NhZ2UKIyMjCkRJU1BMQVlfRFVSQVRJT049JHtESVNQTEFZX0RVUkFUSU9OOjB9CgojIyMKIyMjIExvZyB0byBmaWxlCiMjIwojIyMgYWxsIGxvZyBtZXNzYWdlcyB3aWxsIGJlIHJlZGlyZWN0ZWQgdG8gbG9nIGZpbGUgc3BlY2lmaWVkCiMjIyB0aGlzIHNhbWUgcGF0aCB3aWxsIGJlIHVzZWQgaW5zaWRlIGFuZCBvdXRzaWRlIG9mIHRoZSBjb250YWluZXIKIyMjCkJBU0hfRlJBTUVXT1JLX0xPR19GSUxFPSR7QkFTSF9GUkFNRVdPUktfTE9HX0ZJTEU6LSR7RlJBTUVXT1JLX1JPT1RfRElSfS9sb2dzL2Jhc2gubG9nfQoKIyMjCiMjIyBMT0cgTGV2ZWwKIyMjIG1pbmltdW0gbGV2ZWwgb2YgdGhlIG1lc3NhZ2VzIHRoYXQgd2lsbCBiZSBsb2dnZWQgaW50byBMT0dfRklMRQojIyMKIyMjIDA6IE5PIExPRwojIyMgMTogRVJST1IKIyMjIDI6IFdBUk5JTkcKIyMjIDM6IElORk8KIyMjIDQ6IERFQlVHCiMjIwpCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw9JHtCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw6LTB9CgojIGFic29sdXRlIGRpcmVjdG9yeSBjb250YWluaW5nIGRiIGltcG9ydCBzcWwgZHVtcHMKREJfSU1QT1JUX0RVTVBfRElSPSR7REJfSU1QT1JUX0RVTVBfRElSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2RiSW1wb3J0RHVtcHN9CgojIGdhcmJhZ2UgY29sbGVjdCBhbGwgZmlsZXMgZm9yIHdoaWNoIG1vZGlmaWNhdGlvbiBpcyBncmVhdGVyIHRoYW4gZWc6IDMwIGRheXMgKCszMCkKIyBlYWNoIHRpbWUgYW4gZXhpc3RpbmcgZmlsZSBpcyB1c2VkIGJ5IGRiSW1wb3J0L2RiSW1wb3J0VGFibGUKIyB0aGUgZmlsZSBtb2RpZmljYXRpb24gdGltZSBpcyBzZXQgdG8gbm93CkRCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUz0ke0RCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUzotKzMwfQoKIyBhYnNvbHV0ZSBkaXJlY3RvcnkgY29udGFpbmluZyBkYlNjcmlwdHMgdXNlZCBieSBkYlNjcmlwdEFsbERhdGFiYXNlcwpTQ1JJUFRTX0ZPTERFUj0ke1NDUklQVFNfRk9MREVSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2NvbmYvZGJTY3JpcHRzfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEFXUyBQYXJhbWV0ZXJzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KUzNfQkFTRV9VUkw9JHtTM19CQVNFX1VSTDotfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvc3RtYW4gUGFyYW1ldGVycwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClBPU1RNQU5fQVBJX0tFWT0K" \ + "755" + +declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" + + BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" + if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then + FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" + else + # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR + FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" + fi + # shellcheck disable=SC2034 + FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" + # shellcheck disable=SC2034 + FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + + if [[ -f "${HOME}/.bash-tools/.env" ]]; then + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") + fi + + local envFile="${HOME}/.bash-tools/.env" + if [[ ! -f "${envFile}" ]]; then + mkdir -p "${HOME}/.bash-tools" + ( + echo "#!/usr/bin/env bash" + # shellcheck disable=SC2154 + echo "${embed_file_bashToolsDefaultConfigTemplate}" + ) >"${envFile}" + Log::displayInfo "Configuration file '${envFile}' created" + else + if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then + ( + echo '# -----------------------------------------------------' + echo '# Postman Parameters' + echo '# -----------------------------------------------------' + echo 'POSTMAN_API_KEY=' + ) >>"${envFile}" fi - ) | sort | uniq + fi + # shellcheck source=/conf/.env + source "${envFile}" || { + Log::displayError "impossible to load '${envFile}'" + exit 1 + } } -# @description check if an element is contained in an array + +# @description convert base64 encoded back to target file +# if target file is executable prepend dir of target +# file to PATH to make binary available everywhere +# it is advised to include in the path of the target file +# the md5sum of the binFile # -# @arg $1 needle:String -# @arg $@ array:String[] -# @exitcode 0 if found -# @exitcode 1 otherwise -# @example -# Array::contains "${libPath}" "${__BASH_FRAMEWORK_IMPORTED_FILES[@]}" -Array::contains() { - local element - for element in "${@:2}"; do - [[ "${element}" = "$1" ]] && return 0 - done - return 1 +# @arg $1 targetFile:String the file to write +# @arg $2 binFileBase64:String the base64 encoded file +# @arg $3 fileMode:String the chmod to set on the file +# @set PATH String prepend target embedded file binary directory to PATH variable if binary executable +Compiler::Embed::extractFileFromBase64() { + local targetFile="$1" + local binFileBase64="$2" + local fileMode="${3:-+x}" + local targetDir="${targetFile%/*}" + + if [[ ! -f "${targetFile}" ]]; then + if [[ ! -d "${targetDir}" ]]; then + mkdir -p "${targetDir}" + fi + base64 -d >"${targetFile}" <<<"${binFileBase64}" + chmod "${fileMode}" "${targetFile}" + fi + + if [[ -x "${targetFile}" ]]; then + Env::pathPrepend "${targetDir}" + fi } + # @description get absolute conf file from specified conf folder deduced using these rules # * from absolute file (ignores and ) # * relative to where script is executed (ignores and ) @@ -481,237 +506,126 @@ Conf::getAbsoluteFile() { return 1 } -# @description mysql query on a given db -# @warning could use QUERY_OPTIONS variable from dsn if defined -# @example -# cat file.sql | Database::query ... -# @arg $1 instanceQuery:&Map (passed by reference) database instance to use -# @arg $2 sqlQuery:String (optional) sql query or sql file to execute. if not provided or empty, the command can be piped -# @arg $3 dbName:String (optional) the db name -# -# @exitcode mysql command status code -Database::query() { - local -n instanceQuery=$1 - local -a mysqlCommand=() - local -a queryOptions - - mysqlCommand+=(mysql) - mysqlCommand+=("--defaults-extra-file=${instanceQuery['AUTH_FILE']}") - IFS=' ' read -r -a queryOptions <<<"${instanceQuery['QUERY_OPTIONS']}" - mysqlCommand+=("${queryOptions[@]}") - if [[ "${instanceQuery['SKIP_COLUMN_NAMES']}" = "1" ]]; then - mysqlCommand+=("-s" "--skip-column-names") - fi - # add optional db name - if [[ -n "${3+x}" ]]; then - mysqlCommand+=("$3") - fi - # add optional sql query - if [[ -n "${2+x}" && -n "$2" && ! -f "$2" ]]; then - mysqlCommand+=("-e") - mysqlCommand+=("$2") - fi - Log::displayDebug "$(printf "execute command: '%s'" "${mysqlCommand[*]}")" - - if [[ -f "$2" ]]; then - "${mysqlCommand[@]}" <"$2" - else - "${mysqlCommand[@]}" - fi -} -# @description concatenate each element of an array with a separator -# but wrapping text when line length is more than provided argument -# The algorithm will try not to cut the array element if it can. -# - if an arg can be placed on current line it will be, -# otherwise current line is printed and arg is added to the new -# current line -# - Empty arg is interpreted as a new line. -# - Add \r to arg in order to force break line and avoid following -# arg to be concatenated with current arg. +# @description list the conf files list available in bash-tools/conf/ folder +# and those overridden in ${HOME}/.bash-tools/ folder # -# @arg $1 glue:String -# @arg $2 maxLineLength:int -# @arg $3 indentNextLine:int -# @arg $@ array:String[] -Array::wrap2() { - local glue="${1-}" - local -i glueLength="${#glue}" - shift || true - local -i maxLineLength=$1 - shift || true - local -i indentNextLine=$1 - shift || true - local indentStr="" - if ((indentNextLine > 0)); then - indentStr="$(head -c "${indentNextLine}" 0)); do - arg="$1" - shift || true - - # replace tab by 2 spaces - arg="${arg//$'\t'/ }" - # remove trailing spaces - arg="${arg%[[:blank:]]}" - if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then - printCurrentLine - ((previousLineEmpty = 1)) - continue - else - if ((previousLineEmpty == 1)); then - printCurrentLine - fi - ((previousLineEmpty = 0)) || true - fi - # convert eol to args - mapfile -t additionalLines <<<"${arg}" - if ((${#additionalLines[@]} > 1)); then - set -- "${additionalLines[@]}" "$@" - continue - fi - - ((argLength = ${#arg})) || true - - # empty arg - if ((argLength == 0)); then - if ((isNewline == 0)); then - # isNewline = 0 means currentLine is not empty - printCurrentLine - fi - continue - fi - - if ((isNewline == 0)); then - glueLength="${#glue}" - else - glueLength="0" - fi - if ((currentLineLength + argLength + glueLength > maxLineLength)); then - if ((argLength + glueLength > maxLineLength)); then - # arg is too long to even fit on one line - # we have to split the arg on current and next line - local -i remainingLineLength - ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) - appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" - printCurrentLine - arg="${arg:${remainingLineLength}}" - # remove leading spaces - arg="${arg##[[:blank:]]}" - - set -- "${arg}" "$@" - else - # the arg can fit on next line - printCurrentLine - appendToCurrentLine "${arg}" "${argLength}" - fi - else - appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" - fi - done - if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then - printCurrentLine + if [[ -d "${DEFAULT_CONF_DIR}" ]]; then + Conf::list "${DEFAULT_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" fi - ) | sed -E -e 's/[[:blank:]]+$//' + if [[ -d "${HOME_CONF_DIR}" ]]; then + Conf::list "${HOME_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" + fi + ) | sort | uniq } -# @description check if command specified exists or return 1 -# with error and message if not -# -# @arg $1 commandName:String on which existence must be checked -# @arg $2 helpIfNotExists:String a help command to display if the command does not exist + +# @description list files of dir with given extension and display it as a list one by line # -# @exitcode 1 if the command specified does not exist -# @stderr diagnostic information + help if second argument is provided -Assert::commandExists() { - local commandName="$1" - local helpIfNotExists="$2" +# @arg $1 dir:String the directory to list +# @arg $2 prefix:String the profile file prefix (default: "") +# @arg $3 ext:String the extension +# @arg $4 findOptions:String find options, eg: -type d (Default value: '-type f') +# @arg $5 indentStr:String the indentation can be any string compatible with sed not containing any / (Default value: ' - ') +# @stdout list of files without extension/directory +# @example text +# - default.local +# - default.remote +# - localhost-root +# @exitcode 1 if directory does not exists +Conf::list() { + local dir="$1" + local prefix="${2:-}" + local ext="${3}" + local findOptions="${4--type f}" + local indentStr="${5- - }" - "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { - Log::displayError "${commandName} is not installed, please install it" - if [[ -n "${helpIfNotExists}" ]]; then - Log::displayInfo "${helpIfNotExists}" - fi - return 1 - } - return 0 + if [[ ! -d "${dir}" ]]; then + Log::displayError "Directory ${dir} does not exist" + fi + if [[ -n "${ext}" && "${ext:0:1}" != "." ]]; then + ext=".${ext}" + fi + ( + # shellcheck disable=SC2086 + cd "${dir}" && + find . -maxdepth 1 ${findOptions} -name "${prefix}*${ext}" | + sed -E "s#^\./${prefix}##g" | + sed -E "s#${ext}\$##g" | sort | sed -E "s#^#${indentStr}#" + ) } -# @description Check that command version is greater than expected minimal version -# display warning if command version greater than expected minimal version -# display error if command version less than expected minimal version and exit 1 -# @arg $1 commandName:String command path -# @arg $2 argVersion:String command line parameters to launch to get command version -# @arg $3 minimalVersion:String expected minimal command version -# @arg $4 parseVersionCallback:Function -# @arg $5 help:String optional help message to display if command does not exist -# @exitcode 0 if command version greater or equal to expected minimal version -# @exitcode 1 if command version less than expected minimal version -# @exitcode 2 if command does not exist -Version::checkMinimal() { - local commandName="$1" - local argVersion="$2" - local minimalVersion="$3" - local parseVersionCallback=${4:-Version::parse} - local help="${5:-}" - - Assert::commandExists "${commandName}" "${help}" || return 2 - - # shellcheck disable=SC2034 - local status=0 - # shellcheck disable=SC2034 - local -a pipeStatus=() - local version - version="$("${commandName}" "${argVersion}" 2>&1 | ${parseVersionCallback} || Bash::handlePipelineFailure status pipeStatus)" - Log::displayDebug "check ${commandName} version ${version} against minimal ${minimalVersion}" +# @description check if dsn file has all the mandatory variables set +# Mandatory variables are: HOSTNAME, USER, PASSWORD, PORT +# +# @arg $1 dsnFileName:String dsn absolute filename +# @set HOSTNAME loaded from dsn file +# @set PORT loaded from dsn file +# @set USER loaded from dsn file +# @set PASSWORD loaded from dsn file +# @exitcode 0 on valid file +# @exitcode 1 if one of the properties of the conf file is invalid or if file not found +# @stderr log output if error found in conf file +Database::checkDsnFile() { + local dsnFileName="$1" + if [[ ! -f "${dsnFileName}" ]]; then + Log::displayError "dsn file ${dsnFileName} not found" + return 1 + fi - Version::compare "${version}" "${minimalVersion}" || { - local result=$? - if [[ "${result}" = "1" ]]; then - Log::displayInfo "${commandName} version is ${version} greater than ${minimalVersion}" - elif [[ "${result}" = "2" ]]; then - Log::displayError "${commandName} minimal version is ${minimalVersion}, your version is ${version}" + ( + unset HOSTNAME PORT PASSWORD USER + # shellcheck source=/src/Database/testsData/dsn_valid.env + source "${dsnFileName}" + if [[ -z ${HOSTNAME+x} ]]; then + Log::displayError "dsn file ${dsnFileName} : HOSTNAME not provided" return 1 fi - return 0 - } - + if [[ -z "${HOSTNAME}" ]]; then + Log::displayWarning "dsn file ${dsnFileName} : HOSTNAME value not provided" + fi + if [[ "${HOSTNAME}" = "localhost" ]]; then + Log::displayWarning "dsn file ${dsnFileName} : check that HOSTNAME should not be 127.0.0.1 instead of localhost" + fi + if [[ -z "${PORT+x}" ]]; then + Log::displayError "dsn file ${dsnFileName} : PORT not provided" + return 1 + fi + if ! [[ ${PORT} =~ ^[0-9]+$ ]]; then + Log::displayError "dsn file ${dsnFileName} : PORT invalid" + return 1 + fi + if [[ -z "${USER+x}" ]]; then + Log::displayError "dsn file ${dsnFileName} : USER not provided" + return 1 + fi + if [[ -z "${PASSWORD+x}" ]]; then + Log::displayError "dsn file ${dsnFileName} : PASSWORD not provided" + return 1 + fi + ) } + # @description create a new db instance # Returns immediately if the instance is already initialized # @@ -764,12 +678,53 @@ Database::newInstance() { instanceNewInstance['SKIP_COLUMN_NAMES']="${SKIP_COLUMN_NAMES:-1}" instanceNewInstance['SSL_OPTIONS']="${MYSQL_SSL_OPTIONS:---ssl-mode=DISABLED}" instanceNewInstance['QUERY_OPTIONS']="${MYSQL_QUERY_OPTIONS:---batch --raw --default-character-set=utf8}" - instanceNewInstance['DUMP_OPTIONS']="${MYSQL_DUMP_OPTIONS:---default-character-set=utf8 --compress --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 ${instanceNewInstance['SSL_OPTIONS']}}" + instanceNewInstance['DUMP_OPTIONS']="${MYSQL_DUMP_OPTIONS:---default-character-set=utf8 --compression-algorithms --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 ${instanceNewInstance['SSL_OPTIONS']}}" instanceNewInstance['DB_IMPORT_OPTIONS']="${DB_IMPORT_OPTIONS:---connect-timeout=5 --batch --raw --default-character-set=utf8}" instanceNewInstance['INITIALIZED']=1 } + +# @description mysql query on a given db +# @warning could use QUERY_OPTIONS variable from dsn if defined +# @example +# cat file.sql | Database::query ... +# @arg $1 instanceQuery:&Map (passed by reference) database instance to use +# @arg $2 sqlQuery:String (optional) sql query or sql file to execute. if not provided or empty, the command can be piped +# @arg $3 dbName:String (optional) the db name +# +# @exitcode mysql command status code +Database::query() { + local -n instanceQuery=$1 + local -a mysqlCommand=() + local -a queryOptions + + mysqlCommand+=(mysql) + mysqlCommand+=("--defaults-extra-file=${instanceQuery['AUTH_FILE']}") + IFS=' ' read -r -a queryOptions <<<"${instanceQuery['QUERY_OPTIONS']}" + mysqlCommand+=("${queryOptions[@]}") + if [[ "${instanceQuery['SKIP_COLUMN_NAMES']}" = "1" ]]; then + mysqlCommand+=("-s" "--skip-column-names") + fi + # add optional db name + if [[ -n "${3+x}" ]]; then + mysqlCommand+=("$3") + fi + # add optional sql query + if [[ -n "${2+x}" && -n "$2" && ! -f "$2" ]]; then + mysqlCommand+=("-e") + mysqlCommand+=("$2") + fi + Log::displayDebug "$(printf "execute command: '%s'" "${mysqlCommand[*]}")" + + if [[ -f "$2" ]]; then + "${mysqlCommand[@]}" <"$2" + else + "${mysqlCommand[@]}" + fi +} + + # @description set the general options to use on mysql command to query the database # Differs than setOptions in the way that these options could change each time # @@ -781,145 +736,101 @@ Database::setQueryOptions() { instanceSetQueryOptions['QUERY_OPTIONS']="$2" } -# @description ignore exit code 141 from simple command pipes -# @example use with: -# local resultingStatus=0 -# local -a originalPipeStatus=() -# cmd1 | cmd2 || Bash::handlePipelineFailure resultingStatus originalPipeStatus || true -# [[ "${resultingStatus}" = "0" ]] -# @arg $1 resultingStatusCode:&int (passed by reference) (optional) resulting status code -# @arg $2 originalStatus:int[] (passed by reference) (optional) copy of original PIPESTATUS array -# @env PIPESTATUS assuming that this function is called like in the example provided -# @see https://unix.stackexchange.com/a/709880/582856 -Bash::handlePipelineFailure() { - local -a pipeStatusBackup=("${PIPESTATUS[@]}") - local -n handlePipelineFailure_resultingStatusCode=$1 - local -n handlePipelineFailure_originalStatus=$2 - # shellcheck disable=SC2034 - handlePipelineFailure_originalStatus=("${pipeStatusBackup[@]}") - handlePipelineFailure_resultingStatusCode=0 - local statusCode - for statusCode in "${pipeStatusBackup[@]}"; do - if ((statusCode == 141)); then - return 0 - elif ((statusCode > 0)); then - # shellcheck disable=SC2034 - handlePipelineFailure_resultingStatusCode="${statusCode}" - break + +# @description check if all requirements are satisfied +# to execute dbImport commands +Db::checkRequirements() { + if [[ "${SKIP_REQUIREMENTS_CHECKS:-0}" = "1" ]]; then + return 0 + fi + local -i failures=0 + echo + Assert::commandExists mysql "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqldump "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists pv "sudo apt-get install -y pv" || ((++failures)) + Assert::commandExists gawk "sudo apt-get install -y gawk" || ((++failures)) + Assert::commandExists awk "sudo apt-get install -y gawk" || ((++failures)) + Version::checkMinimal "gawk" "--version" "5.0.1" || ((++failures)) + return "${failures}" +} + + +# @description prepend directories to the PATH environment variable +# @arg $@ args:String[] list of directories to prepend +# @set PATH update PATH with the directories prepended +Env::pathPrepend() { + local arg + for arg in "$@"; do + if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then + PATH="$(realpath "${arg}"):${PATH}" fi done - return "${handlePipelineFailure_resultingStatusCode}" } -bashToolsDefaultConfigTemplate="${bashToolsDefaultConfigTemplate:-$( - cat <<'EOF' -# shellcheck disable=SC2034 -# Default settings -# you can override these settings by creating ${HOME}/.bash-tools/.env file - -### -### DISPLAY Level -### minimum level of the messages that will be displayed on screen -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} - -### -### DISPLAY duration -### 0: no duration is displayed on the messages -### 1: duration between previous message and current is displayed -### with the message -### -DISPLAY_DURATION=${DISPLAY_DURATION:0} - -### -### Log to file -### -### all log messages will be redirected to log file specified -### this same path will be used inside and outside of the container -### -BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} - -### -### LOG Level -### minimum level of the messages that will be logged into LOG_FILE -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} - -# absolute directory containing db import sql dumps -DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} - -# garbage collect all files for which modification is greater than eg: 30 days (+30) -# each time an existing file is used by dbImport/dbImportTable -# the file modification time is set to now -DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} - -# absolute directory containing dbScripts used by dbScriptAllDatabases -SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} - -# ----------------------------------------------------- -# AWS Parameters -# ----------------------------------------------------- -S3_BASE_URL=${S3_BASE_URL:-} - -# ----------------------------------------------------- -# Postman Parameters -# ----------------------------------------------------- -POSTMAN_API_KEY= -EOF -)}" +# @description ensure env files are loaded +# @arg $@ list of default files to load at the end +# @exitcode 1 if one of env files fails to load +# @stderr diagnostics information is displayed +# shellcheck disable=SC2120 +Env::requireLoad() { + export REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 -# @description loads ~/.bash-tools/.env if available -# if not creates it from a default template -# else check if new options need to be added -BashTools::Conf::requireLoad() { - local envFile="${HOME}/.bash-tools/.env" - if [[ ! -f "${envFile}" ]]; then - mkdir -p "${HOME}/.bash-tools" - ( - echo "#!/usr/bin/env bash" - echo "${bashToolsDefaultConfigTemplate}" - ) >"${envFile}" - Log::displayInfo "Configuration file '${envFile}' created" - else - if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then - ( - echo '# -----------------------------------------------------' - echo '# Postman Parameters' - echo '# -----------------------------------------------------' - echo 'POSTMAN_API_KEY=' - ) >>"${envFile}" - fi + local -a defaultFiles=("$@") + # get list of possible config files + local -a configFiles=() + if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then + # BASH_FRAMEWORK_ENV_FILES is an array + configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") fi - # shellcheck source=/conf/.env - source "${envFile}" || { - Log::displayError "impossible to load '${envFile}'" + local localFrameworkConfigFile + localFrameworkConfigFile="$(pwd)/.framework-config" + if [[ -f "${localFrameworkConfigFile}" ]]; then + configFiles+=("${localFrameworkConfigFile}") + fi + if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then + configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") + fi + configFiles+=("${optionEnvFiles[@]}") + configFiles+=("${defaultFiles[@]}") + + for file in "${configFiles[@]}"; do + # shellcheck source=/.framework-config + CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { + Log::displayError "while loading config file: ${file}" + return 1 + } + done +} + + +# @description concatenate 2 paths and ensure the path is correct using realpath -m +# @arg $1 basePath:String +# @arg $2 subPath:String +File::concatenatePath() { + + if [[ "${REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Linux::requireRealpathCommand has not been loaded" exit 1 - } + fi + + local basePath="$1" + local subPath="$2" + local fullPath="${basePath:+${basePath}/}${subPath}" + + realpath -m "${fullPath}" 2>/dev/null } -# @description ensure COMMAND_BIN_DIR env var is set -# and PATH correctly prepared -# @noargs -# @set COMMAND_BIN_DIR string the directory where to find this command -# @set PATH string add directory where to find this command binary -Compiler::Facade::requireCommandBinDir() { - COMMAND_BIN_DIR="${CURRENT_DIR}" - Env::pathPrepend "${COMMAND_BIN_DIR}" + +# @description create a temp file using default TMPDIR variable +# @env TMPDIR String (default value /tmp) +# @arg $1 templateName:String template name to use(optional) +Framework::createTempFile() { + mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" } + # @description ensure running user is not root # @exitcode 1 if current user is root # @stderr diagnostics information is displayed @@ -929,6 +840,25 @@ Linux::requireExecutedAsUser() { fi } + +# @description ensure command realpath is available +# @exitcode 1 if realpath command not available +# @stderr diagnostics information is displayed +Linux::requireRealpathCommand() { + export REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED=1 + + Assert::commandExists realpath +} + + +# @description ensure command tar is available +# @exitcode 1 if tar command not available +# @stderr diagnostics information is displayed +Linux::requireTarCommand() { + Assert::commandExists tar +} + + declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" @@ -967,30 +897,81 @@ Log::computeDuration() { fi } -# @description log message to file + +# @description Display message using debug color (gray) # @arg $1 message:String the message to display -Log::logInfo() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then - Log::logMessage "${2:-INFO}" "$1" +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayDebug() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then + Log::computeDuration + echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logDebug "$1" } -# @description log message to file + +# @description Display message using error color (red) # @arg $1 message:String the message to display -Log::logDebug() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then - Log::logMessage "${2:-DEBUG}" "$1" +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayError() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then + Log::computeDuration + echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logError "$1" +} + + +# @description Display message using info color (bg light blue/fg white) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayInfo() { + local type="${2:-INFO}" + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logInfo "$1" "${type}" +} + + +# @description Display message using warning color (yellow) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayWarning() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then + Log::computeDuration + echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logWarning "$1" +} + + +# @description Display message using error color (red) and exit immediately with error status 1 +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::fatal() { + Log::computeDuration + echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + Log::logFatal "$1" + exit 1 } + # @description log message to file # @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" +Log::logDebug() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then + Log::logMessage "${2:-DEBUG}" "$1" fi } + # @description log message to file # @arg $1 message:String the message to display Log::logError() { @@ -999,20 +980,6 @@ Log::logError() { fi } -# @description check if tty (interactive mode) is active -# @noargs -# @exitcode 1 if tty not active -# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive -# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive -Assert::tty() { - if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then - return 1 - fi - if [[ "${INTERACTIVE:-0}" = "1" ]]; then - return 0 - fi - tty -s -} # @description log message to file # @arg $1 message:String the message to display @@ -1020,6 +987,16 @@ Log::logFatal() { Log::logMessage "${2:-FATAL}" "$1" } + +# @description log message to file +# @arg $1 message:String the message to display +Log::logInfo() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then + Log::logMessage "${2:-INFO}" "$1" + fi +} + + # @description Internal: common log message # @example text # [date]|[levelMsg]|message @@ -1033,9 +1010,18 @@ Log::logFatal() { # @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty # @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages # @stderr diagnostics information is displayed -# @require Env::requireLoad -# @require Log::requireLoad Log::logMessage() { + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Log::requireLoad has not been loaded" + exit 1 + fi + local levelMsg="$1" local msg="$2" local date @@ -1047,90 +1033,251 @@ Log::logMessage() { fi } -# @description To be called before logging in the log file -# @arg $1 file:string log file name -# @arg $2 maxLogFilesCount:int maximum number of log files -Log::rotate() { - local file="$1" - local maxLogFilesCount="${2:-5}" - if [[ ! -f "${file}" ]]; then - Log::displayDebug "Log file ${file} doesn't exist yet" - return 0 +# @description log message to file +# @arg $1 message:String the message to display +Log::logWarning() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then + Log::logMessage "${2:-WARNING}" "$1" + fi +} + + +# @description activate or not Log::display* and Log::log* functions +# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL +# environment variables loaded by Env::requireLoad +# try to create log file and rotate it if necessary +# @noargs +# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable +# @env BASH_FRAMEWORK_DISPLAY_LEVEL int +# @env BASH_FRAMEWORK_LOG_LEVEL int +# @env BASH_FRAMEWORK_LOG_FILE String +# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 +# @exitcode 0 always successful +# @stderr diagnostics information about log file is displayed +Log::requireLoad() { + export REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement UI::requireTheme has not been loaded" + exit 1 + fi + + if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + export BASH_FRAMEWORK_LOG_LEVEL + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then + if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 + fi + elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + # will always be created even if not in info level + Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" + if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then + Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + fi + fi +} + + +# @description To be called before logging in the log file +# @arg $1 file:string log file name +# @arg $2 maxLogFilesCount:int maximum number of log files +Log::rotate() { + local file="$1" + local maxLogFilesCount="${2:-5}" + + if [[ ! -f "${file}" ]]; then + Log::displayDebug "Log file ${file} doesn't exist yet" + return 0 + fi + local i + for ((i = maxLogFilesCount - 1; i > 0; i--)); do + Log::displayInfo "Log rotation ${file}.${i} to ${file}.$((i + 1))" + mv "${file}."{"${i}","$((i + 1))"} &>/dev/null || true + done + if cp "${file}" "${file}.1" &>/dev/null; then + echo >"${file}" # reset log file + Log::displayInfo "Log rotation ${file} to ${file}.1" + fi +} + + +# @description draw a line with the character passed in parameter repeated depending on terminal width +# @arg $1 character:String character to use as separator (default value #) +UI::drawLine() { + local character="${1:-#}" + local -i width=${COLUMNS:-0} + if ((width == 0)) && [[ -t 1 ]]; then + width=$(tput cols) + fi + if ((width == 0)); then + width=80 + fi + printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") + echo +} + + +# @description load color theme +# @noargs +# @env BASH_FRAMEWORK_THEME String theme to use +# @env LOAD_THEME int 0 to avoid loading theme +# @exitcode 0 always successful +UI::requireTheme() { + export REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + + if [[ "${LOAD_THEME:-1}" = "1" ]]; then + UI::theme "${BASH_FRAMEWORK_THEME-default}" + fi +} + + +# @description load colors theme constants +# @warning if tty not opened, noColor theme will be chosen +# @arg $1 theme:String the theme to use (default, noColor) +# @arg $@ args:String[] +# @set __ERROR_COLOR String indicate error status +# @set __INFO_COLOR String indicate info status +# @set __SUCCESS_COLOR String indicate success status +# @set __WARNING_COLOR String indicate warning status +# @set __SKIPPED_COLOR String indicate skipped status +# @set __DEBUG_COLOR String indicate debug status +# @set __HELP_COLOR String indicate help status +# @set __TEST_COLOR String not used +# @set __TEST_ERROR_COLOR String not used +# @set __HELP_TITLE_COLOR String used to display help title in help strings +# @set __HELP_OPTION_COLOR String used to display highlight options in help strings +# +# @set __RESET_COLOR String reset default color +# +# @set __HELP_EXAMPLE String to remove +# @set __HELP_TITLE String to remove +# @set __HELP_NORMAL String to remove +# shellcheck disable=SC2034 +UI::theme() { + local theme="${1-default}" + if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then + theme="noColor" fi - local i - for ((i = maxLogFilesCount - 1; i > 0; i--)); do - Log::displayInfo "Log rotation ${file}.${i} to ${file}.$((i + 1))" - mv "${file}."{"${i}","$((i + 1))"} &>/dev/null || true - done - if cp "${file}" "${file}.1" &>/dev/null; then - echo >"${file}" # reset log file - Log::displayInfo "Log rotation ${file} to ${file}.1" + case "${theme}" in + default | default-force) + theme="default" + ;; + noColor) ;; + *) + Log::fatal "invalid theme provided" + ;; + esac + if [[ "${theme}" = "default" ]]; then + BASH_FRAMEWORK_THEME="default" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='\e[31m' # Red + __INFO_COLOR='\e[44m' # white on lightBlue + __SUCCESS_COLOR='\e[32m' # Green + __WARNING_COLOR='\e[33m' # Yellow + __SKIPPED_COLOR='\e[33m' # Yellow + __DEBUG_COLOR='\e[37m' # Gray + __HELP_COLOR='\e[7;49;33m' # Black on Gold + __TEST_COLOR='\e[100m' # Light magenta + __TEST_ERROR_COLOR='\e[41m' # white on red + __HELP_TITLE_COLOR="\e[1;37m" # Bold + __HELP_OPTION_COLOR="\e[1;34m" # Blue + # Internal: reset color + __RESET_COLOR='\e[0m' # Reset Color + # shellcheck disable=SC2155,SC2034 + __HELP_EXAMPLE="$(echo -e "\e[2;97m")" + # shellcheck disable=SC2155,SC2034 + __HELP_TITLE="$(echo -e "\e[1;37m")" + # shellcheck disable=SC2155,SC2034 + __HELP_NORMAL="$(echo -e "\033[0m")" + else + BASH_FRAMEWORK_THEME="noColor" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='' + __INFO_COLOR='' + __SUCCESS_COLOR='' + __WARNING_COLOR='' + __SKIPPED_COLOR='' + __DEBUG_COLOR='' + __HELP_COLOR='' + __TEST_COLOR='' + __TEST_ERROR_COLOR='' + __HELP_TITLE_COLOR='' + __HELP_OPTION_COLOR='' + # Internal: reset color + __RESET_COLOR='' + __HELP_EXAMPLE='' + __HELP_TITLE='' + __HELP_NORMAL='' fi } -# @description list files of dir with given extension and display it as a list one by line -# -# @arg $1 dir:String the directory to list -# @arg $2 prefix:String the profile file prefix (default: "") -# @arg $3 ext:String the extension -# @arg $4 findOptions:String find options, eg: -type d (Default value: '-type f') -# @arg $5 indentStr:String the indentation can be any string compatible with sed not containing any / (Default value: ' - ') -# @stdout list of files without extension/directory -# @example text -# - default.local -# - default.remote -# - localhost-root -# @exitcode 1 if directory does not exists -Conf::list() { - local dir="$1" - local prefix="${2:-}" - local ext="${3}" - local findOptions="${4--type f}" - local indentStr="${5- - }" - if [[ ! -d "${dir}" ]]; then - Log::displayError "Directory ${dir} does not exist" - fi - if [[ -n "${ext}" && "${ext:0:1}" != "." ]]; then - ext=".${ext}" - fi - ( - # shellcheck disable=SC2086 - cd "${dir}" && - find . -maxdepth 1 ${findOptions} -name "${prefix}*${ext}" | - sed -E "s#^\./${prefix}##g" | - sed -E "s#${ext}\$##g" | sort | sed -E "s#^#${indentStr}#" - ) -} +# @description Check that command version is greater than expected minimal version +# display warning if command version greater than expected minimal version +# display error if command version less than expected minimal version and exit 1 +# @arg $1 commandName:String command path +# @arg $2 argVersion:String command line parameters to launch to get command version +# @arg $3 minimalVersion:String expected minimal command version +# @arg $4 parseVersionCallback:Function +# @arg $5 help:String optional help message to display if command does not exist +# @exitcode 0 if command version greater or equal to expected minimal version +# @exitcode 1 if command version less than expected minimal version +# @exitcode 2 if command does not exist +Version::checkMinimal() { + local commandName="$1" + local argVersion="$2" + local minimalVersion="$3" + local parseVersionCallback=${4:-Version::parse} + local help="${5:-}" -# @description concatenate 2 paths and ensure the path is correct using realpath -m -# @arg $1 basePath:String -# @arg $2 subPath:String -# @require Linux::requireRealpathCommand -File::concatenatePath() { - local basePath="$1" - local subPath="$2" - local fullPath="${basePath:+${basePath}/}${subPath}" + Assert::commandExists "${commandName}" "${help}" || return 2 - realpath -m "${fullPath}" 2>/dev/null -} + # shellcheck disable=SC2034 + local status=0 + # shellcheck disable=SC2034 + local -a pipeStatus=() + local version + version="$("${commandName}" "${argVersion}" 2>&1 | ${parseVersionCallback} || Bash::handlePipelineFailure status pipeStatus)" + + Log::displayDebug "check ${commandName} version ${version} against minimal ${minimalVersion}" + + Version::compare "${version}" "${minimalVersion}" || { + local result=$? + if [[ "${result}" = "1" ]]; then + Log::displayInfo "${commandName} version is ${version} greater than ${minimalVersion}" + elif [[ "${result}" = "2" ]]; then + Log::displayError "${commandName} minimal version is ${minimalVersion}, your version is ${version}" + return 1 + fi + return 0 + } -# @description filter to keep only version number from a string -# @arg $@ files:String[] the files to filter -# @exitcode * if one of the filter command fails -# @stdin you can use stdin as alternative to files argument -# @stdout the filtered content -# shellcheck disable=SC2120 -Version::parse() { - # match anything, print(p), exit on first match(Q) - sed -En \ - -e 's/\x1b\[[0-9;]*[mGKHF]//g' \ - -e 's/[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/' \ - -e '//{p;Q}' \ - "$@" } + # @description compare 2 version numbers # @arg $1 version1:String version 1 # @arg $2 version2:String version 2 @@ -1153,144 +1300,42 @@ Version::compare() { # fill empty fields in ver2 with zeros ver2[i]=0 fi - if ((10#${ver1[i]} > 10#${ver2[i]})); then - return 1 - fi - if ((10#${ver1[i]} < 10#${ver2[i]})); then - return 2 - fi - done - return 0 -} - -# @description check if dsn file has all the mandatory variables set -# Mandatory variables are: HOSTNAME, USER, PASSWORD, PORT -# -# @arg $1 dsnFileName:String dsn absolute filename -# @set HOSTNAME loaded from dsn file -# @set PORT loaded from dsn file -# @set USER loaded from dsn file -# @set PASSWORD loaded from dsn file -# @exitcode 0 on valid file -# @exitcode 1 if one of the properties of the conf file is invalid or if file not found -# @stderr log output if error found in conf file -Database::checkDsnFile() { - local dsnFileName="$1" - if [[ ! -f "${dsnFileName}" ]]; then - Log::displayError "dsn file ${dsnFileName} not found" - return 1 - fi - - ( - unset HOSTNAME PORT PASSWORD USER - # shellcheck source=/src/Database/testsData/dsn_valid.env - source "${dsnFileName}" - if [[ -z ${HOSTNAME+x} ]]; then - Log::displayError "dsn file ${dsnFileName} : HOSTNAME not provided" - return 1 - fi - if [[ -z "${HOSTNAME}" ]]; then - Log::displayWarning "dsn file ${dsnFileName} : HOSTNAME value not provided" - fi - if [[ "${HOSTNAME}" = "localhost" ]]; then - Log::displayWarning "dsn file ${dsnFileName} : check that HOSTNAME should not be 127.0.0.1 instead of localhost" - fi - if [[ -z "${PORT+x}" ]]; then - Log::displayError "dsn file ${dsnFileName} : PORT not provided" - return 1 - fi - if ! [[ ${PORT} =~ ^[0-9]+$ ]]; then - Log::displayError "dsn file ${dsnFileName} : PORT invalid" - return 1 - fi - if [[ -z "${USER+x}" ]]; then - Log::displayError "dsn file ${dsnFileName} : USER not provided" - return 1 - fi - if [[ -z "${PASSWORD+x}" ]]; then - Log::displayError "dsn file ${dsnFileName} : PASSWORD not provided" - return 1 - fi - ) -} - -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" - fi + if ((10#${ver1[i]} > 10#${ver2[i]})); then + return 1 + fi + if ((10#${ver1[i]} < 10#${ver2[i]})); then + return 2 + fi done + return 0 } -# @description load color theme -# @noargs -# @env BASH_FRAMEWORK_THEME String theme to use -# @env LOAD_THEME int 0 to avoid loading theme -# @exitcode 0 always successful -UI::requireTheme() { - if [[ "${LOAD_THEME:-1}" = "1" ]]; then - UI::theme "${BASH_FRAMEWORK_THEME-default}" - fi -} -# @description ensure command realpath is available -# @exitcode 1 if realpath command not available -# @stderr diagnostics information is displayed -Linux::requireRealpathCommand() { - Assert::commandExists realpath +# @description filter to keep only version number from a string +# @arg $@ files:String[] the files to filter +# @exitcode * if one of the filter command fails +# @stdin you can use stdin as alternative to files argument +# @stdout the filtered content +# shellcheck disable=SC2120 +Version::parse() { + # match anything, print(p), exit on first match(Q) + sed -En \ + -e 's/\x1b\[[0-9;]*[mGKHF]//g' \ + -e 's/[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/' \ + -e '//{p;Q}' \ + "$@" } - # FUNCTIONS -facade_main_dbImportStreamsh() { -BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" -if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then - FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" -else - # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR - FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" -fi -FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" -FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" - -# @require BashTools::Conf::requireLoad -if [[ -f "${HOME}/.bash-tools/.env" ]]; then - export BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") -fi -# REQUIRES -Env::requireLoad -UI::requireTheme -Log::requireLoad -Linux::requireRealpathCommand -BashTools::Conf::requireLoad -Compiler::Facade::requireCommandBinDir -Linux::requireExecutedAsUser - -# @require Compiler::Facade::requireCommandBinDir -# shellcheck disable=SC2034 - -# default values -declare optionProfile="" -declare argTargetDbName="" -declare argDumpFile="" -declare optionTargetDsn="" -declare optionCharacterSet="" -declare defaultTargetCharacterSet="" -declare profileCommandFile="" - -# other configuration -declare copyrightBeginYear="2020" -declare PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbImportProfiles" -declare HOME_PROFILES_DIR="${HOME}/.bash-tools/dbImportProfiles" declare -a BASH_FRAMEWORK_ARGV_FILTERED=() +beforeParseCallback() { + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + copyrightCallback() { if [[ -z "${copyrightBeginYear}" ]]; then copyrightBeginYear="$(date +%Y)" @@ -1327,13 +1372,14 @@ updateArgListQuietCallback() { :; } # shellcheck disable=SC2317 # if function is overridden optionHelpCallback() { - dbImportStreamCommand help + Log::displayError "optionHelpCallback needs to be overridden" exit 0 } # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { - echo "${SCRIPT_NAME} version 2.0" + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version ${versionNumber}" exit 0 } @@ -1351,21 +1397,22 @@ optionEnvFileCallback() { optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_INFO} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionDebugVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_DEBUG} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionTraceVerboseCallback() { + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vvv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_TRACE} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } getLevel() { @@ -1389,6 +1436,7 @@ getLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -1410,6 +1458,7 @@ getVerboseLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -1420,7 +1469,7 @@ optionDisplayLevelCallback() { logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1429,19 +1478,20 @@ optionLogLevelCallback() { local logLevel verboseLevel logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionLogFileCallback() { local logFile="$2" - echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionQuietCallback() { - echo "BASH_FRAMEWORK_QUIET_MODE=1" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_QUIET_MODE=1" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1472,6 +1522,7 @@ optionBashFrameworkConfigCallback() { defaultFrameworkConfig="$( cat <<'EOF' + # copied from src/_includes/.framework-config.default # shellcheck disable=SC2034 @@ -1489,7 +1540,7 @@ NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-conf # describe the files that are allowed to not have an associated bats file BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" # describe the files that are allowed to not have a function matching the filename -FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|\.tpl$|/testsData/|^manualTests/|\.bats$}" +FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|/testsData/|^manualTests/|\.bats$}" # Source directories if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then FRAMEWORK_SRC_DIRS=( @@ -1505,6 +1556,7 @@ BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" + EOF )" @@ -1513,7 +1565,7 @@ overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" commandOptionParseFinished() { # load default template framework config defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" - echo "${defaultFrameworkConfig}" > "${defaultEnvFile}" + echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") if [[ -f "${envFile}" ]]; then files+=("${envFile}") @@ -1531,30 +1583,99 @@ commandOptionParseFinished() { fi } -# default values -declare optionProfile="default" -declare optionTables="" -declare profileCommandFile="" -profileOptionHelpCallback() { - echo "the name of the profile to use in order to include or exclude tables" - echo "(if not specified in default.sh from 'User profiles directory' if exists or 'Default profiles directory')" + +declare PROFILES_DIR +declare HOME_PROFILES_DIR +# shellcheck disable=SC2034 +declare versionNumber="2.0" +# shellcheck disable=SC2034 +declare copyrightBeginYear="2020" +# shellcheck disable=SC2034 +declare defaultFromDsn="default.remote" + +beforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad + Linux::requireExecutedAsUser + Linux::requireRealpathCommand } -optionTablesCallback() { - if [[ ! ${optionTables} =~ ^[A-Za-z0-9_]+(,[A-Za-z0-9_]+)*$ ]]; then - Log::fatal "Command ${SCRIPT_NAME} - Table list is not valid : ${optionTables}" +initConf() { + # shellcheck disable=SC2034 + PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbImportProfiles" + HOME_PROFILES_DIR="${HOME}/.bash-tools/dbImportProfiles" + Db::checkRequirements +} + +optionHelpCallback() { + dbImportStreamCommandHelp + exit 0 +} + +longDescriptionFunction() { + local profilesList="" + local dsnList="" + dsnList="$(Conf::getMergedList "dsn" "env")" + profilesList="$(Conf::getMergedList "dbImportProfiles" "sh" || true)" + + echo -e "${__HELP_TITLE}Default profiles directory:${__HELP_NORMAL}" + echo -e "${PROFILES_DIR-configuration error}" + echo + echo -e "${__HELP_TITLE}User profiles directory:${__HELP_NORMAL}" + echo -e "${HOME_PROFILES_DIR-configuration error}" + echo -e "Allows to override profiles defined in 'Default profiles directory'" + echo + echo -e "${__HELP_TITLE}List of available profiles:${__HELP_NORMAL}" + echo -e "${profilesList}" + echo + echo -e "${__HELP_TITLE}List of available dsn:${__HELP_NORMAL}" + echo -e "${dsnList}" +} + +dbImportStreamCommandCallback() { + if [[ -z "${argTargetDbName}" ]]; then + Log::fatal "you must provide argTargetDbName" + fi + # shellcheck disable=SC2154 + if [[ ! -f "${argDumpFile}" ]]; then + Log::fatal "invalid argDumpFile provided - file does not exist" fi } -profileOptionCallback() { - local -a profilesArray - readarray -t profilesArray < <(Conf::getMergedList "dbImportProfiles" "sh" "" || true) - if ! Array::contains "$2" "${profilesArray[@]}"; then - Log::displayError "${SCRIPT_NAME} - invalid profile '$2' provided" - return 1 + + +# shellcheck disable=SC2034 +declare defaultTargetCollationName="utf8_general_ci" +declare defaultTargetCharacterSet="utf8" + +initializeDefaultTargetMysqlOptions() { + local -n dbFromInstanceTargetMysql=$1 + local fromDbName="$2" + + # get remote db collation name + if [[ -n ${optionCollationName+x} && -z "${optionCollationName}" ]]; then + optionCollationName=$(Database::query dbFromInstanceTargetMysql \ + "SELECT default_collation_name FROM information_schema.SCHEMATA WHERE schema_name = \"${fromDbName}\";" "information_schema") + fi + + # get remote db character set + if [[ -z "${optionCharacterSet}" ]]; then + optionCharacterSet=$(Database::query dbFromInstanceTargetMysql \ + "SELECT default_character_set_name FROM information_schema.SCHEMATA WHERE schema_name = \"${fromDbName}\";" "information_schema") fi } + + + +profileOptionHelpFunction() { + Array::wrap2 " " 80 4 \ + " The name of the profile to use in order to include or exclude tables." + echo +} + initProfileCommandCallback() { if [[ "${optionProfile}" != "default" && -n "${optionTables}" ]]; then Log::fatal "Command ${SCRIPT_NAME} - you cannot use table and profile options at the same time" @@ -1583,708 +1704,865 @@ initProfileCommandCallback() { Log::displayInfo "${profileMsgInfo}" } +profileOptionCallback() { + local -a profilesArray + readarray -t profilesArray < <(Conf::getMergedList "dbImportProfiles" "sh" "" || true) + if ! Array::contains "$2" "${profilesArray[@]}"; then + Log::displayError "${SCRIPT_NAME} - invalid profile '$2' provided" + return 1 + fi +} + +optionTablesCallback() { + if [[ ! ${optionTables} =~ ^[A-Za-z0-9_]+(,[A-Za-z0-9_]+)*$ ]]; then + Log::fatal "Command ${SCRIPT_NAME} - Table list is not valid : ${optionTables}" + fi +} + + + +optionVersionCallback() { + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version ${versionNumber}" + Db::checkRequirements + exit 0 +} + + + +beforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + + +# ------------------------------------------ +# Command dbImportStreamCommand +# ------------------------------------------ + +# options variables initialization +declare optionProfile="default" +declare optionTables="" declare optionTargetDsn="default.local" declare optionCharacterSet="" -declare defaultTargetCharacterSet="utf8" +declare optionHelp="0" +declare optionConfig="0" +declare optionBashFrameworkConfig="" +declare optionInfoVerbose="0" +declare optionDebugVerbose="0" +declare optionTraceVerbose="0" +declare -a optionEnvFiles=() +declare optionLogLevel="" +declare optionLogFile="" +declare optionDisplayLevel="" +declare optionNoColor="0" +declare optionTheme="default" +declare optionVersion="0" +declare optionQuiet="0" +# arguments variables initialization +declare argDumpFile="" +declare argTargetDbName="" +# @description parse command options and arguments for dbImportStreamCommand +dbImportStreamCommandParse() { + Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" + Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" + optionProfile="default" + local -i options_parse_optionParsedCountOptionProfile + ((options_parse_optionParsedCountOptionProfile = 0)) || true + optionTables="" + local -i options_parse_optionParsedCountOptionTables + ((options_parse_optionParsedCountOptionTables = 0)) || true + optionTargetDsn="default.local" + local -i options_parse_optionParsedCountOptionTargetDsn + ((options_parse_optionParsedCountOptionTargetDsn = 0)) || true + optionCharacterSet="" + local -i options_parse_optionParsedCountOptionCharacterSet + ((options_parse_optionParsedCountOptionCharacterSet = 0)) || true + optionHelp="0" + local -i options_parse_optionParsedCountOptionHelp + ((options_parse_optionParsedCountOptionHelp = 0)) || true + optionConfig="0" + local -i options_parse_optionParsedCountOptionConfig + ((options_parse_optionParsedCountOptionConfig = 0)) || true + optionBashFrameworkConfig="" + local -i options_parse_optionParsedCountOptionBashFrameworkConfig + ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true + optionInfoVerbose="0" + local -i options_parse_optionParsedCountOptionInfoVerbose + ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true + optionDebugVerbose="0" + local -i options_parse_optionParsedCountOptionDebugVerbose + ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true + optionTraceVerbose="0" + local -i options_parse_optionParsedCountOptionTraceVerbose + ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true + + optionLogLevel="" + local -i options_parse_optionParsedCountOptionLogLevel + ((options_parse_optionParsedCountOptionLogLevel = 0)) || true + optionLogFile="" + local -i options_parse_optionParsedCountOptionLogFile + ((options_parse_optionParsedCountOptionLogFile = 0)) || true + optionDisplayLevel="" + local -i options_parse_optionParsedCountOptionDisplayLevel + ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true + optionNoColor="0" + local -i options_parse_optionParsedCountOptionNoColor + ((options_parse_optionParsedCountOptionNoColor = 0)) || true + optionTheme="default" + local -i options_parse_optionParsedCountOptionTheme + ((options_parse_optionParsedCountOptionTheme = 0)) || true + optionVersion="0" + local -i options_parse_optionParsedCountOptionVersion + ((options_parse_optionParsedCountOptionVersion = 0)) || true + optionQuiet="0" + local -i options_parse_optionParsedCountOptionQuiet + ((options_parse_optionParsedCountOptionQuiet = 0)) || true + + argDumpFile="" + local -i options_parse_argParsedCountArgDumpFile + ((options_parse_argParsedCountArgDumpFile = 0)) || true + + argTargetDbName="" + local -i options_parse_argParsedCountArgTargetDbName + ((options_parse_argParsedCountArgTargetDbName = 0)) || true -initializeDefaultTargetMysqlOptions() { - local -n dbFromInstanceTargetMysql=$1 - local fromDbName="$2" - # get remote db collation name - if [[ -n ${optionCollationName+x} && -z "${optionCollationName}" ]]; then - optionCollationName=$(Database::query dbFromInstanceTargetMysql \ - "SELECT default_collation_name FROM information_schema.SCHEMATA WHERE schema_name = \"${fromDbName}\";" "information_schema") - fi + # shellcheck disable=SC2034 + local -i options_parse_parsedArgIndex=0 + while (($# > 0)); do + local options_parse_arg="$1" + local argOptDefaultBehavior=0 + case "${options_parse_arg}" in + # Option 1/18 + # optionProfile alts --profile|-p + # type: String min 0 max 1 + --profile | -p) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi - # get remote db character set - if [[ -z "${optionCharacterSet}" ]]; then - optionCharacterSet=$(Database::query dbFromInstanceTargetMysql \ - "SELECT default_character_set_name FROM information_schema.SCHEMATA WHERE schema_name = \"${fromDbName}\";" "information_schema") - fi -} + if ((options_parse_optionParsedCountOptionProfile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionProfile)) + # shellcheck disable=SC2034 + optionProfile="$1" + profileOptionCallback "${options_parse_arg}" "${optionProfile}" + + ;; + + # Option 2/18 + # optionTables alts --tables + # type: String min 0 max 1 + --tables) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi -dbImportStreamCommand() { - local options_parse_cmd="$1" - shift || true + if ((options_parse_optionParsedCountOptionTables >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTables)) + # shellcheck disable=SC2034 + optionTables="$1" + optionTablesCallback "${options_parse_arg}" "${optionTables}" + + ;; + + # Option 3/18 + # optionTargetDsn alts --target-dsn|-t + # type: String min 0 max 1 + --target-dsn | -t) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi - if [[ "${options_parse_cmd}" = "parse" ]]; then - local -i options_parse_optionParsedCountOptionProfile - ((options_parse_optionParsedCountOptionProfile = 0)) || true - local -i options_parse_optionParsedCountOptionTables - ((options_parse_optionParsedCountOptionTables = 0)) || true - local -i options_parse_optionParsedCountOptionTargetDsn - ((options_parse_optionParsedCountOptionTargetDsn = 0)) || true - local -i options_parse_optionParsedCountOptionCharacterSet - ((options_parse_optionParsedCountOptionCharacterSet = 0)) || true - local -i options_parse_optionParsedCountOptionBashFrameworkConfig - ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true - optionConfig="0" - local -i options_parse_optionParsedCountOptionConfig - ((options_parse_optionParsedCountOptionConfig = 0)) || true - optionInfoVerbose="0" - local -i options_parse_optionParsedCountOptionInfoVerbose - ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true - optionDebugVerbose="0" - local -i options_parse_optionParsedCountOptionDebugVerbose - ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true - optionTraceVerbose="0" - local -i options_parse_optionParsedCountOptionTraceVerbose - ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionNoColor="0" - local -i options_parse_optionParsedCountOptionNoColor - ((options_parse_optionParsedCountOptionNoColor = 0)) || true - optionTheme="default" - local -i options_parse_optionParsedCountOptionTheme - ((options_parse_optionParsedCountOptionTheme = 0)) || true - optionHelp="0" - local -i options_parse_optionParsedCountOptionHelp - ((options_parse_optionParsedCountOptionHelp = 0)) || true - optionVersion="0" - local -i options_parse_optionParsedCountOptionVersion - ((options_parse_optionParsedCountOptionVersion = 0)) || true - optionQuiet="0" - local -i options_parse_optionParsedCountOptionQuiet - ((options_parse_optionParsedCountOptionQuiet = 0)) || true - local -i options_parse_optionParsedCountOptionLogLevel - ((options_parse_optionParsedCountOptionLogLevel = 0)) || true - local -i options_parse_optionParsedCountOptionLogFile - ((options_parse_optionParsedCountOptionLogFile = 0)) || true - local -i options_parse_optionParsedCountOptionDisplayLevel - ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true - local -i options_parse_argParsedCountArgDumpFile - ((options_parse_argParsedCountArgDumpFile = 0)) || true - local -i options_parse_argParsedCountArgTargetDbName - ((options_parse_argParsedCountArgTargetDbName = 0)) || true - # shellcheck disable=SC2034 - local -i options_parse_parsedArgIndex=0 - while (($# > 0)); do - local options_parse_arg="$1" - local argOptDefaultBehavior=0 - case "${options_parse_arg}" in - # Option 1/18 - # Option optionProfile --profile|-p variableType String min 0 max 1 authorizedValues '' regexp '' - --profile | -p) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionProfile >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionProfile)) - # shellcheck disable=SC2034 - optionProfile="$1" - profileOptionCallback "${options_parse_arg}" "${optionProfile}" - ;; - # Option 2/18 - # Option optionTables --tables variableType String min 0 max 1 authorizedValues '' regexp '' - --tables) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionTables >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTables)) - # shellcheck disable=SC2034 - optionTables="$1" - optionTablesCallback "${options_parse_arg}" "${optionTables}" - ;; - # Option 3/18 - # Option optionTargetDsn --target-dsn|-t variableType String min 0 max 1 authorizedValues '' regexp '' - --target-dsn | -t) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionTargetDsn >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTargetDsn)) - # shellcheck disable=SC2034 - optionTargetDsn="$1" - ;; - # Option 4/18 - # Option optionCharacterSet --character-set|-c variableType String min 0 max 1 authorizedValues '' regexp '' - --character-set | -c) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionCharacterSet >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionCharacterSet)) - # shellcheck disable=SC2034 - optionCharacterSet="$1" - ;; - # Option 5/18 - # Option optionBashFrameworkConfig --bash-framework-config variableType String min 0 max 1 authorizedValues '' regexp '' - --bash-framework-config) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) - # shellcheck disable=SC2034 - optionBashFrameworkConfig="$1" - optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" - ;; - # Option 6/18 - # Option optionConfig --config variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --config) - # shellcheck disable=SC2034 - optionConfig="1" - if ((options_parse_optionParsedCountOptionConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionConfig)) - ;; - # Option 7/18 - # Option optionInfoVerbose --verbose|-v variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --verbose | -v) - # shellcheck disable=SC2034 - optionInfoVerbose="1" - if ((options_parse_optionParsedCountOptionInfoVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionInfoVerbose)) - optionInfoVerboseCallback "${options_parse_arg}" - updateArgListInfoVerboseCallback "${options_parse_arg}" - ;; - # Option 8/18 - # Option optionDebugVerbose -vv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vv) - # shellcheck disable=SC2034 - optionDebugVerbose="1" - if ((options_parse_optionParsedCountOptionDebugVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDebugVerbose)) - optionDebugVerboseCallback "${options_parse_arg}" - updateArgListDebugVerboseCallback "${options_parse_arg}" - ;; - # Option 9/18 - # Option optionTraceVerbose -vvv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vvv) - # shellcheck disable=SC2034 - optionTraceVerbose="1" - if ((options_parse_optionParsedCountOptionTraceVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTraceVerbose)) - optionTraceVerboseCallback "${options_parse_arg}" - updateArgListTraceVerboseCallback "${options_parse_arg}" - ;; - # Option 10/18 - # Option optionEnvFiles --env-file variableType StringArray min 0 max -1 authorizedValues '' regexp '' - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - ;; - # Option 11/18 - # Option optionNoColor --no-color variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --no-color) - # shellcheck disable=SC2034 - optionNoColor="1" - if ((options_parse_optionParsedCountOptionNoColor >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionNoColor)) - optionNoColorCallback "${options_parse_arg}" - updateArgListNoColorCallback "${options_parse_arg}" - ;; - # Option 12/18 - # Option optionTheme --theme variableType String min 0 max 1 authorizedValues 'default|default-force|noColor' regexp '' - --theme) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default|default-force|noColor)" - return 1 - fi - if ((options_parse_optionParsedCountOptionTheme >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTheme)) - # shellcheck disable=SC2034 - optionTheme="$1" - optionThemeCallback "${options_parse_arg}" "${optionTheme}" - updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" - ;; - # Option 13/18 - # Option optionHelp --help|-h variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --help | -h) - # shellcheck disable=SC2034 - optionHelp="1" - if ((options_parse_optionParsedCountOptionHelp >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionHelp)) - optionHelpCallback "${options_parse_arg}" - ;; - # Option 14/18 - # Option optionVersion --version variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --version) - # shellcheck disable=SC2034 - optionVersion="1" - if ((options_parse_optionParsedCountOptionVersion >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionVersion)) - optionVersionCallback "${options_parse_arg}" - ;; - # Option 15/18 - # Option optionQuiet --quiet|-q variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --quiet | -q) - # shellcheck disable=SC2034 - optionQuiet="1" - if ((options_parse_optionParsedCountOptionQuiet >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionQuiet)) - optionQuietCallback "${options_parse_arg}" - updateArgListQuietCallback "${options_parse_arg}" - ;; - # Option 16/18 - # Option optionLogLevel --log-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --log-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogLevel)) - # shellcheck disable=SC2034 - optionLogLevel="$1" - optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - ;; - # Option 17/18 - # Option optionLogFile --log-file variableType String min 0 max 1 authorizedValues '' regexp '' - --log-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogFile >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + if ((options_parse_optionParsedCountOptionTargetDsn >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTargetDsn)) + # shellcheck disable=SC2034 + optionTargetDsn="$1" + ;; + + # Option 4/18 + # optionCharacterSet alts --character-set|-c + # type: String min 0 max 1 + --character-set | -c) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionCharacterSet >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionCharacterSet)) + # shellcheck disable=SC2034 + optionCharacterSet="$1" + ;; + + # Option 5/18 + # optionHelp alts --help|-h + # type: Boolean min 0 max 1 + --help | -h) + # shellcheck disable=SC2034 + optionHelp="1" + + if ((options_parse_optionParsedCountOptionHelp >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionHelp)) + optionHelpCallback "${options_parse_arg}" "${optionHelp}" + + ;; + + # Option 6/18 + # optionConfig alts --config + # type: Boolean min 0 max 1 + --config) + # shellcheck disable=SC2034 + optionConfig="1" + + if ((options_parse_optionParsedCountOptionConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionConfig)) + ;; + + # Option 7/18 + # optionBashFrameworkConfig alts --bash-framework-config + # type: String min 0 max 1 + --bash-framework-config) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) + # shellcheck disable=SC2034 + optionBashFrameworkConfig="$1" + optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" + + ;; + + # Option 8/18 + # optionInfoVerbose alts --verbose|-v + # type: Boolean min 0 max 1 + --verbose | -v) + # shellcheck disable=SC2034 + optionInfoVerbose="1" + + if ((options_parse_optionParsedCountOptionInfoVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionInfoVerbose)) + optionInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + updateArgListInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + ;; + + # Option 9/18 + # optionDebugVerbose alts -vv + # type: Boolean min 0 max 1 + -vv) + # shellcheck disable=SC2034 + optionDebugVerbose="1" + + if ((options_parse_optionParsedCountOptionDebugVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDebugVerbose)) + optionDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + updateArgListDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + ;; + + # Option 10/18 + # optionTraceVerbose alts -vvv + # type: Boolean min 0 max 1 + -vvv) + # shellcheck disable=SC2034 + optionTraceVerbose="1" + + if ((options_parse_optionParsedCountOptionTraceVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTraceVerbose)) + optionTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + updateArgListTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + ;; + + # Option 11/18 + # optionEnvFiles alts --env-file + # type: StringArray min 0 max -1 + --env-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + ((++options_parse_optionParsedCountOptionEnvFiles)) + optionEnvFiles+=("$1") + optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + ;; + + # Option 12/18 + # optionLogLevel alts --log-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --log-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogLevel)) + # shellcheck disable=SC2034 + optionLogLevel="$1" + optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + ;; + + # Option 13/18 + # optionLogFile alts --log-file + # type: String min 0 max 1 + --log-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogFile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogFile)) + # shellcheck disable=SC2034 + optionLogFile="$1" + optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + ;; + + # Option 14/18 + # optionDisplayLevel alts --display-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --display-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionDisplayLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDisplayLevel)) + # shellcheck disable=SC2034 + optionDisplayLevel="$1" + optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + ;; + + # Option 15/18 + # optionNoColor alts --no-color + # type: Boolean min 0 max 1 + --no-color) + # shellcheck disable=SC2034 + optionNoColor="1" + + if ((options_parse_optionParsedCountOptionNoColor >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionNoColor)) + optionNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + updateArgListNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + ;; + + # Option 16/18 + # optionTheme alts --theme + # type: String min 0 max 1 + # authorizedValues: default|default-force|noColor + --theme) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ default|default-force|noColor ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionTheme >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTheme)) + # shellcheck disable=SC2034 + optionTheme="$1" + optionThemeCallback "${options_parse_arg}" "${optionTheme}" + + updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" + + ;; + + # Option 17/18 + # optionVersion alts --version + # type: Boolean min 0 max 1 + --version) + # shellcheck disable=SC2034 + optionVersion="1" + + if ((options_parse_optionParsedCountOptionVersion >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionVersion)) + optionVersionCallback "${options_parse_arg}" "${optionVersion}" + + ;; + + # Option 18/18 + # optionQuiet alts --quiet|-q + # type: Boolean min 0 max 1 + --quiet | -q) + # shellcheck disable=SC2034 + optionQuiet="1" + + if ((options_parse_optionParsedCountOptionQuiet >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionQuiet)) + optionQuietCallback "${options_parse_arg}" "${optionQuiet}" + + updateArgListQuietCallback "${options_parse_arg}" "${optionQuiet}" + + ;; + + -*) + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" + return 1 + fi + ;; + *) + ((minParsedArgIndex0 = 0)) || true + ((maxParsedArgIndex0 = 0)) || true + ((minParsedArgIndex1 = minParsedArgIndex0 + 1)) || true + ((maxParsedArgIndex1 = maxParsedArgIndex0 + 1)) || true + ((minParsedArgIndex2 = minParsedArgIndex1 + 1)) || true + ((maxParsedArgIndex2 = maxParsedArgIndex1 + 1)) || true + ((incrementArg = 1 )) + if ((0)); then + # Technical if - never reached + : + + # Argument 1/2 - argDumpFile + # Argument argDumpFile min 1 max 1 + # Argument argDumpFile authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && + options_parse_parsedArgIndex < maxParsedArgIndex1 )); then + if ((options_parse_argParsedCountArgDumpFile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument argDumpFile - Maximum number of argument occurrences reached(1)" return 1 fi - ((++options_parse_optionParsedCountOptionLogFile)) + ((++options_parse_argParsedCountArgDumpFile)) # shellcheck disable=SC2034 - optionLogFile="$1" - optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" - updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" - ;; - # Option 18/18 - # Option optionDisplayLevel --display-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --display-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionDisplayLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + argDumpFile="${options_parse_arg}" + + + # Argument 2/2 - argTargetDbName + # Argument argTargetDbName min 1 max 1 + # Argument argTargetDbName authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex1 && + options_parse_parsedArgIndex < maxParsedArgIndex2 )); then + if ((options_parse_argParsedCountArgTargetDbName >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument argTargetDbName - Maximum number of argument occurrences reached(1)" return 1 fi - ((++options_parse_optionParsedCountOptionDisplayLevel)) + ((++options_parse_argParsedCountArgTargetDbName)) # shellcheck disable=SC2034 - optionDisplayLevel="$1" - optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - ;; - -*) + argTargetDbName="${options_parse_arg}" + + + # else too much args + else + + if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" + # too much args and no unknownArgumentCallbacks configured + Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" return 1 fi - ;; - *) - if ((0)); then - # Technical if - never reached - : - # Argument 1/2 - # Argument argDumpFile min 1 max 1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 0 && options_parse_parsedArgIndex < 1)); then - if ((options_parse_argParsedCountArgDumpFile >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument argDumpFile - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountArgDumpFile)) - # shellcheck disable=SC2034 - argDumpFile="${options_parse_arg}" - # Argument 2/2 - # Argument argTargetDbName min 1 max 1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 1 && options_parse_parsedArgIndex < 2)); then - if ((options_parse_argParsedCountArgTargetDbName >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument argTargetDbName - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountArgTargetDbName)) - # shellcheck disable=SC2034 - argTargetDbName="${options_parse_arg}" - else - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" - return 1 - fi - fi + + fi + if ((incrementArg == 1)); then ((++options_parse_parsedArgIndex)) - ;; - esac - shift || true - done - if ((options_parse_argParsedCountArgDumpFile < 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument 'argDumpFile' should be provided at least 1 time(s)" - return 1 - fi - if ((options_parse_argParsedCountArgTargetDbName < 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument 'argTargetDbName' should be provided at least 1 time(s)" - return 1 - fi - commandOptionParseFinished - initProfileCommandCallback - dbImportStreamCommandCallback - Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" - Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" - elif [[ "${options_parse_cmd}" = "help" ]]; then - Array::wrap2 " " 80 0 "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" "stream tar.gz file or gz file through mysql" - echo + fi + ;; + esac + shift || true + done - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "${SCRIPT_NAME}" "[OPTIONS]" "[ARGUMENTS]")" - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ - "${SCRIPT_NAME}" \ - "[--profile|-p ]" "[--tables ]" "[--target-dsn|-t ]" "[--character-set|-c ]" "[--bash-framework-config ]" "[--config]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--no-color]" "[--theme ]" "[--help|-h]" "[--version]" "[--quiet|-q]" "[--log-level ]" "[--log-file ]" "[--display-level ]")" - echo - echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}argDumpFile${__HELP_NORMAL} {single} (mandatory)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(the\ of\ the\ file\ that\ will\ be\ streamed\ through\ mysql) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}argTargetDbName${__HELP_NORMAL} {single} (mandatory)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(the\ name\ of\ the\ mysql\ target\ database) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}PROFILE OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--profile${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-p ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054,SC2206 - mapfile -t helpArray < <(profileOptionHelpCallback) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--tables ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(import\ only\ table\ specified\ in\ the\ list.\ \ If\ aws\ mode\,\ ignore\ profile\ option) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}TARGET OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--target-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-t ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(dsn\ to\ use\ for\ target\ database\ \(Default:\ default.local\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--character-set${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-c ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(change\ the\ character\ set\ used\ during\ database\ creation\ \(default\ value:\ utf8\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(use\ alternate\ bash\ framework\ configuration.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ configuration) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(info\ level\ verbose\ mode\ \(alias\ of\ --display-level\ INFO\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(debug\ level\ verbose\ mode\ \(alias\ of\ --display-level\ DEBUG\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(trace\ level\ verbose\ mode\ \(alias\ of\ --display-level\ TRACE\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Load\ the\ specified\ env\ file\ \(deprecated\,\ please\ use\ --bash-framework-config\ option\ instead\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Produce\ monochrome\ output.\ alias\ of\ --theme\ noColor.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' - echo ' Possible values: default|default-force|noColor' - echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ this\ command\ help) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Print\ version\ information\ and\ quit) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(quiet\ mode\,\ doesn\'t\ display\ any\ output) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ file) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(set\ display\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e """ -${__HELP_TITLE}Default profiles directory:${__HELP_NORMAL} -${PROFILES_DIR-configuration error} - -${__HELP_TITLE}User profiles directory:${__HELP_NORMAL} -${HOME_PROFILES_DIR-configuration error} -Allows to override profiles defined in "Default profiles directory" - -${__HELP_TITLE}List of available profiles:${__HELP_NORMAL} -${profilesList} - -${__HELP_TITLE}List of available dsn:${__HELP_NORMAL} -${dsnList}""" - echo - echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo '2.0' - echo - echo -e "${__HELP_TITLE_COLOR}AUTHOR:${__RESET_COLOR}" - echo '[François Chastanet](https://github.com/fchastanet)' - echo - echo -e "${__HELP_TITLE_COLOR}SOURCE FILE:${__RESET_COLOR}" - echo 'https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/DbImport/dbImportStream.sh' - echo - echo -e "${__HELP_TITLE_COLOR}LICENSE:${__RESET_COLOR}" - echo 'MIT License' - echo - Array::wrap2 ' ' 76 4 "$(copyrightCallback)" - else - Log::displayError "Command ${SCRIPT_NAME} - Option command invalid: '${options_parse_cmd}'" + + + + + + + + + + + + + + + + + + if ((options_parse_argParsedCountArgDumpFile < 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument 'argDumpFile' should be provided at least 1 time(s)" return 1 fi + + if ((options_parse_argParsedCountArgTargetDbName < 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument 'argTargetDbName' should be provided at least 1 time(s)" + return 1 + fi || return $? + + + + commandOptionParseFinished + initProfileCommandCallback + dbImportStreamCommandCallback + } -checkRequirements() { - if [[ "${SKIP_REQUIREMENTS_CHECKS:-0}" = "1" ]]; then - return 0 - fi - local -i failures=0 +# @description display command options and arguments help for dbImportStreamCommand +dbImportStreamCommandHelp() { + Array::wrap2 ' ' 80 0 "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" \ + "Stream tar.gz file or gz file through mysql." + echo echo - Assert::commandExists mysql "sudo apt-get install -y mysql-client" || ((++failures)) - Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" || ((++failures)) - Assert::commandExists mysqldump "sudo apt-get install -y mysql-client" || ((++failures)) - Assert::commandExists pv "sudo apt-get install -y pv" || ((++failures)) - Assert::commandExists gawk "sudo apt-get install -y gawk" || ((++failures)) - Assert::commandExists awk "sudo apt-get install -y gawk" || ((++failures)) - Version::checkMinimal "gawk" "--version" "5.0.1" || ((++failures)) - return "${failures}" -} -optionVersionCallback() { - echo "${SCRIPT_NAME} version 2.0" - checkRequirements - exit 0 -} + # ------------------------------------------ + # usage section + # ------------------------------------------ + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "dbImportStream [OPTIONS] [ARGUMENTS]" + echo + # ------------------------------------------ + # usage/options section + # ------------------------------------------ + optionsAltList=("[--profile|-p ]" "[--tables ]" "[--target-dsn|-t ]" "[--character-set|-c ]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" + ) + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ + "dbImportStream" "${optionsAltList[@]}" + echo -optionHelpCallback() { - local profilesList="" - local dsnList="" - dsnList="$(Conf::getMergedList "dsn" "env")" - profilesList="$(Conf::getMergedList "dbImportProfiles" "sh" || true)" + # ------------------------------------------ + # usage/arguments section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - dbImportStreamCommand help | envsubst - checkRequirements - exit 0 -} + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}argDumpFile${__HELP_NORMAL} {single} (mandatory) + " + Array::wrap2 ' ' 76 4 " " "The file that will be streamed through mysql." + echo + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}argTargetDbName${__HELP_NORMAL} {single} (mandatory) + " + Array::wrap2 ' ' 76 4 " " "The name of the mysql target database." + echo + # ------------------------------------------ + # options section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}PROFILE OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--profile${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-p ${__HELP_NORMAL} {single}" + profileOptionHelpFunction -dbImportStreamCommandCallback() { - if [[ -z "${argTargetDbName}" ]]; then - Log::fatal "you must provide argTargetDbName" - fi - if [[ ! -f "${argDumpFile}" ]]; then - Log::fatal "invalid argDumpFile provided - file does not exist" - fi -} -dbImportStreamCommand parse "${BASH_FRAMEWORK_ARGV[@]}" -declare awkScript -awkScript="$( - cat <<'EOF' -BEGIN{ - write=1 -} -{ - buffer = substr($0, 1, 150) - line = $0 - if(match(buffer, /^LOCK TABLES `([^`]+)` WRITE;$/, arr) != 0) { - # check if inserts are part of the profile - tableName=arr[1] - if (! (tableName in map)) { - profileCmd = "echo '" tableName "' | " PROFILE_COMMAND " | grep -q " tableName - map[tableName] = (system(profileCmd) == 0) - } - if (map[tableName]) { - print "\033[44m" "begin insert " tableName "\033[0m" > "/dev/stderr" - line = line "\nTRUNCATE TABLE `" tableName "`;" - write=1 - } else { - print "ignore table " tableName > "/dev/stderr" - write=0 - } - } else if(match(buffer, /^commit;$/, arr) != 0) { - write=1 - } else if(match(buffer, /SET NAMES ([^ ]+)/, arr) != 0) { - if (CHARACTER_SET != "") { - sub(/SET NAMES ([^ ]+)/, "SET NAMES " CHARACTER_SET, line) - } - write=1 - } else if(match(buffer, /SET character_set_client = ([^ ]+)/, arr) != 0 && substr(arr[1], 0, 1) != "@") { - if (CHARACTER_SET != "") { - sub(/SET character_set_client = ([^ ]+)/, "SET character_set_client = " CHARACTER_SET, line) - } - write=1 - } + Array::wrap2 ' ' 76 6 " Default value: " "default" + echo - if (write == 1) { - print line - } -} -EOF -)" + echo -e " ${__HELP_OPTION_COLOR}--tables ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Import only table specified in the list." "If aws mode, ignore profile option." "" + echo -# @require Linux::requireExecutedAsUser -run() { - # create db instances - local -Agx dbTargetInstance + echo + echo -e "${__HELP_TITLE_COLOR}TARGET OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--target-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-t ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Dsn to use for target database." + echo - Database::newInstance dbTargetInstance "${optionTargetDsn}" - Database::setQueryOptions dbTargetInstance "${dbTargetInstance[QUERY_OPTIONS]} --connect-timeout=5" - Log::displayInfo "Using target dsn ${dbTargetInstance['DSN_FILE']}" - initializeDefaultTargetMysqlOptions dbTargetInstance "${argTargetDbName}" + Array::wrap2 ' ' 76 6 " Default value: " "default.local" + echo - # TODO character set should be retrieved from dump files if possible - local remoteCharacterSet="${optionCharacterSet:-${defaultRemoteCharacterSet}}" + echo -e " ${__HELP_OPTION_COLOR}--character-set${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-c ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Change the character set used during database creation." + echo - local status=0 - # shellcheck disable=SC2034 - local -a pipeStatus=() - # shellcheck disable=2086 - ( - if [[ "${argDumpFile}" =~ \.tar.gz$ ]]; then - tar xOfz "${argDumpFile}" - elif [[ "${argDumpFile}" =~ \.gz$ ]]; then - zcat "${argDumpFile}" - fi - ) | - awk \ - -v PROFILE_COMMAND="${profileCommandFile}" \ - -v CHARACTER_SET="${remoteCharacterSet}" \ - --source "${awkScript}" \ - - | - mysql \ - "--defaults-extra-file=${dbTargetInstance['AUTH_FILE']}" \ - ${dbTargetInstance['DB_IMPORT_OPTIONS']} \ - "${argTargetDbName}" || - # zcat will continue to write to stdout whereas awk has finished if table has been found - # we detect this case because zcat will return code 141 because pipe closed - Bash::handlePipelineFailure status pipeStatus -} - -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run -fi + echo + echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays this command help" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays configuration" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" + echo + + + echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" + Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log file" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set display level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." + echo + Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" + echo + + Array::wrap2 ' ' 76 6 " Default value: " "default" + echo + + echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Print version information and quit." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." + echo + + + # ------------------------------------------ + # longDescription section + # ------------------------------------------ + echo + echo + echo -e "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" + longDescriptionFunction + # ------------------------------------------ + # version section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" + echo "2.0" + # ------------------------------------------ + # author section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}AUTHOR: ${__RESET_COLOR}" + echo "[François Chastanet](https://github.com/fchastanet)" + # ------------------------------------------ + # sourceFile section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImportStream/binary-dbImportStream.yaml" + # ------------------------------------------ + # license section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}LICENSE: ${__RESET_COLOR}" + echo "MIT License" + # ------------------------------------------ + # copyright section + # ------------------------------------------ + Array::wrap2 ' ' 76 0 "$(copyrightCallback)" } -facade_main_dbImportStreamsh "$@" + +beforeParseCallback +initConf + +dbImportStreamCommandParse "$@" +MAIN_FUNCTION_NAME="main" +main() { + +Linux::requireTarCommand +Compiler::Embed::extractFileFromBase64 \ + "${PERSISTENT_TMPDIR:-/tmp}/6565a8a43f2126f7367983c8747b544f/dbImportStreamScript" \ + "QkVHSU57CiAgd3JpdGU9MQp9CnsKICBidWZmZXIgPSBzdWJzdHIoJDAsIDEsIDE1MCkKICBsaW5lID0gJDAKICBpZihtYXRjaChidWZmZXIsIC9eTE9DSyBUQUJMRVMgYChbXmBdKylgIFdSSVRFOyQvLCBhcnIpICE9IDApIHsKICAgICMgY2hlY2sgaWYgaW5zZXJ0cyBhcmUgcGFydCBvZiB0aGUgcHJvZmlsZQogICAgdGFibGVOYW1lPWFyclsxXQogICAgaWYgKCEgKHRhYmxlTmFtZSBpbiBtYXApKSB7CiAgICAgIHByb2ZpbGVDbWQgPSAiZWNobyAnIiB0YWJsZU5hbWUgIicgfCAiIFBST0ZJTEVfQ09NTUFORCAiIHwgZ3JlcCAtcSAiIHRhYmxlTmFtZQogICAgICBtYXBbdGFibGVOYW1lXSA9IChzeXN0ZW0ocHJvZmlsZUNtZCkgPT0gMCkKICAgIH0KICAgIGlmIChtYXBbdGFibGVOYW1lXSkgewogICAgICBwcmludCAiXDAzM1s0NG0iICJiZWdpbiBpbnNlcnQgIiB0YWJsZU5hbWUgIlwwMzNbMG0iICA+ICIvZGV2L3N0ZGVyciIKICAgICAgbGluZSA9IGxpbmUgIlxuVFJVTkNBVEUgVEFCTEUgYCIgdGFibGVOYW1lICJgOyIKICAgICAgd3JpdGU9MQogICAgfSBlbHNlIHsKICAgICAgcHJpbnQgImlnbm9yZSB0YWJsZSAiIHRhYmxlTmFtZSAgPiAiL2Rldi9zdGRlcnIiCiAgICAgIHdyaXRlPTAKICAgIH0KICB9IGVsc2UgaWYobWF0Y2goYnVmZmVyLCAvXmNvbW1pdDskLywgYXJyKSAhPSAwKSB7CiAgICB3cml0ZT0xCiAgfSBlbHNlIGlmKG1hdGNoKGJ1ZmZlciwgL1NFVCBOQU1FUyAoW14gXSspLywgYXJyKSAhPSAwKSB7CiAgICBpZiAoQ0hBUkFDVEVSX1NFVCAhPSAiIikgewogICAgICBzdWIoL1NFVCBOQU1FUyAoW14gXSspLywgIlNFVCBOQU1FUyAiIENIQVJBQ1RFUl9TRVQsIGxpbmUpCiAgICB9CiAgICB3cml0ZT0xCiAgfSBlbHNlIGlmKG1hdGNoKGJ1ZmZlciwgL1NFVCBjaGFyYWN0ZXJfc2V0X2NsaWVudCA9IChbXiBdKykvLCBhcnIpICE9IDAgJiYgc3Vic3RyKGFyclsxXSwgMCwgMSkgIT0gIkAiKSB7CiAgICBpZiAoQ0hBUkFDVEVSX1NFVCAhPSAiIikgewogICAgICBzdWIoL1NFVCBjaGFyYWN0ZXJfc2V0X2NsaWVudCA9IChbXiBdKykvLCAiU0VUIGNoYXJhY3Rlcl9zZXRfY2xpZW50ID0gIiBDSEFSQUNURVJfU0VULCBsaW5lKQogICAgfQogICAgd3JpdGU9MQogIH0KCgogIGlmICh3cml0ZSA9PSAxKSB7CiAgICBwcmludCBsaW5lCiAgfQp9Cg==" \ + "644" + +declare -gx embed_file_dbImportStreamScript="${PERSISTENT_TMPDIR:-/tmp}/6565a8a43f2126f7367983c8747b544f/dbImportStreamScript" + + +# create db instances +declare -Agx dbTargetInstance + +# shellcheck disable=SC2154 +Database::newInstance dbTargetInstance "${optionTargetDsn}" +Database::setQueryOptions dbTargetInstance "${dbTargetInstance[QUERY_OPTIONS]} --connect-timeout=5" +Log::displayInfo "Using target dsn ${dbTargetInstance['DSN_FILE']}" + +# shellcheck disable=SC2154 +initializeDefaultTargetMysqlOptions dbTargetInstance "${argTargetDbName}" + +# TODO character set should be retrieved from dump files if possible +declare remoteCharacterSet="${optionCharacterSet:-${defaultRemoteCharacterSet}}" + +# shellcheck disable=SC2034 +declare status=0 +# shellcheck disable=SC2034 +declare -a pipeStatus=() + +# shellcheck disable=2086,2154 +( + if [[ "${argDumpFile}" =~ \.tar.gz$ ]]; then + tar xOfz "${argDumpFile}" + elif [[ "${argDumpFile}" =~ \.gz$ ]]; then + zcat "${argDumpFile}" + fi +) | ( + awk \ + -v PROFILE_COMMAND="${profileCommandFile}" \ + -v CHARACTER_SET="${remoteCharacterSet}" \ + -f "${embed_file_dbImportStreamScript}" \ + - +) | ( + mysql \ + "--defaults-extra-file=${dbTargetInstance['AUTH_FILE']}" \ + ${dbTargetInstance['DB_IMPORT_OPTIONS']} \ + "${argTargetDbName}" +) || ( + # zcat will continue to write to stdout whereas awk has finished if table has been found + # we detect this case because zcat will return code 141 because pipe closed + Bash::handlePipelineFailure status pipeStatus +) + +} + +# if file is sourced avoid calling main function +# shellcheck disable=SC2178 +BASH_SOURCE=".$0" # cannot be changed in bash +# shellcheck disable=SC2128 +if test ".$0" == ".${BASH_SOURCE}"; then + if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then + main "$@" &>/dev/null + else + main "$@" + fi +fi diff --git a/bin/doc b/bin/doc index 395b2fa7..5e767439 100755 --- a/bin/doc +++ b/bin/doc @@ -376,7 +376,7 @@ Env::pathPrepend() { # @stderr diagnostics information is displayed # shellcheck disable=SC2120 Env::requireLoad() { - REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 + export REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 local -a defaultFiles=("$@") # get list of possible config files @@ -565,7 +565,7 @@ Git::pullIfNoChanges() { # @exitcode 1 if git command not available # @stderr diagnostics information is displayed Git::requireGitCommand() { - REQUIRE_FUNCTION_GIT_REQUIRE_GIT_COMMAND_LOADED=1 + export REQUIRE_FUNCTION_GIT_REQUIRE_GIT_COMMAND_LOADED=1 Assert::commandExists git } @@ -809,7 +809,7 @@ Log::logWarning() { # @exitcode 0 always successful # @stderr diagnostics information about log file is displayed Log::requireLoad() { - REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + export REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then @@ -1006,7 +1006,7 @@ UI::drawLine() { # @env LOAD_THEME int 0 to avoid loading theme # @exitcode 0 always successful UI::requireTheme() { - REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + export REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 if [[ "${LOAD_THEME:-1}" = "1" ]]; then UI::theme "${BASH_FRAMEWORK_THEME-default}" diff --git a/bin/install b/bin/install index d6c2443f..eff90144 100755 --- a/bin/install +++ b/bin/install @@ -361,7 +361,7 @@ Backup::dir() { # @stderr diagnostics information is displayed # shellcheck disable=SC2120 Env::requireLoad() { - REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 + export REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 local -a defaultFiles=("$@") # get list of possible config files @@ -413,7 +413,7 @@ Linux::requireExecutedAsUser() { # @exitcode 1 if tar command not available # @stderr diagnostics information is displayed Linux::requireTarCommand() { - REQUIRE_FUNCTION_LINUX_REQUIRE_TAR_COMMAND_LOADED=1 + export REQUIRE_FUNCTION_LINUX_REQUIRE_TAR_COMMAND_LOADED=1 Assert::commandExists tar } @@ -638,7 +638,7 @@ Log::logWarning() { # @exitcode 0 always successful # @stderr diagnostics information about log file is displayed Log::requireLoad() { - REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + export REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then @@ -728,7 +728,7 @@ UI::drawLine() { # @env LOAD_THEME int 0 to avoid loading theme # @exitcode 0 always successful UI::requireTheme() { - REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + export REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 if [[ "${LOAD_THEME:-1}" = "1" ]]; then UI::theme "${BASH_FRAMEWORK_THEME-default}" diff --git a/bin/installRequirements b/bin/installRequirements index 1df2e575..76a7b1c3 100755 --- a/bin/installRequirements +++ b/bin/installRequirements @@ -313,7 +313,7 @@ Assert::tty() { # @stderr diagnostics information is displayed # shellcheck disable=SC2120 Env::requireLoad() { - REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 + export REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 local -a defaultFiles=("$@") # get list of possible config files @@ -446,7 +446,7 @@ Git::pullIfNoChanges() { # @exitcode 1 if git command not available # @stderr diagnostics information is displayed Git::requireGitCommand() { - REQUIRE_FUNCTION_GIT_REQUIRE_GIT_COMMAND_LOADED=1 + export REQUIRE_FUNCTION_GIT_REQUIRE_GIT_COMMAND_LOADED=1 Assert::commandExists git } @@ -659,7 +659,7 @@ Log::logWarning() { # @exitcode 0 always successful # @stderr diagnostics information about log file is displayed Log::requireLoad() { - REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + export REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then @@ -749,7 +749,7 @@ UI::drawLine() { # @env LOAD_THEME int 0 to avoid loading theme # @exitcode 0 always successful UI::requireTheme() { - REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + export REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 if [[ "${LOAD_THEME:-1}" = "1" ]]; then UI::theme "${BASH_FRAMEWORK_THEME-default}" diff --git a/bin/mysql2puml b/bin/mysql2puml index ca4279ee..2d37fe89 100755 --- a/bin/mysql2puml +++ b/bin/mysql2puml @@ -502,7 +502,7 @@ Env::pathPrepend() { # @stderr diagnostics information is displayed # shellcheck disable=SC2120 Env::requireLoad() { - REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 + export REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 local -a defaultFiles=("$@") # get list of possible config files @@ -579,7 +579,7 @@ Framework::createTempFile() { # @exitcode 1 if realpath command not available # @stderr diagnostics information is displayed Linux::requireRealpathCommand() { - REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED=1 + export REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED=1 Assert::commandExists realpath } @@ -790,7 +790,7 @@ Log::logWarning() { # @exitcode 0 always successful # @stderr diagnostics information about log file is displayed Log::requireLoad() { - REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + export REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then @@ -880,7 +880,7 @@ UI::drawLine() { # @env LOAD_THEME int 0 to avoid loading theme # @exitcode 0 always successful UI::requireTheme() { - REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + export REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 if [[ "${LOAD_THEME:-1}" = "1" ]]; then UI::theme "${BASH_FRAMEWORK_THEME-default}" diff --git a/src/_binaries/Database/dbImport/dbImport-main.sh b/src/_binaries/Database/dbImport/dbImport-main.sh index 9b228f6a..3549c1d1 100755 --- a/src/_binaries/Database/dbImport/dbImport-main.sh +++ b/src/_binaries/Database/dbImport/dbImport-main.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# @embed "${BASH_TOOLS_ROOT_DIR}/src/_binaries/DbImport/dumpSizeQuery.sql" AS dumpSizeQuery +# @embed "${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbImport/dumpSizeQuery.sql" AS dumpSizeQuery # dump header/footer read -r -d '\0' DUMP_HEADER <<-EOM diff --git a/src/_binaries/DbImport/dumpSizeQuery.sql b/src/_binaries/Database/dbImport/dumpSizeQuery.sql similarity index 100% rename from src/_binaries/DbImport/dumpSizeQuery.sql rename to src/_binaries/Database/dbImport/dumpSizeQuery.sql diff --git a/src/_binaries/Database/dbImportStream/binary-dbImportStream.yaml b/src/_binaries/Database/dbImportStream/binary-dbImportStream.yaml new file mode 100644 index 00000000..57ad5b2d --- /dev/null +++ b/src/_binaries/Database/dbImportStream/binary-dbImportStream.yaml @@ -0,0 +1,46 @@ +extends: + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsProfile.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsMysqlTarget.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" + +vars: + SRC_FILE_PATH: src/_binaries/Database/dbImportStream/binary-dbImportStream.yaml + +compilerConfig: + targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/dbImportStream" + relativeRootDirBasedOnTargetDir: .. + srcDirs: + - ${BASH_TOOLS_ROOT_DIR}/src +binData: + commands: + default: + functionName: dbImportStreamCommand + version: "2.0" + commandName: dbImportStream + beforeParseCallbacks: + - beforeParseCallback + - initConf + callbacks: + - dbImportStreamCommandCallback@100 + definitionFiles: + 20: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbImportStream/dbImportStream-options.sh + 99: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.sh + mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbImportStream/dbImportStream-main.sh + help: Stream tar.gz file or gz file through mysql. + longDescription: longDescriptionFunction + args: + - help: The file that will be streamed through mysql. + type: String + min: 1 + max: 1 + name: argDumpFile + variableName: argDumpFile + + - help: The name of the mysql target database. + type: String + min: 1 + max: 1 + name: argTargetDbName + variableName: argTargetDbName diff --git a/src/_binaries/Database/dbImportStream/dbImportStream-main.sh b/src/_binaries/Database/dbImportStream/dbImportStream-main.sh new file mode 100755 index 00000000..33f25e00 --- /dev/null +++ b/src/_binaries/Database/dbImportStream/dbImportStream-main.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# @embed "${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbImportStream/dbImportStream.awk" AS dbImportStreamScript + +# create db instances +declare -Agx dbTargetInstance + +# shellcheck disable=SC2154 +Database::newInstance dbTargetInstance "${optionTargetDsn}" +Database::setQueryOptions dbTargetInstance "${dbTargetInstance[QUERY_OPTIONS]} --connect-timeout=5" +Log::displayInfo "Using target dsn ${dbTargetInstance['DSN_FILE']}" + +# shellcheck disable=SC2154 +initializeDefaultTargetMysqlOptions dbTargetInstance "${argTargetDbName}" + +# TODO character set should be retrieved from dump files if possible +declare remoteCharacterSet="${optionCharacterSet:-${defaultRemoteCharacterSet}}" + +# shellcheck disable=SC2034 +declare status=0 +# shellcheck disable=SC2034 +declare -a pipeStatus=() + +# shellcheck disable=2086,2154 +( + if [[ "${argDumpFile}" =~ \.tar.gz$ ]]; then + tar xOfz "${argDumpFile}" + elif [[ "${argDumpFile}" =~ \.gz$ ]]; then + zcat "${argDumpFile}" + fi +) | ( + awk \ + -v PROFILE_COMMAND="${profileCommandFile}" \ + -v CHARACTER_SET="${remoteCharacterSet}" \ + -f "${embed_file_dbImportStreamScript}" \ + - +) | ( + mysql \ + "--defaults-extra-file=${dbTargetInstance['AUTH_FILE']}" \ + ${dbTargetInstance['DB_IMPORT_OPTIONS']} \ + "${argTargetDbName}" +) || ( + # zcat will continue to write to stdout whereas awk has finished if table has been found + # we detect this case because zcat will return code 141 because pipe closed + Bash::handlePipelineFailure status pipeStatus +) diff --git a/src/_binaries/Database/dbImportStream/dbImportStream-options.sh b/src/_binaries/Database/dbImportStream/dbImportStream-options.sh new file mode 100755 index 00000000..72e208de --- /dev/null +++ b/src/_binaries/Database/dbImportStream/dbImportStream-options.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash + +declare PROFILES_DIR +declare HOME_PROFILES_DIR +# shellcheck disable=SC2034 +declare versionNumber="2.0" +# shellcheck disable=SC2034 +declare copyrightBeginYear="2020" +# shellcheck disable=SC2034 +declare defaultFromDsn="default.remote" + +beforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad + Linux::requireExecutedAsUser + Linux::requireRealpathCommand +} + +initConf() { + # shellcheck disable=SC2034 + PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbImportProfiles" + HOME_PROFILES_DIR="${HOME}/.bash-tools/dbImportProfiles" + Db::checkRequirements +} + +optionHelpCallback() { + dbImportStreamCommandHelp + exit 0 +} + +longDescriptionFunction() { + local profilesList="" + local dsnList="" + dsnList="$(Conf::getMergedList "dsn" "env")" + profilesList="$(Conf::getMergedList "dbImportProfiles" "sh" || true)" + + echo -e "${__HELP_TITLE}Default profiles directory:${__HELP_NORMAL}" + echo -e "${PROFILES_DIR-configuration error}" + echo + echo -e "${__HELP_TITLE}User profiles directory:${__HELP_NORMAL}" + echo -e "${HOME_PROFILES_DIR-configuration error}" + echo -e "Allows to override profiles defined in 'Default profiles directory'" + echo + echo -e "${__HELP_TITLE}List of available profiles:${__HELP_NORMAL}" + echo -e "${profilesList}" + echo + echo -e "${__HELP_TITLE}List of available dsn:${__HELP_NORMAL}" + echo -e "${dsnList}" +} + +dbImportStreamCommandCallback() { + if [[ -z "${argTargetDbName}" ]]; then + Log::fatal "you must provide argTargetDbName" + fi + # shellcheck disable=SC2154 + if [[ ! -f "${argDumpFile}" ]]; then + Log::fatal "invalid argDumpFile provided - file does not exist" + fi +} diff --git a/src/_binaries/DbImport/dbImportStream.awk b/src/_binaries/Database/dbImportStream/dbImportStream.awk similarity index 100% rename from src/_binaries/DbImport/dbImportStream.awk rename to src/_binaries/Database/dbImportStream/dbImportStream.awk diff --git a/src/_binaries/DbImport/dbImportStream.bats b/src/_binaries/Database/dbImportStream/dbImportStream.bats similarity index 93% rename from src/_binaries/DbImport/dbImportStream.bats rename to src/_binaries/Database/dbImportStream/dbImportStream.bats index dc69b28e..27bcba65 100755 --- a/src/_binaries/DbImport/dbImportStream.bats +++ b/src/_binaries/Database/dbImportStream/dbImportStream.bats @@ -1,7 +1,7 @@ #!/usr/bin/env bash # shellcheck source=src/batsHeaders.sh -source "$(cd "${BATS_TEST_DIRNAME}/../.." && pwd)/batsHeaders.sh" +source "$(cd "${BATS_TEST_DIRNAME}/../../.." && pwd)/batsHeaders.sh" # shellcheck source=vendor/bash-tools-framework/src/Log/_.sh source "${FRAMEWORK_ROOT_DIR}/src/Log/_.sh" || exit 1 diff --git a/src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt b/src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt new file mode 100644 index 00000000..ab672725 --- /dev/null +++ b/src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt @@ -0,0 +1,94 @@ +SYNOPSIS: Stream tar.gz file or gz file through mysql. + +USAGE: dbImportStream [OPTIONS] [ARGUMENTS] +USAGE: dbImportStream [--profile|-p ] + [--tables ] [--target-dsn|-t ] + [--character-set|-c ] [--help|-h] [--config] + [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] + [--env-file ] [--log-level ] [--log-file ] + [--display-level ] [--no-color] [--theme ] [--version] + [--quiet|-q] + +ARGUMENTS: + argDumpFile {single} (mandatory) + The file that will be streamed through mysql. + argTargetDbName {single} (mandatory) + The name of the mysql target database. + +PROFILE OPTIONS: + --profile, -p  {single} + The name of the profile to use in order to include or exclude tables. + Default value: default + --tables  {single} + Import only table specified in the list. + If aws mode, ignore profile option. + + +TARGET OPTIONS: + --target-dsn, -t  {single} + Dsn to use for target database. + Default value: default.local + --character-set, -c  {single} + Change the character set used during database creation. + +GLOBAL OPTIONS: + --help, -h {single} + Displays this command help + --config {single} + Displays configuration + --bash-framework-config  {single} + Use alternate bash framework configuration. + --verbose, -v {single} + Info level verbose mode (alias of --display-level INFO) + -vv {single} + Debug level verbose mode (alias of --display-level DEBUG) + -vvv {single} + Trace level verbose mode (alias of --display-level TRACE) + --env-file  {list} (optional) + Load the specified env file (deprecated, please use --bash-framework-con + fig option instead) + --log-level  {single} + Set log level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --log-file  {single} + Set log file + --display-level  {single} + Set display level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --no-color {single} + Produce monochrome output. alias of --theme noColor. + --theme  {single} + Choose color theme - default-force means colors will be produced even if + command is piped. + Possible values: default, default-force, noColor + Default value: default + --version {single} + Print version information and quit. + --quiet, -q {single} + Quiet mode, doesn't display any output. + + +DESCRIPTION: +Default profiles directory: +/bash/conf/dbImportProfiles + +User profiles directory: +home/.bash-tools/dbImportProfiles +Allows to override profiles defined in 'Default profiles directory' + +List of available profiles: + + +List of available dsn: + - default.local + - default.remote + - localhost-root + +VERSION: 2.0 + +AUTHOR: [François Chastanet](https://github.com/fchastanet) + +SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImportStream/binary-dbImportStream.yaml + +LICENSE: MIT License +Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/DbImport/testsData/dsn/default.local.env b/src/_binaries/Database/dbImportStream/testsData/dsn/default.local.env similarity index 100% rename from src/_binaries/DbImport/testsData/dsn/default.local.env rename to src/_binaries/Database/dbImportStream/testsData/dsn/default.local.env diff --git a/src/_binaries/DbImport/testsData/dsn/default.remote.env b/src/_binaries/Database/dbImportStream/testsData/dsn/default.remote.env similarity index 100% rename from src/_binaries/DbImport/testsData/dsn/default.remote.env rename to src/_binaries/Database/dbImportStream/testsData/dsn/default.remote.env diff --git a/src/_binaries/DbImport/testsData/dsn/localhost-root.env b/src/_binaries/Database/dbImportStream/testsData/dsn/localhost-root.env similarity index 100% rename from src/_binaries/DbImport/testsData/dsn/localhost-root.env rename to src/_binaries/Database/dbImportStream/testsData/dsn/localhost-root.env diff --git a/src/_binaries/DbImport/dbImportStream.options.tpl b/src/_binaries/DbImport/dbImportStream.options.tpl deleted file mode 100644 index 10ea9b5e..00000000 --- a/src/_binaries/DbImport/dbImportStream.options.tpl +++ /dev/null @@ -1,77 +0,0 @@ -% -declare versionNumber="2.0" -declare commandFunctionName="dbImportStreamCommand" -declare help="stream tar.gz file or gz file through mysql" -# shellcheck disable=SC2016 -declare longDescription=''' -${__HELP_TITLE}Default profiles directory:${__HELP_NORMAL} -${PROFILES_DIR-configuration error} - -${__HELP_TITLE}User profiles directory:${__HELP_NORMAL} -${HOME_PROFILES_DIR-configuration error} -Allows to override profiles defined in "Default profiles directory" - -${__HELP_TITLE}List of available profiles:${__HELP_NORMAL} -${profilesList} - -${__HELP_TITLE}List of available dsn:${__HELP_NORMAL} -${dsnList}''' -declare defaultFromDsn="default.remote" -% - -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.base.tpl)" -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.profile.tpl)" -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.mysql.target.tpl)" - -% -# shellcheck source=/dev/null -source <( - - Options::generateArg \ - --help "the of the file that will be streamed through mysql" \ - --min 1 \ - --max 1 \ - --name "argDumpFile" \ - --variable-name "argDumpFile" \ - --function-name argDumpFileFunction - - Options::generateArg \ - --help "the name of the mysql target database" \ - --min 1 \ - --max 1 \ - --name "argTargetDbName" \ - --variable-name "argTargetDbName" \ - --function-name argTargetDbNameFunction - -) -options+=( - argDumpFileFunction - argTargetDbNameFunction - --callback dbImportStreamCommandCallback -) -Options::generateCommand "${options[@]}" -% - -.INCLUDE "$(dynamicTemplateDir _includes/dbTools.requirements.tpl)" - -optionHelpCallback() { - local profilesList="" - local dsnList="" - dsnList="$(Conf::getMergedList "dsn" "env")" - profilesList="$(Conf::getMergedList "dbImportProfiles" "sh" || true)" - - <% ${commandFunctionName} %> help | envsubst - Db::checkRequirements - exit 0 -} - -dbImportStreamCommandCallback() { - if [[ -z "${argTargetDbName}" ]]; then - Log::fatal "you must provide argTargetDbName" - fi - if [[ ! -f "${argDumpFile}" ]]; then - Log::fatal "invalid argDumpFile provided - file does not exist" - fi -} - -<% ${commandFunctionName} %> parse "${BASH_FRAMEWORK_ARGV[@]}" diff --git a/src/_binaries/DbImport/dbImportStream.sh b/src/_binaries/DbImport/dbImportStream.sh deleted file mode 100755 index 0c6a71f3..00000000 --- a/src/_binaries/DbImport/dbImportStream.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env bash -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/dbImportStream -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE -# shellcheck disable=SC2034 - -# default values -declare optionProfile="" -declare argTargetDbName="" -declare argDumpFile="" -declare optionTargetDsn="" -declare optionCharacterSet="" -declare defaultTargetCharacterSet="" -declare profileCommandFile="" - -# other configuration -declare copyrightBeginYear="2020" -declare PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbImportProfiles" -declare HOME_PROFILES_DIR="${HOME}/.bash-tools/dbImportProfiles" - -.INCLUDE "$(dynamicTemplateDir _binaries/DbImport/dbImportStream.options.tpl)" - -declare awkScript -awkScript="$( - cat <<'EOF' -.INCLUDE "$(dynamicSrcFile "_binaries/DbImport/dbImportStream.awk")" -EOF -)" - -# @require Linux::requireExecutedAsUser -run() { - - # create db instances - local -Agx dbTargetInstance - - Database::newInstance dbTargetInstance "${optionTargetDsn}" - Database::setQueryOptions dbTargetInstance "${dbTargetInstance[QUERY_OPTIONS]} --connect-timeout=5" - Log::displayInfo "Using target dsn ${dbTargetInstance['DSN_FILE']}" - - initializeDefaultTargetMysqlOptions dbTargetInstance "${argTargetDbName}" - - # TODO character set should be retrieved from dump files if possible - local remoteCharacterSet="${optionCharacterSet:-${defaultRemoteCharacterSet}}" - - local status=0 - # shellcheck disable=SC2034 - local -a pipeStatus=() - - # shellcheck disable=2086 - ( - if [[ "${argDumpFile}" =~ \.tar.gz$ ]]; then - tar xOfz "${argDumpFile}" - elif [[ "${argDumpFile}" =~ \.gz$ ]]; then - zcat "${argDumpFile}" - fi - ) | - awk \ - -v PROFILE_COMMAND="${profileCommandFile}" \ - -v CHARACTER_SET="${remoteCharacterSet}" \ - --source "${awkScript}" \ - - | - mysql \ - "--defaults-extra-file=${dbTargetInstance['AUTH_FILE']}" \ - ${dbTargetInstance['DB_IMPORT_OPTIONS']} \ - "${argTargetDbName}" || - # zcat will continue to write to stdout whereas awk has finished if table has been found - # we detect this case because zcat will return code 141 because pipe closed - Bash::handlePipelineFailure status pipeStatus -} - -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run -fi diff --git a/src/_binaries/DbImport/dbImportTableRename.awk b/src/_binaries/DbImport/dbImportTableRename.awk deleted file mode 100644 index b4c5d480..00000000 --- a/src/_binaries/DbImport/dbImportTableRename.awk +++ /dev/null @@ -1,20 +0,0 @@ -# Rename table -# variables -# SOURCE_TABLE -# TARGET_TABLE -BEGIN{ - RENAME=(SOURCE_TABLE != TARGET_TABLE) - pattern="^(LOCK TABLES|DROP TABLE IF EXISTS|CREATE TABLE|\\/\\*![[:digit:]]+ ALTER TABLE|INSERT INTO) `(" SOURCE_TABLE ")`" -} -{ - line = $0 - buffer = substr($0, 0, 150) -} -buffer ~ pattern { - if (RENAME) { - line = gensub(/^(LOCK TABLES|DROP TABLE IF EXISTS|CREATE TABLE|\/\*![0-9]+ ALTER TABLE|INSERT INTO) `([^`]+)`/, "\\1 `" TARGET_TABLE "`", "g", line) - } -} -{ - print line -} diff --git a/src/_binaries/DbImport/testsData/.env b/src/_binaries/DbImport/testsData/.env deleted file mode 100755 index 24e891d2..00000000 --- a/src/_binaries/DbImport/testsData/.env +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env bash -# shellcheck disable=SC2034 - -# Default settings -# you can override these settings by creating ${HOME}/.bash-tools/.env file -### -### LOG Level -### minimum level of the messages that will be logged into LOG_FILE -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_LOG_LEVEL=0 - -### -### DISPLAY Level -### minimum level of the messages that will be displayed on screen -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} - -### -### Log to file -### -### all log messages will be redirected to log file specified -### this same path will be used inside and outside of the container -### -BASH_FRAMEWORK_LOG_FILE= - -# absolute directory containing db import sql dumps -DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} - -# garbage collect all files for which modification is greater than eg: 30 days (+30) -# each time an existing file is used by dbImport/dbImportTable -# the file modification time is set to now -DB_IMPORT_GARBAGE_COLLECT_DAYS=+30 - -# absolute directory containing dbScripts used by dbScriptAllDatabases -SCRIPTS_FOLDER=${HOME}/.bash-tools/conf/dbScripts - -# ----------------------------------------------------- -# AWS Parameters -# ----------------------------------------------------- -S3_BASE_URL=s3://s3server/exports diff --git a/src/_binaries/DbImport/testsData/auto_default.local_fromDb_20.sh b/src/_binaries/DbImport/testsData/auto_default.local_fromDb_20.sh deleted file mode 100755 index a332f1c1..00000000 --- a/src/_binaries/DbImport/testsData/auto_default.local_fromDb_20.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -# cat represents the whole list of tables -cat | - grep -v '^table1$' | # table size 29MB - grep -v '^table2$' | # table size 10MB - # grep -v '^table3$' | # table size 4MB - cat diff --git a/src/_binaries/DbImport/testsData/auto_default.local_fromDb_70.sh b/src/_binaries/DbImport/testsData/auto_default.local_fromDb_70.sh deleted file mode 100755 index a6919c07..00000000 --- a/src/_binaries/DbImport/testsData/auto_default.local_fromDb_70.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -# cat represents the whole list of tables -cat | - grep -v '^table1$' | # table size 29MB - # grep -v '^table2$' | # table size 10MB - # grep -v '^table3$' | # table size 4MB - cat diff --git a/src/_binaries/DbImport/testsData/dbImport.help.txt b/src/_binaries/DbImport/testsData/dbImport.help.txt deleted file mode 100644 index a4541bfc..00000000 --- a/src/_binaries/DbImport/testsData/dbImport.help.txt +++ /dev/null @@ -1,122 +0,0 @@ -DESCRIPTION: -Import source db into target db using eventual table filter -USAGE: dbImport [OPTIONS] [ARGUMENTS] -USAGE: dbImport [--profile|-p ] - [--tables ] [--from-dsn|-f ] - [--skip-schema|-s] [--from-aws|-a ] [--target-dsn|-t ] - [--character-set|-c ] [--collation-name|-o ] - [--bash-framework-config ] [--config] [--verbose|-v] [-vv] [-vvv] - [--env-file ] [--no-color] [--theme ] [--help|-h] [--version] - [--quiet|-q] [--log-level ] [--log-file ] - [--display-level ] - -ARGUMENTS: - fromDbName {single} (mandatory) - the name of the source/remote database - [targetDbName {single}] - the name of the target database, use fromDbName(without extension) if not pr - ovided - -PROFILE OPTIONS: - --profile, -p  {single} - the name of the profile to use in order to include or exclude tables (if not - specified in default.sh from 'User profiles directory' if exists or 'Def - ault profiles directory') - --tables  {single} - import only table specified in the list. If aws mode, ignore profile option - -FROM OPTIONS: - --from-dsn, -f  {single} - dsn to use for source database - - this option is incompatible with -a|--from-aws option - --skip-schema, -s {single} - avoid to import the schema - --from-aws, -a  {single} - db dump will be downloaded from s3 instead of using remote db. The value is the name of the file without s3 location (Only .gz or tar.gz f - ile are supported). This option is incompatible with -f|--from-dsn option - -TARGET OPTIONS: - --target-dsn, -t  {single} - dsn to use for target database (Default: default.local) - --character-set, -c  {single} - change the character set used during database creation (default value: utf8) - --collation-name, -o  {single} - change the collation name used during database creation (default value: utf8 - _general_ci) - -GLOBAL OPTIONS: - --bash-framework-config  {single} - use alternate bash framework configuration. - --config {single} - Display configuration - --verbose, -v {single} - info level verbose mode (alias of --display-level INFO) - -vv {single} - debug level verbose mode (alias of --display-level DEBUG) - -vvv {single} - trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-config - option instead) - --no-color {single} - Produce monochrome output. alias of --theme noColor. - --theme  {single} - choose color theme - default-force means colors will be produced even if com - mand is piped - Default value: default - Possible values: default|default-force|noColor - --help, -h {single} - Display this command help - --version {single} - Print version information and quit - --quiet, -q {single} - quiet mode, doesn't display any output - --log-level  {single} - Set log level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - --log-file  {single} - Set log file - --display-level  {single} - set display level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - -Default profiles directory: -/bash/conf/dbImportProfiles - -User profiles directory: -home/.bash-tools/dbImportProfiles -Allows to override profiles defined in Default profiles directory - -List of available profiles: - - all - - default - - none - -List of available dsn: - - default.local - - default.remote - - localhost-root - -Aws s3 location: -s3://s3server/exports - -Example 1: from one database to another one -TODO - -Example 2: import from S3 -TODO - -VERSION: 2.0 - -[1;37mAUTHOR: -[François Chastanet](https://github.com/fchastanet) - -SOURCE FILE: -https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/DbImport/dbImport.sh - -LICENSE: -MIT License - -Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/DbImport/testsData/dbImportProfile.tableList1 b/src/_binaries/DbImport/testsData/dbImportProfile.tableList1 deleted file mode 100644 index f02c5c5f..00000000 --- a/src/_binaries/DbImport/testsData/dbImportProfile.tableList1 +++ /dev/null @@ -1,3 +0,0 @@ -table1 29 -table2 10 -table3 4 diff --git a/src/_binaries/DbImport/testsData/dbImportProfiles/all.sh b/src/_binaries/DbImport/testsData/dbImportProfiles/all.sh deleted file mode 100755 index 09b79b19..00000000 --- a/src/_binaries/DbImport/testsData/dbImportProfiles/all.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash - -# cat represents the whole list of tables -cat diff --git a/src/_binaries/DbImport/testsData/dbImportProfiles/default.sh b/src/_binaries/DbImport/testsData/dbImportProfiles/default.sh deleted file mode 100755 index 8cf9b92b..00000000 --- a/src/_binaries/DbImport/testsData/dbImportProfiles/default.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -# cat represents the whole list of tables -cat | - grep -v '.*_log' | - grep -v '.*logs' | - grep -v '.*tracking' | - grep -v '.*stats' | - grep -v '.*history.*' | - # always finish by a cat to be sure the command does not return exit code != 0 - cat || { - # avoid failure on premature process close (check Bash::handlePipelineFailure) - declare exitCode=$?; if (( exitCode == 141 )); then exit 0; fi - exit "${exitCode}" - } diff --git a/src/_binaries/DbImport/testsData/dbImportProfiles/none.sh b/src/_binaries/DbImport/testsData/dbImportProfiles/none.sh deleted file mode 100755 index 5604fdca..00000000 --- a/src/_binaries/DbImport/testsData/dbImportProfiles/none.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -# useful if you want to dump only the db structure - -# cat represents the whole list of tables -echo 'propel_migration' diff --git a/src/_binaries/DbImport/testsData/dbImportStream.help.txt b/src/_binaries/DbImport/testsData/dbImportStream.help.txt deleted file mode 100644 index 0ff1b343..00000000 --- a/src/_binaries/DbImport/testsData/dbImportStream.help.txt +++ /dev/null @@ -1,92 +0,0 @@ -DESCRIPTION: stream tar.gz file or gz file through mysql -USAGE: dbImportStream [OPTIONS] [ARGUMENTS] -USAGE: dbImportStream [--profile|-p ] - [--tables ] [--target-dsn|-t ] - [--character-set|-c ] [--bash-framework-config ] [--config] - [--verbose|-v] [-vv] [-vvv] [--env-file ] [--no-color] - [--theme ] [--help|-h] [--version] [--quiet|-q] [--log-level ] - [--log-file ] [--display-level ] - -ARGUMENTS: - argDumpFile {single} (mandatory) - the of the file that will be streamed through mysql - argTargetDbName {single} (mandatory) - the name of the mysql target database - -PROFILE OPTIONS: - --profile, -p  {single} - the name of the profile to use in order to include or exclude tables (if not - specified in default.sh from 'User profiles directory' if exists or 'Def - ault profiles directory') - --tables  {single} - import only table specified in the list. If aws mode, ignore profile option - -TARGET OPTIONS: - --target-dsn, -t  {single} - dsn to use for target database (Default: default.local) - --character-set, -c  {single} - change the character set used during database creation (default value: utf8) - -GLOBAL OPTIONS: - --bash-framework-config  {single} - use alternate bash framework configuration. - --config {single} - Display configuration - --verbose, -v {single} - info level verbose mode (alias of --display-level INFO) - -vv {single} - debug level verbose mode (alias of --display-level DEBUG) - -vvv {single} - trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-config - option instead) - --no-color {single} - Produce monochrome output. alias of --theme noColor. - --theme  {single} - choose color theme - default-force means colors will be produced even if com - mand is piped - Default value: default - Possible values: default|default-force|noColor - --help, -h {single} - Display this command help - --version {single} - Print version information and quit - --quiet, -q {single} - quiet mode, doesn't display any output - --log-level  {single} - Set log level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - --log-file  {single} - Set log file - --display-level  {single} - set display level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - -Default profiles directory: -/bash/conf/dbImportProfiles - -User profiles directory: -home/.bash-tools/dbImportProfiles -Allows to override profiles defined in Default profiles directory - -List of available profiles: - - -List of available dsn: - - default.local - - default.remote - - localhost-root - -VERSION: 2.0 - -AUTHOR: -[François Chastanet](https://github.com/fchastanet) - -SOURCE FILE: -https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/DbImport/dbImportStream.sh - -LICENSE: -MIT License - -Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/DbImport/testsData/dbImportTableDump.sql b/src/_binaries/DbImport/testsData/dbImportTableDump.sql deleted file mode 100644 index e832194e..00000000 --- a/src/_binaries/DbImport/testsData/dbImportTableDump.sql +++ /dev/null @@ -1,35 +0,0 @@ --- MySQL dump 10.13 Distrib 8.0.22, for Linux (x86_64) --- --- Host: 127.0.0.1 Database: fromDb --- ------------------------------------------------------ --- Server version 8.0.21 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!50503 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Dumping data for table `emptyTable` --- - -DROP TABLE IF EXISTS `dataTable`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `dataTable` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `modification_date` datetime NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2011640 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - -LOCK TABLES `dataTable` WRITE; -/*!40000 ALTER TABLE `dataTable` DISABLE KEYS */; -INSERT INTO `dataTable` VALUES (1,1,'Picture:','en-GB'),(2,2,'Name:','en-GB'),(3,3,'First name:','en-GB'),(4,4,'Login:','en-GB'),(5,5,'Password:','en-GB'),(6,6,'E-mail:','en-GB'),(7,7,'Reference number:','en-GB'),(8,8,'Presentation:','en-GB'),(9,9,'Web:','en-GB'),(10,10,'Twitter:','en-GB'),(11,11,'LinkedIn:','en-GB'),(12,12,'Languages:','en-GB'),(13,13,'Time zone:','en-GB'); -/*!40000 ALTER TABLE `dataTable` ENABLE KEYS */; diff --git a/src/_binaries/DbImport/testsData/dump.sql b/src/_binaries/DbImport/testsData/dump.sql deleted file mode 100644 index 273f7eb0..00000000 --- a/src/_binaries/DbImport/testsData/dump.sql +++ /dev/null @@ -1,84 +0,0 @@ --- MySQL dump 10.13 Distrib 8.0.22, for Linux (x86_64) --- --- Host: 127.0.0.1 Database: fromDb --- ------------------------------------------------------ --- Server version 8.0.21 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!50503 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Dumping data for table `emptyTable` --- - -DROP TABLE IF EXISTS `emptyTable`; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `emptyTable` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `modification_date` datetime NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2011640 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - -LOCK TABLES `emptyTable` WRITE; -/*!40000 ALTER TABLE `emptyTable` DISABLE KEYS */; -/*!40000 ALTER TABLE `emptyTable` ENABLE KEYS */; -UNLOCK TABLES; - -DROP TABLE IF EXISTS `dataTable`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `dataTable` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `modification_date` datetime NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2011640 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - -LOCK TABLES `dataTable` WRITE; -/*!40000 ALTER TABLE `dataTable` DISABLE KEYS */; -INSERT INTO `dataTable` VALUES (1,1,'Picture:','en-GB'),(2,2,'Name:','en-GB'),(3,3,'First name:','en-GB'),(4,4,'Login:','en-GB'),(5,5,'Password:','en-GB'),(6,6,'E-mail:','en-GB'),(7,7,'Reference number:','en-GB'),(8,8,'Presentation:','en-GB'),(9,9,'Web:','en-GB'),(10,10,'Twitter:','en-GB'),(11,11,'LinkedIn:','en-GB'),(12,12,'Languages:','en-GB'),(13,13,'Time zone:','en-GB'); -/*!40000 ALTER TABLE `dataTable` ENABLE KEYS */; -UNLOCK TABLES; - -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `otherTable` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `modification_date` datetime NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2011640 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; - -LOCK TABLES `otherTable` WRITE; -/*!40000 ALTER TABLE `dataTable` DISABLE KEYS */; -INSERT INTO `otherTable` VALUES (1,1,'Picture:','en-GB'),(2,2,'Name:','en-GB'),(3,3,'First name:','en-GB'),(4,4,'Login:','en-GB'),(5,5,'Password:','en-GB'),(6,6,'E-mail:','en-GB'),(7,7,'Reference number:','en-GB'),(8,8,'Presentation:','en-GB'),(9,9,'Web:','en-GB'),(10,10,'Twitter:','en-GB'),(11,11,'LinkedIn:','en-GB'),(12,12,'Languages:','en-GB'),(13,13,'Time zone:','en-GB'); -/*!40000 ALTER TABLE `dataTable` ENABLE KEYS */; -UNLOCK TABLES; - - --- --- Dumping routines for database 'fromDb' --- -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2021-01-18 22:52:14 -COMMIT; -SET AUTOCOMMIT=@OLD_AUTOCOMMIT; -SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; -SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; diff --git a/src/_binaries/DbImport/testsData/dumpMissingSchema.sql b/src/_binaries/DbImport/testsData/dumpMissingSchema.sql deleted file mode 100644 index de982be2..00000000 --- a/src/_binaries/DbImport/testsData/dumpMissingSchema.sql +++ /dev/null @@ -1,61 +0,0 @@ -SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS = 0; -SET @OLD_AUTOCOMMIT=@@AUTOCOMMIT, AUTOCOMMIT = 0; -SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS = 0; -SET names 'utf8'; --- MySQL dump 10.13 Distrib 8.0.22, for Linux (x86_64) --- --- Host: 127.0.0.1 Database: fromDb --- ------------------------------------------------------ --- Server version 8.0.21 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!50503 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Dumping data for table `emptyTable` --- - -LOCK TABLES `emptyTable` WRITE; -/*!40000 ALTER TABLE `emptyTable` DISABLE KEYS */; -/*!40000 ALTER TABLE `emptyTable` ENABLE KEYS */; -UNLOCK TABLES; - -LOCK TABLES `dataTable` WRITE; -/*!40000 ALTER TABLE `dataTable` DISABLE KEYS */; -INSERT INTO `dataTable` VALUES (1,1,'Picture:','en-GB'),(2,2,'Name:','en-GB'),(3,3,'First name:','en-GB'),(4,4,'Login:','en-GB'),(5,5,'Password:','en-GB'),(6,6,'E-mail:','en-GB'),(7,7,'Reference number:','en-GB'),(8,8,'Presentation:','en-GB'),(9,9,'Web:','en-GB'),(10,10,'Twitter:','en-GB'),(11,11,'LinkedIn:','en-GB'),(12,12,'Languages:','en-GB'),(13,13,'Time zone:','en-GB'); -/*!40000 ALTER TABLE `dataTable` ENABLE KEYS */; -UNLOCK TABLES; - -LOCK TABLES `otherTable` WRITE; -/*!40000 ALTER TABLE `dataTable` DISABLE KEYS */; -INSERT INTO `otherTable` VALUES (1,1,'Picture:','en-GB'),(2,2,'Name:','en-GB'),(3,3,'First name:','en-GB'),(4,4,'Login:','en-GB'),(5,5,'Password:','en-GB'),(6,6,'E-mail:','en-GB'),(7,7,'Reference number:','en-GB'),(8,8,'Presentation:','en-GB'),(9,9,'Web:','en-GB'),(10,10,'Twitter:','en-GB'),(11,11,'LinkedIn:','en-GB'),(12,12,'Languages:','en-GB'),(13,13,'Time zone:','en-GB'); -/*!40000 ALTER TABLE `dataTable` ENABLE KEYS */; -UNLOCK TABLES; - - --- --- Dumping routines for database 'fromDb' --- -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2021-01-18 22:52:14 -COMMIT; -SET AUTOCOMMIT=@OLD_AUTOCOMMIT; -SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; -SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; diff --git a/src/_binaries/DbImport/testsData/dumpSchema.sql b/src/_binaries/DbImport/testsData/dumpSchema.sql deleted file mode 100644 index 83509a2a..00000000 --- a/src/_binaries/DbImport/testsData/dumpSchema.sql +++ /dev/null @@ -1,64 +0,0 @@ --- MySQL dump 10.13 Distrib 8.0.22, for Linux (x86_64) --- --- Host: 127.0.0.1 Database: fromDb --- ------------------------------------------------------ --- Server version 8.0.21 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!50503 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Dumping data for table `emptyTable` --- - -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `emptyTable` IF NOT EXISTS ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `modification_date` datetime NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2011640 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `dataTable` IF NOT EXISTS ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `modification_date` datetime NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2011640 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `otherTable` IF NOT EXISTS ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `modification_date` datetime NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2011640 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; - --- --- Dumping routines for database 'fromDb' --- -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2021-01-18 22:52:14 -COMMIT; -SET AUTOCOMMIT=@OLD_AUTOCOMMIT; -SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; -SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; diff --git a/src/_binaries/DbImport/testsData/empty-dump.sql b/src/_binaries/DbImport/testsData/empty-dump.sql deleted file mode 100644 index c378ac9a..00000000 --- a/src/_binaries/DbImport/testsData/empty-dump.sql +++ /dev/null @@ -1 +0,0 @@ --- EMPTY DUMP diff --git a/src/_binaries/DbImport/testsData/expectedDbImportProfileTableListQuery.sql b/src/_binaries/DbImport/testsData/expectedDbImportProfileTableListQuery.sql deleted file mode 100644 index bb2068de..00000000 --- a/src/_binaries/DbImport/testsData/expectedDbImportProfileTableListQuery.sql +++ /dev/null @@ -1 +0,0 @@ -SELECT TABLE_NAME AS tableName, ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024) as maxSize FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'fromDb' AND TABLE_TYPE NOT IN('VIEW') ORDER BY maxSize DESC diff --git a/src/_binaries/DbImport/testsData/expectedDbImportTableDump.sql b/src/_binaries/DbImport/testsData/expectedDbImportTableDump.sql deleted file mode 100644 index 8291932a..00000000 --- a/src/_binaries/DbImport/testsData/expectedDbImportTableDump.sql +++ /dev/null @@ -1,42 +0,0 @@ -SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS = 0; -SET @OLD_AUTOCOMMIT=@@AUTOCOMMIT, AUTOCOMMIT = 0; -SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS = 0; --- MySQL dump 10.13 Distrib 8.0.22, for Linux (x86_64) --- --- Host: 127.0.0.1 Database: fromDb --- ------------------------------------------------------ --- Server version 8.0.21 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!50503 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Dumping data for table `emptyTable` --- - -DROP TABLE IF EXISTS `dataTable`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `dataTable` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `modification_date` datetime NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2011640 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - -LOCK TABLES `dataTable` WRITE; -/*!40000 ALTER TABLE `dataTable` DISABLE KEYS */; -INSERT INTO `dataTable` VALUES (1,1,'Picture:','en-GB'),(2,2,'Name:','en-GB'),(3,3,'First name:','en-GB'),(4,4,'Login:','en-GB'),(5,5,'Password:','en-GB'),(6,6,'E-mail:','en-GB'),(7,7,'Reference number:','en-GB'),(8,8,'Presentation:','en-GB'),(9,9,'Web:','en-GB'),(10,10,'Twitter:','en-GB'),(11,11,'LinkedIn:','en-GB'),(12,12,'Languages:','en-GB'),(13,13,'Time zone:','en-GB'); -/*!40000 ALTER TABLE `dataTable` ENABLE KEYS */; -UNLOCK TABLES; -SET AUTOCOMMIT=@OLD_AUTOCOMMIT; -SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; -SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; diff --git a/src/_binaries/DbImport/testsData/expectedDbImportTableDumpRenamed.sql b/src/_binaries/DbImport/testsData/expectedDbImportTableDumpRenamed.sql deleted file mode 100644 index 2818b786..00000000 --- a/src/_binaries/DbImport/testsData/expectedDbImportTableDumpRenamed.sql +++ /dev/null @@ -1,42 +0,0 @@ -SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS = 0; -SET @OLD_AUTOCOMMIT=@@AUTOCOMMIT, AUTOCOMMIT = 0; -SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS = 0; --- MySQL dump 10.13 Distrib 8.0.22, for Linux (x86_64) --- --- Host: 127.0.0.1 Database: fromDb --- ------------------------------------------------------ --- Server version 8.0.21 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!50503 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Dumping data for table `emptyTable` --- - -DROP TABLE IF EXISTS `newTableName`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `newTableName` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `modification_date` datetime NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2011640 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - -LOCK TABLES `newTableName` WRITE; -/*!40000 ALTER TABLE `newTableName` DISABLE KEYS */; -INSERT INTO `newTableName` VALUES (1,1,'Picture:','en-GB'),(2,2,'Name:','en-GB'),(3,3,'First name:','en-GB'),(4,4,'Login:','en-GB'),(5,5,'Password:','en-GB'),(6,6,'E-mail:','en-GB'),(7,7,'Reference number:','en-GB'),(8,8,'Presentation:','en-GB'),(9,9,'Web:','en-GB'),(10,10,'Twitter:','en-GB'),(11,11,'LinkedIn:','en-GB'),(12,12,'Languages:','en-GB'),(13,13,'Time zone:','en-GB'); -/*!40000 ALTER TABLE `newTableName` ENABLE KEYS */; -UNLOCK TABLES; -SET AUTOCOMMIT=@OLD_AUTOCOMMIT; -SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; -SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; diff --git a/src/_binaries/DbImport/testsData/gawk b/src/_binaries/DbImport/testsData/gawk deleted file mode 100755 index aed42815..00000000 --- a/src/_binaries/DbImport/testsData/gawk +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash -if [[ "$1" = "--version" ]]; then - echo "GNU Awk 5.1.1, API: 2.0 (GNU MPFR 4.1.0, GNU MP 6.2.0)" -else - echo "$( Date: Sun, 1 Sep 2024 17:51:19 +0200 Subject: [PATCH 08/24] compiled dbQueryAllDatabases using go compiler --- .pre-commit-config-github.yaml | 2 +- .pre-commit-config.yaml | 2 +- .vscode/launch.json | 7 + bin/dbImport | 9 +- bin/dbImportProfile | 19 +- bin/dbImportStream | 9 +- bin/dbQueryAllDatabases | 3369 +++++++++-------- bin/doc | 5 +- bin/install | 5 +- bin/installRequirements | 5 +- bin/mysql2puml | 7 +- .../mysql2puml/testsData/mysql2puml.help.txt | 3 +- .../dbImport/testsData/dbImport.help.txt | 2 +- .../dbImportProfile-options.sh | 12 +- .../testsData/dbImportProfile.help.txt | 9 +- .../testsData/dbImportStream.help.txt | 3 +- .../binary-dbQueryAllDatabases.yaml | 68 + .../dbQueryAllDatabases-main.sh | 39 + .../dbQueryAllDatabases-options.sh | 106 + .../dbQueryAllDatabases.awk | 1 + .../dbQueryAllDatabases.bats | 13 +- .../testsData/databaseSize.envProvided.sh | 0 .../testsData/databaseSize.result_db1 | 0 .../testsData/databaseSize.result_db2 | 0 .../testsData/dbQueryAllDatabases.help.txt | 102 + .../testsData/dbQueryAllDatabases.result | 0 .../testsData/dsn_local.env | 0 .../testsData/dsn_valid.env | 0 .../dbQueryAllDatabases}/testsData/gawk | 0 .../testsData/getUserDbList.query | 0 .../testsData/getUserDbList.result | 0 .../dbQueryAllDatabases}/testsData/parallel | 0 .../dbQueryAllDatabases}/testsData/pv | 0 .../dbQueryAllDatabases.options.tpl | 117 - .../dbQueryAllDatabases.sh | 69 - .../testsData/dbQueryAllDatabases.help.txt | 97 - .../commandDefinitions/optionsJobs.sh | 12 + .../commandDefinitions/optionsJobs.yaml | 21 + .../commandDefinitions/optionsProgressBar.sh | 7 + .../optionsProgressBar.yaml | 21 + src/_binaries/options/options.jobs.tpl | 32 - src/_binaries/options/options.progressBar.tpl | 26 - 42 files changed, 2289 insertions(+), 1910 deletions(-) create mode 100644 src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml create mode 100755 src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-main.sh create mode 100755 src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-options.sh rename src/_binaries/{DbQueryAllDatabases => Database/dbQueryAllDatabases}/dbQueryAllDatabases.awk (95%) rename src/_binaries/{DbQueryAllDatabases => Database/dbQueryAllDatabases}/dbQueryAllDatabases.bats (85%) rename src/_binaries/{DbQueryAllDatabases => Database/dbQueryAllDatabases}/testsData/databaseSize.envProvided.sh (100%) rename src/_binaries/{DbQueryAllDatabases => Database/dbQueryAllDatabases}/testsData/databaseSize.result_db1 (100%) rename src/_binaries/{DbQueryAllDatabases => Database/dbQueryAllDatabases}/testsData/databaseSize.result_db2 (100%) create mode 100644 src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt rename src/_binaries/{DbQueryAllDatabases => Database/dbQueryAllDatabases}/testsData/dbQueryAllDatabases.result (100%) rename src/_binaries/{DbQueryAllDatabases => Database/dbQueryAllDatabases}/testsData/dsn_local.env (100%) rename src/_binaries/{DbQueryAllDatabases => Database/dbQueryAllDatabases}/testsData/dsn_valid.env (100%) rename src/_binaries/{DbQueryAllDatabases => Database/dbQueryAllDatabases}/testsData/gawk (100%) rename src/_binaries/{DbQueryAllDatabases => Database/dbQueryAllDatabases}/testsData/getUserDbList.query (100%) rename src/_binaries/{DbQueryAllDatabases => Database/dbQueryAllDatabases}/testsData/getUserDbList.result (100%) rename src/_binaries/{DbQueryAllDatabases => Database/dbQueryAllDatabases}/testsData/parallel (100%) rename src/_binaries/{DbQueryAllDatabases => Database/dbQueryAllDatabases}/testsData/pv (100%) delete mode 100644 src/_binaries/DbQueryAllDatabases/dbQueryAllDatabases.options.tpl delete mode 100755 src/_binaries/DbQueryAllDatabases/dbQueryAllDatabases.sh delete mode 100644 src/_binaries/DbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt create mode 100755 src/_binaries/commandDefinitions/optionsJobs.sh create mode 100644 src/_binaries/commandDefinitions/optionsJobs.yaml create mode 100755 src/_binaries/commandDefinitions/optionsProgressBar.sh create mode 100644 src/_binaries/commandDefinitions/optionsProgressBar.yaml delete mode 100644 src/_binaries/options/options.jobs.tpl delete mode 100644 src/_binaries/options/options.progressBar.tpl diff --git a/.pre-commit-config-github.yaml b/.pre-commit-config-github.yaml index 84b5c69e..3896717e 100644 --- a/.pre-commit-config-github.yaml +++ b/.pre-commit-config-github.yaml @@ -205,6 +205,6 @@ repos: stages: [manual] # GITHUB - repo: https://github.com/fchastanet/bash-compiler - rev: 0.3.2 + rev: 0.3.3 hooks: - id: buildBashBinaries diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 76771297..96d577df 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -200,6 +200,6 @@ repos: stages: [] # GITHUB - repo: https://github.com/fchastanet/bash-compiler - rev: 0.3.2 + rev: 0.3.3 hooks: - id: buildBashBinaries diff --git a/.vscode/launch.json b/.vscode/launch.json index 23c1c722..f4f34909 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,6 +10,13 @@ "name": "Bash-Debug (generic)", "program": "${file}" }, + { + "type": "bashdb", + "request": "launch", + "name": "dbQueryAllDatabases", + "program": "bin/dbQueryAllDatabases", + "args": ["-f", "ckls.local"] + }, { "type": "bashdb", "request": "launch", diff --git a/bin/dbImport b/bin/dbImport index 34e5a8fa..6d324390 100755 --- a/bin/dbImport +++ b/bin/dbImport @@ -2522,8 +2522,9 @@ dbImportCommandParse() { # @description display command options and arguments help for dbImportCommand dbImportCommandHelp() { - Array::wrap2 ' ' 80 0 "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" \ - "Import source db into target db using eventual table filter." + echo -e "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" + Array::wrap2 ' ' 76 4 " " "Import source db into target db using eventual table filter." + echo echo @@ -2549,11 +2550,11 @@ dbImportCommandHelp() { Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}fromDbName${__HELP_NORMAL} {single} (mandatory) " - Array::wrap2 ' ' 76 4 " " "The name of the source/remote database." + Array::wrap2 ' ' 76 4 " " "The name of the source/remote database." echo Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}${__HELP_NORMAL} {single}] " - Array::wrap2 ' ' 76 4 " " "The name of the target database" "Default value: (without extension)" "" + Array::wrap2 ' ' 76 4 " " "The name of the target database" "Default value: (without extension)" "" echo # ------------------------------------------ # options section diff --git a/bin/dbImportProfile b/bin/dbImportProfile index 3977c09c..5ffd591d 100755 --- a/bin/dbImportProfile +++ b/bin/dbImportProfile @@ -1632,24 +1632,24 @@ longDescriptionFunction() { optionProfileHelpFunction() { Array::wrap2 " " 80 4 \ - " The name of the profile to write in profiles directory.\r" \ + " The name of the profile to write in profiles directory.\n" \ "If not provided, the file name pattern will be 'auto__.sh'" echo } optionFromDsnHelpFunction() { Array::wrap2 " " 80 4 \ - " dsn to use for source database (Default: ${defaultFromDsn})\r" \ + " dsn to use for source database (Default: ${defaultFromDsn})\n" \ "if not provided, the file name pattern will be 'auto__.sh'" echo } optionRatioHelpFunction() { Array::wrap2 " " 80 4 \ - " define the ratio to use (0 to 100% - default 70).\r" \ - "- 0 means profile will filter out all the tables\r" \ - "- 100 means profile will keep all the tables.\r" \ - "Eg: 70 means that tables with size(table+index)\r" \ + " define the ratio to use (0 to 100% - default 70).\n" \ + "- 0 means profile will filter out all the tables\n" \ + "- 100 means profile will keep all the tables.\n" \ + "Eg: 70 means that tables with size(table+index)\n" \ "that are greater than 70% of the max table size will be excluded." echo } @@ -2199,8 +2199,9 @@ dbImportProfileCommandParse() { # @description display command options and arguments help for dbImportProfileCommand dbImportProfileCommandHelp() { - Array::wrap2 ' ' 80 0 "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" \ - "Generate optimized profiles to be used by dbImport." + echo -e "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" + Array::wrap2 ' ' 76 4 " " "Generate optimized profiles to be used by dbImport." + echo echo @@ -2226,7 +2227,7 @@ dbImportProfileCommandHelp() { Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}fromDbName${__HELP_NORMAL} {single} (mandatory) " - Array::wrap2 ' ' 76 4 " " "The name of the source/remote database." + Array::wrap2 ' ' 76 4 " " "The name of the source/remote database." echo # ------------------------------------------ # options section diff --git a/bin/dbImportStream b/bin/dbImportStream index 61577f2e..4f65ab2b 100755 --- a/bin/dbImportStream +++ b/bin/dbImportStream @@ -2300,8 +2300,9 @@ dbImportStreamCommandParse() { # @description display command options and arguments help for dbImportStreamCommand dbImportStreamCommandHelp() { - Array::wrap2 ' ' 80 0 "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" \ - "Stream tar.gz file or gz file through mysql." + echo -e "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" + Array::wrap2 ' ' 76 4 " " "Stream tar.gz file or gz file through mysql." + echo echo @@ -2327,11 +2328,11 @@ dbImportStreamCommandHelp() { Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}argDumpFile${__HELP_NORMAL} {single} (mandatory) " - Array::wrap2 ' ' 76 4 " " "The file that will be streamed through mysql." + Array::wrap2 ' ' 76 4 " " "The file that will be streamed through mysql." echo Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}argTargetDbName${__HELP_NORMAL} {single} (mandatory) " - Array::wrap2 ' ' 76 4 " " "The name of the mysql target database." + Array::wrap2 ' ' 76 4 " " "The name of the mysql target database." echo # ------------------------------------------ # options section diff --git a/bin/dbQueryAllDatabases b/bin/dbQueryAllDatabases index e09b6a08..073f4a64 100755 --- a/bin/dbQueryAllDatabases +++ b/bin/dbQueryAllDatabases @@ -1,13 +1,12 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FACADE FROM https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/DbQueryAllDatabases/dbQueryAllDatabases.sh +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml # DO NOT EDIT IT # @generated ############################################################################### # shellcheck disable=SC2288,SC2034 -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/dbQueryAllDatabases -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE + + # ensure that no user aliases could interfere with # commands used in this script @@ -62,13 +61,6 @@ interruptManagement() { kill -s INT "$$" } trap interruptManagement INT -SCRIPT_NAME=${0##*/} -REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" -if [[ -n "${EMBED_CURRENT_DIR}" ]]; then - CURRENT_DIR="${EMBED_CURRENT_DIR}" -else - CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" -fi ################################################ # Temp dir management @@ -102,6 +94,20 @@ cleanOnExit() { } trap cleanOnExit EXIT HUP QUIT ABRT TERM + +SCRIPT_NAME=${0##*/} +REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" +if [[ -n "${EMBED_CURRENT_DIR}" ]]; then + CURRENT_DIR="${EMBED_CURRENT_DIR}" +else + CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" +fi +FRAMEWORK_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" +FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" +FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + # @description Log namespace provides 2 kind of functions # - Log::display* allows to display given message with # given display level @@ -132,505 +138,296 @@ export __VERBOSE_LEVEL_DEBUG=2 # @description verbose level info export __VERBOSE_LEVEL_TRACE=3 -# @description Display message using info color (bg light blue/fg white) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayInfo() { - local type="${2:-INFO}" - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logInfo "$1" "${type}" -} -# @description Display message using debug color (gray) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayDebug() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then - Log::computeDuration - echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 +# @description concatenate each element of an array with a separator +# but wrapping text when line length is more than provided argument +# The algorithm will try not to cut the array element if it can. +# - if an arg can be placed on current line it will be, +# otherwise current line is printed and arg is added to the new +# current line +# - Empty arg is interpreted as a new line. +# - Add \r to arg in order to force break line and avoid following +# arg to be concatenated with current arg. +# +# @arg $1 glue:String +# @arg $2 maxLineLength:int +# @arg $3 indentNextLine:int +# @arg $@ array:String[] +Array::wrap2() { + local glue="${1-}" + local -i glueLength="${#glue}" + shift || true + local -i maxLineLength=$1 + shift || true + local -i indentNextLine=$1 + shift || true + local indentStr="" + if ((indentNextLine > 0)); then + indentStr="$(head -c "${indentNextLine}" 0)); do + arg="$1" + shift || true - # shellcheck disable=SC2154 - if ! Database::query dbInstance "${query}" "${db}" | sed "s/\t/${optionSeparator}/g"; then - Log::fatal "database ${db} error" 1>&2 - fi + # replace tab by 2 spaces + arg="${arg//$'\t'/ }" + # remove trailing spaces + arg="${arg%[[:blank:]]}" + if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then + printCurrentLine + ((previousLineEmpty = 1)) + continue + else + if ((previousLineEmpty == 1)); then + printCurrentLine + fi + ((previousLineEmpty = 0)) || true + fi + # convert eol to args + mapfile -t additionalLines <<<"${arg}" + if ((${#additionalLines[@]} > 1)); then + set -- "${additionalLines[@]}" "$@" + continue + fi + + ((argLength = ${#arg})) || true + + # empty arg + if ((argLength == 0)); then + if ((isNewline == 0)); then + # isNewline = 0 means currentLine is not empty + printCurrentLine + fi + continue + fi + + if ((isNewline == 0)); then + glueLength="${#glue}" + else + glueLength="0" + fi + if ((currentLineLength + argLength + glueLength > maxLineLength)); then + if ((argLength + glueLength > maxLineLength)); then + # arg is too long to even fit on one line + # we have to split the arg on current and next line + local -i remainingLineLength + ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) + appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" + printCurrentLine + arg="${arg:${remainingLineLength}}" + # remove leading spaces + arg="${arg##[[:blank:]]}" + + set -- "${arg}" "$@" + else + # the arg can fit on next line + printCurrentLine + appendToCurrentLine "${arg}" "${argLength}" + fi + else + appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" + fi + done + if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then + printCurrentLine + fi + ) | sed -E -e 's/[[:blank:]]+$//' } -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logWarning "$1" + +# @description check if command specified exists or return 1 +# with error and message if not +# +# @arg $1 commandName:String on which existence must be checked +# @arg $2 helpIfNotExists:String a help command to display if the command does not exist +# +# @exitcode 1 if the command specified does not exist +# @stderr diagnostic information + help if second argument is provided +Assert::commandExists() { + local commandName="$1" + local helpIfNotExists="$2" + + "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { + Log::displayError "${commandName} is not installed, please install it" + if [[ -n "${helpIfNotExists}" ]]; then + Log::displayInfo "${helpIfNotExists}" + fi + return 1 + } + return 0 } -# @description Display message using error color (red) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayError() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then - Log::computeDuration - echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description check if tty (interactive mode) is active +# @noargs +# @exitcode 1 if tty not active +# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive +# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive +Assert::tty() { + if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then + return 1 fi - Log::logError "$1" + if [[ "${INTERACTIVE:-0}" = "1" ]]; then + return 0 + fi + tty -s } -# @description load colors theme constants -# @warning if tty not opened, noColor theme will be chosen -# @arg $1 theme:String the theme to use (default, noColor) -# @arg $@ args:String[] -# @set __ERROR_COLOR String indicate error status -# @set __INFO_COLOR String indicate info status -# @set __SUCCESS_COLOR String indicate success status -# @set __WARNING_COLOR String indicate warning status -# @set __SKIPPED_COLOR String indicate skipped status -# @set __DEBUG_COLOR String indicate debug status -# @set __HELP_COLOR String indicate help status -# @set __TEST_COLOR String not used -# @set __TEST_ERROR_COLOR String not used -# @set __HELP_TITLE_COLOR String used to display help title in help strings -# @set __HELP_OPTION_COLOR String used to display highlight options in help strings -# -# @set __RESET_COLOR String reset default color -# -# @set __HELP_EXAMPLE String to remove -# @set __HELP_TITLE String to remove -# @set __HELP_NORMAL String to remove -# shellcheck disable=SC2034 -UI::theme() { - local theme="${1-default}" - if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then - theme="noColor" - fi - case "${theme}" in - default | default-force) - theme="default" - ;; - noColor) ;; - *) - Log::fatal "invalid theme provided" - ;; - esac - if [[ "${theme}" = "default" ]]; then - BASH_FRAMEWORK_THEME="default" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='\e[31m' # Red - __INFO_COLOR='\e[44m' # white on lightBlue - __SUCCESS_COLOR='\e[32m' # Green - __WARNING_COLOR='\e[33m' # Yellow - __SKIPPED_COLOR='\e[33m' # Yellow - __DEBUG_COLOR='\e[37m' # Gray - __HELP_COLOR='\e[7;49;33m' # Black on Gold - __TEST_COLOR='\e[100m' # Light magenta - __TEST_ERROR_COLOR='\e[41m' # white on red - __HELP_TITLE_COLOR="\e[1;37m" # Bold - __HELP_OPTION_COLOR="\e[1;34m" # Blue - # Internal: reset color - __RESET_COLOR='\e[0m' # Reset Color - # shellcheck disable=SC2155,SC2034 - __HELP_EXAMPLE="$(echo -e "\e[2;97m")" - # shellcheck disable=SC2155,SC2034 - __HELP_TITLE="$(echo -e "\e[1;37m")" - # shellcheck disable=SC2155,SC2034 - __HELP_NORMAL="$(echo -e "\033[0m")" - else - BASH_FRAMEWORK_THEME="noColor" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='' - __INFO_COLOR='' - __SUCCESS_COLOR='' - __WARNING_COLOR='' - __SKIPPED_COLOR='' - __DEBUG_COLOR='' - __HELP_COLOR='' - __TEST_COLOR='' - __TEST_ERROR_COLOR='' - __HELP_TITLE_COLOR='' - __HELP_OPTION_COLOR='' - # Internal: reset color - __RESET_COLOR='' - __HELP_EXAMPLE='' - __HELP_TITLE='' - __HELP_NORMAL='' - fi -} - -# @description draw a line with the character passed in parameter repeated depending on terminal width -# @arg $1 character:String character to use as separator (default value #) -UI::drawLine() { - local character="${1:-#}" - local -i width=${COLUMNS:-0} - if ((width == 0)) && [[ -t 1 ]]; then - width=$(tput cols) - fi - if ((width == 0)); then - width=80 - fi - printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") - echo -} - -# @description Display message using error color (red) and exit immediately with error status 1 -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::fatal() { - Log::computeDuration - echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - Log::logFatal "$1" - exit 1 -} - -# @description create a temp file using default TMPDIR variable -# initialized in _includes/_commonHeader.sh -# @env TMPDIR String (default value /tmp) -# @arg $1 templateName:String template name to use(optional) -Framework::createTempFile() { - mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" -} - -# @description ensure env files are loaded -# @arg $@ list of default files to load at the end -# @exitcode 1 if one of env files fails to load -# @stderr diagnostics information is displayed -# shellcheck disable=SC2120 -Env::requireLoad() { - local -a defaultFiles=("$@") - # get list of possible config files - local -a configFiles=() - if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then - # BASH_FRAMEWORK_ENV_FILES is an array - configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") - fi - local localFrameworkConfigFile - localFrameworkConfigFile="$(pwd)/.framework-config" - if [[ -f "${localFrameworkConfigFile}" ]]; then - configFiles+=("${localFrameworkConfigFile}") - fi - if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then - configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") - fi - configFiles+=("${optionEnvFiles[@]}") - configFiles+=("${defaultFiles[@]}") - - for file in "${configFiles[@]}"; do - # shellcheck source=/.framework-config - CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { - Log::displayError "while loading config file: ${file}" - return 1 - } - done -} - -# @description activate or not Log::display* and Log::log* functions -# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL -# environment variables loaded by Env::requireLoad -# try to create log file and rotate it if necessary -# @noargs -# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable -# @env BASH_FRAMEWORK_DISPLAY_LEVEL int -# @env BASH_FRAMEWORK_LOG_LEVEL int -# @env BASH_FRAMEWORK_LOG_FILE String -# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 -# @exitcode 0 always successful -# @stderr diagnostics information about log file is displayed -# @require Env::requireLoad -# @require UI::requireTheme -Log::requireLoad() { - if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - export BASH_FRAMEWORK_LOG_LEVEL - fi - - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then - if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 - fi - elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - fi - - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - # will always be created even if not in info level - Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" - if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then - Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" - fi - fi -} - -# @description concatenate each element of an array with a separator -# but wrapping text when line length is more than provided argument -# The algorithm will try not to cut the array element if it can. -# - if an arg can be placed on current line it will be, -# otherwise current line is printed and arg is added to the new -# current line -# - Empty arg is interpreted as a new line. -# - Add \r to arg in order to force break line and avoid following -# arg to be concatenated with current arg. -# -# @arg $1 glue:String -# @arg $2 maxLineLength:int -# @arg $3 indentNextLine:int -# @arg $@ array:String[] -Array::wrap2() { - local glue="${1-}" - local -i glueLength="${#glue}" - shift || true - local -i maxLineLength=$1 - shift || true - local -i indentNextLine=$1 - shift || true - local indentStr="" - if ((indentNextLine > 0)); then - indentStr="$(head -c "${indentNextLine}" 0)); do - arg="$1" - shift || true - - # replace tab by 2 spaces - arg="${arg//$'\t'/ }" - # remove trailing spaces - arg="${arg%[[:blank:]]}" - if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then - printCurrentLine - ((previousLineEmpty = 1)) - continue - else - if ((previousLineEmpty == 1)); then - printCurrentLine - fi - ((previousLineEmpty = 0)) || true - fi - # convert eol to args - mapfile -t additionalLines <<<"${arg}" - if ((${#additionalLines[@]} > 1)); then - set -- "${additionalLines[@]}" "$@" - continue - fi - - ((argLength = ${#arg})) || true - - # empty arg - if ((argLength == 0)); then - if ((isNewline == 0)); then - # isNewline = 0 means currentLine is not empty - printCurrentLine - fi - continue - fi - - if ((isNewline == 0)); then - glueLength="${#glue}" - else - glueLength="0" - fi - if ((currentLineLength + argLength + glueLength > maxLineLength)); then - if ((argLength + glueLength > maxLineLength)); then - # arg is too long to even fit on one line - # we have to split the arg on current and next line - local -i remainingLineLength - ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) - appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" - printCurrentLine - arg="${arg:${remainingLineLength}}" - # remove leading spaces - arg="${arg##[[:blank:]]}" - set -- "${arg}" "$@" - else - # the arg can fit on next line - printCurrentLine - appendToCurrentLine "${arg}" "${argLength}" - fi - else - appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" - fi - done - if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then - printCurrentLine +# @description ignore exit code 141 from simple command pipes +# @example use with: +# local resultingStatus=0 +# local -a originalPipeStatus=() +# cmd1 | cmd2 || Bash::handlePipelineFailure resultingStatus originalPipeStatus || true +# [[ "${resultingStatus}" = "0" ]] +# @arg $1 resultingStatusCode:&int (passed by reference) (optional) resulting status code +# @arg $2 originalStatus:int[] (passed by reference) (optional) copy of original PIPESTATUS array +# @env PIPESTATUS assuming that this function is called like in the example provided +# @see https://unix.stackexchange.com/a/709880/582856 +Bash::handlePipelineFailure() { + local -a pipeStatusBackup=("${PIPESTATUS[@]}") + local -n handlePipelineFailure_resultingStatusCode=$1 + local -n handlePipelineFailure_originalStatus=$2 + # shellcheck disable=SC2034 + handlePipelineFailure_originalStatus=("${pipeStatusBackup[@]}") + handlePipelineFailure_resultingStatusCode=0 + local statusCode + for statusCode in "${pipeStatusBackup[@]}"; do + if ((statusCode == 141)); then + return 0 + elif ((statusCode > 0)); then + # shellcheck disable=SC2034 + handlePipelineFailure_resultingStatusCode="${statusCode}" + break fi - ) | sed -E -e 's/[[:blank:]]+$//' + done + return "${handlePipelineFailure_resultingStatusCode}" } -# @description check if command specified exists or return 1 -# with error and message if not -# -# @arg $1 commandName:String on which existence must be checked -# @arg $2 helpIfNotExists:String a help command to display if the command does not exist -# -# @exitcode 1 if the command specified does not exist -# @stderr diagnostic information + help if second argument is provided -Assert::commandExists() { - local commandName="$1" - local helpIfNotExists="$2" - - "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { - Log::displayError "${commandName} is not installed, please install it" - if [[ -n "${helpIfNotExists}" ]]; then - Log::displayInfo "${helpIfNotExists}" - fi - return 1 - } - return 0 -} -# @description Check that command version is greater than expected minimal version -# display warning if command version greater than expected minimal version -# display error if command version less than expected minimal version and exit 1 -# @arg $1 commandName:String command path -# @arg $2 argVersion:String command line parameters to launch to get command version -# @arg $3 minimalVersion:String expected minimal command version -# @arg $4 parseVersionCallback:Function -# @arg $5 help:String optional help message to display if command does not exist -# @exitcode 0 if command version greater or equal to expected minimal version -# @exitcode 1 if command version less than expected minimal version -# @exitcode 2 if command does not exist -Version::checkMinimal() { - local commandName="$1" - local argVersion="$2" - local minimalVersion="$3" - local parseVersionCallback=${4:-Version::parse} - local help="${5:-}" +# @description loads ~/.bash-tools/.env if available +# if not creates it from a default template +# else check if new options need to be added +BashTools::Conf::requireLoad() { +Linux::requireTarCommand +Compiler::Embed::extractFileFromBase64 \ + "${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" \ + "IyEvdXNyL2Jpbi9lbnYgYmFzaAojIHNoZWxsY2hlY2sgZGlzYWJsZT1TQzIwMzQKCiMgRGVmYXVsdCBzZXR0aW5ncwojIHlvdSBjYW4gb3ZlcnJpZGUgdGhlc2Ugc2V0dGluZ3MgYnkgY3JlYXRpbmcgJHtIT01FfS8uYmFzaC10b29scy8uZW52IGZpbGUKCiMjIwojIyMgRElTUExBWSBMZXZlbAojIyMgbWluaW11bSBsZXZlbCBvZiB0aGUgbWVzc2FnZXMgdGhhdCB3aWxsIGJlIGRpc3BsYXllZCBvbiBzY3JlZW4KIyMjCiMjIyAwOiBOTyBMT0cKIyMjIDE6IEVSUk9SCiMjIyAyOiBXQVJOSU5HCiMjIyAzOiBJTkZPCiMjIyA0OiBERUJVRwojIyMKQkFTSF9GUkFNRVdPUktfRElTUExBWV9MRVZFTD0ke0JBU0hfRlJBTUVXT1JLX0RJU1BMQVlfTEVWRUw6LTN9CgojIyMKIyMjIERJU1BMQVkgZHVyYXRpb24KIyMjIDA6IG5vIGR1cmF0aW9uIGlzIGRpc3BsYXllZCBvbiB0aGUgbWVzc2FnZXMKIyMjIDE6IGR1cmF0aW9uIGJldHdlZW4gcHJldmlvdXMgbWVzc2FnZSBhbmQgY3VycmVudCBpcyBkaXNwbGF5ZWQKIyMjIHdpdGggdGhlIG1lc3NhZ2UKIyMjCkRJU1BMQVlfRFVSQVRJT049JHtESVNQTEFZX0RVUkFUSU9OOjB9CgojIyMKIyMjIExvZyB0byBmaWxlCiMjIwojIyMgYWxsIGxvZyBtZXNzYWdlcyB3aWxsIGJlIHJlZGlyZWN0ZWQgdG8gbG9nIGZpbGUgc3BlY2lmaWVkCiMjIyB0aGlzIHNhbWUgcGF0aCB3aWxsIGJlIHVzZWQgaW5zaWRlIGFuZCBvdXRzaWRlIG9mIHRoZSBjb250YWluZXIKIyMjCkJBU0hfRlJBTUVXT1JLX0xPR19GSUxFPSR7QkFTSF9GUkFNRVdPUktfTE9HX0ZJTEU6LSR7RlJBTUVXT1JLX1JPT1RfRElSfS9sb2dzL2Jhc2gubG9nfQoKIyMjCiMjIyBMT0cgTGV2ZWwKIyMjIG1pbmltdW0gbGV2ZWwgb2YgdGhlIG1lc3NhZ2VzIHRoYXQgd2lsbCBiZSBsb2dnZWQgaW50byBMT0dfRklMRQojIyMKIyMjIDA6IE5PIExPRwojIyMgMTogRVJST1IKIyMjIDI6IFdBUk5JTkcKIyMjIDM6IElORk8KIyMjIDQ6IERFQlVHCiMjIwpCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw9JHtCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw6LTB9CgojIGFic29sdXRlIGRpcmVjdG9yeSBjb250YWluaW5nIGRiIGltcG9ydCBzcWwgZHVtcHMKREJfSU1QT1JUX0RVTVBfRElSPSR7REJfSU1QT1JUX0RVTVBfRElSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2RiSW1wb3J0RHVtcHN9CgojIGdhcmJhZ2UgY29sbGVjdCBhbGwgZmlsZXMgZm9yIHdoaWNoIG1vZGlmaWNhdGlvbiBpcyBncmVhdGVyIHRoYW4gZWc6IDMwIGRheXMgKCszMCkKIyBlYWNoIHRpbWUgYW4gZXhpc3RpbmcgZmlsZSBpcyB1c2VkIGJ5IGRiSW1wb3J0L2RiSW1wb3J0VGFibGUKIyB0aGUgZmlsZSBtb2RpZmljYXRpb24gdGltZSBpcyBzZXQgdG8gbm93CkRCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUz0ke0RCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUzotKzMwfQoKIyBhYnNvbHV0ZSBkaXJlY3RvcnkgY29udGFpbmluZyBkYlNjcmlwdHMgdXNlZCBieSBkYlNjcmlwdEFsbERhdGFiYXNlcwpTQ1JJUFRTX0ZPTERFUj0ke1NDUklQVFNfRk9MREVSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2NvbmYvZGJTY3JpcHRzfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEFXUyBQYXJhbWV0ZXJzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KUzNfQkFTRV9VUkw9JHtTM19CQVNFX1VSTDotfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvc3RtYW4gUGFyYW1ldGVycwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClBPU1RNQU5fQVBJX0tFWT0K" \ + "755" - Assert::commandExists "${commandName}" "${help}" || return 2 +declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" + BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" + if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then + FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" + else + # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR + FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" + fi # shellcheck disable=SC2034 - local status=0 + FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" # shellcheck disable=SC2034 - local -a pipeStatus=() - local version - version="$("${commandName}" "${argVersion}" 2>&1 | ${parseVersionCallback} || Bash::handlePipelineFailure status pipeStatus)" + FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" - Log::displayDebug "check ${commandName} version ${version} against minimal ${minimalVersion}" + if [[ -f "${HOME}/.bash-tools/.env" ]]; then + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") + fi - Version::compare "${version}" "${minimalVersion}" || { - local result=$? - if [[ "${result}" = "1" ]]; then - Log::displayInfo "${commandName} version is ${version} greater than ${minimalVersion}" - elif [[ "${result}" = "2" ]]; then - Log::displayError "${commandName} minimal version is ${minimalVersion}, your version is ${version}" - return 1 + local envFile="${HOME}/.bash-tools/.env" + if [[ ! -f "${envFile}" ]]; then + mkdir -p "${HOME}/.bash-tools" + ( + echo "#!/usr/bin/env bash" + # shellcheck disable=SC2154 + echo "${embed_file_bashToolsDefaultConfigTemplate}" + ) >"${envFile}" + Log::displayInfo "Configuration file '${envFile}' created" + else + if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then + ( + echo '# -----------------------------------------------------' + echo '# Postman Parameters' + echo '# -----------------------------------------------------' + echo 'POSTMAN_API_KEY=' + ) >>"${envFile}" fi - return 0 + fi + # shellcheck source=/conf/.env + source "${envFile}" || { + Log::displayError "impossible to load '${envFile}'" + exit 1 } - } -# @description list the conf files list available in bash-tools/conf/ folder -# and those overridden in ${HOME}/.bash-tools/ folder -# -# @arg $1 confFolder:String the directory name (not the path) to list -# @arg $2 extension:String the extension (.sh by default) -# @arg $3 indentStr:String the indentation (' - ' by default) can be any string compatible with sed not containing any / -# -# @stdout list of files without extension/directory -# @example text -# - default.local -# - default.remote -# - localhost-root -Conf::getMergedList() { - local confFolder="$1" - local extension="${2-sh}" - local indentStr="${3- - }" - - local DEFAULT_CONF_DIR="${FRAMEWORK_ROOT_DIR}/conf/${confFolder}" - local HOME_CONF_DIR="${HOME}/.bash-tools/${confFolder}" - ( - if [[ -d "${DEFAULT_CONF_DIR}" ]]; then - Conf::list "${DEFAULT_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" - fi - if [[ -d "${HOME_CONF_DIR}" ]]; then - Conf::list "${HOME_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" +# @description convert base64 encoded back to target file +# if target file is executable prepend dir of target +# file to PATH to make binary available everywhere +# it is advised to include in the path of the target file +# the md5sum of the binFile +# +# @arg $1 targetFile:String the file to write +# @arg $2 binFileBase64:String the base64 encoded file +# @arg $3 fileMode:String the chmod to set on the file +# @set PATH String prepend target embedded file binary directory to PATH variable if binary executable +Compiler::Embed::extractFileFromBase64() { + local targetFile="$1" + local binFileBase64="$2" + local fileMode="${3:-+x}" + local targetDir="${targetFile%/*}" + + if [[ ! -f "${targetFile}" ]]; then + if [[ ! -d "${targetDir}" ]]; then + mkdir -p "${targetDir}" fi - ) | sort | uniq + base64 -d >"${targetFile}" <<<"${binFileBase64}" + chmod "${fileMode}" "${targetFile}" + fi + + if [[ -x "${targetFile}" ]]; then + Env::pathPrepend "${targetDir}" + fi } + # @description get absolute conf file from specified conf folder deduced using these rules # * from absolute file (ignores and ) # * relative to where script is executed (ignores and ) @@ -686,12 +483,152 @@ Conf::getAbsoluteFile() { testAbs "$(File::concatenatePath "${FRAMEWORK_ROOT_DIR}/conf" "${confFolder}")/${conf}" && return 0 fi - # file not found - Log::displayError "conf file '${conf}' not found" + # file not found + Log::displayError "conf file '${conf}' not found" + + return 1 +} + + +# @description list the conf files list available in bash-tools/conf/ folder +# and those overridden in ${HOME}/.bash-tools/ folder +# +# @arg $1 confFolder:String the directory name (not the path) to list +# @arg $2 extension:String the extension (.sh by default) +# @arg $3 indentStr:String the indentation (' - ' by default) can be any string compatible with sed not containing any / +# +# @stdout list of files without extension/directory +# @example text +# - default.local +# - default.remote +# - localhost-root +Conf::getMergedList() { + local confFolder="$1" + local extension="${2-sh}" + local indentStr="${3- - }" + + local DEFAULT_CONF_DIR="${FRAMEWORK_ROOT_DIR}/conf/${confFolder}" + local HOME_CONF_DIR="${HOME}/.bash-tools/${confFolder}" + + ( + if [[ -d "${DEFAULT_CONF_DIR}" ]]; then + Conf::list "${DEFAULT_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" + fi + if [[ -d "${HOME_CONF_DIR}" ]]; then + Conf::list "${HOME_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" + fi + ) | sort | uniq +} + + +# @description list files of dir with given extension and display it as a list one by line +# +# @arg $1 dir:String the directory to list +# @arg $2 prefix:String the profile file prefix (default: "") +# @arg $3 ext:String the extension +# @arg $4 findOptions:String find options, eg: -type d (Default value: '-type f') +# @arg $5 indentStr:String the indentation can be any string compatible with sed not containing any / (Default value: ' - ') +# @stdout list of files without extension/directory +# @example text +# - default.local +# - default.remote +# - localhost-root +# @exitcode 1 if directory does not exists +Conf::list() { + local dir="$1" + local prefix="${2:-}" + local ext="${3}" + local findOptions="${4--type f}" + local indentStr="${5- - }" + + if [[ ! -d "${dir}" ]]; then + Log::displayError "Directory ${dir} does not exist" + fi + if [[ -n "${ext}" && "${ext:0:1}" != "." ]]; then + ext=".${ext}" + fi + ( + # shellcheck disable=SC2086 + cd "${dir}" && + find . -maxdepth 1 ${findOptions} -name "${prefix}*${ext}" | + sed -E "s#^\./${prefix}##g" | + sed -E "s#${ext}\$##g" | sort | sed -E "s#^#${indentStr}#" + ) +} + + +# @description check if dsn file has all the mandatory variables set +# Mandatory variables are: HOSTNAME, USER, PASSWORD, PORT +# +# @arg $1 dsnFileName:String dsn absolute filename +# @set HOSTNAME loaded from dsn file +# @set PORT loaded from dsn file +# @set USER loaded from dsn file +# @set PASSWORD loaded from dsn file +# @exitcode 0 on valid file +# @exitcode 1 if one of the properties of the conf file is invalid or if file not found +# @stderr log output if error found in conf file +Database::checkDsnFile() { + local dsnFileName="$1" + if [[ ! -f "${dsnFileName}" ]]; then + Log::displayError "dsn file ${dsnFileName} not found" + return 1 + fi + + ( + unset HOSTNAME PORT PASSWORD USER + # shellcheck source=/src/Database/testsData/dsn_valid.env + source "${dsnFileName}" + if [[ -z ${HOSTNAME+x} ]]; then + Log::displayError "dsn file ${dsnFileName} : HOSTNAME not provided" + return 1 + fi + if [[ -z "${HOSTNAME}" ]]; then + Log::displayWarning "dsn file ${dsnFileName} : HOSTNAME value not provided" + fi + if [[ "${HOSTNAME}" = "localhost" ]]; then + Log::displayWarning "dsn file ${dsnFileName} : check that HOSTNAME should not be 127.0.0.1 instead of localhost" + fi + if [[ -z "${PORT+x}" ]]; then + Log::displayError "dsn file ${dsnFileName} : PORT not provided" + return 1 + fi + if ! [[ ${PORT} =~ ^[0-9]+$ ]]; then + Log::displayError "dsn file ${dsnFileName} : PORT invalid" + return 1 + fi + if [[ -z "${USER+x}" ]]; then + Log::displayError "dsn file ${dsnFileName} : USER not provided" + return 1 + fi + if [[ -z "${PASSWORD+x}" ]]; then + Log::displayError "dsn file ${dsnFileName} : PASSWORD not provided" + return 1 + fi + ) +} - return 1 + +# @description databases's list of given mysql server +# +# @example text +# - information_schema +# - mysql +# - performance_schema +# - sys +# +# @arg $1 instanceUserDbList:&Map (passed by reference) database instance to use +# @stdout the list of db except mysql admin ones (see example) +# @exitcode * the query exit code +Database::getUserDbList() { + # shellcheck disable=SC2034 + local -n instanceUserDbList=$1 + # shellcheck disable=SC2016 + local sql='SELECT `schema_name` from INFORMATION_SCHEMA.SCHEMATA WHERE `schema_name` NOT IN("information_schema", "mysql", "performance_schema", "sys")' + Database::query instanceUserDbList "${sql}" } + # @description create a new db instance # Returns immediately if the instance is already initialized # @@ -744,12 +681,53 @@ Database::newInstance() { instanceNewInstance['SKIP_COLUMN_NAMES']="${SKIP_COLUMN_NAMES:-1}" instanceNewInstance['SSL_OPTIONS']="${MYSQL_SSL_OPTIONS:---ssl-mode=DISABLED}" instanceNewInstance['QUERY_OPTIONS']="${MYSQL_QUERY_OPTIONS:---batch --raw --default-character-set=utf8}" - instanceNewInstance['DUMP_OPTIONS']="${MYSQL_DUMP_OPTIONS:---default-character-set=utf8 --compress --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 ${instanceNewInstance['SSL_OPTIONS']}}" + instanceNewInstance['DUMP_OPTIONS']="${MYSQL_DUMP_OPTIONS:---default-character-set=utf8 --compression-algorithms --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 ${instanceNewInstance['SSL_OPTIONS']}}" instanceNewInstance['DB_IMPORT_OPTIONS']="${DB_IMPORT_OPTIONS:---connect-timeout=5 --batch --raw --default-character-set=utf8}" instanceNewInstance['INITIALIZED']=1 } + +# @description mysql query on a given db +# @warning could use QUERY_OPTIONS variable from dsn if defined +# @example +# cat file.sql | Database::query ... +# @arg $1 instanceQuery:&Map (passed by reference) database instance to use +# @arg $2 sqlQuery:String (optional) sql query or sql file to execute. if not provided or empty, the command can be piped +# @arg $3 dbName:String (optional) the db name +# +# @exitcode mysql command status code +Database::query() { + local -n instanceQuery=$1 + local -a mysqlCommand=() + local -a queryOptions + + mysqlCommand+=(mysql) + mysqlCommand+=("--defaults-extra-file=${instanceQuery['AUTH_FILE']}") + IFS=' ' read -r -a queryOptions <<<"${instanceQuery['QUERY_OPTIONS']}" + mysqlCommand+=("${queryOptions[@]}") + if [[ "${instanceQuery['SKIP_COLUMN_NAMES']}" = "1" ]]; then + mysqlCommand+=("-s" "--skip-column-names") + fi + # add optional db name + if [[ -n "${3+x}" ]]; then + mysqlCommand+=("$3") + fi + # add optional sql query + if [[ -n "${2+x}" && -n "$2" && ! -f "$2" ]]; then + mysqlCommand+=("-e") + mysqlCommand+=("$2") + fi + Log::displayDebug "$(printf "execute command: '%s'" "${mysqlCommand[*]}")" + + if [[ -f "$2" ]]; then + "${mysqlCommand[@]}" <"$2" + else + "${mysqlCommand[@]}" + fi +} + + # @description set the general options to use on mysql command to query the database # Differs than setOptions in the way that these options could change each time # @@ -761,134 +739,176 @@ Database::setQueryOptions() { instanceSetQueryOptions['QUERY_OPTIONS']="$2" } -# @description databases's list of given mysql server -# -# @example text -# - information_schema -# - mysql -# - performance_schema -# - sys + +# @description by default we skip the column names +# but sometimes we need column names to display some results +# disable this option temporarily and then restore it to true # -# @arg $1 instanceUserDbList:&Map (passed by reference) database instance to use -# @stdout the list of db except mysql admin ones (see example) -# @exitcode * the query exit code -Database::getUserDbList() { +# @arg $1 instanceSetQueryOptions:&Map (passed by reference) database instance to use +# @arg $2 skipColumnNames:Boolean 0 to disable, 1 to enable (hide column names) +Database::skipColumnNames() { + local -n instanceSkipColumnNames=$1 # shellcheck disable=SC2034 - local -n instanceUserDbList=$1 - # shellcheck disable=SC2016 - local sql='SELECT `schema_name` from INFORMATION_SCHEMA.SCHEMATA WHERE `schema_name` NOT IN("information_schema", "mysql", "performance_schema", "sys")' - Database::query instanceUserDbList "${sql}" + instanceSkipColumnNames['SKIP_COLUMN_NAMES']="$2" } -bashToolsDefaultConfigTemplate="${bashToolsDefaultConfigTemplate:-$( - cat <<'EOF' -# shellcheck disable=SC2034 -# Default settings -# you can override these settings by creating ${HOME}/.bash-tools/.env file - -### -### DISPLAY Level -### minimum level of the messages that will be displayed on screen -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} - -### -### DISPLAY duration -### 0: no duration is displayed on the messages -### 1: duration between previous message and current is displayed -### with the message -### -DISPLAY_DURATION=${DISPLAY_DURATION:0} - -### -### Log to file -### -### all log messages will be redirected to log file specified -### this same path will be used inside and outside of the container -### -BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} - -### -### LOG Level -### minimum level of the messages that will be logged into LOG_FILE -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} - -# absolute directory containing db import sql dumps -DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} - -# garbage collect all files for which modification is greater than eg: 30 days (+30) -# each time an existing file is used by dbImport/dbImportTable -# the file modification time is set to now -DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} - -# absolute directory containing dbScripts used by dbScriptAllDatabases -SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} - -# ----------------------------------------------------- -# AWS Parameters -# ----------------------------------------------------- -S3_BASE_URL=${S3_BASE_URL:-} - -# ----------------------------------------------------- -# Postman Parameters -# ----------------------------------------------------- -POSTMAN_API_KEY= -EOF -)}" +# @description check if all requirements are satisfied +# to execute dbImport commands +Db::checkRequirements() { + if [[ "${SKIP_REQUIREMENTS_CHECKS:-0}" = "1" ]]; then + return 0 + fi + local -i failures=0 + echo + Assert::commandExists mysql "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqldump "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists pv "sudo apt-get install -y pv" || ((++failures)) + Assert::commandExists gawk "sudo apt-get install -y gawk" || ((++failures)) + Assert::commandExists awk "sudo apt-get install -y gawk" || ((++failures)) + Version::checkMinimal "gawk" "--version" "5.0.1" || ((++failures)) + return "${failures}" +} -# @description loads ~/.bash-tools/.env if available -# if not creates it from a default template -# else check if new options need to be added -BashTools::Conf::requireLoad() { - local envFile="${HOME}/.bash-tools/.env" - if [[ ! -f "${envFile}" ]]; then - mkdir -p "${HOME}/.bash-tools" - ( - echo "#!/usr/bin/env bash" - echo "${bashToolsDefaultConfigTemplate}" - ) >"${envFile}" - Log::displayInfo "Configuration file '${envFile}' created" - else - if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then - ( - echo '# -----------------------------------------------------' - echo '# Postman Parameters' - echo '# -----------------------------------------------------' - echo 'POSTMAN_API_KEY=' - ) >>"${envFile}" + +# @description used to execute given query when using +# dbScriptAllDatabases +# @arg $1 dsn:String +# @arg $2 db:String +# @env query String +# @env optionSeparator String +Db::queryOneDatabase() { + + if [[ "${REQUIRE_FUNCTION_LINUX_REQUIRE_EXECUTED_AS_USER_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Linux::requireExecutedAsUser has not been loaded" + exit 1 + fi + + # query and optionSeparator are passed via export + local dsn="$1" + local db="$2" + + local -A dbInstance + Database::newInstance dbInstance "${dsn}" + Database::setQueryOptions dbInstance "${dbInstance[QUERY_OPTIONS]} --connect-timeout=5" + + # identify columns header + echo -n "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" + Database::skipColumnNames dbInstance 0 + + # shellcheck disable=SC2154 + if ! Database::query dbInstance "${query}" "${db}" | sed "s/\t/${optionSeparator}/g"; then + Log::fatal "database ${db} error" 1>&2 + fi +} + + +# @description prepend directories to the PATH environment variable +# @arg $@ args:String[] list of directories to prepend +# @set PATH update PATH with the directories prepended +Env::pathPrepend() { + local arg + for arg in "$@"; do + if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then + PATH="$(realpath "${arg}"):${PATH}" fi + done +} + + +# @description ensure env files are loaded +# @arg $@ list of default files to load at the end +# @exitcode 1 if one of env files fails to load +# @stderr diagnostics information is displayed +# shellcheck disable=SC2120 +Env::requireLoad() { + export REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 + + local -a defaultFiles=("$@") + # get list of possible config files + local -a configFiles=() + if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then + # BASH_FRAMEWORK_ENV_FILES is an array + configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") + fi + local localFrameworkConfigFile + localFrameworkConfigFile="$(pwd)/.framework-config" + if [[ -f "${localFrameworkConfigFile}" ]]; then + configFiles+=("${localFrameworkConfigFile}") + fi + if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then + configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") + fi + configFiles+=("${optionEnvFiles[@]}") + configFiles+=("${defaultFiles[@]}") + + for file in "${configFiles[@]}"; do + # shellcheck source=/.framework-config + CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { + Log::displayError "while loading config file: ${file}" + return 1 + } + done +} + + +# @description concatenate 2 paths and ensure the path is correct using realpath -m +# @arg $1 basePath:String +# @arg $2 subPath:String +File::concatenatePath() { + + if [[ "${REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Linux::requireRealpathCommand has not been loaded" + exit 1 + fi + + local basePath="$1" + local subPath="$2" + local fullPath="${basePath:+${basePath}/}${subPath}" + + realpath -m "${fullPath}" 2>/dev/null +} + + +# @description create a temp file using default TMPDIR variable +# @env TMPDIR String (default value /tmp) +# @arg $1 templateName:String template name to use(optional) +Framework::createTempFile() { + mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" +} + + +# @description ensure running user is not root +# @exitcode 1 if current user is root +# @stderr diagnostics information is displayed +Linux::requireExecutedAsUser() { + export REQUIRE_FUNCTION_LINUX_REQUIRE_EXECUTED_AS_USER_LOADED=1 + + if [[ "$(id -u)" = "0" ]]; then + Log::fatal "this script should be executed as normal user" fi - # shellcheck source=/conf/.env - source "${envFile}" || { - Log::displayError "impossible to load '${envFile}'" - exit 1 - } } -# @description ensure COMMAND_BIN_DIR env var is set -# and PATH correctly prepared -# @noargs -# @set COMMAND_BIN_DIR string the directory where to find this command -# @set PATH string add directory where to find this command binary -Compiler::Facade::requireCommandBinDir() { - COMMAND_BIN_DIR="${CURRENT_DIR}" - Env::pathPrepend "${COMMAND_BIN_DIR}" + +# @description ensure command realpath is available +# @exitcode 1 if realpath command not available +# @stderr diagnostics information is displayed +Linux::requireRealpathCommand() { + export REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED=1 + + Assert::commandExists realpath +} + + +# @description ensure command tar is available +# @exitcode 1 if tar command not available +# @stderr diagnostics information is displayed +Linux::requireTarCommand() { + Assert::commandExists tar } + declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" @@ -927,81 +947,81 @@ Log::computeDuration() { fi } -# @description log message to file + +# @description Display message using debug color (gray) # @arg $1 message:String the message to display -Log::logInfo() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then - Log::logMessage "${2:-INFO}" "$1" +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayDebug() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then + Log::computeDuration + echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logDebug "$1" } -# @description log message to file + +# @description Display message using error color (red) # @arg $1 message:String the message to display -Log::logDebug() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then - Log::logMessage "${2:-DEBUG}" "$1" +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayError() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then + Log::computeDuration + echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logError "$1" } -# @description by default we skip the column names -# but sometimes we need column names to display some results -# disable this option temporarily and then restore it to true -# -# @arg $1 instanceSetQueryOptions:&Map (passed by reference) database instance to use -# @arg $2 skipColumnNames:Boolean 0 to disable, 1 to enable (hide column names) -Database::skipColumnNames() { - local -n instanceSkipColumnNames=$1 - # shellcheck disable=SC2034 - instanceSkipColumnNames['SKIP_COLUMN_NAMES']="$2" + +# @description Display message using info color (bg light blue/fg white) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayInfo() { + local type="${2:-INFO}" + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logInfo "$1" "${type}" } -# @description mysql query on a given db -# @warning could use QUERY_OPTIONS variable from dsn if defined -# @example -# cat file.sql | Database::query ... -# @arg $1 instanceQuery:&Map (passed by reference) database instance to use -# @arg $2 sqlQuery:String (optional) sql query or sql file to execute. if not provided or empty, the command can be piped -# @arg $3 dbName:String (optional) the db name -# -# @exitcode mysql command status code -Database::query() { - local -n instanceQuery=$1 - local -a mysqlCommand=() - local -a queryOptions - mysqlCommand+=(mysql) - mysqlCommand+=("--defaults-extra-file=${instanceQuery['AUTH_FILE']}") - IFS=' ' read -r -a queryOptions <<<"${instanceQuery['QUERY_OPTIONS']}" - mysqlCommand+=("${queryOptions[@]}") - if [[ "${instanceQuery['SKIP_COLUMN_NAMES']}" = "1" ]]; then - mysqlCommand+=("-s" "--skip-column-names") - fi - # add optional db name - if [[ -n "${3+x}" ]]; then - mysqlCommand+=("$3") - fi - # add optional sql query - if [[ -n "${2+x}" && -n "$2" && ! -f "$2" ]]; then - mysqlCommand+=("-e") - mysqlCommand+=("$2") +# @description Display message using warning color (yellow) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayWarning() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then + Log::computeDuration + echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi - Log::displayDebug "$(printf "execute command: '%s'" "${mysqlCommand[*]}")" + Log::logWarning "$1" +} - if [[ -f "$2" ]]; then - "${mysqlCommand[@]}" <"$2" - else - "${mysqlCommand[@]}" - fi + +# @description Display message using error color (red) and exit immediately with error status 1 +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::fatal() { + Log::computeDuration + echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + Log::logFatal "$1" + exit 1 } + # @description log message to file # @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" +Log::logDebug() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then + Log::logMessage "${2:-DEBUG}" "$1" fi } + # @description log message to file # @arg $1 message:String the message to display Log::logError() { @@ -1010,20 +1030,6 @@ Log::logError() { fi } -# @description check if tty (interactive mode) is active -# @noargs -# @exitcode 1 if tty not active -# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive -# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive -Assert::tty() { - if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then - return 1 - fi - if [[ "${INTERACTIVE:-0}" = "1" ]]; then - return 0 - fi - tty -s -} # @description log message to file # @arg $1 message:String the message to display @@ -1031,6 +1037,16 @@ Log::logFatal() { Log::logMessage "${2:-FATAL}" "$1" } + +# @description log message to file +# @arg $1 message:String the message to display +Log::logInfo() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then + Log::logMessage "${2:-INFO}" "$1" + fi +} + + # @description Internal: common log message # @example text # [date]|[levelMsg]|message @@ -1044,9 +1060,18 @@ Log::logFatal() { # @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty # @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages # @stderr diagnostics information is displayed -# @require Env::requireLoad -# @require Log::requireLoad Log::logMessage() { + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Log::requireLoad has not been loaded" + exit 1 + fi + local levelMsg="$1" local msg="$2" local date @@ -1058,287 +1083,309 @@ Log::logMessage() { fi } -# @description To be called before logging in the log file -# @arg $1 file:string log file name -# @arg $2 maxLogFilesCount:int maximum number of log files -Log::rotate() { - local file="$1" - local maxLogFilesCount="${2:-5}" - if [[ ! -f "${file}" ]]; then - Log::displayDebug "Log file ${file} doesn't exist yet" - return 0 - fi - local i - for ((i = maxLogFilesCount - 1; i > 0; i--)); do - Log::displayInfo "Log rotation ${file}.${i} to ${file}.$((i + 1))" - mv "${file}."{"${i}","$((i + 1))"} &>/dev/null || true - done - if cp "${file}" "${file}.1" &>/dev/null; then - echo >"${file}" # reset log file - Log::displayInfo "Log rotation ${file} to ${file}.1" +# @description log message to file +# @arg $1 message:String the message to display +Log::logWarning() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then + Log::logMessage "${2:-WARNING}" "$1" fi } -# @description filter to keep only version number from a string -# @arg $@ files:String[] the files to filter -# @exitcode * if one of the filter command fails -# @stdin you can use stdin as alternative to files argument -# @stdout the filtered content -# shellcheck disable=SC2120 -Version::parse() { - # match anything, print(p), exit on first match(Q) - sed -En \ - -e 's/\x1b\[[0-9;]*[mGKHF]//g' \ - -e 's/[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/' \ - -e '//{p;Q}' \ - "$@" -} -# @description ignore exit code 141 from simple command pipes -# @example use with: -# local resultingStatus=0 -# local -a originalPipeStatus=() -# cmd1 | cmd2 || Bash::handlePipelineFailure resultingStatus originalPipeStatus || true -# [[ "${resultingStatus}" = "0" ]] -# @arg $1 resultingStatusCode:&int (passed by reference) (optional) resulting status code -# @arg $2 originalStatus:int[] (passed by reference) (optional) copy of original PIPESTATUS array -# @env PIPESTATUS assuming that this function is called like in the example provided -# @see https://unix.stackexchange.com/a/709880/582856 -Bash::handlePipelineFailure() { - local -a pipeStatusBackup=("${PIPESTATUS[@]}") - local -n handlePipelineFailure_resultingStatusCode=$1 - local -n handlePipelineFailure_originalStatus=$2 - # shellcheck disable=SC2034 - handlePipelineFailure_originalStatus=("${pipeStatusBackup[@]}") - handlePipelineFailure_resultingStatusCode=0 - local statusCode - for statusCode in "${pipeStatusBackup[@]}"; do - if ((statusCode == 141)); then - return 0 - elif ((statusCode > 0)); then - # shellcheck disable=SC2034 - handlePipelineFailure_resultingStatusCode="${statusCode}" - break +# @description activate or not Log::display* and Log::log* functions +# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL +# environment variables loaded by Env::requireLoad +# try to create log file and rotate it if necessary +# @noargs +# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable +# @env BASH_FRAMEWORK_DISPLAY_LEVEL int +# @env BASH_FRAMEWORK_LOG_LEVEL int +# @env BASH_FRAMEWORK_LOG_FILE String +# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 +# @exitcode 0 always successful +# @stderr diagnostics information about log file is displayed +Log::requireLoad() { + export REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement UI::requireTheme has not been loaded" + exit 1 + fi + + if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + export BASH_FRAMEWORK_LOG_LEVEL + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then + if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 + fi + elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 fi - done - return "${handlePipelineFailure_resultingStatusCode}" + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + # will always be created even if not in info level + Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" + if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then + Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + fi + fi } -# @description compare 2 version numbers -# @arg $1 version1:String version 1 -# @arg $2 version2:String version 2 -# @exitcode 0 if equal -# @exitcode 1 if version1 > version2 -# @exitcode 2 else -Version::compare() { - if [[ "$1" = "$2" ]]; then + +# @description To be called before logging in the log file +# @arg $1 file:string log file name +# @arg $2 maxLogFilesCount:int maximum number of log files +Log::rotate() { + local file="$1" + local maxLogFilesCount="${2:-5}" + + if [[ ! -f "${file}" ]]; then + Log::displayDebug "Log file ${file} doesn't exist yet" return 0 fi - local IFS=. - # shellcheck disable=2206 - local i ver1=($1) ver2=($2) - # fill empty fields in ver1 with zeros - for ((i = ${#ver1[@]}; i < ${#ver2[@]}; i++)); do - ver1[i]=0 - done - for ((i = 0; i < ${#ver1[@]}; i++)); do - if [[ -z "${ver2[i]+unset}" ]] || [[ -z ${ver2[i]} ]]; then - # fill empty fields in ver2 with zeros - ver2[i]=0 - fi - if ((10#${ver1[i]} > 10#${ver2[i]})); then - return 1 - fi - if ((10#${ver1[i]} < 10#${ver2[i]})); then - return 2 - fi + local i + for ((i = maxLogFilesCount - 1; i > 0; i--)); do + Log::displayInfo "Log rotation ${file}.${i} to ${file}.$((i + 1))" + mv "${file}."{"${i}","$((i + 1))"} &>/dev/null || true done - return 0 + if cp "${file}" "${file}.1" &>/dev/null; then + echo >"${file}" # reset log file + Log::displayInfo "Log rotation ${file} to ${file}.1" + fi } -# @description list files of dir with given extension and display it as a list one by line -# -# @arg $1 dir:String the directory to list -# @arg $2 prefix:String the profile file prefix (default: "") -# @arg $3 ext:String the extension -# @arg $4 findOptions:String find options, eg: -type d (Default value: '-type f') -# @arg $5 indentStr:String the indentation can be any string compatible with sed not containing any / (Default value: ' - ') -# @stdout list of files without extension/directory -# @example text -# - default.local -# - default.remote -# - localhost-root -# @exitcode 1 if directory does not exists -Conf::list() { - local dir="$1" - local prefix="${2:-}" - local ext="${3}" - local findOptions="${4--type f}" - local indentStr="${5- - }" - if [[ ! -d "${dir}" ]]; then - Log::displayError "Directory ${dir} does not exist" +# @description draw a line with the character passed in parameter repeated depending on terminal width +# @arg $1 character:String character to use as separator (default value #) +UI::drawLine() { + local character="${1:-#}" + local -i width=${COLUMNS:-0} + if ((width == 0)) && [[ -t 1 ]]; then + width=$(tput cols) fi - if [[ -n "${ext}" && "${ext:0:1}" != "." ]]; then - ext=".${ext}" + if ((width == 0)); then + width=80 fi - ( - # shellcheck disable=SC2086 - cd "${dir}" && - find . -maxdepth 1 ${findOptions} -name "${prefix}*${ext}" | - sed -E "s#^\./${prefix}##g" | - sed -E "s#${ext}\$##g" | sort | sed -E "s#^#${indentStr}#" - ) + printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") + echo } -# @description concatenate 2 paths and ensure the path is correct using realpath -m -# @arg $1 basePath:String -# @arg $2 subPath:String -# @require Linux::requireRealpathCommand -File::concatenatePath() { - local basePath="$1" - local subPath="$2" - local fullPath="${basePath:+${basePath}/}${subPath}" - realpath -m "${fullPath}" 2>/dev/null +# @description load color theme +# @noargs +# @env BASH_FRAMEWORK_THEME String theme to use +# @env LOAD_THEME int 0 to avoid loading theme +# @exitcode 0 always successful +UI::requireTheme() { + export REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + + if [[ "${LOAD_THEME:-1}" = "1" ]]; then + UI::theme "${BASH_FRAMEWORK_THEME-default}" + fi } -# @description check if dsn file has all the mandatory variables set -# Mandatory variables are: HOSTNAME, USER, PASSWORD, PORT + +# @description load colors theme constants +# @warning if tty not opened, noColor theme will be chosen +# @arg $1 theme:String the theme to use (default, noColor) +# @arg $@ args:String[] +# @set __ERROR_COLOR String indicate error status +# @set __INFO_COLOR String indicate info status +# @set __SUCCESS_COLOR String indicate success status +# @set __WARNING_COLOR String indicate warning status +# @set __SKIPPED_COLOR String indicate skipped status +# @set __DEBUG_COLOR String indicate debug status +# @set __HELP_COLOR String indicate help status +# @set __TEST_COLOR String not used +# @set __TEST_ERROR_COLOR String not used +# @set __HELP_TITLE_COLOR String used to display help title in help strings +# @set __HELP_OPTION_COLOR String used to display highlight options in help strings # -# @arg $1 dsnFileName:String dsn absolute filename -# @set HOSTNAME loaded from dsn file -# @set PORT loaded from dsn file -# @set USER loaded from dsn file -# @set PASSWORD loaded from dsn file -# @exitcode 0 on valid file -# @exitcode 1 if one of the properties of the conf file is invalid or if file not found -# @stderr log output if error found in conf file -Database::checkDsnFile() { - local dsnFileName="$1" - if [[ ! -f "${dsnFileName}" ]]; then - Log::displayError "dsn file ${dsnFileName} not found" - return 1 +# @set __RESET_COLOR String reset default color +# +# @set __HELP_EXAMPLE String to remove +# @set __HELP_TITLE String to remove +# @set __HELP_NORMAL String to remove +# shellcheck disable=SC2034 +UI::theme() { + local theme="${1-default}" + if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then + theme="noColor" + fi + case "${theme}" in + default | default-force) + theme="default" + ;; + noColor) ;; + *) + Log::fatal "invalid theme provided" + ;; + esac + if [[ "${theme}" = "default" ]]; then + BASH_FRAMEWORK_THEME="default" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='\e[31m' # Red + __INFO_COLOR='\e[44m' # white on lightBlue + __SUCCESS_COLOR='\e[32m' # Green + __WARNING_COLOR='\e[33m' # Yellow + __SKIPPED_COLOR='\e[33m' # Yellow + __DEBUG_COLOR='\e[37m' # Gray + __HELP_COLOR='\e[7;49;33m' # Black on Gold + __TEST_COLOR='\e[100m' # Light magenta + __TEST_ERROR_COLOR='\e[41m' # white on red + __HELP_TITLE_COLOR="\e[1;37m" # Bold + __HELP_OPTION_COLOR="\e[1;34m" # Blue + # Internal: reset color + __RESET_COLOR='\e[0m' # Reset Color + # shellcheck disable=SC2155,SC2034 + __HELP_EXAMPLE="$(echo -e "\e[2;97m")" + # shellcheck disable=SC2155,SC2034 + __HELP_TITLE="$(echo -e "\e[1;37m")" + # shellcheck disable=SC2155,SC2034 + __HELP_NORMAL="$(echo -e "\033[0m")" + else + BASH_FRAMEWORK_THEME="noColor" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='' + __INFO_COLOR='' + __SUCCESS_COLOR='' + __WARNING_COLOR='' + __SKIPPED_COLOR='' + __DEBUG_COLOR='' + __HELP_COLOR='' + __TEST_COLOR='' + __TEST_ERROR_COLOR='' + __HELP_TITLE_COLOR='' + __HELP_OPTION_COLOR='' + # Internal: reset color + __RESET_COLOR='' + __HELP_EXAMPLE='' + __HELP_TITLE='' + __HELP_NORMAL='' fi +} - ( - unset HOSTNAME PORT PASSWORD USER - # shellcheck source=/src/Database/testsData/dsn_valid.env - source "${dsnFileName}" - if [[ -z ${HOSTNAME+x} ]]; then - Log::displayError "dsn file ${dsnFileName} : HOSTNAME not provided" - return 1 - fi - if [[ -z "${HOSTNAME}" ]]; then - Log::displayWarning "dsn file ${dsnFileName} : HOSTNAME value not provided" - fi - if [[ "${HOSTNAME}" = "localhost" ]]; then - Log::displayWarning "dsn file ${dsnFileName} : check that HOSTNAME should not be 127.0.0.1 instead of localhost" - fi - if [[ -z "${PORT+x}" ]]; then - Log::displayError "dsn file ${dsnFileName} : PORT not provided" - return 1 - fi - if ! [[ ${PORT} =~ ^[0-9]+$ ]]; then - Log::displayError "dsn file ${dsnFileName} : PORT invalid" + +# @description Check that command version is greater than expected minimal version +# display warning if command version greater than expected minimal version +# display error if command version less than expected minimal version and exit 1 +# @arg $1 commandName:String command path +# @arg $2 argVersion:String command line parameters to launch to get command version +# @arg $3 minimalVersion:String expected minimal command version +# @arg $4 parseVersionCallback:Function +# @arg $5 help:String optional help message to display if command does not exist +# @exitcode 0 if command version greater or equal to expected minimal version +# @exitcode 1 if command version less than expected minimal version +# @exitcode 2 if command does not exist +Version::checkMinimal() { + local commandName="$1" + local argVersion="$2" + local minimalVersion="$3" + local parseVersionCallback=${4:-Version::parse} + local help="${5:-}" + + Assert::commandExists "${commandName}" "${help}" || return 2 + + # shellcheck disable=SC2034 + local status=0 + # shellcheck disable=SC2034 + local -a pipeStatus=() + local version + version="$("${commandName}" "${argVersion}" 2>&1 | ${parseVersionCallback} || Bash::handlePipelineFailure status pipeStatus)" + + Log::displayDebug "check ${commandName} version ${version} against minimal ${minimalVersion}" + + Version::compare "${version}" "${minimalVersion}" || { + local result=$? + if [[ "${result}" = "1" ]]; then + Log::displayInfo "${commandName} version is ${version} greater than ${minimalVersion}" + elif [[ "${result}" = "2" ]]; then + Log::displayError "${commandName} minimal version is ${minimalVersion}, your version is ${version}" return 1 fi - if [[ -z "${USER+x}" ]]; then - Log::displayError "dsn file ${dsnFileName} : USER not provided" - return 1 + return 0 + } + +} + + +# @description compare 2 version numbers +# @arg $1 version1:String version 1 +# @arg $2 version2:String version 2 +# @exitcode 0 if equal +# @exitcode 1 if version1 > version2 +# @exitcode 2 else +Version::compare() { + if [[ "$1" = "$2" ]]; then + return 0 + fi + local IFS=. + # shellcheck disable=2206 + local i ver1=($1) ver2=($2) + # fill empty fields in ver1 with zeros + for ((i = ${#ver1[@]}; i < ${#ver2[@]}; i++)); do + ver1[i]=0 + done + for ((i = 0; i < ${#ver1[@]}; i++)); do + if [[ -z "${ver2[i]+unset}" ]] || [[ -z ${ver2[i]} ]]; then + # fill empty fields in ver2 with zeros + ver2[i]=0 fi - if [[ -z "${PASSWORD+x}" ]]; then - Log::displayError "dsn file ${dsnFileName} : PASSWORD not provided" + if ((10#${ver1[i]} > 10#${ver2[i]})); then return 1 fi - ) -} - -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" + if ((10#${ver1[i]} < 10#${ver2[i]})); then + return 2 fi done + return 0 } -# @description load color theme -# @noargs -# @env BASH_FRAMEWORK_THEME String theme to use -# @env LOAD_THEME int 0 to avoid loading theme -# @exitcode 0 always successful -UI::requireTheme() { - if [[ "${LOAD_THEME:-1}" = "1" ]]; then - UI::theme "${BASH_FRAMEWORK_THEME-default}" - fi -} -# @description ensure command realpath is available -# @exitcode 1 if realpath command not available -# @stderr diagnostics information is displayed -Linux::requireRealpathCommand() { - Assert::commandExists realpath +# @description filter to keep only version number from a string +# @arg $@ files:String[] the files to filter +# @exitcode * if one of the filter command fails +# @stdin you can use stdin as alternative to files argument +# @stdout the filtered content +# shellcheck disable=SC2120 +Version::parse() { + # match anything, print(p), exit on first match(Q) + sed -En \ + -e 's/\x1b\[[0-9;]*[mGKHF]//g' \ + -e 's/[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/' \ + -e '//{p;Q}' \ + "$@" } - # FUNCTIONS -facade_main_dbQueryAllDatabasessh() { -BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" -if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then - FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" -else - # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR - FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" -fi -FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" -FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" - -# @require BashTools::Conf::requireLoad -if [[ -f "${HOME}/.bash-tools/.env" ]]; then - export BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") -fi -# REQUIRES -Linux::requireExecutedAsUser -Env::requireLoad -UI::requireTheme -Log::requireLoad -Linux::requireRealpathCommand -BashTools::Conf::requireLoad -Compiler::Facade::requireCommandBinDir - -# @require Compiler::Facade::requireCommandBinDir -# shellcheck disable=SC2034 - -# later on, parallel calls this script(inception) -if [[ "$1" = "DbQueryOneDatabase" ]]; then - shift || true - Linux::requireExecutedAsUser - Db::queryOneDatabase "$@" - exit 0 -fi - -#default values -# default value for FROM_DSN if from-aws not set -declare queryIsFile="0" -declare optionSeparator="|" -declare argQuery="" - -# other configuration -declare copyrightBeginYear="2020" -declare QUERIES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbQueries" -declare HOME_QUERIES_DIR="${HOME}/.bash-tools/dbQueries" - -declare example1=$'dbQueryAllDatabases databaseSize -j 12 --separator "|" --bar 2>/dev/null | column -s "|" -t -n -c 40' declare -a BASH_FRAMEWORK_ARGV_FILTERED=() +beforeParseCallback() { + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + copyrightCallback() { if [[ -z "${copyrightBeginYear}" ]]; then copyrightBeginYear="$(date +%Y)" @@ -1375,13 +1422,14 @@ updateArgListQuietCallback() { :; } # shellcheck disable=SC2317 # if function is overridden optionHelpCallback() { - dbQueryAllDatabasesCommand help + Log::displayError "optionHelpCallback needs to be overridden" exit 0 } # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { - echo "${SCRIPT_NAME} version 2.0" + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version ${versionNumber}" exit 0 } @@ -1399,21 +1447,22 @@ optionEnvFileCallback() { optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_INFO} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionDebugVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_DEBUG} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionTraceVerboseCallback() { + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vvv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_TRACE} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } getLevel() { @@ -1437,6 +1486,7 @@ getLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -1458,6 +1508,7 @@ getVerboseLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -1468,7 +1519,7 @@ optionDisplayLevelCallback() { logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1477,19 +1528,20 @@ optionLogLevelCallback() { local logLevel verboseLevel logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionLogFileCallback() { local logFile="$2" - echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionQuietCallback() { - echo "BASH_FRAMEWORK_QUIET_MODE=1" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_QUIET_MODE=1" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1520,6 +1572,7 @@ optionBashFrameworkConfigCallback() { defaultFrameworkConfig="$( cat <<'EOF' + # copied from src/_includes/.framework-config.default # shellcheck disable=SC2034 @@ -1537,7 +1590,7 @@ NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-conf # describe the files that are allowed to not have an associated bats file BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" # describe the files that are allowed to not have a function matching the filename -FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|\.tpl$|/testsData/|^manualTests/|\.bats$}" +FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|/testsData/|^manualTests/|\.bats$}" # Source directories if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then FRAMEWORK_SRC_DIRS=( @@ -1553,6 +1606,7 @@ BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" + EOF )" @@ -1561,7 +1615,7 @@ overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" commandOptionParseFinished() { # load default template framework config defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" - echo "${defaultFrameworkConfig}" > "${defaultEnvFile}" + echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") if [[ -f "${envFile}" ]]; then files+=("${envFile}") @@ -1579,11 +1633,20 @@ commandOptionParseFinished() { fi } -# default values -declare optionFromDsn="" + + +beforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + + optionJobsCallback() { - if ! [[ ${optionJobs} =~ ^[0-9]+$ ]]; then + # shellcheck disable=SC2154 + if ! [[ "${optionJobs}" =~ ^[0-9]+$ ]]; then Log::fatal "number of jobs is incorrect" fi @@ -1592,575 +1655,63 @@ optionJobsCallback() { fi } + + declare -a PARALLEL_OPTIONS optionProgressBarCallback() { PARALLEL_OPTIONS+=(--bar) } -dbQueryAllDatabasesCommand() { - local options_parse_cmd="$1" - shift || true - if [[ "${options_parse_cmd}" = "parse" ]]; then - optionJobs="1" - local -i options_parse_optionParsedCountOptionJobs - ((options_parse_optionParsedCountOptionJobs = 0)) || true - optionProgressBar="0" - local -i options_parse_optionParsedCountOptionProgressBar - ((options_parse_optionParsedCountOptionProgressBar = 0)) || true - optionSeparator="|" - local -i options_parse_optionParsedCountOptionSeparator - ((options_parse_optionParsedCountOptionSeparator = 0)) || true - local -i options_parse_optionParsedCountOptionFromDsn - ((options_parse_optionParsedCountOptionFromDsn = 0)) || true - local -i options_parse_optionParsedCountOptionBashFrameworkConfig - ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true - optionConfig="0" - local -i options_parse_optionParsedCountOptionConfig - ((options_parse_optionParsedCountOptionConfig = 0)) || true - optionInfoVerbose="0" - local -i options_parse_optionParsedCountOptionInfoVerbose - ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true - optionDebugVerbose="0" - local -i options_parse_optionParsedCountOptionDebugVerbose - ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true - optionTraceVerbose="0" - local -i options_parse_optionParsedCountOptionTraceVerbose - ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionNoColor="0" - local -i options_parse_optionParsedCountOptionNoColor - ((options_parse_optionParsedCountOptionNoColor = 0)) || true - optionTheme="default" - local -i options_parse_optionParsedCountOptionTheme - ((options_parse_optionParsedCountOptionTheme = 0)) || true - optionHelp="0" - local -i options_parse_optionParsedCountOptionHelp - ((options_parse_optionParsedCountOptionHelp = 0)) || true - optionVersion="0" - local -i options_parse_optionParsedCountOptionVersion - ((options_parse_optionParsedCountOptionVersion = 0)) || true - optionQuiet="0" - local -i options_parse_optionParsedCountOptionQuiet - ((options_parse_optionParsedCountOptionQuiet = 0)) || true - local -i options_parse_optionParsedCountOptionLogLevel - ((options_parse_optionParsedCountOptionLogLevel = 0)) || true - local -i options_parse_optionParsedCountOptionLogFile - ((options_parse_optionParsedCountOptionLogFile = 0)) || true - local -i options_parse_optionParsedCountOptionDisplayLevel - ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true - local -i options_parse_argParsedCountArgQuery - ((options_parse_argParsedCountArgQuery = 0)) || true - # shellcheck disable=SC2034 - local -i options_parse_parsedArgIndex=0 - while (($# > 0)); do - local options_parse_arg="$1" - local argOptDefaultBehavior=0 - case "${options_parse_arg}" in - # Option 1/18 - # Option optionJobs --jobs|-j variableType String min 0 max 1 authorizedValues '' regexp '' - --jobs | -j) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionJobs >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionJobs)) - # shellcheck disable=SC2034 - optionJobs="$1" - optionJobsCallback "${options_parse_arg}" "${optionJobs}" - ;; - # Option 2/18 - # Option optionProgressBar --bar|-b variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --bar | -b) - # shellcheck disable=SC2034 - optionProgressBar="1" - if ((options_parse_optionParsedCountOptionProgressBar >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionProgressBar)) - optionProgressBarCallback "${options_parse_arg}" - ;; - # Option 3/18 - # Option optionSeparator --separator|-s variableType String min 0 max 1 authorizedValues '' regexp '' - --separator | -s) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionSeparator >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionSeparator)) - # shellcheck disable=SC2034 - optionSeparator="$1" - optionSeparatorCallback "${options_parse_arg}" "${optionSeparator}" - ;; - # Option 4/18 - # Option optionFromDsn --from-dsn|-f variableType String min 0 max 1 authorizedValues '' regexp '' - --from-dsn | -f) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionFromDsn >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionFromDsn)) - # shellcheck disable=SC2034 - optionFromDsn="$1" - ;; - # Option 5/18 - # Option optionBashFrameworkConfig --bash-framework-config variableType String min 0 max 1 authorizedValues '' regexp '' - --bash-framework-config) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) - # shellcheck disable=SC2034 - optionBashFrameworkConfig="$1" - optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" - ;; - # Option 6/18 - # Option optionConfig --config variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --config) - # shellcheck disable=SC2034 - optionConfig="1" - if ((options_parse_optionParsedCountOptionConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionConfig)) - ;; - # Option 7/18 - # Option optionInfoVerbose --verbose|-v variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --verbose | -v) - # shellcheck disable=SC2034 - optionInfoVerbose="1" - if ((options_parse_optionParsedCountOptionInfoVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionInfoVerbose)) - optionInfoVerboseCallback "${options_parse_arg}" - updateArgListInfoVerboseCallback "${options_parse_arg}" - ;; - # Option 8/18 - # Option optionDebugVerbose -vv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vv) - # shellcheck disable=SC2034 - optionDebugVerbose="1" - if ((options_parse_optionParsedCountOptionDebugVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDebugVerbose)) - optionDebugVerboseCallback "${options_parse_arg}" - updateArgListDebugVerboseCallback "${options_parse_arg}" - ;; - # Option 9/18 - # Option optionTraceVerbose -vvv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vvv) - # shellcheck disable=SC2034 - optionTraceVerbose="1" - if ((options_parse_optionParsedCountOptionTraceVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTraceVerbose)) - optionTraceVerboseCallback "${options_parse_arg}" - updateArgListTraceVerboseCallback "${options_parse_arg}" - ;; - # Option 10/18 - # Option optionEnvFiles --env-file variableType StringArray min 0 max -1 authorizedValues '' regexp '' - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - ;; - # Option 11/18 - # Option optionNoColor --no-color variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --no-color) - # shellcheck disable=SC2034 - optionNoColor="1" - if ((options_parse_optionParsedCountOptionNoColor >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionNoColor)) - optionNoColorCallback "${options_parse_arg}" - updateArgListNoColorCallback "${options_parse_arg}" - ;; - # Option 12/18 - # Option optionTheme --theme variableType String min 0 max 1 authorizedValues 'default|default-force|noColor' regexp '' - --theme) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default|default-force|noColor)" - return 1 - fi - if ((options_parse_optionParsedCountOptionTheme >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTheme)) - # shellcheck disable=SC2034 - optionTheme="$1" - optionThemeCallback "${options_parse_arg}" "${optionTheme}" - updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" - ;; - # Option 13/18 - # Option optionHelp --help|-h variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --help | -h) - # shellcheck disable=SC2034 - optionHelp="1" - if ((options_parse_optionParsedCountOptionHelp >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionHelp)) - optionHelpCallback "${options_parse_arg}" - ;; - # Option 14/18 - # Option optionVersion --version variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --version) - # shellcheck disable=SC2034 - optionVersion="1" - if ((options_parse_optionParsedCountOptionVersion >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionVersion)) - optionVersionCallback "${options_parse_arg}" - ;; - # Option 15/18 - # Option optionQuiet --quiet|-q variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --quiet | -q) - # shellcheck disable=SC2034 - optionQuiet="1" - if ((options_parse_optionParsedCountOptionQuiet >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionQuiet)) - optionQuietCallback "${options_parse_arg}" - updateArgListQuietCallback "${options_parse_arg}" - ;; - # Option 16/18 - # Option optionLogLevel --log-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --log-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogLevel)) - # shellcheck disable=SC2034 - optionLogLevel="$1" - optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - ;; - # Option 17/18 - # Option optionLogFile --log-file variableType String min 0 max 1 authorizedValues '' regexp '' - --log-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogFile >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogFile)) - # shellcheck disable=SC2034 - optionLogFile="$1" - optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" - updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" - ;; - # Option 18/18 - # Option optionDisplayLevel --display-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --display-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionDisplayLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDisplayLevel)) - # shellcheck disable=SC2034 - optionDisplayLevel="$1" - optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - ;; - -*) - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" - return 1 - fi - ;; - *) - if ((0)); then - # Technical if - never reached - : - # Argument 1/1 - # Argument argQuery min 1 max 1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 0 && options_parse_parsedArgIndex < 1)); then - if ((options_parse_argParsedCountArgQuery >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument argQuery - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountArgQuery)) - # shellcheck disable=SC2034 - argQuery="${options_parse_arg}" - argQueryCallback "${argQuery}" -- "${@:2}" - else - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" - return 1 - fi - fi - ((++options_parse_parsedArgIndex)) - ;; - esac - shift || true - done - if ((options_parse_argParsedCountArgQuery < 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument 'argQuery' should be provided at least 1 time(s)" - return 1 - fi - commandOptionParseFinished - dbQueryAllDatabasesCommandCallback - Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" - Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" - elif [[ "${options_parse_cmd}" = "help" ]]; then - Array::wrap2 " " 80 0 "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" "Execute a query on multiple databases in order to generate a report with tsv format, query can be parallelized on multiple databases" - echo - - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "${SCRIPT_NAME}" "[OPTIONS]" "[ARGUMENTS]")" - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ - "${SCRIPT_NAME}" \ - "[--jobs|-j ]" "[--bar|-b]" "[--separator|-s ]" "[--from-dsn|-f ]" "[--bash-framework-config ]" "[--config]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--no-color]" "[--theme ]" "[--help|-h]" "[--version]" "[--quiet|-q]" "[--log-level ]" "[--log-file ]" "[--display-level ]")" - echo - echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}argQuery${__HELP_NORMAL} {single} (mandatory)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=($'Query to execute \n - , try to execute the mysql query provided by the file \n - , search for query file in queries directory (see below) \n - else the argument is interpreted as query string') - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--jobs${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-j ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(specify\ the\ number\ of\ db\ to\ query\ in\ parallel) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: 1' - echo -e " ${__HELP_OPTION_COLOR}--bar${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-b${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Show\ progress\ as\ a\ progress\ bar.\ In\ the\ bar\ is\ shown:\ %\ of\ jobs\ completed\,\ estimated\ seconds\ left\,\ and\ number\ of\ jobs\ started.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--separator${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-s ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(character\ to\ use\ to\ separate\ mysql\ column) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: |' - echo - echo -e "${__HELP_TITLE_COLOR}QUERY OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--from-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-f ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(target\ mysql\ server) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(use\ alternate\ bash\ framework\ configuration.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ configuration) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(info\ level\ verbose\ mode\ \(alias\ of\ --display-level\ INFO\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(debug\ level\ verbose\ mode\ \(alias\ of\ --display-level\ DEBUG\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(trace\ level\ verbose\ mode\ \(alias\ of\ --display-level\ TRACE\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Load\ the\ specified\ env\ file\ \(deprecated\,\ please\ use\ --bash-framework-config\ option\ instead\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Produce\ monochrome\ output.\ alias\ of\ --theme\ noColor.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' - echo ' Possible values: default|default-force|noColor' - echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ this\ command\ help) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Print\ version\ information\ and\ quit) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(quiet\ mode\,\ doesn\'t\ display\ any\ output) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ file) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(set\ display\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e """ -${__HELP_TITLE}LIST OF AVAILABLE DSN:${__HELP_NORMAL} -${dsnList} - -${__HELP_TITLE}DEFAULT QUERIES DIRECTORY:${__HELP_NORMAL} -${QUERIES_DIR-configuration error} - -${__HELP_TITLE}USER QUERIES DIRECTORY:${__HELP_NORMAL} -${HOME_QUERIES_DIR-configuration error} -Allows to override queries defined in "Default queries directory" - -${__HELP_TITLE}LIST OF AVAILABLE QUERIES:${__HELP_NORMAL} -${queriesList} - -${__HELP_TITLE}EXAMPLES:${__HELP_NORMAL} -${__HELP_EXAMPLE}${example1}${__HELP_NORMAL}""" - echo - echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo '2.0' - echo - echo -e "${__HELP_TITLE_COLOR}AUTHOR:${__RESET_COLOR}" - echo '[François Chastanet](https://github.com/fchastanet)' - echo - echo -e "${__HELP_TITLE_COLOR}SOURCE FILE:${__RESET_COLOR}" - echo 'https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/DbQueryAllDatabases/dbQueryAllDatabases.sh' - echo - echo -e "${__HELP_TITLE_COLOR}LICENSE:${__RESET_COLOR}" - echo 'MIT License' - echo - Array::wrap2 ' ' 76 4 "$(copyrightCallback)" - else - Log::displayError "Command ${SCRIPT_NAME} - Option command invalid: '${options_parse_cmd}'" - return 1 - fi + +optionVersionCallback() { + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version ${versionNumber}" + Db::checkRequirements + exit 0 } -checkRequirements() { - if [[ "${SKIP_REQUIREMENTS_CHECKS:-0}" = "1" ]]; then - return 0 - fi - local -i failures=0 - echo - Assert::commandExists mysql "sudo apt-get install -y mysql-client" || ((++failures)) - Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" || ((++failures)) - Assert::commandExists mysqldump "sudo apt-get install -y mysql-client" || ((++failures)) - Assert::commandExists pv "sudo apt-get install -y pv" || ((++failures)) - Assert::commandExists gawk "sudo apt-get install -y gawk" || ((++failures)) - Assert::commandExists awk "sudo apt-get install -y gawk" || ((++failures)) - Version::checkMinimal "gawk" "--version" "5.0.1" || ((++failures)) - return "${failures}" + + +declare QUERIES_DIR +declare HOME_QUERIES_DIR +declare queryIsFile="0" +declare optionSeparator="|" + +# shellcheck disable=SC2034 +declare versionNumber="2.0" +# shellcheck disable=SC2034 +declare copyrightBeginYear="2020" +# shellcheck disable=SC2034 +declare defaultFromDsn="default.remote" + +beforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad + Linux::requireExecutedAsUser + Linux::requireRealpathCommand } -optionVersionCallback() { - echo "${SCRIPT_NAME} version 2.0" - checkRequirements - exit 0 +initConf() { + # shellcheck disable=SC2034 + QUERIES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbQueries" + HOME_QUERIES_DIR="${HOME}/.bash-tools/dbQueries" + + Db::checkRequirements } optionHelpCallback() { - local dsnList queriesList - dsnList="$(Conf::getMergedList "dsn" "env")" - queriesList="$(Conf::getMergedList "dbQueries" "sql" || true)" - - dbQueryAllDatabasesCommand help | envsubst - checkRequirements + dbQueryAllDatabasesCommandHelp exit 0 } optionSeparatorCallback() { + # shellcheck disable=SC2154 if ((${#optionSeparator} != 1)); then - Log::fatal "Command ${SCRIPT_NAME} - only one character is accepter as separator" + Log::fatal "Command ${SCRIPT_NAME} - only one character is accepted as separator" fi if [[ ${optionSeparator} =~ [a-zA-Z0-9/\ ] ]]; then @@ -2168,12 +1719,52 @@ optionSeparatorCallback() { fi } +longDescriptionFunction() { + local example1=$'dbQueryAllDatabases databaseSize -j 12 --separator "|" --bar 2>/dev/null | column -s "|" -t -n -c 40' + + local dsnList queriesList + dsnList="$(Conf::getMergedList "dsn" "env" " - ")" + queriesList="$(Conf::getMergedList "dbQueries" "sql" " - " || true)" + + echo -e "${__HELP_TITLE} LIST OF AVAILABLE DSN:${__HELP_NORMAL}" + echo -e "${dsnList}" + echo + echo -e "${__HELP_TITLE} DEFAULT QUERIES DIRECTORY:${__HELP_NORMAL}" + echo -e " ${QUERIES_DIR-configuration error}" + echo + echo -e "${__HELP_TITLE} USER QUERIES DIRECTORY:${__HELP_NORMAL}" + echo -e " ${HOME_QUERIES_DIR-configuration error}" + echo -e " Allows to override queries defined in 'Default queries directory'" + echo + echo -e "${__HELP_TITLE} LIST OF AVAILABLE QUERIES:${__HELP_NORMAL}" + echo -e "${queriesList}" + echo + echo -e "${__HELP_TITLE} EXAMPLES:${__HELP_NORMAL}" + echo -e " ${__HELP_EXAMPLE}${example1}${__HELP_NORMAL}" +} + +argQueryHelpFunction() { + Array::wrap2 " " 80 6 \ + " Query to execute" "\n" \ + "- , try to execute the mysql query provided by the file" "\n" \ + '- , search for query file in queries directory (see below)' "\n" \ + '- else the argument is interpreted as query string' +} + +dbQueryAllDatabasesEveryArgumentCallback() { + if [[ -n "${DB_QUERY_ALL_DATABASES_COMMAND:-}" ]]; then + # a command is defined, then accept that arguments can be overridden + return 1 + fi +} + argQueryCallback() { if [[ -f "${argQuery}" ]]; then queryIsFile="1" else declare queryAbsoluteFile queryAbsoluteFile="$(Conf::getAbsoluteFile "dbQueries" "${argQuery}" "sql")" && { + # shellcheck disable=SC2034 queryIsFile="1" argQuery="${queryAbsoluteFile}" Log::displayInfo "Using query file ${queryAbsoluteFile}" @@ -2184,71 +1775,801 @@ argQueryCallback() { dbQueryAllDatabasesCommandCallback() { if [[ -z "${optionFromDsn}" ]]; then # default value for FROM_DSN if from-aws not set - optionFromDsn="default.remote" + optionFromDsn="${defaultFromDsn}" fi } -dbQueryAllDatabasesCommand parse "${BASH_FRAMEWORK_ARGV[@]}" -declare awkScript -awkScript="$( - cat <<'EOF' -BEGIN{ - headerPrinted=0 -} -{ - buffer = substr($0, 0, 35) - line = $0 - if(buffer == "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@") { - if (headerPrinted == 0) { - line=substr(line, 36) - headerPrinted=1 - } else { - next - } - } - print line +# ------------------------------------------ +# Command dbQueryAllDatabasesCommand +# ------------------------------------------ + +# options variables initialization +declare optionJobs="1" +declare optionProgressBar="0" +declare optionHelp="0" +declare optionConfig="0" +declare optionBashFrameworkConfig="" +declare optionInfoVerbose="0" +declare optionDebugVerbose="0" +declare optionTraceVerbose="0" +declare -a optionEnvFiles=() +declare optionLogLevel="" +declare optionLogFile="" +declare optionDisplayLevel="" +declare optionNoColor="0" +declare optionTheme="default" +declare optionVersion="0" +declare optionQuiet="0" +declare optionSeparator="|" +declare optionFromDsn="" +# arguments variables initialization +declare argQuery="" +# @description parse command options and arguments for dbQueryAllDatabasesCommand +dbQueryAllDatabasesCommandParse() { + Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" + Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" + optionJobs="1" + local -i options_parse_optionParsedCountOptionJobs + ((options_parse_optionParsedCountOptionJobs = 0)) || true + optionProgressBar="0" + local -i options_parse_optionParsedCountOptionProgressBar + ((options_parse_optionParsedCountOptionProgressBar = 0)) || true + optionHelp="0" + local -i options_parse_optionParsedCountOptionHelp + ((options_parse_optionParsedCountOptionHelp = 0)) || true + optionConfig="0" + local -i options_parse_optionParsedCountOptionConfig + ((options_parse_optionParsedCountOptionConfig = 0)) || true + optionBashFrameworkConfig="" + local -i options_parse_optionParsedCountOptionBashFrameworkConfig + ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true + optionInfoVerbose="0" + local -i options_parse_optionParsedCountOptionInfoVerbose + ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true + optionDebugVerbose="0" + local -i options_parse_optionParsedCountOptionDebugVerbose + ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true + optionTraceVerbose="0" + local -i options_parse_optionParsedCountOptionTraceVerbose + ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true + + optionLogLevel="" + local -i options_parse_optionParsedCountOptionLogLevel + ((options_parse_optionParsedCountOptionLogLevel = 0)) || true + optionLogFile="" + local -i options_parse_optionParsedCountOptionLogFile + ((options_parse_optionParsedCountOptionLogFile = 0)) || true + optionDisplayLevel="" + local -i options_parse_optionParsedCountOptionDisplayLevel + ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true + optionNoColor="0" + local -i options_parse_optionParsedCountOptionNoColor + ((options_parse_optionParsedCountOptionNoColor = 0)) || true + optionTheme="default" + local -i options_parse_optionParsedCountOptionTheme + ((options_parse_optionParsedCountOptionTheme = 0)) || true + optionVersion="0" + local -i options_parse_optionParsedCountOptionVersion + ((options_parse_optionParsedCountOptionVersion = 0)) || true + optionQuiet="0" + local -i options_parse_optionParsedCountOptionQuiet + ((options_parse_optionParsedCountOptionQuiet = 0)) || true + optionSeparator="|" + local -i options_parse_optionParsedCountOptionSeparator + ((options_parse_optionParsedCountOptionSeparator = 0)) || true + optionFromDsn="" + local -i options_parse_optionParsedCountOptionFromDsn + ((options_parse_optionParsedCountOptionFromDsn = 0)) || true + + argQuery="" + local -i options_parse_argParsedCountArgQuery + ((options_parse_argParsedCountArgQuery = 0)) || true + + + # shellcheck disable=SC2034 + local -i options_parse_parsedArgIndex=0 + while (($# > 0)); do + local options_parse_arg="$1" + local argOptDefaultBehavior=0 + case "${options_parse_arg}" in + # Option 1/18 + # optionJobs alts --jobs|-j + # type: String min 0 max 1 + --jobs | -j) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionJobs >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionJobs)) + # shellcheck disable=SC2034 + optionJobs="$1" + optionJobsCallback "${options_parse_arg}" "${optionJobs}" + + ;; + + # Option 2/18 + # optionProgressBar alts --bar|-b + # type: Boolean min 0 max 1 + --bar | -b) + # shellcheck disable=SC2034 + optionProgressBar="1" + + if ((options_parse_optionParsedCountOptionProgressBar >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionProgressBar)) + optionProgressBarCallback "${options_parse_arg}" "${optionProgressBar}" + + ;; + + # Option 3/18 + # optionHelp alts --help|-h + # type: Boolean min 0 max 1 + --help | -h) + # shellcheck disable=SC2034 + optionHelp="1" + + if ((options_parse_optionParsedCountOptionHelp >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionHelp)) + optionHelpCallback "${options_parse_arg}" "${optionHelp}" + + ;; + + # Option 4/18 + # optionConfig alts --config + # type: Boolean min 0 max 1 + --config) + # shellcheck disable=SC2034 + optionConfig="1" + + if ((options_parse_optionParsedCountOptionConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionConfig)) + ;; + + # Option 5/18 + # optionBashFrameworkConfig alts --bash-framework-config + # type: String min 0 max 1 + --bash-framework-config) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) + # shellcheck disable=SC2034 + optionBashFrameworkConfig="$1" + optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" + + ;; + + # Option 6/18 + # optionInfoVerbose alts --verbose|-v + # type: Boolean min 0 max 1 + --verbose | -v) + # shellcheck disable=SC2034 + optionInfoVerbose="1" + + if ((options_parse_optionParsedCountOptionInfoVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionInfoVerbose)) + optionInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + updateArgListInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + ;; + + # Option 7/18 + # optionDebugVerbose alts -vv + # type: Boolean min 0 max 1 + -vv) + # shellcheck disable=SC2034 + optionDebugVerbose="1" + + if ((options_parse_optionParsedCountOptionDebugVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDebugVerbose)) + optionDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + updateArgListDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + ;; + + # Option 8/18 + # optionTraceVerbose alts -vvv + # type: Boolean min 0 max 1 + -vvv) + # shellcheck disable=SC2034 + optionTraceVerbose="1" + + if ((options_parse_optionParsedCountOptionTraceVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTraceVerbose)) + optionTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + updateArgListTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + ;; + + # Option 9/18 + # optionEnvFiles alts --env-file + # type: StringArray min 0 max -1 + --env-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + ((++options_parse_optionParsedCountOptionEnvFiles)) + optionEnvFiles+=("$1") + optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + ;; + + # Option 10/18 + # optionLogLevel alts --log-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --log-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogLevel)) + # shellcheck disable=SC2034 + optionLogLevel="$1" + optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + ;; + + # Option 11/18 + # optionLogFile alts --log-file + # type: String min 0 max 1 + --log-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogFile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogFile)) + # shellcheck disable=SC2034 + optionLogFile="$1" + optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + ;; + + # Option 12/18 + # optionDisplayLevel alts --display-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --display-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionDisplayLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDisplayLevel)) + # shellcheck disable=SC2034 + optionDisplayLevel="$1" + optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + ;; + + # Option 13/18 + # optionNoColor alts --no-color + # type: Boolean min 0 max 1 + --no-color) + # shellcheck disable=SC2034 + optionNoColor="1" + + if ((options_parse_optionParsedCountOptionNoColor >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionNoColor)) + optionNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + updateArgListNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + ;; + + # Option 14/18 + # optionTheme alts --theme + # type: String min 0 max 1 + # authorizedValues: default|default-force|noColor + --theme) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ default|default-force|noColor ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionTheme >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTheme)) + # shellcheck disable=SC2034 + optionTheme="$1" + optionThemeCallback "${options_parse_arg}" "${optionTheme}" + + updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" + + ;; + + # Option 15/18 + # optionVersion alts --version + # type: Boolean min 0 max 1 + --version) + # shellcheck disable=SC2034 + optionVersion="1" + + if ((options_parse_optionParsedCountOptionVersion >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionVersion)) + optionVersionCallback "${options_parse_arg}" "${optionVersion}" + + ;; + + # Option 16/18 + # optionQuiet alts --quiet|-q + # type: Boolean min 0 max 1 + --quiet | -q) + # shellcheck disable=SC2034 + optionQuiet="1" + + if ((options_parse_optionParsedCountOptionQuiet >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionQuiet)) + optionQuietCallback "${options_parse_arg}" "${optionQuiet}" + + updateArgListQuietCallback "${options_parse_arg}" "${optionQuiet}" + + ;; + + # Option 17/18 + # optionSeparator alts --separator|-s + # type: String min 0 max 1 + --separator | -s) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionSeparator >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionSeparator)) + # shellcheck disable=SC2034 + optionSeparator="$1" + optionSeparatorCallback "${options_parse_arg}" "${optionSeparator}" + + ;; + + # Option 18/18 + # optionFromDsn alts --from-dsn|-f + # type: String min 0 max 1 + --from-dsn | -f) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionFromDsn >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionFromDsn)) + # shellcheck disable=SC2034 + optionFromDsn="$1" + ;; + + -*) + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" + return 1 + fi + ;; + *) + ((minParsedArgIndex0 = 0)) || true + ((maxParsedArgIndex0 = 0)) || true + ((minParsedArgIndex1 = minParsedArgIndex0 + 1)) || true + ((maxParsedArgIndex1 = maxParsedArgIndex0 + 1)) || true + ((incrementArg = 1 )) + if ((0)); then + # Technical if - never reached + : + + # Argument 1/1 - argQuery + # Argument argQuery min 1 max 1 + # Argument argQuery authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && + options_parse_parsedArgIndex < maxParsedArgIndex1 )); then + if ((options_parse_argParsedCountArgQuery >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument argQuery - Maximum number of argument occurrences reached(1)" + return 1 + fi + ((++options_parse_argParsedCountArgQuery)) + # shellcheck disable=SC2034 + argQuery="${options_parse_arg}" + argQueryCallback "${argQuery}" -- "${@:2}" + + + # else too much args + else + + # shellcheck disable=SC2317 + dbQueryAllDatabasesEveryArgumentCallback "" "${options_parse_arg}"|| argOptDefaultBehavior=$? + + + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + # too much args and no unknownArgumentCallbacks configured + Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" + return 1 + fi + + fi + if ((incrementArg == 1)); then + ((++options_parse_parsedArgIndex)) + fi + ;; + esac + shift || true + done + + + + + + + + + + + + + + + + + + + if ((options_parse_argParsedCountArgQuery < 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument 'argQuery' should be provided at least 1 time(s)" + return 1 + fi || return $? + + + commandOptionParseFinished + dbQueryAllDatabasesCommandCallback } -EOF -)" +# @description display command options and arguments help for dbQueryAllDatabasesCommand +dbQueryAllDatabasesCommandHelp() { + echo -e "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" + Array::wrap2 ' ' 76 4 " " "Execute a query on multiple databases to generate a tsv format report.\\n The query can be parallelized on multiple databases." -# @require Linux::requireExecutedAsUser -run() { + echo + echo - # query contains the sql from argQuery or from query string if -q option is provided - declare query="${argQuery}" - if [[ "${queryIsFile}" = "1" ]]; then - query="$(cat "${argQuery}")" - fi + # ------------------------------------------ + # usage section + # ------------------------------------------ + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "dbQueryAllDatabases [OPTIONS] [ARGUMENTS]" + echo + # ------------------------------------------ + # usage/options section + # ------------------------------------------ + optionsAltList=("[--jobs|-j ]" "[--bar|-b]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--separator|-s ]" "[--from-dsn|-f ]" + ) + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ + "dbQueryAllDatabases" "${optionsAltList[@]}" + echo - declare -Agx dbInstance - Database::newInstance dbInstance "${optionFromDsn}" - Database::setQueryOptions dbInstance "${dbInstance['QUERY_OPTIONS']} --connect-timeout=5" - Log::displayInfo "Using dsn ${dbInstance['DSN_FILE']}" + # ------------------------------------------ + # usage/arguments section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - # list of all databases - allDbs="$(Database::getUserDbList dbInstance)" - PARALLEL_OPTIONS+=("--linebuffer" "-j" "${optionJobs}") + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}argQuery${__HELP_NORMAL} {single} (mandatory) + " + argQueryHelpFunction - # query all databases - export query - export optionSeparator - export optionFromDsn - # shellcheck disable=SC2154 - echo "${allDbs}" | - SHELL=$(type -p bash) parallel --eta --progress -m "${PARALLEL_OPTIONS[@]}" \ - ::: "$0" "DbQueryOneDatabase" "${optionFromDsn}" | - awk --source "${awkScript}" - + # ------------------------------------------ + # options section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}JOB OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--jobs${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-j ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "The number of databases to query in parallel." + echo + + + Array::wrap2 ' ' 76 6 " Default value: " "1" + echo + echo + echo -e "${__HELP_TITLE_COLOR}PROGRESS BAR OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--bar${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-b${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Show progress as a progress bar. In the bar is shown: % of jobs completed," "estimated seconds left, and number of jobs started." "" + echo + + + echo + echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays this command help" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays configuration" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" + Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log file" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set display level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." + echo + Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" + echo + + Array::wrap2 ' ' 76 6 " Default value: " "default" + echo + + echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Print version information and quit." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." + echo + + + echo + echo -e "${__HELP_TITLE_COLOR}QUERY OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--separator${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-s ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Character to use to separate mysql column." + echo + + + Array::wrap2 ' ' 76 6 " Default value: " "|" + echo + + echo -e " ${__HELP_OPTION_COLOR}--from-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-f ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Target mysql server." + echo + + + # ------------------------------------------ + # longDescription section + # ------------------------------------------ + echo + echo + echo -e "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" + longDescriptionFunction + # ------------------------------------------ + # version section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" + echo "2.0" + # ------------------------------------------ + # author section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}AUTHOR: ${__RESET_COLOR}" + echo "[François Chastanet](https://github.com/fchastanet)" + # ------------------------------------------ + # sourceFile section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml" + # ------------------------------------------ + # license section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}LICENSE: ${__RESET_COLOR}" + echo "MIT License" + # ------------------------------------------ + # copyright section + # ------------------------------------------ + Array::wrap2 ' ' 76 0 "$(copyrightCallback)" } -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run + +beforeParseCallback +initConf + +dbQueryAllDatabasesCommandParse "$@" +MAIN_FUNCTION_NAME="main" +main() { + +Linux::requireTarCommand +Compiler::Embed::extractFileFromBase64 \ + "${PERSISTENT_TMPDIR:-/tmp}/f256a4bbcf47bbd258e277f86718408c/dbQueryAllDatabasesScript" \ + "QkVHSU57CiAgaGVhZGVyUHJpbnRlZD0wCn0KL14kLyB7bmV4dH0KewogIGJ1ZmZlciA9IHN1YnN0cigkMCwgMCwgMzUpCiAgbGluZSA9ICQwCiAgaWYoYnVmZmVyID09ICJAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQCIpIHsKICAgIGlmIChoZWFkZXJQcmludGVkID09IDApIHsKICAgICAgbGluZT1zdWJzdHIobGluZSwgMzYpCiAgICAgIGhlYWRlclByaW50ZWQ9MQogICAgfSBlbHNlIHsKICAgICAgbmV4dAogICAgfQogIH0KICBwcmludCBsaW5lCgp9Cg==" \ + "644" + +declare -gx embed_file_dbQueryAllDatabasesScript="${PERSISTENT_TMPDIR:-/tmp}/f256a4bbcf47bbd258e277f86718408c/dbQueryAllDatabasesScript" + + +# later on, parallel calls this script(inception) +if [[ "${DB_QUERY_ALL_DATABASES_COMMAND}" = "DbQueryOneDatabase" ]]; then + shift || true + Linux::requireExecutedAsUser + Db::queryOneDatabase "${ORIGINAL_BASH_FRAMEWORK_ARGV[@]}" + exit 0 fi -} +# query contains the sql from argQuery or from query string if -q option is provided +# shellcheck disable=SC2154 +declare query="${argQuery}" +# shellcheck disable=SC2154 +if [[ "${queryIsFile}" = "1" ]]; then + query="$(cat "${argQuery}")" +fi -facade_main_dbQueryAllDatabasessh "$@" +declare -Agx dbInstance +Database::newInstance dbInstance "${optionFromDsn}" +Database::setQueryOptions dbInstance "${dbInstance['QUERY_OPTIONS']} --connect-timeout=5" +Log::displayInfo "Using dsn ${dbInstance['DSN_FILE']}" + +# list of all databases +allDbs="$(Database::getUserDbList dbInstance)" +PARALLEL_OPTIONS+=("-j" "${optionJobs}") + +# query all databases +export query +export optionSeparator +export optionFromDsn +export DB_QUERY_ALL_DATABASES_COMMAND="DbQueryOneDatabase" +# shellcheck disable=SC2154 +echo "${allDbs}" | + SHELL=$(type -p bash) \ + parallel --bar --eta --progress "${PARALLEL_OPTIONS[@]}" \ + "$0" "${optionFromDsn}" | + awk -f "${embed_file_dbQueryAllDatabasesScript}" - + +} + +# if file is sourced avoid calling main function +# shellcheck disable=SC2178 +BASH_SOURCE=".$0" # cannot be changed in bash +# shellcheck disable=SC2128 +if test ".$0" == ".${BASH_SOURCE}"; then + if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then + main "$@" &>/dev/null + else + main "$@" + fi +fi diff --git a/bin/doc b/bin/doc index 5e767439..45cc4093 100755 --- a/bin/doc +++ b/bin/doc @@ -1777,8 +1777,9 @@ docCommandParse() { # @description display command options and arguments help for docCommand docCommandHelp() { - Array::wrap2 ' ' 80 0 "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" \ - "Generate markdown documentation." + echo -e "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" + Array::wrap2 ' ' 76 4 " " "Generate markdown documentation." + echo echo diff --git a/bin/install b/bin/install index eff90144..8fc804fd 100755 --- a/bin/install +++ b/bin/install @@ -1487,8 +1487,9 @@ installCommandParse() { # @description display command options and arguments help for installCommand installCommandHelp() { - Array::wrap2 ' ' 80 0 "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" \ - "Installs bash-tools." + echo -e "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" + Array::wrap2 ' ' 76 4 " " "Installs bash-tools." + echo echo diff --git a/bin/installRequirements b/bin/installRequirements index 76a7b1c3..211b19d4 100755 --- a/bin/installRequirements +++ b/bin/installRequirements @@ -1490,8 +1490,9 @@ installRequirementsCommandParse() { # @description display command options and arguments help for installRequirementsCommand installRequirementsCommandHelp() { - Array::wrap2 ' ' 80 0 "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" \ - "Installs requirements." + echo -e "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" + Array::wrap2 ' ' 76 4 " " "Installs requirements." + echo echo diff --git a/bin/mysql2puml b/bin/mysql2puml index 2d37fe89..1950a4db 100755 --- a/bin/mysql2puml +++ b/bin/mysql2puml @@ -1724,8 +1724,9 @@ mysql2pumlCommandParse() { # @description display command options and arguments help for mysql2pumlCommand mysql2pumlCommandHelp() { - Array::wrap2 ' ' 80 0 "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" \ - "Converts mysql dump sql schema to plantuml format." + echo -e "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" + Array::wrap2 ' ' 76 4 " " "Converts mysql dump sql schema to plantuml format." + echo echo @@ -1751,7 +1752,7 @@ mysql2pumlCommandHelp() { Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}sqlFile${__HELP_NORMAL} {single}] " - Array::wrap2 ' ' 76 4 " " "Sql filepath to parse (read from stdin if not provided)." + Array::wrap2 ' ' 76 4 " " "Sql filepath to parse (read from stdin if not provided)." echo # ------------------------------------------ # options section diff --git a/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt b/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt index 897faea0..6e850ec4 100644 --- a/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt +++ b/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt @@ -1,4 +1,5 @@ -SYNOPSIS: Converts mysql dump sql schema to plantuml format. +SYNOPSIS: + Converts mysql dump sql schema to plantuml format. USAGE: mysql2puml [OPTIONS] [ARGUMENTS] USAGE: mysql2puml [--help|-h] [--config] diff --git a/src/_binaries/Database/dbImport/testsData/dbImport.help.txt b/src/_binaries/Database/dbImport/testsData/dbImport.help.txt index 29dc37e7..a5f2038d 100644 --- a/src/_binaries/Database/dbImport/testsData/dbImport.help.txt +++ b/src/_binaries/Database/dbImport/testsData/dbImport.help.txt @@ -1,5 +1,5 @@ SYNOPSIS: -Import source db into target db using eventual table filter. + Import source db into target db using eventual table filter. USAGE: dbImport [OPTIONS] [ARGUMENTS] USAGE: dbImport [--collation-name|-o ] diff --git a/src/_binaries/Database/dbImportProfile/dbImportProfile-options.sh b/src/_binaries/Database/dbImportProfile/dbImportProfile-options.sh index c5130972..c4f1ffac 100755 --- a/src/_binaries/Database/dbImportProfile/dbImportProfile-options.sh +++ b/src/_binaries/Database/dbImportProfile/dbImportProfile-options.sh @@ -41,24 +41,24 @@ longDescriptionFunction() { optionProfileHelpFunction() { Array::wrap2 " " 80 4 \ - " The name of the profile to write in profiles directory.\r" \ + " The name of the profile to write in profiles directory.\n" \ "If not provided, the file name pattern will be 'auto__.sh'" echo } optionFromDsnHelpFunction() { Array::wrap2 " " 80 4 \ - " dsn to use for source database (Default: ${defaultFromDsn})\r" \ + " dsn to use for source database (Default: ${defaultFromDsn})\n" \ "if not provided, the file name pattern will be 'auto__.sh'" echo } optionRatioHelpFunction() { Array::wrap2 " " 80 4 \ - " define the ratio to use (0 to 100% - default 70).\r" \ - "- 0 means profile will filter out all the tables\r" \ - "- 100 means profile will keep all the tables.\r" \ - "Eg: 70 means that tables with size(table+index)\r" \ + " define the ratio to use (0 to 100% - default 70).\n" \ + "- 0 means profile will filter out all the tables\n" \ + "- 100 means profile will keep all the tables.\n" \ + "Eg: 70 means that tables with size(table+index)\n" \ "that are greater than 70% of the max table size will be excluded." echo } diff --git a/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt b/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt index 18aa4a26..4c19ec9c 100644 --- a/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt +++ b/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt @@ -1,4 +1,5 @@ -SYNOPSIS: Generate optimized profiles to be used by dbImport. +SYNOPSIS: + Generate optimized profiles to be used by dbImport. USAGE: dbImportProfile [OPTIONS] [ARGUMENTS] USAGE: dbImportProfile [--help|-h] [--config] @@ -51,16 +52,22 @@ OPTIONS: --profile, -p  {single} The name of the profile to write in profiles directory. + If not provided, the file name pattern will be 'auto__.sh' --from-dsn, -f  {single} dsn to use for source database (Default: default.remote) + if not provided, the file name pattern will be 'auto__.sh' Default value: default.remote --ratio, -r  {single} define the ratio to use (0 to 100% - default 70). + - 0 means profile will filter out all the tables + - 100 means profile will keep all the tables. + Eg: 70 means that tables with size(table+index) + that are greater than 70% of the max table size will be excluded. Default value: 70 diff --git a/src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt b/src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt index ab672725..9b5bc05d 100644 --- a/src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt +++ b/src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt @@ -1,4 +1,5 @@ -SYNOPSIS: Stream tar.gz file or gz file through mysql. +SYNOPSIS: + Stream tar.gz file or gz file through mysql. USAGE: dbImportStream [OPTIONS] [ARGUMENTS] USAGE: dbImportStream [--profile|-p ] diff --git a/src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml b/src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml new file mode 100644 index 00000000..590147d9 --- /dev/null +++ b/src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml @@ -0,0 +1,68 @@ +extends: + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsJobs.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsProgressBar.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" + +vars: + SRC_FILE_PATH: src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml + +compilerConfig: + targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/dbQueryAllDatabases" + relativeRootDirBasedOnTargetDir: .. + srcDirs: + - ${BASH_TOOLS_ROOT_DIR}/src +binData: + commands: + default: + functionName: dbQueryAllDatabasesCommand + version: "2.0" + commandName: dbQueryAllDatabases + beforeParseCallbacks: + - beforeParseCallback + - initConf + callbacks: + - dbQueryAllDatabasesCommandCallback@100 + everyArgumentCallbacks: + - dbQueryAllDatabasesEveryArgumentCallback + definitionFiles: + 11: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.sh + 99: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-options.sh + mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-main.sh + optionGroups: + groupSourceDbOptions: + title: "QUERY OPTIONS:" + help: + "Execute a query on multiple databases to generate a tsv format + report.\\n The query can be parallelized on multiple databases." + longDescription: longDescriptionFunction + args: + - type: String + min: 1 + max: 1 + name: argQuery + variableName: argQuery + help: argQueryHelpFunction + callbacks: + - argQueryCallback + options: + - variableName: optionSeparator + group: groupSourceDbOptions + type: String + help: Character to use to separate mysql column. + helpValueName: separator + defaultValue: "|" + callbacks: + - optionSeparatorCallback + alts: + - --separator + - -s + - variableName: optionFromDsn + group: groupSourceDbOptions + type: String + help: Target mysql server. + helpValueName: dsn + alts: + - --from-dsn + - -f diff --git a/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-main.sh b/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-main.sh new file mode 100755 index 00000000..9a96faca --- /dev/null +++ b/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-main.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# @embed "${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases.awk" AS dbQueryAllDatabasesScript + +# later on, parallel calls this script(inception) +if [[ "${DB_QUERY_ALL_DATABASES_COMMAND}" = "DbQueryOneDatabase" ]]; then + shift || true + Linux::requireExecutedAsUser + Db::queryOneDatabase "${ORIGINAL_BASH_FRAMEWORK_ARGV[@]}" + exit 0 +fi + +# query contains the sql from argQuery or from query string if -q option is provided +# shellcheck disable=SC2154 +declare query="${argQuery}" +# shellcheck disable=SC2154 +if [[ "${queryIsFile}" = "1" ]]; then + query="$(cat "${argQuery}")" +fi + +declare -Agx dbInstance +Database::newInstance dbInstance "${optionFromDsn}" +Database::setQueryOptions dbInstance "${dbInstance['QUERY_OPTIONS']} --connect-timeout=5" +Log::displayInfo "Using dsn ${dbInstance['DSN_FILE']}" + +# list of all databases +allDbs="$(Database::getUserDbList dbInstance)" +PARALLEL_OPTIONS+=("-j" "${optionJobs}") + +# query all databases +export query +export optionSeparator +export optionFromDsn +export DB_QUERY_ALL_DATABASES_COMMAND="DbQueryOneDatabase" +# shellcheck disable=SC2154 +echo "${allDbs}" | + SHELL=$(type -p bash) \ + parallel --bar --eta --progress "${PARALLEL_OPTIONS[@]}" \ + "$0" "${optionFromDsn}" | + awk -f "${embed_file_dbQueryAllDatabasesScript}" - diff --git a/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-options.sh b/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-options.sh new file mode 100755 index 00000000..3ba73815 --- /dev/null +++ b/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-options.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash + +declare QUERIES_DIR +declare HOME_QUERIES_DIR +declare queryIsFile="0" +declare optionSeparator="|" + +# shellcheck disable=SC2034 +declare versionNumber="2.0" +# shellcheck disable=SC2034 +declare copyrightBeginYear="2020" +# shellcheck disable=SC2034 +declare defaultFromDsn="default.remote" + +beforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad + Linux::requireExecutedAsUser + Linux::requireRealpathCommand +} + +initConf() { + # shellcheck disable=SC2034 + QUERIES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbQueries" + HOME_QUERIES_DIR="${HOME}/.bash-tools/dbQueries" + + Db::checkRequirements +} + +optionHelpCallback() { + dbQueryAllDatabasesCommandHelp + exit 0 +} + +optionSeparatorCallback() { + # shellcheck disable=SC2154 + if ((${#optionSeparator} != 1)); then + Log::fatal "Command ${SCRIPT_NAME} - only one character is accepted as separator" + fi + + if [[ ${optionSeparator} =~ [a-zA-Z0-9/\ ] ]]; then + Log::fatal "Command ${SCRIPT_NAME} - characters alphanumeric, slash(/) and space( ) are not supported as separator" + fi +} + +longDescriptionFunction() { + local example1=$'dbQueryAllDatabases databaseSize -j 12 --separator "|" --bar 2>/dev/null | column -s "|" -t -n -c 40' + + local dsnList queriesList + dsnList="$(Conf::getMergedList "dsn" "env" " - ")" + queriesList="$(Conf::getMergedList "dbQueries" "sql" " - " || true)" + + echo -e "${__HELP_TITLE} LIST OF AVAILABLE DSN:${__HELP_NORMAL}" + echo -e "${dsnList}" + echo + echo -e "${__HELP_TITLE} DEFAULT QUERIES DIRECTORY:${__HELP_NORMAL}" + echo -e " ${QUERIES_DIR-configuration error}" + echo + echo -e "${__HELP_TITLE} USER QUERIES DIRECTORY:${__HELP_NORMAL}" + echo -e " ${HOME_QUERIES_DIR-configuration error}" + echo -e " Allows to override queries defined in 'Default queries directory'" + echo + echo -e "${__HELP_TITLE} LIST OF AVAILABLE QUERIES:${__HELP_NORMAL}" + echo -e "${queriesList}" + echo + echo -e "${__HELP_TITLE} EXAMPLES:${__HELP_NORMAL}" + echo -e " ${__HELP_EXAMPLE}${example1}${__HELP_NORMAL}" +} + +argQueryHelpFunction() { + Array::wrap2 " " 80 6 \ + " Query to execute" "\n" \ + "- , try to execute the mysql query provided by the file" "\n" \ + '- , search for query file in queries directory (see below)' "\n" \ + '- else the argument is interpreted as query string' +} + +dbQueryAllDatabasesEveryArgumentCallback() { + if [[ -n "${DB_QUERY_ALL_DATABASES_COMMAND:-}" ]]; then + # a command is defined, then accept that arguments can be overridden + return 1 + fi +} + +argQueryCallback() { + if [[ -f "${argQuery}" ]]; then + queryIsFile="1" + else + declare queryAbsoluteFile + queryAbsoluteFile="$(Conf::getAbsoluteFile "dbQueries" "${argQuery}" "sql")" && { + # shellcheck disable=SC2034 + queryIsFile="1" + argQuery="${queryAbsoluteFile}" + Log::displayInfo "Using query file ${queryAbsoluteFile}" + } + fi +} + +dbQueryAllDatabasesCommandCallback() { + if [[ -z "${optionFromDsn}" ]]; then + # default value for FROM_DSN if from-aws not set + optionFromDsn="${defaultFromDsn}" + fi +} diff --git a/src/_binaries/DbQueryAllDatabases/dbQueryAllDatabases.awk b/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases.awk similarity index 95% rename from src/_binaries/DbQueryAllDatabases/dbQueryAllDatabases.awk rename to src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases.awk index 73a34cc9..24d199e2 100644 --- a/src/_binaries/DbQueryAllDatabases/dbQueryAllDatabases.awk +++ b/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases.awk @@ -1,6 +1,7 @@ BEGIN{ headerPrinted=0 } +/^$/ {next} { buffer = substr($0, 0, 35) line = $0 diff --git a/src/_binaries/DbQueryAllDatabases/dbQueryAllDatabases.bats b/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases.bats similarity index 85% rename from src/_binaries/DbQueryAllDatabases/dbQueryAllDatabases.bats rename to src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases.bats index 7b6ad94c..baf31aa8 100755 --- a/src/_binaries/DbQueryAllDatabases/dbQueryAllDatabases.bats +++ b/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases.bats @@ -1,7 +1,7 @@ #!/usr/bin/env bash # shellcheck source=src/batsHeaders.sh -source "$(cd "${BATS_TEST_DIRNAME}/../.." && pwd)/batsHeaders.sh" +source "$(cd "${BATS_TEST_DIRNAME}/../../.." && pwd)/batsHeaders.sh" setup() { export TMPDIR="${BATS_TEST_TMPDIR}" @@ -49,13 +49,13 @@ function Database::dbQueryAllDatabases::query_file_not_provided { #@test function Database::dbQueryAllDatabases::providingEnvFileChangeDbConnectionParametersAndRetrieveDbSize { #@test # shellcheck disable=SC2016,SC2086 stub mysql \ - '\* --batch --raw --default-character-set=utf8 --connect-timeout=5 -s --skip-column-names -e \* : echo "$9" >'" "${HOME}/query1" ; cat "${BATS_TEST_DIRNAME}/testsData/getUserDbList.result"" \ - '\* --batch --raw --default-character-set=utf8 --connect-timeout=5 db1 -e \* : echo -n "${8}" >'" "${HOME}/query2" ; cat "${BATS_TEST_DIRNAME}/testsData/databaseSize.result_db1"" \ - '\* --batch --raw --default-character-set=utf8 --connect-timeout=5 db2 -e \* : echo -n "${8}" >'" "${HOME}/query3" ; cat "${BATS_TEST_DIRNAME}/testsData/databaseSize.result_db2"" + '\* --batch --raw --default-character-set=utf8 --connect-timeout=5 -s --skip-column-names -e \* : echo "$9" >'" '${HOME}/query1' ; cat '${BATS_TEST_DIRNAME}/testsData/getUserDbList.result'" \ + '\* --batch --raw --default-character-set=utf8 --connect-timeout=5 db1 -e \* : echo -n "${8}" >'" '${HOME}/query2' ; cat '${BATS_TEST_DIRNAME}/testsData/databaseSize.result_db1'" \ + '\* --batch --raw --default-character-set=utf8 --connect-timeout=5 db2 -e \* : echo -n "${8}" >'" '${HOME}/query3' ; cat '${BATS_TEST_DIRNAME}/testsData/databaseSize.result_db2'" # shellcheck disable=SC2016 stub parallel \ - '--eta --progress -m --linebuffer -j 1 ::: * DbQueryOneDatabase * * : while IFS= read -r db; do "$8" "$9" "${10}" "${db}"; done' + '--bar --eta --progress -j 1 * * * : while IFS= read -r db; do export DB_QUERY_ALL_DATABASES_COMMAND="DbQueryOneDatabase"; "$6" "$7" "${db}"; done' f() { # shellcheck disable=SC2317 @@ -88,7 +88,7 @@ function Database::dbQueryAllDatabases::multipleJobs { #@test # shellcheck disable=SC2016 stub parallel \ - '--eta --progress -m --linebuffer -j 8 ::: * DbQueryOneDatabase * * : while IFS= read -r db; do "$8" "$9" "${10}" "${db}"; done' + '--bar --eta --progress -j 8 * * : while IFS= read -r db; do export DB_QUERY_ALL_DATABASES_COMMAND="DbQueryOneDatabase"; "$6" "$7" "${db}"; done' f() { # shellcheck disable=SC2317 @@ -100,7 +100,6 @@ function Database::dbQueryAllDatabases::multipleJobs { #@test run f assert_output "$(cat "${BATS_TEST_DIRNAME}/testsData/dbQueryAllDatabases.result")" [[ -f "${HOME}/query1" ]] - cat "${HOME}/query1" [[ "$(cat "${HOME}/query1")" == "$(cat "${BATS_TEST_DIRNAME}/testsData/getUserDbList.query")" ]] [[ -f "${HOME}/query2" ]] echo >>"${HOME}/query2" # add a new line as megalinter add a newline at end of file diff --git a/src/_binaries/DbQueryAllDatabases/testsData/databaseSize.envProvided.sh b/src/_binaries/Database/dbQueryAllDatabases/testsData/databaseSize.envProvided.sh similarity index 100% rename from src/_binaries/DbQueryAllDatabases/testsData/databaseSize.envProvided.sh rename to src/_binaries/Database/dbQueryAllDatabases/testsData/databaseSize.envProvided.sh diff --git a/src/_binaries/DbQueryAllDatabases/testsData/databaseSize.result_db1 b/src/_binaries/Database/dbQueryAllDatabases/testsData/databaseSize.result_db1 similarity index 100% rename from src/_binaries/DbQueryAllDatabases/testsData/databaseSize.result_db1 rename to src/_binaries/Database/dbQueryAllDatabases/testsData/databaseSize.result_db1 diff --git a/src/_binaries/DbQueryAllDatabases/testsData/databaseSize.result_db2 b/src/_binaries/Database/dbQueryAllDatabases/testsData/databaseSize.result_db2 similarity index 100% rename from src/_binaries/DbQueryAllDatabases/testsData/databaseSize.result_db2 rename to src/_binaries/Database/dbQueryAllDatabases/testsData/databaseSize.result_db2 diff --git a/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt b/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt new file mode 100644 index 00000000..2bf72ab4 --- /dev/null +++ b/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt @@ -0,0 +1,102 @@ +SYNOPSIS: + Execute a query on multiple databases to generate a tsv format report. + The query can be parallelized on multiple databases. + +USAGE: dbQueryAllDatabases [OPTIONS] [ARGUMENTS] +USAGE: dbQueryAllDatabases [--jobs|-j ] [--bar|-b] + [--help|-h] [--config] [--bash-framework-config ] + [--verbose|-v] [-vv] [-vvv] [--env-file ] [--log-level ] + [--log-file ] [--display-level ] [--no-color] + [--theme ] [--version] [--quiet|-q] [--separator|-s ] + [--from-dsn|-f ] + +ARGUMENTS: + argQuery {single} (mandatory) + Query to execute + - , try to execute the mysql query provided by the file + + - , search for query file in queries directory (see below) + + - else the argument is interpreted as query string +JOB OPTIONS: + --jobs, -j  {single} + The number of databases to query in parallel. + Default value: 1 + +PROGRESS BAR OPTIONS: + --bar, -b {single} + + Show progress as a progress bar. In the bar is shown: % of jobs completed, + estimated seconds left, and number of jobs started. + + +GLOBAL OPTIONS: + --help, -h {single} + Displays this command help + --config {single} + Displays configuration + --bash-framework-config  {single} + Use alternate bash framework configuration. + --verbose, -v {single} + Info level verbose mode (alias of --display-level INFO) + -vv {single} + Debug level verbose mode (alias of --display-level DEBUG) + -vvv {single} + Trace level verbose mode (alias of --display-level TRACE) + --env-file  {list} (optional) + Load the specified env file (deprecated, please use --bash-framework-con + fig option instead) + --log-level  {single} + Set log level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --log-file  {single} + Set log file + --display-level  {single} + Set display level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --no-color {single} + Produce monochrome output. alias of --theme noColor. + --theme  {single} + Choose color theme - default-force means colors will be produced even if + command is piped. + Possible values: default, default-force, noColor + Default value: default + --version {single} + Print version information and quit. + --quiet, -q {single} + Quiet mode, doesn't display any output. + +QUERY OPTIONS: + --separator, -s  {single} + Character to use to separate mysql column. + Default value: | + --from-dsn, -f  {single} + Target mysql server. + + +DESCRIPTION: + LIST OF AVAILABLE DSN: + - dsn_local + - dsn_valid + + DEFAULT QUERIES DIRECTORY: + /bash/conf/dbQueries + + USER QUERIES DIRECTORY: + home/.bash-tools/dbQueries + Allows to override queries defined in 'Default queries directory' + + LIST OF AVAILABLE QUERIES: + + + EXAMPLES: + dbQueryAllDatabases databaseSize -j 12 --separator "|" --bar 2>/dev/null | column -s "|" -t -n -c 40 + +VERSION: 2.0 + +AUTHOR: [François Chastanet](https://github.com/fchastanet) + +SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml + +LICENSE: MIT License +Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/DbQueryAllDatabases/testsData/dbQueryAllDatabases.result b/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.result similarity index 100% rename from src/_binaries/DbQueryAllDatabases/testsData/dbQueryAllDatabases.result rename to src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.result diff --git a/src/_binaries/DbQueryAllDatabases/testsData/dsn_local.env b/src/_binaries/Database/dbQueryAllDatabases/testsData/dsn_local.env similarity index 100% rename from src/_binaries/DbQueryAllDatabases/testsData/dsn_local.env rename to src/_binaries/Database/dbQueryAllDatabases/testsData/dsn_local.env diff --git a/src/_binaries/DbQueryAllDatabases/testsData/dsn_valid.env b/src/_binaries/Database/dbQueryAllDatabases/testsData/dsn_valid.env similarity index 100% rename from src/_binaries/DbQueryAllDatabases/testsData/dsn_valid.env rename to src/_binaries/Database/dbQueryAllDatabases/testsData/dsn_valid.env diff --git a/src/_binaries/DbQueryAllDatabases/testsData/gawk b/src/_binaries/Database/dbQueryAllDatabases/testsData/gawk similarity index 100% rename from src/_binaries/DbQueryAllDatabases/testsData/gawk rename to src/_binaries/Database/dbQueryAllDatabases/testsData/gawk diff --git a/src/_binaries/DbQueryAllDatabases/testsData/getUserDbList.query b/src/_binaries/Database/dbQueryAllDatabases/testsData/getUserDbList.query similarity index 100% rename from src/_binaries/DbQueryAllDatabases/testsData/getUserDbList.query rename to src/_binaries/Database/dbQueryAllDatabases/testsData/getUserDbList.query diff --git a/src/_binaries/DbQueryAllDatabases/testsData/getUserDbList.result b/src/_binaries/Database/dbQueryAllDatabases/testsData/getUserDbList.result similarity index 100% rename from src/_binaries/DbQueryAllDatabases/testsData/getUserDbList.result rename to src/_binaries/Database/dbQueryAllDatabases/testsData/getUserDbList.result diff --git a/src/_binaries/DbQueryAllDatabases/testsData/parallel b/src/_binaries/Database/dbQueryAllDatabases/testsData/parallel similarity index 100% rename from src/_binaries/DbQueryAllDatabases/testsData/parallel rename to src/_binaries/Database/dbQueryAllDatabases/testsData/parallel diff --git a/src/_binaries/DbQueryAllDatabases/testsData/pv b/src/_binaries/Database/dbQueryAllDatabases/testsData/pv similarity index 100% rename from src/_binaries/DbQueryAllDatabases/testsData/pv rename to src/_binaries/Database/dbQueryAllDatabases/testsData/pv diff --git a/src/_binaries/DbQueryAllDatabases/dbQueryAllDatabases.options.tpl b/src/_binaries/DbQueryAllDatabases/dbQueryAllDatabases.options.tpl deleted file mode 100644 index 4cba7255..00000000 --- a/src/_binaries/DbQueryAllDatabases/dbQueryAllDatabases.options.tpl +++ /dev/null @@ -1,117 +0,0 @@ -% -declare versionNumber="2.0" -declare commandFunctionName="dbQueryAllDatabasesCommand" -declare help="Execute a query on multiple databases in order to generate a report with tsv format, query can be parallelized on multiple databases" -# shellcheck disable=SC2016 -declare longDescription=''' -${__HELP_TITLE}LIST OF AVAILABLE DSN:${__HELP_NORMAL} -${dsnList} - -${__HELP_TITLE}DEFAULT QUERIES DIRECTORY:${__HELP_NORMAL} -${QUERIES_DIR-configuration error} - -${__HELP_TITLE}USER QUERIES DIRECTORY:${__HELP_NORMAL} -${HOME_QUERIES_DIR-configuration error} -Allows to override queries defined in "Default queries directory" - -${__HELP_TITLE}LIST OF AVAILABLE QUERIES:${__HELP_NORMAL} -${queriesList} - -${__HELP_TITLE}EXAMPLES:${__HELP_NORMAL} -${__HELP_EXAMPLE}${example1}${__HELP_NORMAL} -''' -declare defaultFromDsn="default.remote" -% - -declare example1=$'dbQueryAllDatabases databaseSize -j 12 --separator "|" --bar 2>/dev/null | column -s "|" -t -n -c 40' - -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.base.tpl)" -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.dsn.tpl)" -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.jobs.tpl)" -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.progressBar.tpl)" - -% -# shellcheck source=/dev/null -source <( - Options::generateGroup \ - --title "QUERY OPTIONS:" \ - --function-name groupSourceDbOptionsFunction - - argQueryCallback() { :; } - Options::generateArg \ - --min 1 \ - --max 1 \ - --help "$(echo \ - "Query to execute" $'\n' \ - "- , try to execute the mysql query provided by the file" $'\n' \ - '- , search for query file in queries directory (see below)' $'\n' \ - '- else the argument is interpreted as query string' \ - )" \ - --variable-name "argQuery" \ - --callback argQueryCallback \ - --function-name argQueryFunction - - optionSeparatorCallback() { :; } - # shellcheck disable=SC2116 - Options::generateOption \ - --help-value-name "separator" \ - --help "character to use to separate mysql column" \ - --alt "--separator" \ - --alt "-s" \ - --variable-type "String" \ - --default-value "|" \ - --callback optionSeparatorCallback \ - --variable-name "optionSeparator" \ - --function-name optionSeparatorFunction -) -options+=( - argQueryFunction - optionSeparatorFunction - --callback dbQueryAllDatabasesCommandCallback -) -Options::generateCommand "${options[@]}" -% - -.INCLUDE "$(dynamicTemplateDir _includes/dbTools.requirements.tpl)" - -optionHelpCallback() { - local dsnList queriesList - dsnList="$(Conf::getMergedList "dsn" "env")" - queriesList="$(Conf::getMergedList "dbQueries" "sql" || true)" - - <% ${commandFunctionName} %> help | envsubst - Db::checkRequirements - exit 0 -} - -optionSeparatorCallback() { - if ((${#optionSeparator} != 1)); then - Log::fatal "Command ${SCRIPT_NAME} - only one character is accepter as separator" - fi - - if [[ ${optionSeparator} =~ [a-zA-Z0-9/\ ] ]]; then - Log::fatal "Command ${SCRIPT_NAME} - characters alphanumeric, slash(/) and space( ) are not supported as separator" - fi -} - -argQueryCallback() { - if [[ -f "${argQuery}" ]]; then - queryIsFile="1" - else - declare queryAbsoluteFile - queryAbsoluteFile="$(Conf::getAbsoluteFile "dbQueries" "${argQuery}" "sql")" && { - queryIsFile="1" - argQuery="${queryAbsoluteFile}" - Log::displayInfo "Using query file ${queryAbsoluteFile}" - } - fi -} - -dbQueryAllDatabasesCommandCallback() { - if [[ -z "${optionFromDsn}" ]]; then - # default value for FROM_DSN if from-aws not set - optionFromDsn="<% ${defaultFromDsn} %>" - fi -} - -<% ${commandFunctionName} %> parse "${BASH_FRAMEWORK_ARGV[@]}" diff --git a/src/_binaries/DbQueryAllDatabases/dbQueryAllDatabases.sh b/src/_binaries/DbQueryAllDatabases/dbQueryAllDatabases.sh deleted file mode 100755 index d197d685..00000000 --- a/src/_binaries/DbQueryAllDatabases/dbQueryAllDatabases.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env bash -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/dbQueryAllDatabases -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE -# shellcheck disable=SC2034 - -# later on, parallel calls this script(inception) -if [[ "$1" = "DbQueryOneDatabase" ]]; then - shift || true - Linux::requireExecutedAsUser - Db::queryOneDatabase "$@" - exit 0 -fi - -#default values -# default value for FROM_DSN if from-aws not set -declare queryIsFile="0" -declare optionSeparator="|" -declare argQuery="" - -# other configuration -declare copyrightBeginYear="2020" -declare QUERIES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbQueries" -declare HOME_QUERIES_DIR="${HOME}/.bash-tools/dbQueries" - -.INCLUDE "$(dynamicTemplateDir _binaries/DbQueryAllDatabases/dbQueryAllDatabases.options.tpl)" - -declare awkScript -awkScript="$( - cat <<'EOF' -.INCLUDE "$(dynamicSrcFile "_binaries/DbQueryAllDatabases/dbQueryAllDatabases.awk")" - -EOF -)" - -# @require Linux::requireExecutedAsUser -run() { - - # query contains the sql from argQuery or from query string if -q option is provided - declare query="${argQuery}" - if [[ "${queryIsFile}" = "1" ]]; then - query="$(cat "${argQuery}")" - fi - - declare -Agx dbInstance - Database::newInstance dbInstance "${optionFromDsn}" - Database::setQueryOptions dbInstance "${dbInstance['QUERY_OPTIONS']} --connect-timeout=5" - Log::displayInfo "Using dsn ${dbInstance['DSN_FILE']}" - - # list of all databases - allDbs="$(Database::getUserDbList dbInstance)" - PARALLEL_OPTIONS+=("--linebuffer" "-j" "${optionJobs}") - - # query all databases - export query - export optionSeparator - export optionFromDsn - # shellcheck disable=SC2154 - echo "${allDbs}" | - SHELL=$(type -p bash) parallel --eta --progress -m "${PARALLEL_OPTIONS[@]}" \ - ::: "$0" "DbQueryOneDatabase" "${optionFromDsn}" | - awk --source "${awkScript}" - -} - -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run -fi diff --git a/src/_binaries/DbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt b/src/_binaries/DbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt deleted file mode 100644 index f117977c..00000000 --- a/src/_binaries/DbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt +++ /dev/null @@ -1,97 +0,0 @@ -DESCRIPTION: Execute a query on multiple databases in order to gene -rate a report with tsv format, query can be parallelized on multiple databases -USAGE: dbQueryAllDatabases [OPTIONS] [ARGUMENTS] -USAGE: dbQueryAllDatabases [--jobs|-j ] [--bar|-b] - [--separator|-s ] [--from-dsn|-f ] - [--bash-framework-config ] [--config] [--verbose|-v] [-vv] [-vvv] - [--env-file ] [--no-color] [--theme ] [--help|-h] [--version] - [--quiet|-q] [--log-level ] [--log-file ] - [--display-level ] - -ARGUMENTS: - argQuery {single} (mandatory) - Query to execute - - , try to execute the mysql query provided by the file - - , search for query file in queries directory (see below) - - else the argument is interpreted as query string - -OPTIONS: - --jobs, -j  {single} - specify the number of db to query in parallel - Default value: 1 - --bar, -b {single} - Show progress as a progress bar. In the bar is shown: % of jobs completed, e - stimated seconds left, and number of jobs started. - --separator, -s  {single} - character to use to separate mysql column - Default value: | - -QUERY OPTIONS: - --from-dsn, -f  {single} - target mysql server - -GLOBAL OPTIONS: - --bash-framework-config  {single} - use alternate bash framework configuration. - --config {single} - Display configuration - --verbose, -v {single} - info level verbose mode (alias of --display-level INFO) - -vv {single} - debug level verbose mode (alias of --display-level DEBUG) - -vvv {single} - trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-config - option instead) - --no-color {single} - Produce monochrome output. alias of --theme noColor. - --theme  {single} - choose color theme - default-force means colors will be produced even if com - mand is piped - Default value: default - Possible values: default|default-force|noColor - --help, -h {single} - Display this command help - --version {single} - Print version information and quit - --quiet, -q {single} - quiet mode, doesn't display any output - --log-level  {single} - Set log level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - --log-file  {single} - Set log file - --display-level  {single} - set display level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - -LIST OF AVAILABLE DSN: - - dsn_local - - dsn_valid - -DEFAULT QUERIES DIRECTORY: -/bash/conf/dbQueries - -USER QUERIES DIRECTORY: -home/.bash-tools/dbQueries -Allows to override queries defined in Default queries directory - -LIST OF AVAILABLE QUERIES: - - -EXAMPLES: -dbQueryAllDatabases databaseSize -j 12 --separator "|" --bar 2>/dev/null | column -s "|" -t -n -c 40 - -VERSION: 2.0 - -AUTHOR: -[François Chastanet](https://github.com/fchastanet) - -SOURCE FILE: -https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/DbQueryAllDatabases/dbQueryAllDatabases.sh - -LICENSE: -MIT License - -Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/commandDefinitions/optionsJobs.sh b/src/_binaries/commandDefinitions/optionsJobs.sh new file mode 100755 index 00000000..3d16ea27 --- /dev/null +++ b/src/_binaries/commandDefinitions/optionsJobs.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +optionJobsCallback() { + # shellcheck disable=SC2154 + if ! [[ "${optionJobs}" =~ ^[0-9]+$ ]]; then + Log::fatal "number of jobs is incorrect" + fi + + if [[ ${optionJobs} -lt 1 ]]; then + Log::fatal "number of jobs must be greater than 0" + fi +} diff --git a/src/_binaries/commandDefinitions/optionsJobs.yaml b/src/_binaries/commandDefinitions/optionsJobs.yaml new file mode 100644 index 00000000..b72c037b --- /dev/null +++ b/src/_binaries/commandDefinitions/optionsJobs.yaml @@ -0,0 +1,21 @@ +--- +binData: + commands: + default: + definitionFiles: + 26: "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsJobs.sh" + optionGroups: + groupJobOptions: + title: "JOB OPTIONS:" + options: + - variableName: optionJobs + group: groupJobOptions + type: String + help: The number of databases to query in parallel. + helpValueName: jobsCount + defaultValue: 1 + callbacks: + - optionJobsCallback + alts: + - --jobs + - -j diff --git a/src/_binaries/commandDefinitions/optionsProgressBar.sh b/src/_binaries/commandDefinitions/optionsProgressBar.sh new file mode 100755 index 00000000..a576d471 --- /dev/null +++ b/src/_binaries/commandDefinitions/optionsProgressBar.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +declare -a PARALLEL_OPTIONS + +optionProgressBarCallback() { + PARALLEL_OPTIONS+=(--bar) +} diff --git a/src/_binaries/commandDefinitions/optionsProgressBar.yaml b/src/_binaries/commandDefinitions/optionsProgressBar.yaml new file mode 100644 index 00000000..f9720872 --- /dev/null +++ b/src/_binaries/commandDefinitions/optionsProgressBar.yaml @@ -0,0 +1,21 @@ +--- +binData: + commands: + default: + definitionFiles: + 27: "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsProgressBar.sh" + optionGroups: + groupProgressBarOptions: + title: "PROGRESS BAR OPTIONS:" + options: + - variableName: optionProgressBar + group: groupProgressBarOptions + type: Boolean + help: | + Show progress as a progress bar. In the bar is shown: % of jobs completed, + estimated seconds left, and number of jobs started. + callbacks: + - optionProgressBarCallback + alts: + - --bar + - -b diff --git a/src/_binaries/options/options.jobs.tpl b/src/_binaries/options/options.jobs.tpl deleted file mode 100644 index 88362f24..00000000 --- a/src/_binaries/options/options.jobs.tpl +++ /dev/null @@ -1,32 +0,0 @@ -% - -# shellcheck source=/dev/null -source <( - optionJobsCallback() { :; } - # shellcheck disable=SC2116 - Options::generateOption \ - --help "specify the number of db to query in parallel" \ - --help-value-name "jobsCount" \ - --variable-type "String" \ - --default-value "1" \ - --alt "--jobs" \ - --alt "-j" \ - --callback optionJobsCallback \ - --variable-name "optionJobs" \ - --function-name optionJobsFunction - -) -options+=( - optionJobsFunction -) -% - -optionJobsCallback() { - if ! [[ ${optionJobs} =~ ^[0-9]+$ ]]; then - Log::fatal "number of jobs is incorrect" - fi - - if [[ ${optionJobs} -lt 1 ]]; then - Log::fatal "number of jobs must be greater than 0" - fi -} diff --git a/src/_binaries/options/options.progressBar.tpl b/src/_binaries/options/options.progressBar.tpl deleted file mode 100644 index b6f44608..00000000 --- a/src/_binaries/options/options.progressBar.tpl +++ /dev/null @@ -1,26 +0,0 @@ -% - -# shellcheck source=/dev/null -source <( - optionProgressBarCallback() { :; } - # shellcheck disable=SC2116 - Options::generateOption \ - --help "Show progress as a progress bar. In the bar is shown: % of jobs completed, estimated seconds left, and number of jobs started." \ - --variable-type "Boolean" \ - --alt "--bar" \ - --alt "-b" \ - --callback optionProgressBarCallback \ - --variable-name "optionProgressBar" \ - --function-name optionProgressBarFunction - -) -options+=( - optionProgressBarFunction -) -% - -declare -a PARALLEL_OPTIONS - -optionProgressBarCallback() { - PARALLEL_OPTIONS+=(--bar) -} From 26e783ada32df0c6d834c850080b5814f8f8ef2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Sun, 1 Sep 2024 22:26:14 +0200 Subject: [PATCH 09/24] compiled dbScriptAllDatabases using go compiler --- bin/dbImportProfile | 91 +- bin/dbQueryAllDatabases | 106 +- bin/dbScriptAllDatabases | 4152 +++++++++-------- .../binary-dbImportProfile.yaml | 11 +- .../testsData/dbImportProfile.help.txt | 14 +- .../binary-dbQueryAllDatabases.yaml | 18 +- .../testsData/dbQueryAllDatabases.help.txt | 22 +- .../binary-dbScriptAllDatabases.yaml | 89 + .../dbScriptAllDatabases-main.sh | 35 + .../dbScriptAllDatabases-options.sh | 105 + .../dbScriptAllDatabases.bats | 4 +- .../testsData/databaseSize.envProvided.sh | 0 .../testsData/databaseSize.result_db1 | 0 .../testsData/databaseSize.result_db2 | 0 .../testsData/dbScriptAllDatabases.help.txt | 122 + .../testsData/dbScriptAllDatabases.result | 0 .../testsData/dsn_local.env | 0 .../testsData/dsn_valid.env | 0 .../testsData/getUserDbList.result | 0 .../dbScriptAllDatabases}/testsData/parallel | 0 .../testsData/parallelDbScriptAllDatabases | 0 .../dbScriptAllDatabases}/testsData/pv | 0 .../dbScriptAllDatabases.options.tpl | 159 - .../dbScriptAllDatabases.sh | 64 - .../testsData/dbScriptAllDatabases.help.txt | 118 - .../testsData/getUserDbList.query | 1 - .../commandDefinitions/optionsFromDsn.yaml | 16 + .../commandDefinitions/optionsJobs.sh | 6 + .../commandDefinitions/optionsJobs.yaml | 12 + .../commandDefinitions/optionsProgressBar.sh | 7 - .../optionsProgressBar.yaml | 21 - 31 files changed, 2819 insertions(+), 2354 deletions(-) create mode 100644 src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml create mode 100755 src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-main.sh create mode 100755 src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-options.sh rename src/_binaries/{DbScriptAllDatabases => Database/dbScriptAllDatabases}/dbScriptAllDatabases.bats (93%) rename src/_binaries/{DbScriptAllDatabases => Database/dbScriptAllDatabases}/testsData/databaseSize.envProvided.sh (100%) rename src/_binaries/{DbScriptAllDatabases => Database/dbScriptAllDatabases}/testsData/databaseSize.result_db1 (100%) rename src/_binaries/{DbScriptAllDatabases => Database/dbScriptAllDatabases}/testsData/databaseSize.result_db2 (100%) create mode 100644 src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt rename src/_binaries/{DbScriptAllDatabases => Database/dbScriptAllDatabases}/testsData/dbScriptAllDatabases.result (100%) rename src/_binaries/{DbScriptAllDatabases => Database/dbScriptAllDatabases}/testsData/dsn_local.env (100%) rename src/_binaries/{DbScriptAllDatabases => Database/dbScriptAllDatabases}/testsData/dsn_valid.env (100%) rename src/_binaries/{DbScriptAllDatabases => Database/dbScriptAllDatabases}/testsData/getUserDbList.result (100%) rename src/_binaries/{DbScriptAllDatabases => Database/dbScriptAllDatabases}/testsData/parallel (100%) rename src/_binaries/{DbScriptAllDatabases => Database/dbScriptAllDatabases}/testsData/parallelDbScriptAllDatabases (100%) rename src/_binaries/{DbScriptAllDatabases => Database/dbScriptAllDatabases}/testsData/pv (100%) delete mode 100644 src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.options.tpl delete mode 100755 src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.sh delete mode 100644 src/_binaries/DbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt delete mode 100644 src/_binaries/DbScriptAllDatabases/testsData/getUserDbList.query create mode 100644 src/_binaries/commandDefinitions/optionsFromDsn.yaml delete mode 100755 src/_binaries/commandDefinitions/optionsProgressBar.sh delete mode 100644 src/_binaries/commandDefinitions/optionsProgressBar.yaml diff --git a/bin/dbImportProfile b/bin/dbImportProfile index 5ffd591d..b6272ae4 100755 --- a/bin/dbImportProfile +++ b/bin/dbImportProfile @@ -1698,6 +1698,7 @@ beforeParseCallback() { # ------------------------------------------ # options variables initialization +declare optionFromDsn="" declare optionHelp="0" declare optionConfig="0" declare optionBashFrameworkConfig="" @@ -1713,7 +1714,6 @@ declare optionTheme="default" declare optionVersion="0" declare optionQuiet="0" declare optionProfile="" -declare optionFromDsn="default.remote" declare optionRatio="70" # arguments variables initialization declare fromDbName="" @@ -1721,6 +1721,9 @@ declare fromDbName="" dbImportProfileCommandParse() { Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" + optionFromDsn="" + local -i options_parse_optionParsedCountOptionFromDsn + ((options_parse_optionParsedCountOptionFromDsn = 0)) || true optionHelp="0" local -i options_parse_optionParsedCountOptionHelp ((options_parse_optionParsedCountOptionHelp = 0)) || true @@ -1764,9 +1767,6 @@ dbImportProfileCommandParse() { optionProfile="" local -i options_parse_optionParsedCountOptionProfile ((options_parse_optionParsedCountOptionProfile = 0)) || true - optionFromDsn="default.remote" - local -i options_parse_optionParsedCountOptionFromDsn - ((options_parse_optionParsedCountOptionFromDsn = 0)) || true optionRatio="70" local -i options_parse_optionParsedCountOptionRatio ((options_parse_optionParsedCountOptionRatio = 0)) || true @@ -1783,6 +1783,25 @@ dbImportProfileCommandParse() { local argOptDefaultBehavior=0 case "${options_parse_arg}" in # Option 1/17 + # optionFromDsn alts --from-dsn|-f + # type: String min 0 max 1 + --from-dsn | -f) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionFromDsn >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionFromDsn)) + # shellcheck disable=SC2034 + optionFromDsn="$1" + ;; + + # Option 2/17 # optionHelp alts --help|-h # type: Boolean min 0 max 1 --help | -h) @@ -1798,7 +1817,7 @@ dbImportProfileCommandParse() { ;; - # Option 2/17 + # Option 3/17 # optionConfig alts --config # type: Boolean min 0 max 1 --config) @@ -1812,7 +1831,7 @@ dbImportProfileCommandParse() { ((++options_parse_optionParsedCountOptionConfig)) ;; - # Option 3/17 + # Option 4/17 # optionBashFrameworkConfig alts --bash-framework-config # type: String min 0 max 1 --bash-framework-config) @@ -1833,7 +1852,7 @@ dbImportProfileCommandParse() { ;; - # Option 4/17 + # Option 5/17 # optionInfoVerbose alts --verbose|-v # type: Boolean min 0 max 1 --verbose | -v) @@ -1851,7 +1870,7 @@ dbImportProfileCommandParse() { ;; - # Option 5/17 + # Option 6/17 # optionDebugVerbose alts -vv # type: Boolean min 0 max 1 -vv) @@ -1869,7 +1888,7 @@ dbImportProfileCommandParse() { ;; - # Option 6/17 + # Option 7/17 # optionTraceVerbose alts -vvv # type: Boolean min 0 max 1 -vvv) @@ -1887,7 +1906,7 @@ dbImportProfileCommandParse() { ;; - # Option 7/17 + # Option 8/17 # optionEnvFiles alts --env-file # type: StringArray min 0 max -1 --env-file) @@ -1905,7 +1924,7 @@ dbImportProfileCommandParse() { ;; - # Option 8/17 + # Option 9/17 # optionLogLevel alts --log-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1933,7 +1952,7 @@ dbImportProfileCommandParse() { ;; - # Option 9/17 + # Option 10/17 # optionLogFile alts --log-file # type: String min 0 max 1 --log-file) @@ -1956,7 +1975,7 @@ dbImportProfileCommandParse() { ;; - # Option 10/17 + # Option 11/17 # optionDisplayLevel alts --display-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1984,7 +2003,7 @@ dbImportProfileCommandParse() { ;; - # Option 11/17 + # Option 12/17 # optionNoColor alts --no-color # type: Boolean min 0 max 1 --no-color) @@ -2002,7 +2021,7 @@ dbImportProfileCommandParse() { ;; - # Option 12/17 + # Option 13/17 # optionTheme alts --theme # type: String min 0 max 1 # authorizedValues: default|default-force|noColor @@ -2030,7 +2049,7 @@ dbImportProfileCommandParse() { ;; - # Option 13/17 + # Option 14/17 # optionVersion alts --version # type: Boolean min 0 max 1 --version) @@ -2046,7 +2065,7 @@ dbImportProfileCommandParse() { ;; - # Option 14/17 + # Option 15/17 # optionQuiet alts --quiet|-q # type: Boolean min 0 max 1 --quiet | -q) @@ -2064,7 +2083,7 @@ dbImportProfileCommandParse() { ;; - # Option 15/17 + # Option 16/17 # optionProfile alts --profile|-p # type: String min 0 max 1 --profile | -p) @@ -2083,25 +2102,6 @@ dbImportProfileCommandParse() { optionProfile="$1" ;; - # Option 16/17 - # optionFromDsn alts --from-dsn|-f - # type: String min 0 max 1 - --from-dsn | -f) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - if ((options_parse_optionParsedCountOptionFromDsn >= 1 )); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionFromDsn)) - # shellcheck disable=SC2034 - optionFromDsn="$1" - ;; - # Option 17/17 # optionRatio alts --ratio|-r # type: String min 0 max 1 @@ -2213,7 +2213,7 @@ dbImportProfileCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--profile|-p ]" "[--from-dsn|-f ]" "[--ratio|-r ]" + optionsAltList=("[--from-dsn|-f ]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--profile|-p ]" "[--ratio|-r ]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "dbImportProfile" "${optionsAltList[@]}" @@ -2232,6 +2232,13 @@ dbImportProfileCommandHelp() { # ------------------------------------------ # options section # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}SOURCE OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--from-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-f ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Target mysql server." + echo + + echo echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" @@ -2330,14 +2337,6 @@ dbImportProfileCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--from-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-f ${__HELP_NORMAL} {single}" - optionFromDsnHelpFunction - - - - Array::wrap2 ' ' 76 6 " Default value: " "default.remote" - echo - echo -e " ${__HELP_OPTION_COLOR}--ratio${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-r ${__HELP_NORMAL} {single}" optionRatioHelpFunction diff --git a/bin/dbQueryAllDatabases b/bin/dbQueryAllDatabases index 073f4a64..ecee877b 100755 --- a/bin/dbQueryAllDatabases +++ b/bin/dbQueryAllDatabases @@ -1644,6 +1644,12 @@ beforeParseCallback() { +declare -a PARALLEL_OPTIONS + +optionProgressBarCallback() { + PARALLEL_OPTIONS+=(--bar) +} + optionJobsCallback() { # shellcheck disable=SC2154 if ! [[ "${optionJobs}" =~ ^[0-9]+$ ]]; then @@ -1657,14 +1663,6 @@ optionJobsCallback() { -declare -a PARALLEL_OPTIONS - -optionProgressBarCallback() { - PARALLEL_OPTIONS+=(--bar) -} - - - optionVersionCallback() { # shellcheck disable=SC2154 echo "${SCRIPT_NAME} version ${versionNumber}" @@ -1787,6 +1785,7 @@ dbQueryAllDatabasesCommandCallback() { # options variables initialization declare optionJobs="1" declare optionProgressBar="0" +declare optionFromDsn="" declare optionHelp="0" declare optionConfig="0" declare optionBashFrameworkConfig="" @@ -1802,7 +1801,6 @@ declare optionTheme="default" declare optionVersion="0" declare optionQuiet="0" declare optionSeparator="|" -declare optionFromDsn="" # arguments variables initialization declare argQuery="" # @description parse command options and arguments for dbQueryAllDatabasesCommand @@ -1815,6 +1813,9 @@ dbQueryAllDatabasesCommandParse() { optionProgressBar="0" local -i options_parse_optionParsedCountOptionProgressBar ((options_parse_optionParsedCountOptionProgressBar = 0)) || true + optionFromDsn="" + local -i options_parse_optionParsedCountOptionFromDsn + ((options_parse_optionParsedCountOptionFromDsn = 0)) || true optionHelp="0" local -i options_parse_optionParsedCountOptionHelp ((options_parse_optionParsedCountOptionHelp = 0)) || true @@ -1858,9 +1859,6 @@ dbQueryAllDatabasesCommandParse() { optionSeparator="|" local -i options_parse_optionParsedCountOptionSeparator ((options_parse_optionParsedCountOptionSeparator = 0)) || true - optionFromDsn="" - local -i options_parse_optionParsedCountOptionFromDsn - ((options_parse_optionParsedCountOptionFromDsn = 0)) || true argQuery="" local -i options_parse_argParsedCountArgQuery @@ -1911,6 +1909,25 @@ dbQueryAllDatabasesCommandParse() { ;; # Option 3/18 + # optionFromDsn alts --from-dsn|-f + # type: String min 0 max 1 + --from-dsn | -f) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionFromDsn >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionFromDsn)) + # shellcheck disable=SC2034 + optionFromDsn="$1" + ;; + + # Option 4/18 # optionHelp alts --help|-h # type: Boolean min 0 max 1 --help | -h) @@ -1926,7 +1943,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 4/18 + # Option 5/18 # optionConfig alts --config # type: Boolean min 0 max 1 --config) @@ -1940,7 +1957,7 @@ dbQueryAllDatabasesCommandParse() { ((++options_parse_optionParsedCountOptionConfig)) ;; - # Option 5/18 + # Option 6/18 # optionBashFrameworkConfig alts --bash-framework-config # type: String min 0 max 1 --bash-framework-config) @@ -1961,7 +1978,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 6/18 + # Option 7/18 # optionInfoVerbose alts --verbose|-v # type: Boolean min 0 max 1 --verbose | -v) @@ -1979,7 +1996,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 7/18 + # Option 8/18 # optionDebugVerbose alts -vv # type: Boolean min 0 max 1 -vv) @@ -1997,7 +2014,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 8/18 + # Option 9/18 # optionTraceVerbose alts -vvv # type: Boolean min 0 max 1 -vvv) @@ -2015,7 +2032,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 9/18 + # Option 10/18 # optionEnvFiles alts --env-file # type: StringArray min 0 max -1 --env-file) @@ -2033,7 +2050,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 10/18 + # Option 11/18 # optionLogLevel alts --log-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -2061,7 +2078,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 11/18 + # Option 12/18 # optionLogFile alts --log-file # type: String min 0 max 1 --log-file) @@ -2084,7 +2101,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 12/18 + # Option 13/18 # optionDisplayLevel alts --display-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -2112,7 +2129,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 13/18 + # Option 14/18 # optionNoColor alts --no-color # type: Boolean min 0 max 1 --no-color) @@ -2130,7 +2147,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 14/18 + # Option 15/18 # optionTheme alts --theme # type: String min 0 max 1 # authorizedValues: default|default-force|noColor @@ -2158,7 +2175,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 15/18 + # Option 16/18 # optionVersion alts --version # type: Boolean min 0 max 1 --version) @@ -2174,7 +2191,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 16/18 + # Option 17/18 # optionQuiet alts --quiet|-q # type: Boolean min 0 max 1 --quiet | -q) @@ -2192,7 +2209,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 17/18 + # Option 18/18 # optionSeparator alts --separator|-s # type: String min 0 max 1 --separator | -s) @@ -2213,25 +2230,6 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 18/18 - # optionFromDsn alts --from-dsn|-f - # type: String min 0 max 1 - --from-dsn | -f) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - if ((options_parse_optionParsedCountOptionFromDsn >= 1 )); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionFromDsn)) - # shellcheck disable=SC2034 - optionFromDsn="$1" - ;; - -*) if [[ "${argOptDefaultBehavior}" = "0" ]]; then Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" @@ -2329,7 +2327,7 @@ dbQueryAllDatabasesCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--jobs|-j ]" "[--bar|-b]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--separator|-s ]" "[--from-dsn|-f ]" + optionsAltList=("[--jobs|-j ]" "[--bar|-b]" "[--from-dsn|-f ]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--separator|-s ]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "dbQueryAllDatabases" "${optionsAltList[@]}" @@ -2357,13 +2355,19 @@ dbQueryAllDatabasesCommandHelp() { Array::wrap2 ' ' 76 6 " Default value: " "1" echo - echo - echo -e "${__HELP_TITLE_COLOR}PROGRESS BAR OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--bar${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-b${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Show progress as a progress bar. In the bar is shown: % of jobs completed," "estimated seconds left, and number of jobs started." "" echo + echo + echo -e "${__HELP_TITLE_COLOR}SOURCE OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--from-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-f ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Target mysql server." + echo + + echo echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" @@ -2463,12 +2467,6 @@ dbQueryAllDatabasesCommandHelp() { Array::wrap2 ' ' 76 6 " Default value: " "|" echo - - echo -e " ${__HELP_OPTION_COLOR}--from-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-f ${__HELP_NORMAL} {single}" - Array::wrap2 ' ' 76 4 " " "Target mysql server." - echo - - # ------------------------------------------ # longDescription section # ------------------------------------------ diff --git a/bin/dbScriptAllDatabases b/bin/dbScriptAllDatabases index ea2a17f3..3fa881ac 100755 --- a/bin/dbScriptAllDatabases +++ b/bin/dbScriptAllDatabases @@ -1,13 +1,12 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FACADE FROM https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.sh +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml # DO NOT EDIT IT # @generated ############################################################################### # shellcheck disable=SC2288,SC2034 -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/dbScriptAllDatabases -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE + + # ensure that no user aliases could interfere with # commands used in this script @@ -62,13 +61,6 @@ interruptManagement() { kill -s INT "$$" } trap interruptManagement INT -SCRIPT_NAME=${0##*/} -REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" -if [[ -n "${EMBED_CURRENT_DIR}" ]]; then - CURRENT_DIR="${EMBED_CURRENT_DIR}" -else - CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" -fi ################################################ # Temp dir management @@ -102,6 +94,20 @@ cleanOnExit() { } trap cleanOnExit EXIT HUP QUIT ABRT TERM + +SCRIPT_NAME=${0##*/} +REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" +if [[ -n "${EMBED_CURRENT_DIR}" ]]; then + CURRENT_DIR="${EMBED_CURRENT_DIR}" +else + CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" +fi +FRAMEWORK_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" +FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" +FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + # @description Log namespace provides 2 kind of functions # - Log::display* allows to display given message with # given display level @@ -132,1994 +138,2458 @@ export __VERBOSE_LEVEL_DEBUG=2 # @description verbose level info export __VERBOSE_LEVEL_TRACE=3 -# @description Display message using info color (bg light blue/fg white) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayInfo() { - local type="${2:-INFO}" - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description concatenate each element of an array with a separator +# but wrapping text when line length is more than provided argument +# The algorithm will try not to cut the array element if it can. +# - if an arg can be placed on current line it will be, +# otherwise current line is printed and arg is added to the new +# current line +# - Empty arg is interpreted as a new line. +# - Add \r to arg in order to force break line and avoid following +# arg to be concatenated with current arg. +# +# @arg $1 glue:String +# @arg $2 maxLineLength:int +# @arg $3 indentNextLine:int +# @arg $@ array:String[] +Array::wrap2() { + local glue="${1-}" + local -i glueLength="${#glue}" + shift || true + local -i maxLineLength=$1 + shift || true + local -i indentNextLine=$1 + shift || true + local indentStr="" + if ((indentNextLine > 0)); then + indentStr="$(head -c "${indentNextLine}" 0)); do + arg="$1" + shift || true + + # replace tab by 2 spaces + arg="${arg//$'\t'/ }" + # remove trailing spaces + arg="${arg%[[:blank:]]}" + if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then + printCurrentLine + ((previousLineEmpty = 1)) + continue + else + if ((previousLineEmpty == 1)); then + printCurrentLine + fi + ((previousLineEmpty = 0)) || true + fi + # convert eol to args + mapfile -t additionalLines <<<"${arg}" + if ((${#additionalLines[@]} > 1)); then + set -- "${additionalLines[@]}" "$@" + continue + fi + + ((argLength = ${#arg})) || true + + # empty arg + if ((argLength == 0)); then + if ((isNewline == 0)); then + # isNewline = 0 means currentLine is not empty + printCurrentLine + fi + continue + fi + + if ((isNewline == 0)); then + glueLength="${#glue}" + else + glueLength="0" + fi + if ((currentLineLength + argLength + glueLength > maxLineLength)); then + if ((argLength + glueLength > maxLineLength)); then + # arg is too long to even fit on one line + # we have to split the arg on current and next line + local -i remainingLineLength + ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) + appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" + printCurrentLine + arg="${arg:${remainingLineLength}}" + # remove leading spaces + arg="${arg##[[:blank:]]}" + + set -- "${arg}" "$@" + else + # the arg can fit on next line + printCurrentLine + appendToCurrentLine "${arg}" "${argLength}" + fi + else + appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" + fi + done + if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then + printCurrentLine + fi + ) | sed -E -e 's/[[:blank:]]+$//' } -# @description Display message using debug color (gray) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayDebug() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then - Log::computeDuration - echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logDebug "$1" + +# @description check if command specified exists or return 1 +# with error and message if not +# +# @arg $1 commandName:String on which existence must be checked +# @arg $2 helpIfNotExists:String a help command to display if the command does not exist +# +# @exitcode 1 if the command specified does not exist +# @stderr diagnostic information + help if second argument is provided +Assert::commandExists() { + local commandName="$1" + local helpIfNotExists="$2" + + "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { + Log::displayError "${commandName} is not installed, please install it" + if [[ -n "${helpIfNotExists}" ]]; then + Log::displayInfo "${helpIfNotExists}" + fi + return 1 + } + return 0 } -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description check if tty (interactive mode) is active +# @noargs +# @exitcode 1 if tty not active +# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive +# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive +Assert::tty() { + if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then + return 1 fi - Log::logWarning "$1" + if [[ "${INTERACTIVE:-0}" = "1" ]]; then + return 0 + fi + tty -s } -# @description Display message using error color (red) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayError() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then - Log::computeDuration - echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logError "$1" + +# @description ignore exit code 141 from simple command pipes +# @example use with: +# local resultingStatus=0 +# local -a originalPipeStatus=() +# cmd1 | cmd2 || Bash::handlePipelineFailure resultingStatus originalPipeStatus || true +# [[ "${resultingStatus}" = "0" ]] +# @arg $1 resultingStatusCode:&int (passed by reference) (optional) resulting status code +# @arg $2 originalStatus:int[] (passed by reference) (optional) copy of original PIPESTATUS array +# @env PIPESTATUS assuming that this function is called like in the example provided +# @see https://unix.stackexchange.com/a/709880/582856 +Bash::handlePipelineFailure() { + local -a pipeStatusBackup=("${PIPESTATUS[@]}") + local -n handlePipelineFailure_resultingStatusCode=$1 + local -n handlePipelineFailure_originalStatus=$2 + # shellcheck disable=SC2034 + handlePipelineFailure_originalStatus=("${pipeStatusBackup[@]}") + handlePipelineFailure_resultingStatusCode=0 + local statusCode + for statusCode in "${pipeStatusBackup[@]}"; do + if ((statusCode == 141)); then + return 0 + elif ((statusCode > 0)); then + # shellcheck disable=SC2034 + handlePipelineFailure_resultingStatusCode="${statusCode}" + break + fi + done + return "${handlePipelineFailure_resultingStatusCode}" } -# @description load colors theme constants -# @warning if tty not opened, noColor theme will be chosen -# @arg $1 theme:String the theme to use (default, noColor) -# @arg $@ args:String[] -# @set __ERROR_COLOR String indicate error status -# @set __INFO_COLOR String indicate info status -# @set __SUCCESS_COLOR String indicate success status -# @set __WARNING_COLOR String indicate warning status -# @set __SKIPPED_COLOR String indicate skipped status -# @set __DEBUG_COLOR String indicate debug status -# @set __HELP_COLOR String indicate help status -# @set __TEST_COLOR String not used -# @set __TEST_ERROR_COLOR String not used -# @set __HELP_TITLE_COLOR String used to display help title in help strings -# @set __HELP_OPTION_COLOR String used to display highlight options in help strings -# -# @set __RESET_COLOR String reset default color -# -# @set __HELP_EXAMPLE String to remove -# @set __HELP_TITLE String to remove -# @set __HELP_NORMAL String to remove -# shellcheck disable=SC2034 -UI::theme() { - local theme="${1-default}" - if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then - theme="noColor" - fi - case "${theme}" in - default | default-force) - theme="default" + +# @description loads ~/.bash-tools/.env if available +# if not creates it from a default template +# else check if new options need to be added +BashTools::Conf::requireLoad() { +Linux::requireTarCommand +Compiler::Embed::extractFileFromBase64 \ + "${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" \ + "IyEvdXNyL2Jpbi9lbnYgYmFzaAojIHNoZWxsY2hlY2sgZGlzYWJsZT1TQzIwMzQKCiMgRGVmYXVsdCBzZXR0aW5ncwojIHlvdSBjYW4gb3ZlcnJpZGUgdGhlc2Ugc2V0dGluZ3MgYnkgY3JlYXRpbmcgJHtIT01FfS8uYmFzaC10b29scy8uZW52IGZpbGUKCiMjIwojIyMgRElTUExBWSBMZXZlbAojIyMgbWluaW11bSBsZXZlbCBvZiB0aGUgbWVzc2FnZXMgdGhhdCB3aWxsIGJlIGRpc3BsYXllZCBvbiBzY3JlZW4KIyMjCiMjIyAwOiBOTyBMT0cKIyMjIDE6IEVSUk9SCiMjIyAyOiBXQVJOSU5HCiMjIyAzOiBJTkZPCiMjIyA0OiBERUJVRwojIyMKQkFTSF9GUkFNRVdPUktfRElTUExBWV9MRVZFTD0ke0JBU0hfRlJBTUVXT1JLX0RJU1BMQVlfTEVWRUw6LTN9CgojIyMKIyMjIERJU1BMQVkgZHVyYXRpb24KIyMjIDA6IG5vIGR1cmF0aW9uIGlzIGRpc3BsYXllZCBvbiB0aGUgbWVzc2FnZXMKIyMjIDE6IGR1cmF0aW9uIGJldHdlZW4gcHJldmlvdXMgbWVzc2FnZSBhbmQgY3VycmVudCBpcyBkaXNwbGF5ZWQKIyMjIHdpdGggdGhlIG1lc3NhZ2UKIyMjCkRJU1BMQVlfRFVSQVRJT049JHtESVNQTEFZX0RVUkFUSU9OOjB9CgojIyMKIyMjIExvZyB0byBmaWxlCiMjIwojIyMgYWxsIGxvZyBtZXNzYWdlcyB3aWxsIGJlIHJlZGlyZWN0ZWQgdG8gbG9nIGZpbGUgc3BlY2lmaWVkCiMjIyB0aGlzIHNhbWUgcGF0aCB3aWxsIGJlIHVzZWQgaW5zaWRlIGFuZCBvdXRzaWRlIG9mIHRoZSBjb250YWluZXIKIyMjCkJBU0hfRlJBTUVXT1JLX0xPR19GSUxFPSR7QkFTSF9GUkFNRVdPUktfTE9HX0ZJTEU6LSR7RlJBTUVXT1JLX1JPT1RfRElSfS9sb2dzL2Jhc2gubG9nfQoKIyMjCiMjIyBMT0cgTGV2ZWwKIyMjIG1pbmltdW0gbGV2ZWwgb2YgdGhlIG1lc3NhZ2VzIHRoYXQgd2lsbCBiZSBsb2dnZWQgaW50byBMT0dfRklMRQojIyMKIyMjIDA6IE5PIExPRwojIyMgMTogRVJST1IKIyMjIDI6IFdBUk5JTkcKIyMjIDM6IElORk8KIyMjIDQ6IERFQlVHCiMjIwpCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw9JHtCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw6LTB9CgojIGFic29sdXRlIGRpcmVjdG9yeSBjb250YWluaW5nIGRiIGltcG9ydCBzcWwgZHVtcHMKREJfSU1QT1JUX0RVTVBfRElSPSR7REJfSU1QT1JUX0RVTVBfRElSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2RiSW1wb3J0RHVtcHN9CgojIGdhcmJhZ2UgY29sbGVjdCBhbGwgZmlsZXMgZm9yIHdoaWNoIG1vZGlmaWNhdGlvbiBpcyBncmVhdGVyIHRoYW4gZWc6IDMwIGRheXMgKCszMCkKIyBlYWNoIHRpbWUgYW4gZXhpc3RpbmcgZmlsZSBpcyB1c2VkIGJ5IGRiSW1wb3J0L2RiSW1wb3J0VGFibGUKIyB0aGUgZmlsZSBtb2RpZmljYXRpb24gdGltZSBpcyBzZXQgdG8gbm93CkRCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUz0ke0RCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUzotKzMwfQoKIyBhYnNvbHV0ZSBkaXJlY3RvcnkgY29udGFpbmluZyBkYlNjcmlwdHMgdXNlZCBieSBkYlNjcmlwdEFsbERhdGFiYXNlcwpTQ1JJUFRTX0ZPTERFUj0ke1NDUklQVFNfRk9MREVSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2NvbmYvZGJTY3JpcHRzfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEFXUyBQYXJhbWV0ZXJzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KUzNfQkFTRV9VUkw9JHtTM19CQVNFX1VSTDotfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvc3RtYW4gUGFyYW1ldGVycwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClBPU1RNQU5fQVBJX0tFWT0K" \ + "755" + +declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" + + BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" + if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then + FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" + else + # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR + FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" + fi + # shellcheck disable=SC2034 + FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" + # shellcheck disable=SC2034 + FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + + if [[ -f "${HOME}/.bash-tools/.env" ]]; then + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") + fi + + local envFile="${HOME}/.bash-tools/.env" + if [[ ! -f "${envFile}" ]]; then + mkdir -p "${HOME}/.bash-tools" + ( + echo "#!/usr/bin/env bash" + # shellcheck disable=SC2154 + echo "${embed_file_bashToolsDefaultConfigTemplate}" + ) >"${envFile}" + Log::displayInfo "Configuration file '${envFile}' created" + else + if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then + ( + echo '# -----------------------------------------------------' + echo '# Postman Parameters' + echo '# -----------------------------------------------------' + echo 'POSTMAN_API_KEY=' + ) >>"${envFile}" + fi + fi + # shellcheck source=/conf/.env + source "${envFile}" || { + Log::displayError "impossible to load '${envFile}'" + exit 1 + } +} + + +# @description convert base64 encoded back to target file +# if target file is executable prepend dir of target +# file to PATH to make binary available everywhere +# it is advised to include in the path of the target file +# the md5sum of the binFile +# +# @arg $1 targetFile:String the file to write +# @arg $2 binFileBase64:String the base64 encoded file +# @arg $3 fileMode:String the chmod to set on the file +# @set PATH String prepend target embedded file binary directory to PATH variable if binary executable +Compiler::Embed::extractFileFromBase64() { + local targetFile="$1" + local binFileBase64="$2" + local fileMode="${3:-+x}" + local targetDir="${targetFile%/*}" + + if [[ ! -f "${targetFile}" ]]; then + if [[ ! -d "${targetDir}" ]]; then + mkdir -p "${targetDir}" + fi + base64 -d >"${targetFile}" <<<"${binFileBase64}" + chmod "${fileMode}" "${targetFile}" + fi + + if [[ -x "${targetFile}" ]]; then + Env::pathPrepend "${targetDir}" + fi +} + + +# @description get absolute conf file from specified conf folder deduced using these rules +# * from absolute file (ignores and ) +# * relative to where script is executed (ignores and ) +# * from home/.bash-tools/ +# * from framework conf/ +# +# @arg $1 confFolder:String the directory name (not the path) to list +# @arg $2 conf:String file to use without extension +# @arg $3 extension:String the extension (.sh by default) +# +# @stdout absolute conf filename +# @exitcode 1 if file is not found in any location +Conf::getAbsoluteFile() { + local confFolder="$1" + local conf="$2" + local extension="${3-.sh}" + if [[ -n "${extension}" && "${extension:0:1}" != "." ]]; then + extension=".${extension}" + fi + + testAbs() { + local result + result="$(realpath -e "$1" 2>/dev/null)" + # shellcheck disable=SC2181 + if [[ "$?" = "0" && -f "${result}" ]]; then + echo "${result}" + return 0 + fi + return 1 + } + + # conf is absolute file (including extension) + testAbs "${confFolder}${extension}" && return 0 + # conf is absolute file + testAbs "${confFolder}" && return 0 + # conf is absolute file (including extension) + testAbs "${conf}${extension}" && return 0 + # conf is absolute file + testAbs "${conf}" && return 0 + + # relative to where script is executed (including extension) + if [[ -n "${CURRENT_DIR+xxx}" ]]; then + testAbs "$(File::concatenatePath "${CURRENT_DIR}" "${confFolder}")/${conf}${extension}" && return 0 + fi + # from home/.bash-tools/ + testAbs "$(File::concatenatePath "${HOME}/.bash-tools" "${confFolder}")/${conf}${extension}" && return 0 + + if [[ -n "${FRAMEWORK_ROOT_DIR+xxx}" ]]; then + # from framework conf/ (including extension) + testAbs "$(File::concatenatePath "${FRAMEWORK_ROOT_DIR}/conf" "${confFolder}")/${conf}${extension}" && return 0 + + # from framework conf/ + testAbs "$(File::concatenatePath "${FRAMEWORK_ROOT_DIR}/conf" "${confFolder}")/${conf}" && return 0 + fi + + # file not found + Log::displayError "conf file '${conf}' not found" + + return 1 +} + + +# @description list the conf files list available in bash-tools/conf/ folder +# and those overridden in ${HOME}/.bash-tools/ folder +# +# @arg $1 confFolder:String the directory name (not the path) to list +# @arg $2 extension:String the extension (.sh by default) +# @arg $3 indentStr:String the indentation (' - ' by default) can be any string compatible with sed not containing any / +# +# @stdout list of files without extension/directory +# @example text +# - default.local +# - default.remote +# - localhost-root +Conf::getMergedList() { + local confFolder="$1" + local extension="${2-sh}" + local indentStr="${3- - }" + + local DEFAULT_CONF_DIR="${FRAMEWORK_ROOT_DIR}/conf/${confFolder}" + local HOME_CONF_DIR="${HOME}/.bash-tools/${confFolder}" + + ( + if [[ -d "${DEFAULT_CONF_DIR}" ]]; then + Conf::list "${DEFAULT_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" + fi + if [[ -d "${HOME_CONF_DIR}" ]]; then + Conf::list "${HOME_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" + fi + ) | sort | uniq +} + + +# @description list files of dir with given extension and display it as a list one by line +# +# @arg $1 dir:String the directory to list +# @arg $2 prefix:String the profile file prefix (default: "") +# @arg $3 ext:String the extension +# @arg $4 findOptions:String find options, eg: -type d (Default value: '-type f') +# @arg $5 indentStr:String the indentation can be any string compatible with sed not containing any / (Default value: ' - ') +# @stdout list of files without extension/directory +# @example text +# - default.local +# - default.remote +# - localhost-root +# @exitcode 1 if directory does not exists +Conf::list() { + local dir="$1" + local prefix="${2:-}" + local ext="${3}" + local findOptions="${4--type f}" + local indentStr="${5- - }" + + if [[ ! -d "${dir}" ]]; then + Log::displayError "Directory ${dir} does not exist" + fi + if [[ -n "${ext}" && "${ext:0:1}" != "." ]]; then + ext=".${ext}" + fi + ( + # shellcheck disable=SC2086 + cd "${dir}" && + find . -maxdepth 1 ${findOptions} -name "${prefix}*${ext}" | + sed -E "s#^\./${prefix}##g" | + sed -E "s#${ext}\$##g" | sort | sed -E "s#^#${indentStr}#" + ) +} + + +# @description check if dsn file has all the mandatory variables set +# Mandatory variables are: HOSTNAME, USER, PASSWORD, PORT +# +# @arg $1 dsnFileName:String dsn absolute filename +# @set HOSTNAME loaded from dsn file +# @set PORT loaded from dsn file +# @set USER loaded from dsn file +# @set PASSWORD loaded from dsn file +# @exitcode 0 on valid file +# @exitcode 1 if one of the properties of the conf file is invalid or if file not found +# @stderr log output if error found in conf file +Database::checkDsnFile() { + local dsnFileName="$1" + if [[ ! -f "${dsnFileName}" ]]; then + Log::displayError "dsn file ${dsnFileName} not found" + return 1 + fi + + ( + unset HOSTNAME PORT PASSWORD USER + # shellcheck source=/src/Database/testsData/dsn_valid.env + source "${dsnFileName}" + if [[ -z ${HOSTNAME+x} ]]; then + Log::displayError "dsn file ${dsnFileName} : HOSTNAME not provided" + return 1 + fi + if [[ -z "${HOSTNAME}" ]]; then + Log::displayWarning "dsn file ${dsnFileName} : HOSTNAME value not provided" + fi + if [[ "${HOSTNAME}" = "localhost" ]]; then + Log::displayWarning "dsn file ${dsnFileName} : check that HOSTNAME should not be 127.0.0.1 instead of localhost" + fi + if [[ -z "${PORT+x}" ]]; then + Log::displayError "dsn file ${dsnFileName} : PORT not provided" + return 1 + fi + if ! [[ ${PORT} =~ ^[0-9]+$ ]]; then + Log::displayError "dsn file ${dsnFileName} : PORT invalid" + return 1 + fi + if [[ -z "${USER+x}" ]]; then + Log::displayError "dsn file ${dsnFileName} : USER not provided" + return 1 + fi + if [[ -z "${PASSWORD+x}" ]]; then + Log::displayError "dsn file ${dsnFileName} : PASSWORD not provided" + return 1 + fi + ) +} + + +# @description databases's list of given mysql server +# +# @example text +# - information_schema +# - mysql +# - performance_schema +# - sys +# +# @arg $1 instanceUserDbList:&Map (passed by reference) database instance to use +# @stdout the list of db except mysql admin ones (see example) +# @exitcode * the query exit code +Database::getUserDbList() { + # shellcheck disable=SC2034 + local -n instanceUserDbList=$1 + # shellcheck disable=SC2016 + local sql='SELECT `schema_name` from INFORMATION_SCHEMA.SCHEMATA WHERE `schema_name` NOT IN("information_schema", "mysql", "performance_schema", "sys")' + Database::query instanceUserDbList "${sql}" +} + + +# @description create a new db instance +# Returns immediately if the instance is already initialized +# +# @arg $1 instanceNewInstance:&Map (passed by reference) database instance to use +# @arg $2 dsn:String dsn profile - load the dsn.env profile deduced using rules defined in Conf::getAbsoluteFile +# +# @example +# declare -Agx dbInstance +# Database::newInstance dbInstance "default.local" +# +# @exitcode 1 if dns file not able to loaded +Database::newInstance() { + local -n instanceNewInstance=$1 + local dsn="$2" + local DSN_FILE + + if [[ -v instanceNewInstance['INITIALIZED'] && "${instanceNewInstance['INITIALIZED']:-0}" == "1" ]]; then + return + fi + + # final auth file generated from dns file + instanceNewInstance['AUTH_FILE']="" + instanceNewInstance['DSN_FILE']="" + + # check dsn file + DSN_FILE="$(Conf::getAbsoluteFile "dsn" "${dsn}" "env")" || return 1 + Database::checkDsnFile "${DSN_FILE}" || return 1 + instanceNewInstance['DSN_FILE']="${DSN_FILE}" + + # shellcheck source=/src/Database/testsData/dsn_valid.env + source "${instanceNewInstance['DSN_FILE']}" + + instanceNewInstance['USER']="${USER}" + instanceNewInstance['PASSWORD']="${PASSWORD}" + instanceNewInstance['HOSTNAME']="${HOSTNAME}" + instanceNewInstance['PORT']="${PORT}" + + # generate authFile for easy authentication + instanceNewInstance['AUTH_FILE']=$(mktemp -p "${TMPDIR:-/tmp}" -t "mysql.XXXXXXXXXXXX") + ( + echo "[client]" + echo "user = ${USER}" + echo "password = ${PASSWORD}" + echo "host = ${HOSTNAME}" + echo "port = ${PORT}" + ) >"${instanceNewInstance['AUTH_FILE']}" + + # some of those values can be overridden using the dsn file + # SKIP_COLUMN_NAMES enabled by default + instanceNewInstance['SKIP_COLUMN_NAMES']="${SKIP_COLUMN_NAMES:-1}" + instanceNewInstance['SSL_OPTIONS']="${MYSQL_SSL_OPTIONS:---ssl-mode=DISABLED}" + instanceNewInstance['QUERY_OPTIONS']="${MYSQL_QUERY_OPTIONS:---batch --raw --default-character-set=utf8}" + instanceNewInstance['DUMP_OPTIONS']="${MYSQL_DUMP_OPTIONS:---default-character-set=utf8 --compression-algorithms --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 ${instanceNewInstance['SSL_OPTIONS']}}" + instanceNewInstance['DB_IMPORT_OPTIONS']="${DB_IMPORT_OPTIONS:---connect-timeout=5 --batch --raw --default-character-set=utf8}" + + instanceNewInstance['INITIALIZED']=1 +} + + +# @description mysql query on a given db +# @warning could use QUERY_OPTIONS variable from dsn if defined +# @example +# cat file.sql | Database::query ... +# @arg $1 instanceQuery:&Map (passed by reference) database instance to use +# @arg $2 sqlQuery:String (optional) sql query or sql file to execute. if not provided or empty, the command can be piped +# @arg $3 dbName:String (optional) the db name +# +# @exitcode mysql command status code +Database::query() { + local -n instanceQuery=$1 + local -a mysqlCommand=() + local -a queryOptions + + mysqlCommand+=(mysql) + mysqlCommand+=("--defaults-extra-file=${instanceQuery['AUTH_FILE']}") + IFS=' ' read -r -a queryOptions <<<"${instanceQuery['QUERY_OPTIONS']}" + mysqlCommand+=("${queryOptions[@]}") + if [[ "${instanceQuery['SKIP_COLUMN_NAMES']}" = "1" ]]; then + mysqlCommand+=("-s" "--skip-column-names") + fi + # add optional db name + if [[ -n "${3+x}" ]]; then + mysqlCommand+=("$3") + fi + # add optional sql query + if [[ -n "${2+x}" && -n "$2" && ! -f "$2" ]]; then + mysqlCommand+=("-e") + mysqlCommand+=("$2") + fi + Log::displayDebug "$(printf "execute command: '%s'" "${mysqlCommand[*]}")" + + if [[ -f "$2" ]]; then + "${mysqlCommand[@]}" <"$2" + else + "${mysqlCommand[@]}" + fi +} + + +# @description set the general options to use on mysql command to query the database +# Differs than setOptions in the way that these options could change each time +# +# @arg $1 instanceSetQueryOptions:&Map (passed by reference) database instance to use +# @arg $2 optionsList:String query options list +Database::setQueryOptions() { + local -n instanceSetQueryOptions=$1 + # shellcheck disable=SC2034 + instanceSetQueryOptions['QUERY_OPTIONS']="$2" +} + + +# @description check if all requirements are satisfied +# to execute dbImport commands +Db::checkRequirements() { + if [[ "${SKIP_REQUIREMENTS_CHECKS:-0}" = "1" ]]; then + return 0 + fi + local -i failures=0 + echo + Assert::commandExists mysql "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqldump "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists pv "sudo apt-get install -y pv" || ((++failures)) + Assert::commandExists gawk "sudo apt-get install -y gawk" || ((++failures)) + Assert::commandExists awk "sudo apt-get install -y gawk" || ((++failures)) + Version::checkMinimal "gawk" "--version" "5.0.1" || ((++failures)) + return "${failures}" +} + + +# @description prepend directories to the PATH environment variable +# @arg $@ args:String[] list of directories to prepend +# @set PATH update PATH with the directories prepended +Env::pathPrepend() { + local arg + for arg in "$@"; do + if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then + PATH="$(realpath "${arg}"):${PATH}" + fi + done +} + + +# @description ensure env files are loaded +# @arg $@ list of default files to load at the end +# @exitcode 1 if one of env files fails to load +# @stderr diagnostics information is displayed +# shellcheck disable=SC2120 +Env::requireLoad() { + export REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 + + local -a defaultFiles=("$@") + # get list of possible config files + local -a configFiles=() + if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then + # BASH_FRAMEWORK_ENV_FILES is an array + configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") + fi + local localFrameworkConfigFile + localFrameworkConfigFile="$(pwd)/.framework-config" + if [[ -f "${localFrameworkConfigFile}" ]]; then + configFiles+=("${localFrameworkConfigFile}") + fi + if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then + configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") + fi + configFiles+=("${optionEnvFiles[@]}") + configFiles+=("${defaultFiles[@]}") + + for file in "${configFiles[@]}"; do + # shellcheck source=/.framework-config + CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { + Log::displayError "while loading config file: ${file}" + return 1 + } + done +} + + +# @description concatenate 2 paths and ensure the path is correct using realpath -m +# @arg $1 basePath:String +# @arg $2 subPath:String +File::concatenatePath() { + + if [[ "${REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Linux::requireRealpathCommand has not been loaded" + exit 1 + fi + + local basePath="$1" + local subPath="$2" + local fullPath="${basePath:+${basePath}/}${subPath}" + + realpath -m "${fullPath}" 2>/dev/null +} + + +# @description create a temp file using default TMPDIR variable +# @env TMPDIR String (default value /tmp) +# @arg $1 templateName:String template name to use(optional) +Framework::createTempFile() { + mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" +} + + +# @description ensure running user is not root +# @exitcode 1 if current user is root +# @stderr diagnostics information is displayed +Linux::requireExecutedAsUser() { + if [[ "$(id -u)" = "0" ]]; then + Log::fatal "this script should be executed as normal user" + fi +} + + +# @description ensure command realpath is available +# @exitcode 1 if realpath command not available +# @stderr diagnostics information is displayed +Linux::requireRealpathCommand() { + export REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED=1 + + Assert::commandExists realpath +} + + +# @description ensure command tar is available +# @exitcode 1 if tar command not available +# @stderr diagnostics information is displayed +Linux::requireTarCommand() { + Assert::commandExists tar +} + + +declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR +FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" +LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" +LOG_LAST_LOG_DATE_INIT=1 +LOG_LAST_DURATION_STR="" + +# @description compute duration since last call to this function +# the result is set in following env variables. +# in ss.sss (seconds followed by milliseconds precision 3 decimals) +# @noargs +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @set LOG_LAST_LOG_DATE_INIT int (default 1) set to 0 at first call, allows to detect reference log +# @set LOG_LAST_DURATION_STR String the last duration displayed +# @set LOG_LAST_LOG_DATE String the last log date that will be used to compute next diff +Log::computeDuration() { + if ((${DISPLAY_DURATION:-0} == 1)); then + local -i duration=0 + local -i delta=0 + local -i currentLogDate + currentLogDate="${EPOCHREALTIME/[^0-9]/}" + if ((LOG_LAST_LOG_DATE_INIT == 1)); then + LOG_LAST_LOG_DATE_INIT=0 + LOG_LAST_DURATION_STR="Ref" + else + duration=$(((currentLogDate - FIRST_LOG_DATE) / 1000000)) + delta=$(((currentLogDate - LOG_LAST_LOG_DATE) / 1000000)) + LOG_LAST_DURATION_STR="${duration}s/+${delta}s" + fi + LOG_LAST_LOG_DATE="${currentLogDate}" + # shellcheck disable=SC2034 + local microSeconds="${EPOCHREALTIME#*.}" + LOG_LAST_DURATION_STR="$(printf '%(%T)T.%03.0f\n' "${EPOCHSECONDS}" "${microSeconds:0:3}")(${LOG_LAST_DURATION_STR}) - " + else + # shellcheck disable=SC2034 + LOG_LAST_DURATION_STR="" + fi +} + + +# @description Display message using debug color (gray) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayDebug() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then + Log::computeDuration + echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logDebug "$1" +} + + +# @description Display message using error color (red) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayError() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then + Log::computeDuration + echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logError "$1" +} + + +# @description Display message using info color (bg light blue/fg white) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayInfo() { + local type="${2:-INFO}" + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logInfo "$1" "${type}" +} + + +# @description Display message using warning color (yellow) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayWarning() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then + Log::computeDuration + echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logWarning "$1" +} + + +# @description Display message using error color (red) and exit immediately with error status 1 +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::fatal() { + Log::computeDuration + echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + Log::logFatal "$1" + exit 1 +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logDebug() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then + Log::logMessage "${2:-DEBUG}" "$1" + fi +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logError() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_ERROR)); then + Log::logMessage "${2:-ERROR}" "$1" + fi +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logFatal() { + Log::logMessage "${2:-FATAL}" "$1" +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logInfo() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then + Log::logMessage "${2:-INFO}" "$1" + fi +} + + +# @description Internal: common log message +# @example text +# [date]|[levelMsg]|message +# +# @example text +# 2020-01-19 19:20:21|ERROR |log error +# 2020-01-19 19:20:21|SKIPPED|log skipped +# +# @arg $1 levelMsg:String message's level description (eg: STATUS, ERROR, ...) +# @arg $2 msg:String the message to display +# @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty +# @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages +# @stderr diagnostics information is displayed +Log::logMessage() { + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Log::requireLoad has not been loaded" + exit 1 + fi + + local levelMsg="$1" + local msg="$2" + local date + + if [[ -n "${BASH_FRAMEWORK_LOG_FILE}" ]] && ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + date="$(date '+%Y-%m-%d %H:%M:%S')" + touch "${BASH_FRAMEWORK_LOG_FILE}" + printf "%s|%7s|%s\n" "${date}" "${levelMsg}" "${msg}" >>"${BASH_FRAMEWORK_LOG_FILE}" + fi +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logWarning() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then + Log::logMessage "${2:-WARNING}" "$1" + fi +} + + +# @description activate or not Log::display* and Log::log* functions +# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL +# environment variables loaded by Env::requireLoad +# try to create log file and rotate it if necessary +# @noargs +# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable +# @env BASH_FRAMEWORK_DISPLAY_LEVEL int +# @env BASH_FRAMEWORK_LOG_LEVEL int +# @env BASH_FRAMEWORK_LOG_FILE String +# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 +# @exitcode 0 always successful +# @stderr diagnostics information about log file is displayed +Log::requireLoad() { + export REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement UI::requireTheme has not been loaded" + exit 1 + fi + + if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + export BASH_FRAMEWORK_LOG_LEVEL + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then + if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 + fi + elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + # will always be created even if not in info level + Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" + if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then + Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + fi + fi +} + + +# @description To be called before logging in the log file +# @arg $1 file:string log file name +# @arg $2 maxLogFilesCount:int maximum number of log files +Log::rotate() { + local file="$1" + local maxLogFilesCount="${2:-5}" + + if [[ ! -f "${file}" ]]; then + Log::displayDebug "Log file ${file} doesn't exist yet" + return 0 + fi + local i + for ((i = maxLogFilesCount - 1; i > 0; i--)); do + Log::displayInfo "Log rotation ${file}.${i} to ${file}.$((i + 1))" + mv "${file}."{"${i}","$((i + 1))"} &>/dev/null || true + done + if cp "${file}" "${file}.1" &>/dev/null; then + echo >"${file}" # reset log file + Log::displayInfo "Log rotation ${file} to ${file}.1" + fi +} + + +# @description draw a line with the character passed in parameter repeated depending on terminal width +# @arg $1 character:String character to use as separator (default value #) +UI::drawLine() { + local character="${1:-#}" + local -i width=${COLUMNS:-0} + if ((width == 0)) && [[ -t 1 ]]; then + width=$(tput cols) + fi + if ((width == 0)); then + width=80 + fi + printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") + echo +} + + +# @description load color theme +# @noargs +# @env BASH_FRAMEWORK_THEME String theme to use +# @env LOAD_THEME int 0 to avoid loading theme +# @exitcode 0 always successful +UI::requireTheme() { + export REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + + if [[ "${LOAD_THEME:-1}" = "1" ]]; then + UI::theme "${BASH_FRAMEWORK_THEME-default}" + fi +} + + +# @description load colors theme constants +# @warning if tty not opened, noColor theme will be chosen +# @arg $1 theme:String the theme to use (default, noColor) +# @arg $@ args:String[] +# @set __ERROR_COLOR String indicate error status +# @set __INFO_COLOR String indicate info status +# @set __SUCCESS_COLOR String indicate success status +# @set __WARNING_COLOR String indicate warning status +# @set __SKIPPED_COLOR String indicate skipped status +# @set __DEBUG_COLOR String indicate debug status +# @set __HELP_COLOR String indicate help status +# @set __TEST_COLOR String not used +# @set __TEST_ERROR_COLOR String not used +# @set __HELP_TITLE_COLOR String used to display help title in help strings +# @set __HELP_OPTION_COLOR String used to display highlight options in help strings +# +# @set __RESET_COLOR String reset default color +# +# @set __HELP_EXAMPLE String to remove +# @set __HELP_TITLE String to remove +# @set __HELP_NORMAL String to remove +# shellcheck disable=SC2034 +UI::theme() { + local theme="${1-default}" + if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then + theme="noColor" + fi + case "${theme}" in + default | default-force) + theme="default" + ;; + noColor) ;; + *) + Log::fatal "invalid theme provided" + ;; + esac + if [[ "${theme}" = "default" ]]; then + BASH_FRAMEWORK_THEME="default" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='\e[31m' # Red + __INFO_COLOR='\e[44m' # white on lightBlue + __SUCCESS_COLOR='\e[32m' # Green + __WARNING_COLOR='\e[33m' # Yellow + __SKIPPED_COLOR='\e[33m' # Yellow + __DEBUG_COLOR='\e[37m' # Gray + __HELP_COLOR='\e[7;49;33m' # Black on Gold + __TEST_COLOR='\e[100m' # Light magenta + __TEST_ERROR_COLOR='\e[41m' # white on red + __HELP_TITLE_COLOR="\e[1;37m" # Bold + __HELP_OPTION_COLOR="\e[1;34m" # Blue + # Internal: reset color + __RESET_COLOR='\e[0m' # Reset Color + # shellcheck disable=SC2155,SC2034 + __HELP_EXAMPLE="$(echo -e "\e[2;97m")" + # shellcheck disable=SC2155,SC2034 + __HELP_TITLE="$(echo -e "\e[1;37m")" + # shellcheck disable=SC2155,SC2034 + __HELP_NORMAL="$(echo -e "\033[0m")" + else + BASH_FRAMEWORK_THEME="noColor" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='' + __INFO_COLOR='' + __SUCCESS_COLOR='' + __WARNING_COLOR='' + __SKIPPED_COLOR='' + __DEBUG_COLOR='' + __HELP_COLOR='' + __TEST_COLOR='' + __TEST_ERROR_COLOR='' + __HELP_TITLE_COLOR='' + __HELP_OPTION_COLOR='' + # Internal: reset color + __RESET_COLOR='' + __HELP_EXAMPLE='' + __HELP_TITLE='' + __HELP_NORMAL='' + fi +} + + +# @description Check that command version is greater than expected minimal version +# display warning if command version greater than expected minimal version +# display error if command version less than expected minimal version and exit 1 +# @arg $1 commandName:String command path +# @arg $2 argVersion:String command line parameters to launch to get command version +# @arg $3 minimalVersion:String expected minimal command version +# @arg $4 parseVersionCallback:Function +# @arg $5 help:String optional help message to display if command does not exist +# @exitcode 0 if command version greater or equal to expected minimal version +# @exitcode 1 if command version less than expected minimal version +# @exitcode 2 if command does not exist +Version::checkMinimal() { + local commandName="$1" + local argVersion="$2" + local minimalVersion="$3" + local parseVersionCallback=${4:-Version::parse} + local help="${5:-}" + + Assert::commandExists "${commandName}" "${help}" || return 2 + + # shellcheck disable=SC2034 + local status=0 + # shellcheck disable=SC2034 + local -a pipeStatus=() + local version + version="$("${commandName}" "${argVersion}" 2>&1 | ${parseVersionCallback} || Bash::handlePipelineFailure status pipeStatus)" + + Log::displayDebug "check ${commandName} version ${version} against minimal ${minimalVersion}" + + Version::compare "${version}" "${minimalVersion}" || { + local result=$? + if [[ "${result}" = "1" ]]; then + Log::displayInfo "${commandName} version is ${version} greater than ${minimalVersion}" + elif [[ "${result}" = "2" ]]; then + Log::displayError "${commandName} minimal version is ${minimalVersion}, your version is ${version}" + return 1 + fi + return 0 + } + +} + + +# @description compare 2 version numbers +# @arg $1 version1:String version 1 +# @arg $2 version2:String version 2 +# @exitcode 0 if equal +# @exitcode 1 if version1 > version2 +# @exitcode 2 else +Version::compare() { + if [[ "$1" = "$2" ]]; then + return 0 + fi + local IFS=. + # shellcheck disable=2206 + local i ver1=($1) ver2=($2) + # fill empty fields in ver1 with zeros + for ((i = ${#ver1[@]}; i < ${#ver2[@]}; i++)); do + ver1[i]=0 + done + for ((i = 0; i < ${#ver1[@]}; i++)); do + if [[ -z "${ver2[i]+unset}" ]] || [[ -z ${ver2[i]} ]]; then + # fill empty fields in ver2 with zeros + ver2[i]=0 + fi + if ((10#${ver1[i]} > 10#${ver2[i]})); then + return 1 + fi + if ((10#${ver1[i]} < 10#${ver2[i]})); then + return 2 + fi + done + return 0 +} + + +# @description filter to keep only version number from a string +# @arg $@ files:String[] the files to filter +# @exitcode * if one of the filter command fails +# @stdin you can use stdin as alternative to files argument +# @stdout the filtered content +# shellcheck disable=SC2120 +Version::parse() { + # match anything, print(p), exit on first match(Q) + sed -En \ + -e 's/\x1b\[[0-9;]*[mGKHF]//g' \ + -e 's/[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/' \ + -e '//{p;Q}' \ + "$@" +} +# FUNCTIONS + + +declare -a BASH_FRAMEWORK_ARGV_FILTERED=() + +beforeParseCallback() { + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + +copyrightCallback() { + if [[ -z "${copyrightBeginYear}" ]]; then + copyrightBeginYear="$(date +%Y)" + fi + echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" +} + +# shellcheck disable=SC2317 # if function is overridden +updateArgListInfoVerboseCallback() { + BASH_FRAMEWORK_ARGV_FILTERED+=(--verbose) +} +# shellcheck disable=SC2317 # if function is overridden +updateArgListDebugVerboseCallback() { + BASH_FRAMEWORK_ARGV_FILTERED+=(-vv) +} +# shellcheck disable=SC2317 # if function is overridden +updateArgListTraceVerboseCallback() { + BASH_FRAMEWORK_ARGV_FILTERED+=(-vvv) +} +# shellcheck disable=SC2317 # if function is overridden +updateArgListEnvFileCallback() { :; } +# shellcheck disable=SC2317 # if function is overridden +updateArgListLogLevelCallback() { :; } +# shellcheck disable=SC2317 # if function is overridden +updateArgListDisplayLevelCallback() { :; } +# shellcheck disable=SC2317 # if function is overridden +updateArgListNoColorCallback() { + BASH_FRAMEWORK_ARGV_FILTERED+=(--no-color) +} +# shellcheck disable=SC2317 # if function is overridden +updateArgListThemeCallback() { :; } +# shellcheck disable=SC2317 # if function is overridden +updateArgListQuietCallback() { :; } + +# shellcheck disable=SC2317 # if function is overridden +optionHelpCallback() { + Log::displayError "optionHelpCallback needs to be overridden" + exit 0 +} + +# shellcheck disable=SC2317 # if function is overridden +optionVersionCallback() { + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version ${versionNumber}" + exit 0 +} + +# shellcheck disable=SC2317 # if function is overridden +optionEnvFileCallback() { + local envFile="$2" + Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" + if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" + exit 1 + fi +} + +# shellcheck disable=SC2317 # if function is overridden +optionInfoVerboseCallback() { + BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' + BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_INFO} + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionDebugVerboseCallback() { + BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vv' + BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_DEBUG} + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionTraceVerboseCallback() { + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vvv' + BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_TRACE} + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" +} + +getLevel() { + local levelName="$1" + case "${levelName^^}" in + OFF) + echo "${__LEVEL_OFF}" + ;; + ERR | ERROR) + echo "${__LEVEL_ERROR}" + ;; + WARN | WARNING) + echo "${__LEVEL_WARNING}" + ;; + INFO) + echo "${__LEVEL_INFO}" + ;; + DEBUG | TRACE) + echo "${__LEVEL_DEBUG}" ;; - noColor) ;; *) - Log::fatal "invalid theme provided" + Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" + return 1 ;; esac - if [[ "${theme}" = "default" ]]; then - BASH_FRAMEWORK_THEME="default" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='\e[31m' # Red - __INFO_COLOR='\e[44m' # white on lightBlue - __SUCCESS_COLOR='\e[32m' # Green - __WARNING_COLOR='\e[33m' # Yellow - __SKIPPED_COLOR='\e[33m' # Yellow - __DEBUG_COLOR='\e[37m' # Gray - __HELP_COLOR='\e[7;49;33m' # Black on Gold - __TEST_COLOR='\e[100m' # Light magenta - __TEST_ERROR_COLOR='\e[41m' # white on red - __HELP_TITLE_COLOR="\e[1;37m" # Bold - __HELP_OPTION_COLOR="\e[1;34m" # Blue - # Internal: reset color - __RESET_COLOR='\e[0m' # Reset Color - # shellcheck disable=SC2155,SC2034 - __HELP_EXAMPLE="$(echo -e "\e[2;97m")" - # shellcheck disable=SC2155,SC2034 - __HELP_TITLE="$(echo -e "\e[1;37m")" - # shellcheck disable=SC2155,SC2034 - __HELP_NORMAL="$(echo -e "\033[0m")" - else - BASH_FRAMEWORK_THEME="noColor" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='' - __INFO_COLOR='' - __SUCCESS_COLOR='' - __WARNING_COLOR='' - __SKIPPED_COLOR='' - __DEBUG_COLOR='' - __HELP_COLOR='' - __TEST_COLOR='' - __TEST_ERROR_COLOR='' - __HELP_TITLE_COLOR='' - __HELP_OPTION_COLOR='' - # Internal: reset color - __RESET_COLOR='' - __HELP_EXAMPLE='' - __HELP_TITLE='' - __HELP_NORMAL='' +} + +getVerboseLevel() { + local levelName="$1" + case "${levelName^^}" in + OFF) + echo "${__VERBOSE_LEVEL_OFF}" + ;; + ERR | ERROR | WARN | WARNING | INFO) + echo "${__VERBOSE_LEVEL_INFO}" + ;; + DEBUG) + echo "${__VERBOSE_LEVEL_DEBUG}" + ;; + TRACE) + echo "${__VERBOSE_LEVEL_TRACE}" + ;; + *) + Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" + return 1 + ;; + esac +} + +# shellcheck disable=SC2317 # if function is overridden +optionDisplayLevelCallback() { + local level="$2" + local logLevel verboseLevel + logLevel="$(getLevel "${level}")" + verboseLevel="$(getVerboseLevel "${level}")" + BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionLogLevelCallback() { + local level="$2" + local logLevel verboseLevel + logLevel="$(getLevel "${level}")" + verboseLevel="$(getVerboseLevel "${level}")" + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} + echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionLogFileCallback() { + local logFile="$2" + echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionQuietCallback() { + echo "BASH_FRAMEWORK_QUIET_MODE=1" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionNoColorCallback() { + UI::theme "noColor" +} + +# shellcheck disable=SC2317 # if function is overridden +optionThemeCallback() { + UI::theme "$2" +} + +displayConfig() { + echo "Config" + UI::drawLine "-" + local var + while read -r var; do + printf '%-40s = %s\n' "${var}" "$(declare -p "${var}" | sed -E -e 's/^[^=]+=(.*)/\1/')" + done < <(typeset -p | awk 'match($3, "^(BASH_FRAMEWORK_[^=]+)=", m) { print m[1] }' | sort) + exit 0 +} + +optionBashFrameworkConfigCallback() { + if [[ ! -f "$2" ]]; then + Log::fatal "Command ${SCRIPT_NAME} - Bash framework config file '$2' does not exists" + fi +} + +defaultFrameworkConfig="$( + cat <<'EOF' + +# copied from src/_includes/.framework-config.default +# shellcheck disable=SC2034 + +REAL_SCRIPT_FILE="${REAL_SCRIPT_FILE:-$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")}" +FRAMEWORK_ROOT_DIR="${FRAMEWORK_ROOT_DIR:-${REAL_SCRIPT_FILE%/*/*}}" +FRAMEWORK_SRC_DIR="${FRAMEWORK_SRC_DIR:-${FRAMEWORK_ROOT_DIR}/src}" +FRAMEWORK_BIN_DIR="${FRAMEWORK_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/bin}" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_VENDOR_DIR:-${FRAMEWORK_ROOT_DIR}/vendor}" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_VENDOR_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/vendor/bin}" + +# describe the functions that will be skipped from being imported +FRAMEWORK_FUNCTIONS_IGNORE_REGEXP="${FRAMEWORK_FUNCTIONS_IGNORE_REGEXP:-^(Namespace::functions|Functions::myFunction|Namespace::requireSomething|Acquire::ForceIPv4)$}" +# describe the files that do not contain function to be imported +NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/_binaries|^src/_includes|^src/batsHeaders.sh$|^src/_standalone)}" +# describe the files that are allowed to not have an associated bats file +BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" +# describe the files that are allowed to not have a function matching the filename +FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|/testsData/|^manualTests/|\.bats$}" +# Source directories +if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then + FRAMEWORK_SRC_DIRS=( + "${FRAMEWORK_ROOT_DIR}/src" + ) +fi + +# export here all the variables that will be used in your templates +export REPOSITORY_URL="${REPOSITORY_URL:-https://github.com/fchastanet/bash-tools-framework}" + +BASH_FRAMEWORK_THEME="${BASH_FRAMEWORK_THEME:-default}" +BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" +BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" +BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" +BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" + +EOF +)" + +overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" + +commandOptionParseFinished() { + # load default template framework config + defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" + echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" + local -a files=("${defaultEnvFile}") + if [[ -f "${envFile}" ]]; then + files+=("${envFile}") + fi + # shellcheck disable=SC2154 + if [[ -f "${optionBashFrameworkConfig}" ]]; then + files+=("${optionBashFrameworkConfig}") + fi + files+=("${overrideEnvFile}") + Env::requireLoad "${files[@]}" + Log::requireLoad + # shellcheck disable=SC2154 + if [[ "${optionConfig}" = "1" ]]; then + displayConfig fi } -# @description draw a line with the character passed in parameter repeated depending on terminal width -# @arg $1 character:String character to use as separator (default value #) -UI::drawLine() { - local character="${1:-#}" - local -i width=${COLUMNS:-0} - if ((width == 0)) && [[ -t 1 ]]; then - width=$(tput cols) - fi - if ((width == 0)); then - width=80 - fi - printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") - echo + + +beforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + + + +declare SCRIPTS_DIR +declare HOME_SCRIPTS_DIR +# shellcheck disable=SC2034 +declare versionNumber="2.0" +# shellcheck disable=SC2034 +declare copyrightBeginYear="2020" +# shellcheck disable=SC2034 +declare defaultFromDsn="default.remote" +# shellcheck disable=SC2034 +declare outputDirectory="${HOME}/.bash-tools/output" + +beforeParseCallback() { + Assert::commandExists mysql "sudo apt-get install -y mysql-client" + Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" + Assert::commandExists parallel "sudo apt-get install -y parallel" + + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad + Linux::requireExecutedAsUser + Linux::requireRealpathCommand +} + +initConf() { + # shellcheck disable=SC2034 + SCRIPTS_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbScripts" + HOME_SCRIPTS_DIR="${HOME}/.bash-tools/dbScripts" + Db::checkRequirements } -# @description Display message using error color (red) and exit immediately with error status 1 -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::fatal() { - Log::computeDuration - echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - Log::logFatal "$1" - exit 1 +optionHelpCallback() { + dbScriptAllDatabasesCommandHelp + exit 0 } -# @description create a temp file using default TMPDIR variable -# initialized in _includes/_commonHeader.sh -# @env TMPDIR String (default value /tmp) -# @arg $1 templateName:String template name to use(optional) -Framework::createTempFile() { - mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" +longDescriptionFunction() { + local dsnList scriptsList + dsnList="$(Conf::getMergedList "dsn" "env")" + scriptsList="$(Conf::getMergedList "dbScripts" "sh")" + + echo -e "${__HELP_TITLE}NOTE:${__HELP_NORMAL}" + echo -e "the use of output, log-format, verbose options highly depends on the script used" + echo + echo -e "${__HELP_TITLE}LIST OF AVAILABLE DSN:${__HELP_NORMAL}" + echo -e "${dsnList}" + echo + echo -e "${__HELP_TITLE}DEFAULT SCRIPTS DIRECTORY:${__HELP_NORMAL}" + echo -e "${SCRIPTS_DIR-configuration error}" + echo + echo -e "${__HELP_TITLE}USER SCRIPTS DIRECTORY:${__HELP_NORMAL}" + echo -e "${HOME_SCRIPTS_DIR-configuration error}" + echo -e "Allows to override queries defined in 'Default scripts directory'" + echo + echo -e "${__HELP_TITLE}LIST OF AVAILABLE SCRIPTS:${__HELP_NORMAL}" + echo -e "${scriptsList}" + echo + echo -e "${__HELP_TITLE}EXAMPLES:${__HELP_NORMAL} script conf/dbScripts/extractData.sh" + echo -e " executes query databaseSize (see conf/dbQueries/databaseSize.sql) on each db and log the result in log file in default output dir, call it using" + echo -e " ${__HELP_EXAMPLE}$0 -j 10 extractData databaseSize${__HELP_NORMAL}" + echo + echo -e " executes query databaseSize on each db and display the result on stdout (2>/dev/null hides information messages)" + echo -e " ${__HELP_EXAMPLE}$0 -j 10 --log-format none extractData databaseSize${__HELP_NORMAL}" + echo + echo -e " use --verbose to get some debug information" + echo -e " ${__HELP_EXAMPLE}$0 -j 10 --log-format none --verbose extractData databaseSize${__HELP_NORMAL}" + echo + echo -e "${__HELP_TITLE}USE CASES:${__HELP_NORMAL}" + echo -e " you can use this script in order to check that each db model conforms with your ORM schema" + echo -e " simply create a new script in conf/dbQueries that will call your orm schema checker" + echo + echo -e " update multiple db at once (simple to complex update script)" } -# @description ensure env files are loaded -# @arg $@ list of default files to load at the end -# @exitcode 1 if one of env files fails to load -# @stderr diagnostics information is displayed -# shellcheck disable=SC2120 -Env::requireLoad() { - local -a defaultFiles=("$@") - # get list of possible config files - local -a configFiles=() - if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then - # BASH_FRAMEWORK_ENV_FILES is an array - configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") - fi - local localFrameworkConfigFile - localFrameworkConfigFile="$(pwd)/.framework-config" - if [[ -f "${localFrameworkConfigFile}" ]]; then - configFiles+=("${localFrameworkConfigFile}") +outputDirectoryCallback() { + if [[ "${optionOutputDir:0:1}" != "/" ]]; then + # relative path + optionOutputDir="${PWD}/${optionOutputDir}" fi - if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then - configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") + mkdir -p "${optionOutputDir}" || Log::fatal "unable to create directory ${optionOutputDir}" + if [[ ! -d "${optionOutputDir}" || ! -w "${optionOutputDir}" ]]; then + Log::fatal "output dir is not correct or not writable" fi - configFiles+=("${optionEnvFiles[@]}") - configFiles+=("${defaultFiles[@]}") +} - for file in "${configFiles[@]}"; do - # shellcheck source=/.framework-config - CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { - Log::displayError "while loading config file: ${file}" - return 1 +argScriptToExecuteCallback() { + if [[ ! -f "${argScriptToExecute}" ]]; then + declare scriptAbsoluteFile + scriptAbsoluteFile="$(Conf::getAbsoluteFile "dbScripts" "${argScriptToExecute}" "sh")" && { + argScriptToExecute="${scriptAbsoluteFile}" + if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then + Log::displayInfo "Using script file ${scriptAbsoluteFile}" + fi } - done + fi } -# @description activate or not Log::display* and Log::log* functions -# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL -# environment variables loaded by Env::requireLoad -# try to create log file and rotate it if necessary -# @noargs -# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable -# @env BASH_FRAMEWORK_DISPLAY_LEVEL int -# @env BASH_FRAMEWORK_LOG_LEVEL int -# @env BASH_FRAMEWORK_LOG_FILE String -# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 -# @exitcode 0 always successful -# @stderr diagnostics information about log file is displayed -# @require Env::requireLoad -# @require UI::requireTheme -Log::requireLoad() { - if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - export BASH_FRAMEWORK_LOG_LEVEL +dbScriptAllDatabasesCommandCallback() { + if [[ -z "${optionFromDsn}" ]]; then + # default value for FROM_DSN if from-aws not set + optionFromDsn="${defaultFromDsn}" fi +} - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then - if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 - fi - elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - fi - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - # will always be created even if not in info level - Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" - if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then - Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" - fi - fi + +declare -a PARALLEL_OPTIONS + +optionProgressBarCallback() { + PARALLEL_OPTIONS+=(--bar) } -# @description concatenate each element of an array with a separator -# but wrapping text when line length is more than provided argument -# The algorithm will try not to cut the array element if it can. -# - if an arg can be placed on current line it will be, -# otherwise current line is printed and arg is added to the new -# current line -# - Empty arg is interpreted as a new line. -# - Add \r to arg in order to force break line and avoid following -# arg to be concatenated with current arg. -# -# @arg $1 glue:String -# @arg $2 maxLineLength:int -# @arg $3 indentNextLine:int -# @arg $@ array:String[] -Array::wrap2() { - local glue="${1-}" - local -i glueLength="${#glue}" - shift || true - local -i maxLineLength=$1 - shift || true - local -i indentNextLine=$1 - shift || true - local indentStr="" - if ((indentNextLine > 0)); then - indentStr="$(head -c "${indentNextLine}" 0)); do - arg="$1" - shift || true +optionVersionCallback() { + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version ${versionNumber}" + Db::checkRequirements + exit 0 +} - # replace tab by 2 spaces - arg="${arg//$'\t'/ }" - # remove trailing spaces - arg="${arg%[[:blank:]]}" - if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then - printCurrentLine - ((previousLineEmpty = 1)) - continue - else - if ((previousLineEmpty == 1)); then - printCurrentLine + +# ------------------------------------------ +# Command dbScriptAllDatabasesCommand +# ------------------------------------------ + +# options variables initialization +declare optionJobs="1" +declare optionProgressBar="0" +declare optionFromDsn="" +declare optionHelp="0" +declare optionConfig="0" +declare optionBashFrameworkConfig="" +declare optionInfoVerbose="0" +declare optionDebugVerbose="0" +declare optionTraceVerbose="0" +declare -a optionEnvFiles=() +declare optionLogLevel="" +declare optionLogFile="" +declare optionDisplayLevel="" +declare optionNoColor="0" +declare optionTheme="default" +declare optionVersion="0" +declare optionQuiet="0" +declare -a optionDatabases=() +declare optionOutputDir="" +declare optionLogFormat="none" +# arguments variables initialization +declare argScriptToExecute="" +declare -a scriptArguments=() +# @description parse command options and arguments for dbScriptAllDatabasesCommand +dbScriptAllDatabasesCommandParse() { + Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" + Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" + optionJobs="1" + local -i options_parse_optionParsedCountOptionJobs + ((options_parse_optionParsedCountOptionJobs = 0)) || true + optionProgressBar="0" + local -i options_parse_optionParsedCountOptionProgressBar + ((options_parse_optionParsedCountOptionProgressBar = 0)) || true + optionFromDsn="" + local -i options_parse_optionParsedCountOptionFromDsn + ((options_parse_optionParsedCountOptionFromDsn = 0)) || true + optionHelp="0" + local -i options_parse_optionParsedCountOptionHelp + ((options_parse_optionParsedCountOptionHelp = 0)) || true + optionConfig="0" + local -i options_parse_optionParsedCountOptionConfig + ((options_parse_optionParsedCountOptionConfig = 0)) || true + optionBashFrameworkConfig="" + local -i options_parse_optionParsedCountOptionBashFrameworkConfig + ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true + optionInfoVerbose="0" + local -i options_parse_optionParsedCountOptionInfoVerbose + ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true + optionDebugVerbose="0" + local -i options_parse_optionParsedCountOptionDebugVerbose + ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true + optionTraceVerbose="0" + local -i options_parse_optionParsedCountOptionTraceVerbose + ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true + + optionLogLevel="" + local -i options_parse_optionParsedCountOptionLogLevel + ((options_parse_optionParsedCountOptionLogLevel = 0)) || true + optionLogFile="" + local -i options_parse_optionParsedCountOptionLogFile + ((options_parse_optionParsedCountOptionLogFile = 0)) || true + optionDisplayLevel="" + local -i options_parse_optionParsedCountOptionDisplayLevel + ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true + optionNoColor="0" + local -i options_parse_optionParsedCountOptionNoColor + ((options_parse_optionParsedCountOptionNoColor = 0)) || true + optionTheme="default" + local -i options_parse_optionParsedCountOptionTheme + ((options_parse_optionParsedCountOptionTheme = 0)) || true + optionVersion="0" + local -i options_parse_optionParsedCountOptionVersion + ((options_parse_optionParsedCountOptionVersion = 0)) || true + optionQuiet="0" + local -i options_parse_optionParsedCountOptionQuiet + ((options_parse_optionParsedCountOptionQuiet = 0)) || true + local -i options_parse_optionParsedCountOptionDatabases + ((options_parse_optionParsedCountOptionDatabases = 0)) || true + optionOutputDir="" + local -i options_parse_optionParsedCountOptionOutputDir + ((options_parse_optionParsedCountOptionOutputDir = 0)) || true + optionLogFormat="none" + local -i options_parse_optionParsedCountOptionLogFormat + ((options_parse_optionParsedCountOptionLogFormat = 0)) || true + + argScriptToExecute="" + local -i options_parse_argParsedCountArgScriptToExecute + ((options_parse_argParsedCountArgScriptToExecute = 0)) || true + + scriptArguments=() + + + # shellcheck disable=SC2034 + local -i options_parse_parsedArgIndex=0 + while (($# > 0)); do + local options_parse_arg="$1" + local argOptDefaultBehavior=0 + case "${options_parse_arg}" in + # Option 1/20 + # optionJobs alts --jobs|-j + # type: String min 0 max 1 + --jobs | -j) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 fi - ((previousLineEmpty = 0)) || true - fi - # convert eol to args - mapfile -t additionalLines <<<"${arg}" - if ((${#additionalLines[@]} > 1)); then - set -- "${additionalLines[@]}" "$@" - continue - fi - ((argLength = ${#arg})) || true + if ((options_parse_optionParsedCountOptionJobs >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionJobs)) + # shellcheck disable=SC2034 + optionJobs="$1" + optionJobsCallback "${options_parse_arg}" "${optionJobs}" + + ;; + + # Option 2/20 + # optionProgressBar alts --bar|-b + # type: Boolean min 0 max 1 + --bar | -b) + # shellcheck disable=SC2034 + optionProgressBar="1" + + if ((options_parse_optionParsedCountOptionProgressBar >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionProgressBar)) + optionProgressBarCallback "${options_parse_arg}" "${optionProgressBar}" + + ;; + + # Option 3/20 + # optionFromDsn alts --from-dsn|-f + # type: String min 0 max 1 + --from-dsn | -f) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi - # empty arg - if ((argLength == 0)); then - if ((isNewline == 0)); then - # isNewline = 0 means currentLine is not empty - printCurrentLine + if ((options_parse_optionParsedCountOptionFromDsn >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionFromDsn)) + # shellcheck disable=SC2034 + optionFromDsn="$1" + ;; + + # Option 4/20 + # optionHelp alts --help|-h + # type: Boolean min 0 max 1 + --help | -h) + # shellcheck disable=SC2034 + optionHelp="1" + + if ((options_parse_optionParsedCountOptionHelp >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionHelp)) + optionHelpCallback "${options_parse_arg}" "${optionHelp}" + + ;; + + # Option 5/20 + # optionConfig alts --config + # type: Boolean min 0 max 1 + --config) + # shellcheck disable=SC2034 + optionConfig="1" + + if ((options_parse_optionParsedCountOptionConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionConfig)) + ;; + + # Option 6/20 + # optionBashFrameworkConfig alts --bash-framework-config + # type: String min 0 max 1 + --bash-framework-config) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) + # shellcheck disable=SC2034 + optionBashFrameworkConfig="$1" + optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" + + ;; + + # Option 7/20 + # optionInfoVerbose alts --verbose|-v + # type: Boolean min 0 max 1 + --verbose | -v) + # shellcheck disable=SC2034 + optionInfoVerbose="1" + + if ((options_parse_optionParsedCountOptionInfoVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 fi - continue - fi + ((++options_parse_optionParsedCountOptionInfoVerbose)) + optionInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" - if ((isNewline == 0)); then - glueLength="${#glue}" - else - glueLength="0" - fi - if ((currentLineLength + argLength + glueLength > maxLineLength)); then - if ((argLength + glueLength > maxLineLength)); then - # arg is too long to even fit on one line - # we have to split the arg on current and next line - local -i remainingLineLength - ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) - appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" - printCurrentLine - arg="${arg:${remainingLineLength}}" - # remove leading spaces - arg="${arg##[[:blank:]]}" + updateArgListInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" - set -- "${arg}" "$@" - else - # the arg can fit on next line - printCurrentLine - appendToCurrentLine "${arg}" "${argLength}" + ;; + + # Option 8/20 + # optionDebugVerbose alts -vv + # type: Boolean min 0 max 1 + -vv) + # shellcheck disable=SC2034 + optionDebugVerbose="1" + + if ((options_parse_optionParsedCountOptionDebugVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 fi - else - appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" - fi - done - if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then - printCurrentLine - fi - ) | sed -E -e 's/[[:blank:]]+$//' -} + ((++options_parse_optionParsedCountOptionDebugVerbose)) + optionDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" -# @description list the conf files list available in bash-tools/conf/ folder -# and those overridden in ${HOME}/.bash-tools/ folder -# -# @arg $1 confFolder:String the directory name (not the path) to list -# @arg $2 extension:String the extension (.sh by default) -# @arg $3 indentStr:String the indentation (' - ' by default) can be any string compatible with sed not containing any / -# -# @stdout list of files without extension/directory -# @example text -# - default.local -# - default.remote -# - localhost-root -Conf::getMergedList() { - local confFolder="$1" - local extension="${2-sh}" - local indentStr="${3- - }" + updateArgListDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" - local DEFAULT_CONF_DIR="${FRAMEWORK_ROOT_DIR}/conf/${confFolder}" - local HOME_CONF_DIR="${HOME}/.bash-tools/${confFolder}" + ;; - ( - if [[ -d "${DEFAULT_CONF_DIR}" ]]; then - Conf::list "${DEFAULT_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" - fi - if [[ -d "${HOME_CONF_DIR}" ]]; then - Conf::list "${HOME_CONF_DIR}" "" "${extension}" "-type f" "${indentStr}" - fi - ) | sort | uniq -} + # Option 9/20 + # optionTraceVerbose alts -vvv + # type: Boolean min 0 max 1 + -vvv) + # shellcheck disable=SC2034 + optionTraceVerbose="1" -# @description get absolute conf file from specified conf folder deduced using these rules -# * from absolute file (ignores and ) -# * relative to where script is executed (ignores and ) -# * from home/.bash-tools/ -# * from framework conf/ -# -# @arg $1 confFolder:String the directory name (not the path) to list -# @arg $2 conf:String file to use without extension -# @arg $3 extension:String the extension (.sh by default) -# -# @stdout absolute conf filename -# @exitcode 1 if file is not found in any location -Conf::getAbsoluteFile() { - local confFolder="$1" - local conf="$2" - local extension="${3-.sh}" - if [[ -n "${extension}" && "${extension:0:1}" != "." ]]; then - extension=".${extension}" - fi + if ((options_parse_optionParsedCountOptionTraceVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTraceVerbose)) + optionTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" - testAbs() { - local result - result="$(realpath -e "$1" 2>/dev/null)" - # shellcheck disable=SC2181 - if [[ "$?" = "0" && -f "${result}" ]]; then - echo "${result}" - return 0 - fi - return 1 - } + updateArgListTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" - # conf is absolute file (including extension) - testAbs "${confFolder}${extension}" && return 0 - # conf is absolute file - testAbs "${confFolder}" && return 0 - # conf is absolute file (including extension) - testAbs "${conf}${extension}" && return 0 - # conf is absolute file - testAbs "${conf}" && return 0 + ;; - # relative to where script is executed (including extension) - if [[ -n "${CURRENT_DIR+xxx}" ]]; then - testAbs "$(File::concatenatePath "${CURRENT_DIR}" "${confFolder}")/${conf}${extension}" && return 0 - fi - # from home/.bash-tools/ - testAbs "$(File::concatenatePath "${HOME}/.bash-tools" "${confFolder}")/${conf}${extension}" && return 0 + # Option 10/20 + # optionEnvFiles alts --env-file + # type: StringArray min 0 max -1 + --env-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi - if [[ -n "${FRAMEWORK_ROOT_DIR+xxx}" ]]; then - # from framework conf/ (including extension) - testAbs "$(File::concatenatePath "${FRAMEWORK_ROOT_DIR}/conf" "${confFolder}")/${conf}${extension}" && return 0 + ((++options_parse_optionParsedCountOptionEnvFiles)) + optionEnvFiles+=("$1") + optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - # from framework conf/ - testAbs "$(File::concatenatePath "${FRAMEWORK_ROOT_DIR}/conf" "${confFolder}")/${conf}" && return 0 - fi + updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - # file not found - Log::displayError "conf file '${conf}' not found" + ;; - return 1 -} + # Option 11/20 + # optionLogLevel alts --log-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --log-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi -# @description check if command specified exists or return 1 -# with error and message if not -# -# @arg $1 commandName:String on which existence must be checked -# @arg $2 helpIfNotExists:String a help command to display if the command does not exist -# -# @exitcode 1 if the command specified does not exist -# @stderr diagnostic information + help if second argument is provided -Assert::commandExists() { - local commandName="$1" - local helpIfNotExists="$2" + if ((options_parse_optionParsedCountOptionLogLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogLevel)) + # shellcheck disable=SC2034 + optionLogLevel="$1" + optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + ;; + + # Option 12/20 + # optionLogFile alts --log-file + # type: String min 0 max 1 + --log-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi - "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { - Log::displayError "${commandName} is not installed, please install it" - if [[ -n "${helpIfNotExists}" ]]; then - Log::displayInfo "${helpIfNotExists}" - fi - return 1 - } - return 0 -} + if ((options_parse_optionParsedCountOptionLogFile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogFile)) + # shellcheck disable=SC2034 + optionLogFile="$1" + optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + ;; + + # Option 13/20 + # optionDisplayLevel alts --display-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --display-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi -# @description create a new db instance -# Returns immediately if the instance is already initialized -# -# @arg $1 instanceNewInstance:&Map (passed by reference) database instance to use -# @arg $2 dsn:String dsn profile - load the dsn.env profile deduced using rules defined in Conf::getAbsoluteFile -# -# @example -# declare -Agx dbInstance -# Database::newInstance dbInstance "default.local" -# -# @exitcode 1 if dns file not able to loaded -Database::newInstance() { - local -n instanceNewInstance=$1 - local dsn="$2" - local DSN_FILE + if ((options_parse_optionParsedCountOptionDisplayLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDisplayLevel)) + # shellcheck disable=SC2034 + optionDisplayLevel="$1" + optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - if [[ -v instanceNewInstance['INITIALIZED'] && "${instanceNewInstance['INITIALIZED']:-0}" == "1" ]]; then - return - fi + updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - # final auth file generated from dns file - instanceNewInstance['AUTH_FILE']="" - instanceNewInstance['DSN_FILE']="" + ;; - # check dsn file - DSN_FILE="$(Conf::getAbsoluteFile "dsn" "${dsn}" "env")" || return 1 - Database::checkDsnFile "${DSN_FILE}" || return 1 - instanceNewInstance['DSN_FILE']="${DSN_FILE}" + # Option 14/20 + # optionNoColor alts --no-color + # type: Boolean min 0 max 1 + --no-color) + # shellcheck disable=SC2034 + optionNoColor="1" - # shellcheck source=/src/Database/testsData/dsn_valid.env - source "${instanceNewInstance['DSN_FILE']}" + if ((options_parse_optionParsedCountOptionNoColor >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionNoColor)) + optionNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + updateArgListNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + ;; + + # Option 15/20 + # optionTheme alts --theme + # type: String min 0 max 1 + # authorizedValues: default|default-force|noColor + --theme) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ default|default-force|noColor ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + return 1 + fi - instanceNewInstance['USER']="${USER}" - instanceNewInstance['PASSWORD']="${PASSWORD}" - instanceNewInstance['HOSTNAME']="${HOSTNAME}" - instanceNewInstance['PORT']="${PORT}" + if ((options_parse_optionParsedCountOptionTheme >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTheme)) + # shellcheck disable=SC2034 + optionTheme="$1" + optionThemeCallback "${options_parse_arg}" "${optionTheme}" - # generate authFile for easy authentication - instanceNewInstance['AUTH_FILE']=$(mktemp -p "${TMPDIR:-/tmp}" -t "mysql.XXXXXXXXXXXX") - ( - echo "[client]" - echo "user = ${USER}" - echo "password = ${PASSWORD}" - echo "host = ${HOSTNAME}" - echo "port = ${PORT}" - ) >"${instanceNewInstance['AUTH_FILE']}" + updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" - # some of those values can be overridden using the dsn file - # SKIP_COLUMN_NAMES enabled by default - instanceNewInstance['SKIP_COLUMN_NAMES']="${SKIP_COLUMN_NAMES:-1}" - instanceNewInstance['SSL_OPTIONS']="${MYSQL_SSL_OPTIONS:---ssl-mode=DISABLED}" - instanceNewInstance['QUERY_OPTIONS']="${MYSQL_QUERY_OPTIONS:---batch --raw --default-character-set=utf8}" - instanceNewInstance['DUMP_OPTIONS']="${MYSQL_DUMP_OPTIONS:---default-character-set=utf8 --compress --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 ${instanceNewInstance['SSL_OPTIONS']}}" - instanceNewInstance['DB_IMPORT_OPTIONS']="${DB_IMPORT_OPTIONS:---connect-timeout=5 --batch --raw --default-character-set=utf8}" + ;; - instanceNewInstance['INITIALIZED']=1 -} + # Option 16/20 + # optionVersion alts --version + # type: Boolean min 0 max 1 + --version) + # shellcheck disable=SC2034 + optionVersion="1" -# @description set the general options to use on mysql command to query the database -# Differs than setOptions in the way that these options could change each time -# -# @arg $1 instanceSetQueryOptions:&Map (passed by reference) database instance to use -# @arg $2 optionsList:String query options list -Database::setQueryOptions() { - local -n instanceSetQueryOptions=$1 - # shellcheck disable=SC2034 - instanceSetQueryOptions['QUERY_OPTIONS']="$2" -} + if ((options_parse_optionParsedCountOptionVersion >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionVersion)) + optionVersionCallback "${options_parse_arg}" "${optionVersion}" -# @description databases's list of given mysql server -# -# @example text -# - information_schema -# - mysql -# - performance_schema -# - sys -# -# @arg $1 instanceUserDbList:&Map (passed by reference) database instance to use -# @stdout the list of db except mysql admin ones (see example) -# @exitcode * the query exit code -Database::getUserDbList() { - # shellcheck disable=SC2034 - local -n instanceUserDbList=$1 - # shellcheck disable=SC2016 - local sql='SELECT `schema_name` from INFORMATION_SCHEMA.SCHEMATA WHERE `schema_name` NOT IN("information_schema", "mysql", "performance_schema", "sys")' - Database::query instanceUserDbList "${sql}" -} + ;; -bashToolsDefaultConfigTemplate="${bashToolsDefaultConfigTemplate:-$( - cat <<'EOF' -# shellcheck disable=SC2034 + # Option 17/20 + # optionQuiet alts --quiet|-q + # type: Boolean min 0 max 1 + --quiet | -q) + # shellcheck disable=SC2034 + optionQuiet="1" -# Default settings -# you can override these settings by creating ${HOME}/.bash-tools/.env file - -### -### DISPLAY Level -### minimum level of the messages that will be displayed on screen -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} - -### -### DISPLAY duration -### 0: no duration is displayed on the messages -### 1: duration between previous message and current is displayed -### with the message -### -DISPLAY_DURATION=${DISPLAY_DURATION:0} - -### -### Log to file -### -### all log messages will be redirected to log file specified -### this same path will be used inside and outside of the container -### -BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} - -### -### LOG Level -### minimum level of the messages that will be logged into LOG_FILE -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} - -# absolute directory containing db import sql dumps -DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} - -# garbage collect all files for which modification is greater than eg: 30 days (+30) -# each time an existing file is used by dbImport/dbImportTable -# the file modification time is set to now -DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} - -# absolute directory containing dbScripts used by dbScriptAllDatabases -SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} - -# ----------------------------------------------------- -# AWS Parameters -# ----------------------------------------------------- -S3_BASE_URL=${S3_BASE_URL:-} - -# ----------------------------------------------------- -# Postman Parameters -# ----------------------------------------------------- -POSTMAN_API_KEY= -EOF -)}" + if ((options_parse_optionParsedCountOptionQuiet >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionQuiet)) + optionQuietCallback "${options_parse_arg}" "${optionQuiet}" -# @description loads ~/.bash-tools/.env if available -# if not creates it from a default template -# else check if new options need to be added -BashTools::Conf::requireLoad() { - local envFile="${HOME}/.bash-tools/.env" - if [[ ! -f "${envFile}" ]]; then - mkdir -p "${HOME}/.bash-tools" - ( - echo "#!/usr/bin/env bash" - echo "${bashToolsDefaultConfigTemplate}" - ) >"${envFile}" - Log::displayInfo "Configuration file '${envFile}' created" - else - if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then - ( - echo '# -----------------------------------------------------' - echo '# Postman Parameters' - echo '# -----------------------------------------------------' - echo 'POSTMAN_API_KEY=' - ) >>"${envFile}" - fi - fi - # shellcheck source=/conf/.env - source "${envFile}" || { - Log::displayError "impossible to load '${envFile}'" - exit 1 - } -} + updateArgListQuietCallback "${options_parse_arg}" "${optionQuiet}" -# @description ensure COMMAND_BIN_DIR env var is set -# and PATH correctly prepared -# @noargs -# @set COMMAND_BIN_DIR string the directory where to find this command -# @set PATH string add directory where to find this command binary -Compiler::Facade::requireCommandBinDir() { - COMMAND_BIN_DIR="${CURRENT_DIR}" - Env::pathPrepend "${COMMAND_BIN_DIR}" -} + ;; -# @description ensure running user is not root -# @exitcode 1 if current user is root -# @stderr diagnostics information is displayed -Linux::requireExecutedAsUser() { - if [[ "$(id -u)" = "0" ]]; then - Log::fatal "this script should be executed as normal user" - fi -} + # Option 18/20 + # optionDatabases alts --database + # type: StringArray min 0 max 1 + --database) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi -declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR -FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" -LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" -LOG_LAST_LOG_DATE_INIT=1 -LOG_LAST_DURATION_STR="" + if ((options_parse_optionParsedCountOptionDatabases >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDatabases)) + optionDatabases+=("$1") + ;; + + # Option 19/20 + # optionOutputDir alts --output|-o + # type: String min 0 max 1 + --output | -o) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi -# @description compute duration since last call to this function -# the result is set in following env variables. -# in ss.sss (seconds followed by milliseconds precision 3 decimals) -# @noargs -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @set LOG_LAST_LOG_DATE_INIT int (default 1) set to 0 at first call, allows to detect reference log -# @set LOG_LAST_DURATION_STR String the last duration displayed -# @set LOG_LAST_LOG_DATE String the last log date that will be used to compute next diff -Log::computeDuration() { - if ((${DISPLAY_DURATION:-0} == 1)); then - local -i duration=0 - local -i delta=0 - local -i currentLogDate - currentLogDate="${EPOCHREALTIME/[^0-9]/}" - if ((LOG_LAST_LOG_DATE_INIT == 1)); then - LOG_LAST_LOG_DATE_INIT=0 - LOG_LAST_DURATION_STR="Ref" - else - duration=$(((currentLogDate - FIRST_LOG_DATE) / 1000000)) - delta=$(((currentLogDate - LOG_LAST_LOG_DATE) / 1000000)) - LOG_LAST_DURATION_STR="${duration}s/+${delta}s" - fi - LOG_LAST_LOG_DATE="${currentLogDate}" - # shellcheck disable=SC2034 - local microSeconds="${EPOCHREALTIME#*.}" - LOG_LAST_DURATION_STR="$(printf '%(%T)T.%03.0f\n' "${EPOCHSECONDS}" "${microSeconds:0:3}")(${LOG_LAST_DURATION_STR}) - " - else - # shellcheck disable=SC2034 - LOG_LAST_DURATION_STR="" - fi -} + if ((options_parse_optionParsedCountOptionOutputDir >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionOutputDir)) + # shellcheck disable=SC2034 + optionOutputDir="$1" + outputDirectoryCallback "${options_parse_arg}" "${optionOutputDir}" + + ;; + + # Option 20/20 + # optionLogFormat alts --log-format|-l + # type: String min 0 max 1 + # authorizedValues: none|log + --log-format | -l) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ none|log ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([none log])" + return 1 + fi -# @description log message to file -# @arg $1 message:String the message to display -Log::logInfo() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then - Log::logMessage "${2:-INFO}" "$1" - fi -} + if ((options_parse_optionParsedCountOptionLogFormat >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogFormat)) + # shellcheck disable=SC2034 + optionLogFormat="$1" + outputDirectoryCallback "${options_parse_arg}" "${optionLogFormat}" -# @description log message to file -# @arg $1 message:String the message to display -Log::logDebug() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then - Log::logMessage "${2:-DEBUG}" "$1" - fi -} + ;; -# @description log message to file -# @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" - fi -} + -*) + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" + return 1 + fi + ;; + *) + ((minParsedArgIndex0 = 0)) || true + ((maxParsedArgIndex0 = 0)) || true + ((minParsedArgIndex1 = minParsedArgIndex0 + 1)) || true + ((maxParsedArgIndex1 = maxParsedArgIndex0 + 1)) || true + ((minParsedArgIndex2 = minParsedArgIndex1 + 0)) || true + ((maxParsedArgIndex2 = maxParsedArgIndex1)) || true + ((incrementArg = 1 )) + if ((0)); then + # Technical if - never reached + : + + # Argument 1/2 - argScriptToExecute + # Argument argScriptToExecute min 1 max 1 + # Argument argScriptToExecute authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && + options_parse_parsedArgIndex < maxParsedArgIndex1 )); then + if ((options_parse_argParsedCountArgScriptToExecute >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument scriptToExecute - Maximum number of argument occurrences reached(1)" + return 1 + fi + ((++options_parse_argParsedCountArgScriptToExecute)) + # shellcheck disable=SC2034 + argScriptToExecute="${options_parse_arg}" + argScriptToExecuteCallback "${argScriptToExecute}" -- "${@:2}" -# @description log message to file -# @arg $1 message:String the message to display -Log::logError() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_ERROR)); then - Log::logMessage "${2:-ERROR}" "$1" - fi -} -# @description check if tty (interactive mode) is active -# @noargs -# @exitcode 1 if tty not active -# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive -# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive -Assert::tty() { - if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then - return 1 - fi - if [[ "${INTERACTIVE:-0}" = "1" ]]; then - return 0 - fi - tty -s -} + # Argument 2/2 - scriptArguments + # Argument scriptArguments min 0 max -1 + # Argument scriptArguments authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex1 )); then + ((++options_parse_argParsedCountScriptArguments)) + # shellcheck disable=SC2034 + # shellcheck disable=SC2034 + scriptArguments+=("${options_parse_arg}") -# @description log message to file -# @arg $1 message:String the message to display -Log::logFatal() { - Log::logMessage "${2:-FATAL}" "$1" -} -# @description Internal: common log message -# @example text -# [date]|[levelMsg]|message -# -# @example text -# 2020-01-19 19:20:21|ERROR |log error -# 2020-01-19 19:20:21|SKIPPED|log skipped -# -# @arg $1 levelMsg:String message's level description (eg: STATUS, ERROR, ...) -# @arg $2 msg:String the message to display -# @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty -# @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages -# @stderr diagnostics information is displayed -# @require Env::requireLoad -# @require Log::requireLoad -Log::logMessage() { - local levelMsg="$1" - local msg="$2" - local date + # else too much args + else - if [[ -n "${BASH_FRAMEWORK_LOG_FILE}" ]] && ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - date="$(date '+%Y-%m-%d %H:%M:%S')" - touch "${BASH_FRAMEWORK_LOG_FILE}" - printf "%s|%7s|%s\n" "${date}" "${levelMsg}" "${msg}" >>"${BASH_FRAMEWORK_LOG_FILE}" - fi -} -# @description To be called before logging in the log file -# @arg $1 file:string log file name -# @arg $2 maxLogFilesCount:int maximum number of log files -Log::rotate() { - local file="$1" - local maxLogFilesCount="${2:-5}" + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + # too much args and no unknownArgumentCallbacks configured + Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" + return 1 + fi - if [[ ! -f "${file}" ]]; then - Log::displayDebug "Log file ${file} doesn't exist yet" - return 0 - fi - local i - for ((i = maxLogFilesCount - 1; i > 0; i--)); do - Log::displayInfo "Log rotation ${file}.${i} to ${file}.$((i + 1))" - mv "${file}."{"${i}","$((i + 1))"} &>/dev/null || true + fi + if ((incrementArg == 1)); then + ((++options_parse_parsedArgIndex)) + fi + ;; + esac + shift || true done - if cp "${file}" "${file}.1" &>/dev/null; then - echo >"${file}" # reset log file - Log::displayInfo "Log rotation ${file} to ${file}.1" - fi -} -# @description list files of dir with given extension and display it as a list one by line -# -# @arg $1 dir:String the directory to list -# @arg $2 prefix:String the profile file prefix (default: "") -# @arg $3 ext:String the extension -# @arg $4 findOptions:String find options, eg: -type d (Default value: '-type f') -# @arg $5 indentStr:String the indentation can be any string compatible with sed not containing any / (Default value: ' - ') -# @stdout list of files without extension/directory -# @example text -# - default.local -# - default.remote -# - localhost-root -# @exitcode 1 if directory does not exists -Conf::list() { - local dir="$1" - local prefix="${2:-}" - local ext="${3}" - local findOptions="${4--type f}" - local indentStr="${5- - }" - if [[ ! -d "${dir}" ]]; then - Log::displayError "Directory ${dir} does not exist" - fi - if [[ -n "${ext}" && "${ext:0:1}" != "." ]]; then - ext=".${ext}" - fi - ( - # shellcheck disable=SC2086 - cd "${dir}" && - find . -maxdepth 1 ${findOptions} -name "${prefix}*${ext}" | - sed -E "s#^\./${prefix}##g" | - sed -E "s#${ext}\$##g" | sort | sed -E "s#^#${indentStr}#" - ) -} -# @description concatenate 2 paths and ensure the path is correct using realpath -m -# @arg $1 basePath:String -# @arg $2 subPath:String -# @require Linux::requireRealpathCommand -File::concatenatePath() { - local basePath="$1" - local subPath="$2" - local fullPath="${basePath:+${basePath}/}${subPath}" - realpath -m "${fullPath}" 2>/dev/null -} -# @description check if dsn file has all the mandatory variables set -# Mandatory variables are: HOSTNAME, USER, PASSWORD, PORT -# -# @arg $1 dsnFileName:String dsn absolute filename -# @set HOSTNAME loaded from dsn file -# @set PORT loaded from dsn file -# @set USER loaded from dsn file -# @set PASSWORD loaded from dsn file -# @exitcode 0 on valid file -# @exitcode 1 if one of the properties of the conf file is invalid or if file not found -# @stderr log output if error found in conf file -Database::checkDsnFile() { - local dsnFileName="$1" - if [[ ! -f "${dsnFileName}" ]]; then - Log::displayError "dsn file ${dsnFileName} not found" - return 1 - fi - ( - unset HOSTNAME PORT PASSWORD USER - # shellcheck source=/src/Database/testsData/dsn_valid.env - source "${dsnFileName}" - if [[ -z ${HOSTNAME+x} ]]; then - Log::displayError "dsn file ${dsnFileName} : HOSTNAME not provided" - return 1 - fi - if [[ -z "${HOSTNAME}" ]]; then - Log::displayWarning "dsn file ${dsnFileName} : HOSTNAME value not provided" - fi - if [[ "${HOSTNAME}" = "localhost" ]]; then - Log::displayWarning "dsn file ${dsnFileName} : check that HOSTNAME should not be 127.0.0.1 instead of localhost" - fi - if [[ -z "${PORT+x}" ]]; then - Log::displayError "dsn file ${dsnFileName} : PORT not provided" - return 1 - fi - if ! [[ ${PORT} =~ ^[0-9]+$ ]]; then - Log::displayError "dsn file ${dsnFileName} : PORT invalid" - return 1 - fi - if [[ -z "${USER+x}" ]]; then - Log::displayError "dsn file ${dsnFileName} : USER not provided" - return 1 - fi - if [[ -z "${PASSWORD+x}" ]]; then - Log::displayError "dsn file ${dsnFileName} : PASSWORD not provided" - return 1 - fi - ) -} -# @description mysql query on a given db -# @warning could use QUERY_OPTIONS variable from dsn if defined -# @example -# cat file.sql | Database::query ... -# @arg $1 instanceQuery:&Map (passed by reference) database instance to use -# @arg $2 sqlQuery:String (optional) sql query or sql file to execute. if not provided or empty, the command can be piped -# @arg $3 dbName:String (optional) the db name -# -# @exitcode mysql command status code -Database::query() { - local -n instanceQuery=$1 - local -a mysqlCommand=() - local -a queryOptions - mysqlCommand+=(mysql) - mysqlCommand+=("--defaults-extra-file=${instanceQuery['AUTH_FILE']}") - IFS=' ' read -r -a queryOptions <<<"${instanceQuery['QUERY_OPTIONS']}" - mysqlCommand+=("${queryOptions[@]}") - if [[ "${instanceQuery['SKIP_COLUMN_NAMES']}" = "1" ]]; then - mysqlCommand+=("-s" "--skip-column-names") - fi - # add optional db name - if [[ -n "${3+x}" ]]; then - mysqlCommand+=("$3") - fi - # add optional sql query - if [[ -n "${2+x}" && -n "$2" && ! -f "$2" ]]; then - mysqlCommand+=("-e") - mysqlCommand+=("$2") - fi - Log::displayDebug "$(printf "execute command: '%s'" "${mysqlCommand[*]}")" - if [[ -f "$2" ]]; then - "${mysqlCommand[@]}" <"$2" - else - "${mysqlCommand[@]}" - fi -} -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" - fi - done -} -# @description load color theme -# @noargs -# @env BASH_FRAMEWORK_THEME String theme to use -# @env LOAD_THEME int 0 to avoid loading theme -# @exitcode 0 always successful -UI::requireTheme() { - if [[ "${LOAD_THEME:-1}" = "1" ]]; then - UI::theme "${BASH_FRAMEWORK_THEME-default}" - fi -} -# @description ensure command realpath is available -# @exitcode 1 if realpath command not available -# @stderr diagnostics information is displayed -Linux::requireRealpathCommand() { - Assert::commandExists realpath + + + + + + + + + if ((options_parse_argParsedCountArgScriptToExecute < 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument 'scriptToExecute' should be provided at least 1 time(s)" + return 1 + fi || return $? + + + commandOptionParseFinished + dbScriptAllDatabasesCommandCallback + } -# FUNCTIONS +# @description display command options and arguments help for dbScriptAllDatabasesCommand +dbScriptAllDatabasesCommandHelp() { + echo -e "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" + Array::wrap2 ' ' 76 4 " " "Allows to execute a script on each database of specified mysql server." "" -facade_main_dbScriptAllDatabasessh() { -BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" -if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then - FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" -else - # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR - FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" -fi -FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" -FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + echo + echo -# @require BashTools::Conf::requireLoad -if [[ -f "${HOME}/.bash-tools/.env" ]]; then - export BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") -fi -# REQUIRES -Env::requireLoad -UI::requireTheme -Log::requireLoad -Linux::requireRealpathCommand -BashTools::Conf::requireLoad -Compiler::Facade::requireCommandBinDir -Linux::requireExecutedAsUser - -# @require Compiler::Facade::requireCommandBinDir -# shellcheck disable=SC2034 + # ------------------------------------------ + # usage section + # ------------------------------------------ + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "dbScriptAllDatabases [OPTIONS] [ARGUMENTS]" + echo + # ------------------------------------------ + # usage/options section + # ------------------------------------------ + optionsAltList=("[--jobs|-j ]" "[--bar|-b]" "[--from-dsn|-f ]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--database ]" "[--output|-o ]" "[--log-format|-l ]" + ) + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ + "dbScriptAllDatabases" "${optionsAltList[@]}" + echo -# default values -declare outputDirectory="${HOME}/.bash-tools/output" -declare optionFromDsn="" -declare optionOutputDir="" -declare optionLogFormat="" -declare optionJobs="" -declare argScriptToExecute="" -declare -a scriptArguments=() + # ------------------------------------------ + # usage/arguments section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" -# other configuration -declare copyrightBeginYear="2020" -declare QUERIES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbQueries" -declare HOME_QUERIES_DIR="${HOME}/.bash-tools/dbQueries" + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}scriptToExecute${__HELP_NORMAL} {single} (mandatory) + " + Array::wrap2 ' ' 76 4 " " "The script that will be executed on each databases." + echo + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}scriptArguments${__HELP_NORMAL} {list} (optional)] + " + Array::wrap2 ' ' 76 4 " " "Optional parameters to pass to the script." + echo + # ------------------------------------------ + # options section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}JOB OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--jobs${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-j ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "The number of databases to query in parallel." + echo -declare -a BASH_FRAMEWORK_ARGV_FILTERED=() -copyrightCallback() { - if [[ -z "${copyrightBeginYear}" ]]; then - copyrightBeginYear="$(date +%Y)" - fi - echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" -} + Array::wrap2 ' ' 76 6 " Default value: " "1" + echo -# shellcheck disable=SC2317 # if function is overridden -updateArgListInfoVerboseCallback() { - BASH_FRAMEWORK_ARGV_FILTERED+=(--verbose) -} -# shellcheck disable=SC2317 # if function is overridden -updateArgListDebugVerboseCallback() { - BASH_FRAMEWORK_ARGV_FILTERED+=(-vv) -} -# shellcheck disable=SC2317 # if function is overridden -updateArgListTraceVerboseCallback() { - BASH_FRAMEWORK_ARGV_FILTERED+=(-vvv) -} -# shellcheck disable=SC2317 # if function is overridden -updateArgListEnvFileCallback() { :; } -# shellcheck disable=SC2317 # if function is overridden -updateArgListLogLevelCallback() { :; } -# shellcheck disable=SC2317 # if function is overridden -updateArgListDisplayLevelCallback() { :; } -# shellcheck disable=SC2317 # if function is overridden -updateArgListNoColorCallback() { - BASH_FRAMEWORK_ARGV_FILTERED+=(--no-color) -} -# shellcheck disable=SC2317 # if function is overridden -updateArgListThemeCallback() { :; } -# shellcheck disable=SC2317 # if function is overridden -updateArgListQuietCallback() { :; } + echo -e " ${__HELP_OPTION_COLOR}--bar${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-b${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Show progress as a progress bar. In the bar is shown: % of jobs completed," "estimated seconds left, and number of jobs started." "" + echo -# shellcheck disable=SC2317 # if function is overridden -optionHelpCallback() { - dbScriptAllDatabasesCommand help - exit 0 -} -# shellcheck disable=SC2317 # if function is overridden -optionVersionCallback() { - echo "${SCRIPT_NAME} version 2.0" - exit 0 -} + echo + echo -e "${__HELP_TITLE_COLOR}SOURCE OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--from-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-f ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Target mysql server." + echo -# shellcheck disable=SC2317 # if function is overridden -optionEnvFileCallback() { - local envFile="$2" - Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" - if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" - exit 1 - fi -} -# shellcheck disable=SC2317 # if function is overridden -optionInfoVerboseCallback() { - BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' - BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_INFO} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >> "${overrideEnvFile}" -} + echo + echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays this command help" + echo -# shellcheck disable=SC2317 # if function is overridden -optionDebugVerboseCallback() { - BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vv' - BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_DEBUG} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" -} -# shellcheck disable=SC2317 # if function is overridden -optionTraceVerboseCallback() { - BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vvv' - BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_TRACE} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" -} -getLevel() { - local levelName="$1" - case "${levelName^^}" in - OFF) - echo "${__LEVEL_OFF}" - ;; - ERR | ERROR) - echo "${__LEVEL_ERROR}" - ;; - WARN | WARNING) - echo "${__LEVEL_WARNING}" - ;; - INFO) - echo "${__LEVEL_INFO}" - ;; - DEBUG | TRACE) - echo "${__LEVEL_DEBUG}" - ;; - *) - Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" - return 1 - esac -} + echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays configuration" + echo -getVerboseLevel() { - local levelName="$1" - case "${levelName^^}" in - OFF) - echo "${__VERBOSE_LEVEL_OFF}" - ;; - ERR | ERROR | WARN | WARNING | INFO) - echo "${__VERBOSE_LEVEL_INFO}" - ;; - DEBUG) - echo "${__VERBOSE_LEVEL_DEBUG}" - ;; - TRACE) - echo "${__VERBOSE_LEVEL_TRACE}" - ;; - *) - Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" - return 1 - esac -} -# shellcheck disable=SC2317 # if function is overridden -optionDisplayLevelCallback() { - local level="$2" - local logLevel verboseLevel - logLevel="$(getLevel "${level}")" - verboseLevel="$(getVerboseLevel "${level}")" - BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >> "${overrideEnvFile}" -} -# shellcheck disable=SC2317 # if function is overridden -optionLogLevelCallback() { - local level="$2" - local logLevel verboseLevel - logLevel="$(getLevel "${level}")" - verboseLevel="$(getVerboseLevel "${level}")" - BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >> "${overrideEnvFile}" -} + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." + echo -# shellcheck disable=SC2317 # if function is overridden -optionLogFileCallback() { - local logFile="$2" - echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >> "${overrideEnvFile}" -} -# shellcheck disable=SC2317 # if function is overridden -optionQuietCallback() { - echo "BASH_FRAMEWORK_QUIET_MODE=1" >> "${overrideEnvFile}" -} -# shellcheck disable=SC2317 # if function is overridden -optionNoColorCallback() { - UI::theme "noColor" -} + echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" + echo -# shellcheck disable=SC2317 # if function is overridden -optionThemeCallback() { - UI::theme "$2" -} -displayConfig() { - echo "Config" - UI::drawLine "-" - local var - while read -r var; do - printf '%-40s = %s\n' "${var}" "$(declare -p "${var}" | sed -E -e 's/^[^=]+=(.*)/\1/')" - done < <(typeset -p | awk 'match($3, "^(BASH_FRAMEWORK_[^=]+)=", m) { print m[1] }' | sort) - exit 0 -} -optionBashFrameworkConfigCallback() { - if [[ ! -f "$2" ]]; then - Log::fatal "Command ${SCRIPT_NAME} - Bash framework config file '$2' does not exists" - fi -} + echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" + Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log file" + echo + + -defaultFrameworkConfig="$( - cat <<'EOF' -# copied from src/_includes/.framework-config.default -# shellcheck disable=SC2034 + echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set display level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo -REAL_SCRIPT_FILE="${REAL_SCRIPT_FILE:-$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")}" -FRAMEWORK_ROOT_DIR="${FRAMEWORK_ROOT_DIR:-${REAL_SCRIPT_FILE%/*/*}}" -FRAMEWORK_SRC_DIR="${FRAMEWORK_SRC_DIR:-${FRAMEWORK_ROOT_DIR}/src}" -FRAMEWORK_BIN_DIR="${FRAMEWORK_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/bin}" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_VENDOR_DIR:-${FRAMEWORK_ROOT_DIR}/vendor}" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_VENDOR_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/vendor/bin}" -# describe the functions that will be skipped from being imported -FRAMEWORK_FUNCTIONS_IGNORE_REGEXP="${FRAMEWORK_FUNCTIONS_IGNORE_REGEXP:-^(Namespace::functions|Functions::myFunction|Namespace::requireSomething|Acquire::ForceIPv4)$}" -# describe the files that do not contain function to be imported -NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/_binaries|^src/_includes|^src/batsHeaders.sh$|^src/_standalone)}" -# describe the files that are allowed to not have an associated bats file -BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" -# describe the files that are allowed to not have a function matching the filename -FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|\.tpl$|/testsData/|^manualTests/|\.bats$}" -# Source directories -if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then - FRAMEWORK_SRC_DIRS=( - "${FRAMEWORK_ROOT_DIR}/src" - ) -fi + echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." + echo -# export here all the variables that will be used in your templates -export REPOSITORY_URL="${REPOSITORY_URL:-https://github.com/fchastanet/bash-tools-framework}" -BASH_FRAMEWORK_THEME="${BASH_FRAMEWORK_THEME:-default}" -BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" -BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" -BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" -BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" -EOF -)" -overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" + echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." + echo + Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" + echo -commandOptionParseFinished() { - # load default template framework config - defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" - echo "${defaultFrameworkConfig}" > "${defaultEnvFile}" - local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi - # shellcheck disable=SC2154 - if [[ -f "${optionBashFrameworkConfig}" ]]; then - files+=("${optionBashFrameworkConfig}") - fi - files+=("${overrideEnvFile}") - Env::requireLoad "${files[@]}" - Log::requireLoad - # shellcheck disable=SC2154 - if [[ "${optionConfig}" = "1" ]]; then - displayConfig - fi -} + Array::wrap2 ' ' 76 6 " Default value: " "default" + echo -# default values -declare optionFromDsn="" + echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Print version information and quit." + echo -optionJobsCallback() { - if ! [[ ${optionJobs} =~ ^[0-9]+$ ]]; then - Log::fatal "number of jobs is incorrect" - fi - if [[ ${optionJobs} -lt 1 ]]; then - Log::fatal "number of jobs must be greater than 0" - fi -} -declare -a PARALLEL_OPTIONS + echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." + echo -optionProgressBarCallback() { - PARALLEL_OPTIONS+=(--bar) -} -dbScriptAllDatabasesCommand() { - local options_parse_cmd="$1" - shift || true + echo + echo -e "${__HELP_TITLE_COLOR}SCRIPTS OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--database ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "If provided will check only this db, otherwise script will be executed on all dbs of mysql server." + echo - if [[ "${options_parse_cmd}" = "parse" ]]; then - optionJobs="1" - local -i options_parse_optionParsedCountOptionJobs - ((options_parse_optionParsedCountOptionJobs = 0)) || true - optionProgressBar="0" - local -i options_parse_optionParsedCountOptionProgressBar - ((options_parse_optionParsedCountOptionProgressBar = 0)) || true - local -i options_parse_optionParsedCountOptionFromDsn - ((options_parse_optionParsedCountOptionFromDsn = 0)) || true - local -i options_parse_optionParsedCountOptionOutputDir - ((options_parse_optionParsedCountOptionOutputDir = 0)) || true - optionLogFormat="none" - local -i options_parse_optionParsedCountOptionLogFormat - ((options_parse_optionParsedCountOptionLogFormat = 0)) || true - local -i options_parse_optionParsedCountOptionBashFrameworkConfig - ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true - optionConfig="0" - local -i options_parse_optionParsedCountOptionConfig - ((options_parse_optionParsedCountOptionConfig = 0)) || true - optionInfoVerbose="0" - local -i options_parse_optionParsedCountOptionInfoVerbose - ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true - optionDebugVerbose="0" - local -i options_parse_optionParsedCountOptionDebugVerbose - ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true - optionTraceVerbose="0" - local -i options_parse_optionParsedCountOptionTraceVerbose - ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionNoColor="0" - local -i options_parse_optionParsedCountOptionNoColor - ((options_parse_optionParsedCountOptionNoColor = 0)) || true - optionTheme="default" - local -i options_parse_optionParsedCountOptionTheme - ((options_parse_optionParsedCountOptionTheme = 0)) || true - optionHelp="0" - local -i options_parse_optionParsedCountOptionHelp - ((options_parse_optionParsedCountOptionHelp = 0)) || true - optionVersion="0" - local -i options_parse_optionParsedCountOptionVersion - ((options_parse_optionParsedCountOptionVersion = 0)) || true - optionQuiet="0" - local -i options_parse_optionParsedCountOptionQuiet - ((options_parse_optionParsedCountOptionQuiet = 0)) || true - local -i options_parse_optionParsedCountOptionLogLevel - ((options_parse_optionParsedCountOptionLogLevel = 0)) || true - local -i options_parse_optionParsedCountOptionLogFile - ((options_parse_optionParsedCountOptionLogFile = 0)) || true - local -i options_parse_optionParsedCountOptionDisplayLevel - ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true - local -i options_parse_argParsedCountArgScriptToExecute - ((options_parse_argParsedCountArgScriptToExecute = 0)) || true - local -i options_parse_argParsedCountScriptArguments - ((options_parse_argParsedCountScriptArguments = 0)) || true - # shellcheck disable=SC2034 - local -i options_parse_parsedArgIndex=0 - while (($# > 0)); do - local options_parse_arg="$1" - local argOptDefaultBehavior=0 - case "${options_parse_arg}" in - # Option 1/20 - # Option optionJobs --jobs|-j variableType String min 0 max 1 authorizedValues '' regexp '' - --jobs | -j) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionJobs >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionJobs)) - # shellcheck disable=SC2034 - optionJobs="$1" - optionJobsCallback "${options_parse_arg}" "${optionJobs}" - ;; - # Option 2/20 - # Option optionProgressBar --bar|-b variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --bar | -b) - # shellcheck disable=SC2034 - optionProgressBar="1" - if ((options_parse_optionParsedCountOptionProgressBar >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionProgressBar)) - optionProgressBarCallback "${options_parse_arg}" - ;; - # Option 3/20 - # Option optionFromDsn --from-dsn|-f variableType String min 0 max 1 authorizedValues '' regexp '' - --from-dsn | -f) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionFromDsn >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionFromDsn)) - # shellcheck disable=SC2034 - optionFromDsn="$1" - ;; - # Option 4/20 - # Option optionDatabases --database variableType StringArray min 0 max -1 authorizedValues '' regexp '' - --database) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - ((++options_parse_optionParsedCountOptionDatabases)) - optionDatabases+=("$1") - ;; - # Option 5/20 - # Option optionOutputDir --output|-o variableType String min 0 max 1 authorizedValues '' regexp '' - --output | -o) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionOutputDir >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionOutputDir)) - # shellcheck disable=SC2034 - optionOutputDir="$1" - outputDirectoryCallback "${options_parse_arg}" "${optionOutputDir}" - ;; - # Option 6/20 - # Option optionLogFormat --log-format|-l variableType String min 0 max 1 authorizedValues 'none|log' regexp '' - --log-format | -l) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ none|log ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(none|log)" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogFormat >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogFormat)) - # shellcheck disable=SC2034 - optionLogFormat="$1" - ;; - # Option 7/20 - # Option optionBashFrameworkConfig --bash-framework-config variableType String min 0 max 1 authorizedValues '' regexp '' - --bash-framework-config) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) - # shellcheck disable=SC2034 - optionBashFrameworkConfig="$1" - optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" - ;; - # Option 8/20 - # Option optionConfig --config variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --config) - # shellcheck disable=SC2034 - optionConfig="1" - if ((options_parse_optionParsedCountOptionConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionConfig)) - ;; - # Option 9/20 - # Option optionInfoVerbose --verbose|-v variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --verbose | -v) - # shellcheck disable=SC2034 - optionInfoVerbose="1" - if ((options_parse_optionParsedCountOptionInfoVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionInfoVerbose)) - optionInfoVerboseCallback "${options_parse_arg}" - updateArgListInfoVerboseCallback "${options_parse_arg}" - ;; - # Option 10/20 - # Option optionDebugVerbose -vv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vv) - # shellcheck disable=SC2034 - optionDebugVerbose="1" - if ((options_parse_optionParsedCountOptionDebugVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDebugVerbose)) - optionDebugVerboseCallback "${options_parse_arg}" - updateArgListDebugVerboseCallback "${options_parse_arg}" - ;; - # Option 11/20 - # Option optionTraceVerbose -vvv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vvv) - # shellcheck disable=SC2034 - optionTraceVerbose="1" - if ((options_parse_optionParsedCountOptionTraceVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTraceVerbose)) - optionTraceVerboseCallback "${options_parse_arg}" - updateArgListTraceVerboseCallback "${options_parse_arg}" - ;; - # Option 12/20 - # Option optionEnvFiles --env-file variableType StringArray min 0 max -1 authorizedValues '' regexp '' - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - ;; - # Option 13/20 - # Option optionNoColor --no-color variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --no-color) - # shellcheck disable=SC2034 - optionNoColor="1" - if ((options_parse_optionParsedCountOptionNoColor >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionNoColor)) - optionNoColorCallback "${options_parse_arg}" - updateArgListNoColorCallback "${options_parse_arg}" - ;; - # Option 14/20 - # Option optionTheme --theme variableType String min 0 max 1 authorizedValues 'default|default-force|noColor' regexp '' - --theme) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default|default-force|noColor)" - return 1 - fi - if ((options_parse_optionParsedCountOptionTheme >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTheme)) - # shellcheck disable=SC2034 - optionTheme="$1" - optionThemeCallback "${options_parse_arg}" "${optionTheme}" - updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" - ;; - # Option 15/20 - # Option optionHelp --help|-h variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --help | -h) - # shellcheck disable=SC2034 - optionHelp="1" - if ((options_parse_optionParsedCountOptionHelp >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionHelp)) - optionHelpCallback "${options_parse_arg}" - ;; - # Option 16/20 - # Option optionVersion --version variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --version) - # shellcheck disable=SC2034 - optionVersion="1" - if ((options_parse_optionParsedCountOptionVersion >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionVersion)) - optionVersionCallback "${options_parse_arg}" - ;; - # Option 17/20 - # Option optionQuiet --quiet|-q variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --quiet | -q) - # shellcheck disable=SC2034 - optionQuiet="1" - if ((options_parse_optionParsedCountOptionQuiet >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionQuiet)) - optionQuietCallback "${options_parse_arg}" - updateArgListQuietCallback "${options_parse_arg}" - ;; - # Option 18/20 - # Option optionLogLevel --log-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --log-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogLevel)) - # shellcheck disable=SC2034 - optionLogLevel="$1" - optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - ;; - # Option 19/20 - # Option optionLogFile --log-file variableType String min 0 max 1 authorizedValues '' regexp '' - --log-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogFile >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogFile)) - # shellcheck disable=SC2034 - optionLogFile="$1" - optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" - updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" - ;; - # Option 20/20 - # Option optionDisplayLevel --display-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --display-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionDisplayLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDisplayLevel)) - # shellcheck disable=SC2034 - optionDisplayLevel="$1" - optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - ;; - -*) - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" - return 1 - fi - ;; - *) - if ((0)); then - # Technical if - never reached - : - # Argument 1/2 - # Argument argScriptToExecute min 1 max 1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 0 && options_parse_parsedArgIndex < 1)); then - if ((options_parse_argParsedCountArgScriptToExecute >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument scriptToExecute - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountArgScriptToExecute)) - # shellcheck disable=SC2034 - argScriptToExecute="${options_parse_arg}" - argScriptToExecuteCallback "${argScriptToExecute}" -- "${@:2}" - # Argument 2/2 - # Argument scriptArguments min 0 max -1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 1)); then - ((++options_parse_argParsedCountScriptArguments)) - # shellcheck disable=SC2034 - scriptArguments+=("${options_parse_arg}") - else - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" - return 1 - fi - fi - ((++options_parse_parsedArgIndex)) - ;; - esac - shift || true - done - if ((options_parse_argParsedCountArgScriptToExecute < 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument 'scriptToExecute' should be provided at least 1 time(s)" - return 1 - fi - commandOptionParseFinished - dbScriptAllDatabasesCommandCallback - Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" - Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" - elif [[ "${options_parse_cmd}" = "help" ]]; then - Array::wrap2 " " 80 0 "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" "Allows to execute a script on each database of specified mysql server" - echo - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "${SCRIPT_NAME}" "[OPTIONS]" "[ARGUMENTS]")" - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ - "${SCRIPT_NAME}" \ - "[--jobs|-j ]" "[--bar|-b]" "[--from-dsn|-f ]" "[--database ]" "[--output|-o ]" "[--log-format|-l ]" "[--bash-framework-config ]" "[--config]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--no-color]" "[--theme ]" "[--help|-h]" "[--version]" "[--quiet|-q]" "[--log-level ]" "[--log-file ]" "[--display-level ]")" - echo - echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}scriptToExecute${__HELP_NORMAL} {single} (mandatory)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(the\ script\ that\ will\ be\ executed\ on\ each\ databases) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " [${__HELP_OPTION_COLOR}scriptArguments${__HELP_NORMAL} {list} (optional)]" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(optional\ parameters\ to\ pass\ to\ the\ script) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--jobs${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-j ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(specify\ the\ number\ of\ db\ to\ query\ in\ parallel) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: 1' - echo -e " ${__HELP_OPTION_COLOR}--bar${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-b${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Show\ progress\ as\ a\ progress\ bar.\ In\ the\ bar\ is\ shown:\ %\ of\ jobs\ completed\,\ estimated\ seconds\ left\,\ and\ number\ of\ jobs\ started.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}SCRIPT OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--from-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-f ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(target\ mysql\ server) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--database ${__HELP_NORMAL} {list} (optional)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(if\ provided\ will\ check\ only\ this\ db\,\ otherwise\ script\ will\ be\ executed\ on\ all\ dbs\ of\ mysql\ server) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--output${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-o ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(output\ directory\,\ see\ log-format\ option) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--log-format${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-l ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(if\ output\ dir\ provided\,\ will\ log\ each\ db\ result\ to\ log\ file) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: none' - echo ' Possible values: none|log' - echo - echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(use\ alternate\ bash\ framework\ configuration.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ configuration) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(info\ level\ verbose\ mode\ \(alias\ of\ --display-level\ INFO\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(debug\ level\ verbose\ mode\ \(alias\ of\ --display-level\ DEBUG\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(trace\ level\ verbose\ mode\ \(alias\ of\ --display-level\ TRACE\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Load\ the\ specified\ env\ file\ \(deprecated\,\ please\ use\ --bash-framework-config\ option\ instead\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Produce\ monochrome\ output.\ alias\ of\ --theme\ noColor.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' - echo ' Possible values: default|default-force|noColor' - echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ this\ command\ help) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Print\ version\ information\ and\ quit) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(quiet\ mode\,\ doesn\'t\ display\ any\ output) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ file) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(set\ display\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e """ -${__HELP_TITLE}NOTE:${__HELP_NORMAL} -the use of output, log-format, verbose options highly depends on the script used - -${__HELP_TITLE}LIST OF AVAILABLE DSN:${__HELP_NORMAL} -${dsnList} - -${__HELP_TITLE}DEFAULT QUERIES DIRECTORY:${__HELP_NORMAL} -${QUERIES_DIR-configuration error} - -${__HELP_TITLE}USER QUERIES DIRECTORY:${__HELP_NORMAL} -${HOME_QUERIES_DIR-configuration error} -Allows to override queries defined in "Default queries directory" - -${__HELP_TITLE}LIST OF AVAILABLE SCRIPTS:${__HELP_NORMAL} -${scriptsList} - -${__HELP_TITLE}EXAMPLES:${__HELP_NORMAL} script conf/dbScripts/extractData.sh - executes query databaseSize (see conf/dbQueries/databaseSize.sql) on each db and log the result in log file in default output dir, call it using - ${__HELP_EXAMPLE}$0 -j 10 extractData databaseSize${__HELP_NORMAL} - - executes query databaseSize on each db and display the result on stdout (2>/dev/null hides information messages) - ${__HELP_EXAMPLE}$0 -j 10 --log-format none extractData databaseSize${__HELP_NORMAL} - - use --verbose to get some debug information - ${__HELP_EXAMPLE}$0 -j 10 --log-format none --verbose extractData databaseSize${__HELP_NORMAL} - -${__HELP_TITLE}USE CASES:${__HELP_NORMAL} - you can use this script in order to check that each db model conforms with your ORM schema - simply create a new script in conf/dbQueries that will call your orm schema checker - - update multiple db at once (simple to complex update script)""" - echo - echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo '2.0' - echo - echo -e "${__HELP_TITLE_COLOR}AUTHOR:${__RESET_COLOR}" - echo '[François Chastanet](https://github.com/fchastanet)' - echo - echo -e "${__HELP_TITLE_COLOR}SOURCE FILE:${__RESET_COLOR}" - echo 'https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.sh' - echo - echo -e "${__HELP_TITLE_COLOR}LICENSE:${__RESET_COLOR}" - echo 'MIT License' - echo - Array::wrap2 ' ' 76 4 "$(copyrightCallback)" - else - Log::displayError "Command ${SCRIPT_NAME} - Option command invalid: '${options_parse_cmd}'" - return 1 - fi -} -optionHelpCallback() { - local dsnList queriesList scriptsList - dsnList="$(Conf::getMergedList "dsn" "env")" - queriesList="$(Conf::getMergedList "dbQueries" "sql" || true)" - scriptsList="$(Conf::getMergedList "dbScripts" "sh")" + echo -e " ${__HELP_OPTION_COLOR}--output${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-o ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Output directory, see log-format option." + echo - dbScriptAllDatabasesCommand help | envsubst - exit 0 -} -outputDirectoryCallback() { - if [[ "${optionOutputDir:0:1}" != "/" ]]; then - # relative path - optionOutputDir="${PWD}/${optionOutputDir}" - fi - mkdir -p "${optionOutputDir}" || Log::fatal "unable to create directory ${optionOutputDir}" - if [[ ! -d "${optionOutputDir}" || ! -w "${optionOutputDir}" ]]; then - Log::fatal "output dir is not correct or not writable" - fi -} -argScriptToExecuteCallback() { - if [[ ! -f "${argScriptToExecute}" ]]; then - declare scriptAbsoluteFile - scriptAbsoluteFile="$(Conf::getAbsoluteFile "dbScripts" "${argScriptToExecute}" "sh")" && { - argScriptToExecute="${scriptAbsoluteFile}" - if (( BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG )); then - Log::displayInfo "Using script file ${scriptAbsoluteFile}" - fi - } - fi -} + echo -e " ${__HELP_OPTION_COLOR}--log-format${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-l ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "If output dir provided, will log each db result to log file." + echo + Array::wrap2 ' ' 76 6 " Possible values: " "none, " "log" + echo -dbScriptAllDatabasesCommandCallback() { - if [[ -z "${optionFromDsn}" ]]; then - # default value for FROM_DSN if from-aws not set - optionFromDsn="default.remote" - fi + Array::wrap2 ' ' 76 6 " Default value: " "none" + echo + # ------------------------------------------ + # longDescription section + # ------------------------------------------ + echo + echo + echo -e "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" + longDescriptionFunction + # ------------------------------------------ + # version section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" + echo "2.0" + # ------------------------------------------ + # author section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}AUTHOR: ${__RESET_COLOR}" + echo "[François Chastanet](https://github.com/fchastanet)" + # ------------------------------------------ + # sourceFile section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml" + # ------------------------------------------ + # license section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}LICENSE: ${__RESET_COLOR}" + echo "MIT License" + # ------------------------------------------ + # copyright section + # ------------------------------------------ + Array::wrap2 ' ' 76 0 "$(copyrightCallback)" } -dbScriptAllDatabasesCommand parse "${BASH_FRAMEWORK_ARGV[@]}" -# @require Linux::requireExecutedAsUser -run() { +beforeParseCallback +initConf - # check dependencies - Assert::commandExists mysql "sudo apt-get install -y mysql-client" - Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" - Assert::commandExists parallel "sudo apt-get install -y parallel" +dbScriptAllDatabasesCommandParse "$@" +MAIN_FUNCTION_NAME="main" +main() { - # create db instance - declare -Agx dbInstance - Database::newInstance dbInstance "${optionFromDsn}" - Database::setQueryOptions dbInstance "${dbInstance['QUERY_OPTIONS']} --connect-timeout=5" - if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then - Log::displayInfo "Using dsn ${dbInstance['DSN_FILE']}" - fi +# create db instance +declare -Agx dbInstance +# shellcheck disable=SC2154 +Database::newInstance dbInstance "${optionFromDsn}" +Database::setQueryOptions dbInstance "${dbInstance['QUERY_OPTIONS']} --connect-timeout=5" +if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then + Log::displayInfo "Using dsn ${dbInstance['DSN_FILE']}" +fi - # list of all databases - if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then - Log::displayInfo "get the list of all databases" - fi +# list of all databases +if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then + Log::displayInfo "get the list of all databases" +fi - if ((${#optionDatabases[@]} == 0)); then - mapfile -t optionDatabases < <(Database::getUserDbList dbInstance) - fi +if ((${#optionDatabases[@]} == 0)); then + mapfile -t optionDatabases < <(Database::getUserDbList dbInstance) +fi - if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then - Log::displayInfo "processing ${#optionDatabases[@]} databases using ${optionJobs} jobs" - fi +if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then + # shellcheck disable=SC2154 + Log::displayInfo "processing ${#optionDatabases[@]} databases using ${optionJobs} jobs" +fi - export selectedQueryFile - export MYSQL_OPTIONS +export selectedQueryFile +export MYSQL_OPTIONS +PARALLEL_OPTIONS+=("-j" "${optionJobs}") - printf '%s\n' "${optionDatabases[@]}" | parallel --eta --progress --tag --jobs="${optionJobs}" \ +# shellcheck disable=SC2154 +printf '%s\n' "${optionDatabases[@]}" | + SHELL=$(type -p bash) \ + parallel --bar --eta --progress --tag "${PARALLEL_OPTIONS[@]}" \ "${argScriptToExecute}" "${optionFromDsn}" "${optionLogFormat}" "${BASH_FRAMEWORK_ARGS_VERBOSE}" \ "${optionOutputDir}" "${PWD}" "${scriptArguments[@]}" -} - -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run -fi } -facade_main_dbScriptAllDatabasessh "$@" +# if file is sourced avoid calling main function +# shellcheck disable=SC2178 +BASH_SOURCE=".$0" # cannot be changed in bash +# shellcheck disable=SC2128 +if test ".$0" == ".${BASH_SOURCE}"; then + if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then + main "$@" &>/dev/null + else + main "$@" + fi +fi diff --git a/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml b/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml index bbab2601..c7c18a62 100644 --- a/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml +++ b/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml @@ -1,5 +1,6 @@ extends: - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsFromDsn.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" @@ -38,16 +39,6 @@ binData: - --profile - -p - - variableName: optionFromDsn - group: OptionsGroup - type: String - defaultValue: default.remote - help: optionFromDsnHelpFunction - helpValueName: dsn - alts: - - --from-dsn - - -f - - variableName: optionRatio group: OptionsGroup type: String diff --git a/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt b/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt index 4c19ec9c..5246d038 100644 --- a/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt +++ b/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt @@ -2,17 +2,20 @@ Generate optimized profiles to be used by dbImport. USAGE: dbImportProfile [OPTIONS] [ARGUMENTS] -USAGE: dbImportProfile [--help|-h] [--config] +USAGE: dbImportProfile [--from-dsn|-f ] [--help|-h] [--config] [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] [--env-file ] [--log-level ] [--log-file ] [--display-level ] [--no-color] [--theme ] [--version] - [--quiet|-q] [--profile|-p ] [--from-dsn|-f ] - [--ratio|-r ] + [--quiet|-q] [--profile|-p ] [--ratio|-r ] ARGUMENTS: fromDbName {single} (mandatory) The name of the source/remote database. +SOURCE OPTIONS: + --from-dsn, -f  {single} + Target mysql server. + GLOBAL OPTIONS: --help, -h {single} Displays this command help @@ -54,11 +57,6 @@ The name of the profile to write in profiles directory. If not provided, the file name pattern will be 'auto__.sh' - --from-dsn, -f  {single} - dsn to use for source database (Default: default.remote) - - if not provided, the file name pattern will be 'auto__.sh' - Default value: default.remote --ratio, -r  {single} define the ratio to use (0 to 100% - default 70). diff --git a/src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml b/src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml index 590147d9..12d3701e 100644 --- a/src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml +++ b/src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml @@ -1,6 +1,6 @@ extends: - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsJobs.yaml" - - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsProgressBar.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsFromDsn.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" @@ -30,13 +30,13 @@ binData: 11: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.sh 99: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-options.sh mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-main.sh - optionGroups: - groupSourceDbOptions: - title: "QUERY OPTIONS:" help: "Execute a query on multiple databases to generate a tsv format report.\\n The query can be parallelized on multiple databases." longDescription: longDescriptionFunction + optionGroups: + groupQueryOptions: + title: "QUERY OPTIONS:" args: - type: String min: 1 @@ -48,7 +48,7 @@ binData: - argQueryCallback options: - variableName: optionSeparator - group: groupSourceDbOptions + group: groupQueryOptions type: String help: Character to use to separate mysql column. helpValueName: separator @@ -58,11 +58,3 @@ binData: alts: - --separator - -s - - variableName: optionFromDsn - group: groupSourceDbOptions - type: String - help: Target mysql server. - helpValueName: dsn - alts: - - --from-dsn - - -f diff --git a/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt b/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt index 2bf72ab4..f76e3e14 100644 --- a/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt +++ b/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt @@ -1,18 +1,20 @@ SYNOPSIS: Execute a query on multiple databases to generate a tsv format report. + The query can be parallelized on multiple databases. USAGE: dbQueryAllDatabases [OPTIONS] [ARGUMENTS] USAGE: dbQueryAllDatabases [--jobs|-j ] [--bar|-b] - [--help|-h] [--config] [--bash-framework-config ] - [--verbose|-v] [-vv] [-vvv] [--env-file ] [--log-level ] - [--log-file ] [--display-level ] [--no-color] - [--theme ] [--version] [--quiet|-q] [--separator|-s ] - [--from-dsn|-f ] + [--from-dsn|-f ] [--help|-h] [--config] + [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] + [--env-file ] [--log-level ] [--log-file ] + [--display-level ] [--no-color] [--theme ] [--version] + [--quiet|-q] [--separator|-s ] ARGUMENTS: argQuery {single} (mandatory) - Query to execute + Query to execute + - , try to execute the mysql query provided by the file - , search for query file in queries directory (see below) @@ -22,14 +24,16 @@ --jobs, -j  {single} The number of databases to query in parallel. Default value: 1 - -PROGRESS BAR OPTIONS: --bar, -b {single} Show progress as a progress bar. In the bar is shown: % of jobs completed, estimated seconds left, and number of jobs started. +SOURCE OPTIONS: + --from-dsn, -f  {single} + Target mysql server. + GLOBAL OPTIONS: --help, -h {single} Displays this command help @@ -70,8 +74,6 @@ --separator, -s  {single} Character to use to separate mysql column. Default value: | - --from-dsn, -f  {single} - Target mysql server. DESCRIPTION: diff --git a/src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml b/src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml new file mode 100644 index 00000000..b6f6ceb4 --- /dev/null +++ b/src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml @@ -0,0 +1,89 @@ +extends: + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsJobs.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsFromDsn.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" + +vars: + SRC_FILE_PATH: src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml + +compilerConfig: + targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/dbScriptAllDatabases" + relativeRootDirBasedOnTargetDir: .. + srcDirs: + - ${BASH_TOOLS_ROOT_DIR}/src +binData: + commands: + default: + functionName: dbScriptAllDatabasesCommand + version: "2.0" + commandName: dbScriptAllDatabases + beforeParseCallbacks: + - beforeParseCallback + - initConf + callbacks: + - dbScriptAllDatabasesCommandCallback@100 + definitionFiles: + 11: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.sh + 20: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-options.sh + mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-main.sh + help: | + Allows to execute a script on each database of specified mysql server. + longDescription: longDescriptionFunction + optionGroups: + groupScriptsOptions: + title: "SCRIPTS OPTIONS:" + args: + - help: The script that will be executed on each databases. + type: String + min: 1 + max: 1 + name: scriptToExecute + callbacks: + - argScriptToExecuteCallback + variableName: argScriptToExecute + + - help: Optional parameters to pass to the script. + type: StringArray + min: 0 + max: -1 + name: scriptArguments + variableName: scriptArguments + + options: + - variableName: optionDatabases + group: groupScriptsOptions + type: StringArray + help: + If provided will check only this db, otherwise script will be + executed on all dbs of mysql server. + helpValueName: dbName + alts: + - --database + + - variableName: optionOutputDir + group: groupScriptsOptions + type: String + help: Output directory, see log-format option. + helpValueName: outputDirectory + callbacks: + - outputDirectoryCallback + alts: + - --output + - -o + + - variableName: optionLogFormat + group: groupScriptsOptions + type: String + help: If output dir provided, will log each db result to log file. + helpValueName: logFormat + authorizedValuesList: + - none + - log + defaultValue: none + callbacks: + - outputDirectoryCallback + alts: + - --log-format + - -l diff --git a/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-main.sh b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-main.sh new file mode 100755 index 00000000..9964305b --- /dev/null +++ b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-main.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +# create db instance +declare -Agx dbInstance +# shellcheck disable=SC2154 +Database::newInstance dbInstance "${optionFromDsn}" +Database::setQueryOptions dbInstance "${dbInstance['QUERY_OPTIONS']} --connect-timeout=5" +if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then + Log::displayInfo "Using dsn ${dbInstance['DSN_FILE']}" +fi + +# list of all databases +if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then + Log::displayInfo "get the list of all databases" +fi + +if ((${#optionDatabases[@]} == 0)); then + mapfile -t optionDatabases < <(Database::getUserDbList dbInstance) +fi + +if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then + # shellcheck disable=SC2154 + Log::displayInfo "processing ${#optionDatabases[@]} databases using ${optionJobs} jobs" +fi + +export selectedQueryFile +export MYSQL_OPTIONS +PARALLEL_OPTIONS+=("-j" "${optionJobs}") + +# shellcheck disable=SC2154 +printf '%s\n' "${optionDatabases[@]}" | + SHELL=$(type -p bash) \ + parallel --bar --eta --progress --tag "${PARALLEL_OPTIONS[@]}" \ + "${argScriptToExecute}" "${optionFromDsn}" "${optionLogFormat}" "${BASH_FRAMEWORK_ARGS_VERBOSE}" \ + "${optionOutputDir}" "${PWD}" "${scriptArguments[@]}" diff --git a/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-options.sh b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-options.sh new file mode 100755 index 00000000..596994cb --- /dev/null +++ b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-options.sh @@ -0,0 +1,105 @@ +#!/usr/bin/env bash + +declare SCRIPTS_DIR +declare HOME_SCRIPTS_DIR +# shellcheck disable=SC2034 +declare versionNumber="2.0" +# shellcheck disable=SC2034 +declare copyrightBeginYear="2020" +# shellcheck disable=SC2034 +declare defaultFromDsn="default.remote" +# shellcheck disable=SC2034 +declare outputDirectory="${HOME}/.bash-tools/output" + +beforeParseCallback() { + Assert::commandExists mysql "sudo apt-get install -y mysql-client" + Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" + Assert::commandExists parallel "sudo apt-get install -y parallel" + + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad + Linux::requireExecutedAsUser + Linux::requireRealpathCommand +} + +initConf() { + # shellcheck disable=SC2034 + SCRIPTS_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbScripts" + HOME_SCRIPTS_DIR="${HOME}/.bash-tools/dbScripts" + Db::checkRequirements +} + +optionHelpCallback() { + dbScriptAllDatabasesCommandHelp + exit 0 +} + +longDescriptionFunction() { + local dsnList scriptsList + dsnList="$(Conf::getMergedList "dsn" "env")" + scriptsList="$(Conf::getMergedList "dbScripts" "sh")" + + echo -e "${__HELP_TITLE}NOTE:${__HELP_NORMAL}" + echo -e "the use of output, log-format, verbose options highly depends on the script used" + echo + echo -e "${__HELP_TITLE}LIST OF AVAILABLE DSN:${__HELP_NORMAL}" + echo -e "${dsnList}" + echo + echo -e "${__HELP_TITLE}DEFAULT SCRIPTS DIRECTORY:${__HELP_NORMAL}" + echo -e "${SCRIPTS_DIR-configuration error}" + echo + echo -e "${__HELP_TITLE}USER SCRIPTS DIRECTORY:${__HELP_NORMAL}" + echo -e "${HOME_SCRIPTS_DIR-configuration error}" + echo -e "Allows to override queries defined in 'Default scripts directory'" + echo + echo -e "${__HELP_TITLE}LIST OF AVAILABLE SCRIPTS:${__HELP_NORMAL}" + echo -e "${scriptsList}" + echo + echo -e "${__HELP_TITLE}EXAMPLES:${__HELP_NORMAL} script conf/dbScripts/extractData.sh" + echo -e " executes query databaseSize (see conf/dbQueries/databaseSize.sql) on each db and log the result in log file in default output dir, call it using" + echo -e " ${__HELP_EXAMPLE}$0 -j 10 extractData databaseSize${__HELP_NORMAL}" + echo + echo -e " executes query databaseSize on each db and display the result on stdout (2>/dev/null hides information messages)" + echo -e " ${__HELP_EXAMPLE}$0 -j 10 --log-format none extractData databaseSize${__HELP_NORMAL}" + echo + echo -e " use --verbose to get some debug information" + echo -e " ${__HELP_EXAMPLE}$0 -j 10 --log-format none --verbose extractData databaseSize${__HELP_NORMAL}" + echo + echo -e "${__HELP_TITLE}USE CASES:${__HELP_NORMAL}" + echo -e " you can use this script in order to check that each db model conforms with your ORM schema" + echo -e " simply create a new script in conf/dbQueries that will call your orm schema checker" + echo + echo -e " update multiple db at once (simple to complex update script)" +} + +outputDirectoryCallback() { + if [[ "${optionOutputDir:0:1}" != "/" ]]; then + # relative path + optionOutputDir="${PWD}/${optionOutputDir}" + fi + mkdir -p "${optionOutputDir}" || Log::fatal "unable to create directory ${optionOutputDir}" + if [[ ! -d "${optionOutputDir}" || ! -w "${optionOutputDir}" ]]; then + Log::fatal "output dir is not correct or not writable" + fi +} + +argScriptToExecuteCallback() { + if [[ ! -f "${argScriptToExecute}" ]]; then + declare scriptAbsoluteFile + scriptAbsoluteFile="$(Conf::getAbsoluteFile "dbScripts" "${argScriptToExecute}" "sh")" && { + argScriptToExecute="${scriptAbsoluteFile}" + if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then + Log::displayInfo "Using script file ${scriptAbsoluteFile}" + fi + } + fi +} + +dbScriptAllDatabasesCommandCallback() { + if [[ -z "${optionFromDsn}" ]]; then + # default value for FROM_DSN if from-aws not set + optionFromDsn="${defaultFromDsn}" + fi +} diff --git a/src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.bats b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases.bats similarity index 93% rename from src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.bats rename to src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases.bats index 38419764..a032b493 100755 --- a/src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.bats +++ b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases.bats @@ -1,7 +1,7 @@ #!/usr/bin/env bash # shellcheck source=src/batsHeaders.sh -source "$(cd "${BATS_TEST_DIRNAME}/../.." && pwd)/batsHeaders.sh" +source "$(cd "${BATS_TEST_DIRNAME}/../../.." && pwd)/batsHeaders.sh" setup() { export TMPDIR="${BATS_TEST_TMPDIR}" @@ -59,7 +59,7 @@ function Database::dbScriptAllDatabases::extractData { #@test # shellcheck disable=SC2016 stub parallel \ - '--eta --progress --tag --jobs=1 * * * * * * * * : while IFS= read -r db; do "${@:5}" "${db}"; done' + '--bar --eta --progress --tag -j 1 * * * * * * * * : while IFS= read -r db; do "${@:7}" "${db}"; done' run "${binDir}/dbScriptAllDatabases" \ -f "${BATS_TEST_DIRNAME}/testsData/databaseSize.envProvided.sh" \ diff --git a/src/_binaries/DbScriptAllDatabases/testsData/databaseSize.envProvided.sh b/src/_binaries/Database/dbScriptAllDatabases/testsData/databaseSize.envProvided.sh similarity index 100% rename from src/_binaries/DbScriptAllDatabases/testsData/databaseSize.envProvided.sh rename to src/_binaries/Database/dbScriptAllDatabases/testsData/databaseSize.envProvided.sh diff --git a/src/_binaries/DbScriptAllDatabases/testsData/databaseSize.result_db1 b/src/_binaries/Database/dbScriptAllDatabases/testsData/databaseSize.result_db1 similarity index 100% rename from src/_binaries/DbScriptAllDatabases/testsData/databaseSize.result_db1 rename to src/_binaries/Database/dbScriptAllDatabases/testsData/databaseSize.result_db1 diff --git a/src/_binaries/DbScriptAllDatabases/testsData/databaseSize.result_db2 b/src/_binaries/Database/dbScriptAllDatabases/testsData/databaseSize.result_db2 similarity index 100% rename from src/_binaries/DbScriptAllDatabases/testsData/databaseSize.result_db2 rename to src/_binaries/Database/dbScriptAllDatabases/testsData/databaseSize.result_db2 diff --git a/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt b/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt new file mode 100644 index 00000000..b6f9311a --- /dev/null +++ b/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt @@ -0,0 +1,122 @@ +SYNOPSIS: + Allows to execute a script on each database of specified mysql server. + +USAGE: dbScriptAllDatabases [OPTIONS] [ARGUMENTS] +USAGE: dbScriptAllDatabases [--jobs|-j ] [--bar|-b] + [--from-dsn|-f ] [--help|-h] [--config] + [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] + [--env-file ] [--log-level ] [--log-file ] + [--display-level ] [--no-color] [--theme ] [--version] + [--quiet|-q] [--database ] [--output|-o ] + [--log-format|-l ] + +ARGUMENTS: + scriptToExecute {single} (mandatory) + The script that will be executed on each databases. + [scriptArguments {list} (optional)] + Optional parameters to pass to the script. + +JOB OPTIONS: + --jobs, -j  {single} + The number of databases to query in parallel. + Default value: 1 + --bar, -b {single} + + Show progress as a progress bar. In the bar is shown: % of jobs completed, + estimated seconds left, and number of jobs started. + + +SOURCE OPTIONS: + --from-dsn, -f  {single} + Target mysql server. + +GLOBAL OPTIONS: + --help, -h {single} + Displays this command help + --config {single} + Displays configuration + --bash-framework-config  {single} + Use alternate bash framework configuration. + --verbose, -v {single} + Info level verbose mode (alias of --display-level INFO) + -vv {single} + Debug level verbose mode (alias of --display-level DEBUG) + -vvv {single} + Trace level verbose mode (alias of --display-level TRACE) + --env-file  {list} (optional) + Load the specified env file (deprecated, please use --bash-framework-con + fig option instead) + --log-level  {single} + Set log level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --log-file  {single} + Set log file + --display-level  {single} + Set display level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --no-color {single} + Produce monochrome output. alias of --theme noColor. + --theme  {single} + Choose color theme - default-force means colors will be produced even if + command is piped. + Possible values: default, default-force, noColor + Default value: default + --version {single} + Print version information and quit. + --quiet, -q {single} + Quiet mode, doesn't display any output. + +SCRIPTS OPTIONS: + --database  {single} + If provided will check only this db, otherwise script will be executed o + n all dbs of mysql server. + --output, -o  {single} + Output directory, see log-format option. + --log-format, -l  {single} + If output dir provided, will log each db result to log file. + Possible values: none, log + Default value: none + + +DESCRIPTION: +NOTE: +the use of output, log-format, verbose options highly depends on the script used + +LIST OF AVAILABLE DSN: + - dsn_local + - dsn_valid + +DEFAULT SCRIPTS DIRECTORY: +/bash/conf/dbScripts + +USER SCRIPTS DIRECTORY: +home/.bash-tools/dbScripts +Allows to override queries defined in 'Default scripts directory' + +LIST OF AVAILABLE SCRIPTS: + + +EXAMPLES: script conf/dbScripts/extractData.sh + executes query databaseSize (see conf/dbQueries/databaseSize.sql) on each db and log the result in log file in default output dir, call it using + /bash/bin/dbScriptAllDatabases -j 10 extractData databaseSize + + executes query databaseSize on each db and display the result on stdout (2>/dev/null hides information messages) + /bash/bin/dbScriptAllDatabases -j 10 --log-format none extractData databaseSize + + use --verbose to get some debug information + /bash/bin/dbScriptAllDatabases -j 10 --log-format none --verbose extractData databaseSize + +USE CASES: + you can use this script in order to check that each db model conforms with your ORM schema + simply create a new script in conf/dbQueries that will call your orm schema checker + + update multiple db at once (simple to complex update script) + +VERSION: 2.0 + +AUTHOR: [François Chastanet](https://github.com/fchastanet) + +SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml + +LICENSE: MIT License +Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/DbScriptAllDatabases/testsData/dbScriptAllDatabases.result b/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.result similarity index 100% rename from src/_binaries/DbScriptAllDatabases/testsData/dbScriptAllDatabases.result rename to src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.result diff --git a/src/_binaries/DbScriptAllDatabases/testsData/dsn_local.env b/src/_binaries/Database/dbScriptAllDatabases/testsData/dsn_local.env similarity index 100% rename from src/_binaries/DbScriptAllDatabases/testsData/dsn_local.env rename to src/_binaries/Database/dbScriptAllDatabases/testsData/dsn_local.env diff --git a/src/_binaries/DbScriptAllDatabases/testsData/dsn_valid.env b/src/_binaries/Database/dbScriptAllDatabases/testsData/dsn_valid.env similarity index 100% rename from src/_binaries/DbScriptAllDatabases/testsData/dsn_valid.env rename to src/_binaries/Database/dbScriptAllDatabases/testsData/dsn_valid.env diff --git a/src/_binaries/DbScriptAllDatabases/testsData/getUserDbList.result b/src/_binaries/Database/dbScriptAllDatabases/testsData/getUserDbList.result similarity index 100% rename from src/_binaries/DbScriptAllDatabases/testsData/getUserDbList.result rename to src/_binaries/Database/dbScriptAllDatabases/testsData/getUserDbList.result diff --git a/src/_binaries/DbScriptAllDatabases/testsData/parallel b/src/_binaries/Database/dbScriptAllDatabases/testsData/parallel similarity index 100% rename from src/_binaries/DbScriptAllDatabases/testsData/parallel rename to src/_binaries/Database/dbScriptAllDatabases/testsData/parallel diff --git a/src/_binaries/DbScriptAllDatabases/testsData/parallelDbScriptAllDatabases b/src/_binaries/Database/dbScriptAllDatabases/testsData/parallelDbScriptAllDatabases similarity index 100% rename from src/_binaries/DbScriptAllDatabases/testsData/parallelDbScriptAllDatabases rename to src/_binaries/Database/dbScriptAllDatabases/testsData/parallelDbScriptAllDatabases diff --git a/src/_binaries/DbScriptAllDatabases/testsData/pv b/src/_binaries/Database/dbScriptAllDatabases/testsData/pv similarity index 100% rename from src/_binaries/DbScriptAllDatabases/testsData/pv rename to src/_binaries/Database/dbScriptAllDatabases/testsData/pv diff --git a/src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.options.tpl b/src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.options.tpl deleted file mode 100644 index ab637986..00000000 --- a/src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.options.tpl +++ /dev/null @@ -1,159 +0,0 @@ -% -declare versionNumber="2.0" -declare commandFunctionName="dbScriptAllDatabasesCommand" -declare help="Allows to execute a script on each database of specified mysql server" -# shellcheck disable=SC2016 -declare longDescription=''' -${__HELP_TITLE}NOTE:${__HELP_NORMAL} -the use of output, log-format, verbose options highly depends on the script used - -${__HELP_TITLE}LIST OF AVAILABLE DSN:${__HELP_NORMAL} -${dsnList} - -${__HELP_TITLE}DEFAULT QUERIES DIRECTORY:${__HELP_NORMAL} -${QUERIES_DIR-configuration error} - -${__HELP_TITLE}USER QUERIES DIRECTORY:${__HELP_NORMAL} -${HOME_QUERIES_DIR-configuration error} -Allows to override queries defined in "Default queries directory" - -${__HELP_TITLE}LIST OF AVAILABLE SCRIPTS:${__HELP_NORMAL} -${scriptsList} - -${__HELP_TITLE}EXAMPLES:${__HELP_NORMAL} script conf/dbScripts/extractData.sh - executes query databaseSize (see conf/dbQueries/databaseSize.sql) on each db and log the result in log file in default output dir, call it using - ${__HELP_EXAMPLE}$0 -j 10 extractData databaseSize${__HELP_NORMAL} - - executes query databaseSize on each db and display the result on stdout (2>/dev/null hides information messages) - ${__HELP_EXAMPLE}$0 -j 10 --log-format none extractData databaseSize${__HELP_NORMAL} - - use --verbose to get some debug information - ${__HELP_EXAMPLE}$0 -j 10 --log-format none --verbose extractData databaseSize${__HELP_NORMAL} - -${__HELP_TITLE}USE CASES:${__HELP_NORMAL} - you can use this script in order to check that each db model conforms with your ORM schema - simply create a new script in conf/dbQueries that will call your orm schema checker - - update multiple db at once (simple to complex update script) - -''' -declare defaultFromDsn="default.remote" -% - -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.base.tpl)" -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.dsn.tpl)" -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.jobs.tpl)" -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.progressBar.tpl)" - -% -# shellcheck source=/dev/null -source <( - Options::generateGroup \ - --title "SCRIPT OPTIONS:" \ - --function-name groupSourceDbOptionsFunction - - argScriptToExecuteCallback() { :; } - Options::generateArg \ - --help "the script that will be executed on each databases" \ - --name 'scriptToExecute' \ - --min 1 \ - --max 1 \ - --variable-name "argScriptToExecute" \ - --callback argScriptToExecuteCallback \ - --function-name argScriptToExecuteFunction - - Options::generateArg \ - --help "optional parameters to pass to the script" \ - --name 'scriptArguments' \ - --min 0 \ - --max -1 \ - --variable-name "scriptArguments" \ - --function-name scriptArgumentsFunction - - # shellcheck disable=SC2116 - Options::generateOption \ - --help "if provided will check only this db, otherwise script will be executed on all dbs of mysql server" \ - --help-value-name "dbName" \ - --variable-type "StringArray" \ - --group groupSourceDbOptionsFunction \ - --alt "--database" \ - --variable-name "optionDatabases" \ - --function-name optionDatabasesFunction - - # shellcheck disable=SC2116 - Options::generateOption \ - --help "output directory, see log-format option" \ - --help-value-name "outputDirectory" \ - --variable-type "String" \ - --group groupSourceDbOptionsFunction \ - --alt "--output" \ - --alt "-o" \ - --callback outputDirectoryCallback \ - --variable-name "optionOutputDir" \ - --function-name optionOutputDirFunction - - # shellcheck disable=SC2116 - Options::generateOption \ - --help "if output dir provided, will log each db result to log file" \ - --help-value-name "logFormat" \ - --authorized-values "none|log" \ - --default-value "none" \ - --group groupSourceDbOptionsFunction \ - --alt "--log-format" \ - --alt "-l" \ - --variable-type "String" \ - --variable-name "optionLogFormat" \ - --function-name optionLogFormatFunction -) -options+=( - argScriptToExecuteFunction - scriptArgumentsFunction - optionDatabasesFunction - optionOutputDirFunction - optionLogFormatFunction - --callback dbScriptAllDatabasesCommandCallback -) -Options::generateCommand "${options[@]}" -% - -optionHelpCallback() { - local dsnList queriesList scriptsList - dsnList="$(Conf::getMergedList "dsn" "env")" - queriesList="$(Conf::getMergedList "dbQueries" "sql" || true)" - scriptsList="$(Conf::getMergedList "dbScripts" "sh")" - - <% ${commandFunctionName} %> help | envsubst - exit 0 -} - -outputDirectoryCallback() { - if [[ "${optionOutputDir:0:1}" != "/" ]]; then - # relative path - optionOutputDir="${PWD}/${optionOutputDir}" - fi - mkdir -p "${optionOutputDir}" || Log::fatal "unable to create directory ${optionOutputDir}" - if [[ ! -d "${optionOutputDir}" || ! -w "${optionOutputDir}" ]]; then - Log::fatal "output dir is not correct or not writable" - fi -} - -argScriptToExecuteCallback() { - if [[ ! -f "${argScriptToExecute}" ]]; then - declare scriptAbsoluteFile - scriptAbsoluteFile="$(Conf::getAbsoluteFile "dbScripts" "${argScriptToExecute}" "sh")" && { - argScriptToExecute="${scriptAbsoluteFile}" - if (( BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG )); then - Log::displayInfo "Using script file ${scriptAbsoluteFile}" - fi - } - fi -} - -dbScriptAllDatabasesCommandCallback() { - if [[ -z "${optionFromDsn}" ]]; then - # default value for FROM_DSN if from-aws not set - optionFromDsn="<% ${defaultFromDsn} %>" - fi -} - -<% ${commandFunctionName} %> parse "${BASH_FRAMEWORK_ARGV[@]}" diff --git a/src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.sh b/src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.sh deleted file mode 100755 index 775d0ef8..00000000 --- a/src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env bash -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/dbScriptAllDatabases -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE -# shellcheck disable=SC2034 - -# default values -declare outputDirectory="${HOME}/.bash-tools/output" -declare optionFromDsn="" -declare optionOutputDir="" -declare optionLogFormat="" -declare optionJobs="" -declare argScriptToExecute="" -declare -a scriptArguments=() - -# other configuration -declare copyrightBeginYear="2020" -declare QUERIES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbQueries" -declare HOME_QUERIES_DIR="${HOME}/.bash-tools/dbQueries" - -.INCLUDE "$(dynamicTemplateDir _binaries/DbScriptAllDatabases/dbScriptAllDatabases.options.tpl)" - -# @require Linux::requireExecutedAsUser -run() { - - # check dependencies - Assert::commandExists mysql "sudo apt-get install -y mysql-client" - Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" - Assert::commandExists parallel "sudo apt-get install -y parallel" - - # create db instance - declare -Agx dbInstance - Database::newInstance dbInstance "${optionFromDsn}" - Database::setQueryOptions dbInstance "${dbInstance['QUERY_OPTIONS']} --connect-timeout=5" - if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then - Log::displayInfo "Using dsn ${dbInstance['DSN_FILE']}" - fi - - # list of all databases - if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then - Log::displayInfo "get the list of all databases" - fi - - if ((${#optionDatabases[@]} == 0)); then - mapfile -t optionDatabases < <(Database::getUserDbList dbInstance) - fi - - if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_DEBUG)); then - Log::displayInfo "processing ${#optionDatabases[@]} databases using ${optionJobs} jobs" - fi - - export selectedQueryFile - export MYSQL_OPTIONS - - printf '%s\n' "${optionDatabases[@]}" | parallel --eta --progress --tag --jobs="${optionJobs}" \ - "${argScriptToExecute}" "${optionFromDsn}" "${optionLogFormat}" "${BASH_FRAMEWORK_ARGS_VERBOSE}" \ - "${optionOutputDir}" "${PWD}" "${scriptArguments[@]}" -} - -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run -fi diff --git a/src/_binaries/DbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt b/src/_binaries/DbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt deleted file mode 100644 index be81fafe..00000000 --- a/src/_binaries/DbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt +++ /dev/null @@ -1,118 +0,0 @@ -DESCRIPTION: -Allows to execute a script on each database of specified mysql server -USAGE: dbScriptAllDatabases [OPTIONS] [ARGUMENTS] -USAGE: dbScriptAllDatabases [--jobs|-j ] [--bar|-b] - [--from-dsn|-f ] [--database ] [--output|-o ] - [--log-format|-l ] [--bash-framework-config ] [--config] - [--verbose|-v] [-vv] [-vvv] [--env-file ] [--no-color] - [--theme ] [--help|-h] [--version] [--quiet|-q] [--log-level ] - [--log-file ] [--display-level ] - -ARGUMENTS: - scriptToExecute {single} (mandatory) - the script that will be executed on each databases - [scriptArguments {list} (optional)] - optional parameters to pass to the script - -OPTIONS: - --jobs, -j  {single} - specify the number of db to query in parallel - Default value: 1 - --bar, -b {single} - Show progress as a progress bar. In the bar is shown: % of jobs completed, e - stimated seconds left, and number of jobs started. - -SCRIPT OPTIONS: - --from-dsn, -f  {single} - target mysql server - --database  {list} (optional) - if provided will check only this db, otherwise script will be executed on al - l dbs of mysql server - --output, -o  {single} - output directory, see log-format option - --log-format, -l  {single} - if output dir provided, will log each db result to log file - Default value: none - Possible values: none|log - -GLOBAL OPTIONS: - --bash-framework-config  {single} - use alternate bash framework configuration. - --config {single} - Display configuration - --verbose, -v {single} - info level verbose mode (alias of --display-level INFO) - -vv {single} - debug level verbose mode (alias of --display-level DEBUG) - -vvv {single} - trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-config - option instead) - --no-color {single} - Produce monochrome output. alias of --theme noColor. - --theme  {single} - choose color theme - default-force means colors will be produced even if com - mand is piped - Default value: default - Possible values: default|default-force|noColor - --help, -h {single} - Display this command help - --version {single} - Print version information and quit - --quiet, -q {single} - quiet mode, doesn't display any output - --log-level  {single} - Set log level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - --log-file  {single} - Set log file - --display-level  {single} - set display level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - -NOTE: -the use of output, log-format, verbose options highly depends on the script used - -LIST OF AVAILABLE DSN: - - dsn_local - - dsn_valid - -DEFAULT QUERIES DIRECTORY: -/bash/conf/dbQueries - -USER QUERIES DIRECTORY: -home/.bash-tools/dbQueries -Allows to override queries defined in Default queries directory - -LIST OF AVAILABLE SCRIPTS: - - -EXAMPLES: script conf/dbScripts/extractData.sh - executes query databaseSize (see conf/dbQueries/databaseSize.sql) on each db and log the result in log file in default output dir, call it using - /bash/bin/dbScriptAllDatabases -j 10 extractData databaseSize - - executes query databaseSize on each db and display the result on stdout (2>/dev/null hides information messages) - /bash/bin/dbScriptAllDatabases -j 10 --log-format none extractData databaseSize - - use --verbose to get some debug information - /bash/bin/dbScriptAllDatabases -j 10 --log-format none --verbose extractData databaseSize - -USE CASES: - you can use this script in order to check that each db model conforms with your ORM schema - simply create a new script in conf/dbQueries that will call your orm schema checker - - update multiple db at once (simple to complex update script) - -VERSION: 2.0 - -AUTHOR: -[François Chastanet](https://github.com/fchastanet) - -SOURCE FILE: -https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/DbScriptAllDatabases/dbScriptAllDatabases.sh - -LICENSE: -MIT License - -Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/DbScriptAllDatabases/testsData/getUserDbList.query b/src/_binaries/DbScriptAllDatabases/testsData/getUserDbList.query deleted file mode 100644 index d889d8d2..00000000 --- a/src/_binaries/DbScriptAllDatabases/testsData/getUserDbList.query +++ /dev/null @@ -1 +0,0 @@ -SELECT `schema_name` from INFORMATION_SCHEMA.SCHEMATA WHERE `schema_name` NOT IN("information_schema", "mysql", "performance_schema", "sys") diff --git a/src/_binaries/commandDefinitions/optionsFromDsn.yaml b/src/_binaries/commandDefinitions/optionsFromDsn.yaml new file mode 100644 index 00000000..415204b8 --- /dev/null +++ b/src/_binaries/commandDefinitions/optionsFromDsn.yaml @@ -0,0 +1,16 @@ +--- +binData: + commands: + default: + optionGroups: + groupSourceDbOptions: + title: "SOURCE OPTIONS:" + options: + - variableName: optionFromDsn + group: groupSourceDbOptions + type: String + help: Target mysql server. + helpValueName: dsn + alts: + - --from-dsn + - -f diff --git a/src/_binaries/commandDefinitions/optionsJobs.sh b/src/_binaries/commandDefinitions/optionsJobs.sh index 3d16ea27..4551ee3f 100755 --- a/src/_binaries/commandDefinitions/optionsJobs.sh +++ b/src/_binaries/commandDefinitions/optionsJobs.sh @@ -1,5 +1,11 @@ #!/usr/bin/env bash +declare -a PARALLEL_OPTIONS + +optionProgressBarCallback() { + PARALLEL_OPTIONS+=(--bar) +} + optionJobsCallback() { # shellcheck disable=SC2154 if ! [[ "${optionJobs}" =~ ^[0-9]+$ ]]; then diff --git a/src/_binaries/commandDefinitions/optionsJobs.yaml b/src/_binaries/commandDefinitions/optionsJobs.yaml index b72c037b..d5352c4d 100644 --- a/src/_binaries/commandDefinitions/optionsJobs.yaml +++ b/src/_binaries/commandDefinitions/optionsJobs.yaml @@ -19,3 +19,15 @@ binData: alts: - --jobs - -j + + - variableName: optionProgressBar + group: groupJobOptions + type: Boolean + help: | + Show progress as a progress bar. In the bar is shown: % of jobs completed, + estimated seconds left, and number of jobs started. + callbacks: + - optionProgressBarCallback + alts: + - --bar + - -b diff --git a/src/_binaries/commandDefinitions/optionsProgressBar.sh b/src/_binaries/commandDefinitions/optionsProgressBar.sh deleted file mode 100755 index a576d471..00000000 --- a/src/_binaries/commandDefinitions/optionsProgressBar.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -declare -a PARALLEL_OPTIONS - -optionProgressBarCallback() { - PARALLEL_OPTIONS+=(--bar) -} diff --git a/src/_binaries/commandDefinitions/optionsProgressBar.yaml b/src/_binaries/commandDefinitions/optionsProgressBar.yaml deleted file mode 100644 index f9720872..00000000 --- a/src/_binaries/commandDefinitions/optionsProgressBar.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -binData: - commands: - default: - definitionFiles: - 27: "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsProgressBar.sh" - optionGroups: - groupProgressBarOptions: - title: "PROGRESS BAR OPTIONS:" - options: - - variableName: optionProgressBar - group: groupProgressBarOptions - type: Boolean - help: | - Show progress as a progress bar. In the bar is shown: % of jobs completed, - estimated seconds left, and number of jobs started. - callbacks: - - optionProgressBarCallback - alts: - - --bar - - -b From 2385553900f1f7033fd0f809589a208eb65b0a13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Sun, 1 Sep 2024 23:04:00 +0200 Subject: [PATCH 10/24] compiled extractData using go compiler --- conf/dbScripts/extractData | 2122 +++++++++++++---- .../binary-extractData.yaml | 27 + .../dbScriptOneDatabase.sh | 13 + .../dbScriptAllDatabases/extractData.sh | 33 + .../testsData/dbScriptAllDatabases.help.txt | 1 + .../DbScriptAllDatabases/extractData.sh | 41 - 6 files changed, 1773 insertions(+), 464 deletions(-) create mode 100644 src/_binaries/Database/dbScriptAllDatabases/binary-extractData.yaml rename src/{_includes => _binaries/Database/dbScriptAllDatabases}/dbScriptOneDatabase.sh (86%) create mode 100755 src/_binaries/Database/dbScriptAllDatabases/extractData.sh delete mode 100755 src/_binaries/DbScriptAllDatabases/extractData.sh diff --git a/conf/dbScripts/extractData b/conf/dbScripts/extractData index ffe75f11..2b208e9a 100755 --- a/conf/dbScripts/extractData +++ b/conf/dbScripts/extractData @@ -1,12 +1,12 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FACADE FROM https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/DbScriptAllDatabases/extractData.sh +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/extractData/binary-extractData.yaml # DO NOT EDIT IT # @generated ############################################################################### # shellcheck disable=SC2288,SC2034 -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/conf/dbScripts/extractData -# FACADE + + # ensure that no user aliases could interfere with # commands used in this script @@ -61,13 +61,6 @@ interruptManagement() { kill -s INT "$$" } trap interruptManagement INT -SCRIPT_NAME=${0##*/} -REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" -if [[ -n "${EMBED_CURRENT_DIR}" ]]; then - CURRENT_DIR="${EMBED_CURRENT_DIR}" -else - CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" -fi ################################################ # Temp dir management @@ -101,6 +94,20 @@ cleanOnExit() { } trap cleanOnExit EXIT HUP QUIT ABRT TERM + +SCRIPT_NAME=${0##*/} +REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" +if [[ -n "${EMBED_CURRENT_DIR}" ]]; then + CURRENT_DIR="${EMBED_CURRENT_DIR}" +else + CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" +fi +FRAMEWORK_ROOT_DIR="$(cd "${CURRENT_DIR}/../.." && pwd -P)" +FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" +FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + # @description Log namespace provides 2 kind of functions # - Log::display* allows to display given message with # given display level @@ -131,31 +138,159 @@ export __VERBOSE_LEVEL_DEBUG=2 # @description verbose level info export __VERBOSE_LEVEL_TRACE=3 -# @description Display message using info color (bg light blue/fg white) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayInfo() { - local type="${2:-INFO}" - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description concatenate each element of an array with a separator +# but wrapping text when line length is more than provided argument +# The algorithm will try not to cut the array element if it can. +# - if an arg can be placed on current line it will be, +# otherwise current line is printed and arg is added to the new +# current line +# - Empty arg is interpreted as a new line. +# - Add \r to arg in order to force break line and avoid following +# arg to be concatenated with current arg. +# +# @arg $1 glue:String +# @arg $2 maxLineLength:int +# @arg $3 indentNextLine:int +# @arg $@ array:String[] +Array::wrap2() { + local glue="${1-}" + local -i glueLength="${#glue}" + shift || true + local -i maxLineLength=$1 + shift || true + local -i indentNextLine=$1 + shift || true + local indentStr="" + if ((indentNextLine > 0)); then + indentStr="$(head -c "${indentNextLine}" 0)); do + arg="$1" + shift || true + + # replace tab by 2 spaces + arg="${arg//$'\t'/ }" + # remove trailing spaces + arg="${arg%[[:blank:]]}" + if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then + printCurrentLine + ((previousLineEmpty = 1)) + continue + else + if ((previousLineEmpty == 1)); then + printCurrentLine + fi + ((previousLineEmpty = 0)) || true + fi + # convert eol to args + mapfile -t additionalLines <<<"${arg}" + if ((${#additionalLines[@]} > 1)); then + set -- "${additionalLines[@]}" "$@" + continue + fi + + ((argLength = ${#arg})) || true + + # empty arg + if ((argLength == 0)); then + if ((isNewline == 0)); then + # isNewline = 0 means currentLine is not empty + printCurrentLine + fi + continue + fi + + if ((isNewline == 0)); then + glueLength="${#glue}" + else + glueLength="0" + fi + if ((currentLineLength + argLength + glueLength > maxLineLength)); then + if ((argLength + glueLength > maxLineLength)); then + # arg is too long to even fit on one line + # we have to split the arg on current and next line + local -i remainingLineLength + ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) + appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" + printCurrentLine + arg="${arg:${remainingLineLength}}" + # remove leading spaces + arg="${arg##[[:blank:]]}" + + set -- "${arg}" "$@" + else + # the arg can fit on next line + printCurrentLine + appendToCurrentLine "${arg}" "${argLength}" + fi + else + appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" + fi + done + if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then + printCurrentLine + fi + ) | sed -E -e 's/[[:blank:]]+$//' } -# @description Display message using debug color (gray) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayDebug() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then - Log::computeDuration - echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logDebug "$1" + +# @description check if command specified exists or return 1 +# with error and message if not +# +# @arg $1 commandName:String on which existence must be checked +# @arg $2 helpIfNotExists:String a help command to display if the command does not exist +# +# @exitcode 1 if the command specified does not exist +# @stderr diagnostic information + help if second argument is provided +Assert::commandExists() { + local commandName="$1" + local helpIfNotExists="$2" + + "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { + Log::displayError "${commandName} is not installed, please install it" + if [[ -n "${helpIfNotExists}" ]]; then + Log::displayInfo "${helpIfNotExists}" + fi + return 1 + } + return 0 } + # @description exits with message if current user is root # @noargs # @exitcode 1 if current user is root @@ -166,86 +301,144 @@ Assert::expectNonRootUser() { fi } -# @description Display message using error color (red) and exit immediately with error status 1 -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::fatal() { - Log::computeDuration - echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - Log::logFatal "$1" - exit 1 -} - -# @description create a new db instance -# Returns immediately if the instance is already initialized -# -# @arg $1 instanceNewInstance:&Map (passed by reference) database instance to use -# @arg $2 dsn:String dsn profile - load the dsn.env profile deduced using rules defined in Conf::getAbsoluteFile -# -# @example -# declare -Agx dbInstance -# Database::newInstance dbInstance "default.local" -# -# @exitcode 1 if dns file not able to loaded -Database::newInstance() { - local -n instanceNewInstance=$1 - local dsn="$2" - local DSN_FILE - if [[ -v instanceNewInstance['INITIALIZED'] && "${instanceNewInstance['INITIALIZED']:-0}" == "1" ]]; then - return +# @description check if tty (interactive mode) is active +# @noargs +# @exitcode 1 if tty not active +# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive +# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive +Assert::tty() { + if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then + return 1 fi + if [[ "${INTERACTIVE:-0}" = "1" ]]; then + return 0 + fi + tty -s +} - # final auth file generated from dns file - instanceNewInstance['AUTH_FILE']="" - instanceNewInstance['DSN_FILE']="" - # check dsn file - DSN_FILE="$(Conf::getAbsoluteFile "dsn" "${dsn}" "env")" || return 1 - Database::checkDsnFile "${DSN_FILE}" || return 1 - instanceNewInstance['DSN_FILE']="${DSN_FILE}" +# @description ignore exit code 141 from simple command pipes +# @example use with: +# local resultingStatus=0 +# local -a originalPipeStatus=() +# cmd1 | cmd2 || Bash::handlePipelineFailure resultingStatus originalPipeStatus || true +# [[ "${resultingStatus}" = "0" ]] +# @arg $1 resultingStatusCode:&int (passed by reference) (optional) resulting status code +# @arg $2 originalStatus:int[] (passed by reference) (optional) copy of original PIPESTATUS array +# @env PIPESTATUS assuming that this function is called like in the example provided +# @see https://unix.stackexchange.com/a/709880/582856 +Bash::handlePipelineFailure() { + local -a pipeStatusBackup=("${PIPESTATUS[@]}") + local -n handlePipelineFailure_resultingStatusCode=$1 + local -n handlePipelineFailure_originalStatus=$2 + # shellcheck disable=SC2034 + handlePipelineFailure_originalStatus=("${pipeStatusBackup[@]}") + handlePipelineFailure_resultingStatusCode=0 + local statusCode + for statusCode in "${pipeStatusBackup[@]}"; do + if ((statusCode == 141)); then + return 0 + elif ((statusCode > 0)); then + # shellcheck disable=SC2034 + handlePipelineFailure_resultingStatusCode="${statusCode}" + break + fi + done + return "${handlePipelineFailure_resultingStatusCode}" +} - # shellcheck source=/src/Database/testsData/dsn_valid.env - source "${instanceNewInstance['DSN_FILE']}" - instanceNewInstance['USER']="${USER}" - instanceNewInstance['PASSWORD']="${PASSWORD}" - instanceNewInstance['HOSTNAME']="${HOSTNAME}" - instanceNewInstance['PORT']="${PORT}" +# @description loads ~/.bash-tools/.env if available +# if not creates it from a default template +# else check if new options need to be added +BashTools::Conf::requireLoad() { +Linux::requireTarCommand +Compiler::Embed::extractFileFromBase64 \ + "${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" \ + "IyEvdXNyL2Jpbi9lbnYgYmFzaAojIHNoZWxsY2hlY2sgZGlzYWJsZT1TQzIwMzQKCiMgRGVmYXVsdCBzZXR0aW5ncwojIHlvdSBjYW4gb3ZlcnJpZGUgdGhlc2Ugc2V0dGluZ3MgYnkgY3JlYXRpbmcgJHtIT01FfS8uYmFzaC10b29scy8uZW52IGZpbGUKCiMjIwojIyMgRElTUExBWSBMZXZlbAojIyMgbWluaW11bSBsZXZlbCBvZiB0aGUgbWVzc2FnZXMgdGhhdCB3aWxsIGJlIGRpc3BsYXllZCBvbiBzY3JlZW4KIyMjCiMjIyAwOiBOTyBMT0cKIyMjIDE6IEVSUk9SCiMjIyAyOiBXQVJOSU5HCiMjIyAzOiBJTkZPCiMjIyA0OiBERUJVRwojIyMKQkFTSF9GUkFNRVdPUktfRElTUExBWV9MRVZFTD0ke0JBU0hfRlJBTUVXT1JLX0RJU1BMQVlfTEVWRUw6LTN9CgojIyMKIyMjIERJU1BMQVkgZHVyYXRpb24KIyMjIDA6IG5vIGR1cmF0aW9uIGlzIGRpc3BsYXllZCBvbiB0aGUgbWVzc2FnZXMKIyMjIDE6IGR1cmF0aW9uIGJldHdlZW4gcHJldmlvdXMgbWVzc2FnZSBhbmQgY3VycmVudCBpcyBkaXNwbGF5ZWQKIyMjIHdpdGggdGhlIG1lc3NhZ2UKIyMjCkRJU1BMQVlfRFVSQVRJT049JHtESVNQTEFZX0RVUkFUSU9OOjB9CgojIyMKIyMjIExvZyB0byBmaWxlCiMjIwojIyMgYWxsIGxvZyBtZXNzYWdlcyB3aWxsIGJlIHJlZGlyZWN0ZWQgdG8gbG9nIGZpbGUgc3BlY2lmaWVkCiMjIyB0aGlzIHNhbWUgcGF0aCB3aWxsIGJlIHVzZWQgaW5zaWRlIGFuZCBvdXRzaWRlIG9mIHRoZSBjb250YWluZXIKIyMjCkJBU0hfRlJBTUVXT1JLX0xPR19GSUxFPSR7QkFTSF9GUkFNRVdPUktfTE9HX0ZJTEU6LSR7RlJBTUVXT1JLX1JPT1RfRElSfS9sb2dzL2Jhc2gubG9nfQoKIyMjCiMjIyBMT0cgTGV2ZWwKIyMjIG1pbmltdW0gbGV2ZWwgb2YgdGhlIG1lc3NhZ2VzIHRoYXQgd2lsbCBiZSBsb2dnZWQgaW50byBMT0dfRklMRQojIyMKIyMjIDA6IE5PIExPRwojIyMgMTogRVJST1IKIyMjIDI6IFdBUk5JTkcKIyMjIDM6IElORk8KIyMjIDQ6IERFQlVHCiMjIwpCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw9JHtCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw6LTB9CgojIGFic29sdXRlIGRpcmVjdG9yeSBjb250YWluaW5nIGRiIGltcG9ydCBzcWwgZHVtcHMKREJfSU1QT1JUX0RVTVBfRElSPSR7REJfSU1QT1JUX0RVTVBfRElSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2RiSW1wb3J0RHVtcHN9CgojIGdhcmJhZ2UgY29sbGVjdCBhbGwgZmlsZXMgZm9yIHdoaWNoIG1vZGlmaWNhdGlvbiBpcyBncmVhdGVyIHRoYW4gZWc6IDMwIGRheXMgKCszMCkKIyBlYWNoIHRpbWUgYW4gZXhpc3RpbmcgZmlsZSBpcyB1c2VkIGJ5IGRiSW1wb3J0L2RiSW1wb3J0VGFibGUKIyB0aGUgZmlsZSBtb2RpZmljYXRpb24gdGltZSBpcyBzZXQgdG8gbm93CkRCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUz0ke0RCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUzotKzMwfQoKIyBhYnNvbHV0ZSBkaXJlY3RvcnkgY29udGFpbmluZyBkYlNjcmlwdHMgdXNlZCBieSBkYlNjcmlwdEFsbERhdGFiYXNlcwpTQ1JJUFRTX0ZPTERFUj0ke1NDUklQVFNfRk9MREVSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2NvbmYvZGJTY3JpcHRzfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEFXUyBQYXJhbWV0ZXJzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KUzNfQkFTRV9VUkw9JHtTM19CQVNFX1VSTDotfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvc3RtYW4gUGFyYW1ldGVycwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClBPU1RNQU5fQVBJX0tFWT0K" \ + "755" - # generate authFile for easy authentication - instanceNewInstance['AUTH_FILE']=$(mktemp -p "${TMPDIR:-/tmp}" -t "mysql.XXXXXXXXXXXX") - ( - echo "[client]" - echo "user = ${USER}" - echo "password = ${PASSWORD}" - echo "host = ${HOSTNAME}" - echo "port = ${PORT}" - ) >"${instanceNewInstance['AUTH_FILE']}" +declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" - # some of those values can be overridden using the dsn file - # SKIP_COLUMN_NAMES enabled by default - instanceNewInstance['SKIP_COLUMN_NAMES']="${SKIP_COLUMN_NAMES:-1}" - instanceNewInstance['SSL_OPTIONS']="${MYSQL_SSL_OPTIONS:---ssl-mode=DISABLED}" - instanceNewInstance['QUERY_OPTIONS']="${MYSQL_QUERY_OPTIONS:---batch --raw --default-character-set=utf8}" - instanceNewInstance['DUMP_OPTIONS']="${MYSQL_DUMP_OPTIONS:---default-character-set=utf8 --compress --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 ${instanceNewInstance['SSL_OPTIONS']}}" - instanceNewInstance['DB_IMPORT_OPTIONS']="${DB_IMPORT_OPTIONS:---connect-timeout=5 --batch --raw --default-character-set=utf8}" + BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" + if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then + FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" + else + # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR + FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" + fi + # shellcheck disable=SC2034 + FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" + # shellcheck disable=SC2034 + FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" - instanceNewInstance['INITIALIZED']=1 + if [[ -f "${HOME}/.bash-tools/.env" ]]; then + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") + fi + + local envFile="${HOME}/.bash-tools/.env" + if [[ ! -f "${envFile}" ]]; then + mkdir -p "${HOME}/.bash-tools" + ( + echo "#!/usr/bin/env bash" + # shellcheck disable=SC2154 + echo "${embed_file_bashToolsDefaultConfigTemplate}" + ) >"${envFile}" + Log::displayInfo "Configuration file '${envFile}' created" + else + if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then + ( + echo '# -----------------------------------------------------' + echo '# Postman Parameters' + echo '# -----------------------------------------------------' + echo 'POSTMAN_API_KEY=' + ) >>"${envFile}" + fi + fi + # shellcheck source=/conf/.env + source "${envFile}" || { + Log::displayError "impossible to load '${envFile}'" + exit 1 + } } -# @description set the general options to use on mysql command to query the database -# Differs than setOptions in the way that these options could change each time + +# @description convert base64 encoded back to target file +# if target file is executable prepend dir of target +# file to PATH to make binary available everywhere +# it is advised to include in the path of the target file +# the md5sum of the binFile # -# @arg $1 instanceSetQueryOptions:&Map (passed by reference) database instance to use -# @arg $2 optionsList:String query options list -Database::setQueryOptions() { - local -n instanceSetQueryOptions=$1 - # shellcheck disable=SC2034 - instanceSetQueryOptions['QUERY_OPTIONS']="$2" +# @arg $1 targetFile:String the file to write +# @arg $2 binFileBase64:String the base64 encoded file +# @arg $3 fileMode:String the chmod to set on the file +# @set PATH String prepend target embedded file binary directory to PATH variable if binary executable +Compiler::Embed::extractFileFromBase64() { + local targetFile="$1" + local binFileBase64="$2" + local fileMode="${3:-+x}" + local targetDir="${targetFile%/*}" + + if [[ ! -f "${targetFile}" ]]; then + if [[ ! -d "${targetDir}" ]]; then + mkdir -p "${targetDir}" + fi + base64 -d >"${targetFile}" <<<"${binFileBase64}" + chmod "${fileMode}" "${targetFile}" + fi + + if [[ -x "${targetFile}" ]]; then + Env::pathPrepend "${targetDir}" + fi } + # @description get absolute conf file from specified conf folder deduced using these rules # * from absolute file (ignores and ) # * relative to where script is executed (ignores and ) @@ -307,72 +500,310 @@ Conf::getAbsoluteFile() { return 1 } -# @description by default we skip the column names -# but sometimes we need column names to display some results -# disable this option temporarily and then restore it to true -# -# @arg $1 instanceSetQueryOptions:&Map (passed by reference) database instance to use -# @arg $2 skipColumnNames:Boolean 0 to disable, 1 to enable (hide column names) -Database::skipColumnNames() { - local -n instanceSkipColumnNames=$1 - # shellcheck disable=SC2034 - instanceSkipColumnNames['SKIP_COLUMN_NAMES']="$2" -} -# @description mysql query on a given db -# @warning could use QUERY_OPTIONS variable from dsn if defined -# @example -# cat file.sql | Database::query ... -# @arg $1 instanceQuery:&Map (passed by reference) database instance to use -# @arg $2 sqlQuery:String (optional) sql query or sql file to execute. if not provided or empty, the command can be piped -# @arg $3 dbName:String (optional) the db name +# @description check if dsn file has all the mandatory variables set +# Mandatory variables are: HOSTNAME, USER, PASSWORD, PORT # -# @exitcode mysql command status code -Database::query() { - local -n instanceQuery=$1 - local -a mysqlCommand=() - local -a queryOptions - - mysqlCommand+=(mysql) - mysqlCommand+=("--defaults-extra-file=${instanceQuery['AUTH_FILE']}") - IFS=' ' read -r -a queryOptions <<<"${instanceQuery['QUERY_OPTIONS']}" - mysqlCommand+=("${queryOptions[@]}") - if [[ "${instanceQuery['SKIP_COLUMN_NAMES']}" = "1" ]]; then - mysqlCommand+=("-s" "--skip-column-names") - fi - # add optional db name - if [[ -n "${3+x}" ]]; then - mysqlCommand+=("$3") - fi - # add optional sql query - if [[ -n "${2+x}" && -n "$2" && ! -f "$2" ]]; then - mysqlCommand+=("-e") - mysqlCommand+=("$2") - fi - Log::displayDebug "$(printf "execute command: '%s'" "${mysqlCommand[*]}")" - - if [[ -f "$2" ]]; then - "${mysqlCommand[@]}" <"$2" - else - "${mysqlCommand[@]}" +# @arg $1 dsnFileName:String dsn absolute filename +# @set HOSTNAME loaded from dsn file +# @set PORT loaded from dsn file +# @set USER loaded from dsn file +# @set PASSWORD loaded from dsn file +# @exitcode 0 on valid file +# @exitcode 1 if one of the properties of the conf file is invalid or if file not found +# @stderr log output if error found in conf file +Database::checkDsnFile() { + local dsnFileName="$1" + if [[ ! -f "${dsnFileName}" ]]; then + Log::displayError "dsn file ${dsnFileName} not found" + return 1 fi -} -# @description ensure COMMAND_BIN_DIR env var is set -# and PATH correctly prepared -# @noargs -# @set COMMAND_BIN_DIR string the directory where to find this command -# @set PATH string add directory where to find this command binary -Compiler::Facade::requireCommandBinDir() { - COMMAND_BIN_DIR="${CURRENT_DIR}" - Env::pathPrepend "${COMMAND_BIN_DIR}" -} - -declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR -FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" -LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" -LOG_LAST_LOG_DATE_INIT=1 -LOG_LAST_DURATION_STR="" + ( + unset HOSTNAME PORT PASSWORD USER + # shellcheck source=/src/Database/testsData/dsn_valid.env + source "${dsnFileName}" + if [[ -z ${HOSTNAME+x} ]]; then + Log::displayError "dsn file ${dsnFileName} : HOSTNAME not provided" + return 1 + fi + if [[ -z "${HOSTNAME}" ]]; then + Log::displayWarning "dsn file ${dsnFileName} : HOSTNAME value not provided" + fi + if [[ "${HOSTNAME}" = "localhost" ]]; then + Log::displayWarning "dsn file ${dsnFileName} : check that HOSTNAME should not be 127.0.0.1 instead of localhost" + fi + if [[ -z "${PORT+x}" ]]; then + Log::displayError "dsn file ${dsnFileName} : PORT not provided" + return 1 + fi + if ! [[ ${PORT} =~ ^[0-9]+$ ]]; then + Log::displayError "dsn file ${dsnFileName} : PORT invalid" + return 1 + fi + if [[ -z "${USER+x}" ]]; then + Log::displayError "dsn file ${dsnFileName} : USER not provided" + return 1 + fi + if [[ -z "${PASSWORD+x}" ]]; then + Log::displayError "dsn file ${dsnFileName} : PASSWORD not provided" + return 1 + fi + ) +} + + +# @description create a new db instance +# Returns immediately if the instance is already initialized +# +# @arg $1 instanceNewInstance:&Map (passed by reference) database instance to use +# @arg $2 dsn:String dsn profile - load the dsn.env profile deduced using rules defined in Conf::getAbsoluteFile +# +# @example +# declare -Agx dbInstance +# Database::newInstance dbInstance "default.local" +# +# @exitcode 1 if dns file not able to loaded +Database::newInstance() { + local -n instanceNewInstance=$1 + local dsn="$2" + local DSN_FILE + + if [[ -v instanceNewInstance['INITIALIZED'] && "${instanceNewInstance['INITIALIZED']:-0}" == "1" ]]; then + return + fi + + # final auth file generated from dns file + instanceNewInstance['AUTH_FILE']="" + instanceNewInstance['DSN_FILE']="" + + # check dsn file + DSN_FILE="$(Conf::getAbsoluteFile "dsn" "${dsn}" "env")" || return 1 + Database::checkDsnFile "${DSN_FILE}" || return 1 + instanceNewInstance['DSN_FILE']="${DSN_FILE}" + + # shellcheck source=/src/Database/testsData/dsn_valid.env + source "${instanceNewInstance['DSN_FILE']}" + + instanceNewInstance['USER']="${USER}" + instanceNewInstance['PASSWORD']="${PASSWORD}" + instanceNewInstance['HOSTNAME']="${HOSTNAME}" + instanceNewInstance['PORT']="${PORT}" + + # generate authFile for easy authentication + instanceNewInstance['AUTH_FILE']=$(mktemp -p "${TMPDIR:-/tmp}" -t "mysql.XXXXXXXXXXXX") + ( + echo "[client]" + echo "user = ${USER}" + echo "password = ${PASSWORD}" + echo "host = ${HOSTNAME}" + echo "port = ${PORT}" + ) >"${instanceNewInstance['AUTH_FILE']}" + + # some of those values can be overridden using the dsn file + # SKIP_COLUMN_NAMES enabled by default + instanceNewInstance['SKIP_COLUMN_NAMES']="${SKIP_COLUMN_NAMES:-1}" + instanceNewInstance['SSL_OPTIONS']="${MYSQL_SSL_OPTIONS:---ssl-mode=DISABLED}" + instanceNewInstance['QUERY_OPTIONS']="${MYSQL_QUERY_OPTIONS:---batch --raw --default-character-set=utf8}" + instanceNewInstance['DUMP_OPTIONS']="${MYSQL_DUMP_OPTIONS:---default-character-set=utf8 --compression-algorithms --hex-blob --routines --triggers --single-transaction --set-gtid-purged=OFF --column-statistics=0 ${instanceNewInstance['SSL_OPTIONS']}}" + instanceNewInstance['DB_IMPORT_OPTIONS']="${DB_IMPORT_OPTIONS:---connect-timeout=5 --batch --raw --default-character-set=utf8}" + + instanceNewInstance['INITIALIZED']=1 +} + + +# @description mysql query on a given db +# @warning could use QUERY_OPTIONS variable from dsn if defined +# @example +# cat file.sql | Database::query ... +# @arg $1 instanceQuery:&Map (passed by reference) database instance to use +# @arg $2 sqlQuery:String (optional) sql query or sql file to execute. if not provided or empty, the command can be piped +# @arg $3 dbName:String (optional) the db name +# +# @exitcode mysql command status code +Database::query() { + local -n instanceQuery=$1 + local -a mysqlCommand=() + local -a queryOptions + + mysqlCommand+=(mysql) + mysqlCommand+=("--defaults-extra-file=${instanceQuery['AUTH_FILE']}") + IFS=' ' read -r -a queryOptions <<<"${instanceQuery['QUERY_OPTIONS']}" + mysqlCommand+=("${queryOptions[@]}") + if [[ "${instanceQuery['SKIP_COLUMN_NAMES']}" = "1" ]]; then + mysqlCommand+=("-s" "--skip-column-names") + fi + # add optional db name + if [[ -n "${3+x}" ]]; then + mysqlCommand+=("$3") + fi + # add optional sql query + if [[ -n "${2+x}" && -n "$2" && ! -f "$2" ]]; then + mysqlCommand+=("-e") + mysqlCommand+=("$2") + fi + Log::displayDebug "$(printf "execute command: '%s'" "${mysqlCommand[*]}")" + + if [[ -f "$2" ]]; then + "${mysqlCommand[@]}" <"$2" + else + "${mysqlCommand[@]}" + fi +} + + +# @description set the general options to use on mysql command to query the database +# Differs than setOptions in the way that these options could change each time +# +# @arg $1 instanceSetQueryOptions:&Map (passed by reference) database instance to use +# @arg $2 optionsList:String query options list +Database::setQueryOptions() { + local -n instanceSetQueryOptions=$1 + # shellcheck disable=SC2034 + instanceSetQueryOptions['QUERY_OPTIONS']="$2" +} + + +# @description by default we skip the column names +# but sometimes we need column names to display some results +# disable this option temporarily and then restore it to true +# +# @arg $1 instanceSetQueryOptions:&Map (passed by reference) database instance to use +# @arg $2 skipColumnNames:Boolean 0 to disable, 1 to enable (hide column names) +Database::skipColumnNames() { + local -n instanceSkipColumnNames=$1 + # shellcheck disable=SC2034 + instanceSkipColumnNames['SKIP_COLUMN_NAMES']="$2" +} + + +# @description check if all requirements are satisfied +# to execute dbImport commands +Db::checkRequirements() { + if [[ "${SKIP_REQUIREMENTS_CHECKS:-0}" = "1" ]]; then + return 0 + fi + local -i failures=0 + echo + Assert::commandExists mysql "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqldump "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists pv "sudo apt-get install -y pv" || ((++failures)) + Assert::commandExists gawk "sudo apt-get install -y gawk" || ((++failures)) + Assert::commandExists awk "sudo apt-get install -y gawk" || ((++failures)) + Version::checkMinimal "gawk" "--version" "5.0.1" || ((++failures)) + return "${failures}" +} + + +# @description prepend directories to the PATH environment variable +# @arg $@ args:String[] list of directories to prepend +# @set PATH update PATH with the directories prepended +Env::pathPrepend() { + local arg + for arg in "$@"; do + if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then + PATH="$(realpath "${arg}"):${PATH}" + fi + done +} + + +# @description ensure env files are loaded +# @arg $@ list of default files to load at the end +# @exitcode 1 if one of env files fails to load +# @stderr diagnostics information is displayed +# shellcheck disable=SC2120 +Env::requireLoad() { + export REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 + + local -a defaultFiles=("$@") + # get list of possible config files + local -a configFiles=() + if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then + # BASH_FRAMEWORK_ENV_FILES is an array + configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") + fi + local localFrameworkConfigFile + localFrameworkConfigFile="$(pwd)/.framework-config" + if [[ -f "${localFrameworkConfigFile}" ]]; then + configFiles+=("${localFrameworkConfigFile}") + fi + if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then + configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") + fi + configFiles+=("${optionEnvFiles[@]}") + configFiles+=("${defaultFiles[@]}") + + for file in "${configFiles[@]}"; do + # shellcheck source=/.framework-config + CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { + Log::displayError "while loading config file: ${file}" + return 1 + } + done +} + + +# @description concatenate 2 paths and ensure the path is correct using realpath -m +# @arg $1 basePath:String +# @arg $2 subPath:String +File::concatenatePath() { + + if [[ "${REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Linux::requireRealpathCommand has not been loaded" + exit 1 + fi + + local basePath="$1" + local subPath="$2" + local fullPath="${basePath:+${basePath}/}${subPath}" + + realpath -m "${fullPath}" 2>/dev/null +} + + +# @description create a temp file using default TMPDIR variable +# @env TMPDIR String (default value /tmp) +# @arg $1 templateName:String template name to use(optional) +Framework::createTempFile() { + mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" +} + + +# @description ensure running user is not root +# @exitcode 1 if current user is root +# @stderr diagnostics information is displayed +Linux::requireExecutedAsUser() { + if [[ "$(id -u)" = "0" ]]; then + Log::fatal "this script should be executed as normal user" + fi +} + + +# @description ensure command realpath is available +# @exitcode 1 if realpath command not available +# @stderr diagnostics information is displayed +Linux::requireRealpathCommand() { + export REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED=1 + + Assert::commandExists realpath +} + + +# @description ensure command tar is available +# @exitcode 1 if tar command not available +# @stderr diagnostics information is displayed +Linux::requireTarCommand() { + Assert::commandExists tar +} + + +declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR +FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" +LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" +LOG_LAST_LOG_DATE_INIT=1 +LOG_LAST_DURATION_STR="" # @description compute duration since last call to this function # the result is set in following env variables. @@ -406,115 +837,106 @@ Log::computeDuration() { fi } -# @description log message to file + +# @description Display message using debug color (gray) # @arg $1 message:String the message to display -Log::logInfo() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then - Log::logMessage "${2:-INFO}" "$1" +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayDebug() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then + Log::computeDuration + echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logDebug "$1" } -# @description log message to file + +# @description Display message using error color (red) # @arg $1 message:String the message to display -Log::logDebug() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then - Log::logMessage "${2:-DEBUG}" "$1" +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayError() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then + Log::computeDuration + echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logError "$1" } -# @description log message to file + +# @description Display message using info color (bg light blue/fg white) # @arg $1 message:String the message to display -Log::logFatal() { - Log::logMessage "${2:-FATAL}" "$1" +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayInfo() { + local type="${2:-INFO}" + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logInfo "$1" "${type}" } -# @description check if dsn file has all the mandatory variables set -# Mandatory variables are: HOSTNAME, USER, PASSWORD, PORT -# -# @arg $1 dsnFileName:String dsn absolute filename -# @set HOSTNAME loaded from dsn file -# @set PORT loaded from dsn file -# @set USER loaded from dsn file -# @set PASSWORD loaded from dsn file -# @exitcode 0 on valid file -# @exitcode 1 if one of the properties of the conf file is invalid or if file not found -# @stderr log output if error found in conf file -Database::checkDsnFile() { - local dsnFileName="$1" - if [[ ! -f "${dsnFileName}" ]]; then - Log::displayError "dsn file ${dsnFileName} not found" - return 1 + +# @description Display message using warning color (yellow) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayWarning() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then + Log::computeDuration + echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logWarning "$1" +} - ( - unset HOSTNAME PORT PASSWORD USER - # shellcheck source=/src/Database/testsData/dsn_valid.env - source "${dsnFileName}" - if [[ -z ${HOSTNAME+x} ]]; then - Log::displayError "dsn file ${dsnFileName} : HOSTNAME not provided" - return 1 - fi - if [[ -z "${HOSTNAME}" ]]; then - Log::displayWarning "dsn file ${dsnFileName} : HOSTNAME value not provided" - fi - if [[ "${HOSTNAME}" = "localhost" ]]; then - Log::displayWarning "dsn file ${dsnFileName} : check that HOSTNAME should not be 127.0.0.1 instead of localhost" - fi - if [[ -z "${PORT+x}" ]]; then - Log::displayError "dsn file ${dsnFileName} : PORT not provided" - return 1 - fi - if ! [[ ${PORT} =~ ^[0-9]+$ ]]; then - Log::displayError "dsn file ${dsnFileName} : PORT invalid" - return 1 - fi - if [[ -z "${USER+x}" ]]; then - Log::displayError "dsn file ${dsnFileName} : USER not provided" - return 1 - fi - if [[ -z "${PASSWORD+x}" ]]; then - Log::displayError "dsn file ${dsnFileName} : PASSWORD not provided" - return 1 - fi - ) + +# @description Display message using error color (red) and exit immediately with error status 1 +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::fatal() { + Log::computeDuration + echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + Log::logFatal "$1" + exit 1 } -# @description concatenate 2 paths and ensure the path is correct using realpath -m -# @arg $1 basePath:String -# @arg $2 subPath:String -# @require Linux::requireRealpathCommand -File::concatenatePath() { - local basePath="$1" - local subPath="$2" - local fullPath="${basePath:+${basePath}/}${subPath}" - realpath -m "${fullPath}" 2>/dev/null +# @description log message to file +# @arg $1 message:String the message to display +Log::logDebug() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then + Log::logMessage "${2:-DEBUG}" "$1" + fi } -# @description Display message using error color (red) + +# @description log message to file # @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayError() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then - Log::computeDuration - echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 +Log::logError() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_ERROR)); then + Log::logMessage "${2:-ERROR}" "$1" fi - Log::logError "$1" } -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" - fi - done + +# @description log message to file +# @arg $1 message:String the message to display +Log::logFatal() { + Log::logMessage "${2:-FATAL}" "$1" +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logInfo() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then + Log::logMessage "${2:-INFO}" "$1" + fi } + # @description Internal: common log message # @example text # [date]|[levelMsg]|message @@ -528,9 +950,18 @@ Env::pathPrepend() { # @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty # @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages # @stderr diagnostics information is displayed -# @require Env::requireLoad -# @require Log::requireLoad Log::logMessage() { + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Log::requireLoad has not been loaded" + exit 1 + fi + local levelMsg="$1" local msg="$2" local date @@ -542,32 +973,6 @@ Log::logMessage() { fi } -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logWarning "$1" -} - -# @description log message to file -# @arg $1 message:String the message to display -Log::logError() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_ERROR)); then - Log::logMessage "${2:-ERROR}" "$1" - fi -} - -# @description ensure command realpath is available -# @exitcode 1 if realpath command not available -# @stderr diagnostics information is displayed -Linux::requireRealpathCommand() { - Assert::commandExists realpath -} # @description log message to file # @arg $1 message:String the message to display @@ -577,60 +982,6 @@ Log::logWarning() { fi } -# @description check if command specified exists or return 1 -# with error and message if not -# -# @arg $1 commandName:String on which existence must be checked -# @arg $2 helpIfNotExists:String a help command to display if the command does not exist -# -# @exitcode 1 if the command specified does not exist -# @stderr diagnostic information + help if second argument is provided -Assert::commandExists() { - local commandName="$1" - local helpIfNotExists="$2" - - "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { - Log::displayError "${commandName} is not installed, please install it" - if [[ -n "${helpIfNotExists}" ]]; then - Log::displayInfo "${helpIfNotExists}" - fi - return 1 - } - return 0 -} - -# @description ensure env files are loaded -# @arg $@ list of default files to load at the end -# @exitcode 1 if one of env files fails to load -# @stderr diagnostics information is displayed -# shellcheck disable=SC2120 -Env::requireLoad() { - local -a defaultFiles=("$@") - # get list of possible config files - local -a configFiles=() - if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then - # BASH_FRAMEWORK_ENV_FILES is an array - configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") - fi - local localFrameworkConfigFile - localFrameworkConfigFile="$(pwd)/.framework-config" - if [[ -f "${localFrameworkConfigFile}" ]]; then - configFiles+=("${localFrameworkConfigFile}") - fi - if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then - configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") - fi - configFiles+=("${optionEnvFiles[@]}") - configFiles+=("${defaultFiles[@]}") - - for file in "${configFiles[@]}"; do - # shellcheck source=/.framework-config - CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { - Log::displayError "while loading config file: ${file}" - return 1 - } - done -} # @description activate or not Log::display* and Log::log* functions # based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL @@ -644,9 +995,20 @@ Env::requireLoad() { # @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 # @exitcode 0 always successful # @stderr diagnostics information about log file is displayed -# @require Env::requireLoad -# @require UI::requireTheme Log::requireLoad() { + export REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement UI::requireTheme has not been loaded" + exit 1 + fi + if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} export BASH_FRAMEWORK_LOG_LEVEL @@ -678,6 +1040,7 @@ Log::requireLoad() { fi } + # @description To be called before logging in the log file # @arg $1 file:string log file name # @arg $2 maxLogFilesCount:int maximum number of log files @@ -700,17 +1063,37 @@ Log::rotate() { fi } + +# @description draw a line with the character passed in parameter repeated depending on terminal width +# @arg $1 character:String character to use as separator (default value #) +UI::drawLine() { + local character="${1:-#}" + local -i width=${COLUMNS:-0} + if ((width == 0)) && [[ -t 1 ]]; then + width=$(tput cols) + fi + if ((width == 0)); then + width=80 + fi + printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") + echo +} + + # @description load color theme # @noargs # @env BASH_FRAMEWORK_THEME String theme to use # @env LOAD_THEME int 0 to avoid loading theme # @exitcode 0 always successful UI::requireTheme() { + export REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + if [[ "${LOAD_THEME:-1}" = "1" ]]; then UI::theme "${BASH_FRAMEWORK_THEME-default}" fi } + # @description load colors theme constants # @warning if tty not opened, noColor theme will be chosen # @arg $1 theme:String the theme to use (default, noColor) @@ -738,86 +1121,427 @@ UI::theme() { if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then theme="noColor" fi - case "${theme}" in - default | default-force) - theme="default" + case "${theme}" in + default | default-force) + theme="default" + ;; + noColor) ;; + *) + Log::fatal "invalid theme provided" + ;; + esac + if [[ "${theme}" = "default" ]]; then + BASH_FRAMEWORK_THEME="default" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='\e[31m' # Red + __INFO_COLOR='\e[44m' # white on lightBlue + __SUCCESS_COLOR='\e[32m' # Green + __WARNING_COLOR='\e[33m' # Yellow + __SKIPPED_COLOR='\e[33m' # Yellow + __DEBUG_COLOR='\e[37m' # Gray + __HELP_COLOR='\e[7;49;33m' # Black on Gold + __TEST_COLOR='\e[100m' # Light magenta + __TEST_ERROR_COLOR='\e[41m' # white on red + __HELP_TITLE_COLOR="\e[1;37m" # Bold + __HELP_OPTION_COLOR="\e[1;34m" # Blue + # Internal: reset color + __RESET_COLOR='\e[0m' # Reset Color + # shellcheck disable=SC2155,SC2034 + __HELP_EXAMPLE="$(echo -e "\e[2;97m")" + # shellcheck disable=SC2155,SC2034 + __HELP_TITLE="$(echo -e "\e[1;37m")" + # shellcheck disable=SC2155,SC2034 + __HELP_NORMAL="$(echo -e "\033[0m")" + else + BASH_FRAMEWORK_THEME="noColor" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='' + __INFO_COLOR='' + __SUCCESS_COLOR='' + __WARNING_COLOR='' + __SKIPPED_COLOR='' + __DEBUG_COLOR='' + __HELP_COLOR='' + __TEST_COLOR='' + __TEST_ERROR_COLOR='' + __HELP_TITLE_COLOR='' + __HELP_OPTION_COLOR='' + # Internal: reset color + __RESET_COLOR='' + __HELP_EXAMPLE='' + __HELP_TITLE='' + __HELP_NORMAL='' + fi +} + + +# @description Check that command version is greater than expected minimal version +# display warning if command version greater than expected minimal version +# display error if command version less than expected minimal version and exit 1 +# @arg $1 commandName:String command path +# @arg $2 argVersion:String command line parameters to launch to get command version +# @arg $3 minimalVersion:String expected minimal command version +# @arg $4 parseVersionCallback:Function +# @arg $5 help:String optional help message to display if command does not exist +# @exitcode 0 if command version greater or equal to expected minimal version +# @exitcode 1 if command version less than expected minimal version +# @exitcode 2 if command does not exist +Version::checkMinimal() { + local commandName="$1" + local argVersion="$2" + local minimalVersion="$3" + local parseVersionCallback=${4:-Version::parse} + local help="${5:-}" + + Assert::commandExists "${commandName}" "${help}" || return 2 + + # shellcheck disable=SC2034 + local status=0 + # shellcheck disable=SC2034 + local -a pipeStatus=() + local version + version="$("${commandName}" "${argVersion}" 2>&1 | ${parseVersionCallback} || Bash::handlePipelineFailure status pipeStatus)" + + Log::displayDebug "check ${commandName} version ${version} against minimal ${minimalVersion}" + + Version::compare "${version}" "${minimalVersion}" || { + local result=$? + if [[ "${result}" = "1" ]]; then + Log::displayInfo "${commandName} version is ${version} greater than ${minimalVersion}" + elif [[ "${result}" = "2" ]]; then + Log::displayError "${commandName} minimal version is ${minimalVersion}, your version is ${version}" + return 1 + fi + return 0 + } + +} + + +# @description compare 2 version numbers +# @arg $1 version1:String version 1 +# @arg $2 version2:String version 2 +# @exitcode 0 if equal +# @exitcode 1 if version1 > version2 +# @exitcode 2 else +Version::compare() { + if [[ "$1" = "$2" ]]; then + return 0 + fi + local IFS=. + # shellcheck disable=2206 + local i ver1=($1) ver2=($2) + # fill empty fields in ver1 with zeros + for ((i = ${#ver1[@]}; i < ${#ver2[@]}; i++)); do + ver1[i]=0 + done + for ((i = 0; i < ${#ver1[@]}; i++)); do + if [[ -z "${ver2[i]+unset}" ]] || [[ -z ${ver2[i]} ]]; then + # fill empty fields in ver2 with zeros + ver2[i]=0 + fi + if ((10#${ver1[i]} > 10#${ver2[i]})); then + return 1 + fi + if ((10#${ver1[i]} < 10#${ver2[i]})); then + return 2 + fi + done + return 0 +} + + +# @description filter to keep only version number from a string +# @arg $@ files:String[] the files to filter +# @exitcode * if one of the filter command fails +# @stdin you can use stdin as alternative to files argument +# @stdout the filtered content +# shellcheck disable=SC2120 +Version::parse() { + # match anything, print(p), exit on first match(Q) + sed -En \ + -e 's/\x1b\[[0-9;]*[mGKHF]//g' \ + -e 's/[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/' \ + -e '//{p;Q}' \ + "$@" +} +# FUNCTIONS + + +declare -a BASH_FRAMEWORK_ARGV_FILTERED=() + +beforeParseCallback() { + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + +copyrightCallback() { + if [[ -z "${copyrightBeginYear}" ]]; then + copyrightBeginYear="$(date +%Y)" + fi + echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" +} + +# shellcheck disable=SC2317 # if function is overridden +updateArgListInfoVerboseCallback() { + BASH_FRAMEWORK_ARGV_FILTERED+=(--verbose) +} +# shellcheck disable=SC2317 # if function is overridden +updateArgListDebugVerboseCallback() { + BASH_FRAMEWORK_ARGV_FILTERED+=(-vv) +} +# shellcheck disable=SC2317 # if function is overridden +updateArgListTraceVerboseCallback() { + BASH_FRAMEWORK_ARGV_FILTERED+=(-vvv) +} +# shellcheck disable=SC2317 # if function is overridden +updateArgListEnvFileCallback() { :; } +# shellcheck disable=SC2317 # if function is overridden +updateArgListLogLevelCallback() { :; } +# shellcheck disable=SC2317 # if function is overridden +updateArgListDisplayLevelCallback() { :; } +# shellcheck disable=SC2317 # if function is overridden +updateArgListNoColorCallback() { + BASH_FRAMEWORK_ARGV_FILTERED+=(--no-color) +} +# shellcheck disable=SC2317 # if function is overridden +updateArgListThemeCallback() { :; } +# shellcheck disable=SC2317 # if function is overridden +updateArgListQuietCallback() { :; } + +# shellcheck disable=SC2317 # if function is overridden +optionHelpCallback() { + Log::displayError "optionHelpCallback needs to be overridden" + exit 0 +} + +# shellcheck disable=SC2317 # if function is overridden +optionVersionCallback() { + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version ${versionNumber}" + exit 0 +} + +# shellcheck disable=SC2317 # if function is overridden +optionEnvFileCallback() { + local envFile="$2" + Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" + if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" + exit 1 + fi +} + +# shellcheck disable=SC2317 # if function is overridden +optionInfoVerboseCallback() { + BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' + BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_INFO} + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionDebugVerboseCallback() { + BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vv' + BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_DEBUG} + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionTraceVerboseCallback() { + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vvv' + BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_TRACE} + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" +} + +getLevel() { + local levelName="$1" + case "${levelName^^}" in + OFF) + echo "${__LEVEL_OFF}" + ;; + ERR | ERROR) + echo "${__LEVEL_ERROR}" + ;; + WARN | WARNING) + echo "${__LEVEL_WARNING}" + ;; + INFO) + echo "${__LEVEL_INFO}" + ;; + DEBUG | TRACE) + echo "${__LEVEL_DEBUG}" ;; - noColor) ;; *) - Log::fatal "invalid theme provided" + Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" + return 1 ;; esac - if [[ "${theme}" = "default" ]]; then - BASH_FRAMEWORK_THEME="default" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='\e[31m' # Red - __INFO_COLOR='\e[44m' # white on lightBlue - __SUCCESS_COLOR='\e[32m' # Green - __WARNING_COLOR='\e[33m' # Yellow - __SKIPPED_COLOR='\e[33m' # Yellow - __DEBUG_COLOR='\e[37m' # Gray - __HELP_COLOR='\e[7;49;33m' # Black on Gold - __TEST_COLOR='\e[100m' # Light magenta - __TEST_ERROR_COLOR='\e[41m' # white on red - __HELP_TITLE_COLOR="\e[1;37m" # Bold - __HELP_OPTION_COLOR="\e[1;34m" # Blue - # Internal: reset color - __RESET_COLOR='\e[0m' # Reset Color - # shellcheck disable=SC2155,SC2034 - __HELP_EXAMPLE="$(echo -e "\e[2;97m")" - # shellcheck disable=SC2155,SC2034 - __HELP_TITLE="$(echo -e "\e[1;37m")" - # shellcheck disable=SC2155,SC2034 - __HELP_NORMAL="$(echo -e "\033[0m")" - else - BASH_FRAMEWORK_THEME="noColor" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='' - __INFO_COLOR='' - __SUCCESS_COLOR='' - __WARNING_COLOR='' - __SKIPPED_COLOR='' - __DEBUG_COLOR='' - __HELP_COLOR='' - __TEST_COLOR='' - __TEST_ERROR_COLOR='' - __HELP_TITLE_COLOR='' - __HELP_OPTION_COLOR='' - # Internal: reset color - __RESET_COLOR='' - __HELP_EXAMPLE='' - __HELP_TITLE='' - __HELP_NORMAL='' +} + +getVerboseLevel() { + local levelName="$1" + case "${levelName^^}" in + OFF) + echo "${__VERBOSE_LEVEL_OFF}" + ;; + ERR | ERROR | WARN | WARNING | INFO) + echo "${__VERBOSE_LEVEL_INFO}" + ;; + DEBUG) + echo "${__VERBOSE_LEVEL_DEBUG}" + ;; + TRACE) + echo "${__VERBOSE_LEVEL_TRACE}" + ;; + *) + Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" + return 1 + ;; + esac +} + +# shellcheck disable=SC2317 # if function is overridden +optionDisplayLevelCallback() { + local level="$2" + local logLevel verboseLevel + logLevel="$(getLevel "${level}")" + verboseLevel="$(getVerboseLevel "${level}")" + BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionLogLevelCallback() { + local level="$2" + local logLevel verboseLevel + logLevel="$(getLevel "${level}")" + verboseLevel="$(getVerboseLevel "${level}")" + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} + echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionLogFileCallback() { + local logFile="$2" + echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionQuietCallback() { + echo "BASH_FRAMEWORK_QUIET_MODE=1" >>"${overrideEnvFile}" +} + +# shellcheck disable=SC2317 # if function is overridden +optionNoColorCallback() { + UI::theme "noColor" +} + +# shellcheck disable=SC2317 # if function is overridden +optionThemeCallback() { + UI::theme "$2" +} + +displayConfig() { + echo "Config" + UI::drawLine "-" + local var + while read -r var; do + printf '%-40s = %s\n' "${var}" "$(declare -p "${var}" | sed -E -e 's/^[^=]+=(.*)/\1/')" + done < <(typeset -p | awk 'match($3, "^(BASH_FRAMEWORK_[^=]+)=", m) { print m[1] }' | sort) + exit 0 +} + +optionBashFrameworkConfigCallback() { + if [[ ! -f "$2" ]]; then + Log::fatal "Command ${SCRIPT_NAME} - Bash framework config file '$2' does not exists" fi } -# @description check if tty (interactive mode) is active -# @noargs -# @exitcode 1 if tty not active -# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive -# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive -Assert::tty() { - if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then - return 1 +defaultFrameworkConfig="$( + cat <<'EOF' + +# copied from src/_includes/.framework-config.default +# shellcheck disable=SC2034 + +REAL_SCRIPT_FILE="${REAL_SCRIPT_FILE:-$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")}" +FRAMEWORK_ROOT_DIR="${FRAMEWORK_ROOT_DIR:-${REAL_SCRIPT_FILE%/*/*}}" +FRAMEWORK_SRC_DIR="${FRAMEWORK_SRC_DIR:-${FRAMEWORK_ROOT_DIR}/src}" +FRAMEWORK_BIN_DIR="${FRAMEWORK_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/bin}" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_VENDOR_DIR:-${FRAMEWORK_ROOT_DIR}/vendor}" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_VENDOR_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/vendor/bin}" + +# describe the functions that will be skipped from being imported +FRAMEWORK_FUNCTIONS_IGNORE_REGEXP="${FRAMEWORK_FUNCTIONS_IGNORE_REGEXP:-^(Namespace::functions|Functions::myFunction|Namespace::requireSomething|Acquire::ForceIPv4)$}" +# describe the files that do not contain function to be imported +NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/_binaries|^src/_includes|^src/batsHeaders.sh$|^src/_standalone)}" +# describe the files that are allowed to not have an associated bats file +BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" +# describe the files that are allowed to not have a function matching the filename +FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|/testsData/|^manualTests/|\.bats$}" +# Source directories +if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then + FRAMEWORK_SRC_DIRS=( + "${FRAMEWORK_ROOT_DIR}/src" + ) +fi + +# export here all the variables that will be used in your templates +export REPOSITORY_URL="${REPOSITORY_URL:-https://github.com/fchastanet/bash-tools-framework}" + +BASH_FRAMEWORK_THEME="${BASH_FRAMEWORK_THEME:-default}" +BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" +BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" +BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" +BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" + +EOF +)" + +overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" + +commandOptionParseFinished() { + # load default template framework config + defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" + echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" + local -a files=("${defaultEnvFile}") + if [[ -f "${envFile}" ]]; then + files+=("${envFile}") fi - if [[ "${INTERACTIVE:-0}" = "1" ]]; then - return 0 + # shellcheck disable=SC2154 + if [[ -f "${optionBashFrameworkConfig}" ]]; then + files+=("${optionBashFrameworkConfig}") + fi + files+=("${overrideEnvFile}") + Env::requireLoad "${files[@]}" + Log::requireLoad + # shellcheck disable=SC2154 + if [[ "${optionConfig}" = "1" ]]; then + displayConfig fi - tty -s } -# FUNCTIONS -facade_main_extractDatash() { -# REQUIRES -Linux::requireRealpathCommand -Env::requireLoad -Log::requireLoad -UI::requireTheme -Compiler::Facade::requireCommandBinDir -# @require Compiler::Facade::requireCommandBinDir -# shellcheck disable=SC2154 +beforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + + + +optionVersionCallback() { + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version ${versionNumber}" + Db::checkRequirements + exit 0 +} + + ############################################################ # INTERNAL USE ONLY @@ -858,41 +1582,593 @@ init() { Database::setQueryOptions dbInstance "${dbInstance[QUERY_OPTIONS]} --connect-timeout=5" } -run() { - # extra parameters passed through dbScriptAllDatabases - declare query="${scriptParameters[0]}" - declare queryName="customQuery" - declare queryFile="${query}" - queryFile="$(Conf::getAbsoluteFile "dbQueries" "${queryFile}" "sql" 2>/dev/null || echo "")" - if [[ -n "${queryFile}" ]]; then - queryName="$(basename "${queryFile%.*}")" - query="$(cat "${queryFile}")" - fi +unknownArg() { + : +} - # create log file - declare logFile="" - if [[ "${LOG_FORMAT}" = "log" ]]; then - declare logFile="${outputDir}/${db}_${queryName}.log" - exec 6>&1 1>"${logFile}" # redirect stdout to logFile - fi +beforeParseCallback() { + Env::requireLoad + UI::requireTheme + Log::requireLoad + Linux::requireExecutedAsUser + Linux::requireRealpathCommand + init +} - Database::skipColumnNames dbInstance 0 - Database::query dbInstance "${query}" "${db}" || true - Database::skipColumnNames dbInstance 1 - if [[ "${LOG_FORMAT}" = "log" ]]; then - # restore stdout - exec 1>&6 6>&- - fi +# ------------------------------------------ +# Command extractDataCommand +# ------------------------------------------ + +# options variables initialization +declare optionHelp="0" +declare optionConfig="0" +declare optionBashFrameworkConfig="" +declare optionInfoVerbose="0" +declare optionDebugVerbose="0" +declare optionTraceVerbose="0" +declare -a optionEnvFiles=() +declare optionLogLevel="" +declare optionLogFile="" +declare optionDisplayLevel="" +declare optionNoColor="0" +declare optionTheme="default" +declare optionVersion="0" +declare optionQuiet="0" +# arguments variables initialization +# @description parse command options and arguments for extractDataCommand +extractDataCommandParse() { + Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" + Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" + optionHelp="0" + local -i options_parse_optionParsedCountOptionHelp + ((options_parse_optionParsedCountOptionHelp = 0)) || true + optionConfig="0" + local -i options_parse_optionParsedCountOptionConfig + ((options_parse_optionParsedCountOptionConfig = 0)) || true + optionBashFrameworkConfig="" + local -i options_parse_optionParsedCountOptionBashFrameworkConfig + ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true + optionInfoVerbose="0" + local -i options_parse_optionParsedCountOptionInfoVerbose + ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true + optionDebugVerbose="0" + local -i options_parse_optionParsedCountOptionDebugVerbose + ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true + optionTraceVerbose="0" + local -i options_parse_optionParsedCountOptionTraceVerbose + ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true + + optionLogLevel="" + local -i options_parse_optionParsedCountOptionLogLevel + ((options_parse_optionParsedCountOptionLogLevel = 0)) || true + optionLogFile="" + local -i options_parse_optionParsedCountOptionLogFile + ((options_parse_optionParsedCountOptionLogFile = 0)) || true + optionDisplayLevel="" + local -i options_parse_optionParsedCountOptionDisplayLevel + ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true + optionNoColor="0" + local -i options_parse_optionParsedCountOptionNoColor + ((options_parse_optionParsedCountOptionNoColor = 0)) || true + optionTheme="default" + local -i options_parse_optionParsedCountOptionTheme + ((options_parse_optionParsedCountOptionTheme = 0)) || true + optionVersion="0" + local -i options_parse_optionParsedCountOptionVersion + ((options_parse_optionParsedCountOptionVersion = 0)) || true + optionQuiet="0" + local -i options_parse_optionParsedCountOptionQuiet + ((options_parse_optionParsedCountOptionQuiet = 0)) || true - if [[ "${LOG_FORMAT}" = "log" ]]; then - Log::displayInfo "result available in '${logFile}'" - fi + + # shellcheck disable=SC2034 + local -i options_parse_parsedArgIndex=0 + while (($# > 0)); do + local options_parse_arg="$1" + local argOptDefaultBehavior=0 + case "${options_parse_arg}" in + # Option 1/14 + # optionHelp alts --help|-h + # type: Boolean min 0 max 1 + --help | -h) + # shellcheck disable=SC2034 + optionHelp="1" + + if ((options_parse_optionParsedCountOptionHelp >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionHelp)) + optionHelpCallback "${options_parse_arg}" "${optionHelp}" + + ;; + + # Option 2/14 + # optionConfig alts --config + # type: Boolean min 0 max 1 + --config) + # shellcheck disable=SC2034 + optionConfig="1" + + if ((options_parse_optionParsedCountOptionConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionConfig)) + ;; + + # Option 3/14 + # optionBashFrameworkConfig alts --bash-framework-config + # type: String min 0 max 1 + --bash-framework-config) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) + # shellcheck disable=SC2034 + optionBashFrameworkConfig="$1" + optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" + + ;; + + # Option 4/14 + # optionInfoVerbose alts --verbose|-v + # type: Boolean min 0 max 1 + --verbose | -v) + # shellcheck disable=SC2034 + optionInfoVerbose="1" + + if ((options_parse_optionParsedCountOptionInfoVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionInfoVerbose)) + optionInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + updateArgListInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + ;; + + # Option 5/14 + # optionDebugVerbose alts -vv + # type: Boolean min 0 max 1 + -vv) + # shellcheck disable=SC2034 + optionDebugVerbose="1" + + if ((options_parse_optionParsedCountOptionDebugVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDebugVerbose)) + optionDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + updateArgListDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + ;; + + # Option 6/14 + # optionTraceVerbose alts -vvv + # type: Boolean min 0 max 1 + -vvv) + # shellcheck disable=SC2034 + optionTraceVerbose="1" + + if ((options_parse_optionParsedCountOptionTraceVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTraceVerbose)) + optionTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + updateArgListTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + ;; + + # Option 7/14 + # optionEnvFiles alts --env-file + # type: StringArray min 0 max -1 + --env-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + ((++options_parse_optionParsedCountOptionEnvFiles)) + optionEnvFiles+=("$1") + optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + ;; + + # Option 8/14 + # optionLogLevel alts --log-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --log-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogLevel)) + # shellcheck disable=SC2034 + optionLogLevel="$1" + optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + ;; + + # Option 9/14 + # optionLogFile alts --log-file + # type: String min 0 max 1 + --log-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogFile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogFile)) + # shellcheck disable=SC2034 + optionLogFile="$1" + optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + ;; + + # Option 10/14 + # optionDisplayLevel alts --display-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --display-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionDisplayLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDisplayLevel)) + # shellcheck disable=SC2034 + optionDisplayLevel="$1" + optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + ;; + + # Option 11/14 + # optionNoColor alts --no-color + # type: Boolean min 0 max 1 + --no-color) + # shellcheck disable=SC2034 + optionNoColor="1" + + if ((options_parse_optionParsedCountOptionNoColor >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionNoColor)) + optionNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + updateArgListNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + ;; + + # Option 12/14 + # optionTheme alts --theme + # type: String min 0 max 1 + # authorizedValues: default|default-force|noColor + --theme) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ default|default-force|noColor ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionTheme >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTheme)) + # shellcheck disable=SC2034 + optionTheme="$1" + optionThemeCallback "${options_parse_arg}" "${optionTheme}" + + updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" + + ;; + + # Option 13/14 + # optionVersion alts --version + # type: Boolean min 0 max 1 + --version) + # shellcheck disable=SC2034 + optionVersion="1" + + if ((options_parse_optionParsedCountOptionVersion >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionVersion)) + optionVersionCallback "${options_parse_arg}" "${optionVersion}" + + ;; + + # Option 14/14 + # optionQuiet alts --quiet|-q + # type: Boolean min 0 max 1 + --quiet | -q) + # shellcheck disable=SC2034 + optionQuiet="1" + + if ((options_parse_optionParsedCountOptionQuiet >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionQuiet)) + optionQuietCallback "${options_parse_arg}" "${optionQuiet}" + + updateArgListQuietCallback "${options_parse_arg}" "${optionQuiet}" + + ;; + + -*) + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" + return 1 + fi + ;; + *) + # no arg configured, call unknownArgumentCallback + + # shellcheck disable=SC2317 + unknownArg "${options_parse_arg}" + + + if ((incrementArg == 1)); then + ((++options_parse_parsedArgIndex)) + fi + ;; + esac + shift || true + done || return $? + + commandOptionParseFinished + +} + +# @description display command options and arguments help for extractDataCommand +extractDataCommandHelp() { + echo -e "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" + Array::wrap2 ' ' 76 4 " " "" + + echo + echo + + # ------------------------------------------ + # usage section + # ------------------------------------------ + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "extractData [OPTIONS] " + echo + # ------------------------------------------ + # usage/options section + # ------------------------------------------ + optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" + ) + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ + "extractData" "${optionsAltList[@]}" + echo + + # ------------------------------------------ + # options section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays this command help" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays configuration" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" + Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log file" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set display level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." + echo + Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" + echo + + Array::wrap2 ' ' 76 6 " Default value: " "default" + echo + + echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Print version information and quit." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." + echo + + + # ------------------------------------------ + # version section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" + echo "2.0" + # ------------------------------------------ + # author section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}AUTHOR: ${__RESET_COLOR}" + echo "[François Chastanet](https://github.com/fchastanet)" + # ------------------------------------------ + # sourceFile section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/extractData/binary-extractData.yaml" + # ------------------------------------------ + # license section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}LICENSE: ${__RESET_COLOR}" + echo "MIT License" + # ------------------------------------------ + # copyright section + # ------------------------------------------ + Array::wrap2 ' ' 76 0 "$(copyrightCallback)" } -init -run + +beforeParseCallback + +extractDataCommandParse "$@" +MAIN_FUNCTION_NAME="main" +main() { + +# extra parameters passed through dbScriptAllDatabases +# shellcheck disable=SC2154 +declare query="${scriptParameters[0]}" +declare queryName="customQuery" +declare queryFile="${query}" +queryFile="$(Conf::getAbsoluteFile "dbQueries" "${queryFile}" "sql" 2>/dev/null || echo "")" +if [[ -n "${queryFile}" ]]; then + queryName="$(basename "${queryFile%.*}")" + query="$(cat "${queryFile}")" +fi + +# create log file +declare logFile="" +if [[ "${LOG_FORMAT}" = "log" ]]; then + # shellcheck disable=SC2154 + declare logFile="${outputDir}/${db}_${queryName}.log" + exec 6>&1 1>"${logFile}" # redirect stdout to logFile +fi + +Database::skipColumnNames dbInstance 0 +Database::query dbInstance "${query}" "${db}" || true +Database::skipColumnNames dbInstance 1 + +if [[ "${LOG_FORMAT}" = "log" ]]; then + # restore stdout + exec 1>&6 6>&- +fi + +if [[ "${LOG_FORMAT}" = "log" ]]; then + Log::displayInfo "result available in '${logFile}'" +fi } -facade_main_extractDatash "$@" +# if file is sourced avoid calling main function +# shellcheck disable=SC2178 +BASH_SOURCE=".$0" # cannot be changed in bash +# shellcheck disable=SC2128 +if test ".$0" == ".${BASH_SOURCE}"; then + if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then + main "$@" &>/dev/null + else + main "$@" + fi +fi diff --git a/src/_binaries/Database/dbScriptAllDatabases/binary-extractData.yaml b/src/_binaries/Database/dbScriptAllDatabases/binary-extractData.yaml new file mode 100644 index 00000000..3318b9d6 --- /dev/null +++ b/src/_binaries/Database/dbScriptAllDatabases/binary-extractData.yaml @@ -0,0 +1,27 @@ +extends: + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" + +vars: + SRC_FILE_PATH: src/_binaries/Database/extractData/binary-extractData.yaml + +compilerConfig: + targetFile: "${BASH_TOOLS_ROOT_DIR}/conf/dbScripts/extractData" + relativeRootDirBasedOnTargetDir: ../.. + srcDirs: + - ${BASH_TOOLS_ROOT_DIR}/src +binData: + commands: + default: + functionName: extractDataCommand + version: "2.0" + commandName: extractData + beforeParseCallbacks: + - beforeParseCallback + unknownArgumentCallbacks: + - unknownArg + definitionFiles: + 11: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.sh + 99: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbScriptAllDatabases/dbScriptOneDatabase.sh + mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbScriptAllDatabases/extractData.sh diff --git a/src/_includes/dbScriptOneDatabase.sh b/src/_binaries/Database/dbScriptAllDatabases/dbScriptOneDatabase.sh similarity index 86% rename from src/_includes/dbScriptOneDatabase.sh rename to src/_binaries/Database/dbScriptAllDatabases/dbScriptOneDatabase.sh index c378d6a5..9e0b7b86 100755 --- a/src/_includes/dbScriptOneDatabase.sh +++ b/src/_binaries/Database/dbScriptAllDatabases/dbScriptOneDatabase.sh @@ -38,3 +38,16 @@ init() { Database::newInstance dbInstance "${DSN}" Database::setQueryOptions dbInstance "${dbInstance[QUERY_OPTIONS]} --connect-timeout=5" } + +unknownArg() { + : +} + +beforeParseCallback() { + Env::requireLoad + UI::requireTheme + Log::requireLoad + Linux::requireExecutedAsUser + Linux::requireRealpathCommand + init +} diff --git a/src/_binaries/Database/dbScriptAllDatabases/extractData.sh b/src/_binaries/Database/dbScriptAllDatabases/extractData.sh new file mode 100755 index 00000000..5be2ceea --- /dev/null +++ b/src/_binaries/Database/dbScriptAllDatabases/extractData.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +# extra parameters passed through dbScriptAllDatabases +# shellcheck disable=SC2154 +declare query="${scriptParameters[0]}" +declare queryName="customQuery" +declare queryFile="${query}" +queryFile="$(Conf::getAbsoluteFile "dbQueries" "${queryFile}" "sql" 2>/dev/null || echo "")" +if [[ -n "${queryFile}" ]]; then + queryName="$(basename "${queryFile%.*}")" + query="$(cat "${queryFile}")" +fi + +# create log file +declare logFile="" +if [[ "${LOG_FORMAT}" = "log" ]]; then + # shellcheck disable=SC2154 + declare logFile="${outputDir}/${db}_${queryName}.log" + exec 6>&1 1>"${logFile}" # redirect stdout to logFile +fi + +Database::skipColumnNames dbInstance 0 +Database::query dbInstance "${query}" "${db}" || true +Database::skipColumnNames dbInstance 1 + +if [[ "${LOG_FORMAT}" = "log" ]]; then + # restore stdout + exec 1>&6 6>&- +fi + +if [[ "${LOG_FORMAT}" = "log" ]]; then + Log::displayInfo "result available in '${logFile}'" +fi diff --git a/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt b/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt index b6f9311a..d828df61 100644 --- a/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt +++ b/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt @@ -1,6 +1,7 @@ SYNOPSIS: Allows to execute a script on each database of specified mysql server. + USAGE: dbScriptAllDatabases [OPTIONS] [ARGUMENTS] USAGE: dbScriptAllDatabases [--jobs|-j ] [--bar|-b] [--from-dsn|-f ] [--help|-h] [--config] diff --git a/src/_binaries/DbScriptAllDatabases/extractData.sh b/src/_binaries/DbScriptAllDatabases/extractData.sh deleted file mode 100755 index a32f2b7a..00000000 --- a/src/_binaries/DbScriptAllDatabases/extractData.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/conf/dbScripts/extractData -# FACADE -# shellcheck disable=SC2154 - -.INCLUDE "$(dynamicSrcFile _includes/dbScriptOneDatabase.sh)" - -run() { - # extra parameters passed through dbScriptAllDatabases - declare query="${scriptParameters[0]}" - declare queryName="customQuery" - declare queryFile="${query}" - queryFile="$(Conf::getAbsoluteFile "dbQueries" "${queryFile}" "sql" 2>/dev/null || echo "")" - if [[ -n "${queryFile}" ]]; then - queryName="$(basename "${queryFile%.*}")" - query="$(cat "${queryFile}")" - fi - - # create log file - declare logFile="" - if [[ "${LOG_FORMAT}" = "log" ]]; then - declare logFile="${outputDir}/${db}_${queryName}.log" - exec 6>&1 1>"${logFile}" # redirect stdout to logFile - fi - - Database::skipColumnNames dbInstance 0 - Database::query dbInstance "${query}" "${db}" || true - Database::skipColumnNames dbInstance 1 - - if [[ "${LOG_FORMAT}" = "log" ]]; then - # restore stdout - exec 1>&6 6>&- - fi - - if [[ "${LOG_FORMAT}" = "log" ]]; then - Log::displayInfo "result available in '${logFile}'" - fi -} - -init -run From fdf45890a51941db1ff0bcb874204751d3d465f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Mon, 2 Sep 2024 01:52:54 +0200 Subject: [PATCH 11/24] compiled cli using go compiler --- .pre-commit-config-github.yaml | 2 +- .pre-commit-config.yaml | 2 +- bin/cli | 3011 ++++++++++------- bin/dbImport | 16 +- bin/dbImportProfile | 16 +- bin/dbImportStream | 16 +- bin/dbQueryAllDatabases | 14 +- bin/dbScriptAllDatabases | 16 +- bin/doc | 14 +- bin/install | 1712 ---------- bin/installRequirements | 14 +- bin/mysql2puml | 14 +- conf/dbScripts/extractData | 12 +- install | 1926 ++++++----- .../mysql2puml/binary-mysql2puml.yaml | 3 +- .../mysql2puml/mysql2puml-options.sh | 4 - .../Converters/mysql2puml/mysql2puml.bats | 2 +- .../mysql2puml/testsData/mysql2puml.help.txt | 4 +- .../Database/dbImport/binary-dbImport.yaml | 3 +- .../Database/dbImport/dbImport-options.sh | 4 - .../dbImport/testsData/dbImport.help.txt | 2 +- .../binary-dbImportProfile.yaml | 3 +- .../dbImportProfile-options.sh | 4 - .../testsData/dbImportProfile.help.txt | 4 +- .../dbImportStream/binary-dbImportStream.yaml | 3 +- .../dbImportStream/dbImportStream-options.sh | 4 - .../testsData/dbImportStream.help.txt | 2 +- .../binary-dbQueryAllDatabases.yaml | 3 +- .../dbQueryAllDatabases-options.sh | 2 - .../testsData/dbQueryAllDatabases.help.txt | 2 +- .../binary-dbScriptAllDatabases.yaml | 3 +- .../binary-extractData.yaml | 3 +- .../dbScriptAllDatabases-options.sh | 4 - .../testsData/dbScriptAllDatabases.help.txt | 2 +- src/_binaries/Docker/cli.options.tpl | 117 - src/_binaries/Docker/cli.sh | 85 - src/_binaries/Docker/cli/binary-cli.yaml | 51 + src/_binaries/Docker/cli/cli-main.sh | 46 + src/_binaries/Docker/cli/cli-options.sh | 106 + src/_binaries/Docker/{ => cli}/cli.bats | 2 +- .../Docker/cli/testsData/cli.help.txt | 109 + .../{ => cli}/testsData/my-container.sh | 0 src/_binaries/Docker/testsData/cli.help.txt | 109 - src/_binaries/Git/gitIsAncestorOf.options.tpl | 2 +- .../Git/upgradeGithubRelease.options.tpl | 1 + src/_binaries/Postman/command.postmanCli.tpl | 2 +- src/_binaries/build/doc/binary-doc.yaml | 3 +- src/_binaries/build/doc/doc-options.sh | 4 - .../build/install/binary-install.yaml | 7 +- .../build/install/install-options.sh | 4 - .../binary-installRequirements.yaml | 3 +- .../installRequirements-options.sh | 4 - .../commandDefinitions/optionsVersion.sh | 2 +- 53 files changed, 3160 insertions(+), 4343 deletions(-) delete mode 100755 bin/install delete mode 100644 src/_binaries/Docker/cli.options.tpl delete mode 100755 src/_binaries/Docker/cli.sh create mode 100644 src/_binaries/Docker/cli/binary-cli.yaml create mode 100755 src/_binaries/Docker/cli/cli-main.sh create mode 100755 src/_binaries/Docker/cli/cli-options.sh rename src/_binaries/Docker/{ => cli}/cli.bats (98%) create mode 100644 src/_binaries/Docker/cli/testsData/cli.help.txt rename src/_binaries/Docker/{ => cli}/testsData/my-container.sh (100%) delete mode 100644 src/_binaries/Docker/testsData/cli.help.txt diff --git a/.pre-commit-config-github.yaml b/.pre-commit-config-github.yaml index 3896717e..51c805e0 100644 --- a/.pre-commit-config-github.yaml +++ b/.pre-commit-config-github.yaml @@ -205,6 +205,6 @@ repos: stages: [manual] # GITHUB - repo: https://github.com/fchastanet/bash-compiler - rev: 0.3.3 + rev: 0.3.4 hooks: - id: buildBashBinaries diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 96d577df..d36161ac 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -200,6 +200,6 @@ repos: stages: [] # GITHUB - repo: https://github.com/fchastanet/bash-compiler - rev: 0.3.3 + rev: 0.3.4 hooks: - id: buildBashBinaries diff --git a/bin/cli b/bin/cli index 4c72b512..92e34636 100755 --- a/bin/cli +++ b/bin/cli @@ -1,13 +1,12 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FACADE FROM https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Docker/cli.sh +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Docker/cli/binary-cli.yaml # DO NOT EDIT IT # @generated ############################################################################### # shellcheck disable=SC2288,SC2034 -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/cli -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE + + # ensure that no user aliases could interfere with # commands used in this script @@ -62,13 +61,6 @@ interruptManagement() { kill -s INT "$$" } trap interruptManagement INT -SCRIPT_NAME=${0##*/} -REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" -if [[ -n "${EMBED_CURRENT_DIR}" ]]; then - CURRENT_DIR="${EMBED_CURRENT_DIR}" -else - CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" -fi ################################################ # Temp dir management @@ -102,6 +94,20 @@ cleanOnExit() { } trap cleanOnExit EXIT HUP QUIT ABRT TERM + +SCRIPT_NAME=${0##*/} +REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" +if [[ -n "${EMBED_CURRENT_DIR}" ]]; then + CURRENT_DIR="${EMBED_CURRENT_DIR}" +else + CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" +fi +FRAMEWORK_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" +FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" +FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + # @description Log namespace provides 2 kind of functions # - Log::display* allows to display given message with # given display level @@ -132,413 +138,372 @@ export __VERBOSE_LEVEL_DEBUG=2 # @description verbose level info export __VERBOSE_LEVEL_TRACE=3 -# @description Display message using info color (bg light blue/fg white) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayInfo() { - local type="${2:-INFO}" - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logInfo "$1" "${type}" -} -# @description Display message using debug color (gray) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayDebug() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then - Log::computeDuration - echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 +# @description concatenate each element of an array with a separator +# but wrapping text when line length is more than provided argument +# The algorithm will try not to cut the array element if it can. +# - if an arg can be placed on current line it will be, +# otherwise current line is printed and arg is added to the new +# current line +# - Empty arg is interpreted as a new line. +# - Add \r to arg in order to force break line and avoid following +# arg to be concatenated with current arg. +# +# @arg $1 glue:String +# @arg $2 maxLineLength:int +# @arg $3 indentNextLine:int +# @arg $@ array:String[] +Array::wrap2() { + local glue="${1-}" + local -i glueLength="${#glue}" + shift || true + local -i maxLineLength=$1 + shift || true + local -i indentNextLine=$1 + shift || true + local indentStr="" + if ((indentNextLine > 0)); then + indentStr="$(head -c "${indentNextLine}" 0)); do + arg="$1" + shift || true + + # replace tab by 2 spaces + arg="${arg//$'\t'/ }" + # remove trailing spaces + arg="${arg%[[:blank:]]}" + if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then + printCurrentLine + ((previousLineEmpty = 1)) + continue + else + if ((previousLineEmpty == 1)); then + printCurrentLine + fi + ((previousLineEmpty = 0)) || true + fi + # convert eol to args + mapfile -t additionalLines <<<"${arg}" + if ((${#additionalLines[@]} > 1)); then + set -- "${additionalLines[@]}" "$@" + continue + fi + + ((argLength = ${#arg})) || true + + # empty arg + if ((argLength == 0)); then + if ((isNewline == 0)); then + # isNewline = 0 means currentLine is not empty + printCurrentLine + fi + continue + fi + + if ((isNewline == 0)); then + glueLength="${#glue}" + else + glueLength="0" + fi + if ((currentLineLength + argLength + glueLength > maxLineLength)); then + if ((argLength + glueLength > maxLineLength)); then + # arg is too long to even fit on one line + # we have to split the arg on current and next line + local -i remainingLineLength + ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) + appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" + printCurrentLine + arg="${arg:${remainingLineLength}}" + # remove leading spaces + arg="${arg##[[:blank:]]}" + + set -- "${arg}" "$@" + else + # the arg can fit on next line + printCurrentLine + appendToCurrentLine "${arg}" "${argLength}" + fi + else + appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" + fi + done + if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then + printCurrentLine + fi + ) | sed -E -e 's/[[:blank:]]+$//' } -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logWarning "$1" + +# @description check if command specified exists or return 1 +# with error and message if not +# +# @arg $1 commandName:String on which existence must be checked +# @arg $2 helpIfNotExists:String a help command to display if the command does not exist +# +# @exitcode 1 if the command specified does not exist +# @stderr diagnostic information + help if second argument is provided +Assert::commandExists() { + local commandName="$1" + local helpIfNotExists="$2" + + "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { + Log::displayError "${commandName} is not installed, please install it" + if [[ -n "${helpIfNotExists}" ]]; then + Log::displayInfo "${helpIfNotExists}" + fi + return 1 + } + return 0 } -# @description Display message using error color (red) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayError() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then - Log::computeDuration - echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description check if tty (interactive mode) is active +# @noargs +# @exitcode 1 if tty not active +# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive +# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive +Assert::tty() { + if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then + return 1 fi - Log::logError "$1" + if [[ "${INTERACTIVE:-0}" = "1" ]]; then + return 0 + fi + tty -s } -# @description load colors theme constants -# @warning if tty not opened, noColor theme will be chosen -# @arg $1 theme:String the theme to use (default, noColor) -# @arg $@ args:String[] -# @set __ERROR_COLOR String indicate error status -# @set __INFO_COLOR String indicate info status -# @set __SUCCESS_COLOR String indicate success status -# @set __WARNING_COLOR String indicate warning status -# @set __SKIPPED_COLOR String indicate skipped status -# @set __DEBUG_COLOR String indicate debug status -# @set __HELP_COLOR String indicate help status -# @set __TEST_COLOR String not used -# @set __TEST_ERROR_COLOR String not used -# @set __HELP_TITLE_COLOR String used to display help title in help strings -# @set __HELP_OPTION_COLOR String used to display highlight options in help strings -# -# @set __RESET_COLOR String reset default color + +# @description determine if the script is executed under windows (using wsl) +# cspell:disable +# @example text +# uname GitBash windows (with wsl) => MINGW64_NT-10.0 ZOXFL-6619QN2 2.10.0(0.325/5/3) 2018-06-13 23:34 x86_64 Msys +# uname GitBash windows (wo wsl) => MINGW64_NT-10.0 frsa02-j5cbkc2 2.9.0(0.318/5/3) 2018-01-12 23:37 x86_64 Msys +# uname wsl => Linux ZOXFL-6619QN2 4.4.0-17134-Microsoft #112-Microsoft Thu Jun 07 22:57:00 PST 2018 x86_64 x86_64 x86_64 GNU/Linux +# cspell:enable # -# @set __HELP_EXAMPLE String to remove -# @set __HELP_TITLE String to remove -# @set __HELP_NORMAL String to remove -# shellcheck disable=SC2034 -UI::theme() { - local theme="${1-default}" - if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then - theme="noColor" - fi - case "${theme}" in - default | default-force) - theme="default" - ;; - noColor) ;; - *) - Log::fatal "invalid theme provided" - ;; - esac - if [[ "${theme}" = "default" ]]; then - BASH_FRAMEWORK_THEME="default" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='\e[31m' # Red - __INFO_COLOR='\e[44m' # white on lightBlue - __SUCCESS_COLOR='\e[32m' # Green - __WARNING_COLOR='\e[33m' # Yellow - __SKIPPED_COLOR='\e[33m' # Yellow - __DEBUG_COLOR='\e[37m' # Gray - __HELP_COLOR='\e[7;49;33m' # Black on Gold - __TEST_COLOR='\e[100m' # Light magenta - __TEST_ERROR_COLOR='\e[41m' # white on red - __HELP_TITLE_COLOR="\e[1;37m" # Bold - __HELP_OPTION_COLOR="\e[1;34m" # Blue - # Internal: reset color - __RESET_COLOR='\e[0m' # Reset Color - # shellcheck disable=SC2155,SC2034 - __HELP_EXAMPLE="$(echo -e "\e[2;97m")" - # shellcheck disable=SC2155,SC2034 - __HELP_TITLE="$(echo -e "\e[1;37m")" - # shellcheck disable=SC2155,SC2034 - __HELP_NORMAL="$(echo -e "\033[0m")" - else - BASH_FRAMEWORK_THEME="noColor" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='' - __INFO_COLOR='' - __SUCCESS_COLOR='' - __WARNING_COLOR='' - __SKIPPED_COLOR='' - __DEBUG_COLOR='' - __HELP_COLOR='' - __TEST_COLOR='' - __TEST_ERROR_COLOR='' - __HELP_TITLE_COLOR='' - __HELP_OPTION_COLOR='' - # Internal: reset color - __RESET_COLOR='' - __HELP_EXAMPLE='' - __HELP_TITLE='' - __HELP_NORMAL='' - fi +# @exitcode 1 on error +Assert::windows() { + [[ "$(uname -o)" = "Msys" ]] } -# @description draw a line with the character passed in parameter repeated depending on terminal width -# @arg $1 character:String character to use as separator (default value #) -UI::drawLine() { - local character="${1:-#}" - local -i width=${COLUMNS:-0} - if ((width == 0)) && [[ -t 1 ]]; then - width=$(tput cols) - fi - if ((width == 0)); then - width=80 - fi - printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") - echo -} -# @description Display message using error color (red) and exit immediately with error status 1 -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::fatal() { - Log::computeDuration - echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - Log::logFatal "$1" - exit 1 +# @description ignore exit code 141 from simple command pipes +# @example use with: +# local resultingStatus=0 +# local -a originalPipeStatus=() +# cmd1 | cmd2 || Bash::handlePipelineFailure resultingStatus originalPipeStatus || true +# [[ "${resultingStatus}" = "0" ]] +# @arg $1 resultingStatusCode:&int (passed by reference) (optional) resulting status code +# @arg $2 originalStatus:int[] (passed by reference) (optional) copy of original PIPESTATUS array +# @env PIPESTATUS assuming that this function is called like in the example provided +# @see https://unix.stackexchange.com/a/709880/582856 +Bash::handlePipelineFailure() { + local -a pipeStatusBackup=("${PIPESTATUS[@]}") + local -n handlePipelineFailure_resultingStatusCode=$1 + local -n handlePipelineFailure_originalStatus=$2 + # shellcheck disable=SC2034 + handlePipelineFailure_originalStatus=("${pipeStatusBackup[@]}") + handlePipelineFailure_resultingStatusCode=0 + local statusCode + for statusCode in "${pipeStatusBackup[@]}"; do + if ((statusCode == 141)); then + return 0 + elif ((statusCode > 0)); then + # shellcheck disable=SC2034 + handlePipelineFailure_resultingStatusCode="${statusCode}" + break + fi + done + return "${handlePipelineFailure_resultingStatusCode}" } -# @description create a temp file using default TMPDIR variable -# initialized in _includes/_commonHeader.sh -# @env TMPDIR String (default value /tmp) -# @arg $1 templateName:String template name to use(optional) -Framework::createTempFile() { - mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" -} -# @description ensure env files are loaded -# @arg $@ list of default files to load at the end -# @exitcode 1 if one of env files fails to load -# @stderr diagnostics information is displayed -# shellcheck disable=SC2120 -Env::requireLoad() { - local -a defaultFiles=("$@") - # get list of possible config files - local -a configFiles=() - if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then - # BASH_FRAMEWORK_ENV_FILES is an array - configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") - fi - local localFrameworkConfigFile - localFrameworkConfigFile="$(pwd)/.framework-config" - if [[ -f "${localFrameworkConfigFile}" ]]; then - configFiles+=("${localFrameworkConfigFile}") - fi - if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then - configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") - fi - configFiles+=("${optionEnvFiles[@]}") - configFiles+=("${defaultFiles[@]}") +# @description loads ~/.bash-tools/.env if available +# if not creates it from a default template +# else check if new options need to be added +BashTools::Conf::requireLoad() { +Linux::requireTarCommand +Compiler::Embed::extractFileFromBase64 \ + "${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" \ + "IyEvdXNyL2Jpbi9lbnYgYmFzaAojIHNoZWxsY2hlY2sgZGlzYWJsZT1TQzIwMzQKCiMgRGVmYXVsdCBzZXR0aW5ncwojIHlvdSBjYW4gb3ZlcnJpZGUgdGhlc2Ugc2V0dGluZ3MgYnkgY3JlYXRpbmcgJHtIT01FfS8uYmFzaC10b29scy8uZW52IGZpbGUKCiMjIwojIyMgRElTUExBWSBMZXZlbAojIyMgbWluaW11bSBsZXZlbCBvZiB0aGUgbWVzc2FnZXMgdGhhdCB3aWxsIGJlIGRpc3BsYXllZCBvbiBzY3JlZW4KIyMjCiMjIyAwOiBOTyBMT0cKIyMjIDE6IEVSUk9SCiMjIyAyOiBXQVJOSU5HCiMjIyAzOiBJTkZPCiMjIyA0OiBERUJVRwojIyMKQkFTSF9GUkFNRVdPUktfRElTUExBWV9MRVZFTD0ke0JBU0hfRlJBTUVXT1JLX0RJU1BMQVlfTEVWRUw6LTN9CgojIyMKIyMjIERJU1BMQVkgZHVyYXRpb24KIyMjIDA6IG5vIGR1cmF0aW9uIGlzIGRpc3BsYXllZCBvbiB0aGUgbWVzc2FnZXMKIyMjIDE6IGR1cmF0aW9uIGJldHdlZW4gcHJldmlvdXMgbWVzc2FnZSBhbmQgY3VycmVudCBpcyBkaXNwbGF5ZWQKIyMjIHdpdGggdGhlIG1lc3NhZ2UKIyMjCkRJU1BMQVlfRFVSQVRJT049JHtESVNQTEFZX0RVUkFUSU9OOjB9CgojIyMKIyMjIExvZyB0byBmaWxlCiMjIwojIyMgYWxsIGxvZyBtZXNzYWdlcyB3aWxsIGJlIHJlZGlyZWN0ZWQgdG8gbG9nIGZpbGUgc3BlY2lmaWVkCiMjIyB0aGlzIHNhbWUgcGF0aCB3aWxsIGJlIHVzZWQgaW5zaWRlIGFuZCBvdXRzaWRlIG9mIHRoZSBjb250YWluZXIKIyMjCkJBU0hfRlJBTUVXT1JLX0xPR19GSUxFPSR7QkFTSF9GUkFNRVdPUktfTE9HX0ZJTEU6LSR7RlJBTUVXT1JLX1JPT1RfRElSfS9sb2dzL2Jhc2gubG9nfQoKIyMjCiMjIyBMT0cgTGV2ZWwKIyMjIG1pbmltdW0gbGV2ZWwgb2YgdGhlIG1lc3NhZ2VzIHRoYXQgd2lsbCBiZSBsb2dnZWQgaW50byBMT0dfRklMRQojIyMKIyMjIDA6IE5PIExPRwojIyMgMTogRVJST1IKIyMjIDI6IFdBUk5JTkcKIyMjIDM6IElORk8KIyMjIDQ6IERFQlVHCiMjIwpCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw9JHtCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw6LTB9CgojIGFic29sdXRlIGRpcmVjdG9yeSBjb250YWluaW5nIGRiIGltcG9ydCBzcWwgZHVtcHMKREJfSU1QT1JUX0RVTVBfRElSPSR7REJfSU1QT1JUX0RVTVBfRElSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2RiSW1wb3J0RHVtcHN9CgojIGdhcmJhZ2UgY29sbGVjdCBhbGwgZmlsZXMgZm9yIHdoaWNoIG1vZGlmaWNhdGlvbiBpcyBncmVhdGVyIHRoYW4gZWc6IDMwIGRheXMgKCszMCkKIyBlYWNoIHRpbWUgYW4gZXhpc3RpbmcgZmlsZSBpcyB1c2VkIGJ5IGRiSW1wb3J0L2RiSW1wb3J0VGFibGUKIyB0aGUgZmlsZSBtb2RpZmljYXRpb24gdGltZSBpcyBzZXQgdG8gbm93CkRCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUz0ke0RCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUzotKzMwfQoKIyBhYnNvbHV0ZSBkaXJlY3RvcnkgY29udGFpbmluZyBkYlNjcmlwdHMgdXNlZCBieSBkYlNjcmlwdEFsbERhdGFiYXNlcwpTQ1JJUFRTX0ZPTERFUj0ke1NDUklQVFNfRk9MREVSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2NvbmYvZGJTY3JpcHRzfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEFXUyBQYXJhbWV0ZXJzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KUzNfQkFTRV9VUkw9JHtTM19CQVNFX1VSTDotfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvc3RtYW4gUGFyYW1ldGVycwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClBPU1RNQU5fQVBJX0tFWT0K" \ + "755" - for file in "${configFiles[@]}"; do - # shellcheck source=/.framework-config - CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { - Log::displayError "while loading config file: ${file}" - return 1 - } - done -} +declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" -# @description activate or not Log::display* and Log::log* functions -# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL -# environment variables loaded by Env::requireLoad -# try to create log file and rotate it if necessary -# @noargs -# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable -# @env BASH_FRAMEWORK_DISPLAY_LEVEL int -# @env BASH_FRAMEWORK_LOG_LEVEL int -# @env BASH_FRAMEWORK_LOG_FILE String -# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 -# @exitcode 0 always successful -# @stderr diagnostics information about log file is displayed -# @require Env::requireLoad -# @require UI::requireTheme -Log::requireLoad() { - if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - export BASH_FRAMEWORK_LOG_LEVEL + BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" + if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then + FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" + else + # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR + FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" fi - - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then - if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 - fi - elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi + # shellcheck disable=SC2034 + FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" + # shellcheck disable=SC2034 + FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + + if [[ -f "${HOME}/.bash-tools/.env" ]]; then + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") fi - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - # will always be created even if not in info level - Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" - if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then - Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + local envFile="${HOME}/.bash-tools/.env" + if [[ ! -f "${envFile}" ]]; then + mkdir -p "${HOME}/.bash-tools" + ( + echo "#!/usr/bin/env bash" + # shellcheck disable=SC2154 + echo "${embed_file_bashToolsDefaultConfigTemplate}" + ) >"${envFile}" + Log::displayInfo "Configuration file '${envFile}' created" + else + if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then + ( + echo '# -----------------------------------------------------' + echo '# Postman Parameters' + echo '# -----------------------------------------------------' + echo 'POSTMAN_API_KEY=' + ) >>"${envFile}" fi fi + # shellcheck source=/conf/.env + source "${envFile}" || { + Log::displayError "impossible to load '${envFile}'" + exit 1 + } } -# @description concatenate each element of an array with a separator -# but wrapping text when line length is more than provided argument -# The algorithm will try not to cut the array element if it can. -# - if an arg can be placed on current line it will be, -# otherwise current line is printed and arg is added to the new -# current line -# - Empty arg is interpreted as a new line. -# - Add \r to arg in order to force break line and avoid following -# arg to be concatenated with current arg. -# -# @arg $1 glue:String -# @arg $2 maxLineLength:int -# @arg $3 indentNextLine:int -# @arg $@ array:String[] -Array::wrap2() { - local glue="${1-}" - local -i glueLength="${#glue}" - shift || true - local -i maxLineLength=$1 - shift || true - local -i indentNextLine=$1 - shift || true - local indentStr="" - if ((indentNextLine > 0)); then - indentStr="$(head -c "${indentNextLine}" 0)); do - arg="$1" - shift || true - - # replace tab by 2 spaces - arg="${arg//$'\t'/ }" - # remove trailing spaces - arg="${arg%[[:blank:]]}" - if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then - printCurrentLine - ((previousLineEmpty = 1)) - continue - else - if ((previousLineEmpty == 1)); then - printCurrentLine - fi - ((previousLineEmpty = 0)) || true - fi - # convert eol to args - mapfile -t additionalLines <<<"${arg}" - if ((${#additionalLines[@]} > 1)); then - set -- "${additionalLines[@]}" "$@" - continue - fi - - ((argLength = ${#arg})) || true - - # empty arg - if ((argLength == 0)); then - if ((isNewline == 0)); then - # isNewline = 0 means currentLine is not empty - printCurrentLine - fi - continue - fi - - if ((isNewline == 0)); then - glueLength="${#glue}" - else - glueLength="0" - fi - if ((currentLineLength + argLength + glueLength > maxLineLength)); then - if ((argLength + glueLength > maxLineLength)); then - # arg is too long to even fit on one line - # we have to split the arg on current and next line - local -i remainingLineLength - ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) - appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" - printCurrentLine - arg="${arg:${remainingLineLength}}" - # remove leading spaces - arg="${arg##[[:blank:]]}" + base64 -d >"${targetFile}" <<<"${binFileBase64}" + chmod "${fileMode}" "${targetFile}" + fi - set -- "${arg}" "$@" - else - # the arg can fit on next line - printCurrentLine - appendToCurrentLine "${arg}" "${argLength}" - fi - else - appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" - fi - done - if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then - printCurrentLine - fi - ) | sed -E -e 's/[[:blank:]]+$//' + if [[ -x "${targetFile}" ]]; then + Env::pathPrepend "${targetDir}" + fi } -# @description get absolute file from name deduced using these rules -# * using absolute/relative file (ignores and -# * from home/.bash-tools// file -# * from framework conf/ file + +# @description get absolute conf file from specified conf folder deduced using these rules +# * from absolute file (ignores and ) +# * relative to where script is executed (ignores and ) +# * from home/.bash-tools/ +# * from framework conf/ # -# @arg $1 confFolder:String directory to use (traditionally below bash-tools conf folder) +# @arg $1 confFolder:String the directory name (not the path) to list # @arg $2 conf:String file to use without extension -# @arg $3 extension:String file extension to use (default: .sh) +# @arg $3 extension:String the extension (.sh by default) # -# @exitcode 1 if file not found or error during file loading -Conf::load() { +# @stdout absolute conf filename +# @exitcode 1 if file is not found in any location +Conf::getAbsoluteFile() { local confFolder="$1" local conf="$2" - local extension="${3:-sh}" - + local extension="${3-.sh}" if [[ -n "${extension}" && "${extension:0:1}" != "." ]]; then extension=".${extension}" fi - # if conf is absolute - local confFile - if [[ "${conf}" == /* ]]; then - # file contains /, consider it as absolute filename - confFile="${conf}" - else - # shellcheck source=/conf/dsn/default.local.env - confFile="${HOME}/.bash-tools/${confFolder}/${conf}${extension}" - if [[ ! -f "${confFile}" ]]; then - confFile="${FRAMEWORK_ROOT_DIR}/conf/${confFolder}/${conf}${extension}" + + testAbs() { + local result + result="$(realpath -e "$1" 2>/dev/null)" + # shellcheck disable=SC2181 + if [[ "$?" = "0" && -f "${result}" ]]; then + echo "${result}" + return 0 fi - fi - if [[ ! -f "${confFile}" ]]; then return 1 + } + + # conf is absolute file (including extension) + testAbs "${confFolder}${extension}" && return 0 + # conf is absolute file + testAbs "${confFolder}" && return 0 + # conf is absolute file (including extension) + testAbs "${conf}${extension}" && return 0 + # conf is absolute file + testAbs "${conf}" && return 0 + + # relative to where script is executed (including extension) + if [[ -n "${CURRENT_DIR+xxx}" ]]; then + testAbs "$(File::concatenatePath "${CURRENT_DIR}" "${confFolder}")/${conf}${extension}" && return 0 fi - # shellcheck disable=SC1090 - source "${confFile}" + # from home/.bash-tools/ + testAbs "$(File::concatenatePath "${HOME}/.bash-tools" "${confFolder}")/${conf}${extension}" && return 0 + + if [[ -n "${FRAMEWORK_ROOT_DIR+xxx}" ]]; then + # from framework conf/ (including extension) + testAbs "$(File::concatenatePath "${FRAMEWORK_ROOT_DIR}/conf" "${confFolder}")/${conf}${extension}" && return 0 + + # from framework conf/ + testAbs "$(File::concatenatePath "${FRAMEWORK_ROOT_DIR}/conf" "${confFolder}")/${conf}" && return 0 + fi + + # file not found + Log::displayError "conf file '${conf}' not found" + + return 1 } + # @description list the conf files list available in bash-tools/conf/ folder # and those overridden in ${HOME}/.bash-tools/ folder # @@ -569,67 +534,81 @@ Conf::getMergedList() { ) | sort | uniq } -# @description get absolute conf file from specified conf folder deduced using these rules -# * from absolute file (ignores and ) -# * relative to where script is executed (ignores and ) -# * from home/.bash-tools/ -# * from framework conf/ + +# @description list files of dir with given extension and display it as a list one by line +# +# @arg $1 dir:String the directory to list +# @arg $2 prefix:String the profile file prefix (default: "") +# @arg $3 ext:String the extension +# @arg $4 findOptions:String find options, eg: -type d (Default value: '-type f') +# @arg $5 indentStr:String the indentation can be any string compatible with sed not containing any / (Default value: ' - ') +# @stdout list of files without extension/directory +# @example text +# - default.local +# - default.remote +# - localhost-root +# @exitcode 1 if directory does not exists +Conf::list() { + local dir="$1" + local prefix="${2:-}" + local ext="${3}" + local findOptions="${4--type f}" + local indentStr="${5- - }" + + if [[ ! -d "${dir}" ]]; then + Log::displayError "Directory ${dir} does not exist" + fi + if [[ -n "${ext}" && "${ext:0:1}" != "." ]]; then + ext=".${ext}" + fi + ( + # shellcheck disable=SC2086 + cd "${dir}" && + find . -maxdepth 1 ${findOptions} -name "${prefix}*${ext}" | + sed -E "s#^\./${prefix}##g" | + sed -E "s#${ext}\$##g" | sort | sed -E "s#^#${indentStr}#" + ) +} + + +# @description get absolute file from name deduced using these rules +# * using absolute/relative file (ignores and +# * from home/.bash-tools// file +# * from framework conf/ file # -# @arg $1 confFolder:String the directory name (not the path) to list +# @arg $1 confFolder:String directory to use (traditionally below bash-tools conf folder) # @arg $2 conf:String file to use without extension -# @arg $3 extension:String the extension (.sh by default) +# @arg $3 extension:String file extension to use (default: .sh) # -# @stdout absolute conf filename -# @exitcode 1 if file is not found in any location -Conf::getAbsoluteFile() { +# @exitcode 1 if file not found or error during file loading +Conf::load() { local confFolder="$1" local conf="$2" - local extension="${3-.sh}" + local extension="${3:-sh}" + if [[ -n "${extension}" && "${extension:0:1}" != "." ]]; then extension=".${extension}" fi - - testAbs() { - local result - result="$(realpath -e "$1" 2>/dev/null)" - # shellcheck disable=SC2181 - if [[ "$?" = "0" && -f "${result}" ]]; then - echo "${result}" - return 0 + # if conf is absolute + local confFile + if [[ "${conf}" == /* ]]; then + # file contains /, consider it as absolute filename + confFile="${conf}" + else + # shellcheck source=/conf/dsn/default.local.env + confFile="${HOME}/.bash-tools/${confFolder}/${conf}${extension}" + if [[ ! -f "${confFile}" ]]; then + confFile="${FRAMEWORK_ROOT_DIR}/conf/${confFolder}/${conf}${extension}" fi - return 1 - } - - # conf is absolute file (including extension) - testAbs "${confFolder}${extension}" && return 0 - # conf is absolute file - testAbs "${confFolder}" && return 0 - # conf is absolute file (including extension) - testAbs "${conf}${extension}" && return 0 - # conf is absolute file - testAbs "${conf}" && return 0 - - # relative to where script is executed (including extension) - if [[ -n "${CURRENT_DIR+xxx}" ]]; then - testAbs "$(File::concatenatePath "${CURRENT_DIR}" "${confFolder}")/${conf}${extension}" && return 0 fi - # from home/.bash-tools/ - testAbs "$(File::concatenatePath "${HOME}/.bash-tools" "${confFolder}")/${conf}${extension}" && return 0 - - if [[ -n "${FRAMEWORK_ROOT_DIR+xxx}" ]]; then - # from framework conf/ (including extension) - testAbs "$(File::concatenatePath "${FRAMEWORK_ROOT_DIR}/conf" "${confFolder}")/${conf}${extension}" && return 0 - - # from framework conf/ - testAbs "$(File::concatenatePath "${FRAMEWORK_ROOT_DIR}/conf" "${confFolder}")/${conf}" && return 0 + if [[ ! -f "${confFile}" ]]; then + return 1 fi - - # file not found - Log::displayError "conf file '${conf}' not found" - - return 1 + # shellcheck disable=SC1090 + source "${confFile}" } + # @description check if dsn file has all the mandatory variables set # Mandatory variables are: HOSTNAME, USER, PASSWORD, PORT # @@ -681,150 +660,119 @@ Database::checkDsnFile() { ) } -# @description check if command specified exists or return 1 -# with error and message if not -# -# @arg $1 commandName:String on which existence must be checked -# @arg $2 helpIfNotExists:String a help command to display if the command does not exist -# -# @exitcode 1 if the command specified does not exist -# @stderr diagnostic information + help if second argument is provided -Assert::commandExists() { - local commandName="$1" - local helpIfNotExists="$2" - "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { - Log::displayError "${commandName} is not installed, please install it" - if [[ -n "${helpIfNotExists}" ]]; then - Log::displayInfo "${helpIfNotExists}" - fi - return 1 - } - return 0 +# @description check if all requirements are satisfied +# to execute dbImport commands +Db::checkRequirements() { + if [[ "${SKIP_REQUIREMENTS_CHECKS:-0}" = "1" ]]; then + return 0 + fi + local -i failures=0 + echo + Assert::commandExists mysql "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqldump "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists pv "sudo apt-get install -y pv" || ((++failures)) + Assert::commandExists gawk "sudo apt-get install -y gawk" || ((++failures)) + Assert::commandExists awk "sudo apt-get install -y gawk" || ((++failures)) + Version::checkMinimal "gawk" "--version" "5.0.1" || ((++failures)) + return "${failures}" } -# @description determine if the script is executed under windows (using wsl) -# cspell:disable -# @example text -# uname GitBash windows (with wsl) => MINGW64_NT-10.0 ZOXFL-6619QN2 2.10.0(0.325/5/3) 2018-06-13 23:34 x86_64 Msys -# uname GitBash windows (wo wsl) => MINGW64_NT-10.0 frsa02-j5cbkc2 2.9.0(0.318/5/3) 2018-01-12 23:37 x86_64 Msys -# uname wsl => Linux ZOXFL-6619QN2 4.4.0-17134-Microsoft #112-Microsoft Thu Jun 07 22:57:00 PST 2018 x86_64 x86_64 x86_64 GNU/Linux -# cspell:enable -# -# @exitcode 1 on error -Assert::windows() { - [[ "$(uname -o)" = "Msys" ]] + +# @description prepend directories to the PATH environment variable +# @arg $@ args:String[] list of directories to prepend +# @set PATH update PATH with the directories prepended +Env::pathPrepend() { + local arg + for arg in "$@"; do + if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then + PATH="$(realpath "${arg}"):${PATH}" + fi + done } -bashToolsDefaultConfigTemplate="${bashToolsDefaultConfigTemplate:-$( - cat <<'EOF' -# shellcheck disable=SC2034 -# Default settings -# you can override these settings by creating ${HOME}/.bash-tools/.env file - -### -### DISPLAY Level -### minimum level of the messages that will be displayed on screen -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} - -### -### DISPLAY duration -### 0: no duration is displayed on the messages -### 1: duration between previous message and current is displayed -### with the message -### -DISPLAY_DURATION=${DISPLAY_DURATION:0} - -### -### Log to file -### -### all log messages will be redirected to log file specified -### this same path will be used inside and outside of the container -### -BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} - -### -### LOG Level -### minimum level of the messages that will be logged into LOG_FILE -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} - -# absolute directory containing db import sql dumps -DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} - -# garbage collect all files for which modification is greater than eg: 30 days (+30) -# each time an existing file is used by dbImport/dbImportTable -# the file modification time is set to now -DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} - -# absolute directory containing dbScripts used by dbScriptAllDatabases -SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} - -# ----------------------------------------------------- -# AWS Parameters -# ----------------------------------------------------- -S3_BASE_URL=${S3_BASE_URL:-} - -# ----------------------------------------------------- -# Postman Parameters -# ----------------------------------------------------- -POSTMAN_API_KEY= -EOF -)}" +# @description ensure env files are loaded +# @arg $@ list of default files to load at the end +# @exitcode 1 if one of env files fails to load +# @stderr diagnostics information is displayed +# shellcheck disable=SC2120 +Env::requireLoad() { + export REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 -# @description loads ~/.bash-tools/.env if available -# if not creates it from a default template -# else check if new options need to be added -BashTools::Conf::requireLoad() { - local envFile="${HOME}/.bash-tools/.env" - if [[ ! -f "${envFile}" ]]; then - mkdir -p "${HOME}/.bash-tools" - ( - echo "#!/usr/bin/env bash" - echo "${bashToolsDefaultConfigTemplate}" - ) >"${envFile}" - Log::displayInfo "Configuration file '${envFile}' created" - else - if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then - ( - echo '# -----------------------------------------------------' - echo '# Postman Parameters' - echo '# -----------------------------------------------------' - echo 'POSTMAN_API_KEY=' - ) >>"${envFile}" - fi + local -a defaultFiles=("$@") + # get list of possible config files + local -a configFiles=() + if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then + # BASH_FRAMEWORK_ENV_FILES is an array + configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") fi - # shellcheck source=/conf/.env - source "${envFile}" || { - Log::displayError "impossible to load '${envFile}'" + local localFrameworkConfigFile + localFrameworkConfigFile="$(pwd)/.framework-config" + if [[ -f "${localFrameworkConfigFile}" ]]; then + configFiles+=("${localFrameworkConfigFile}") + fi + if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then + configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") + fi + configFiles+=("${optionEnvFiles[@]}") + configFiles+=("${defaultFiles[@]}") + + for file in "${configFiles[@]}"; do + # shellcheck source=/.framework-config + CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { + Log::displayError "while loading config file: ${file}" + return 1 + } + done +} + + +# @description concatenate 2 paths and ensure the path is correct using realpath -m +# @arg $1 basePath:String +# @arg $2 subPath:String +File::concatenatePath() { + + if [[ "${REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Linux::requireRealpathCommand has not been loaded" exit 1 - } + fi + + local basePath="$1" + local subPath="$2" + local fullPath="${basePath:+${basePath}/}${subPath}" + + realpath -m "${fullPath}" 2>/dev/null } -# @description ensure COMMAND_BIN_DIR env var is set -# and PATH correctly prepared -# @noargs -# @set COMMAND_BIN_DIR string the directory where to find this command -# @set PATH string add directory where to find this command binary -Compiler::Facade::requireCommandBinDir() { - COMMAND_BIN_DIR="${CURRENT_DIR}" - Env::pathPrepend "${COMMAND_BIN_DIR}" + +# @description create a temp file using default TMPDIR variable +# @env TMPDIR String (default value /tmp) +# @arg $1 templateName:String template name to use(optional) +Framework::createTempFile() { + mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" +} + + +# @description ensure command realpath is available +# @exitcode 1 if realpath command not available +# @stderr diagnostics information is displayed +Linux::requireRealpathCommand() { + export REQUIRE_FUNCTION_LINUX_REQUIRE_REALPATH_COMMAND_LOADED=1 + + Assert::commandExists realpath +} + + +# @description ensure command tar is available +# @exitcode 1 if tar command not available +# @stderr diagnostics information is displayed +Linux::requireTarCommand() { + Assert::commandExists tar } + declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" @@ -863,14 +811,72 @@ Log::computeDuration() { fi } -# @description log message to file + +# @description Display message using debug color (gray) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayDebug() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then + Log::computeDuration + echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logDebug "$1" +} + + +# @description Display message using error color (red) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayError() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then + Log::computeDuration + echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logError "$1" +} + + +# @description Display message using info color (bg light blue/fg white) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayInfo() { + local type="${2:-INFO}" + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logInfo "$1" "${type}" +} + + +# @description Display message using warning color (yellow) # @arg $1 message:String the message to display -Log::logInfo() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then - Log::logMessage "${2:-INFO}" "$1" +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayWarning() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then + Log::computeDuration + echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logWarning "$1" +} + + +# @description Display message using error color (red) and exit immediately with error status 1 +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::fatal() { + Log::computeDuration + echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + Log::logFatal "$1" + exit 1 } + # @description log message to file # @arg $1 message:String the message to display Log::logDebug() { @@ -879,13 +885,6 @@ Log::logDebug() { fi } -# @description log message to file -# @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" - fi -} # @description log message to file # @arg $1 message:String the message to display @@ -895,20 +894,6 @@ Log::logError() { fi } -# @description check if tty (interactive mode) is active -# @noargs -# @exitcode 1 if tty not active -# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive -# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive -Assert::tty() { - if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then - return 1 - fi - if [[ "${INTERACTIVE:-0}" = "1" ]]; then - return 0 - fi - tty -s -} # @description log message to file # @arg $1 message:String the message to display @@ -916,6 +901,16 @@ Log::logFatal() { Log::logMessage "${2:-FATAL}" "$1" } + +# @description log message to file +# @arg $1 message:String the message to display +Log::logInfo() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then + Log::logMessage "${2:-INFO}" "$1" + fi +} + + # @description Internal: common log message # @example text # [date]|[levelMsg]|message @@ -929,9 +924,18 @@ Log::logFatal() { # @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty # @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages # @stderr diagnostics information is displayed -# @require Env::requireLoad -# @require Log::requireLoad Log::logMessage() { + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Log::requireLoad has not been loaded" + exit 1 + fi + local levelMsg="$1" local msg="$2" local date @@ -943,6 +947,74 @@ Log::logMessage() { fi } + +# @description log message to file +# @arg $1 message:String the message to display +Log::logWarning() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then + Log::logMessage "${2:-WARNING}" "$1" + fi +} + + +# @description activate or not Log::display* and Log::log* functions +# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL +# environment variables loaded by Env::requireLoad +# try to create log file and rotate it if necessary +# @noargs +# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable +# @env BASH_FRAMEWORK_DISPLAY_LEVEL int +# @env BASH_FRAMEWORK_LOG_LEVEL int +# @env BASH_FRAMEWORK_LOG_FILE String +# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 +# @exitcode 0 always successful +# @stderr diagnostics information about log file is displayed +Log::requireLoad() { + export REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement UI::requireTheme has not been loaded" + exit 1 + fi + + if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + export BASH_FRAMEWORK_LOG_LEVEL + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then + if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 + fi + elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + # will always be created even if not in info level + Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" + if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then + Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + fi + fi +} + + # @description To be called before logging in the log file # @arg $1 file:string log file name # @arg $2 maxLogFilesCount:int maximum number of log files @@ -965,134 +1037,222 @@ Log::rotate() { fi } -# @description list files of dir with given extension and display it as a list one by line -# -# @arg $1 dir:String the directory to list -# @arg $2 prefix:String the profile file prefix (default: "") -# @arg $3 ext:String the extension -# @arg $4 findOptions:String find options, eg: -type d (Default value: '-type f') -# @arg $5 indentStr:String the indentation can be any string compatible with sed not containing any / (Default value: ' - ') -# @stdout list of files without extension/directory -# @example text -# - default.local -# - default.remote -# - localhost-root -# @exitcode 1 if directory does not exists -Conf::list() { - local dir="$1" - local prefix="${2:-}" - local ext="${3}" - local findOptions="${4--type f}" - local indentStr="${5- - }" - if [[ ! -d "${dir}" ]]; then - Log::displayError "Directory ${dir} does not exist" +# @description draw a line with the character passed in parameter repeated depending on terminal width +# @arg $1 character:String character to use as separator (default value #) +UI::drawLine() { + local character="${1:-#}" + local -i width=${COLUMNS:-0} + if ((width == 0)) && [[ -t 1 ]]; then + width=$(tput cols) fi - if [[ -n "${ext}" && "${ext:0:1}" != "." ]]; then - ext=".${ext}" + if ((width == 0)); then + width=80 + fi + printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") + echo +} + + +# @description load color theme +# @noargs +# @env BASH_FRAMEWORK_THEME String theme to use +# @env LOAD_THEME int 0 to avoid loading theme +# @exitcode 0 always successful +UI::requireTheme() { + export REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + + if [[ "${LOAD_THEME:-1}" = "1" ]]; then + UI::theme "${BASH_FRAMEWORK_THEME-default}" + fi +} + + +# @description load colors theme constants +# @warning if tty not opened, noColor theme will be chosen +# @arg $1 theme:String the theme to use (default, noColor) +# @arg $@ args:String[] +# @set __ERROR_COLOR String indicate error status +# @set __INFO_COLOR String indicate info status +# @set __SUCCESS_COLOR String indicate success status +# @set __WARNING_COLOR String indicate warning status +# @set __SKIPPED_COLOR String indicate skipped status +# @set __DEBUG_COLOR String indicate debug status +# @set __HELP_COLOR String indicate help status +# @set __TEST_COLOR String not used +# @set __TEST_ERROR_COLOR String not used +# @set __HELP_TITLE_COLOR String used to display help title in help strings +# @set __HELP_OPTION_COLOR String used to display highlight options in help strings +# +# @set __RESET_COLOR String reset default color +# +# @set __HELP_EXAMPLE String to remove +# @set __HELP_TITLE String to remove +# @set __HELP_NORMAL String to remove +# shellcheck disable=SC2034 +UI::theme() { + local theme="${1-default}" + if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then + theme="noColor" + fi + case "${theme}" in + default | default-force) + theme="default" + ;; + noColor) ;; + *) + Log::fatal "invalid theme provided" + ;; + esac + if [[ "${theme}" = "default" ]]; then + BASH_FRAMEWORK_THEME="default" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='\e[31m' # Red + __INFO_COLOR='\e[44m' # white on lightBlue + __SUCCESS_COLOR='\e[32m' # Green + __WARNING_COLOR='\e[33m' # Yellow + __SKIPPED_COLOR='\e[33m' # Yellow + __DEBUG_COLOR='\e[37m' # Gray + __HELP_COLOR='\e[7;49;33m' # Black on Gold + __TEST_COLOR='\e[100m' # Light magenta + __TEST_ERROR_COLOR='\e[41m' # white on red + __HELP_TITLE_COLOR="\e[1;37m" # Bold + __HELP_OPTION_COLOR="\e[1;34m" # Blue + # Internal: reset color + __RESET_COLOR='\e[0m' # Reset Color + # shellcheck disable=SC2155,SC2034 + __HELP_EXAMPLE="$(echo -e "\e[2;97m")" + # shellcheck disable=SC2155,SC2034 + __HELP_TITLE="$(echo -e "\e[1;37m")" + # shellcheck disable=SC2155,SC2034 + __HELP_NORMAL="$(echo -e "\033[0m")" + else + BASH_FRAMEWORK_THEME="noColor" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='' + __INFO_COLOR='' + __SUCCESS_COLOR='' + __WARNING_COLOR='' + __SKIPPED_COLOR='' + __DEBUG_COLOR='' + __HELP_COLOR='' + __TEST_COLOR='' + __TEST_ERROR_COLOR='' + __HELP_TITLE_COLOR='' + __HELP_OPTION_COLOR='' + # Internal: reset color + __RESET_COLOR='' + __HELP_EXAMPLE='' + __HELP_TITLE='' + __HELP_NORMAL='' fi - ( - # shellcheck disable=SC2086 - cd "${dir}" && - find . -maxdepth 1 ${findOptions} -name "${prefix}*${ext}" | - sed -E "s#^\./${prefix}##g" | - sed -E "s#${ext}\$##g" | sort | sed -E "s#^#${indentStr}#" - ) } -# @description concatenate 2 paths and ensure the path is correct using realpath -m -# @arg $1 basePath:String -# @arg $2 subPath:String -# @require Linux::requireRealpathCommand -File::concatenatePath() { - local basePath="$1" - local subPath="$2" - local fullPath="${basePath:+${basePath}/}${subPath}" - realpath -m "${fullPath}" 2>/dev/null +# @description Check that command version is greater than expected minimal version +# display warning if command version greater than expected minimal version +# display error if command version less than expected minimal version and exit 1 +# @arg $1 commandName:String command path +# @arg $2 argVersion:String command line parameters to launch to get command version +# @arg $3 minimalVersion:String expected minimal command version +# @arg $4 parseVersionCallback:Function +# @arg $5 help:String optional help message to display if command does not exist +# @exitcode 0 if command version greater or equal to expected minimal version +# @exitcode 1 if command version less than expected minimal version +# @exitcode 2 if command does not exist +Version::checkMinimal() { + local commandName="$1" + local argVersion="$2" + local minimalVersion="$3" + local parseVersionCallback=${4:-Version::parse} + local help="${5:-}" + + Assert::commandExists "${commandName}" "${help}" || return 2 + + # shellcheck disable=SC2034 + local status=0 + # shellcheck disable=SC2034 + local -a pipeStatus=() + local version + version="$("${commandName}" "${argVersion}" 2>&1 | ${parseVersionCallback} || Bash::handlePipelineFailure status pipeStatus)" + + Log::displayDebug "check ${commandName} version ${version} against minimal ${minimalVersion}" + + Version::compare "${version}" "${minimalVersion}" || { + local result=$? + if [[ "${result}" = "1" ]]; then + Log::displayInfo "${commandName} version is ${version} greater than ${minimalVersion}" + elif [[ "${result}" = "2" ]]; then + Log::displayError "${commandName} minimal version is ${minimalVersion}, your version is ${version}" + return 1 + fi + return 0 + } + } -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" + +# @description compare 2 version numbers +# @arg $1 version1:String version 1 +# @arg $2 version2:String version 2 +# @exitcode 0 if equal +# @exitcode 1 if version1 > version2 +# @exitcode 2 else +Version::compare() { + if [[ "$1" = "$2" ]]; then + return 0 + fi + local IFS=. + # shellcheck disable=2206 + local i ver1=($1) ver2=($2) + # fill empty fields in ver1 with zeros + for ((i = ${#ver1[@]}; i < ${#ver2[@]}; i++)); do + ver1[i]=0 + done + for ((i = 0; i < ${#ver1[@]}; i++)); do + if [[ -z "${ver2[i]+unset}" ]] || [[ -z ${ver2[i]} ]]; then + # fill empty fields in ver2 with zeros + ver2[i]=0 + fi + if ((10#${ver1[i]} > 10#${ver2[i]})); then + return 1 + fi + if ((10#${ver1[i]} < 10#${ver2[i]})); then + return 2 fi done + return 0 } -# @description load color theme -# @noargs -# @env BASH_FRAMEWORK_THEME String theme to use -# @env LOAD_THEME int 0 to avoid loading theme -# @exitcode 0 always successful -UI::requireTheme() { - if [[ "${LOAD_THEME:-1}" = "1" ]]; then - UI::theme "${BASH_FRAMEWORK_THEME-default}" - fi -} -# @description ensure command realpath is available -# @exitcode 1 if realpath command not available -# @stderr diagnostics information is displayed -Linux::requireRealpathCommand() { - Assert::commandExists realpath +# @description filter to keep only version number from a string +# @arg $@ files:String[] the files to filter +# @exitcode * if one of the filter command fails +# @stdin you can use stdin as alternative to files argument +# @stdout the filtered content +# shellcheck disable=SC2120 +Version::parse() { + # match anything, print(p), exit on first match(Q) + sed -En \ + -e 's/\x1b\[[0-9;]*[mGKHF]//g' \ + -e 's/[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/' \ + -e '//{p;Q}' \ + "$@" } - # FUNCTIONS -facade_main_clish() { -BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" -if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then - FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" -else - # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR - FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" -fi -FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" -FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" - -# @require BashTools::Conf::requireLoad -if [[ -f "${HOME}/.bash-tools/.env" ]]; then - export BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") -fi -# REQUIRES -Env::requireLoad -UI::requireTheme -Log::requireLoad -Linux::requireRealpathCommand -BashTools::Conf::requireLoad -Compiler::Facade::requireCommandBinDir - -# @require Compiler::Facade::requireCommandBinDir -# shellcheck disable=SC2034 - -# constants -declare defaultUserArg="root" -declare -a defaultCommandArg=("//bin/sh") -declare PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/cliProfiles" -declare HOME_PROFILES_DIR="${HOME}/.bash-tools/cliProfiles" - -# option values -declare containerArg="default" -declare finalUserArg="${defaultUserArg}" -declare finalCommandArg=("${defaultCommandArg[@]}") - -# other values -declare copyrightBeginYear="2020" declare -a BASH_FRAMEWORK_ARGV_FILTERED=() +beforeParseCallback() { + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + copyrightCallback() { - if [[ -z "${copyrightBeginYear}" ]]; then - copyrightBeginYear="$(date +%Y)" - fi - echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" + # + echo "Copyright (c) 2020-now François Chastanet" } # shellcheck disable=SC2317 # if function is overridden @@ -1124,13 +1284,14 @@ updateArgListQuietCallback() { :; } # shellcheck disable=SC2317 # if function is overridden optionHelpCallback() { - cliCommand help + Log::displayError "optionHelpCallback needs to be overridden" exit 0 } # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { - echo "${SCRIPT_NAME} version 2.0" + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version 3.0" exit 0 } @@ -1148,21 +1309,22 @@ optionEnvFileCallback() { optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_INFO} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionDebugVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_DEBUG} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionTraceVerboseCallback() { + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vvv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_TRACE} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } getLevel() { @@ -1186,6 +1348,7 @@ getLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -1207,6 +1370,7 @@ getVerboseLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -1217,7 +1381,7 @@ optionDisplayLevelCallback() { logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1226,19 +1390,20 @@ optionLogLevelCallback() { local logLevel verboseLevel logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionLogFileCallback() { local logFile="$2" - echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionQuietCallback() { - echo "BASH_FRAMEWORK_QUIET_MODE=1" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_QUIET_MODE=1" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1251,653 +1416,885 @@ optionThemeCallback() { UI::theme "$2" } -displayConfig() { - echo "Config" - UI::drawLine "-" - local var - while read -r var; do - printf '%-40s = %s\n' "${var}" "$(declare -p "${var}" | sed -E -e 's/^[^=]+=(.*)/\1/')" - done < <(typeset -p | awk 'match($3, "^(BASH_FRAMEWORK_[^=]+)=", m) { print m[1] }' | sort) - exit 0 -} +displayConfig() { + echo "Config" + UI::drawLine "-" + local var + while read -r var; do + printf '%-40s = %s\n' "${var}" "$(declare -p "${var}" | sed -E -e 's/^[^=]+=(.*)/\1/')" + done < <(typeset -p | awk 'match($3, "^(BASH_FRAMEWORK_[^=]+)=", m) { print m[1] }' | sort) + exit 0 +} + +optionBashFrameworkConfigCallback() { + if [[ ! -f "$2" ]]; then + Log::fatal "Command ${SCRIPT_NAME} - Bash framework config file '$2' does not exists" + fi +} + +defaultFrameworkConfig="$( + cat <<'EOF' + +# copied from src/_includes/.framework-config.default +# shellcheck disable=SC2034 + +REAL_SCRIPT_FILE="${REAL_SCRIPT_FILE:-$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")}" +FRAMEWORK_ROOT_DIR="${FRAMEWORK_ROOT_DIR:-${REAL_SCRIPT_FILE%/*/*}}" +FRAMEWORK_SRC_DIR="${FRAMEWORK_SRC_DIR:-${FRAMEWORK_ROOT_DIR}/src}" +FRAMEWORK_BIN_DIR="${FRAMEWORK_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/bin}" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_VENDOR_DIR:-${FRAMEWORK_ROOT_DIR}/vendor}" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_VENDOR_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/vendor/bin}" + +# describe the functions that will be skipped from being imported +FRAMEWORK_FUNCTIONS_IGNORE_REGEXP="${FRAMEWORK_FUNCTIONS_IGNORE_REGEXP:-^(Namespace::functions|Functions::myFunction|Namespace::requireSomething|Acquire::ForceIPv4)$}" +# describe the files that do not contain function to be imported +NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/_binaries|^src/_includes|^src/batsHeaders.sh$|^src/_standalone)}" +# describe the files that are allowed to not have an associated bats file +BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" +# describe the files that are allowed to not have a function matching the filename +FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|/testsData/|^manualTests/|\.bats$}" +# Source directories +if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then + FRAMEWORK_SRC_DIRS=( + "${FRAMEWORK_ROOT_DIR}/src" + ) +fi + +# export here all the variables that will be used in your templates +export REPOSITORY_URL="${REPOSITORY_URL:-https://github.com/fchastanet/bash-tools-framework}" + +BASH_FRAMEWORK_THEME="${BASH_FRAMEWORK_THEME:-default}" +BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" +BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" +BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" +BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" + +EOF +)" + +overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" + +commandOptionParseFinished() { + # load default template framework config + defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" + echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" + local -a files=("${defaultEnvFile}") + if [[ -f "${envFile}" ]]; then + files+=("${envFile}") + fi + # shellcheck disable=SC2154 + if [[ -f "${optionBashFrameworkConfig}" ]]; then + files+=("${optionBashFrameworkConfig}") + fi + files+=("${overrideEnvFile}") + Env::requireLoad "${files[@]}" + Log::requireLoad + # shellcheck disable=SC2154 + if [[ "${optionConfig}" = "1" ]]; then + displayConfig + fi +} + + + +# option values +declare containerArg="default" +declare finalUserArg="${defaultUserArg}" +declare finalCommandArg=("${defaultCommandArg[@]}") + +declare defaultUserArg="root" +declare -a defaultCommandArg=("//bin/sh") +declare PROFILES_DIR +declare HOME_PROFILES_DIR + +beforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad + Linux::requireRealpathCommand + Assert::commandExists docker "check https://docs.docker.com/engine/install/ubuntu/" +} + +# Internal function that can be used in conf profiles to load the dsn file +loadDsn() { + local dsn="$1" + local dsnFile + dsnFile="$(Conf::getAbsoluteFile "dsn" "${dsn}" "env")" + Database::checkDsnFile "${dsnFile}" + # shellcheck source=/conf/dsn/default.local.env + # shellcheck disable=SC1091 + source "${dsnFile}" +} +export -f loadDsn + +initConf() { + PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/cliProfiles" + HOME_PROFILES_DIR="${HOME}/.bash-tools/cliProfiles" + # load default conf file + Conf::load "cliProfiles" "default" +} + +optionHelpCallback() { + cliCommandHelp + exit 0 +} + +longDescriptionFunction() { + echo -e "${__HELP_TITLE}AVAILABLE PROFILES (from ${PROFILES_DIR})${__HELP_NORMAL}" + echo -e "This list can be overridden in ${HOME_PROFILES_DIR}" + echo + Conf::getMergedList "cliProfiles" ".sh" " - " || true + echo + echo -e "${__HELP_TITLE}AVAILABLE CONTAINERS:${__HELP_NORMAL}" + Array::wrap2 ", " 80 0 "$(docker ps --format '{{.Names}}')" + echo + echo -e "${__HELP_TITLE}EXAMPLES:${__HELP_EXAMPLE}" + echo -e " to connect to mysql container in bash mode with user mysql" + echo -e " ${SCRIPT_NAME} mysql mysql '/bin/bash'" + echo -e " to connect to web container with user root" + echo -e " ${SCRIPT_NAME} web root" + echo -e "${__HELP_NORMAL}" + echo + echo -e "${__HELP_TITLE}CREATE NEW PROFILE:${__HELP_NORMAL}" + echo -e "You can create new profiles in ${HOME_PROFILES_DIR}." + echo -e "This script will be called with the" + echo -e "arguments ${__HELP_OPTION_COLOR}userArg${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}containerArg${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}commandArg${__HELP_NORMAL}" + echo -e "The script has to compute the following" + echo -e "variables ${__HELP_OPTION_COLOR}finalUserArg${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}finalContainerArg${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}finalCommandArg${__HELP_NORMAL}" +} + +containerArgHelpFunction() { + Conf::load "cliProfiles" "default" + echo " Container should be the name of a profile from profile list," + echo " check containers list below." + echo " If not provided, it will load the container specified in default configuration." + # shellcheck disable=SC2154 + echo -e " Default configuration: ${__HELP_OPTION_COLOR}${containerArg}${__HELP_NORMAL}" + # shellcheck disable=SC2154 + echo -e " Default container: ${__HELP_OPTION_COLOR}${finalContainerArg}${__HELP_NORMAL}" +} + +userArgHelpFunction() { + Conf::load "cliProfiles" "default" + echo " user to connect on this container" $'\n' + # shellcheck disable=SC2154 + echo -e " Default user: ${__HELP_OPTION_COLOR}${finalUserArg}${__HELP_NORMAL}" + echo " loaded from profile selected as first arg" + echo " or deduced from default configuration." $'\n' + echo -e " Default configuration: ${__HELP_OPTION_COLOR}${containerArg}${__HELP_NORMAL}" $'\n' + echo " if first arg is not a profile" +} + +commandArgHelpFunction() { + Conf::load "cliProfiles" "default" + echo "The command to execute" $'\n' + # shellcheck disable=SC2154 + echo -e "Default command: ${__HELP_OPTION_COLOR}${finalCommandArg[*]}${__HELP_NORMAL}" + echo " loaded from profile selected as first arg" + echo " or deduced from default configuration." + echo -e "Default configuration: ${__HELP_OPTION_COLOR}${containerArg}${__HELP_NORMAL}" $'\n' + echo "if first arg is not a profile" +} + +# shellcheck disable=SC2317 # if function is overridden +unknownOption() { + commandArg+=("$1") +} + + + +optionVersionCallback() { + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version 3.0" + Db::checkRequirements + exit 0 +} + + +# ------------------------------------------ +# Command cliCommand +# ------------------------------------------ + +# options variables initialization +declare optionHelp="0" +declare optionConfig="0" +declare optionBashFrameworkConfig="" +declare optionInfoVerbose="0" +declare optionDebugVerbose="0" +declare optionTraceVerbose="0" +declare -a optionEnvFiles=() +declare optionLogLevel="" +declare optionLogFile="" +declare optionDisplayLevel="" +declare optionNoColor="0" +declare optionTheme="default" +declare optionVersion="0" +declare optionQuiet="0" +# arguments variables initialization +declare containerArg="" +declare userArg="" +declare commandArg="" +# @description parse command options and arguments for cliCommand +cliCommandParse() { + Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" + Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" + optionHelp="0" + local -i options_parse_optionParsedCountOptionHelp + ((options_parse_optionParsedCountOptionHelp = 0)) || true + optionConfig="0" + local -i options_parse_optionParsedCountOptionConfig + ((options_parse_optionParsedCountOptionConfig = 0)) || true + optionBashFrameworkConfig="" + local -i options_parse_optionParsedCountOptionBashFrameworkConfig + ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true + optionInfoVerbose="0" + local -i options_parse_optionParsedCountOptionInfoVerbose + ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true + optionDebugVerbose="0" + local -i options_parse_optionParsedCountOptionDebugVerbose + ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true + optionTraceVerbose="0" + local -i options_parse_optionParsedCountOptionTraceVerbose + ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true + + optionLogLevel="" + local -i options_parse_optionParsedCountOptionLogLevel + ((options_parse_optionParsedCountOptionLogLevel = 0)) || true + optionLogFile="" + local -i options_parse_optionParsedCountOptionLogFile + ((options_parse_optionParsedCountOptionLogFile = 0)) || true + optionDisplayLevel="" + local -i options_parse_optionParsedCountOptionDisplayLevel + ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true + optionNoColor="0" + local -i options_parse_optionParsedCountOptionNoColor + ((options_parse_optionParsedCountOptionNoColor = 0)) || true + optionTheme="default" + local -i options_parse_optionParsedCountOptionTheme + ((options_parse_optionParsedCountOptionTheme = 0)) || true + optionVersion="0" + local -i options_parse_optionParsedCountOptionVersion + ((options_parse_optionParsedCountOptionVersion = 0)) || true + optionQuiet="0" + local -i options_parse_optionParsedCountOptionQuiet + ((options_parse_optionParsedCountOptionQuiet = 0)) || true + + containerArg="" + local -i options_parse_argParsedCountContainerArg + ((options_parse_argParsedCountContainerArg = 0)) || true + + userArg="" + local -i options_parse_argParsedCountUserArg + ((options_parse_argParsedCountUserArg = 0)) || true + + commandArg="" + local -i options_parse_argParsedCountCommandArg + ((options_parse_argParsedCountCommandArg = 0)) || true + + + # shellcheck disable=SC2034 + local -i options_parse_parsedArgIndex=0 + while (($# > 0)); do + local options_parse_arg="$1" + local argOptDefaultBehavior=0 + case "${options_parse_arg}" in + # Option 1/14 + # optionHelp alts --help|-h + # type: Boolean min 0 max 1 + --help | -h) + # shellcheck disable=SC2034 + optionHelp="1" + + if ((options_parse_optionParsedCountOptionHelp >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionHelp)) + optionHelpCallback "${options_parse_arg}" "${optionHelp}" + + ;; + + # Option 2/14 + # optionConfig alts --config + # type: Boolean min 0 max 1 + --config) + # shellcheck disable=SC2034 + optionConfig="1" + + if ((options_parse_optionParsedCountOptionConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionConfig)) + ;; + + # Option 3/14 + # optionBashFrameworkConfig alts --bash-framework-config + # type: String min 0 max 1 + --bash-framework-config) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) + # shellcheck disable=SC2034 + optionBashFrameworkConfig="$1" + optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" + + ;; + + # Option 4/14 + # optionInfoVerbose alts --verbose|-v + # type: Boolean min 0 max 1 + --verbose | -v) + # shellcheck disable=SC2034 + optionInfoVerbose="1" + + if ((options_parse_optionParsedCountOptionInfoVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionInfoVerbose)) + optionInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + updateArgListInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + ;; + + # Option 5/14 + # optionDebugVerbose alts -vv + # type: Boolean min 0 max 1 + -vv) + # shellcheck disable=SC2034 + optionDebugVerbose="1" + + if ((options_parse_optionParsedCountOptionDebugVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDebugVerbose)) + optionDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + updateArgListDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + ;; + + # Option 6/14 + # optionTraceVerbose alts -vvv + # type: Boolean min 0 max 1 + -vvv) + # shellcheck disable=SC2034 + optionTraceVerbose="1" + + if ((options_parse_optionParsedCountOptionTraceVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTraceVerbose)) + optionTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + updateArgListTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + ;; + + # Option 7/14 + # optionEnvFiles alts --env-file + # type: StringArray min 0 max -1 + --env-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + ((++options_parse_optionParsedCountOptionEnvFiles)) + optionEnvFiles+=("$1") + optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + ;; + + # Option 8/14 + # optionLogLevel alts --log-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --log-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogLevel)) + # shellcheck disable=SC2034 + optionLogLevel="$1" + optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + ;; + + # Option 9/14 + # optionLogFile alts --log-file + # type: String min 0 max 1 + --log-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogFile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogFile)) + # shellcheck disable=SC2034 + optionLogFile="$1" + optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + ;; + + # Option 10/14 + # optionDisplayLevel alts --display-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --display-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionDisplayLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDisplayLevel)) + # shellcheck disable=SC2034 + optionDisplayLevel="$1" + optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" -optionBashFrameworkConfigCallback() { - if [[ ! -f "$2" ]]; then - Log::fatal "Command ${SCRIPT_NAME} - Bash framework config file '$2' does not exists" - fi -} + ;; -defaultFrameworkConfig="$( - cat <<'EOF' -# copied from src/_includes/.framework-config.default -# shellcheck disable=SC2034 + # Option 11/14 + # optionNoColor alts --no-color + # type: Boolean min 0 max 1 + --no-color) + # shellcheck disable=SC2034 + optionNoColor="1" -REAL_SCRIPT_FILE="${REAL_SCRIPT_FILE:-$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")}" -FRAMEWORK_ROOT_DIR="${FRAMEWORK_ROOT_DIR:-${REAL_SCRIPT_FILE%/*/*}}" -FRAMEWORK_SRC_DIR="${FRAMEWORK_SRC_DIR:-${FRAMEWORK_ROOT_DIR}/src}" -FRAMEWORK_BIN_DIR="${FRAMEWORK_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/bin}" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_VENDOR_DIR:-${FRAMEWORK_ROOT_DIR}/vendor}" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_VENDOR_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/vendor/bin}" + if ((options_parse_optionParsedCountOptionNoColor >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionNoColor)) + optionNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + updateArgListNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + ;; + + # Option 12/14 + # optionTheme alts --theme + # type: String min 0 max 1 + # authorizedValues: default|default-force|noColor + --theme) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ default|default-force|noColor ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + return 1 + fi -# describe the functions that will be skipped from being imported -FRAMEWORK_FUNCTIONS_IGNORE_REGEXP="${FRAMEWORK_FUNCTIONS_IGNORE_REGEXP:-^(Namespace::functions|Functions::myFunction|Namespace::requireSomething|Acquire::ForceIPv4)$}" -# describe the files that do not contain function to be imported -NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/_binaries|^src/_includes|^src/batsHeaders.sh$|^src/_standalone)}" -# describe the files that are allowed to not have an associated bats file -BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" -# describe the files that are allowed to not have a function matching the filename -FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|\.tpl$|/testsData/|^manualTests/|\.bats$}" -# Source directories -if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then - FRAMEWORK_SRC_DIRS=( - "${FRAMEWORK_ROOT_DIR}/src" - ) -fi + if ((options_parse_optionParsedCountOptionTheme >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTheme)) + # shellcheck disable=SC2034 + optionTheme="$1" + optionThemeCallback "${options_parse_arg}" "${optionTheme}" -# export here all the variables that will be used in your templates -export REPOSITORY_URL="${REPOSITORY_URL:-https://github.com/fchastanet/bash-tools-framework}" + updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" -BASH_FRAMEWORK_THEME="${BASH_FRAMEWORK_THEME:-default}" -BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" -BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" -BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" -BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" -EOF -)" + ;; -overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" + # Option 13/14 + # optionVersion alts --version + # type: Boolean min 0 max 1 + --version) + # shellcheck disable=SC2034 + optionVersion="1" -commandOptionParseFinished() { - # load default template framework config - defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" - echo "${defaultFrameworkConfig}" > "${defaultEnvFile}" - local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi - # shellcheck disable=SC2154 - if [[ -f "${optionBashFrameworkConfig}" ]]; then - files+=("${optionBashFrameworkConfig}") - fi - files+=("${overrideEnvFile}") - Env::requireLoad "${files[@]}" - Log::requireLoad - # shellcheck disable=SC2154 - if [[ "${optionConfig}" = "1" ]]; then - displayConfig - fi -} + if ((options_parse_optionParsedCountOptionVersion >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionVersion)) + optionVersionCallback "${options_parse_arg}" "${optionVersion}" -cliCommand() { - local options_parse_cmd="$1" - shift || true + ;; - if [[ "${options_parse_cmd}" = "parse" ]]; then - local -i options_parse_optionParsedCountOptionBashFrameworkConfig - ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true - optionConfig="0" - local -i options_parse_optionParsedCountOptionConfig - ((options_parse_optionParsedCountOptionConfig = 0)) || true - optionInfoVerbose="0" - local -i options_parse_optionParsedCountOptionInfoVerbose - ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true - optionDebugVerbose="0" - local -i options_parse_optionParsedCountOptionDebugVerbose - ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true - optionTraceVerbose="0" - local -i options_parse_optionParsedCountOptionTraceVerbose - ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionNoColor="0" - local -i options_parse_optionParsedCountOptionNoColor - ((options_parse_optionParsedCountOptionNoColor = 0)) || true - optionTheme="default" - local -i options_parse_optionParsedCountOptionTheme - ((options_parse_optionParsedCountOptionTheme = 0)) || true - optionHelp="0" - local -i options_parse_optionParsedCountOptionHelp - ((options_parse_optionParsedCountOptionHelp = 0)) || true - optionVersion="0" - local -i options_parse_optionParsedCountOptionVersion - ((options_parse_optionParsedCountOptionVersion = 0)) || true - optionQuiet="0" - local -i options_parse_optionParsedCountOptionQuiet - ((options_parse_optionParsedCountOptionQuiet = 0)) || true - local -i options_parse_optionParsedCountOptionLogLevel - ((options_parse_optionParsedCountOptionLogLevel = 0)) || true - local -i options_parse_optionParsedCountOptionLogFile - ((options_parse_optionParsedCountOptionLogFile = 0)) || true - local -i options_parse_optionParsedCountOptionDisplayLevel - ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true - local -i options_parse_argParsedCountContainerArg - ((options_parse_argParsedCountContainerArg = 0)) || true - local -i options_parse_argParsedCountUserArg - ((options_parse_argParsedCountUserArg = 0)) || true - local -i options_parse_argParsedCountCommandArg - ((options_parse_argParsedCountCommandArg = 0)) || true - # shellcheck disable=SC2034 - local -i options_parse_parsedArgIndex=0 - while (($# > 0)); do - local options_parse_arg="$1" - local argOptDefaultBehavior=0 - case "${options_parse_arg}" in - # Option 1/14 - # Option optionBashFrameworkConfig --bash-framework-config variableType String min 0 max 1 authorizedValues '' regexp '' - --bash-framework-config) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) - # shellcheck disable=SC2034 - optionBashFrameworkConfig="$1" - optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" - ;; - # Option 2/14 - # Option optionConfig --config variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --config) - # shellcheck disable=SC2034 - optionConfig="1" - if ((options_parse_optionParsedCountOptionConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionConfig)) - ;; - # Option 3/14 - # Option optionInfoVerbose --verbose|-v variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --verbose | -v) - # shellcheck disable=SC2034 - optionInfoVerbose="1" - if ((options_parse_optionParsedCountOptionInfoVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionInfoVerbose)) - optionInfoVerboseCallback "${options_parse_arg}" - updateArgListInfoVerboseCallback "${options_parse_arg}" - ;; - # Option 4/14 - # Option optionDebugVerbose -vv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vv) - # shellcheck disable=SC2034 - optionDebugVerbose="1" - if ((options_parse_optionParsedCountOptionDebugVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDebugVerbose)) - optionDebugVerboseCallback "${options_parse_arg}" - updateArgListDebugVerboseCallback "${options_parse_arg}" - ;; - # Option 5/14 - # Option optionTraceVerbose -vvv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vvv) - # shellcheck disable=SC2034 - optionTraceVerbose="1" - if ((options_parse_optionParsedCountOptionTraceVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTraceVerbose)) - optionTraceVerboseCallback "${options_parse_arg}" - updateArgListTraceVerboseCallback "${options_parse_arg}" - ;; - # Option 6/14 - # Option optionEnvFiles --env-file variableType StringArray min 0 max -1 authorizedValues '' regexp '' - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - ;; - # Option 7/14 - # Option optionNoColor --no-color variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --no-color) - # shellcheck disable=SC2034 - optionNoColor="1" - if ((options_parse_optionParsedCountOptionNoColor >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionNoColor)) - optionNoColorCallback "${options_parse_arg}" - updateArgListNoColorCallback "${options_parse_arg}" - ;; - # Option 8/14 - # Option optionTheme --theme variableType String min 0 max 1 authorizedValues 'default|default-force|noColor' regexp '' - --theme) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default|default-force|noColor)" - return 1 - fi - if ((options_parse_optionParsedCountOptionTheme >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTheme)) - # shellcheck disable=SC2034 - optionTheme="$1" - optionThemeCallback "${options_parse_arg}" "${optionTheme}" - updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" - ;; - # Option 9/14 - # Option optionHelp --help|-h variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --help | -h) - # shellcheck disable=SC2034 - optionHelp="1" - if ((options_parse_optionParsedCountOptionHelp >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionHelp)) - optionHelpCallback "${options_parse_arg}" - ;; - # Option 10/14 - # Option optionVersion --version variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --version) - # shellcheck disable=SC2034 - optionVersion="1" - if ((options_parse_optionParsedCountOptionVersion >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionVersion)) - optionVersionCallback "${options_parse_arg}" - ;; - # Option 11/14 - # Option optionQuiet --quiet|-q variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --quiet | -q) - # shellcheck disable=SC2034 - optionQuiet="1" - if ((options_parse_optionParsedCountOptionQuiet >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionQuiet)) - optionQuietCallback "${options_parse_arg}" - updateArgListQuietCallback "${options_parse_arg}" - ;; - # Option 12/14 - # Option optionLogLevel --log-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --log-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + # Option 14/14 + # optionQuiet alts --quiet|-q + # type: Boolean min 0 max 1 + --quiet | -q) + # shellcheck disable=SC2034 + optionQuiet="1" + + if ((options_parse_optionParsedCountOptionQuiet >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionQuiet)) + optionQuietCallback "${options_parse_arg}" "${optionQuiet}" + + updateArgListQuietCallback "${options_parse_arg}" "${optionQuiet}" + + ;; + + -*) + unknownOption "${options_parse_arg}" || argOptDefaultBehavior=$? + ;; + *) + ((minParsedArgIndex0 = 0)) || true + ((maxParsedArgIndex0 = 0)) || true + ((minParsedArgIndex1 = minParsedArgIndex0 + 0)) || true + ((maxParsedArgIndex1 = maxParsedArgIndex0 + 1)) || true + ((minParsedArgIndex2 = minParsedArgIndex1 + 0)) || true + ((maxParsedArgIndex2 = maxParsedArgIndex1 + 1)) || true + ((minParsedArgIndex3 = minParsedArgIndex2 + 0)) || true + ((maxParsedArgIndex3 = maxParsedArgIndex2 + 1)) || true + ((incrementArg = 1 )) + if ((0)); then + # Technical if - never reached + : + + # Argument 1/3 - containerArg + # Argument containerArg min 0 max 1 + # Argument containerArg authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && + options_parse_parsedArgIndex < maxParsedArgIndex1 )); then + if ((options_parse_argParsedCountContainerArg >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument container - Maximum number of argument occurrences reached(1)" return 1 fi - ((++options_parse_optionParsedCountOptionLogLevel)) + ((++options_parse_argParsedCountContainerArg)) # shellcheck disable=SC2034 - optionLogLevel="$1" - optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - ;; - # Option 13/14 - # Option optionLogFile --log-file variableType String min 0 max 1 authorizedValues '' regexp '' - --log-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogFile >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + containerArg="${options_parse_arg}" + + + # Argument 2/3 - userArg + # Argument userArg min 0 max 1 + # Argument userArg authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex1 && + options_parse_parsedArgIndex < maxParsedArgIndex2 )); then + if ((options_parse_argParsedCountUserArg >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument user - Maximum number of argument occurrences reached(1)" return 1 fi - ((++options_parse_optionParsedCountOptionLogFile)) + ((++options_parse_argParsedCountUserArg)) # shellcheck disable=SC2034 - optionLogFile="$1" - optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" - updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" - ;; - # Option 14/14 - # Option optionDisplayLevel --display-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --display-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionDisplayLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + userArg="${options_parse_arg}" + + + # Argument 3/3 - commandArg + # Argument commandArg min 0 max 1 + # Argument commandArg authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex2 && + options_parse_parsedArgIndex < maxParsedArgIndex3 )); then + if ((options_parse_argParsedCountCommandArg >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument command - Maximum number of argument occurrences reached(1)" return 1 fi - ((++options_parse_optionParsedCountOptionDisplayLevel)) + ((++options_parse_argParsedCountCommandArg)) # shellcheck disable=SC2034 - optionDisplayLevel="$1" - optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - ;; - -*) - unknownOption "${options_parse_arg}" - ;; - *) - if ((0)); then - # Technical if - never reached - : - # Argument 1/3 - # Argument containerArg min 0 max 1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 0 && options_parse_parsedArgIndex < 1)); then - if ((options_parse_argParsedCountContainerArg >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument container - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountContainerArg)) - # shellcheck disable=SC2034 - containerArg="${options_parse_arg}" - # Argument 2/3 - # Argument userArg min 0 max 1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 1 && options_parse_parsedArgIndex < 2)); then - if ((options_parse_argParsedCountUserArg >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument user - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountUserArg)) - # shellcheck disable=SC2034 - userArg="${options_parse_arg}" - # Argument 3/3 - # Argument commandArg min 0 max 1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 2 && options_parse_parsedArgIndex < 3)); then - if ((options_parse_argParsedCountCommandArg >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument commandArg - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountCommandArg)) - # shellcheck disable=SC2034 - commandArg="${options_parse_arg}" - else - unknownOption "${options_parse_arg}" - fi + commandArg="${options_parse_arg}" + + + # else too much args + else + + + # no arg configured, call unknownArgumentCallback + + # shellcheck disable=SC2317 + unknownOption "${options_parse_arg}" + + fi + if ((incrementArg == 1)); then ((++options_parse_parsedArgIndex)) - ;; - esac - shift || true - done - commandOptionParseFinished - Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" - Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" - elif [[ "${options_parse_cmd}" = "help" ]]; then - Array::wrap2 " " 80 0 "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" "easy connection to docker container" - echo - - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "${SCRIPT_NAME}" "[OPTIONS]" "[ARGUMENTS]")" - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ - "${SCRIPT_NAME}" \ - "[--bash-framework-config ]" "[--config]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--no-color]" "[--theme ]" "[--help|-h]" "[--version]" "[--quiet|-q]" "[--log-level ]" "[--log-file ]" "[--display-level ]")" - echo - echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - echo -e " [${__HELP_OPTION_COLOR}container${__HELP_NORMAL} {single}]" - local -a helpArray - # shellcheck disable=SC2054,SC2206 - mapfile -t helpArray < <(containerArgHelpCallback) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " [${__HELP_OPTION_COLOR}user${__HELP_NORMAL} {single}]" - local -a helpArray - # shellcheck disable=SC2054,SC2206 - mapfile -t helpArray < <(userArgHelpCallback) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " [${__HELP_OPTION_COLOR}commandArg${__HELP_NORMAL} {single}]" - local -a helpArray - # shellcheck disable=SC2054,SC2206 - mapfile -t helpArray < <(commandArgHelpCallback) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(use\ alternate\ bash\ framework\ configuration.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ configuration) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(info\ level\ verbose\ mode\ \(alias\ of\ --display-level\ INFO\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(debug\ level\ verbose\ mode\ \(alias\ of\ --display-level\ DEBUG\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(trace\ level\ verbose\ mode\ \(alias\ of\ --display-level\ TRACE\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Load\ the\ specified\ env\ file\ \(deprecated\,\ please\ use\ --bash-framework-config\ option\ instead\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Produce\ monochrome\ output.\ alias\ of\ --theme\ noColor.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' - echo ' Possible values: default|default-force|noColor' - echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ this\ command\ help) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Print\ version\ information\ and\ quit) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(quiet\ mode\,\ doesn\'t\ display\ any\ output) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ file) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(set\ display\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e """ -${__HELP_TITLE}AVAILABLE PROFILES (from ${PROFILES_DIR})${__HELP_NORMAL} -This list can be overridden in ${HOME_PROFILES_DIR} - -${profilesList} - -${__HELP_TITLE}AVAILABLE CONTAINERS:${__HELP_NORMAL} -${containers} - -${__HELP_TITLE}EXAMPLES:${__HELP_EXAMPLE} - to connect to mysql container in bash mode with user mysql - ${SCRIPT_NAME} mysql mysql /bin/bash - to connect to web container with user root - ${SCRIPT_NAME} web root -${__HELP_NORMAL} - -${__HELP_TITLE}CREATE NEW PROFILE:${__HELP_NORMAL} -You can create new profiles in ${HOME_PROFILES_DIR}. -This script will be called with the -arguments ${__HELP_OPTION_COLOR}userArg${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}containerArg${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}commandArg${__HELP_NORMAL} -The script has to compute the following -variables ${__HELP_OPTION_COLOR}finalUserArg${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}finalContainerArg${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}finalCommandArg${__HELP_NORMAL}""" - echo - echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo '2.0' - echo - echo -e "${__HELP_TITLE_COLOR}AUTHOR:${__RESET_COLOR}" - echo '[François Chastanet](https://github.com/fchastanet)' - echo - echo -e "${__HELP_TITLE_COLOR}SOURCE FILE:${__RESET_COLOR}" - echo 'https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Docker/cli.sh' - echo - echo -e "${__HELP_TITLE_COLOR}LICENSE:${__RESET_COLOR}" - echo 'MIT License' - echo - Array::wrap2 ' ' 76 4 "$(copyrightCallback)" - else - Log::displayError "Command ${SCRIPT_NAME} - Option command invalid: '${options_parse_cmd}'" - return 1 - fi -} + fi + ;; + esac + shift || true + done || return $? -containerArgHelpCallback() { - Conf::load "cliProfiles" "default" - echo "container should be the name of a profile from profile list," - echo "check containers list below." - echo "If not provided, it will load the container specified in default configuration." - echo "Default configuration: ${__HELP_OPTION_COLOR}${containerArg}${__HELP_NORMAL}" - echo "Default container: ${__HELP_OPTION_COLOR}${finalContainerArg}${__HELP_NORMAL}" -} + commandOptionParseFinished -userArgHelpCallback() { - Conf::load "cliProfiles" "default" - echo "user to connect on this container" $'\n' - echo "Default user: ${__HELP_OPTION_COLOR}${finalUserArg}${__HELP_NORMAL}" - echo " loaded from profile selected as first arg" - echo " or deduced from default configuration." $'\n' - echo "Default configuration: ${__HELP_OPTION_COLOR}${containerArg}${__HELP_NORMAL}" $'\n' - echo "if first arg is not a profile" } -commandArgHelpCallback() { - Conf::load "cliProfiles" "default" - echo "The command to execute" $'\n' - echo "Default command: ${__HELP_OPTION_COLOR}${finalCommandArg[*]}${__HELP_NORMAL}" - echo " loaded from profile selected as first arg" - echo " or deduced from default configuration." - echo "Default configuration: ${__HELP_OPTION_COLOR}${containerArg}${__HELP_NORMAL}" $'\n' - echo "if first arg is not a profile" -} +# @description display command options and arguments help for cliCommand +cliCommandHelp() { + echo -e "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" + Array::wrap2 ' ' 76 4 " " "Easy connection to docker container." -optionHelpCallback() { - local containers - # shellcheck disable=SC2046 - containers="$(Array::wrap2 ", " 80 0 $(docker ps --format '{{.Names}}'))" - local profilesList="" - Conf::load "cliProfiles" "default" + echo + echo + + # ------------------------------------------ + # usage section + # ------------------------------------------ + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "cli [OPTIONS] [ARGUMENTS]" + echo + # ------------------------------------------ + # usage/options section + # ------------------------------------------ + optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" + ) + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ + "cli" "${optionsAltList[@]}" + echo - profilesList="$(Conf::getMergedList "cliProfiles" ".sh" " - " || true)" + # ------------------------------------------ + # usage/arguments section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - cliCommand help | envsubst - exit 0 -} + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}container${__HELP_NORMAL} {single}] + " + containerArgHelpFunction -# shellcheck disable=SC2317 # if function is overridden -unknownOption() { - commandArg+=("$1") -} + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}user${__HELP_NORMAL} {single}] + " + userArgHelpFunction -cliCommand parse "${BASH_FRAMEWORK_ARGV[@]}" + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}command${__HELP_NORMAL} {single}] + " + commandArgHelpFunction -run() { - # Internal function that can be used in conf profiles to load the dsn file - loadDsn() { - local dsn="$1" - local dsnFile - dsnFile="$(Conf::getAbsoluteFile "dsn" "${dsn}" "env")" - Database::checkDsnFile "${dsnFile}" - # shellcheck source=/conf/dsn/default.local.env - # shellcheck disable=SC1091 - source "${dsnFile}" - } - export -f loadDsn + # ------------------------------------------ + # options section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays this command help" + echo - # check dependencies - Assert::commandExists docker "check https://docs.docker.com/engine/install/ubuntu/" - # load default conf file - Conf::load "cliProfiles" "default" - # try to load config file associated to container if provided - if [[ -n "${containerArg}" ]]; then - Conf::load "cliProfiles" "${containerArg}" || { - # conf file not existing fallback to provided args or to default ones if not provided - finalContainerArg="${containerArg}" - finalUserArg=${userArg:-${finalUserArg}} - finalCommandArg=("${commandArg[@]:-${finalCommandArg[@]}}") - } - fi + echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays configuration" + echo - declare -a cmd=() - if Assert::windows; then - # open tty for git bash - cmd+=(winpty) - fi - INTERACTIVE_MODE="-i" - if ! read -r -t 0; then - # command is not piped or TTY not available - INTERACTIVE_MODE+="t" - fi - cmd+=(docker) - cmd+=(exec) - cmd+=("${INTERACTIVE_MODE}") - # ensure column/lines will be updated upon terminal resize - cmd+=(-e) - cmd+=("COLUMNS=$(tput cols)") - cmd+=(-e) - cmd+=("LINES=$(tput lines)") - - cmd+=("--user=${finalUserArg}") - cmd+=("${finalContainerArg}") - cmd+=("${finalCommandArg[@]}") - if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "0" ]]; then - (echo >&2 MSYS_NO_PATHCONV=1 MSYS2_ARG_CONV_EXCL='*' "${cmd[@]}") - fi - MSYS_NO_PATHCONV=1 MSYS2_ARG_CONV_EXCL='*' "${cmd[@]}" + + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" + Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log file" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set display level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." + echo + Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" + echo + + Array::wrap2 ' ' 76 6 " Default value: " "default" + echo + + echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Print version information and quit." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." + echo + + + # ------------------------------------------ + # longDescription section + # ------------------------------------------ + echo + echo + echo -e "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" + longDescriptionFunction + # ------------------------------------------ + # version section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" + echo "3.0" + # ------------------------------------------ + # author section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}AUTHOR: ${__RESET_COLOR}" + echo "[François Chastanet](https://github.com/fchastanet)" + # ------------------------------------------ + # sourceFile section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Docker/cli/binary-cli.yaml" + # ------------------------------------------ + # license section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}LICENSE: ${__RESET_COLOR}" + echo "MIT License" + # ------------------------------------------ + # copyright section + # ------------------------------------------ + Array::wrap2 ' ' 76 0 "$(copyrightCallback)" } -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run + +beforeParseCallback +initConf + +cliCommandParse "$@" +MAIN_FUNCTION_NAME="main" +main() { + +# try to load config file associated to container if provided +if [[ -n "${containerArg}" ]]; then + Conf::load "cliProfiles" "${containerArg}" || { + # conf file not existing fallback to provided args or to default ones if not provided + finalContainerArg="${containerArg}" + finalUserArg=${userArg:-${finalUserArg}} + finalCommandArg=("${commandArg[@]:-${finalCommandArg[@]}}") + } +fi + +declare -a cmd=() +if Assert::windows; then + # open tty for git bash + cmd+=( + MSYS_NO_PATHCONV=1 + MSYS2_ARG_CONV_EXCL='*' + winpty + ) +fi +INTERACTIVE_MODE="-i" +if ! read -r -t 0; then + # command is not piped or TTY not available + INTERACTIVE_MODE+="t" fi +cmd+=(docker) +cmd+=(exec) +cmd+=("${INTERACTIVE_MODE}") +# ensure column/lines will be updated upon terminal resize +cmd+=(-e) +cmd+=("COLUMNS=$(tput cols)") +cmd+=(-e) +cmd+=("LINES=$(tput lines)") + +cmd+=("--user=${finalUserArg}") +cmd+=("${finalContainerArg}") +cmd+=("${finalCommandArg[@]}") + +( + if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "0" ]]; then + set -x + fi + "${cmd[@]}" +) + } -facade_main_clish "$@" +# if file is sourced avoid calling main function +# shellcheck disable=SC2178 +BASH_SOURCE=".$0" # cannot be changed in bash +# shellcheck disable=SC2128 +if test ".$0" == ".${BASH_SOURCE}"; then + if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then + main "$@" &>/dev/null + else + main "$@" + fi +fi diff --git a/bin/dbImport b/bin/dbImport index 6d324390..0a8f0e79 100755 --- a/bin/dbImport +++ b/bin/dbImport @@ -1430,10 +1430,8 @@ beforeParseCallback() { } copyrightCallback() { - if [[ -z "${copyrightBeginYear}" ]]; then - copyrightBeginYear="$(date +%Y)" - fi - echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" + # + echo "Copyright (c) 2020-now François Chastanet" } # shellcheck disable=SC2317 # if function is overridden @@ -1472,7 +1470,7 @@ optionHelpCallback() { # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { # shellcheck disable=SC2154 - echo "${SCRIPT_NAME} version ${versionNumber}" + echo "${SCRIPT_NAME} version 3.0" exit 0 } @@ -1679,10 +1677,6 @@ commandOptionParseFinished() { # shellcheck disable=SC2034 -declare copyrightBeginYear="2020" -# shellcheck disable=SC2034 -declare versionNumber="2.0" - declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" declare defaultTargetCharacterSet="" @@ -1862,7 +1856,7 @@ optionTablesCallback() { optionVersionCallback() { # shellcheck disable=SC2154 - echo "${SCRIPT_NAME} version ${versionNumber}" + echo "${SCRIPT_NAME} version 3.0" Db::checkRequirements exit 0 } @@ -2709,7 +2703,7 @@ dbImportCommandHelp() { # ------------------------------------------ echo echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo "2.0" + echo "3.0" # ------------------------------------------ # author section # ------------------------------------------ diff --git a/bin/dbImportProfile b/bin/dbImportProfile index b6272ae4..a69c4ca6 100755 --- a/bin/dbImportProfile +++ b/bin/dbImportProfile @@ -1343,10 +1343,8 @@ beforeParseCallback() { } copyrightCallback() { - if [[ -z "${copyrightBeginYear}" ]]; then - copyrightBeginYear="$(date +%Y)" - fi - echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" + # + echo "Copyright (c) 2021-now François Chastanet" } # shellcheck disable=SC2317 # if function is overridden @@ -1385,7 +1383,7 @@ optionHelpCallback() { # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { # shellcheck disable=SC2154 - echo "${SCRIPT_NAME} version ${versionNumber}" + echo "${SCRIPT_NAME} version 3.0" exit 0 } @@ -1592,10 +1590,6 @@ commandOptionParseFinished() { declare defaultFromDsn="default.remote" # shellcheck disable=SC2034 -declare versionNumber="2.0" -# shellcheck disable=SC2034 -declare copyrightBeginYear="2020" -# shellcheck disable=SC2034 declare PROFILES_DIR declare HOME_PROFILES_DIR @@ -1678,7 +1672,7 @@ dbImportProfileCommandCallback() { optionVersionCallback() { # shellcheck disable=SC2154 - echo "${SCRIPT_NAME} version ${versionNumber}" + echo "${SCRIPT_NAME} version 3.0" Db::checkRequirements exit 0 } @@ -2356,7 +2350,7 @@ dbImportProfileCommandHelp() { # ------------------------------------------ echo echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo "2.0" + echo "3.0" # ------------------------------------------ # author section # ------------------------------------------ diff --git a/bin/dbImportStream b/bin/dbImportStream index 4f65ab2b..c974d0ca 100755 --- a/bin/dbImportStream +++ b/bin/dbImportStream @@ -1337,10 +1337,8 @@ beforeParseCallback() { } copyrightCallback() { - if [[ -z "${copyrightBeginYear}" ]]; then - copyrightBeginYear="$(date +%Y)" - fi - echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" + # + echo "Copyright (c) 2020-now François Chastanet" } # shellcheck disable=SC2317 # if function is overridden @@ -1379,7 +1377,7 @@ optionHelpCallback() { # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { # shellcheck disable=SC2154 - echo "${SCRIPT_NAME} version ${versionNumber}" + echo "${SCRIPT_NAME} version 3.0" exit 0 } @@ -1588,10 +1586,6 @@ commandOptionParseFinished() { declare PROFILES_DIR declare HOME_PROFILES_DIR # shellcheck disable=SC2034 -declare versionNumber="2.0" -# shellcheck disable=SC2034 -declare copyrightBeginYear="2020" -# shellcheck disable=SC2034 declare defaultFromDsn="default.remote" beforeParseCallback() { @@ -1723,7 +1717,7 @@ optionTablesCallback() { optionVersionCallback() { # shellcheck disable=SC2154 - echo "${SCRIPT_NAME} version ${versionNumber}" + echo "${SCRIPT_NAME} version 3.0" Db::checkRequirements exit 0 } @@ -2469,7 +2463,7 @@ dbImportStreamCommandHelp() { # ------------------------------------------ echo echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo "2.0" + echo "3.0" # ------------------------------------------ # author section # ------------------------------------------ diff --git a/bin/dbQueryAllDatabases b/bin/dbQueryAllDatabases index ecee877b..dfcfb57d 100755 --- a/bin/dbQueryAllDatabases +++ b/bin/dbQueryAllDatabases @@ -1387,10 +1387,8 @@ beforeParseCallback() { } copyrightCallback() { - if [[ -z "${copyrightBeginYear}" ]]; then - copyrightBeginYear="$(date +%Y)" - fi - echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" + # + echo "Copyright (c) 2020-now François Chastanet" } # shellcheck disable=SC2317 # if function is overridden @@ -1429,7 +1427,7 @@ optionHelpCallback() { # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { # shellcheck disable=SC2154 - echo "${SCRIPT_NAME} version ${versionNumber}" + echo "${SCRIPT_NAME} version 3.0" exit 0 } @@ -1665,7 +1663,7 @@ optionJobsCallback() { optionVersionCallback() { # shellcheck disable=SC2154 - echo "${SCRIPT_NAME} version ${versionNumber}" + echo "${SCRIPT_NAME} version 3.0" Db::checkRequirements exit 0 } @@ -1677,8 +1675,6 @@ declare HOME_QUERIES_DIR declare queryIsFile="0" declare optionSeparator="|" -# shellcheck disable=SC2034 -declare versionNumber="2.0" # shellcheck disable=SC2034 declare copyrightBeginYear="2020" # shellcheck disable=SC2034 @@ -2479,7 +2475,7 @@ dbQueryAllDatabasesCommandHelp() { # ------------------------------------------ echo echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo "2.0" + echo "3.0" # ------------------------------------------ # author section # ------------------------------------------ diff --git a/bin/dbScriptAllDatabases b/bin/dbScriptAllDatabases index 3fa881ac..b1a7f381 100755 --- a/bin/dbScriptAllDatabases +++ b/bin/dbScriptAllDatabases @@ -1340,10 +1340,8 @@ beforeParseCallback() { } copyrightCallback() { - if [[ -z "${copyrightBeginYear}" ]]; then - copyrightBeginYear="$(date +%Y)" - fi - echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" + # + echo "Copyright (c) 2020-now François Chastanet" } # shellcheck disable=SC2317 # if function is overridden @@ -1382,7 +1380,7 @@ optionHelpCallback() { # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { # shellcheck disable=SC2154 - echo "${SCRIPT_NAME} version ${versionNumber}" + echo "${SCRIPT_NAME} version 3.0" exit 0 } @@ -1600,10 +1598,6 @@ beforeParseCallback() { declare SCRIPTS_DIR declare HOME_SCRIPTS_DIR # shellcheck disable=SC2034 -declare versionNumber="2.0" -# shellcheck disable=SC2034 -declare copyrightBeginYear="2020" -# shellcheck disable=SC2034 declare defaultFromDsn="default.remote" # shellcheck disable=SC2034 declare outputDirectory="${HOME}/.bash-tools/output" @@ -1724,7 +1718,7 @@ optionJobsCallback() { optionVersionCallback() { # shellcheck disable=SC2154 - echo "${SCRIPT_NAME} version ${versionNumber}" + echo "${SCRIPT_NAME} version 3.0" Db::checkRequirements exit 0 } @@ -2513,7 +2507,7 @@ dbScriptAllDatabasesCommandHelp() { # ------------------------------------------ echo echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo "2.0" + echo "3.0" # ------------------------------------------ # author section # ------------------------------------------ diff --git a/bin/doc b/bin/doc index 45cc4093..b251c67d 100755 --- a/bin/doc +++ b/bin/doc @@ -1105,10 +1105,8 @@ beforeParseCallback() { } copyrightCallback() { - if [[ -z "${copyrightBeginYear}" ]]; then - copyrightBeginYear="$(date +%Y)" - fi - echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" + # + echo "Copyright (c) 2022-now François Chastanet" } # shellcheck disable=SC2317 # if function is overridden @@ -1147,7 +1145,7 @@ optionHelpCallback() { # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { # shellcheck disable=SC2154 - echo "${SCRIPT_NAME} version ${versionNumber}" + echo "${SCRIPT_NAME} version 3.0" exit 0 } @@ -1364,10 +1362,6 @@ updateOptionContinuousIntegrationMode() { # shellcheck disable=SC2034 -declare copyrightBeginYear="2020" -# shellcheck disable=SC2034 -declare versionNumber="1.1" - declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" optionHelpCallback() { @@ -1902,7 +1896,7 @@ docCommandHelp() { # ------------------------------------------ echo echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo "1.1" + echo "3.0" # ------------------------------------------ # author section # ------------------------------------------ diff --git a/bin/install b/bin/install deleted file mode 100755 index 8fc804fd..00000000 --- a/bin/install +++ /dev/null @@ -1,1712 +0,0 @@ -#!/usr/bin/env bash -############################################################################### -# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/build/install/binary-install.yaml -# DO NOT EDIT IT -# @generated -############################################################################### -# shellcheck disable=SC2288,SC2034 - - - -# ensure that no user aliases could interfere with -# commands used in this script -unalias -a || true -shopt -u expand_aliases - -# shellcheck disable=SC2034 -((failures = 0)) || true - -# Bash will remember & return the highest exit code in a chain of pipes. -# This way you can catch the error inside pipes, e.g. mysqldump | gzip -set -o pipefail -set -o errexit - -# Command Substitution can inherit errexit option since bash v4.4 -shopt -s inherit_errexit || true - -# if set, and job control is not active, the shell runs the last command -# of a pipeline not executed in the background in the current shell -# environment. -shopt -s lastpipe - -# a log is generated when a command fails -set -o errtrace - -# use nullglob so that (file*.php) will return an empty array if no file -# matches the wildcard -shopt -s nullglob - -# ensure regexp are interpreted without accentuated characters -export LC_ALL=POSIX - -export TERM=xterm-256color - -# avoid interactive install -export DEBIAN_FRONTEND=noninteractive -export DEBCONF_NONINTERACTIVE_SEEN=true - -# store command arguments for later usage -# shellcheck disable=SC2034 -declare -a BASH_FRAMEWORK_ARGV=("$@") -# shellcheck disable=SC2034 -declare -a ORIGINAL_BASH_FRAMEWORK_ARGV=("$@") - -# @see https://unix.stackexchange.com/a/386856 -# shellcheck disable=SC2317 -interruptManagement() { - # restore SIGINT handler - trap - INT - # ensure that Ctrl-C is trapped by this script and not by sub process - # report to the parent that we have indeed been interrupted - kill -s INT "$$" -} -trap interruptManagement INT - -################################################ -# Temp dir management -################################################ - -KEEP_TEMP_FILES="${KEEP_TEMP_FILES:-0}" -export KEEP_TEMP_FILES - -# PERSISTENT_TMPDIR is not deleted by traps -PERSISTENT_TMPDIR="${TMPDIR:-/tmp}/bash-framework" -export PERSISTENT_TMPDIR -if [[ ! -d "${PERSISTENT_TMPDIR}" ]]; then - mkdir -p "${PERSISTENT_TMPDIR}" -fi - -# shellcheck disable=SC2034 -TMPDIR="$(mktemp -d -p "${PERSISTENT_TMPDIR:-/tmp}" -t bash-framework-$$-XXXXXX)" -export TMPDIR - -# temp dir cleaning -# shellcheck disable=SC2317 -cleanOnExit() { - local rc=$? - if [[ "${KEEP_TEMP_FILES:-0}" = "1" ]]; then - Log::displayInfo "KEEP_TEMP_FILES=1 temp files kept here '${TMPDIR}'" - elif [[ -n "${TMPDIR+xxx}" ]]; then - Log::displayDebug "KEEP_TEMP_FILES=0 removing temp files '${TMPDIR}'" - rm -Rf "${TMPDIR:-/tmp/fake}" >/dev/null 2>&1 - fi - exit "${rc}" -} -trap cleanOnExit EXIT HUP QUIT ABRT TERM - - -SCRIPT_NAME=${0##*/} -REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" -if [[ -n "${EMBED_CURRENT_DIR}" ]]; then - CURRENT_DIR="${EMBED_CURRENT_DIR}" -else - CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" -fi -FRAMEWORK_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" -FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" -FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" - -# @description Log namespace provides 2 kind of functions -# - Log::display* allows to display given message with -# given display level -# - Log::log* allows to log given message with -# given log level -# Log::display* functions automatically log the message too -# @see Env::requireLoad to load the display and log level from .env file - -# @description log level off -export __LEVEL_OFF=0 -# @description log level error -export __LEVEL_ERROR=1 -# @description log level warning -export __LEVEL_WARNING=2 -# @description log level info -export __LEVEL_INFO=3 -# @description log level success -export __LEVEL_SUCCESS=3 -# @description log level debug -export __LEVEL_DEBUG=4 - -# @description verbose level off -export __VERBOSE_LEVEL_OFF=0 -# @description verbose level info -export __VERBOSE_LEVEL_INFO=1 -# @description verbose level info -export __VERBOSE_LEVEL_DEBUG=2 -# @description verbose level info -export __VERBOSE_LEVEL_TRACE=3 - - -# @description concatenate each element of an array with a separator -# but wrapping text when line length is more than provided argument -# The algorithm will try not to cut the array element if it can. -# - if an arg can be placed on current line it will be, -# otherwise current line is printed and arg is added to the new -# current line -# - Empty arg is interpreted as a new line. -# - Add \r to arg in order to force break line and avoid following -# arg to be concatenated with current arg. -# -# @arg $1 glue:String -# @arg $2 maxLineLength:int -# @arg $3 indentNextLine:int -# @arg $@ array:String[] -Array::wrap2() { - local glue="${1-}" - local -i glueLength="${#glue}" - shift || true - local -i maxLineLength=$1 - shift || true - local -i indentNextLine=$1 - shift || true - local indentStr="" - if ((indentNextLine > 0)); then - indentStr="$(head -c "${indentNextLine}" 0)); do - arg="$1" - shift || true - - # replace tab by 2 spaces - arg="${arg//$'\t'/ }" - # remove trailing spaces - arg="${arg%[[:blank:]]}" - if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then - printCurrentLine - ((previousLineEmpty = 1)) - continue - else - if ((previousLineEmpty == 1)); then - printCurrentLine - fi - ((previousLineEmpty = 0)) || true - fi - # convert eol to args - mapfile -t additionalLines <<<"${arg}" - if ((${#additionalLines[@]} > 1)); then - set -- "${additionalLines[@]}" "$@" - continue - fi - - ((argLength = ${#arg})) || true - - # empty arg - if ((argLength == 0)); then - if ((isNewline == 0)); then - # isNewline = 0 means currentLine is not empty - printCurrentLine - fi - continue - fi - - if ((isNewline == 0)); then - glueLength="${#glue}" - else - glueLength="0" - fi - if ((currentLineLength + argLength + glueLength > maxLineLength)); then - if ((argLength + glueLength > maxLineLength)); then - # arg is too long to even fit on one line - # we have to split the arg on current and next line - local -i remainingLineLength - ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) - appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" - printCurrentLine - arg="${arg:${remainingLineLength}}" - # remove leading spaces - arg="${arg##[[:blank:]]}" - - set -- "${arg}" "$@" - else - # the arg can fit on next line - printCurrentLine - appendToCurrentLine "${arg}" "${argLength}" - fi - else - appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" - fi - done - if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then - printCurrentLine - fi - ) | sed -E -e 's/[[:blank:]]+$//' -} - - -# @description check if command specified exists or return 1 -# with error and message if not -# -# @arg $1 commandName:String on which existence must be checked -# @arg $2 helpIfNotExists:String a help command to display if the command does not exist -# -# @exitcode 1 if the command specified does not exist -# @stderr diagnostic information + help if second argument is provided -Assert::commandExists() { - local commandName="$1" - local helpIfNotExists="$2" - - "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { - Log::displayError "${commandName} is not installed, please install it" - if [[ -n "${helpIfNotExists}" ]]; then - Log::displayInfo "${helpIfNotExists}" - fi - return 1 - } - return 0 -} - - -# @description check if tty (interactive mode) is active -# @noargs -# @exitcode 1 if tty not active -# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive -# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive -Assert::tty() { - if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then - return 1 - fi - if [[ "${INTERACTIVE:-0}" = "1" ]]; then - return 0 - fi - tty -s -} - - -# @description Backup given directory in the base directory or in BACKUP_DIR directory -# backup directory name is composed by following fields separated by _: -# - if BACKUP_DIR is not empty then escaped full dir name separated by @ -# - date with format %Y%m%d_%H:%M:%S (Eg: 20240326_14:45:08) -# - .tgz extension -# -# @arg $1 string the base directory -# @arg $2 string the directory to backup from base directory -# -# @env BACKUP_DIR String variable referencing backup directory -# @env FRAMEWORK_ROOT_DIR String used to remove this project directory from displayed backup path -# @env SUDO String allows to use custom sudo prefix command -# -# @exitcode 1 if directory to backup does not exist, 0 if everything is OK -# @exitcode 2 on backup failure -# -# @stderr message about where the directory is backed up -Backup::dir() { - - if [[ "${REQUIRE_FUNCTION_LINUX_REQUIRE_TAR_COMMAND_LOADED:-0}" != 1 ]]; then - echo >&2 "Requirement Linux::requireTarCommand has not been loaded" - exit 1 - fi - - local escapedDirname backupFile - local fromDir="$1" - local dirname="$2" - - if [[ ! -d "${fromDir}/${dirname}" ]]; then - Log::displayError "Backup::dir - directory '${fromDir}/${dirname}' doesn't exist" - return 1 - fi - if [[ -z "${BACKUP_DIR:-}" ]]; then - escapedDirname="$(sed -e 's#^/##; s#/#@#g' <<<"${dirname}")" - backupFile="${fromDir}/${escapedDirname}-$(date +"%Y%m%d_%H:%M:%S").tgz" - else - escapedDirname="$(sed -e 's#^/##; s#/#@#g' <<<"${fromDir}/${dirname}")" - backupFile="${BACKUP_DIR}/${escapedDirname}-$(date +"%Y%m%d_%H:%M:%S").tgz" - fi - - Log::displayInfo "Backup directory '${fromDir}/${dirname}' to ${backupFile}" - if ! ${SUDO:-} tar --same-owner -czf "${backupFile}" "${fromDir}/${dirname}" 2>/dev/null; then - Log::displayError "cannot backup '${fromDir}/${dirname}'" - return 2 - fi -} - - -# @description ensure env files are loaded -# @arg $@ list of default files to load at the end -# @exitcode 1 if one of env files fails to load -# @stderr diagnostics information is displayed -# shellcheck disable=SC2120 -Env::requireLoad() { - export REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 - - local -a defaultFiles=("$@") - # get list of possible config files - local -a configFiles=() - if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then - # BASH_FRAMEWORK_ENV_FILES is an array - configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") - fi - local localFrameworkConfigFile - localFrameworkConfigFile="$(pwd)/.framework-config" - if [[ -f "${localFrameworkConfigFile}" ]]; then - configFiles+=("${localFrameworkConfigFile}") - fi - if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then - configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") - fi - configFiles+=("${optionEnvFiles[@]}") - configFiles+=("${defaultFiles[@]}") - - for file in "${configFiles[@]}"; do - # shellcheck source=/.framework-config - CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { - Log::displayError "while loading config file: ${file}" - return 1 - } - done -} - - -# @description create a temp file using default TMPDIR variable -# @env TMPDIR String (default value /tmp) -# @arg $1 templateName:String template name to use(optional) -Framework::createTempFile() { - mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" -} - - -# @description ensure running user is not root -# @exitcode 1 if current user is root -# @stderr diagnostics information is displayed -Linux::requireExecutedAsUser() { - if [[ "$(id -u)" = "0" ]]; then - Log::fatal "this script should be executed as normal user" - fi -} - - -# @description ensure command tar is available -# @exitcode 1 if tar command not available -# @stderr diagnostics information is displayed -Linux::requireTarCommand() { - export REQUIRE_FUNCTION_LINUX_REQUIRE_TAR_COMMAND_LOADED=1 - - Assert::commandExists tar -} - - -declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR -FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" -LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" -LOG_LAST_LOG_DATE_INIT=1 -LOG_LAST_DURATION_STR="" - -# @description compute duration since last call to this function -# the result is set in following env variables. -# in ss.sss (seconds followed by milliseconds precision 3 decimals) -# @noargs -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @set LOG_LAST_LOG_DATE_INIT int (default 1) set to 0 at first call, allows to detect reference log -# @set LOG_LAST_DURATION_STR String the last duration displayed -# @set LOG_LAST_LOG_DATE String the last log date that will be used to compute next diff -Log::computeDuration() { - if ((${DISPLAY_DURATION:-0} == 1)); then - local -i duration=0 - local -i delta=0 - local -i currentLogDate - currentLogDate="${EPOCHREALTIME/[^0-9]/}" - if ((LOG_LAST_LOG_DATE_INIT == 1)); then - LOG_LAST_LOG_DATE_INIT=0 - LOG_LAST_DURATION_STR="Ref" - else - duration=$(((currentLogDate - FIRST_LOG_DATE) / 1000000)) - delta=$(((currentLogDate - LOG_LAST_LOG_DATE) / 1000000)) - LOG_LAST_DURATION_STR="${duration}s/+${delta}s" - fi - LOG_LAST_LOG_DATE="${currentLogDate}" - # shellcheck disable=SC2034 - local microSeconds="${EPOCHREALTIME#*.}" - LOG_LAST_DURATION_STR="$(printf '%(%T)T.%03.0f\n' "${EPOCHSECONDS}" "${microSeconds:0:3}")(${LOG_LAST_DURATION_STR}) - " - else - # shellcheck disable=SC2034 - LOG_LAST_DURATION_STR="" - fi -} - - -# @description Display message using debug color (gray) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayDebug() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then - Log::computeDuration - echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logDebug "$1" -} - - -# @description Display message using error color (red) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayError() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then - Log::computeDuration - echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logError "$1" -} - - -# @description Display message using info color (bg light blue/fg white) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayInfo() { - local type="${2:-INFO}" - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logInfo "$1" "${type}" -} - - -# @description Display message using skip color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displaySkipped() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__SKIPPED_COLOR}SKIPPED - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logSkipped "$1" -} - - -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logWarning "$1" -} - - -# @description Display message using error color (red) and exit immediately with error status 1 -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::fatal() { - Log::computeDuration - echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - Log::logFatal "$1" - exit 1 -} - - -# @description log message to file -# @arg $1 message:String the message to display -Log::logDebug() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then - Log::logMessage "${2:-DEBUG}" "$1" - fi -} - - -# @description log message to file -# @arg $1 message:String the message to display -Log::logError() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_ERROR)); then - Log::logMessage "${2:-ERROR}" "$1" - fi -} - - -# @description log message to file -# @arg $1 message:String the message to display -Log::logFatal() { - Log::logMessage "${2:-FATAL}" "$1" -} - - -# @description log message to file -# @arg $1 message:String the message to display -Log::logInfo() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then - Log::logMessage "${2:-INFO}" "$1" - fi -} - - -# @description Internal: common log message -# @example text -# [date]|[levelMsg]|message -# -# @example text -# 2020-01-19 19:20:21|ERROR |log error -# 2020-01-19 19:20:21|SKIPPED|log skipped -# -# @arg $1 levelMsg:String message's level description (eg: STATUS, ERROR, ...) -# @arg $2 msg:String the message to display -# @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty -# @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages -# @stderr diagnostics information is displayed -Log::logMessage() { - - if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then - echo >&2 "Requirement Env::requireLoad has not been loaded" - exit 1 - fi - - if [[ "${REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then - echo >&2 "Requirement Log::requireLoad has not been loaded" - exit 1 - fi - - local levelMsg="$1" - local msg="$2" - local date - - if [[ -n "${BASH_FRAMEWORK_LOG_FILE}" ]] && ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - date="$(date '+%Y-%m-%d %H:%M:%S')" - touch "${BASH_FRAMEWORK_LOG_FILE}" - printf "%s|%7s|%s\n" "${date}" "${levelMsg}" "${msg}" >>"${BASH_FRAMEWORK_LOG_FILE}" - fi -} - - -# @description log message to file -# @arg $1 message:String the message to display -Log::logSkipped() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then - Log::logMessage "${2:-SKIPPED}" "$1" - fi -} - - -# @description log message to file -# @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" - fi -} - - -# @description activate or not Log::display* and Log::log* functions -# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL -# environment variables loaded by Env::requireLoad -# try to create log file and rotate it if necessary -# @noargs -# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable -# @env BASH_FRAMEWORK_DISPLAY_LEVEL int -# @env BASH_FRAMEWORK_LOG_LEVEL int -# @env BASH_FRAMEWORK_LOG_FILE String -# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 -# @exitcode 0 always successful -# @stderr diagnostics information about log file is displayed -Log::requireLoad() { - export REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 - - - if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then - echo >&2 "Requirement Env::requireLoad has not been loaded" - exit 1 - fi - - if [[ "${REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED:-0}" != 1 ]]; then - echo >&2 "Requirement UI::requireTheme has not been loaded" - exit 1 - fi - - if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - export BASH_FRAMEWORK_LOG_LEVEL - fi - - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then - if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 - fi - elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - fi - - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - # will always be created even if not in info level - Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" - if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then - Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" - fi - fi -} - - -# @description To be called before logging in the log file -# @arg $1 file:string log file name -# @arg $2 maxLogFilesCount:int maximum number of log files -Log::rotate() { - local file="$1" - local maxLogFilesCount="${2:-5}" - - if [[ ! -f "${file}" ]]; then - Log::displayDebug "Log file ${file} doesn't exist yet" - return 0 - fi - local i - for ((i = maxLogFilesCount - 1; i > 0; i--)); do - Log::displayInfo "Log rotation ${file}.${i} to ${file}.$((i + 1))" - mv "${file}."{"${i}","$((i + 1))"} &>/dev/null || true - done - if cp "${file}" "${file}.1" &>/dev/null; then - echo >"${file}" # reset log file - Log::displayInfo "Log rotation ${file} to ${file}.1" - fi -} - - -# @description draw a line with the character passed in parameter repeated depending on terminal width -# @arg $1 character:String character to use as separator (default value #) -UI::drawLine() { - local character="${1:-#}" - local -i width=${COLUMNS:-0} - if ((width == 0)) && [[ -t 1 ]]; then - width=$(tput cols) - fi - if ((width == 0)); then - width=80 - fi - printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") - echo -} - - -# @description load color theme -# @noargs -# @env BASH_FRAMEWORK_THEME String theme to use -# @env LOAD_THEME int 0 to avoid loading theme -# @exitcode 0 always successful -UI::requireTheme() { - export REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 - - if [[ "${LOAD_THEME:-1}" = "1" ]]; then - UI::theme "${BASH_FRAMEWORK_THEME-default}" - fi -} - - -# @description load colors theme constants -# @warning if tty not opened, noColor theme will be chosen -# @arg $1 theme:String the theme to use (default, noColor) -# @arg $@ args:String[] -# @set __ERROR_COLOR String indicate error status -# @set __INFO_COLOR String indicate info status -# @set __SUCCESS_COLOR String indicate success status -# @set __WARNING_COLOR String indicate warning status -# @set __SKIPPED_COLOR String indicate skipped status -# @set __DEBUG_COLOR String indicate debug status -# @set __HELP_COLOR String indicate help status -# @set __TEST_COLOR String not used -# @set __TEST_ERROR_COLOR String not used -# @set __HELP_TITLE_COLOR String used to display help title in help strings -# @set __HELP_OPTION_COLOR String used to display highlight options in help strings -# -# @set __RESET_COLOR String reset default color -# -# @set __HELP_EXAMPLE String to remove -# @set __HELP_TITLE String to remove -# @set __HELP_NORMAL String to remove -# shellcheck disable=SC2034 -UI::theme() { - local theme="${1-default}" - if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then - theme="noColor" - fi - case "${theme}" in - default | default-force) - theme="default" - ;; - noColor) ;; - *) - Log::fatal "invalid theme provided" - ;; - esac - if [[ "${theme}" = "default" ]]; then - BASH_FRAMEWORK_THEME="default" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='\e[31m' # Red - __INFO_COLOR='\e[44m' # white on lightBlue - __SUCCESS_COLOR='\e[32m' # Green - __WARNING_COLOR='\e[33m' # Yellow - __SKIPPED_COLOR='\e[33m' # Yellow - __DEBUG_COLOR='\e[37m' # Gray - __HELP_COLOR='\e[7;49;33m' # Black on Gold - __TEST_COLOR='\e[100m' # Light magenta - __TEST_ERROR_COLOR='\e[41m' # white on red - __HELP_TITLE_COLOR="\e[1;37m" # Bold - __HELP_OPTION_COLOR="\e[1;34m" # Blue - # Internal: reset color - __RESET_COLOR='\e[0m' # Reset Color - # shellcheck disable=SC2155,SC2034 - __HELP_EXAMPLE="$(echo -e "\e[2;97m")" - # shellcheck disable=SC2155,SC2034 - __HELP_TITLE="$(echo -e "\e[1;37m")" - # shellcheck disable=SC2155,SC2034 - __HELP_NORMAL="$(echo -e "\033[0m")" - else - BASH_FRAMEWORK_THEME="noColor" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='' - __INFO_COLOR='' - __SUCCESS_COLOR='' - __WARNING_COLOR='' - __SKIPPED_COLOR='' - __DEBUG_COLOR='' - __HELP_COLOR='' - __TEST_COLOR='' - __TEST_ERROR_COLOR='' - __HELP_TITLE_COLOR='' - __HELP_OPTION_COLOR='' - # Internal: reset color - __RESET_COLOR='' - __HELP_EXAMPLE='' - __HELP_TITLE='' - __HELP_NORMAL='' - fi -} -# FUNCTIONS - - -declare -a BASH_FRAMEWORK_ARGV_FILTERED=() - -beforeParseCallback() { - Env::requireLoad - UI::requireTheme - Log::requireLoad -} - -copyrightCallback() { - if [[ -z "${copyrightBeginYear}" ]]; then - copyrightBeginYear="$(date +%Y)" - fi - echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" -} - -# shellcheck disable=SC2317 # if function is overridden -updateArgListInfoVerboseCallback() { - BASH_FRAMEWORK_ARGV_FILTERED+=(--verbose) -} -# shellcheck disable=SC2317 # if function is overridden -updateArgListDebugVerboseCallback() { - BASH_FRAMEWORK_ARGV_FILTERED+=(-vv) -} -# shellcheck disable=SC2317 # if function is overridden -updateArgListTraceVerboseCallback() { - BASH_FRAMEWORK_ARGV_FILTERED+=(-vvv) -} -# shellcheck disable=SC2317 # if function is overridden -updateArgListEnvFileCallback() { :; } -# shellcheck disable=SC2317 # if function is overridden -updateArgListLogLevelCallback() { :; } -# shellcheck disable=SC2317 # if function is overridden -updateArgListDisplayLevelCallback() { :; } -# shellcheck disable=SC2317 # if function is overridden -updateArgListNoColorCallback() { - BASH_FRAMEWORK_ARGV_FILTERED+=(--no-color) -} -# shellcheck disable=SC2317 # if function is overridden -updateArgListThemeCallback() { :; } -# shellcheck disable=SC2317 # if function is overridden -updateArgListQuietCallback() { :; } - -# shellcheck disable=SC2317 # if function is overridden -optionHelpCallback() { - Log::displayError "optionHelpCallback needs to be overridden" - exit 0 -} - -# shellcheck disable=SC2317 # if function is overridden -optionVersionCallback() { - # shellcheck disable=SC2154 - echo "${SCRIPT_NAME} version ${versionNumber}" - exit 0 -} - -# shellcheck disable=SC2317 # if function is overridden -optionEnvFileCallback() { - local envFile="$2" - Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" - if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" - exit 1 - fi -} - -# shellcheck disable=SC2317 # if function is overridden -optionInfoVerboseCallback() { - BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' - BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_INFO} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >>"${overrideEnvFile}" -} - -# shellcheck disable=SC2317 # if function is overridden -optionDebugVerboseCallback() { - BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vv' - BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_DEBUG} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" -} - -# shellcheck disable=SC2317 # if function is overridden -optionTraceVerboseCallback() { - # shellcheck disable=SC2034 - BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vvv' - BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_TRACE} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" -} - -getLevel() { - local levelName="$1" - case "${levelName^^}" in - OFF) - echo "${__LEVEL_OFF}" - ;; - ERR | ERROR) - echo "${__LEVEL_ERROR}" - ;; - WARN | WARNING) - echo "${__LEVEL_WARNING}" - ;; - INFO) - echo "${__LEVEL_INFO}" - ;; - DEBUG | TRACE) - echo "${__LEVEL_DEBUG}" - ;; - *) - Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" - return 1 - ;; - esac -} - -getVerboseLevel() { - local levelName="$1" - case "${levelName^^}" in - OFF) - echo "${__VERBOSE_LEVEL_OFF}" - ;; - ERR | ERROR | WARN | WARNING | INFO) - echo "${__VERBOSE_LEVEL_INFO}" - ;; - DEBUG) - echo "${__VERBOSE_LEVEL_DEBUG}" - ;; - TRACE) - echo "${__VERBOSE_LEVEL_TRACE}" - ;; - *) - Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" - return 1 - ;; - esac -} - -# shellcheck disable=SC2317 # if function is overridden -optionDisplayLevelCallback() { - local level="$2" - local logLevel verboseLevel - logLevel="$(getLevel "${level}")" - verboseLevel="$(getVerboseLevel "${level}")" - BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >>"${overrideEnvFile}" -} - -# shellcheck disable=SC2317 # if function is overridden -optionLogLevelCallback() { - local level="$2" - local logLevel verboseLevel - logLevel="$(getLevel "${level}")" - verboseLevel="$(getVerboseLevel "${level}")" - # shellcheck disable=SC2034 - BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >>"${overrideEnvFile}" -} - -# shellcheck disable=SC2317 # if function is overridden -optionLogFileCallback() { - local logFile="$2" - echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >>"${overrideEnvFile}" -} - -# shellcheck disable=SC2317 # if function is overridden -optionQuietCallback() { - echo "BASH_FRAMEWORK_QUIET_MODE=1" >>"${overrideEnvFile}" -} - -# shellcheck disable=SC2317 # if function is overridden -optionNoColorCallback() { - UI::theme "noColor" -} - -# shellcheck disable=SC2317 # if function is overridden -optionThemeCallback() { - UI::theme "$2" -} - -displayConfig() { - echo "Config" - UI::drawLine "-" - local var - while read -r var; do - printf '%-40s = %s\n' "${var}" "$(declare -p "${var}" | sed -E -e 's/^[^=]+=(.*)/\1/')" - done < <(typeset -p | awk 'match($3, "^(BASH_FRAMEWORK_[^=]+)=", m) { print m[1] }' | sort) - exit 0 -} - -optionBashFrameworkConfigCallback() { - if [[ ! -f "$2" ]]; then - Log::fatal "Command ${SCRIPT_NAME} - Bash framework config file '$2' does not exists" - fi -} - -defaultFrameworkConfig="$( - cat <<'EOF' - -# copied from src/_includes/.framework-config.default -# shellcheck disable=SC2034 - -REAL_SCRIPT_FILE="${REAL_SCRIPT_FILE:-$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")}" -FRAMEWORK_ROOT_DIR="${FRAMEWORK_ROOT_DIR:-${REAL_SCRIPT_FILE%/*/*}}" -FRAMEWORK_SRC_DIR="${FRAMEWORK_SRC_DIR:-${FRAMEWORK_ROOT_DIR}/src}" -FRAMEWORK_BIN_DIR="${FRAMEWORK_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/bin}" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_VENDOR_DIR:-${FRAMEWORK_ROOT_DIR}/vendor}" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_VENDOR_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/vendor/bin}" - -# describe the functions that will be skipped from being imported -FRAMEWORK_FUNCTIONS_IGNORE_REGEXP="${FRAMEWORK_FUNCTIONS_IGNORE_REGEXP:-^(Namespace::functions|Functions::myFunction|Namespace::requireSomething|Acquire::ForceIPv4)$}" -# describe the files that do not contain function to be imported -NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/_binaries|^src/_includes|^src/batsHeaders.sh$|^src/_standalone)}" -# describe the files that are allowed to not have an associated bats file -BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" -# describe the files that are allowed to not have a function matching the filename -FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|/testsData/|^manualTests/|\.bats$}" -# Source directories -if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then - FRAMEWORK_SRC_DIRS=( - "${FRAMEWORK_ROOT_DIR}/src" - ) -fi - -# export here all the variables that will be used in your templates -export REPOSITORY_URL="${REPOSITORY_URL:-https://github.com/fchastanet/bash-tools-framework}" - -BASH_FRAMEWORK_THEME="${BASH_FRAMEWORK_THEME:-default}" -BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" -BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" -BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" -BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" - -EOF -)" - -overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" - -commandOptionParseFinished() { - # load default template framework config - defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" - echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" - local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi - # shellcheck disable=SC2154 - if [[ -f "${optionBashFrameworkConfig}" ]]; then - files+=("${optionBashFrameworkConfig}") - fi - files+=("${overrideEnvFile}") - Env::requireLoad "${files[@]}" - Log::requireLoad - # shellcheck disable=SC2154 - if [[ "${optionConfig}" = "1" ]]; then - displayConfig - fi -} - - - -# shellcheck disable=SC2034 -declare copyrightBeginYear="2020" -# shellcheck disable=SC2034 -declare versionNumber="1.1" - -declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" - -optionHelpCallback() { - installCommandHelp - exit 0 -} - - -# ------------------------------------------ -# Command installCommand -# ------------------------------------------ - -# options variables initialization -declare optionHelp="0" -declare optionConfig="0" -declare optionBashFrameworkConfig="" -declare optionInfoVerbose="0" -declare optionDebugVerbose="0" -declare optionTraceVerbose="0" -declare -a optionEnvFiles=() -declare optionLogLevel="" -declare optionLogFile="" -declare optionDisplayLevel="" -declare optionNoColor="0" -declare optionTheme="default" -declare optionVersion="0" -declare optionQuiet="0" -declare optionSkipBackup="0" -# arguments variables initialization -# @description parse command options and arguments for installCommand -installCommandParse() { - Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" - Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" - optionHelp="0" - local -i options_parse_optionParsedCountOptionHelp - ((options_parse_optionParsedCountOptionHelp = 0)) || true - optionConfig="0" - local -i options_parse_optionParsedCountOptionConfig - ((options_parse_optionParsedCountOptionConfig = 0)) || true - optionBashFrameworkConfig="" - local -i options_parse_optionParsedCountOptionBashFrameworkConfig - ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true - optionInfoVerbose="0" - local -i options_parse_optionParsedCountOptionInfoVerbose - ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true - optionDebugVerbose="0" - local -i options_parse_optionParsedCountOptionDebugVerbose - ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true - optionTraceVerbose="0" - local -i options_parse_optionParsedCountOptionTraceVerbose - ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - - optionLogLevel="" - local -i options_parse_optionParsedCountOptionLogLevel - ((options_parse_optionParsedCountOptionLogLevel = 0)) || true - optionLogFile="" - local -i options_parse_optionParsedCountOptionLogFile - ((options_parse_optionParsedCountOptionLogFile = 0)) || true - optionDisplayLevel="" - local -i options_parse_optionParsedCountOptionDisplayLevel - ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true - optionNoColor="0" - local -i options_parse_optionParsedCountOptionNoColor - ((options_parse_optionParsedCountOptionNoColor = 0)) || true - optionTheme="default" - local -i options_parse_optionParsedCountOptionTheme - ((options_parse_optionParsedCountOptionTheme = 0)) || true - optionVersion="0" - local -i options_parse_optionParsedCountOptionVersion - ((options_parse_optionParsedCountOptionVersion = 0)) || true - optionQuiet="0" - local -i options_parse_optionParsedCountOptionQuiet - ((options_parse_optionParsedCountOptionQuiet = 0)) || true - optionSkipBackup="0" - local -i options_parse_optionParsedCountOptionSkipBackup - ((options_parse_optionParsedCountOptionSkipBackup = 0)) || true - - - # shellcheck disable=SC2034 - local -i options_parse_parsedArgIndex=0 - while (($# > 0)); do - local options_parse_arg="$1" - local argOptDefaultBehavior=0 - case "${options_parse_arg}" in - # Option 1/15 - # optionHelp alts --help|-h - # type: Boolean min 0 max 1 - --help | -h) - # shellcheck disable=SC2034 - optionHelp="1" - - if ((options_parse_optionParsedCountOptionHelp >= 1 )); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionHelp)) - optionHelpCallback "${options_parse_arg}" "${optionHelp}" - - ;; - - # Option 2/15 - # optionConfig alts --config - # type: Boolean min 0 max 1 - --config) - # shellcheck disable=SC2034 - optionConfig="1" - - if ((options_parse_optionParsedCountOptionConfig >= 1 )); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionConfig)) - ;; - - # Option 3/15 - # optionBashFrameworkConfig alts --bash-framework-config - # type: String min 0 max 1 - --bash-framework-config) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1 )); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) - # shellcheck disable=SC2034 - optionBashFrameworkConfig="$1" - optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" - - ;; - - # Option 4/15 - # optionInfoVerbose alts --verbose|-v - # type: Boolean min 0 max 1 - --verbose | -v) - # shellcheck disable=SC2034 - optionInfoVerbose="1" - - if ((options_parse_optionParsedCountOptionInfoVerbose >= 1 )); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionInfoVerbose)) - optionInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" - - updateArgListInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" - - ;; - - # Option 5/15 - # optionDebugVerbose alts -vv - # type: Boolean min 0 max 1 - -vv) - # shellcheck disable=SC2034 - optionDebugVerbose="1" - - if ((options_parse_optionParsedCountOptionDebugVerbose >= 1 )); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDebugVerbose)) - optionDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" - - updateArgListDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" - - ;; - - # Option 6/15 - # optionTraceVerbose alts -vvv - # type: Boolean min 0 max 1 - -vvv) - # shellcheck disable=SC2034 - optionTraceVerbose="1" - - if ((options_parse_optionParsedCountOptionTraceVerbose >= 1 )); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTraceVerbose)) - optionTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" - - updateArgListTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" - - ;; - - # Option 7/15 - # optionEnvFiles alts --env-file - # type: StringArray min 0 max -1 - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - ;; - - # Option 8/15 - # optionLogLevel alts --log-level - # type: String min 0 max 1 - # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - --log-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" - return 1 - fi - - if ((options_parse_optionParsedCountOptionLogLevel >= 1 )); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogLevel)) - # shellcheck disable=SC2034 - optionLogLevel="$1" - optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - - updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - - ;; - - # Option 9/15 - # optionLogFile alts --log-file - # type: String min 0 max 1 - --log-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - if ((options_parse_optionParsedCountOptionLogFile >= 1 )); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogFile)) - # shellcheck disable=SC2034 - optionLogFile="$1" - optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" - - updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" - - ;; - - # Option 10/15 - # optionDisplayLevel alts --display-level - # type: String min 0 max 1 - # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - --display-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" - return 1 - fi - - if ((options_parse_optionParsedCountOptionDisplayLevel >= 1 )); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDisplayLevel)) - # shellcheck disable=SC2034 - optionDisplayLevel="$1" - optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - - updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - - ;; - - # Option 11/15 - # optionNoColor alts --no-color - # type: Boolean min 0 max 1 - --no-color) - # shellcheck disable=SC2034 - optionNoColor="1" - - if ((options_parse_optionParsedCountOptionNoColor >= 1 )); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionNoColor)) - optionNoColorCallback "${options_parse_arg}" "${optionNoColor}" - - updateArgListNoColorCallback "${options_parse_arg}" "${optionNoColor}" - - ;; - - # Option 12/15 - # optionTheme alts --theme - # type: String min 0 max 1 - # authorizedValues: default|default-force|noColor - --theme) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" - return 1 - fi - - if ((options_parse_optionParsedCountOptionTheme >= 1 )); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTheme)) - # shellcheck disable=SC2034 - optionTheme="$1" - optionThemeCallback "${options_parse_arg}" "${optionTheme}" - - updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" - - ;; - - # Option 13/15 - # optionVersion alts --version - # type: Boolean min 0 max 1 - --version) - # shellcheck disable=SC2034 - optionVersion="1" - - if ((options_parse_optionParsedCountOptionVersion >= 1 )); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionVersion)) - optionVersionCallback "${options_parse_arg}" "${optionVersion}" - - ;; - - # Option 14/15 - # optionQuiet alts --quiet|-q - # type: Boolean min 0 max 1 - --quiet | -q) - # shellcheck disable=SC2034 - optionQuiet="1" - - if ((options_parse_optionParsedCountOptionQuiet >= 1 )); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionQuiet)) - optionQuietCallback "${options_parse_arg}" "${optionQuiet}" - - updateArgListQuietCallback "${options_parse_arg}" "${optionQuiet}" - - ;; - - # Option 15/15 - # optionSkipBackup alts --skip-backup - # type: Boolean min 0 max 1 - --skip-backup) - # shellcheck disable=SC2034 - optionSkipBackup="1" - - if ((options_parse_optionParsedCountOptionSkipBackup >= 1 )); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionSkipBackup)) - ;; - - -*) - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" - return 1 - fi - ;; - *) - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - # too much args and no unknownArgumentCallbacks configured - Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" - return 1 - fi - ((incrementArg = 0)) - if ((incrementArg == 1)); then - ((++options_parse_parsedArgIndex)) - fi - ;; - esac - shift || true - done || return $? - - commandOptionParseFinished - -} - -# @description display command options and arguments help for installCommand -installCommandHelp() { - echo -e "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" - Array::wrap2 ' ' 76 4 " " "Installs bash-tools." - - echo - echo - - # ------------------------------------------ - # usage section - # ------------------------------------------ - Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "install [OPTIONS] " - echo - # ------------------------------------------ - # usage/options section - # ------------------------------------------ - optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--skip-backup]" - ) - Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ - "install" "${optionsAltList[@]}" - echo - - # ------------------------------------------ - # options section - # ------------------------------------------ - echo - echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" - Array::wrap2 ' ' 76 4 " " "Displays this command help" - echo - - - - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" - Array::wrap2 ' ' 76 4 " " "Displays configuration" - echo - - - - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" - Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." - echo - - - - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" - Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" - echo - - - - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" - Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" - echo - - - - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" - Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" - echo - - - - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" - echo - - - - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" - Array::wrap2 ' ' 76 4 " " "Set log level" - echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo - - - echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" - Array::wrap2 ' ' 76 4 " " "Set log file" - echo - - - - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" - Array::wrap2 ' ' 76 4 " " "Set display level" - echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo - - - echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" - Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." - echo - - - - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" - Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." - echo - Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" - echo - - Array::wrap2 ' ' 76 6 " Default value: " "default" - echo - - echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" - Array::wrap2 ' ' 76 4 " " "Print version information and quit." - echo - - - - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" - Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." - echo - - - echo - echo -e "${__HELP_TITLE_COLOR}OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--skip-backup${__HELP_NORMAL} {single}" - Array::wrap2 ' ' 76 4 " " "skip backup of ~/bash-tools." - echo - - - # ------------------------------------------ - # longDescription section - # ------------------------------------------ - echo - echo - echo -e "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" - declare -a installCommandLongDescription=( - "Installs dependent softwares and configuration needed to use bash-tools:" - "- GNU parallel" - "- Installs or updates default configuration files" - " If ~/.bash-tools directory already exists, a backup will be done before" - ) - Array::wrap2 ' ' 76 0 "${installCommandLongDescription[@]}" - echo - # ------------------------------------------ - # version section - # ------------------------------------------ - echo - echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo "1.1" - # ------------------------------------------ - # author section - # ------------------------------------------ - echo - echo -n -e "${__HELP_TITLE_COLOR}AUTHOR: ${__RESET_COLOR}" - echo "[François Chastanet](https://github.com/fchastanet)" - # ------------------------------------------ - # sourceFile section - # ------------------------------------------ - echo - echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" - echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/build/install/binary-install.yaml" - # ------------------------------------------ - # license section - # ------------------------------------------ - echo - echo -n -e "${__HELP_TITLE_COLOR}LICENSE: ${__RESET_COLOR}" - echo "MIT License" - # ------------------------------------------ - # copyright section - # ------------------------------------------ - Array::wrap2 ' ' 76 0 "$(copyrightCallback)" -} - - -beforeParseCallback - -installCommandParse "$@" -MAIN_FUNCTION_NAME="main" -main() { - -Linux::requireExecutedAsUser -Linux::requireTarCommand - -if ! command -v parallel &>/dev/null; then - Log::displayInfo "We will install GNU parallel software, please enter you sudo password" - sudo apt-get update || true - if sudo apt-get install -y parallel; then - # remove parallel nagware - mkdir -p "${HOME}/.parallel" - touch "${HOME}/.parallel/will-cite" - else - Log::displayWarning "Impossible to install GNU parallel, please install it manually" - fi -else - Log::displaySkipped "parallel is already installed" -fi - -if [[ -d "${HOME}/.bash-tools" ]]; then - # shellcheck disable=SC2154 - if [[ "${optionSkipBackup}" = "1" ]]; then - Log::displayInfo "Backup of ~/.bash-tools is set to be skipped" - else - BACKUP_DIR="${FRAMEWORK_ROOT_DIR}/logs" \ - Backup::dir "${HOME}" ".bash-tools" - fi - - Log::displayInfo "Updating configuration" - cp -R --no-clobber "${BASH_TOOLS_ROOT_DIR}/conf/." "${HOME}/.bash-tools" - if [[ "${FRAMEWORK_ROOT_DIR}/conf/.env" -nt "${HOME}/.bash-tools/.env" ]]; then - Log::displayWarning "${FRAMEWORK_ROOT_DIR}/conf/.env is newer than ${HOME}/.bash-tools/.env, compare the files to check if some updates need to be applied" - else - Log::displaySkipped "${HOME}/.bash-tools/.env is up to date" - fi -else - Log::displayInfo "Installing configuration in ~/.bash-tools" - mkdir -p "${HOME}/.bash-tools" - cp -R "${BASE_DIR}/conf/." "${HOME}/.bash-tools" -fi - -} - -# if file is sourced avoid calling main function -# shellcheck disable=SC2178 -BASH_SOURCE=".$0" # cannot be changed in bash -# shellcheck disable=SC2128 -if test ".$0" == ".${BASH_SOURCE}"; then - if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - main "$@" &>/dev/null - else - main "$@" - fi -fi diff --git a/bin/installRequirements b/bin/installRequirements index 211b19d4..70a15954 100755 --- a/bin/installRequirements +++ b/bin/installRequirements @@ -848,10 +848,8 @@ beforeParseCallback() { } copyrightCallback() { - if [[ -z "${copyrightBeginYear}" ]]; then - copyrightBeginYear="$(date +%Y)" - fi - echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" + # + echo "Copyright (c) 2022-now François Chastanet" } # shellcheck disable=SC2317 # if function is overridden @@ -890,7 +888,7 @@ optionHelpCallback() { # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { # shellcheck disable=SC2154 - echo "${SCRIPT_NAME} version ${versionNumber}" + echo "${SCRIPT_NAME} version 3.0" exit 0 } @@ -1097,10 +1095,6 @@ commandOptionParseFinished() { # shellcheck disable=SC2034 -declare copyrightBeginYear="2020" -# shellcheck disable=SC2034 -declare versionNumber="1.0" - declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" optionHelpCallback() { @@ -1621,7 +1615,7 @@ installRequirementsCommandHelp() { # ------------------------------------------ echo echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo "1.0" + echo "3.0" # ------------------------------------------ # author section # ------------------------------------------ diff --git a/bin/mysql2puml b/bin/mysql2puml index 1950a4db..e4e734c9 100755 --- a/bin/mysql2puml +++ b/bin/mysql2puml @@ -979,10 +979,8 @@ beforeParseCallback() { } copyrightCallback() { - if [[ -z "${copyrightBeginYear}" ]]; then - copyrightBeginYear="$(date +%Y)" - fi - echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" + # + echo "Copyright (c) 2021-now François Chastanet" } # shellcheck disable=SC2317 # if function is overridden @@ -1021,7 +1019,7 @@ optionHelpCallback() { # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { # shellcheck disable=SC2154 - echo "${SCRIPT_NAME} version ${versionNumber}" + echo "${SCRIPT_NAME} version 3.0" exit 0 } @@ -1228,10 +1226,6 @@ commandOptionParseFinished() { # shellcheck disable=SC2034 -declare copyrightBeginYear="2020" -# shellcheck disable=SC2034 -declare versionNumber="1.0" - declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" optionHelpCallback() { @@ -1868,7 +1862,7 @@ mysql2pumlCommandHelp() { # ------------------------------------------ echo echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo "1.0" + echo "3.0" # ------------------------------------------ # author section # ------------------------------------------ diff --git a/conf/dbScripts/extractData b/conf/dbScripts/extractData index 2b208e9a..ad8e0fa4 100755 --- a/conf/dbScripts/extractData +++ b/conf/dbScripts/extractData @@ -1277,10 +1277,8 @@ beforeParseCallback() { } copyrightCallback() { - if [[ -z "${copyrightBeginYear}" ]]; then - copyrightBeginYear="$(date +%Y)" - fi - echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" + # + echo "Copyright (c) 2020-now François Chastanet" } # shellcheck disable=SC2317 # if function is overridden @@ -1319,7 +1317,7 @@ optionHelpCallback() { # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { # shellcheck disable=SC2154 - echo "${SCRIPT_NAME} version ${versionNumber}" + echo "${SCRIPT_NAME} version 3.0" exit 0 } @@ -1536,7 +1534,7 @@ beforeParseCallback() { optionVersionCallback() { # shellcheck disable=SC2154 - echo "${SCRIPT_NAME} version ${versionNumber}" + echo "${SCRIPT_NAME} version 3.0" Db::checkRequirements exit 0 } @@ -2095,7 +2093,7 @@ extractDataCommandHelp() { # ------------------------------------------ echo echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo "2.0" + echo "3.0" # ------------------------------------------ # author section # ------------------------------------------ diff --git a/install b/install index f605bf01..9a910474 100755 --- a/install +++ b/install @@ -1,13 +1,12 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FACADE FROM https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/build/install.sh +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/build/install/binary-install.yaml # DO NOT EDIT IT # @generated ############################################################################### # shellcheck disable=SC2288,SC2034 -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/install -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=. -# FACADE + + # ensure that no user aliases could interfere with # commands used in this script @@ -62,13 +61,6 @@ interruptManagement() { kill -s INT "$$" } trap interruptManagement INT -SCRIPT_NAME=${0##*/} -REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" -if [[ -n "${EMBED_CURRENT_DIR}" ]]; then - CURRENT_DIR="${EMBED_CURRENT_DIR}" -else - CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" -fi ################################################ # Temp dir management @@ -102,6 +94,20 @@ cleanOnExit() { } trap cleanOnExit EXIT HUP QUIT ABRT TERM + +SCRIPT_NAME=${0##*/} +REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" +if [[ -n "${EMBED_CURRENT_DIR}" ]]; then + CURRENT_DIR="${EMBED_CURRENT_DIR}" +else + CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" +fi +FRAMEWORK_ROOT_DIR="$(cd "${CURRENT_DIR}/." && pwd -P)" +FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" +FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + # @description Log namespace provides 2 kind of functions # - Log::display* allows to display given message with # given display level @@ -132,247 +138,6 @@ export __VERBOSE_LEVEL_DEBUG=2 # @description verbose level info export __VERBOSE_LEVEL_TRACE=3 -# @description Display message using info color (bg light blue/fg white) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayInfo() { - local type="${2:-INFO}" - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logInfo "$1" "${type}" -} - -# @description Display message using debug color (gray) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayDebug() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then - Log::computeDuration - echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logDebug "$1" -} - -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logWarning "$1" -} - -# @description Display message using error color (red) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayError() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then - Log::computeDuration - echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logError "$1" -} - -# @description load colors theme constants -# @warning if tty not opened, noColor theme will be chosen -# @arg $1 theme:String the theme to use (default, noColor) -# @arg $@ args:String[] -# @set __ERROR_COLOR String indicate error status -# @set __INFO_COLOR String indicate info status -# @set __SUCCESS_COLOR String indicate success status -# @set __WARNING_COLOR String indicate warning status -# @set __SKIPPED_COLOR String indicate skipped status -# @set __DEBUG_COLOR String indicate debug status -# @set __HELP_COLOR String indicate help status -# @set __TEST_COLOR String not used -# @set __TEST_ERROR_COLOR String not used -# @set __HELP_TITLE_COLOR String used to display help title in help strings -# @set __HELP_OPTION_COLOR String used to display highlight options in help strings -# -# @set __RESET_COLOR String reset default color -# -# @set __HELP_EXAMPLE String to remove -# @set __HELP_TITLE String to remove -# @set __HELP_NORMAL String to remove -# shellcheck disable=SC2034 -UI::theme() { - local theme="${1-default}" - if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then - theme="noColor" - fi - case "${theme}" in - default | default-force) - theme="default" - ;; - noColor) ;; - *) - Log::fatal "invalid theme provided" - ;; - esac - if [[ "${theme}" = "default" ]]; then - BASH_FRAMEWORK_THEME="default" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='\e[31m' # Red - __INFO_COLOR='\e[44m' # white on lightBlue - __SUCCESS_COLOR='\e[32m' # Green - __WARNING_COLOR='\e[33m' # Yellow - __SKIPPED_COLOR='\e[33m' # Yellow - __DEBUG_COLOR='\e[37m' # Gray - __HELP_COLOR='\e[7;49;33m' # Black on Gold - __TEST_COLOR='\e[100m' # Light magenta - __TEST_ERROR_COLOR='\e[41m' # white on red - __HELP_TITLE_COLOR="\e[1;37m" # Bold - __HELP_OPTION_COLOR="\e[1;34m" # Blue - # Internal: reset color - __RESET_COLOR='\e[0m' # Reset Color - # shellcheck disable=SC2155,SC2034 - __HELP_EXAMPLE="$(echo -e "\e[2;97m")" - # shellcheck disable=SC2155,SC2034 - __HELP_TITLE="$(echo -e "\e[1;37m")" - # shellcheck disable=SC2155,SC2034 - __HELP_NORMAL="$(echo -e "\033[0m")" - else - BASH_FRAMEWORK_THEME="noColor" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='' - __INFO_COLOR='' - __SUCCESS_COLOR='' - __WARNING_COLOR='' - __SKIPPED_COLOR='' - __DEBUG_COLOR='' - __HELP_COLOR='' - __TEST_COLOR='' - __TEST_ERROR_COLOR='' - __HELP_TITLE_COLOR='' - __HELP_OPTION_COLOR='' - # Internal: reset color - __RESET_COLOR='' - __HELP_EXAMPLE='' - __HELP_TITLE='' - __HELP_NORMAL='' - fi -} - -# @description draw a line with the character passed in parameter repeated depending on terminal width -# @arg $1 character:String character to use as separator (default value #) -UI::drawLine() { - local character="${1:-#}" - local -i width=${COLUMNS:-0} - if ((width == 0)) && [[ -t 1 ]]; then - width=$(tput cols) - fi - if ((width == 0)); then - width=80 - fi - printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") - echo -} - -# @description Display message using error color (red) and exit immediately with error status 1 -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::fatal() { - Log::computeDuration - echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - Log::logFatal "$1" - exit 1 -} - -# @description create a temp file using default TMPDIR variable -# initialized in _includes/_commonHeader.sh -# @env TMPDIR String (default value /tmp) -# @arg $1 templateName:String template name to use(optional) -Framework::createTempFile() { - mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" -} - -# @description ensure env files are loaded -# @arg $@ list of default files to load at the end -# @exitcode 1 if one of env files fails to load -# @stderr diagnostics information is displayed -# shellcheck disable=SC2120 -Env::requireLoad() { - local -a defaultFiles=("$@") - # get list of possible config files - local -a configFiles=() - if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then - # BASH_FRAMEWORK_ENV_FILES is an array - configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") - fi - local localFrameworkConfigFile - localFrameworkConfigFile="$(pwd)/.framework-config" - if [[ -f "${localFrameworkConfigFile}" ]]; then - configFiles+=("${localFrameworkConfigFile}") - fi - if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then - configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") - fi - configFiles+=("${optionEnvFiles[@]}") - configFiles+=("${defaultFiles[@]}") - - for file in "${configFiles[@]}"; do - # shellcheck source=/.framework-config - CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { - Log::displayError "while loading config file: ${file}" - return 1 - } - done -} - -# @description activate or not Log::display* and Log::log* functions -# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL -# environment variables loaded by Env::requireLoad -# try to create log file and rotate it if necessary -# @noargs -# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable -# @env BASH_FRAMEWORK_DISPLAY_LEVEL int -# @env BASH_FRAMEWORK_LOG_LEVEL int -# @env BASH_FRAMEWORK_LOG_FILE String -# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 -# @exitcode 0 always successful -# @stderr diagnostics information about log file is displayed -# @require Env::requireLoad -# @require UI::requireTheme -Log::requireLoad() { - if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - export BASH_FRAMEWORK_LOG_LEVEL - fi - - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then - if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 - fi - elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - fi - - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - # will always be created even if not in info level - Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" - if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then - Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" - fi - fi -} # @description concatenate each element of an array with a separator # but wrapping text when line length is more than provided argument @@ -502,127 +267,138 @@ Array::wrap2() { ) | sed -E -e 's/[[:blank:]]+$//' } -# @description Display message using skip color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displaySkipped() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__SKIPPED_COLOR}SKIPPED - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description check if command specified exists or return 1 +# with error and message if not +# +# @arg $1 commandName:String on which existence must be checked +# @arg $2 helpIfNotExists:String a help command to display if the command does not exist +# +# @exitcode 1 if the command specified does not exist +# @stderr diagnostic information + help if second argument is provided +Assert::commandExists() { + local commandName="$1" + local helpIfNotExists="$2" + + "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { + Log::displayError "${commandName} is not installed, please install it" + if [[ -n "${helpIfNotExists}" ]]; then + Log::displayInfo "${helpIfNotExists}" + fi + return 1 + } + return 0 +} + + +# @description check if tty (interactive mode) is active +# @noargs +# @exitcode 1 if tty not active +# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive +# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive +Assert::tty() { + if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then + return 1 fi - Log::logSkipped "$1" + if [[ "${INTERACTIVE:-0}" = "1" ]]; then + return 0 + fi + tty -s } -bashToolsDefaultConfigTemplate="${bashToolsDefaultConfigTemplate:-$( - cat <<'EOF' -# shellcheck disable=SC2034 -# Default settings -# you can override these settings by creating ${HOME}/.bash-tools/.env file - -### -### DISPLAY Level -### minimum level of the messages that will be displayed on screen -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} - -### -### DISPLAY duration -### 0: no duration is displayed on the messages -### 1: duration between previous message and current is displayed -### with the message -### -DISPLAY_DURATION=${DISPLAY_DURATION:0} - -### -### Log to file -### -### all log messages will be redirected to log file specified -### this same path will be used inside and outside of the container -### -BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} - -### -### LOG Level -### minimum level of the messages that will be logged into LOG_FILE -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} - -# absolute directory containing db import sql dumps -DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} - -# garbage collect all files for which modification is greater than eg: 30 days (+30) -# each time an existing file is used by dbImport/dbImportTable -# the file modification time is set to now -DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} - -# absolute directory containing dbScripts used by dbScriptAllDatabases -SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} - -# ----------------------------------------------------- -# AWS Parameters -# ----------------------------------------------------- -S3_BASE_URL=${S3_BASE_URL:-} - -# ----------------------------------------------------- -# Postman Parameters -# ----------------------------------------------------- -POSTMAN_API_KEY= -EOF -)}" - -# @description loads ~/.bash-tools/.env if available -# if not creates it from a default template -# else check if new options need to be added -BashTools::Conf::requireLoad() { - local envFile="${HOME}/.bash-tools/.env" - if [[ ! -f "${envFile}" ]]; then - mkdir -p "${HOME}/.bash-tools" - ( - echo "#!/usr/bin/env bash" - echo "${bashToolsDefaultConfigTemplate}" - ) >"${envFile}" - Log::displayInfo "Configuration file '${envFile}' created" +# @description Backup given directory in the base directory or in BACKUP_DIR directory +# backup directory name is composed by following fields separated by _: +# - if BACKUP_DIR is not empty then escaped full dir name separated by @ +# - date with format %Y%m%d_%H:%M:%S (Eg: 20240326_14:45:08) +# - .tgz extension +# +# @arg $1 string the base directory +# @arg $2 string the directory to backup from base directory +# +# @env BACKUP_DIR String variable referencing backup directory +# @env FRAMEWORK_ROOT_DIR String used to remove this project directory from displayed backup path +# @env SUDO String allows to use custom sudo prefix command +# +# @exitcode 1 if directory to backup does not exist, 0 if everything is OK +# @exitcode 2 on backup failure +# +# @stderr message about where the directory is backed up +Backup::dir() { + + if [[ "${REQUIRE_FUNCTION_LINUX_REQUIRE_TAR_COMMAND_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Linux::requireTarCommand has not been loaded" + exit 1 + fi + + local escapedDirname backupFile + local fromDir="$1" + local dirname="$2" + + if [[ ! -d "${fromDir}/${dirname}" ]]; then + Log::displayError "Backup::dir - directory '${fromDir}/${dirname}' doesn't exist" + return 1 + fi + if [[ -z "${BACKUP_DIR:-}" ]]; then + escapedDirname="$(sed -e 's#^/##; s#/#@#g' <<<"${dirname}")" + backupFile="${fromDir}/${escapedDirname}-$(date +"%Y%m%d_%H:%M:%S").tgz" else - if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then - ( - echo '# -----------------------------------------------------' - echo '# Postman Parameters' - echo '# -----------------------------------------------------' - echo 'POSTMAN_API_KEY=' - ) >>"${envFile}" - fi + escapedDirname="$(sed -e 's#^/##; s#/#@#g' <<<"${fromDir}/${dirname}")" + backupFile="${BACKUP_DIR}/${escapedDirname}-$(date +"%Y%m%d_%H:%M:%S").tgz" fi - # shellcheck source=/conf/.env - source "${envFile}" || { - Log::displayError "impossible to load '${envFile}'" - exit 1 - } + + Log::displayInfo "Backup directory '${fromDir}/${dirname}' to ${backupFile}" + if ! ${SUDO:-} tar --same-owner -czf "${backupFile}" "${fromDir}/${dirname}" 2>/dev/null; then + Log::displayError "cannot backup '${fromDir}/${dirname}'" + return 2 + fi +} + + +# @description ensure env files are loaded +# @arg $@ list of default files to load at the end +# @exitcode 1 if one of env files fails to load +# @stderr diagnostics information is displayed +# shellcheck disable=SC2120 +Env::requireLoad() { + export REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 + + local -a defaultFiles=("$@") + # get list of possible config files + local -a configFiles=() + if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then + # BASH_FRAMEWORK_ENV_FILES is an array + configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") + fi + local localFrameworkConfigFile + localFrameworkConfigFile="$(pwd)/.framework-config" + if [[ -f "${localFrameworkConfigFile}" ]]; then + configFiles+=("${localFrameworkConfigFile}") + fi + if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then + configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") + fi + configFiles+=("${optionEnvFiles[@]}") + configFiles+=("${defaultFiles[@]}") + + for file in "${configFiles[@]}"; do + # shellcheck source=/.framework-config + CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { + Log::displayError "while loading config file: ${file}" + return 1 + } + done } -# @description ensure COMMAND_BIN_DIR env var is set -# and PATH correctly prepared -# @noargs -# @set COMMAND_BIN_DIR string the directory where to find this command -# @set PATH string add directory where to find this command binary -Compiler::Facade::requireCommandBinDir() { - COMMAND_BIN_DIR="${CURRENT_DIR}" - Env::pathPrepend "${COMMAND_BIN_DIR}" + +# @description create a temp file using default TMPDIR variable +# @env TMPDIR String (default value /tmp) +# @arg $1 templateName:String template name to use(optional) +Framework::createTempFile() { + mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" } + # @description ensure running user is not root # @exitcode 1 if current user is root # @stderr diagnostics information is displayed @@ -632,6 +408,17 @@ Linux::requireExecutedAsUser() { fi } + +# @description ensure command tar is available +# @exitcode 1 if tar command not available +# @stderr diagnostics information is displayed +Linux::requireTarCommand() { + export REQUIRE_FUNCTION_LINUX_REQUIRE_TAR_COMMAND_LOADED=1 + + Assert::commandExists tar +} + + declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" @@ -670,30 +457,94 @@ Log::computeDuration() { fi } -# @description log message to file + +# @description Display message using debug color (gray) # @arg $1 message:String the message to display -Log::logInfo() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then - Log::logMessage "${2:-INFO}" "$1" +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayDebug() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then + Log::computeDuration + echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logDebug "$1" } -# @description log message to file + +# @description Display message using error color (red) # @arg $1 message:String the message to display -Log::logDebug() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then - Log::logMessage "${2:-DEBUG}" "$1" +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayError() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then + Log::computeDuration + echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logError "$1" +} + + +# @description Display message using info color (bg light blue/fg white) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayInfo() { + local type="${2:-INFO}" + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logInfo "$1" "${type}" +} + + +# @description Display message using skip color (yellow) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displaySkipped() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__SKIPPED_COLOR}SKIPPED - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logSkipped "$1" +} + + +# @description Display message using warning color (yellow) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayWarning() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then + Log::computeDuration + echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logWarning "$1" +} + + +# @description Display message using error color (red) and exit immediately with error status 1 +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::fatal() { + Log::computeDuration + echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + Log::logFatal "$1" + exit 1 } + # @description log message to file # @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" +Log::logDebug() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then + Log::logMessage "${2:-DEBUG}" "$1" fi } + # @description log message to file # @arg $1 message:String the message to display Log::logError() { @@ -702,20 +553,6 @@ Log::logError() { fi } -# @description check if tty (interactive mode) is active -# @noargs -# @exitcode 1 if tty not active -# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive -# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive -Assert::tty() { - if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then - return 1 - fi - if [[ "${INTERACTIVE:-0}" = "1" ]]; then - return 0 - fi - tty -s -} # @description log message to file # @arg $1 message:String the message to display @@ -723,6 +560,16 @@ Log::logFatal() { Log::logMessage "${2:-FATAL}" "$1" } + +# @description log message to file +# @arg $1 message:String the message to display +Log::logInfo() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then + Log::logMessage "${2:-INFO}" "$1" + fi +} + + # @description Internal: common log message # @example text # [date]|[levelMsg]|message @@ -736,9 +583,18 @@ Log::logFatal() { # @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty # @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages # @stderr diagnostics information is displayed -# @require Env::requireLoad -# @require Log::requireLoad Log::logMessage() { + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Log::requireLoad has not been loaded" + exit 1 + fi + local levelMsg="$1" local msg="$2" local date @@ -750,6 +606,83 @@ Log::logMessage() { fi } + +# @description log message to file +# @arg $1 message:String the message to display +Log::logSkipped() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then + Log::logMessage "${2:-SKIPPED}" "$1" + fi +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logWarning() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then + Log::logMessage "${2:-WARNING}" "$1" + fi +} + + +# @description activate or not Log::display* and Log::log* functions +# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL +# environment variables loaded by Env::requireLoad +# try to create log file and rotate it if necessary +# @noargs +# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable +# @env BASH_FRAMEWORK_DISPLAY_LEVEL int +# @env BASH_FRAMEWORK_LOG_LEVEL int +# @env BASH_FRAMEWORK_LOG_FILE String +# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 +# @exitcode 0 always successful +# @stderr diagnostics information about log file is displayed +Log::requireLoad() { + export REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement UI::requireTheme has not been loaded" + exit 1 + fi + + if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + export BASH_FRAMEWORK_LOG_LEVEL + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then + if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 + fi + elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + # will always be created even if not in info level + Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" + if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then + Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + fi + fi +} + + # @description To be called before logging in the log file # @arg $1 file:string log file name # @arg $2 maxLogFilesCount:int maximum number of log files @@ -772,25 +705,22 @@ Log::rotate() { fi } -# @description log message to file -# @arg $1 message:String the message to display -Log::logSkipped() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then - Log::logMessage "${2:-SKIPPED}" "$1" + +# @description draw a line with the character passed in parameter repeated depending on terminal width +# @arg $1 character:String character to use as separator (default value #) +UI::drawLine() { + local character="${1:-#}" + local -i width=${COLUMNS:-0} + if ((width == 0)) && [[ -t 1 ]]; then + width=$(tput cols) + fi + if ((width == 0)); then + width=80 fi + printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") + echo } -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" - fi - done -} # @description load color theme # @noargs @@ -798,51 +728,107 @@ Env::pathPrepend() { # @env LOAD_THEME int 0 to avoid loading theme # @exitcode 0 always successful UI::requireTheme() { + export REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + if [[ "${LOAD_THEME:-1}" = "1" ]]; then UI::theme "${BASH_FRAMEWORK_THEME-default}" fi } - + + +# @description load colors theme constants +# @warning if tty not opened, noColor theme will be chosen +# @arg $1 theme:String the theme to use (default, noColor) +# @arg $@ args:String[] +# @set __ERROR_COLOR String indicate error status +# @set __INFO_COLOR String indicate info status +# @set __SUCCESS_COLOR String indicate success status +# @set __WARNING_COLOR String indicate warning status +# @set __SKIPPED_COLOR String indicate skipped status +# @set __DEBUG_COLOR String indicate debug status +# @set __HELP_COLOR String indicate help status +# @set __TEST_COLOR String not used +# @set __TEST_ERROR_COLOR String not used +# @set __HELP_TITLE_COLOR String used to display help title in help strings +# @set __HELP_OPTION_COLOR String used to display highlight options in help strings +# +# @set __RESET_COLOR String reset default color +# +# @set __HELP_EXAMPLE String to remove +# @set __HELP_TITLE String to remove +# @set __HELP_NORMAL String to remove +# shellcheck disable=SC2034 +UI::theme() { + local theme="${1-default}" + if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then + theme="noColor" + fi + case "${theme}" in + default | default-force) + theme="default" + ;; + noColor) ;; + *) + Log::fatal "invalid theme provided" + ;; + esac + if [[ "${theme}" = "default" ]]; then + BASH_FRAMEWORK_THEME="default" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='\e[31m' # Red + __INFO_COLOR='\e[44m' # white on lightBlue + __SUCCESS_COLOR='\e[32m' # Green + __WARNING_COLOR='\e[33m' # Yellow + __SKIPPED_COLOR='\e[33m' # Yellow + __DEBUG_COLOR='\e[37m' # Gray + __HELP_COLOR='\e[7;49;33m' # Black on Gold + __TEST_COLOR='\e[100m' # Light magenta + __TEST_ERROR_COLOR='\e[41m' # white on red + __HELP_TITLE_COLOR="\e[1;37m" # Bold + __HELP_OPTION_COLOR="\e[1;34m" # Blue + # Internal: reset color + __RESET_COLOR='\e[0m' # Reset Color + # shellcheck disable=SC2155,SC2034 + __HELP_EXAMPLE="$(echo -e "\e[2;97m")" + # shellcheck disable=SC2155,SC2034 + __HELP_TITLE="$(echo -e "\e[1;37m")" + # shellcheck disable=SC2155,SC2034 + __HELP_NORMAL="$(echo -e "\033[0m")" + else + BASH_FRAMEWORK_THEME="noColor" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='' + __INFO_COLOR='' + __SUCCESS_COLOR='' + __WARNING_COLOR='' + __SKIPPED_COLOR='' + __DEBUG_COLOR='' + __HELP_COLOR='' + __TEST_COLOR='' + __TEST_ERROR_COLOR='' + __HELP_TITLE_COLOR='' + __HELP_OPTION_COLOR='' + # Internal: reset color + __RESET_COLOR='' + __HELP_EXAMPLE='' + __HELP_TITLE='' + __HELP_NORMAL='' + fi +} # FUNCTIONS -facade_main_installsh() { -BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/." && pwd -P)" -if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then - FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" -else - # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR - FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" -fi -FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" -FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" - -# @require BashTools::Conf::requireLoad -if [[ -f "${HOME}/.bash-tools/.env" ]]; then - export BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") -fi -# REQUIRES -Env::requireLoad -UI::requireTheme -Log::requireLoad -BashTools::Conf::requireLoad -Compiler::Facade::requireCommandBinDir -Linux::requireExecutedAsUser - -# @require Compiler::Facade::requireCommandBinDir -# shellcheck disable=SC2034 - -declare copyrightBeginYear="2020" -declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" declare -a BASH_FRAMEWORK_ARGV_FILTERED=() +beforeParseCallback() { + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + copyrightCallback() { - if [[ -z "${copyrightBeginYear}" ]]; then - copyrightBeginYear="$(date +%Y)" - fi - echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" + # + echo "Copyright (c) 2022-now François Chastanet" } # shellcheck disable=SC2317 # if function is overridden @@ -874,13 +860,14 @@ updateArgListQuietCallback() { :; } # shellcheck disable=SC2317 # if function is overridden optionHelpCallback() { - installCommand help + Log::displayError "optionHelpCallback needs to be overridden" exit 0 } # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { - echo "${SCRIPT_NAME} version 1.0" + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version 3.0" exit 0 } @@ -898,21 +885,22 @@ optionEnvFileCallback() { optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_INFO} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionDebugVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_DEBUG} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionTraceVerboseCallback() { + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vvv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_TRACE} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } getLevel() { @@ -936,6 +924,7 @@ getLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -957,6 +946,7 @@ getVerboseLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -967,7 +957,7 @@ optionDisplayLevelCallback() { logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -976,19 +966,20 @@ optionLogLevelCallback() { local logLevel verboseLevel logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionLogFileCallback() { local logFile="$2" - echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionQuietCallback() { - echo "BASH_FRAMEWORK_QUIET_MODE=1" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_QUIET_MODE=1" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1019,6 +1010,7 @@ optionBashFrameworkConfigCallback() { defaultFrameworkConfig="$( cat <<'EOF' + # copied from src/_includes/.framework-config.default # shellcheck disable=SC2034 @@ -1036,7 +1028,7 @@ NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-conf # describe the files that are allowed to not have an associated bats file BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" # describe the files that are allowed to not have a function matching the filename -FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|\.tpl$|/testsData/|^manualTests/|\.bats$}" +FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|/testsData/|^manualTests/|\.bats$}" # Source directories if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then FRAMEWORK_SRC_DIRS=( @@ -1052,6 +1044,7 @@ BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" + EOF )" @@ -1060,7 +1053,7 @@ overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" commandOptionParseFinished() { # load default template framework config defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" - echo "${defaultFrameworkConfig}" > "${defaultEnvFile}" + echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") if [[ -f "${envFile}" ]]; then files+=("${envFile}") @@ -1078,427 +1071,636 @@ commandOptionParseFinished() { fi } -installCommand() { - local options_parse_cmd="$1" - shift || true - if [[ "${options_parse_cmd}" = "parse" ]]; then - local -i options_parse_optionParsedCountOptionBashFrameworkConfig - ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true - optionConfig="0" - local -i options_parse_optionParsedCountOptionConfig - ((options_parse_optionParsedCountOptionConfig = 0)) || true - optionInfoVerbose="0" - local -i options_parse_optionParsedCountOptionInfoVerbose - ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true - optionDebugVerbose="0" - local -i options_parse_optionParsedCountOptionDebugVerbose - ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true - optionTraceVerbose="0" - local -i options_parse_optionParsedCountOptionTraceVerbose - ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionNoColor="0" - local -i options_parse_optionParsedCountOptionNoColor - ((options_parse_optionParsedCountOptionNoColor = 0)) || true - optionTheme="default" - local -i options_parse_optionParsedCountOptionTheme - ((options_parse_optionParsedCountOptionTheme = 0)) || true - optionHelp="0" - local -i options_parse_optionParsedCountOptionHelp - ((options_parse_optionParsedCountOptionHelp = 0)) || true - optionVersion="0" - local -i options_parse_optionParsedCountOptionVersion - ((options_parse_optionParsedCountOptionVersion = 0)) || true - optionQuiet="0" - local -i options_parse_optionParsedCountOptionQuiet - ((options_parse_optionParsedCountOptionQuiet = 0)) || true - local -i options_parse_optionParsedCountOptionLogLevel - ((options_parse_optionParsedCountOptionLogLevel = 0)) || true - local -i options_parse_optionParsedCountOptionLogFile - ((options_parse_optionParsedCountOptionLogFile = 0)) || true - local -i options_parse_optionParsedCountOptionDisplayLevel - ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true - # shellcheck disable=SC2034 - local -i options_parse_parsedArgIndex=0 - while (($# > 0)); do - local options_parse_arg="$1" - local argOptDefaultBehavior=0 - case "${options_parse_arg}" in - # Option 1/14 - # Option optionBashFrameworkConfig --bash-framework-config variableType String min 0 max 1 authorizedValues '' regexp '' - --bash-framework-config) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) - # shellcheck disable=SC2034 - optionBashFrameworkConfig="$1" - optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" - ;; - # Option 2/14 - # Option optionConfig --config variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --config) - # shellcheck disable=SC2034 - optionConfig="1" - if ((options_parse_optionParsedCountOptionConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionConfig)) - ;; - # Option 3/14 - # Option optionInfoVerbose --verbose|-v variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --verbose | -v) - # shellcheck disable=SC2034 - optionInfoVerbose="1" - if ((options_parse_optionParsedCountOptionInfoVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionInfoVerbose)) - optionInfoVerboseCallback "${options_parse_arg}" - updateArgListInfoVerboseCallback "${options_parse_arg}" - ;; - # Option 4/14 - # Option optionDebugVerbose -vv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vv) - # shellcheck disable=SC2034 - optionDebugVerbose="1" - if ((options_parse_optionParsedCountOptionDebugVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDebugVerbose)) - optionDebugVerboseCallback "${options_parse_arg}" - updateArgListDebugVerboseCallback "${options_parse_arg}" - ;; - # Option 5/14 - # Option optionTraceVerbose -vvv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vvv) - # shellcheck disable=SC2034 - optionTraceVerbose="1" - if ((options_parse_optionParsedCountOptionTraceVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTraceVerbose)) - optionTraceVerboseCallback "${options_parse_arg}" - updateArgListTraceVerboseCallback "${options_parse_arg}" - ;; - # Option 6/14 - # Option optionEnvFiles --env-file variableType StringArray min 0 max -1 authorizedValues '' regexp '' - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - ;; - # Option 7/14 - # Option optionNoColor --no-color variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --no-color) - # shellcheck disable=SC2034 - optionNoColor="1" - if ((options_parse_optionParsedCountOptionNoColor >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionNoColor)) - optionNoColorCallback "${options_parse_arg}" - updateArgListNoColorCallback "${options_parse_arg}" - ;; - # Option 8/14 - # Option optionTheme --theme variableType String min 0 max 1 authorizedValues 'default|default-force|noColor' regexp '' - --theme) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default|default-force|noColor)" - return 1 - fi - if ((options_parse_optionParsedCountOptionTheme >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTheme)) - # shellcheck disable=SC2034 - optionTheme="$1" - optionThemeCallback "${options_parse_arg}" "${optionTheme}" - updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" - ;; - # Option 9/14 - # Option optionHelp --help|-h variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --help | -h) - # shellcheck disable=SC2034 - optionHelp="1" - if ((options_parse_optionParsedCountOptionHelp >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionHelp)) - optionHelpCallback "${options_parse_arg}" - ;; - # Option 10/14 - # Option optionVersion --version variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --version) - # shellcheck disable=SC2034 - optionVersion="1" - if ((options_parse_optionParsedCountOptionVersion >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionVersion)) - optionVersionCallback "${options_parse_arg}" - ;; - # Option 11/14 - # Option optionQuiet --quiet|-q variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --quiet | -q) - # shellcheck disable=SC2034 - optionQuiet="1" - if ((options_parse_optionParsedCountOptionQuiet >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionQuiet)) - optionQuietCallback "${options_parse_arg}" - updateArgListQuietCallback "${options_parse_arg}" - ;; - # Option 12/14 - # Option optionLogLevel --log-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --log-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogLevel)) - # shellcheck disable=SC2034 - optionLogLevel="$1" - optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - ;; - # Option 13/14 - # Option optionLogFile --log-file variableType String min 0 max 1 authorizedValues '' regexp '' - --log-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogFile >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogFile)) - # shellcheck disable=SC2034 - optionLogFile="$1" - optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" - updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" - ;; - # Option 14/14 - # Option optionDisplayLevel --display-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --display-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionDisplayLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDisplayLevel)) - # shellcheck disable=SC2034 - optionDisplayLevel="$1" - optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - ;; - -*) - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" - return 1 - fi - ;; - *) - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided" - return 1 - fi - ;; - esac - shift || true - done - commandOptionParseFinished - Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" - Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" - elif [[ "${options_parse_cmd}" = "help" ]]; then - Array::wrap2 " " 80 0 "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" "Install dependent softwares and configuration needed to use bash-tools -- GNU parallel -- Install default configuration files" - echo - - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "${SCRIPT_NAME}" "[OPTIONS]")" - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ - "${SCRIPT_NAME}" \ - "[--bash-framework-config ]" "[--config]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--no-color]" "[--theme ]" "[--help|-h]" "[--version]" "[--quiet|-q]" "[--log-level ]" "[--log-file ]" "[--display-level ]")" - echo - echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(use\ alternate\ bash\ framework\ configuration.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ configuration) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(info\ level\ verbose\ mode\ \(alias\ of\ --display-level\ INFO\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(debug\ level\ verbose\ mode\ \(alias\ of\ --display-level\ DEBUG\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(trace\ level\ verbose\ mode\ \(alias\ of\ --display-level\ TRACE\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Load\ the\ specified\ env\ file\ \(deprecated\,\ please\ use\ --bash-framework-config\ option\ instead\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Produce\ monochrome\ output.\ alias\ of\ --theme\ noColor.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' - echo ' Possible values: default|default-force|noColor' - echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ this\ command\ help) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Print\ version\ information\ and\ quit) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(quiet\ mode\,\ doesn\'t\ display\ any\ output) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ file) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(set\ display\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo - echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo '1.0' - echo - echo -e "${__HELP_TITLE_COLOR}AUTHOR:${__RESET_COLOR}" - echo '[François Chastanet](https://github.com/fchastanet)' - echo - echo -e "${__HELP_TITLE_COLOR}SOURCE FILE:${__RESET_COLOR}" - echo 'https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/build/install.sh' - echo - echo -e "${__HELP_TITLE_COLOR}LICENSE:${__RESET_COLOR}" - echo 'MIT License' - echo - Array::wrap2 ' ' 76 4 "$(copyrightCallback)" - else - Log::displayError "Command ${SCRIPT_NAME} - Option command invalid: '${options_parse_cmd}'" - return 1 - fi + +# shellcheck disable=SC2034 +declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" + +optionHelpCallback() { + installCommandHelp + exit 0 } -# @require Linux::requireExecutedAsUser -run() { - if ! command -v parallel &>/dev/null; then - Log::displayInfo "We will install GNU parallel software, please enter you sudo password" - sudo apt update || true - if sudo apt install -y parallel; then - # remove parallel nagware - mkdir -p ~/.parallel - touch ~/.parallel/will-cite - else - Log::displayWarning "Impossible to install GNU parallel, please install it manually" - fi + +# ------------------------------------------ +# Command installCommand +# ------------------------------------------ + +# options variables initialization +declare optionHelp="0" +declare optionConfig="0" +declare optionBashFrameworkConfig="" +declare optionInfoVerbose="0" +declare optionDebugVerbose="0" +declare optionTraceVerbose="0" +declare -a optionEnvFiles=() +declare optionLogLevel="" +declare optionLogFile="" +declare optionDisplayLevel="" +declare optionNoColor="0" +declare optionTheme="default" +declare optionVersion="0" +declare optionQuiet="0" +declare optionSkipBackup="0" +# arguments variables initialization +# @description parse command options and arguments for installCommand +installCommandParse() { + Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" + Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" + optionHelp="0" + local -i options_parse_optionParsedCountOptionHelp + ((options_parse_optionParsedCountOptionHelp = 0)) || true + optionConfig="0" + local -i options_parse_optionParsedCountOptionConfig + ((options_parse_optionParsedCountOptionConfig = 0)) || true + optionBashFrameworkConfig="" + local -i options_parse_optionParsedCountOptionBashFrameworkConfig + ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true + optionInfoVerbose="0" + local -i options_parse_optionParsedCountOptionInfoVerbose + ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true + optionDebugVerbose="0" + local -i options_parse_optionParsedCountOptionDebugVerbose + ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true + optionTraceVerbose="0" + local -i options_parse_optionParsedCountOptionTraceVerbose + ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true + + optionLogLevel="" + local -i options_parse_optionParsedCountOptionLogLevel + ((options_parse_optionParsedCountOptionLogLevel = 0)) || true + optionLogFile="" + local -i options_parse_optionParsedCountOptionLogFile + ((options_parse_optionParsedCountOptionLogFile = 0)) || true + optionDisplayLevel="" + local -i options_parse_optionParsedCountOptionDisplayLevel + ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true + optionNoColor="0" + local -i options_parse_optionParsedCountOptionNoColor + ((options_parse_optionParsedCountOptionNoColor = 0)) || true + optionTheme="default" + local -i options_parse_optionParsedCountOptionTheme + ((options_parse_optionParsedCountOptionTheme = 0)) || true + optionVersion="0" + local -i options_parse_optionParsedCountOptionVersion + ((options_parse_optionParsedCountOptionVersion = 0)) || true + optionQuiet="0" + local -i options_parse_optionParsedCountOptionQuiet + ((options_parse_optionParsedCountOptionQuiet = 0)) || true + optionSkipBackup="0" + local -i options_parse_optionParsedCountOptionSkipBackup + ((options_parse_optionParsedCountOptionSkipBackup = 0)) || true + + + # shellcheck disable=SC2034 + local -i options_parse_parsedArgIndex=0 + while (($# > 0)); do + local options_parse_arg="$1" + local argOptDefaultBehavior=0 + case "${options_parse_arg}" in + # Option 1/15 + # optionHelp alts --help|-h + # type: Boolean min 0 max 1 + --help | -h) + # shellcheck disable=SC2034 + optionHelp="1" + + if ((options_parse_optionParsedCountOptionHelp >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionHelp)) + optionHelpCallback "${options_parse_arg}" "${optionHelp}" + + ;; + + # Option 2/15 + # optionConfig alts --config + # type: Boolean min 0 max 1 + --config) + # shellcheck disable=SC2034 + optionConfig="1" + + if ((options_parse_optionParsedCountOptionConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionConfig)) + ;; + + # Option 3/15 + # optionBashFrameworkConfig alts --bash-framework-config + # type: String min 0 max 1 + --bash-framework-config) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) + # shellcheck disable=SC2034 + optionBashFrameworkConfig="$1" + optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" + + ;; + + # Option 4/15 + # optionInfoVerbose alts --verbose|-v + # type: Boolean min 0 max 1 + --verbose | -v) + # shellcheck disable=SC2034 + optionInfoVerbose="1" + + if ((options_parse_optionParsedCountOptionInfoVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionInfoVerbose)) + optionInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + updateArgListInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + ;; + + # Option 5/15 + # optionDebugVerbose alts -vv + # type: Boolean min 0 max 1 + -vv) + # shellcheck disable=SC2034 + optionDebugVerbose="1" + + if ((options_parse_optionParsedCountOptionDebugVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDebugVerbose)) + optionDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + updateArgListDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + ;; + + # Option 6/15 + # optionTraceVerbose alts -vvv + # type: Boolean min 0 max 1 + -vvv) + # shellcheck disable=SC2034 + optionTraceVerbose="1" + + if ((options_parse_optionParsedCountOptionTraceVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTraceVerbose)) + optionTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + updateArgListTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + ;; + + # Option 7/15 + # optionEnvFiles alts --env-file + # type: StringArray min 0 max -1 + --env-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + ((++options_parse_optionParsedCountOptionEnvFiles)) + optionEnvFiles+=("$1") + optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + ;; + + # Option 8/15 + # optionLogLevel alts --log-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --log-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogLevel)) + # shellcheck disable=SC2034 + optionLogLevel="$1" + optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + ;; + + # Option 9/15 + # optionLogFile alts --log-file + # type: String min 0 max 1 + --log-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogFile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogFile)) + # shellcheck disable=SC2034 + optionLogFile="$1" + optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + ;; + + # Option 10/15 + # optionDisplayLevel alts --display-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --display-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionDisplayLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDisplayLevel)) + # shellcheck disable=SC2034 + optionDisplayLevel="$1" + optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + ;; + + # Option 11/15 + # optionNoColor alts --no-color + # type: Boolean min 0 max 1 + --no-color) + # shellcheck disable=SC2034 + optionNoColor="1" + + if ((options_parse_optionParsedCountOptionNoColor >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionNoColor)) + optionNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + updateArgListNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + ;; + + # Option 12/15 + # optionTheme alts --theme + # type: String min 0 max 1 + # authorizedValues: default|default-force|noColor + --theme) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ default|default-force|noColor ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionTheme >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTheme)) + # shellcheck disable=SC2034 + optionTheme="$1" + optionThemeCallback "${options_parse_arg}" "${optionTheme}" + + updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" + + ;; + + # Option 13/15 + # optionVersion alts --version + # type: Boolean min 0 max 1 + --version) + # shellcheck disable=SC2034 + optionVersion="1" + + if ((options_parse_optionParsedCountOptionVersion >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionVersion)) + optionVersionCallback "${options_parse_arg}" "${optionVersion}" + + ;; + + # Option 14/15 + # optionQuiet alts --quiet|-q + # type: Boolean min 0 max 1 + --quiet | -q) + # shellcheck disable=SC2034 + optionQuiet="1" + + if ((options_parse_optionParsedCountOptionQuiet >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionQuiet)) + optionQuietCallback "${options_parse_arg}" "${optionQuiet}" + + updateArgListQuietCallback "${options_parse_arg}" "${optionQuiet}" + + ;; + + # Option 15/15 + # optionSkipBackup alts --skip-backup + # type: Boolean min 0 max 1 + --skip-backup) + # shellcheck disable=SC2034 + optionSkipBackup="1" + + if ((options_parse_optionParsedCountOptionSkipBackup >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionSkipBackup)) + ;; + + -*) + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" + return 1 + fi + ;; + *) + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + # too much args and no unknownArgumentCallbacks configured + Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" + return 1 + fi + ((incrementArg = 0)) + if ((incrementArg == 1)); then + ((++options_parse_parsedArgIndex)) + fi + ;; + esac + shift || true + done || return $? + + commandOptionParseFinished + +} + +# @description display command options and arguments help for installCommand +installCommandHelp() { + echo -e "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" + Array::wrap2 ' ' 76 4 " " "Installs bash-tools." + + echo + echo + + # ------------------------------------------ + # usage section + # ------------------------------------------ + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "install [OPTIONS] " + echo + # ------------------------------------------ + # usage/options section + # ------------------------------------------ + optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--skip-backup]" + ) + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ + "install" "${optionsAltList[@]}" + echo + + # ------------------------------------------ + # options section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays this command help" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays configuration" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" + Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log file" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set display level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." + echo + Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" + echo + + Array::wrap2 ' ' 76 6 " Default value: " "default" + echo + + echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Print version information and quit." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." + echo + + + echo + echo -e "${__HELP_TITLE_COLOR}OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--skip-backup${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "skip backup of ~/bash-tools." + echo + + + # ------------------------------------------ + # longDescription section + # ------------------------------------------ + echo + echo + echo -e "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" + declare -a installCommandLongDescription=( + "Installs dependent softwares and configuration needed to use bash-tools:" + "- GNU parallel" + "- Installs or updates default configuration files" + " If ~/.bash-tools directory already exists, a backup will be done before" + ) + Array::wrap2 ' ' 76 0 "${installCommandLongDescription[@]}" + echo + # ------------------------------------------ + # version section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" + echo "3.0" + # ------------------------------------------ + # author section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}AUTHOR: ${__RESET_COLOR}" + echo "[François Chastanet](https://github.com/fchastanet)" + # ------------------------------------------ + # sourceFile section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/build/install/binary-install.yaml" + # ------------------------------------------ + # license section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}LICENSE: ${__RESET_COLOR}" + echo "MIT License" + # ------------------------------------------ + # copyright section + # ------------------------------------------ + Array::wrap2 ' ' 76 0 "$(copyrightCallback)" +} + + +beforeParseCallback + +installCommandParse "$@" +MAIN_FUNCTION_NAME="main" +main() { + +Linux::requireExecutedAsUser +Linux::requireTarCommand + +if ! command -v parallel &>/dev/null; then + Log::displayInfo "We will install GNU parallel software, please enter you sudo password" + sudo apt-get update || true + if sudo apt-get install -y parallel; then + # remove parallel nagware + mkdir -p "${HOME}/.parallel" + touch "${HOME}/.parallel/will-cite" else - Log::displaySkipped "parallel is already installed" + Log::displayWarning "Impossible to install GNU parallel, please install it manually" fi +else + Log::displaySkipped "parallel is already installed" +fi - if [[ -d "${HOME}/.bash-tools" ]]; then - Log::displayInfo "Updating configuration" - cp -R --no-clobber "${BASH_TOOLS_ROOT_DIR}/conf/." "${HOME}/.bash-tools" - if [[ "${BASE_DIR}/conf/.env" -nt "${HOME}/.bash-tools/.env" ]]; then - Log::displayWarning "${BASE_DIR}/conf/.env is newer than ${HOME}/.bash-tools/.env, compare the files to check if some updates need to be applied" - else - Log::displaySkipped "${HOME}/.bash-tools/.env is up to date" - fi +if [[ -d "${HOME}/.bash-tools" ]]; then + # shellcheck disable=SC2154 + if [[ "${optionSkipBackup}" = "1" ]]; then + Log::displayInfo "Backup of ~/.bash-tools is set to be skipped" else - Log::displayInfo "Installing configuration in ~/.bash-tools" - mkdir -p ~/.bash-tools - cp -R conf/. ~/.bash-tools + BACKUP_DIR="${FRAMEWORK_ROOT_DIR}/logs" \ + Backup::dir "${HOME}" ".bash-tools" fi -} -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null + Log::displayInfo "Updating configuration" + cp -R --no-clobber "${BASH_TOOLS_ROOT_DIR}/conf/." "${HOME}/.bash-tools" + if [[ "${FRAMEWORK_ROOT_DIR}/conf/.env" -nt "${HOME}/.bash-tools/.env" ]]; then + Log::displayWarning "${FRAMEWORK_ROOT_DIR}/conf/.env is newer than ${HOME}/.bash-tools/.env, compare the files to check if some updates need to be applied" + else + Log::displaySkipped "${HOME}/.bash-tools/.env is up to date" + fi else - run + Log::displayInfo "Installing configuration in ~/.bash-tools" + mkdir -p "${HOME}/.bash-tools" + cp -R "${BASE_DIR}/conf/." "${HOME}/.bash-tools" fi } -facade_main_installsh "$@" +# if file is sourced avoid calling main function +# shellcheck disable=SC2178 +BASH_SOURCE=".$0" # cannot be changed in bash +# shellcheck disable=SC2128 +if test ".$0" == ".${BASH_SOURCE}"; then + if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then + main "$@" &>/dev/null + else + main "$@" + fi +fi diff --git a/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml b/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml index 22e7a3b3..98f4a082 100644 --- a/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml +++ b/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml @@ -12,7 +12,8 @@ binData: commands: default: functionName: mysql2pumlCommand - version: "1.0" + version: "3.0" + copyrightBeginYear: 2021 commandName: mysql2puml beforeParseCallbacks: - beforeParseCallback diff --git a/src/_binaries/Converters/mysql2puml/mysql2puml-options.sh b/src/_binaries/Converters/mysql2puml/mysql2puml-options.sh index f5d4da23..a02e16b8 100755 --- a/src/_binaries/Converters/mysql2puml/mysql2puml-options.sh +++ b/src/_binaries/Converters/mysql2puml/mysql2puml-options.sh @@ -1,10 +1,6 @@ #!/usr/bin/env bash # shellcheck disable=SC2034 -declare copyrightBeginYear="2020" -# shellcheck disable=SC2034 -declare versionNumber="1.0" - declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" optionHelpCallback() { diff --git a/src/_binaries/Converters/mysql2puml/mysql2puml.bats b/src/_binaries/Converters/mysql2puml/mysql2puml.bats index 8a3151b9..40fd9dd4 100755 --- a/src/_binaries/Converters/mysql2puml/mysql2puml.bats +++ b/src/_binaries/Converters/mysql2puml/mysql2puml.bats @@ -21,7 +21,7 @@ function Converters::mysql2puml::display_help { #@test function Converters::mysql2puml::display_version { #@test run "${binDir}/mysql2puml" --version 2>&1 assert_success - assert_line --index 0 "mysql2puml version 1.0" + assert_line --index 0 "mysql2puml version 3.0" } function Converters::mysql2puml::bad_skin_file { #@test diff --git a/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt b/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt index 6e850ec4..e98aa927 100644 --- a/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt +++ b/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt @@ -70,11 +70,11 @@ LIST OF AVAILABLE SKINS: - default -VERSION: 1.0 +VERSION: 3.0 AUTHOR: [François Chastanet](https://github.com/fchastanet) SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml LICENSE: MIT License -Copyright (c) 2020-now François Chastanet +Copyright (c) 2021-now François Chastanet diff --git a/src/_binaries/Database/dbImport/binary-dbImport.yaml b/src/_binaries/Database/dbImport/binary-dbImport.yaml index 0b291789..5f774fcb 100644 --- a/src/_binaries/Database/dbImport/binary-dbImport.yaml +++ b/src/_binaries/Database/dbImport/binary-dbImport.yaml @@ -19,7 +19,8 @@ binData: commands: default: functionName: dbImportCommand - version: "2.0" + version: "3.0" + copyrightBeginYear: 2020 commandName: dbImport beforeParseCallbacks: - beforeParseCallback diff --git a/src/_binaries/Database/dbImport/dbImport-options.sh b/src/_binaries/Database/dbImport/dbImport-options.sh index 4dbceee9..7c3980a2 100755 --- a/src/_binaries/Database/dbImport/dbImport-options.sh +++ b/src/_binaries/Database/dbImport/dbImport-options.sh @@ -1,10 +1,6 @@ #!/usr/bin/env bash # shellcheck disable=SC2034 -declare copyrightBeginYear="2020" -# shellcheck disable=SC2034 -declare versionNumber="2.0" - declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" declare defaultTargetCharacterSet="" diff --git a/src/_binaries/Database/dbImport/testsData/dbImport.help.txt b/src/_binaries/Database/dbImport/testsData/dbImport.help.txt index a5f2038d..413d5460 100644 --- a/src/_binaries/Database/dbImport/testsData/dbImport.help.txt +++ b/src/_binaries/Database/dbImport/testsData/dbImport.help.txt @@ -85,7 +85,7 @@ --quiet, -q {single} Quiet mode, doesn't display any output. -VERSION: 2.0 +VERSION: 3.0 AUTHOR: [François Chastanet](https://github.com/fchastanet) diff --git a/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml b/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml index c7c18a62..fcddfbea 100644 --- a/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml +++ b/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml @@ -16,7 +16,8 @@ binData: commands: default: functionName: dbImportProfileCommand - version: "2.0" + version: "3.0" + copyrightBeginYear: 2021 commandName: dbImportProfile beforeParseCallbacks: - beforeParseCallback diff --git a/src/_binaries/Database/dbImportProfile/dbImportProfile-options.sh b/src/_binaries/Database/dbImportProfile/dbImportProfile-options.sh index c4f1ffac..7cf29b78 100755 --- a/src/_binaries/Database/dbImportProfile/dbImportProfile-options.sh +++ b/src/_binaries/Database/dbImportProfile/dbImportProfile-options.sh @@ -1,10 +1,6 @@ #!/usr/bin/env bash declare defaultFromDsn="default.remote" # shellcheck disable=SC2034 -declare versionNumber="2.0" -# shellcheck disable=SC2034 -declare copyrightBeginYear="2020" -# shellcheck disable=SC2034 declare PROFILES_DIR declare HOME_PROFILES_DIR diff --git a/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt b/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt index 5246d038..f5e45f68 100644 --- a/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt +++ b/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt @@ -86,11 +86,11 @@ Allows to override profiles defined in "Default profiles directory" - default.remote - localhost-root -VERSION: 2.0 +VERSION: 3.0 AUTHOR: [François Chastanet](https://github.com/fchastanet) SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml LICENSE: MIT License -Copyright (c) 2020-now François Chastanet +Copyright (c) 2021-now François Chastanet diff --git a/src/_binaries/Database/dbImportStream/binary-dbImportStream.yaml b/src/_binaries/Database/dbImportStream/binary-dbImportStream.yaml index 57ad5b2d..d9843b92 100644 --- a/src/_binaries/Database/dbImportStream/binary-dbImportStream.yaml +++ b/src/_binaries/Database/dbImportStream/binary-dbImportStream.yaml @@ -17,7 +17,8 @@ binData: commands: default: functionName: dbImportStreamCommand - version: "2.0" + version: "3.0" + copyrightBeginYear: 2020 commandName: dbImportStream beforeParseCallbacks: - beforeParseCallback diff --git a/src/_binaries/Database/dbImportStream/dbImportStream-options.sh b/src/_binaries/Database/dbImportStream/dbImportStream-options.sh index 72e208de..7a7d923a 100755 --- a/src/_binaries/Database/dbImportStream/dbImportStream-options.sh +++ b/src/_binaries/Database/dbImportStream/dbImportStream-options.sh @@ -3,10 +3,6 @@ declare PROFILES_DIR declare HOME_PROFILES_DIR # shellcheck disable=SC2034 -declare versionNumber="2.0" -# shellcheck disable=SC2034 -declare copyrightBeginYear="2020" -# shellcheck disable=SC2034 declare defaultFromDsn="default.remote" beforeParseCallback() { diff --git a/src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt b/src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt index 9b5bc05d..547538fd 100644 --- a/src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt +++ b/src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt @@ -85,7 +85,7 @@ Allows to override profiles defined in 'Default profiles directory' - default.remote - localhost-root -VERSION: 2.0 +VERSION: 3.0 AUTHOR: [François Chastanet](https://github.com/fchastanet) diff --git a/src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml b/src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml index 12d3701e..fb70b4b6 100644 --- a/src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml +++ b/src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml @@ -17,7 +17,8 @@ binData: commands: default: functionName: dbQueryAllDatabasesCommand - version: "2.0" + version: "3.0" + copyrightBeginYear: 2020 commandName: dbQueryAllDatabases beforeParseCallbacks: - beforeParseCallback diff --git a/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-options.sh b/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-options.sh index 3ba73815..82e0f446 100755 --- a/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-options.sh +++ b/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-options.sh @@ -5,8 +5,6 @@ declare HOME_QUERIES_DIR declare queryIsFile="0" declare optionSeparator="|" -# shellcheck disable=SC2034 -declare versionNumber="2.0" # shellcheck disable=SC2034 declare copyrightBeginYear="2020" # shellcheck disable=SC2034 diff --git a/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt b/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt index f76e3e14..a32b2348 100644 --- a/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt +++ b/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt @@ -94,7 +94,7 @@  EXAMPLES: dbQueryAllDatabases databaseSize -j 12 --separator "|" --bar 2>/dev/null | column -s "|" -t -n -c 40 -VERSION: 2.0 +VERSION: 3.0 AUTHOR: [François Chastanet](https://github.com/fchastanet) diff --git a/src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml b/src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml index b6f6ceb4..17dcc2a9 100644 --- a/src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml +++ b/src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml @@ -17,7 +17,8 @@ binData: commands: default: functionName: dbScriptAllDatabasesCommand - version: "2.0" + version: "3.0" + copyrightBeginYear: 2020 commandName: dbScriptAllDatabases beforeParseCallbacks: - beforeParseCallback diff --git a/src/_binaries/Database/dbScriptAllDatabases/binary-extractData.yaml b/src/_binaries/Database/dbScriptAllDatabases/binary-extractData.yaml index 3318b9d6..59432ad8 100644 --- a/src/_binaries/Database/dbScriptAllDatabases/binary-extractData.yaml +++ b/src/_binaries/Database/dbScriptAllDatabases/binary-extractData.yaml @@ -15,7 +15,8 @@ binData: commands: default: functionName: extractDataCommand - version: "2.0" + version: "3.0" + copyrightBeginYear: 2020 commandName: extractData beforeParseCallbacks: - beforeParseCallback diff --git a/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-options.sh b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-options.sh index 596994cb..abd36d41 100755 --- a/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-options.sh +++ b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-options.sh @@ -3,10 +3,6 @@ declare SCRIPTS_DIR declare HOME_SCRIPTS_DIR # shellcheck disable=SC2034 -declare versionNumber="2.0" -# shellcheck disable=SC2034 -declare copyrightBeginYear="2020" -# shellcheck disable=SC2034 declare defaultFromDsn="default.remote" # shellcheck disable=SC2034 declare outputDirectory="${HOME}/.bash-tools/output" diff --git a/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt b/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt index d828df61..7bde3898 100644 --- a/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt +++ b/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt @@ -113,7 +113,7 @@ Allows to override queries defined in 'Default scripts directory' update multiple db at once (simple to complex update script) -VERSION: 2.0 +VERSION: 3.0 AUTHOR: [François Chastanet](https://github.com/fchastanet) diff --git a/src/_binaries/Docker/cli.options.tpl b/src/_binaries/Docker/cli.options.tpl deleted file mode 100644 index 1898170b..00000000 --- a/src/_binaries/Docker/cli.options.tpl +++ /dev/null @@ -1,117 +0,0 @@ -% -declare versionNumber="2.0" -declare commandFunctionName="cliCommand" -declare help="easy connection to docker container" -# shellcheck disable=SC2016 -declare longDescription=''' -${__HELP_TITLE}AVAILABLE PROFILES (from ${PROFILES_DIR})${__HELP_NORMAL} -This list can be overridden in ${HOME_PROFILES_DIR} - -${profilesList} - -${__HELP_TITLE}AVAILABLE CONTAINERS:${__HELP_NORMAL} -${containers} - -${__HELP_TITLE}EXAMPLES:${__HELP_EXAMPLE} - to connect to mysql container in bash mode with user mysql - ${SCRIPT_NAME} mysql mysql '/bin/bash' - to connect to web container with user root - ${SCRIPT_NAME} web root -${__HELP_NORMAL} - -${__HELP_TITLE}CREATE NEW PROFILE:${__HELP_NORMAL} -You can create new profiles in ${HOME_PROFILES_DIR}. -This script will be called with the -arguments ${__HELP_OPTION_COLOR}userArg${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}containerArg${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}commandArg${__HELP_NORMAL} -The script has to compute the following -variables ${__HELP_OPTION_COLOR}finalUserArg${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}finalContainerArg${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}finalCommandArg${__HELP_NORMAL} -''' -% -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.base.tpl)" -% -# shellcheck source=/dev/null -source <( - containerArgHelpCallback() { :; } - Options::generateArg \ - --help containerArgHelpCallback \ - --min 0 \ - --max 1 \ - --name "container" \ - --variable-name "containerArg" \ - --function-name containerArgFunction - - userArgHelpCallback() { :; } - Options::generateArg \ - --help userArgHelpCallback \ - --min 0 \ - --max 1 \ - --name "user" \ - --variable-name "userArg" \ - --function-name userArgFunction - - commandArgHelpCallback() { :; } - Options::generateArg \ - --help commandArgHelpCallback \ - --variable-name "commandArg" \ - --min 0 \ - --name "commandArg" \ - --function-name commandArgFunction -) -options+=( - --unknown-option-callback unknownOption - --unknown-argument-callback unknownOption - containerArgFunction - userArgFunction - commandArgFunction -) -Options::generateCommand "${options[@]}" -% - -containerArgHelpCallback() { - Conf::load "cliProfiles" "default" - echo "container should be the name of a profile from profile list," - echo "check containers list below." - echo "If not provided, it will load the container specified in default configuration." - echo "Default configuration: ${__HELP_OPTION_COLOR}${containerArg}${__HELP_NORMAL}" - echo "Default container: ${__HELP_OPTION_COLOR}${finalContainerArg}${__HELP_NORMAL}" -} - -userArgHelpCallback() { - Conf::load "cliProfiles" "default" - echo "user to connect on this container" $'\n' - echo "Default user: ${__HELP_OPTION_COLOR}${finalUserArg}${__HELP_NORMAL}" - echo " loaded from profile selected as first arg" - echo " or deduced from default configuration." $'\n' - echo "Default configuration: ${__HELP_OPTION_COLOR}${containerArg}${__HELP_NORMAL}" $'\n' - echo "if first arg is not a profile" -} - -commandArgHelpCallback() { - Conf::load "cliProfiles" "default" - echo "The command to execute" $'\n' - echo "Default command: ${__HELP_OPTION_COLOR}${finalCommandArg[*]}${__HELP_NORMAL}" - echo " loaded from profile selected as first arg" - echo " or deduced from default configuration." - echo "Default configuration: ${__HELP_OPTION_COLOR}${containerArg}${__HELP_NORMAL}" $'\n' - echo "if first arg is not a profile" -} - -optionHelpCallback() { - local containers - # shellcheck disable=SC2046 - containers="$(Array::wrap2 ", " 80 0 $(docker ps --format '{{.Names}}'))" - local profilesList="" - Conf::load "cliProfiles" "default" - - profilesList="$(Conf::getMergedList "cliProfiles" ".sh" " - " || true)" - - <% ${commandFunctionName} %> help | envsubst - exit 0 -} - -# shellcheck disable=SC2317 # if function is overridden -unknownOption() { - commandArg+=("$1") -} - -<% ${commandFunctionName} %> parse "${BASH_FRAMEWORK_ARGV[@]}" diff --git a/src/_binaries/Docker/cli.sh b/src/_binaries/Docker/cli.sh deleted file mode 100755 index 376c78e2..00000000 --- a/src/_binaries/Docker/cli.sh +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env bash -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/cli -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE -# shellcheck disable=SC2034 - -# constants -declare defaultUserArg="root" -declare -a defaultCommandArg=("//bin/sh") -declare PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/cliProfiles" -declare HOME_PROFILES_DIR="${HOME}/.bash-tools/cliProfiles" - -# option values -declare containerArg="default" -declare finalUserArg="${defaultUserArg}" -declare finalCommandArg=("${defaultCommandArg[@]}") - -# other values -declare copyrightBeginYear="2020" - -.INCLUDE "$(dynamicTemplateDir _binaries/Docker/cli.options.tpl)" - -run() { - # Internal function that can be used in conf profiles to load the dsn file - loadDsn() { - local dsn="$1" - local dsnFile - dsnFile="$(Conf::getAbsoluteFile "dsn" "${dsn}" "env")" - Database::checkDsnFile "${dsnFile}" - # shellcheck source=/conf/dsn/default.local.env - # shellcheck disable=SC1091 - source "${dsnFile}" - } - export -f loadDsn - - # check dependencies - Assert::commandExists docker "check https://docs.docker.com/engine/install/ubuntu/" - - # load default conf file - Conf::load "cliProfiles" "default" - - # try to load config file associated to container if provided - if [[ -n "${containerArg}" ]]; then - Conf::load "cliProfiles" "${containerArg}" || { - # conf file not existing fallback to provided args or to default ones if not provided - finalContainerArg="${containerArg}" - finalUserArg=${userArg:-${finalUserArg}} - finalCommandArg=("${commandArg[@]:-${finalCommandArg[@]}}") - } - fi - - declare -a cmd=() - if Assert::windows; then - # open tty for git bash - cmd+=(winpty) - fi - INTERACTIVE_MODE="-i" - if ! read -r -t 0; then - # command is not piped or TTY not available - INTERACTIVE_MODE+="t" - fi - - cmd+=(docker) - cmd+=(exec) - cmd+=("${INTERACTIVE_MODE}") - # ensure column/lines will be updated upon terminal resize - cmd+=(-e) - cmd+=("COLUMNS=$(tput cols)") - cmd+=(-e) - cmd+=("LINES=$(tput lines)") - - cmd+=("--user=${finalUserArg}") - cmd+=("${finalContainerArg}") - cmd+=("${finalCommandArg[@]}") - if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "0" ]]; then - (echo >&2 MSYS_NO_PATHCONV=1 MSYS2_ARG_CONV_EXCL='*' "${cmd[@]}") - fi - MSYS_NO_PATHCONV=1 MSYS2_ARG_CONV_EXCL='*' "${cmd[@]}" -} - -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run -fi diff --git a/src/_binaries/Docker/cli/binary-cli.yaml b/src/_binaries/Docker/cli/binary-cli.yaml new file mode 100644 index 00000000..743925b5 --- /dev/null +++ b/src/_binaries/Docker/cli/binary-cli.yaml @@ -0,0 +1,51 @@ +extends: + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" + +vars: + SRC_FILE_PATH: src/_binaries/Docker/cli/binary-cli.yaml + +compilerConfig: + targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/cli" + relativeRootDirBasedOnTargetDir: .. + srcDirs: + - ${BASH_TOOLS_ROOT_DIR}/src +binData: + commands: + default: + functionName: cliCommand + version: "3.0" + copyrightBeginYear: 2020 + commandName: cli + beforeParseCallbacks: + - beforeParseCallback + - initConf + unknownOptionCallbacks: + - unknownOption + unknownArgumentCallbacks: + - unknownOption + definitionFiles: + 20: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Docker/cli/cli-options.sh + mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Docker/cli/cli-main.sh + help: Easy connection to docker container. + longDescription: longDescriptionFunction + args: + - type: String + min: 0 + max: 1 + help: containerArgHelpFunction + name: container + variableName: containerArg + - type: String + min: 0 + max: 1 + help: userArgHelpFunction + name: user + variableName: userArg + - type: String + min: 0 + max: 1 + help: commandArgHelpFunction + name: command + variableName: commandArg diff --git a/src/_binaries/Docker/cli/cli-main.sh b/src/_binaries/Docker/cli/cli-main.sh new file mode 100755 index 00000000..38f04288 --- /dev/null +++ b/src/_binaries/Docker/cli/cli-main.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +# try to load config file associated to container if provided +if [[ -n "${containerArg}" ]]; then + Conf::load "cliProfiles" "${containerArg}" || { + # conf file not existing fallback to provided args or to default ones if not provided + finalContainerArg="${containerArg}" + finalUserArg=${userArg:-${finalUserArg}} + finalCommandArg=("${commandArg[@]:-${finalCommandArg[@]}}") + } +fi + +declare -a cmd=() +if Assert::windows; then + # open tty for git bash + cmd+=( + MSYS_NO_PATHCONV=1 + MSYS2_ARG_CONV_EXCL='*' + winpty + ) +fi +INTERACTIVE_MODE="-i" +if ! read -r -t 0; then + # command is not piped or TTY not available + INTERACTIVE_MODE+="t" +fi + +cmd+=(docker) +cmd+=(exec) +cmd+=("${INTERACTIVE_MODE}") +# ensure column/lines will be updated upon terminal resize +cmd+=(-e) +cmd+=("COLUMNS=$(tput cols)") +cmd+=(-e) +cmd+=("LINES=$(tput lines)") + +cmd+=("--user=${finalUserArg}") +cmd+=("${finalContainerArg}") +cmd+=("${finalCommandArg[@]}") + +( + if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "0" ]]; then + set -x + fi + "${cmd[@]}" +) diff --git a/src/_binaries/Docker/cli/cli-options.sh b/src/_binaries/Docker/cli/cli-options.sh new file mode 100755 index 00000000..ee9b169c --- /dev/null +++ b/src/_binaries/Docker/cli/cli-options.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash + +# option values +declare containerArg="default" +declare finalUserArg="${defaultUserArg}" +declare finalCommandArg=("${defaultCommandArg[@]}") + +declare defaultUserArg="root" +declare -a defaultCommandArg=("//bin/sh") +declare PROFILES_DIR +declare HOME_PROFILES_DIR + +beforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad + Linux::requireRealpathCommand + Assert::commandExists docker "check https://docs.docker.com/engine/install/ubuntu/" +} + +# Internal function that can be used in conf profiles to load the dsn file +loadDsn() { + local dsn="$1" + local dsnFile + dsnFile="$(Conf::getAbsoluteFile "dsn" "${dsn}" "env")" + Database::checkDsnFile "${dsnFile}" + # shellcheck source=/conf/dsn/default.local.env + # shellcheck disable=SC1091 + source "${dsnFile}" +} +export -f loadDsn + +initConf() { + PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/cliProfiles" + HOME_PROFILES_DIR="${HOME}/.bash-tools/cliProfiles" + # load default conf file + Conf::load "cliProfiles" "default" +} + +optionHelpCallback() { + cliCommandHelp + exit 0 +} + +longDescriptionFunction() { + echo -e "${__HELP_TITLE}AVAILABLE PROFILES (from ${PROFILES_DIR})${__HELP_NORMAL}" + echo -e "This list can be overridden in ${HOME_PROFILES_DIR}" + echo + Conf::getMergedList "cliProfiles" ".sh" " - " || true + echo + echo -e "${__HELP_TITLE}AVAILABLE CONTAINERS:${__HELP_NORMAL}" + Array::wrap2 ", " 80 0 "$(docker ps --format '{{"{{"}}.Names{{"}}"}}')" + echo + echo -e "${__HELP_TITLE}EXAMPLES:${__HELP_EXAMPLE}" + echo -e " to connect to mysql container in bash mode with user mysql" + echo -e " ${SCRIPT_NAME} mysql mysql '/bin/bash'" + echo -e " to connect to web container with user root" + echo -e " ${SCRIPT_NAME} web root" + echo -e "${__HELP_NORMAL}" + echo + echo -e "${__HELP_TITLE}CREATE NEW PROFILE:${__HELP_NORMAL}" + echo -e "You can create new profiles in ${HOME_PROFILES_DIR}." + echo -e "This script will be called with the" + echo -e "arguments ${__HELP_OPTION_COLOR}userArg${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}containerArg${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}commandArg${__HELP_NORMAL}" + echo -e "The script has to compute the following" + echo -e "variables ${__HELP_OPTION_COLOR}finalUserArg${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}finalContainerArg${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}finalCommandArg${__HELP_NORMAL}" +} + +containerArgHelpFunction() { + Conf::load "cliProfiles" "default" + echo " Container should be the name of a profile from profile list," + echo " check containers list below." + echo " If not provided, it will load the container specified in default configuration." + # shellcheck disable=SC2154 + echo -e " Default configuration: ${__HELP_OPTION_COLOR}${containerArg}${__HELP_NORMAL}" + # shellcheck disable=SC2154 + echo -e " Default container: ${__HELP_OPTION_COLOR}${finalContainerArg}${__HELP_NORMAL}" +} + +userArgHelpFunction() { + Conf::load "cliProfiles" "default" + echo " user to connect on this container" $'\n' + # shellcheck disable=SC2154 + echo -e " Default user: ${__HELP_OPTION_COLOR}${finalUserArg}${__HELP_NORMAL}" + echo " loaded from profile selected as first arg" + echo " or deduced from default configuration." $'\n' + echo -e " Default configuration: ${__HELP_OPTION_COLOR}${containerArg}${__HELP_NORMAL}" $'\n' + echo " if first arg is not a profile" +} + +commandArgHelpFunction() { + Conf::load "cliProfiles" "default" + echo "The command to execute" $'\n' + # shellcheck disable=SC2154 + echo -e "Default command: ${__HELP_OPTION_COLOR}${finalCommandArg[*]}${__HELP_NORMAL}" + echo " loaded from profile selected as first arg" + echo " or deduced from default configuration." + echo -e "Default configuration: ${__HELP_OPTION_COLOR}${containerArg}${__HELP_NORMAL}" $'\n' + echo "if first arg is not a profile" +} + +# shellcheck disable=SC2317 # if function is overridden +unknownOption() { + commandArg+=("$1") +} diff --git a/src/_binaries/Docker/cli.bats b/src/_binaries/Docker/cli/cli.bats similarity index 98% rename from src/_binaries/Docker/cli.bats rename to src/_binaries/Docker/cli/cli.bats index 099d4483..f76dd647 100755 --- a/src/_binaries/Docker/cli.bats +++ b/src/_binaries/Docker/cli/cli.bats @@ -1,7 +1,7 @@ #!/usr/bin/env bash # shellcheck source=src/batsHeaders.sh -source "$(cd "${BATS_TEST_DIRNAME}/../.." && pwd)/batsHeaders.sh" +source "$(cd "${BATS_TEST_DIRNAME}/../../.." && pwd)/batsHeaders.sh" setup() { export TMPDIR="${BATS_TEST_TMPDIR}" diff --git a/src/_binaries/Docker/cli/testsData/cli.help.txt b/src/_binaries/Docker/cli/testsData/cli.help.txt new file mode 100644 index 00000000..2ed222c4 --- /dev/null +++ b/src/_binaries/Docker/cli/testsData/cli.help.txt @@ -0,0 +1,109 @@ +SYNOPSIS: + Easy connection to docker container. + +USAGE: cli [OPTIONS] [ARGUMENTS] +USAGE: cli [--help|-h] [--config] + [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] + [--env-file ] [--log-level ] [--log-file ] + [--display-level ] [--no-color] [--theme ] [--version] + [--quiet|-q] + +ARGUMENTS: + [container {single}] + Container should be the name of a profile from profile list, + check containers list below. + If not provided, it will load the container specified in default configuration. + Default configuration:  + Default container: project-apache2 + [user {single}] + user to connect on this container + + Default user: www-data + loaded from profile selected as first arg + or deduced from default configuration. + + Default configuration:  + + if first arg is not a profile + [command {single}] +The command to execute + +Default command: /bin/bash + loaded from profile selected as first arg + or deduced from default configuration. +Default configuration:  + +if first arg is not a profile + +GLOBAL OPTIONS: + --help, -h {single} + Displays this command help + --config {single} + Displays configuration + --bash-framework-config  {single} + Use alternate bash framework configuration. + --verbose, -v {single} + Info level verbose mode (alias of --display-level INFO) + -vv {single} + Debug level verbose mode (alias of --display-level DEBUG) + -vvv {single} + Trace level verbose mode (alias of --display-level TRACE) + --env-file  {list} (optional) + Load the specified env file (deprecated, please use --bash-framework-con + fig option instead) + --log-level  {single} + Set log level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --log-file  {single} + Set log file + --display-level  {single} + Set display level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --no-color {single} + Produce monochrome output. alias of --theme noColor. + --theme  {single} + Choose color theme - default-force means colors will be produced even if + command is piped. + Possible values: default, default-force, noColor + Default value: default + --version {single} + Print version information and quit. + --quiet, -q {single} + Quiet mode, doesn't display any output. + + +DESCRIPTION: +AVAILABLE PROFILES (from /bash/conf/cliProfiles) +This list can be overridden in home/.bash-tools/cliProfiles + + - default + - mysql + - mysql.remote + - node + - redis + - web + +AVAILABLE CONTAINERS: + +EXAMPLES: + to connect to mysql container in bash mode with user mysql + cli mysql mysql '/bin/bash' + to connect to web container with user root + cli web root + + +CREATE NEW PROFILE: +You can create new profiles in home/.bash-tools/cliProfiles. +This script will be called with the +arguments userArg, containerArg, commandArg +The script has to compute the following +variables finalUserArg, finalContainerArg, finalCommandArg + +VERSION: 3.0 + +AUTHOR: [François Chastanet](https://github.com/fchastanet) + +SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Docker/cli/binary-cli.yaml + +LICENSE: MIT License +Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Docker/testsData/my-container.sh b/src/_binaries/Docker/cli/testsData/my-container.sh similarity index 100% rename from src/_binaries/Docker/testsData/my-container.sh rename to src/_binaries/Docker/cli/testsData/my-container.sh diff --git a/src/_binaries/Docker/testsData/cli.help.txt b/src/_binaries/Docker/testsData/cli.help.txt deleted file mode 100644 index 2304f675..00000000 --- a/src/_binaries/Docker/testsData/cli.help.txt +++ /dev/null @@ -1,109 +0,0 @@ -DESCRIPTION: easy connection to docker container -USAGE: cli [OPTIONS] [ARGUMENTS] -USAGE: cli [--bash-framework-config ] [--config] - [--verbose|-v] [-vv] [-vvv] [--env-file ] [--no-color] - [--theme ] [--help|-h] [--version] [--quiet|-q] [--log-level ] - [--log-file ] [--display-level ] - -ARGUMENTS: - [container {single}] - container should be the name of a profile from profile list, - check containers list below. If not provided, it will load the container - specified in default configuration. - Default configuration: default - Default container: project-apache2 - [user {single}] - user to connect on this container - - Default user: www-data - loaded from profile selected as first arg - or deduced from default configuration. - - Default configuration: default - - if first arg is not a profile - [commandArg {single}] - The command to execute - - Default command: /bin/bash - loaded from profile selected as first arg - or deduced from default configuration. - Default configuration: default - - if first arg is not a profile - -GLOBAL OPTIONS: - --bash-framework-config  {single} - use alternate bash framework configuration. - --config {single} - Display configuration - --verbose, -v {single} - info level verbose mode (alias of --display-level INFO) - -vv {single} - debug level verbose mode (alias of --display-level DEBUG) - -vvv {single} - trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-config - option instead) - --no-color {single} - Produce monochrome output. alias of --theme noColor. - --theme  {single} - choose color theme - default-force means colors will be produced even if com - mand is piped - Default value: default - Possible values: default|default-force|noColor - --help, -h {single} - Display this command help - --version {single} - Print version information and quit - --quiet, -q {single} - quiet mode, doesn't display any output - --log-level  {single} - Set log level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - --log-file  {single} - Set log file - --display-level  {single} - set display level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - -AVAILABLE PROFILES (from /bash/conf/cliProfiles) -This list can be overridden in home/.bash-tools/cliProfiles - - - default - - mysql - - mysql.remote - - node - - redis - - web - -AVAILABLE CONTAINERS: - - -EXAMPLES: - to connect to mysql container in bash mode with user mysql - cli mysql mysql /bin/bash - to connect to web container with user root - cli web root - - -CREATE NEW PROFILE: -You can create new profiles in home/.bash-tools/cliProfiles. -This script will be called with the -arguments userArg, containerArg, commandArg -The script has to compute the following -variables finalUserArg, finalContainerArg, finalCommandArg - -VERSION: 2.0 - -AUTHOR: -[François Chastanet](https://github.com/fchastanet) - -SOURCE FILE: -https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Docker/cli.sh - -LICENSE: -MIT License - -Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Git/gitIsAncestorOf.options.tpl b/src/_binaries/Git/gitIsAncestorOf.options.tpl index 7f332e96..5de5e65d 100644 --- a/src/_binaries/Git/gitIsAncestorOf.options.tpl +++ b/src/_binaries/Git/gitIsAncestorOf.options.tpl @@ -1,5 +1,5 @@ % -declare versionNumber="1.0" + declare commandFunctionName="gitIsAncestorOfCommand" declare help="check if commit is inside a given branch" declare longDescription=''' diff --git a/src/_binaries/Git/upgradeGithubRelease.options.tpl b/src/_binaries/Git/upgradeGithubRelease.options.tpl index 2025a3dd..a03d3126 100644 --- a/src/_binaries/Git/upgradeGithubRelease.options.tpl +++ b/src/_binaries/Git/upgradeGithubRelease.options.tpl @@ -1,6 +1,7 @@ % declare versionNumber="2.0" declare commandFunctionName="upgradeGithubReleaseCommand" +declare copyrightBeginYear="2023" declare help="retrieve latest binary release from github and install it" # shellcheck disable=SC2016 declare longDescription diff --git a/src/_binaries/Postman/command.postmanCli.tpl b/src/_binaries/Postman/command.postmanCli.tpl index e46935a3..95c17fa3 100644 --- a/src/_binaries/Postman/command.postmanCli.tpl +++ b/src/_binaries/Postman/command.postmanCli.tpl @@ -58,7 +58,7 @@ options+=( Options::generateCommand "${options[@]}" % declare optionPostmanModelConfig="$(pwd -P)/postmanCli.collections.json" -declare copyrightBeginYear="2023" +declare copyrightBeginYear="2024" argCommandHelp() { echo "${__HELP_OPTION_COLOR}pull${__HELP_NORMAL}" $'\r' diff --git a/src/_binaries/build/doc/binary-doc.yaml b/src/_binaries/build/doc/binary-doc.yaml index ee23fe40..decb2c11 100644 --- a/src/_binaries/build/doc/binary-doc.yaml +++ b/src/_binaries/build/doc/binary-doc.yaml @@ -13,7 +13,8 @@ binData: commands: default: functionName: docCommand - version: "1.1" + version: "3.0" + copyrightBeginYear: 2022 commandName: doc beforeParseCallbacks: - beforeParseCallback diff --git a/src/_binaries/build/doc/doc-options.sh b/src/_binaries/build/doc/doc-options.sh index 39895d2b..575d561e 100755 --- a/src/_binaries/build/doc/doc-options.sh +++ b/src/_binaries/build/doc/doc-options.sh @@ -1,10 +1,6 @@ #!/usr/bin/env bash # shellcheck disable=SC2034 -declare copyrightBeginYear="2020" -# shellcheck disable=SC2034 -declare versionNumber="1.1" - declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" optionHelpCallback() { diff --git a/src/_binaries/build/install/binary-install.yaml b/src/_binaries/build/install/binary-install.yaml index 81a6c09f..f60fea99 100644 --- a/src/_binaries/build/install/binary-install.yaml +++ b/src/_binaries/build/install/binary-install.yaml @@ -6,13 +6,14 @@ vars: SRC_FILE_PATH: src/_binaries/build/install/binary-install.yaml compilerConfig: - targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/install" - relativeRootDirBasedOnTargetDir: .. + targetFile: "${BASH_TOOLS_ROOT_DIR}/install" + relativeRootDirBasedOnTargetDir: . binData: commands: default: functionName: installCommand - version: "1.1" + version: "3.0" + copyrightBeginYear: 2022 commandName: install beforeParseCallbacks: - beforeParseCallback diff --git a/src/_binaries/build/install/install-options.sh b/src/_binaries/build/install/install-options.sh index a1439db0..b1cd70fd 100755 --- a/src/_binaries/build/install/install-options.sh +++ b/src/_binaries/build/install/install-options.sh @@ -1,10 +1,6 @@ #!/usr/bin/env bash # shellcheck disable=SC2034 -declare copyrightBeginYear="2020" -# shellcheck disable=SC2034 -declare versionNumber="1.1" - declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" optionHelpCallback() { diff --git a/src/_binaries/build/installRequirements/binary-installRequirements.yaml b/src/_binaries/build/installRequirements/binary-installRequirements.yaml index d975fbb7..d6043d64 100644 --- a/src/_binaries/build/installRequirements/binary-installRequirements.yaml +++ b/src/_binaries/build/installRequirements/binary-installRequirements.yaml @@ -12,7 +12,8 @@ binData: commands: default: functionName: installRequirementsCommand - version: "1.0" + version: "3.0" + copyrightBeginYear: 2022 commandName: installRequirements beforeParseCallbacks: - beforeParseCallback diff --git a/src/_binaries/build/installRequirements/installRequirements-options.sh b/src/_binaries/build/installRequirements/installRequirements-options.sh index 600f3470..c50bccb5 100755 --- a/src/_binaries/build/installRequirements/installRequirements-options.sh +++ b/src/_binaries/build/installRequirements/installRequirements-options.sh @@ -1,10 +1,6 @@ #!/usr/bin/env bash # shellcheck disable=SC2034 -declare copyrightBeginYear="2020" -# shellcheck disable=SC2034 -declare versionNumber="1.0" - declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" optionHelpCallback() { diff --git a/src/_binaries/commandDefinitions/optionsVersion.sh b/src/_binaries/commandDefinitions/optionsVersion.sh index d1af84b9..cb7a8907 100755 --- a/src/_binaries/commandDefinitions/optionsVersion.sh +++ b/src/_binaries/commandDefinitions/optionsVersion.sh @@ -2,7 +2,7 @@ optionVersionCallback() { # shellcheck disable=SC2154 - echo "${SCRIPT_NAME} version ${versionNumber}" + echo "${SCRIPT_NAME} version {{ .RootData.binData.commands.default.version }}" Db::checkRequirements exit 0 } From 9ebab6be0ffa32d1402038c122c4dd579bcf568f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Tue, 3 Sep 2024 21:59:11 +0200 Subject: [PATCH 12/24] renamed binary-*.yaml files to *-binary.yaml --- .pre-commit-config-github.yaml | 4 ++-- .pre-commit-config.yaml | 4 ++-- README.md | 2 +- bin/cli | 4 ++-- bin/dbImport | 4 ++-- bin/dbImportProfile | 4 ++-- bin/dbImportStream | 4 ++-- bin/dbQueryAllDatabases | 4 ++-- bin/dbScriptAllDatabases | 4 ++-- bin/doc | 4 ++-- bin/installRequirements | 4 ++-- bin/mysql2puml | 4 ++-- conf/dbScripts/extractData | 4 ++-- install | 4 ++-- ...binary-mysql2puml.yaml => mysql2puml-binary.yaml} | 2 +- .../mysql2puml/testsData/mysql2puml.help.txt | 2 +- .../{binary-dbImport.yaml => dbImport-binary.yaml} | 2 +- .../Database/dbImport/testsData/dbImport.help.txt | 2 +- ...mportProfile.yaml => dbImportProfile-binary.yaml} | 2 +- .../testsData/dbImportProfile.help.txt | 2 +- ...bImportStream.yaml => dbImportStream-binary.yaml} | 2 +- .../dbImportStream/testsData/dbImportStream.help.txt | 2 +- ...atabases.yaml => dbQueryAllDatabases-binary.yaml} | 2 +- .../testsData/dbQueryAllDatabases.help.txt | 2 +- ...tabases.yaml => dbScriptAllDatabases-binary.yaml} | 2 +- ...nary-extractData.yaml => extractData-binary.yaml} | 2 +- .../testsData/dbScriptAllDatabases.help.txt | 2 +- .../Docker/cli/{binary-cli.yaml => cli-binary.yaml} | 2 +- src/_binaries/Docker/cli/testsData/cli.help.txt | 12 ++++++------ .../build/doc/{binary-doc.yaml => doc-binary.yaml} | 2 +- .../{binary-install.yaml => install-binary.yaml} | 2 +- ...irements.yaml => installRequirements-binary.yaml} | 2 +- 32 files changed, 50 insertions(+), 50 deletions(-) rename src/_binaries/Converters/mysql2puml/{binary-mysql2puml.yaml => mysql2puml-binary.yaml} (94%) rename src/_binaries/Database/dbImport/{binary-dbImport.yaml => dbImport-binary.yaml} (96%) rename src/_binaries/Database/dbImportProfile/{binary-dbImportProfile.yaml => dbImportProfile-binary.yaml} (95%) rename src/_binaries/Database/dbImportStream/{binary-dbImportStream.yaml => dbImportStream-binary.yaml} (95%) rename src/_binaries/Database/dbQueryAllDatabases/{binary-dbQueryAllDatabases.yaml => dbQueryAllDatabases-binary.yaml} (95%) rename src/_binaries/Database/dbScriptAllDatabases/{binary-dbScriptAllDatabases.yaml => dbScriptAllDatabases-binary.yaml} (96%) rename src/_binaries/Database/dbScriptAllDatabases/{binary-extractData.yaml => extractData-binary.yaml} (92%) rename src/_binaries/Docker/cli/{binary-cli.yaml => cli-binary.yaml} (96%) rename src/_binaries/build/doc/{binary-doc.yaml => doc-binary.yaml} (93%) rename src/_binaries/build/install/{binary-install.yaml => install-binary.yaml} (94%) rename src/_binaries/build/installRequirements/{binary-installRequirements.yaml => installRequirements-binary.yaml} (90%) diff --git a/.pre-commit-config-github.yaml b/.pre-commit-config-github.yaml index 51c805e0..28050360 100644 --- a/.pre-commit-config-github.yaml +++ b/.pre-commit-config-github.yaml @@ -171,7 +171,7 @@ repos: args: [ --expected-warnings-count, - "4", + "5", --format, plain, --theme, @@ -183,7 +183,7 @@ repos: args: [ --expected-warnings-count, - "6", + "5", --format, checkstyle, --theme, diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d36161ac..43dec14b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -166,7 +166,7 @@ repos: args: [ --expected-warnings-count, - "4", + "5", --format, plain, --theme, @@ -178,7 +178,7 @@ repos: args: [ --expected-warnings-count, - "6", + "5", --format, checkstyle, --theme, diff --git a/README.md b/README.md index 8a4bcbd9..af892303 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ pre-commit install --hook-type pre-commit --hook-type pre-push ```bash export FRAMEWORK_ROOT_DIR=/home/wsl/fchastanet/bash-tools/vendor/bash-tools-framework export BASH_TOOLS_ROOT_DIR=/home/wsl/fchastanet/bash-tools -go run ./cmd/bash-compiler $(find "${BASH_TOOLS_ROOT_DIR}/src/_binaries" -name 'binary-*.yaml' -print) +go run ./cmd/bash-compiler $(find "${BASH_TOOLS_ROOT_DIR}/src/_binaries" -name '*-binary.yaml' -print) ``` ### 3.3. UT diff --git a/bin/cli b/bin/cli index 92e34636..186d8c7d 100755 --- a/bin/cli +++ b/bin/cli @@ -1,6 +1,6 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Docker/cli/binary-cli.yaml +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Docker/cli/cli-binary.yaml # DO NOT EDIT IT # @generated ############################################################################### @@ -2219,7 +2219,7 @@ cliCommandHelp() { # ------------------------------------------ echo echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" - echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Docker/cli/binary-cli.yaml" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Docker/cli/cli-binary.yaml" # ------------------------------------------ # license section # ------------------------------------------ diff --git a/bin/dbImport b/bin/dbImport index 0a8f0e79..d48c36f4 100755 --- a/bin/dbImport +++ b/bin/dbImport @@ -1,6 +1,6 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImport/binary-dbImport.yaml +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImport/dbImport-binary.yaml # DO NOT EDIT IT # @generated ############################################################################### @@ -2715,7 +2715,7 @@ dbImportCommandHelp() { # ------------------------------------------ echo echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" - echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImport/binary-dbImport.yaml" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImport/dbImport-binary.yaml" # ------------------------------------------ # license section # ------------------------------------------ diff --git a/bin/dbImportProfile b/bin/dbImportProfile index a69c4ca6..1818bd57 100755 --- a/bin/dbImportProfile +++ b/bin/dbImportProfile @@ -1,6 +1,6 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImportProfile/dbImportProfile-binary.yaml # DO NOT EDIT IT # @generated ############################################################################### @@ -2362,7 +2362,7 @@ dbImportProfileCommandHelp() { # ------------------------------------------ echo echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" - echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImportProfile/dbImportProfile-binary.yaml" # ------------------------------------------ # license section # ------------------------------------------ diff --git a/bin/dbImportStream b/bin/dbImportStream index c974d0ca..7acc6ab9 100755 --- a/bin/dbImportStream +++ b/bin/dbImportStream @@ -1,6 +1,6 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImportStream/binary-dbImportStream.yaml +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImportStream/dbImportStream-binary.yaml # DO NOT EDIT IT # @generated ############################################################################### @@ -2475,7 +2475,7 @@ dbImportStreamCommandHelp() { # ------------------------------------------ echo echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" - echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImportStream/binary-dbImportStream.yaml" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImportStream/dbImportStream-binary.yaml" # ------------------------------------------ # license section # ------------------------------------------ diff --git a/bin/dbQueryAllDatabases b/bin/dbQueryAllDatabases index dfcfb57d..fd019ddf 100755 --- a/bin/dbQueryAllDatabases +++ b/bin/dbQueryAllDatabases @@ -1,6 +1,6 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-binary.yaml # DO NOT EDIT IT # @generated ############################################################################### @@ -2487,7 +2487,7 @@ dbQueryAllDatabasesCommandHelp() { # ------------------------------------------ echo echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" - echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-binary.yaml" # ------------------------------------------ # license section # ------------------------------------------ diff --git a/bin/dbScriptAllDatabases b/bin/dbScriptAllDatabases index b1a7f381..4b34f5a7 100755 --- a/bin/dbScriptAllDatabases +++ b/bin/dbScriptAllDatabases @@ -1,6 +1,6 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-binary.yaml # DO NOT EDIT IT # @generated ############################################################################### @@ -2519,7 +2519,7 @@ dbScriptAllDatabasesCommandHelp() { # ------------------------------------------ echo echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" - echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-binary.yaml" # ------------------------------------------ # license section # ------------------------------------------ diff --git a/bin/doc b/bin/doc index b251c67d..7f13186c 100755 --- a/bin/doc +++ b/bin/doc @@ -1,6 +1,6 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/build/doc/binary-doc.yaml +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/build/doc/doc-binary.yaml # DO NOT EDIT IT # @generated ############################################################################### @@ -1908,7 +1908,7 @@ docCommandHelp() { # ------------------------------------------ echo echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" - echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/build/doc/binary-doc.yaml" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/build/doc/doc-binary.yaml" # ------------------------------------------ # license section # ------------------------------------------ diff --git a/bin/installRequirements b/bin/installRequirements index 70a15954..2f74fdae 100755 --- a/bin/installRequirements +++ b/bin/installRequirements @@ -1,6 +1,6 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/build/installRequirements/binary-installRequirements.yaml +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/build/installRequirements/installRequirements-binary.yaml # DO NOT EDIT IT # @generated ############################################################################### @@ -1627,7 +1627,7 @@ installRequirementsCommandHelp() { # ------------------------------------------ echo echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" - echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/build/installRequirements/binary-installRequirements.yaml" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/build/installRequirements/installRequirements-binary.yaml" # ------------------------------------------ # license section # ------------------------------------------ diff --git a/bin/mysql2puml b/bin/mysql2puml index e4e734c9..788b9681 100755 --- a/bin/mysql2puml +++ b/bin/mysql2puml @@ -1,6 +1,6 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Converters/mysql2puml/mysql2puml-binary.yaml # DO NOT EDIT IT # @generated ############################################################################### @@ -1874,7 +1874,7 @@ mysql2pumlCommandHelp() { # ------------------------------------------ echo echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" - echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Converters/mysql2puml/mysql2puml-binary.yaml" # ------------------------------------------ # license section # ------------------------------------------ diff --git a/conf/dbScripts/extractData b/conf/dbScripts/extractData index ad8e0fa4..439c37c4 100755 --- a/conf/dbScripts/extractData +++ b/conf/dbScripts/extractData @@ -1,6 +1,6 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/extractData/binary-extractData.yaml +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/extractData/extractData-binary.yaml # DO NOT EDIT IT # @generated ############################################################################### @@ -2105,7 +2105,7 @@ extractDataCommandHelp() { # ------------------------------------------ echo echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" - echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/extractData/binary-extractData.yaml" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/extractData/extractData-binary.yaml" # ------------------------------------------ # license section # ------------------------------------------ diff --git a/install b/install index 9a910474..94cd53e8 100755 --- a/install +++ b/install @@ -1,6 +1,6 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/build/install/binary-install.yaml +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/build/install/install-binary.yaml # DO NOT EDIT IT # @generated ############################################################################### @@ -1632,7 +1632,7 @@ installCommandHelp() { # ------------------------------------------ echo echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" - echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/build/install/binary-install.yaml" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/build/install/install-binary.yaml" # ------------------------------------------ # license section # ------------------------------------------ diff --git a/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml b/src/_binaries/Converters/mysql2puml/mysql2puml-binary.yaml similarity index 94% rename from src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml rename to src/_binaries/Converters/mysql2puml/mysql2puml-binary.yaml index 98f4a082..3236b44e 100644 --- a/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml +++ b/src/_binaries/Converters/mysql2puml/mysql2puml-binary.yaml @@ -3,7 +3,7 @@ extends: - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" vars: - SRC_FILE_PATH: src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml + SRC_FILE_PATH: src/_binaries/Converters/mysql2puml/mysql2puml-binary.yaml compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/mysql2puml" diff --git a/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt b/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt index e98aa927..8561fa04 100644 --- a/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt +++ b/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt @@ -74,7 +74,7 @@ AUTHOR: [François Chastanet](https://github.com/fchastanet) -SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Converters/mysql2puml/binary-mysql2puml.yaml +SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Converters/mysql2puml/mysql2puml-binary.yaml LICENSE: MIT License Copyright (c) 2021-now François Chastanet diff --git a/src/_binaries/Database/dbImport/binary-dbImport.yaml b/src/_binaries/Database/dbImport/dbImport-binary.yaml similarity index 96% rename from src/_binaries/Database/dbImport/binary-dbImport.yaml rename to src/_binaries/Database/dbImport/dbImport-binary.yaml index 5f774fcb..b2c198bb 100644 --- a/src/_binaries/Database/dbImport/binary-dbImport.yaml +++ b/src/_binaries/Database/dbImport/dbImport-binary.yaml @@ -8,7 +8,7 @@ extends: - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" vars: - SRC_FILE_PATH: src/_binaries/Database/dbImport/binary-dbImport.yaml + SRC_FILE_PATH: src/_binaries/Database/dbImport/dbImport-binary.yaml compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/dbImport" diff --git a/src/_binaries/Database/dbImport/testsData/dbImport.help.txt b/src/_binaries/Database/dbImport/testsData/dbImport.help.txt index 413d5460..7c2bf8ff 100644 --- a/src/_binaries/Database/dbImport/testsData/dbImport.help.txt +++ b/src/_binaries/Database/dbImport/testsData/dbImport.help.txt @@ -89,7 +89,7 @@ AUTHOR: [François Chastanet](https://github.com/fchastanet) -SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImport/binary-dbImport.yaml +SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImport/dbImport-binary.yaml LICENSE: MIT License Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml b/src/_binaries/Database/dbImportProfile/dbImportProfile-binary.yaml similarity index 95% rename from src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml rename to src/_binaries/Database/dbImportProfile/dbImportProfile-binary.yaml index fcddfbea..7249502c 100644 --- a/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml +++ b/src/_binaries/Database/dbImportProfile/dbImportProfile-binary.yaml @@ -5,7 +5,7 @@ extends: - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" vars: - SRC_FILE_PATH: src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml + SRC_FILE_PATH: src/_binaries/Database/dbImportProfile/dbImportProfile-binary.yaml compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/dbImportProfile" diff --git a/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt b/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt index f5e45f68..93a3a111 100644 --- a/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt +++ b/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt @@ -90,7 +90,7 @@ Allows to override profiles defined in "Default profiles directory" AUTHOR: [François Chastanet](https://github.com/fchastanet) -SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImportProfile/binary-dbImportProfile.yaml +SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImportProfile/dbImportProfile-binary.yaml LICENSE: MIT License Copyright (c) 2021-now François Chastanet diff --git a/src/_binaries/Database/dbImportStream/binary-dbImportStream.yaml b/src/_binaries/Database/dbImportStream/dbImportStream-binary.yaml similarity index 95% rename from src/_binaries/Database/dbImportStream/binary-dbImportStream.yaml rename to src/_binaries/Database/dbImportStream/dbImportStream-binary.yaml index d9843b92..a1012ed5 100644 --- a/src/_binaries/Database/dbImportStream/binary-dbImportStream.yaml +++ b/src/_binaries/Database/dbImportStream/dbImportStream-binary.yaml @@ -6,7 +6,7 @@ extends: - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" vars: - SRC_FILE_PATH: src/_binaries/Database/dbImportStream/binary-dbImportStream.yaml + SRC_FILE_PATH: src/_binaries/Database/dbImportStream/dbImportStream-binary.yaml compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/dbImportStream" diff --git a/src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt b/src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt index 547538fd..c954c747 100644 --- a/src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt +++ b/src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt @@ -89,7 +89,7 @@ Allows to override profiles defined in 'Default profiles directory' AUTHOR: [François Chastanet](https://github.com/fchastanet) -SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImportStream/binary-dbImportStream.yaml +SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbImportStream/dbImportStream-binary.yaml LICENSE: MIT License Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml b/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-binary.yaml similarity index 95% rename from src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml rename to src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-binary.yaml index fb70b4b6..cd5110ec 100644 --- a/src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml +++ b/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-binary.yaml @@ -6,7 +6,7 @@ extends: - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" vars: - SRC_FILE_PATH: src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml + SRC_FILE_PATH: src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-binary.yaml compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/dbQueryAllDatabases" diff --git a/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt b/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt index a32b2348..c3451ef0 100644 --- a/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt +++ b/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt @@ -98,7 +98,7 @@ AUTHOR: [François Chastanet](https://github.com/fchastanet) -SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbQueryAllDatabases/binary-dbQueryAllDatabases.yaml +SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-binary.yaml LICENSE: MIT License Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-binary.yaml similarity index 96% rename from src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml rename to src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-binary.yaml index 17dcc2a9..cf0e8bb7 100644 --- a/src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml +++ b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-binary.yaml @@ -6,7 +6,7 @@ extends: - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" vars: - SRC_FILE_PATH: src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml + SRC_FILE_PATH: src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-binary.yaml compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/dbScriptAllDatabases" diff --git a/src/_binaries/Database/dbScriptAllDatabases/binary-extractData.yaml b/src/_binaries/Database/dbScriptAllDatabases/extractData-binary.yaml similarity index 92% rename from src/_binaries/Database/dbScriptAllDatabases/binary-extractData.yaml rename to src/_binaries/Database/dbScriptAllDatabases/extractData-binary.yaml index 59432ad8..d9a4bea0 100644 --- a/src/_binaries/Database/dbScriptAllDatabases/binary-extractData.yaml +++ b/src/_binaries/Database/dbScriptAllDatabases/extractData-binary.yaml @@ -4,7 +4,7 @@ extends: - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" vars: - SRC_FILE_PATH: src/_binaries/Database/extractData/binary-extractData.yaml + SRC_FILE_PATH: src/_binaries/Database/extractData/extractData-binary.yaml compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/conf/dbScripts/extractData" diff --git a/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt b/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt index 7bde3898..682d92fc 100644 --- a/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt +++ b/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt @@ -117,7 +117,7 @@ Allows to override queries defined in 'Default scripts directory' AUTHOR: [François Chastanet](https://github.com/fchastanet) -SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbScriptAllDatabases/binary-dbScriptAllDatabases.yaml +SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-binary.yaml LICENSE: MIT License Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Docker/cli/binary-cli.yaml b/src/_binaries/Docker/cli/cli-binary.yaml similarity index 96% rename from src/_binaries/Docker/cli/binary-cli.yaml rename to src/_binaries/Docker/cli/cli-binary.yaml index 743925b5..979ea398 100644 --- a/src/_binaries/Docker/cli/binary-cli.yaml +++ b/src/_binaries/Docker/cli/cli-binary.yaml @@ -4,7 +4,7 @@ extends: - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" vars: - SRC_FILE_PATH: src/_binaries/Docker/cli/binary-cli.yaml + SRC_FILE_PATH: src/_binaries/Docker/cli/cli-binary.yaml compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/cli" diff --git a/src/_binaries/Docker/cli/testsData/cli.help.txt b/src/_binaries/Docker/cli/testsData/cli.help.txt index 2ed222c4..a0b1dcd2 100644 --- a/src/_binaries/Docker/cli/testsData/cli.help.txt +++ b/src/_binaries/Docker/cli/testsData/cli.help.txt @@ -16,22 +16,22 @@ Default configuration:  Default container: project-apache2 [user {single}] - user to connect on this container + user to connect on this container Default user: www-data loaded from profile selected as first arg - or deduced from default configuration. + or deduced from default configuration. - Default configuration:  + Default configuration:  if first arg is not a profile [command {single}] -The command to execute +The command to execute Default command: /bin/bash loaded from profile selected as first arg or deduced from default configuration. -Default configuration:  +Default configuration:  if first arg is not a profile @@ -103,7 +103,7 @@ variables finalUserArg, finalContainerArg, finalCom AUTHOR: [François Chastanet](https://github.com/fchastanet) -SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Docker/cli/binary-cli.yaml +SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Docker/cli/cli-binary.yaml LICENSE: MIT License Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/build/doc/binary-doc.yaml b/src/_binaries/build/doc/doc-binary.yaml similarity index 93% rename from src/_binaries/build/doc/binary-doc.yaml rename to src/_binaries/build/doc/doc-binary.yaml index decb2c11..f6fb90df 100644 --- a/src/_binaries/build/doc/binary-doc.yaml +++ b/src/_binaries/build/doc/doc-binary.yaml @@ -4,7 +4,7 @@ extends: - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" vars: - SRC_FILE_PATH: src/_binaries/build/doc/binary-doc.yaml + SRC_FILE_PATH: src/_binaries/build/doc/doc-binary.yaml compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/doc" diff --git a/src/_binaries/build/install/binary-install.yaml b/src/_binaries/build/install/install-binary.yaml similarity index 94% rename from src/_binaries/build/install/binary-install.yaml rename to src/_binaries/build/install/install-binary.yaml index f60fea99..f77d051c 100644 --- a/src/_binaries/build/install/binary-install.yaml +++ b/src/_binaries/build/install/install-binary.yaml @@ -3,7 +3,7 @@ extends: - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" vars: - SRC_FILE_PATH: src/_binaries/build/install/binary-install.yaml + SRC_FILE_PATH: src/_binaries/build/install/install-binary.yaml compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/install" diff --git a/src/_binaries/build/installRequirements/binary-installRequirements.yaml b/src/_binaries/build/installRequirements/installRequirements-binary.yaml similarity index 90% rename from src/_binaries/build/installRequirements/binary-installRequirements.yaml rename to src/_binaries/build/installRequirements/installRequirements-binary.yaml index d6043d64..33e71db9 100644 --- a/src/_binaries/build/installRequirements/binary-installRequirements.yaml +++ b/src/_binaries/build/installRequirements/installRequirements-binary.yaml @@ -3,7 +3,7 @@ extends: - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" vars: - SRC_FILE_PATH: src/_binaries/build/installRequirements/binary-installRequirements.yaml + SRC_FILE_PATH: src/_binaries/build/installRequirements/installRequirements-binary.yaml compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/installRequirements" From c2fb0381be24118820eee75919a15ab45c16d2c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Tue, 3 Sep 2024 23:36:07 +0200 Subject: [PATCH 13/24] compiled gitIsAncestorOf using go compiler --- bin/cli | 46 +- bin/gitIsAncestorOf | 2527 +++++++++-------- src/_binaries/Docker/cli/cli-options.sh | 46 +- .../Docker/cli/testsData/cli.help.txt | 27 +- src/_binaries/Git/gitIsAncestorOf.options.tpl | 42 - src/_binaries/Git/gitIsAncestorOf.sh | 32 - .../gitIsAncestorOf-binary.yaml | 40 + .../gitIsAncestorOf/gitIsAncestorOf-main.sh | 42 + .../gitIsAncestorOf-options.sh | 13 + .../gitIsAncestorOf.bats | 2 +- .../testsData/gitIsAncestorOf.help.txt | 67 + .../Git/testsData/gitIsAncestorOf.help.txt | 65 - 12 files changed, 1641 insertions(+), 1308 deletions(-) delete mode 100644 src/_binaries/Git/gitIsAncestorOf.options.tpl delete mode 100755 src/_binaries/Git/gitIsAncestorOf.sh create mode 100644 src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-binary.yaml create mode 100755 src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-main.sh create mode 100755 src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-options.sh rename src/_binaries/Git/{ => gitIsAncestorOf}/gitIsAncestorOf.bats (88%) create mode 100644 src/_binaries/Git/gitIsAncestorOf/testsData/gitIsAncestorOf.help.txt delete mode 100644 src/_binaries/Git/testsData/gitIsAncestorOf.help.txt diff --git a/bin/cli b/bin/cli index 186d8c7d..e0ec39f7 100755 --- a/bin/cli +++ b/bin/cli @@ -1502,6 +1502,7 @@ declare containerArg="default" declare finalUserArg="${defaultUserArg}" declare finalCommandArg=("${defaultCommandArg[@]}") +declare defaultConfiguration="default" declare defaultUserArg="root" declare -a defaultCommandArg=("//bin/sh") declare PROFILES_DIR @@ -1532,7 +1533,7 @@ initConf() { PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/cliProfiles" HOME_PROFILES_DIR="${HOME}/.bash-tools/cliProfiles" # load default conf file - Conf::load "cliProfiles" "default" + Conf::load "cliProfiles" "${defaultConfiguration}" } optionHelpCallback() { @@ -1565,36 +1566,37 @@ longDescriptionFunction() { } containerArgHelpFunction() { - Conf::load "cliProfiles" "default" - echo " Container should be the name of a profile from profile list," - echo " check containers list below." - echo " If not provided, it will load the container specified in default configuration." - # shellcheck disable=SC2154 - echo -e " Default configuration: ${__HELP_OPTION_COLOR}${containerArg}${__HELP_NORMAL}" + Conf::load "cliProfiles" "${defaultConfiguration}" + echo -e " Container should be the name of a profile from profile list," + echo -e " check containers list below." + echo + echo -e " If no value provided, it will load the container" + echo -e " specified in ${__HELP_OPTION_COLOR}${defaultConfiguration}${__HELP_NORMAL} configuration." # shellcheck disable=SC2154 - echo -e " Default container: ${__HELP_OPTION_COLOR}${finalContainerArg}${__HELP_NORMAL}" + echo -e " Default: ${__HELP_OPTION_COLOR}${finalContainerArg}${__HELP_NORMAL}" + echo } userArgHelpFunction() { - Conf::load "cliProfiles" "default" - echo " user to connect on this container" $'\n' + Conf::load "cliProfiles" "${defaultConfiguration}" + echo -e " user to connect on this container" + echo + echo -e " If no value provided, it will load the user" + echo -e " specified in ${__HELP_OPTION_COLOR}${defaultConfiguration}${__HELP_NORMAL} configuration." # shellcheck disable=SC2154 - echo -e " Default user: ${__HELP_OPTION_COLOR}${finalUserArg}${__HELP_NORMAL}" - echo " loaded from profile selected as first arg" - echo " or deduced from default configuration." $'\n' - echo -e " Default configuration: ${__HELP_OPTION_COLOR}${containerArg}${__HELP_NORMAL}" $'\n' - echo " if first arg is not a profile" + echo -e " Default: ${__HELP_OPTION_COLOR}${finalUserArg}${__HELP_NORMAL}" + echo } commandArgHelpFunction() { - Conf::load "cliProfiles" "default" - echo "The command to execute" $'\n' + Conf::load "cliProfiles" "${defaultConfiguration}" + echo -e " The command to execute" + echo + echo -e " If no value provided, it will load the command" + echo -e " specified in ${__HELP_OPTION_COLOR}${defaultConfiguration}${__HELP_NORMAL} configuration." # shellcheck disable=SC2154 - echo -e "Default command: ${__HELP_OPTION_COLOR}${finalCommandArg[*]}${__HELP_NORMAL}" - echo " loaded from profile selected as first arg" - echo " or deduced from default configuration." - echo -e "Default configuration: ${__HELP_OPTION_COLOR}${containerArg}${__HELP_NORMAL}" $'\n' - echo "if first arg is not a profile" + echo -e " Default: ${__HELP_OPTION_COLOR}${finalCommandArg[*]}${__HELP_NORMAL}" + echo } # shellcheck disable=SC2317 # if function is overridden diff --git a/bin/gitIsAncestorOf b/bin/gitIsAncestorOf index 7d8725b8..6aa15e73 100755 --- a/bin/gitIsAncestorOf +++ b/bin/gitIsAncestorOf @@ -1,13 +1,12 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FACADE FROM https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Git/gitIsAncestorOf.sh +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-binary.yaml # DO NOT EDIT IT # @generated ############################################################################### # shellcheck disable=SC2288,SC2034 -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/gitIsAncestorOf -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE + + # ensure that no user aliases could interfere with # commands used in this script @@ -62,13 +61,6 @@ interruptManagement() { kill -s INT "$$" } trap interruptManagement INT -SCRIPT_NAME=${0##*/} -REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" -if [[ -n "${EMBED_CURRENT_DIR}" ]]; then - CURRENT_DIR="${EMBED_CURRENT_DIR}" -else - CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" -fi ################################################ # Temp dir management @@ -102,6 +94,20 @@ cleanOnExit() { } trap cleanOnExit EXIT HUP QUIT ABRT TERM + +SCRIPT_NAME=${0##*/} +REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" +if [[ -n "${EMBED_CURRENT_DIR}" ]]; then + CURRENT_DIR="${EMBED_CURRENT_DIR}" +else + CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" +fi +FRAMEWORK_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" +FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" +FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + # @description Log namespace provides 2 kind of functions # - Log::display* allows to display given message with # given display level @@ -132,181 +138,239 @@ export __VERBOSE_LEVEL_DEBUG=2 # @description verbose level info export __VERBOSE_LEVEL_TRACE=3 -# @description Display message using info color (bg light blue/fg white) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayInfo() { - local type="${2:-INFO}" - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description concatenate each element of an array with a separator +# but wrapping text when line length is more than provided argument +# The algorithm will try not to cut the array element if it can. +# - if an arg can be placed on current line it will be, +# otherwise current line is printed and arg is added to the new +# current line +# - Empty arg is interpreted as a new line. +# - Add \r to arg in order to force break line and avoid following +# arg to be concatenated with current arg. +# +# @arg $1 glue:String +# @arg $2 maxLineLength:int +# @arg $3 indentNextLine:int +# @arg $@ array:String[] +Array::wrap2() { + local glue="${1-}" + local -i glueLength="${#glue}" + shift || true + local -i maxLineLength=$1 + shift || true + local -i indentNextLine=$1 + shift || true + local indentStr="" + if ((indentNextLine > 0)); then + indentStr="$(head -c "${indentNextLine}" 0)); do + arg="$1" + shift || true + + # replace tab by 2 spaces + arg="${arg//$'\t'/ }" + # remove trailing spaces + arg="${arg%[[:blank:]]}" + if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then + printCurrentLine + ((previousLineEmpty = 1)) + continue + else + if ((previousLineEmpty == 1)); then + printCurrentLine + fi + ((previousLineEmpty = 0)) || true + fi + # convert eol to args + mapfile -t additionalLines <<<"${arg}" + if ((${#additionalLines[@]} > 1)); then + set -- "${additionalLines[@]}" "$@" + continue + fi + + ((argLength = ${#arg})) || true + + # empty arg + if ((argLength == 0)); then + if ((isNewline == 0)); then + # isNewline = 0 means currentLine is not empty + printCurrentLine + fi + continue + fi + + if ((isNewline == 0)); then + glueLength="${#glue}" + else + glueLength="0" + fi + if ((currentLineLength + argLength + glueLength > maxLineLength)); then + if ((argLength + glueLength > maxLineLength)); then + # arg is too long to even fit on one line + # we have to split the arg on current and next line + local -i remainingLineLength + ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) + appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" + printCurrentLine + arg="${arg:${remainingLineLength}}" + # remove leading spaces + arg="${arg##[[:blank:]]}" + + set -- "${arg}" "$@" + else + # the arg can fit on next line + printCurrentLine + appendToCurrentLine "${arg}" "${argLength}" + fi + else + appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" + fi + done + if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then + printCurrentLine + fi + ) | sed -E -e 's/[[:blank:]]+$//' } -# @description Display message using debug color (gray) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayDebug() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then - Log::computeDuration - echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logDebug "$1" + +# @description check if command specified exists or return 1 +# with error and message if not +# +# @arg $1 commandName:String on which existence must be checked +# @arg $2 helpIfNotExists:String a help command to display if the command does not exist +# +# @exitcode 1 if the command specified does not exist +# @stderr diagnostic information + help if second argument is provided +Assert::commandExists() { + local commandName="$1" + local helpIfNotExists="$2" + + "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { + Log::displayError "${commandName} is not installed, please install it" + if [[ -n "${helpIfNotExists}" ]]; then + Log::displayInfo "${helpIfNotExists}" + fi + return 1 + } + return 0 } -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description check if tty (interactive mode) is active +# @noargs +# @exitcode 1 if tty not active +# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive +# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive +Assert::tty() { + if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then + return 1 fi - Log::logWarning "$1" + if [[ "${INTERACTIVE:-0}" = "1" ]]; then + return 0 + fi + tty -s } -# @description Display message using error color (red) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayError() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then - Log::computeDuration - echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description ignore exit code 141 from simple command pipes +# @example use with: +# local resultingStatus=0 +# local -a originalPipeStatus=() +# cmd1 | cmd2 || Bash::handlePipelineFailure resultingStatus originalPipeStatus || true +# [[ "${resultingStatus}" = "0" ]] +# @arg $1 resultingStatusCode:&int (passed by reference) (optional) resulting status code +# @arg $2 originalStatus:int[] (passed by reference) (optional) copy of original PIPESTATUS array +# @env PIPESTATUS assuming that this function is called like in the example provided +# @see https://unix.stackexchange.com/a/709880/582856 +Bash::handlePipelineFailure() { + local -a pipeStatusBackup=("${PIPESTATUS[@]}") + local -n handlePipelineFailure_resultingStatusCode=$1 + local -n handlePipelineFailure_originalStatus=$2 + # shellcheck disable=SC2034 + handlePipelineFailure_originalStatus=("${pipeStatusBackup[@]}") + handlePipelineFailure_resultingStatusCode=0 + local statusCode + for statusCode in "${pipeStatusBackup[@]}"; do + if ((statusCode == 141)); then + return 0 + elif ((statusCode > 0)); then + # shellcheck disable=SC2034 + handlePipelineFailure_resultingStatusCode="${statusCode}" + break + fi + done + return "${handlePipelineFailure_resultingStatusCode}" +} + + +# @description check if all requirements are satisfied +# to execute dbImport commands +Db::checkRequirements() { + if [[ "${SKIP_REQUIREMENTS_CHECKS:-0}" = "1" ]]; then + return 0 fi - Log::logError "$1" + local -i failures=0 + echo + Assert::commandExists mysql "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqldump "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists pv "sudo apt-get install -y pv" || ((++failures)) + Assert::commandExists gawk "sudo apt-get install -y gawk" || ((++failures)) + Assert::commandExists awk "sudo apt-get install -y gawk" || ((++failures)) + Version::checkMinimal "gawk" "--version" "5.0.1" || ((++failures)) + return "${failures}" } -# @description load colors theme constants -# @warning if tty not opened, noColor theme will be chosen -# @arg $1 theme:String the theme to use (default, noColor) -# @arg $@ args:String[] -# @set __ERROR_COLOR String indicate error status -# @set __INFO_COLOR String indicate info status -# @set __SUCCESS_COLOR String indicate success status -# @set __WARNING_COLOR String indicate warning status -# @set __SKIPPED_COLOR String indicate skipped status -# @set __DEBUG_COLOR String indicate debug status -# @set __HELP_COLOR String indicate help status -# @set __TEST_COLOR String not used -# @set __TEST_ERROR_COLOR String not used -# @set __HELP_TITLE_COLOR String used to display help title in help strings -# @set __HELP_OPTION_COLOR String used to display highlight options in help strings -# -# @set __RESET_COLOR String reset default color -# -# @set __HELP_EXAMPLE String to remove -# @set __HELP_TITLE String to remove -# @set __HELP_NORMAL String to remove -# shellcheck disable=SC2034 -UI::theme() { - local theme="${1-default}" - if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then - theme="noColor" - fi - case "${theme}" in - default | default-force) - theme="default" - ;; - noColor) ;; - *) - Log::fatal "invalid theme provided" - ;; - esac - if [[ "${theme}" = "default" ]]; then - BASH_FRAMEWORK_THEME="default" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='\e[31m' # Red - __INFO_COLOR='\e[44m' # white on lightBlue - __SUCCESS_COLOR='\e[32m' # Green - __WARNING_COLOR='\e[33m' # Yellow - __SKIPPED_COLOR='\e[33m' # Yellow - __DEBUG_COLOR='\e[37m' # Gray - __HELP_COLOR='\e[7;49;33m' # Black on Gold - __TEST_COLOR='\e[100m' # Light magenta - __TEST_ERROR_COLOR='\e[41m' # white on red - __HELP_TITLE_COLOR="\e[1;37m" # Bold - __HELP_OPTION_COLOR="\e[1;34m" # Blue - # Internal: reset color - __RESET_COLOR='\e[0m' # Reset Color - # shellcheck disable=SC2155,SC2034 - __HELP_EXAMPLE="$(echo -e "\e[2;97m")" - # shellcheck disable=SC2155,SC2034 - __HELP_TITLE="$(echo -e "\e[1;37m")" - # shellcheck disable=SC2155,SC2034 - __HELP_NORMAL="$(echo -e "\033[0m")" - else - BASH_FRAMEWORK_THEME="noColor" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='' - __INFO_COLOR='' - __SUCCESS_COLOR='' - __WARNING_COLOR='' - __SKIPPED_COLOR='' - __DEBUG_COLOR='' - __HELP_COLOR='' - __TEST_COLOR='' - __TEST_ERROR_COLOR='' - __HELP_TITLE_COLOR='' - __HELP_OPTION_COLOR='' - # Internal: reset color - __RESET_COLOR='' - __HELP_EXAMPLE='' - __HELP_TITLE='' - __HELP_NORMAL='' - fi -} - -# @description draw a line with the character passed in parameter repeated depending on terminal width -# @arg $1 character:String character to use as separator (default value #) -UI::drawLine() { - local character="${1:-#}" - local -i width=${COLUMNS:-0} - if ((width == 0)) && [[ -t 1 ]]; then - width=$(tput cols) - fi - if ((width == 0)); then - width=80 - fi - printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") - echo -} - -# @description Display message using error color (red) and exit immediately with error status 1 -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::fatal() { - Log::computeDuration - echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - Log::logFatal "$1" - exit 1 -} - -# @description create a temp file using default TMPDIR variable -# initialized in _includes/_commonHeader.sh -# @env TMPDIR String (default value /tmp) -# @arg $1 templateName:String template name to use(optional) -Framework::createTempFile() { - mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" -} - -# @description ensure env files are loaded -# @arg $@ list of default files to load at the end -# @exitcode 1 if one of env files fails to load -# @stderr diagnostics information is displayed -# shellcheck disable=SC2120 -Env::requireLoad() { - local -a defaultFiles=("$@") - # get list of possible config files - local -a configFiles=() - if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then - # BASH_FRAMEWORK_ENV_FILES is an array - configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") + +# @description ensure env files are loaded +# @arg $@ list of default files to load at the end +# @exitcode 1 if one of env files fails to load +# @stderr diagnostics information is displayed +# shellcheck disable=SC2120 +Env::requireLoad() { + export REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 + + local -a defaultFiles=("$@") + # get list of possible config files + local -a configFiles=() + if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then + # BASH_FRAMEWORK_ENV_FILES is an array + configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") fi local localFrameworkConfigFile localFrameworkConfigFile="$(pwd)/.framework-config" @@ -328,336 +392,144 @@ Env::requireLoad() { done } -# @description activate or not Log::display* and Log::log* functions -# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL -# environment variables loaded by Env::requireLoad -# try to create log file and rotate it if necessary -# @noargs -# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable -# @env BASH_FRAMEWORK_DISPLAY_LEVEL int -# @env BASH_FRAMEWORK_LOG_LEVEL int -# @env BASH_FRAMEWORK_LOG_FILE String -# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 -# @exitcode 0 always successful -# @stderr diagnostics information about log file is displayed -# @require Env::requireLoad -# @require UI::requireTheme -Log::requireLoad() { - if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - export BASH_FRAMEWORK_LOG_LEVEL - fi - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then - if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 - fi - elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - fi +# @description create a temp file using default TMPDIR variable +# @env TMPDIR String (default value /tmp) +# @arg $1 templateName:String template name to use(optional) +Framework::createTempFile() { + mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" +} - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - # will always be created even if not in info level - Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" - if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then - Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + +declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR +FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" +LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" +LOG_LAST_LOG_DATE_INIT=1 +LOG_LAST_DURATION_STR="" + +# @description compute duration since last call to this function +# the result is set in following env variables. +# in ss.sss (seconds followed by milliseconds precision 3 decimals) +# @noargs +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @set LOG_LAST_LOG_DATE_INIT int (default 1) set to 0 at first call, allows to detect reference log +# @set LOG_LAST_DURATION_STR String the last duration displayed +# @set LOG_LAST_LOG_DATE String the last log date that will be used to compute next diff +Log::computeDuration() { + if ((${DISPLAY_DURATION:-0} == 1)); then + local -i duration=0 + local -i delta=0 + local -i currentLogDate + currentLogDate="${EPOCHREALTIME/[^0-9]/}" + if ((LOG_LAST_LOG_DATE_INIT == 1)); then + LOG_LAST_LOG_DATE_INIT=0 + LOG_LAST_DURATION_STR="Ref" + else + duration=$(((currentLogDate - FIRST_LOG_DATE) / 1000000)) + delta=$(((currentLogDate - LOG_LAST_LOG_DATE) / 1000000)) + LOG_LAST_DURATION_STR="${duration}s/+${delta}s" fi + LOG_LAST_LOG_DATE="${currentLogDate}" + # shellcheck disable=SC2034 + local microSeconds="${EPOCHREALTIME#*.}" + LOG_LAST_DURATION_STR="$(printf '%(%T)T.%03.0f\n' "${EPOCHSECONDS}" "${microSeconds:0:3}")(${LOG_LAST_DURATION_STR}) - " + else + # shellcheck disable=SC2034 + LOG_LAST_DURATION_STR="" fi } -# @description concatenate each element of an array with a separator -# but wrapping text when line length is more than provided argument -# The algorithm will try not to cut the array element if it can. -# - if an arg can be placed on current line it will be, -# otherwise current line is printed and arg is added to the new -# current line -# - Empty arg is interpreted as a new line. -# - Add \r to arg in order to force break line and avoid following -# arg to be concatenated with current arg. -# -# @arg $1 glue:String -# @arg $2 maxLineLength:int -# @arg $3 indentNextLine:int -# @arg $@ array:String[] -Array::wrap2() { - local glue="${1-}" - local -i glueLength="${#glue}" - shift || true - local -i maxLineLength=$1 - shift || true - local -i indentNextLine=$1 - shift || true - local indentStr="" - if ((indentNextLine > 0)); then - indentStr="$(head -c "${indentNextLine}" = __LEVEL_DEBUG)); then + Log::computeDuration + echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logDebug "$1" +} - printCurrentLine() { - if ((isNewline == 0)) || ((previousLineEmpty == 1)); then - echo - fi - ((isNewline = 1)) - echo -en "${indentStr}" - ((currentLineLength = indentNextLine)) || true - } - appendToCurrentLine() { - local text="$1" - local -i length=$2 - ((currentLineLength += length)) || true - ((isNewline = 0)) || true - if [[ "${text: -1}" = $'\r' ]]; then - text="${text:0:-1}" - echo -en "${text%%+([[:blank:]])}" - printCurrentLine - else - echo -en "${text%%+([[:blank:]])}" - fi - } - ( - local currentLine - local -i currentLineLength=0 isNewline=1 argLength=0 - local -a additionalLines - local -i previousLineEmpty=0 - local arg="" +# @description Display message using error color (red) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayError() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then + Log::computeDuration + echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logError "$1" +} - while (($# > 0)); do - arg="$1" - shift || true - # replace tab by 2 spaces - arg="${arg//$'\t'/ }" - # remove trailing spaces - arg="${arg%[[:blank:]]}" - if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then - printCurrentLine - ((previousLineEmpty = 1)) - continue - else - if ((previousLineEmpty == 1)); then - printCurrentLine - fi - ((previousLineEmpty = 0)) || true - fi - # convert eol to args - mapfile -t additionalLines <<<"${arg}" - if ((${#additionalLines[@]} > 1)); then - set -- "${additionalLines[@]}" "$@" - continue - fi +# @description Display message using info color (bg light blue/fg white) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayInfo() { + local type="${2:-INFO}" + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logInfo "$1" "${type}" +} - ((argLength = ${#arg})) || true - # empty arg - if ((argLength == 0)); then - if ((isNewline == 0)); then - # isNewline = 0 means currentLine is not empty - printCurrentLine - fi - continue - fi +# @description Display message using warning color (yellow) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayWarning() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then + Log::computeDuration + echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logWarning "$1" +} - if ((isNewline == 0)); then - glueLength="${#glue}" - else - glueLength="0" - fi - if ((currentLineLength + argLength + glueLength > maxLineLength)); then - if ((argLength + glueLength > maxLineLength)); then - # arg is too long to even fit on one line - # we have to split the arg on current and next line - local -i remainingLineLength - ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) - appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" - printCurrentLine - arg="${arg:${remainingLineLength}}" - # remove leading spaces - arg="${arg##[[:blank:]]}" - set -- "${arg}" "$@" - else - # the arg can fit on next line - printCurrentLine - appendToCurrentLine "${arg}" "${argLength}" - fi - else - appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" - fi - done - if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then - printCurrentLine - fi - ) | sed -E -e 's/[[:blank:]]+$//' +# @description Display message using error color (red) and exit immediately with error status 1 +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::fatal() { + Log::computeDuration + echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + Log::logFatal "$1" + exit 1 } -bashToolsDefaultConfigTemplate="${bashToolsDefaultConfigTemplate:-$( - cat <<'EOF' -# shellcheck disable=SC2034 -# Default settings -# you can override these settings by creating ${HOME}/.bash-tools/.env file - -### -### DISPLAY Level -### minimum level of the messages that will be displayed on screen -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} - -### -### DISPLAY duration -### 0: no duration is displayed on the messages -### 1: duration between previous message and current is displayed -### with the message -### -DISPLAY_DURATION=${DISPLAY_DURATION:0} - -### -### Log to file -### -### all log messages will be redirected to log file specified -### this same path will be used inside and outside of the container -### -BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} - -### -### LOG Level -### minimum level of the messages that will be logged into LOG_FILE -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} - -# absolute directory containing db import sql dumps -DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} - -# garbage collect all files for which modification is greater than eg: 30 days (+30) -# each time an existing file is used by dbImport/dbImportTable -# the file modification time is set to now -DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} - -# absolute directory containing dbScripts used by dbScriptAllDatabases -SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} - -# ----------------------------------------------------- -# AWS Parameters -# ----------------------------------------------------- -S3_BASE_URL=${S3_BASE_URL:-} - -# ----------------------------------------------------- -# Postman Parameters -# ----------------------------------------------------- -POSTMAN_API_KEY= -EOF -)}" - -# @description loads ~/.bash-tools/.env if available -# if not creates it from a default template -# else check if new options need to be added -BashTools::Conf::requireLoad() { - local envFile="${HOME}/.bash-tools/.env" - if [[ ! -f "${envFile}" ]]; then - mkdir -p "${HOME}/.bash-tools" - ( - echo "#!/usr/bin/env bash" - echo "${bashToolsDefaultConfigTemplate}" - ) >"${envFile}" - Log::displayInfo "Configuration file '${envFile}' created" - else - if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then - ( - echo '# -----------------------------------------------------' - echo '# Postman Parameters' - echo '# -----------------------------------------------------' - echo 'POSTMAN_API_KEY=' - ) >>"${envFile}" - fi +# @description log message to file +# @arg $1 message:String the message to display +Log::logDebug() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then + Log::logMessage "${2:-DEBUG}" "$1" fi - # shellcheck source=/conf/.env - source "${envFile}" || { - Log::displayError "impossible to load '${envFile}'" - exit 1 - } } -# @description ensure COMMAND_BIN_DIR env var is set -# and PATH correctly prepared -# @noargs -# @set COMMAND_BIN_DIR string the directory where to find this command -# @set PATH string add directory where to find this command binary -Compiler::Facade::requireCommandBinDir() { - COMMAND_BIN_DIR="${CURRENT_DIR}" - Env::pathPrepend "${COMMAND_BIN_DIR}" -} -# @description ensure running user is not root -# @exitcode 1 if current user is root -# @stderr diagnostics information is displayed -Linux::requireExecutedAsUser() { - if [[ "$(id -u)" = "0" ]]; then - Log::fatal "this script should be executed as normal user" +# @description log message to file +# @arg $1 message:String the message to display +Log::logError() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_ERROR)); then + Log::logMessage "${2:-ERROR}" "$1" fi } -declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR -FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" -LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" -LOG_LAST_LOG_DATE_INIT=1 -LOG_LAST_DURATION_STR="" -# @description compute duration since last call to this function -# the result is set in following env variables. -# in ss.sss (seconds followed by milliseconds precision 3 decimals) -# @noargs -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @set LOG_LAST_LOG_DATE_INIT int (default 1) set to 0 at first call, allows to detect reference log -# @set LOG_LAST_DURATION_STR String the last duration displayed -# @set LOG_LAST_LOG_DATE String the last log date that will be used to compute next diff -Log::computeDuration() { - if ((${DISPLAY_DURATION:-0} == 1)); then - local -i duration=0 - local -i delta=0 - local -i currentLogDate - currentLogDate="${EPOCHREALTIME/[^0-9]/}" - if ((LOG_LAST_LOG_DATE_INIT == 1)); then - LOG_LAST_LOG_DATE_INIT=0 - LOG_LAST_DURATION_STR="Ref" - else - duration=$(((currentLogDate - FIRST_LOG_DATE) / 1000000)) - delta=$(((currentLogDate - LOG_LAST_LOG_DATE) / 1000000)) - LOG_LAST_DURATION_STR="${duration}s/+${delta}s" - fi - LOG_LAST_LOG_DATE="${currentLogDate}" - # shellcheck disable=SC2034 - local microSeconds="${EPOCHREALTIME#*.}" - LOG_LAST_DURATION_STR="$(printf '%(%T)T.%03.0f\n' "${EPOCHSECONDS}" "${microSeconds:0:3}")(${LOG_LAST_DURATION_STR}) - " - else - # shellcheck disable=SC2034 - LOG_LAST_DURATION_STR="" - fi +# @description log message to file +# @arg $1 message:String the message to display +Log::logFatal() { + Log::logMessage "${2:-FATAL}" "$1" } + # @description log message to file # @arg $1 message:String the message to display Log::logInfo() { @@ -666,14 +538,44 @@ Log::logInfo() { fi } -# @description log message to file -# @arg $1 message:String the message to display -Log::logDebug() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then - Log::logMessage "${2:-DEBUG}" "$1" + +# @description Internal: common log message +# @example text +# [date]|[levelMsg]|message +# +# @example text +# 2020-01-19 19:20:21|ERROR |log error +# 2020-01-19 19:20:21|SKIPPED|log skipped +# +# @arg $1 levelMsg:String message's level description (eg: STATUS, ERROR, ...) +# @arg $2 msg:String the message to display +# @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty +# @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages +# @stderr diagnostics information is displayed +Log::logMessage() { + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Log::requireLoad has not been loaded" + exit 1 + fi + + local levelMsg="$1" + local msg="$2" + local date + + if [[ -n "${BASH_FRAMEWORK_LOG_FILE}" ]] && ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + date="$(date '+%Y-%m-%d %H:%M:%S')" + touch "${BASH_FRAMEWORK_LOG_FILE}" + printf "%s|%7s|%s\n" "${date}" "${levelMsg}" "${msg}" >>"${BASH_FRAMEWORK_LOG_FILE}" fi } + # @description log message to file # @arg $1 message:String the message to display Log::logWarning() { @@ -682,148 +584,303 @@ Log::logWarning() { fi } -# @description log message to file -# @arg $1 message:String the message to display -Log::logError() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_ERROR)); then - Log::logMessage "${2:-ERROR}" "$1" - fi -} -# @description check if tty (interactive mode) is active -# @noargs -# @exitcode 1 if tty not active -# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive -# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive -Assert::tty() { - if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then - return 1 - fi - if [[ "${INTERACTIVE:-0}" = "1" ]]; then +# @description activate or not Log::display* and Log::log* functions +# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL +# environment variables loaded by Env::requireLoad +# try to create log file and rotate it if necessary +# @noargs +# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable +# @env BASH_FRAMEWORK_DISPLAY_LEVEL int +# @env BASH_FRAMEWORK_LOG_LEVEL int +# @env BASH_FRAMEWORK_LOG_FILE String +# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 +# @exitcode 0 always successful +# @stderr diagnostics information about log file is displayed +Log::requireLoad() { + export REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement UI::requireTheme has not been loaded" + exit 1 + fi + + if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + export BASH_FRAMEWORK_LOG_LEVEL + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then + if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 + fi + elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + # will always be created even if not in info level + Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" + if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then + Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + fi + fi +} + + +# @description To be called before logging in the log file +# @arg $1 file:string log file name +# @arg $2 maxLogFilesCount:int maximum number of log files +Log::rotate() { + local file="$1" + local maxLogFilesCount="${2:-5}" + + if [[ ! -f "${file}" ]]; then + Log::displayDebug "Log file ${file} doesn't exist yet" + return 0 + fi + local i + for ((i = maxLogFilesCount - 1; i > 0; i--)); do + Log::displayInfo "Log rotation ${file}.${i} to ${file}.$((i + 1))" + mv "${file}."{"${i}","$((i + 1))"} &>/dev/null || true + done + if cp "${file}" "${file}.1" &>/dev/null; then + echo >"${file}" # reset log file + Log::displayInfo "Log rotation ${file} to ${file}.1" + fi +} + + +# @description draw a line with the character passed in parameter repeated depending on terminal width +# @arg $1 character:String character to use as separator (default value #) +UI::drawLine() { + local character="${1:-#}" + local -i width=${COLUMNS:-0} + if ((width == 0)) && [[ -t 1 ]]; then + width=$(tput cols) + fi + if ((width == 0)); then + width=80 + fi + printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") + echo +} + + +# @description load color theme +# @noargs +# @env BASH_FRAMEWORK_THEME String theme to use +# @env LOAD_THEME int 0 to avoid loading theme +# @exitcode 0 always successful +UI::requireTheme() { + export REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + + if [[ "${LOAD_THEME:-1}" = "1" ]]; then + UI::theme "${BASH_FRAMEWORK_THEME-default}" + fi +} + + +# @description load colors theme constants +# @warning if tty not opened, noColor theme will be chosen +# @arg $1 theme:String the theme to use (default, noColor) +# @arg $@ args:String[] +# @set __ERROR_COLOR String indicate error status +# @set __INFO_COLOR String indicate info status +# @set __SUCCESS_COLOR String indicate success status +# @set __WARNING_COLOR String indicate warning status +# @set __SKIPPED_COLOR String indicate skipped status +# @set __DEBUG_COLOR String indicate debug status +# @set __HELP_COLOR String indicate help status +# @set __TEST_COLOR String not used +# @set __TEST_ERROR_COLOR String not used +# @set __HELP_TITLE_COLOR String used to display help title in help strings +# @set __HELP_OPTION_COLOR String used to display highlight options in help strings +# +# @set __RESET_COLOR String reset default color +# +# @set __HELP_EXAMPLE String to remove +# @set __HELP_TITLE String to remove +# @set __HELP_NORMAL String to remove +# shellcheck disable=SC2034 +UI::theme() { + local theme="${1-default}" + if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then + theme="noColor" + fi + case "${theme}" in + default | default-force) + theme="default" + ;; + noColor) ;; + *) + Log::fatal "invalid theme provided" + ;; + esac + if [[ "${theme}" = "default" ]]; then + BASH_FRAMEWORK_THEME="default" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='\e[31m' # Red + __INFO_COLOR='\e[44m' # white on lightBlue + __SUCCESS_COLOR='\e[32m' # Green + __WARNING_COLOR='\e[33m' # Yellow + __SKIPPED_COLOR='\e[33m' # Yellow + __DEBUG_COLOR='\e[37m' # Gray + __HELP_COLOR='\e[7;49;33m' # Black on Gold + __TEST_COLOR='\e[100m' # Light magenta + __TEST_ERROR_COLOR='\e[41m' # white on red + __HELP_TITLE_COLOR="\e[1;37m" # Bold + __HELP_OPTION_COLOR="\e[1;34m" # Blue + # Internal: reset color + __RESET_COLOR='\e[0m' # Reset Color + # shellcheck disable=SC2155,SC2034 + __HELP_EXAMPLE="$(echo -e "\e[2;97m")" + # shellcheck disable=SC2155,SC2034 + __HELP_TITLE="$(echo -e "\e[1;37m")" + # shellcheck disable=SC2155,SC2034 + __HELP_NORMAL="$(echo -e "\033[0m")" + else + BASH_FRAMEWORK_THEME="noColor" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='' + __INFO_COLOR='' + __SUCCESS_COLOR='' + __WARNING_COLOR='' + __SKIPPED_COLOR='' + __DEBUG_COLOR='' + __HELP_COLOR='' + __TEST_COLOR='' + __TEST_ERROR_COLOR='' + __HELP_TITLE_COLOR='' + __HELP_OPTION_COLOR='' + # Internal: reset color + __RESET_COLOR='' + __HELP_EXAMPLE='' + __HELP_TITLE='' + __HELP_NORMAL='' + fi +} + + +# @description Check that command version is greater than expected minimal version +# display warning if command version greater than expected minimal version +# display error if command version less than expected minimal version and exit 1 +# @arg $1 commandName:String command path +# @arg $2 argVersion:String command line parameters to launch to get command version +# @arg $3 minimalVersion:String expected minimal command version +# @arg $4 parseVersionCallback:Function +# @arg $5 help:String optional help message to display if command does not exist +# @exitcode 0 if command version greater or equal to expected minimal version +# @exitcode 1 if command version less than expected minimal version +# @exitcode 2 if command does not exist +Version::checkMinimal() { + local commandName="$1" + local argVersion="$2" + local minimalVersion="$3" + local parseVersionCallback=${4:-Version::parse} + local help="${5:-}" + + Assert::commandExists "${commandName}" "${help}" || return 2 + + # shellcheck disable=SC2034 + local status=0 + # shellcheck disable=SC2034 + local -a pipeStatus=() + local version + version="$("${commandName}" "${argVersion}" 2>&1 | ${parseVersionCallback} || Bash::handlePipelineFailure status pipeStatus)" + + Log::displayDebug "check ${commandName} version ${version} against minimal ${minimalVersion}" + + Version::compare "${version}" "${minimalVersion}" || { + local result=$? + if [[ "${result}" = "1" ]]; then + Log::displayInfo "${commandName} version is ${version} greater than ${minimalVersion}" + elif [[ "${result}" = "2" ]]; then + Log::displayError "${commandName} minimal version is ${minimalVersion}, your version is ${version}" + return 1 + fi return 0 - fi - tty -s -} - -# @description log message to file -# @arg $1 message:String the message to display -Log::logFatal() { - Log::logMessage "${2:-FATAL}" "$1" -} - -# @description Internal: common log message -# @example text -# [date]|[levelMsg]|message -# -# @example text -# 2020-01-19 19:20:21|ERROR |log error -# 2020-01-19 19:20:21|SKIPPED|log skipped -# -# @arg $1 levelMsg:String message's level description (eg: STATUS, ERROR, ...) -# @arg $2 msg:String the message to display -# @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty -# @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages -# @stderr diagnostics information is displayed -# @require Env::requireLoad -# @require Log::requireLoad -Log::logMessage() { - local levelMsg="$1" - local msg="$2" - local date + } - if [[ -n "${BASH_FRAMEWORK_LOG_FILE}" ]] && ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - date="$(date '+%Y-%m-%d %H:%M:%S')" - touch "${BASH_FRAMEWORK_LOG_FILE}" - printf "%s|%7s|%s\n" "${date}" "${levelMsg}" "${msg}" >>"${BASH_FRAMEWORK_LOG_FILE}" - fi } -# @description To be called before logging in the log file -# @arg $1 file:string log file name -# @arg $2 maxLogFilesCount:int maximum number of log files -Log::rotate() { - local file="$1" - local maxLogFilesCount="${2:-5}" - if [[ ! -f "${file}" ]]; then - Log::displayDebug "Log file ${file} doesn't exist yet" +# @description compare 2 version numbers +# @arg $1 version1:String version 1 +# @arg $2 version2:String version 2 +# @exitcode 0 if equal +# @exitcode 1 if version1 > version2 +# @exitcode 2 else +Version::compare() { + if [[ "$1" = "$2" ]]; then return 0 fi - local i - for ((i = maxLogFilesCount - 1; i > 0; i--)); do - Log::displayInfo "Log rotation ${file}.${i} to ${file}.$((i + 1))" - mv "${file}."{"${i}","$((i + 1))"} &>/dev/null || true + local IFS=. + # shellcheck disable=2206 + local i ver1=($1) ver2=($2) + # fill empty fields in ver1 with zeros + for ((i = ${#ver1[@]}; i < ${#ver2[@]}; i++)); do + ver1[i]=0 done - if cp "${file}" "${file}.1" &>/dev/null; then - echo >"${file}" # reset log file - Log::displayInfo "Log rotation ${file} to ${file}.1" - fi -} - -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" + for ((i = 0; i < ${#ver1[@]}; i++)); do + if [[ -z "${ver2[i]+unset}" ]] || [[ -z ${ver2[i]} ]]; then + # fill empty fields in ver2 with zeros + ver2[i]=0 + fi + if ((10#${ver1[i]} > 10#${ver2[i]})); then + return 1 + fi + if ((10#${ver1[i]} < 10#${ver2[i]})); then + return 2 fi done + return 0 } -# @description load color theme -# @noargs -# @env BASH_FRAMEWORK_THEME String theme to use -# @env LOAD_THEME int 0 to avoid loading theme -# @exitcode 0 always successful -UI::requireTheme() { - if [[ "${LOAD_THEME:-1}" = "1" ]]; then - UI::theme "${BASH_FRAMEWORK_THEME-default}" - fi -} +# @description filter to keep only version number from a string +# @arg $@ files:String[] the files to filter +# @exitcode * if one of the filter command fails +# @stdin you can use stdin as alternative to files argument +# @stdout the filtered content +# shellcheck disable=SC2120 +Version::parse() { + # match anything, print(p), exit on first match(Q) + sed -En \ + -e 's/\x1b\[[0-9;]*[mGKHF]//g' \ + -e 's/[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/' \ + -e '//{p;Q}' \ + "$@" +} # FUNCTIONS -facade_main_gitIsAncestorOfsh() { -BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" -if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then - FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" -else - # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR - FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" -fi -FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" -FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" - -# @require BashTools::Conf::requireLoad -if [[ -f "${HOME}/.bash-tools/.env" ]]; then - export BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") -fi -# REQUIRES -Env::requireLoad -UI::requireTheme -Log::requireLoad -BashTools::Conf::requireLoad -Compiler::Facade::requireCommandBinDir -Linux::requireExecutedAsUser - -# @require Compiler::Facade::requireCommandBinDir -# shellcheck disable=SC2034 - -declare copyrightBeginYear="2020" -declare claimedBranchArg="" -declare commitArg="" declare -a BASH_FRAMEWORK_ARGV_FILTERED=() +beforeParseCallback() { + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + copyrightCallback() { - if [[ -z "${copyrightBeginYear}" ]]; then - copyrightBeginYear="$(date +%Y)" - fi - echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" + # + echo "Copyright (c) 2020-now François Chastanet" } # shellcheck disable=SC2317 # if function is overridden @@ -855,13 +912,14 @@ updateArgListQuietCallback() { :; } # shellcheck disable=SC2317 # if function is overridden optionHelpCallback() { - gitIsAncestorOfCommand help + Log::displayError "optionHelpCallback needs to be overridden" exit 0 } # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { - echo "${SCRIPT_NAME} version 1.0" + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version 3.0" exit 0 } @@ -879,21 +937,22 @@ optionEnvFileCallback() { optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_INFO} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionDebugVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_DEBUG} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionTraceVerboseCallback() { + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vvv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_TRACE} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } getLevel() { @@ -917,6 +976,7 @@ getLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -938,6 +998,7 @@ getVerboseLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -948,7 +1009,7 @@ optionDisplayLevelCallback() { logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -957,19 +1018,20 @@ optionLogLevelCallback() { local logLevel verboseLevel logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionLogFileCallback() { local logFile="$2" - echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionQuietCallback() { - echo "BASH_FRAMEWORK_QUIET_MODE=1" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_QUIET_MODE=1" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -992,532 +1054,779 @@ displayConfig() { exit 0 } -optionBashFrameworkConfigCallback() { - if [[ ! -f "$2" ]]; then - Log::fatal "Command ${SCRIPT_NAME} - Bash framework config file '$2' does not exists" - fi -} +optionBashFrameworkConfigCallback() { + if [[ ! -f "$2" ]]; then + Log::fatal "Command ${SCRIPT_NAME} - Bash framework config file '$2' does not exists" + fi +} + +defaultFrameworkConfig="$( + cat <<'EOF' + +# copied from src/_includes/.framework-config.default +# shellcheck disable=SC2034 + +REAL_SCRIPT_FILE="${REAL_SCRIPT_FILE:-$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")}" +FRAMEWORK_ROOT_DIR="${FRAMEWORK_ROOT_DIR:-${REAL_SCRIPT_FILE%/*/*}}" +FRAMEWORK_SRC_DIR="${FRAMEWORK_SRC_DIR:-${FRAMEWORK_ROOT_DIR}/src}" +FRAMEWORK_BIN_DIR="${FRAMEWORK_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/bin}" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_VENDOR_DIR:-${FRAMEWORK_ROOT_DIR}/vendor}" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_VENDOR_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/vendor/bin}" + +# describe the functions that will be skipped from being imported +FRAMEWORK_FUNCTIONS_IGNORE_REGEXP="${FRAMEWORK_FUNCTIONS_IGNORE_REGEXP:-^(Namespace::functions|Functions::myFunction|Namespace::requireSomething|Acquire::ForceIPv4)$}" +# describe the files that do not contain function to be imported +NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/_binaries|^src/_includes|^src/batsHeaders.sh$|^src/_standalone)}" +# describe the files that are allowed to not have an associated bats file +BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" +# describe the files that are allowed to not have a function matching the filename +FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|/testsData/|^manualTests/|\.bats$}" +# Source directories +if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then + FRAMEWORK_SRC_DIRS=( + "${FRAMEWORK_ROOT_DIR}/src" + ) +fi + +# export here all the variables that will be used in your templates +export REPOSITORY_URL="${REPOSITORY_URL:-https://github.com/fchastanet/bash-tools-framework}" + +BASH_FRAMEWORK_THEME="${BASH_FRAMEWORK_THEME:-default}" +BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" +BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" +BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" +BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" + +EOF +)" + +overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" + +commandOptionParseFinished() { + # load default template framework config + defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" + echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" + local -a files=("${defaultEnvFile}") + if [[ -f "${envFile}" ]]; then + files+=("${envFile}") + fi + # shellcheck disable=SC2154 + if [[ -f "${optionBashFrameworkConfig}" ]]; then + files+=("${optionBashFrameworkConfig}") + fi + files+=("${overrideEnvFile}") + Env::requireLoad "${files[@]}" + Log::requireLoad + # shellcheck disable=SC2154 + if [[ "${optionConfig}" = "1" ]]; then + displayConfig + fi +} + + + +longDescriptionFunction() { + echo -e " ${__HELP_TITLE}EXIT CODES:${__HELP_NORMAL}" + echo -e " ${__HELP_OPTION_COLOR}1${__HELP_NORMAL}: if commit does not exists" + echo -e " ${__HELP_OPTION_COLOR}2${__HELP_NORMAL}: if ref is not convertible to commit oid" + echo -e " ${__HELP_OPTION_COLOR}3${__HELP_NORMAL}: if commit is not included in given branch" +} + +optionHelpCallback() { + gitIsAncestorOfCommandHelp + exit 0 +} + + + +optionVersionCallback() { + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version 3.0" + Db::checkRequirements + exit 0 +} + + +# ------------------------------------------ +# Command gitIsAncestorOfCommand +# ------------------------------------------ + +# options variables initialization +declare optionHelp="0" +declare optionConfig="0" +declare optionBashFrameworkConfig="" +declare optionInfoVerbose="0" +declare optionDebugVerbose="0" +declare optionTraceVerbose="0" +declare -a optionEnvFiles=() +declare optionLogLevel="" +declare optionLogFile="" +declare optionDisplayLevel="" +declare optionNoColor="0" +declare optionTheme="default" +declare optionVersion="0" +declare optionQuiet="0" +# arguments variables initialization +declare claimedBranchArg="" +declare refArg="" +# @description parse command options and arguments for gitIsAncestorOfCommand +gitIsAncestorOfCommandParse() { + Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" + Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" + optionHelp="0" + local -i options_parse_optionParsedCountOptionHelp + ((options_parse_optionParsedCountOptionHelp = 0)) || true + optionConfig="0" + local -i options_parse_optionParsedCountOptionConfig + ((options_parse_optionParsedCountOptionConfig = 0)) || true + optionBashFrameworkConfig="" + local -i options_parse_optionParsedCountOptionBashFrameworkConfig + ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true + optionInfoVerbose="0" + local -i options_parse_optionParsedCountOptionInfoVerbose + ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true + optionDebugVerbose="0" + local -i options_parse_optionParsedCountOptionDebugVerbose + ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true + optionTraceVerbose="0" + local -i options_parse_optionParsedCountOptionTraceVerbose + ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true + + optionLogLevel="" + local -i options_parse_optionParsedCountOptionLogLevel + ((options_parse_optionParsedCountOptionLogLevel = 0)) || true + optionLogFile="" + local -i options_parse_optionParsedCountOptionLogFile + ((options_parse_optionParsedCountOptionLogFile = 0)) || true + optionDisplayLevel="" + local -i options_parse_optionParsedCountOptionDisplayLevel + ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true + optionNoColor="0" + local -i options_parse_optionParsedCountOptionNoColor + ((options_parse_optionParsedCountOptionNoColor = 0)) || true + optionTheme="default" + local -i options_parse_optionParsedCountOptionTheme + ((options_parse_optionParsedCountOptionTheme = 0)) || true + optionVersion="0" + local -i options_parse_optionParsedCountOptionVersion + ((options_parse_optionParsedCountOptionVersion = 0)) || true + optionQuiet="0" + local -i options_parse_optionParsedCountOptionQuiet + ((options_parse_optionParsedCountOptionQuiet = 0)) || true + + claimedBranchArg="" + local -i options_parse_argParsedCountClaimedBranchArg + ((options_parse_argParsedCountClaimedBranchArg = 0)) || true + + refArg="" + local -i options_parse_argParsedCountRefArg + ((options_parse_argParsedCountRefArg = 0)) || true + + + # shellcheck disable=SC2034 + local -i options_parse_parsedArgIndex=0 + while (($# > 0)); do + local options_parse_arg="$1" + local argOptDefaultBehavior=0 + case "${options_parse_arg}" in + # Option 1/14 + # optionHelp alts --help|-h + # type: Boolean min 0 max 1 + --help | -h) + # shellcheck disable=SC2034 + optionHelp="1" + + if ((options_parse_optionParsedCountOptionHelp >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionHelp)) + optionHelpCallback "${options_parse_arg}" "${optionHelp}" + + ;; + + # Option 2/14 + # optionConfig alts --config + # type: Boolean min 0 max 1 + --config) + # shellcheck disable=SC2034 + optionConfig="1" + + if ((options_parse_optionParsedCountOptionConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionConfig)) + ;; + + # Option 3/14 + # optionBashFrameworkConfig alts --bash-framework-config + # type: String min 0 max 1 + --bash-framework-config) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) + # shellcheck disable=SC2034 + optionBashFrameworkConfig="$1" + optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" + + ;; + + # Option 4/14 + # optionInfoVerbose alts --verbose|-v + # type: Boolean min 0 max 1 + --verbose | -v) + # shellcheck disable=SC2034 + optionInfoVerbose="1" + + if ((options_parse_optionParsedCountOptionInfoVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionInfoVerbose)) + optionInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + updateArgListInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + ;; + + # Option 5/14 + # optionDebugVerbose alts -vv + # type: Boolean min 0 max 1 + -vv) + # shellcheck disable=SC2034 + optionDebugVerbose="1" + + if ((options_parse_optionParsedCountOptionDebugVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDebugVerbose)) + optionDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + updateArgListDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + ;; + + # Option 6/14 + # optionTraceVerbose alts -vvv + # type: Boolean min 0 max 1 + -vvv) + # shellcheck disable=SC2034 + optionTraceVerbose="1" + + if ((options_parse_optionParsedCountOptionTraceVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTraceVerbose)) + optionTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + updateArgListTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + ;; + + # Option 7/14 + # optionEnvFiles alts --env-file + # type: StringArray min 0 max -1 + --env-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + ((++options_parse_optionParsedCountOptionEnvFiles)) + optionEnvFiles+=("$1") + optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + ;; + + # Option 8/14 + # optionLogLevel alts --log-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --log-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogLevel)) + # shellcheck disable=SC2034 + optionLogLevel="$1" + optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + ;; + + # Option 9/14 + # optionLogFile alts --log-file + # type: String min 0 max 1 + --log-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogFile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogFile)) + # shellcheck disable=SC2034 + optionLogFile="$1" + optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + ;; + + # Option 10/14 + # optionDisplayLevel alts --display-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --display-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionDisplayLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDisplayLevel)) + # shellcheck disable=SC2034 + optionDisplayLevel="$1" + optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + ;; + + # Option 11/14 + # optionNoColor alts --no-color + # type: Boolean min 0 max 1 + --no-color) + # shellcheck disable=SC2034 + optionNoColor="1" + + if ((options_parse_optionParsedCountOptionNoColor >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionNoColor)) + optionNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + updateArgListNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + ;; + + # Option 12/14 + # optionTheme alts --theme + # type: String min 0 max 1 + # authorizedValues: default|default-force|noColor + --theme) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ default|default-force|noColor ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionTheme >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTheme)) + # shellcheck disable=SC2034 + optionTheme="$1" + optionThemeCallback "${options_parse_arg}" "${optionTheme}" + + updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" -defaultFrameworkConfig="$( - cat <<'EOF' -# copied from src/_includes/.framework-config.default -# shellcheck disable=SC2034 + ;; -REAL_SCRIPT_FILE="${REAL_SCRIPT_FILE:-$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")}" -FRAMEWORK_ROOT_DIR="${FRAMEWORK_ROOT_DIR:-${REAL_SCRIPT_FILE%/*/*}}" -FRAMEWORK_SRC_DIR="${FRAMEWORK_SRC_DIR:-${FRAMEWORK_ROOT_DIR}/src}" -FRAMEWORK_BIN_DIR="${FRAMEWORK_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/bin}" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_VENDOR_DIR:-${FRAMEWORK_ROOT_DIR}/vendor}" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_VENDOR_BIN_DIR:-${FRAMEWORK_ROOT_DIR}/vendor/bin}" + # Option 13/14 + # optionVersion alts --version + # type: Boolean min 0 max 1 + --version) + # shellcheck disable=SC2034 + optionVersion="1" -# describe the functions that will be skipped from being imported -FRAMEWORK_FUNCTIONS_IGNORE_REGEXP="${FRAMEWORK_FUNCTIONS_IGNORE_REGEXP:-^(Namespace::functions|Functions::myFunction|Namespace::requireSomething|Acquire::ForceIPv4)$}" -# describe the files that do not contain function to be imported -NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/_binaries|^src/_includes|^src/batsHeaders.sh$|^src/_standalone)}" -# describe the files that are allowed to not have an associated bats file -BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" -# describe the files that are allowed to not have a function matching the filename -FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|\.tpl$|/testsData/|^manualTests/|\.bats$}" -# Source directories -if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then - FRAMEWORK_SRC_DIRS=( - "${FRAMEWORK_ROOT_DIR}/src" - ) -fi + if ((options_parse_optionParsedCountOptionVersion >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionVersion)) + optionVersionCallback "${options_parse_arg}" "${optionVersion}" -# export here all the variables that will be used in your templates -export REPOSITORY_URL="${REPOSITORY_URL:-https://github.com/fchastanet/bash-tools-framework}" + ;; -BASH_FRAMEWORK_THEME="${BASH_FRAMEWORK_THEME:-default}" -BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" -BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" -BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" -BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" -EOF -)" + # Option 14/14 + # optionQuiet alts --quiet|-q + # type: Boolean min 0 max 1 + --quiet | -q) + # shellcheck disable=SC2034 + optionQuiet="1" -overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" + if ((options_parse_optionParsedCountOptionQuiet >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionQuiet)) + optionQuietCallback "${options_parse_arg}" "${optionQuiet}" -commandOptionParseFinished() { - # load default template framework config - defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" - echo "${defaultFrameworkConfig}" > "${defaultEnvFile}" - local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi - # shellcheck disable=SC2154 - if [[ -f "${optionBashFrameworkConfig}" ]]; then - files+=("${optionBashFrameworkConfig}") - fi - files+=("${overrideEnvFile}") - Env::requireLoad "${files[@]}" - Log::requireLoad - # shellcheck disable=SC2154 - if [[ "${optionConfig}" = "1" ]]; then - displayConfig - fi -} + updateArgListQuietCallback "${options_parse_arg}" "${optionQuiet}" -gitIsAncestorOfCommand() { - local options_parse_cmd="$1" - shift || true + ;; - if [[ "${options_parse_cmd}" = "parse" ]]; then - local -i options_parse_optionParsedCountOptionBashFrameworkConfig - ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true - optionConfig="0" - local -i options_parse_optionParsedCountOptionConfig - ((options_parse_optionParsedCountOptionConfig = 0)) || true - optionInfoVerbose="0" - local -i options_parse_optionParsedCountOptionInfoVerbose - ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true - optionDebugVerbose="0" - local -i options_parse_optionParsedCountOptionDebugVerbose - ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true - optionTraceVerbose="0" - local -i options_parse_optionParsedCountOptionTraceVerbose - ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionNoColor="0" - local -i options_parse_optionParsedCountOptionNoColor - ((options_parse_optionParsedCountOptionNoColor = 0)) || true - optionTheme="default" - local -i options_parse_optionParsedCountOptionTheme - ((options_parse_optionParsedCountOptionTheme = 0)) || true - optionHelp="0" - local -i options_parse_optionParsedCountOptionHelp - ((options_parse_optionParsedCountOptionHelp = 0)) || true - optionVersion="0" - local -i options_parse_optionParsedCountOptionVersion - ((options_parse_optionParsedCountOptionVersion = 0)) || true - optionQuiet="0" - local -i options_parse_optionParsedCountOptionQuiet - ((options_parse_optionParsedCountOptionQuiet = 0)) || true - local -i options_parse_optionParsedCountOptionLogLevel - ((options_parse_optionParsedCountOptionLogLevel = 0)) || true - local -i options_parse_optionParsedCountOptionLogFile - ((options_parse_optionParsedCountOptionLogFile = 0)) || true - local -i options_parse_optionParsedCountOptionDisplayLevel - ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true - local -i options_parse_argParsedCountClaimedBranchArg - ((options_parse_argParsedCountClaimedBranchArg = 0)) || true - local -i options_parse_argParsedCountCommitArg - ((options_parse_argParsedCountCommitArg = 0)) || true - # shellcheck disable=SC2034 - local -i options_parse_parsedArgIndex=0 - while (($# > 0)); do - local options_parse_arg="$1" - local argOptDefaultBehavior=0 - case "${options_parse_arg}" in - # Option 1/14 - # Option optionBashFrameworkConfig --bash-framework-config variableType String min 0 max 1 authorizedValues '' regexp '' - --bash-framework-config) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) - # shellcheck disable=SC2034 - optionBashFrameworkConfig="$1" - optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" - ;; - # Option 2/14 - # Option optionConfig --config variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --config) - # shellcheck disable=SC2034 - optionConfig="1" - if ((options_parse_optionParsedCountOptionConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionConfig)) - ;; - # Option 3/14 - # Option optionInfoVerbose --verbose|-v variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --verbose | -v) - # shellcheck disable=SC2034 - optionInfoVerbose="1" - if ((options_parse_optionParsedCountOptionInfoVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionInfoVerbose)) - optionInfoVerboseCallback "${options_parse_arg}" - updateArgListInfoVerboseCallback "${options_parse_arg}" - ;; - # Option 4/14 - # Option optionDebugVerbose -vv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vv) - # shellcheck disable=SC2034 - optionDebugVerbose="1" - if ((options_parse_optionParsedCountOptionDebugVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDebugVerbose)) - optionDebugVerboseCallback "${options_parse_arg}" - updateArgListDebugVerboseCallback "${options_parse_arg}" - ;; - # Option 5/14 - # Option optionTraceVerbose -vvv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vvv) - # shellcheck disable=SC2034 - optionTraceVerbose="1" - if ((options_parse_optionParsedCountOptionTraceVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTraceVerbose)) - optionTraceVerboseCallback "${options_parse_arg}" - updateArgListTraceVerboseCallback "${options_parse_arg}" - ;; - # Option 6/14 - # Option optionEnvFiles --env-file variableType StringArray min 0 max -1 authorizedValues '' regexp '' - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - ;; - # Option 7/14 - # Option optionNoColor --no-color variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --no-color) - # shellcheck disable=SC2034 - optionNoColor="1" - if ((options_parse_optionParsedCountOptionNoColor >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionNoColor)) - optionNoColorCallback "${options_parse_arg}" - updateArgListNoColorCallback "${options_parse_arg}" - ;; - # Option 8/14 - # Option optionTheme --theme variableType String min 0 max 1 authorizedValues 'default|default-force|noColor' regexp '' - --theme) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default|default-force|noColor)" - return 1 - fi - if ((options_parse_optionParsedCountOptionTheme >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTheme)) - # shellcheck disable=SC2034 - optionTheme="$1" - optionThemeCallback "${options_parse_arg}" "${optionTheme}" - updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" - ;; - # Option 9/14 - # Option optionHelp --help|-h variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --help | -h) - # shellcheck disable=SC2034 - optionHelp="1" - if ((options_parse_optionParsedCountOptionHelp >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionHelp)) - optionHelpCallback "${options_parse_arg}" - ;; - # Option 10/14 - # Option optionVersion --version variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --version) - # shellcheck disable=SC2034 - optionVersion="1" - if ((options_parse_optionParsedCountOptionVersion >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionVersion)) - optionVersionCallback "${options_parse_arg}" - ;; - # Option 11/14 - # Option optionQuiet --quiet|-q variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --quiet | -q) - # shellcheck disable=SC2034 - optionQuiet="1" - if ((options_parse_optionParsedCountOptionQuiet >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionQuiet)) - optionQuietCallback "${options_parse_arg}" - updateArgListQuietCallback "${options_parse_arg}" - ;; - # Option 12/14 - # Option optionLogLevel --log-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --log-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogLevel)) - # shellcheck disable=SC2034 - optionLogLevel="$1" - optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - ;; - # Option 13/14 - # Option optionLogFile --log-file variableType String min 0 max 1 authorizedValues '' regexp '' - --log-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogFile >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + -*) + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" + return 1 + fi + ;; + *) + ((minParsedArgIndex0 = 0)) || true + ((maxParsedArgIndex0 = 0)) || true + ((minParsedArgIndex1 = minParsedArgIndex0 + 1)) || true + ((maxParsedArgIndex1 = maxParsedArgIndex0 + 1)) || true + ((minParsedArgIndex2 = minParsedArgIndex1 + 1)) || true + ((maxParsedArgIndex2 = maxParsedArgIndex1 + 1)) || true + ((incrementArg = 1 )) + if ((0)); then + # Technical if - never reached + : + + # Argument 1/2 - claimedBranchArg + # Argument claimedBranchArg min 1 max 1 + # Argument claimedBranchArg authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && + options_parse_parsedArgIndex < maxParsedArgIndex1 )); then + if ((options_parse_argParsedCountClaimedBranchArg >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument claimedBranch - Maximum number of argument occurrences reached(1)" return 1 fi - ((++options_parse_optionParsedCountOptionLogFile)) + ((++options_parse_argParsedCountClaimedBranchArg)) # shellcheck disable=SC2034 - optionLogFile="$1" - optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" - updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" - ;; - # Option 14/14 - # Option optionDisplayLevel --display-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --display-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionDisplayLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + claimedBranchArg="${options_parse_arg}" + + + # Argument 2/2 - refArg + # Argument refArg min 1 max 1 + # Argument refArg authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex1 && + options_parse_parsedArgIndex < maxParsedArgIndex2 )); then + if ((options_parse_argParsedCountRefArg >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument ref - Maximum number of argument occurrences reached(1)" return 1 fi - ((++options_parse_optionParsedCountOptionDisplayLevel)) + ((++options_parse_argParsedCountRefArg)) # shellcheck disable=SC2034 - optionDisplayLevel="$1" - optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - ;; - -*) + refArg="${options_parse_arg}" + + + # else too much args + else + + if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" + # too much args and no unknownArgumentCallbacks configured + Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" return 1 fi - ;; - *) - if ((0)); then - # Technical if - never reached - : - # Argument 1/2 - # Argument claimedBranchArg min 1 max 1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 0 && options_parse_parsedArgIndex < 1)); then - if ((options_parse_argParsedCountClaimedBranchArg >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument claimedBranch - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountClaimedBranchArg)) - # shellcheck disable=SC2034 - claimedBranchArg="${options_parse_arg}" - # Argument 2/2 - # Argument commitArg min 1 max 1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 1 && options_parse_parsedArgIndex < 2)); then - if ((options_parse_argParsedCountCommitArg >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument commit - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountCommitArg)) - # shellcheck disable=SC2034 - commitArg="${options_parse_arg}" - else - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" - return 1 - fi - fi + + fi + if ((incrementArg == 1)); then ((++options_parse_parsedArgIndex)) - ;; - esac - shift || true - done - if ((options_parse_argParsedCountClaimedBranchArg < 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument 'claimedBranch' should be provided at least 1 time(s)" - return 1 - fi - if ((options_parse_argParsedCountCommitArg < 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument 'commit' should be provided at least 1 time(s)" - return 1 - fi - commandOptionParseFinished - Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" - Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" - elif [[ "${options_parse_cmd}" = "help" ]]; then - Array::wrap2 " " 80 0 "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" "check if commit is inside a given branch" - echo + fi + ;; + esac + shift || true + done - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "${SCRIPT_NAME}" "[OPTIONS]" "[ARGUMENTS]")" - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ - "${SCRIPT_NAME}" \ - "[--bash-framework-config ]" "[--config]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--no-color]" "[--theme ]" "[--help|-h]" "[--version]" "[--quiet|-q]" "[--log-level ]" "[--log-file ]" "[--display-level ]")" - echo - echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}claimedBranch${__HELP_NORMAL} {single} (mandatory)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(the\ branch\ in\ which\ the\ commit\ will\ be\ searched) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}commit${__HELP_NORMAL} {single} (mandatory)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(the\ commit\ oid\ to\ check) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(use\ alternate\ bash\ framework\ configuration.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ configuration) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(info\ level\ verbose\ mode\ \(alias\ of\ --display-level\ INFO\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(debug\ level\ verbose\ mode\ \(alias\ of\ --display-level\ DEBUG\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(trace\ level\ verbose\ mode\ \(alias\ of\ --display-level\ TRACE\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Load\ the\ specified\ env\ file\ \(deprecated\,\ please\ use\ --bash-framework-config\ option\ instead\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Produce\ monochrome\ output.\ alias\ of\ --theme\ noColor.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' - echo ' Possible values: default|default-force|noColor' - echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ this\ command\ help) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Print\ version\ information\ and\ quit) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(quiet\ mode\,\ doesn\'t\ display\ any\ output) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ file) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(set\ display\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e """ -${__HELP_TITLE}EXIT CODES:${__HELP_NORMAL} -${__HELP_OPTION_COLOR}1${__HELP_NORMAL}: if commit does not exists -${__HELP_OPTION_COLOR}2${__HELP_NORMAL}: if commit is not included in given branch""" - echo - echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo '1.0' - echo - echo -e "${__HELP_TITLE_COLOR}AUTHOR:${__RESET_COLOR}" - echo '[François Chastanet](https://github.com/fchastanet)' - echo - echo -e "${__HELP_TITLE_COLOR}SOURCE FILE:${__RESET_COLOR}" - echo 'https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Git/gitIsAncestorOf.sh' - echo - echo -e "${__HELP_TITLE_COLOR}LICENSE:${__RESET_COLOR}" - echo 'MIT License' - echo - Array::wrap2 ' ' 76 4 "$(copyrightCallback)" - else - Log::displayError "Command ${SCRIPT_NAME} - Option command invalid: '${options_parse_cmd}'" + + + + + + + + + + + + + + if ((options_parse_argParsedCountClaimedBranchArg < 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument 'claimedBranch' should be provided at least 1 time(s)" return 1 fi + + if ((options_parse_argParsedCountRefArg < 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument 'ref' should be provided at least 1 time(s)" + return 1 + fi || return $? + + commandOptionParseFinished + } -gitIsAncestorOfCommand parse "${BASH_FRAMEWORK_ARGV[@]}" +# @description display command options and arguments help for gitIsAncestorOfCommand +gitIsAncestorOfCommandHelp() { + echo -e "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" + Array::wrap2 ' ' 76 4 " " "check if ref is inside a given branch." + + echo + echo + + # ------------------------------------------ + # usage section + # ------------------------------------------ + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "gitIsAncestorOf [OPTIONS] [ARGUMENTS]" + echo + # ------------------------------------------ + # usage/options section + # ------------------------------------------ + optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" + ) + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ + "gitIsAncestorOf" "${optionsAltList[@]}" + echo -# @require Linux::requireExecutedAsUser -run() { + # ------------------------------------------ + # usage/arguments section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - if ! git cat-file -t "${commitArg}" &>/dev/null; then - Log::displayError "Commit ${commitArg} does not exists at all" - exit 1 - fi + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}claimedBranch${__HELP_NORMAL} {single} (mandatory) + " + Array::wrap2 ' ' 76 4 " " "The branch in which the ref will be searched." + echo + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}ref${__HELP_NORMAL} {single} (mandatory) + " + Array::wrap2 ' ' 76 4 " " "The ref to check." + echo + # ------------------------------------------ + # options section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays this command help" + echo - merge_base="$(git merge-base "${commitArg}" "${claimedBranchArg}")" - if [[ -z "${merge_base}" || "${merge_base}" != "$(git rev-parse --verify "${commitArg}")" ]]; then - Log::displayError "Commit ${commitArg} is not an ancestor of branch ${claimedBranchArg}" - exit 2 - fi + + + echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays configuration" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" + Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log file" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set display level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." + echo + Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" + echo + + Array::wrap2 ' ' 76 6 " Default value: " "default" + echo + + echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Print version information and quit." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." + echo + + + # ------------------------------------------ + # longDescription section + # ------------------------------------------ + echo + echo + echo -e "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" + longDescriptionFunction + # ------------------------------------------ + # version section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" + echo "3.0" + # ------------------------------------------ + # author section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}AUTHOR: ${__RESET_COLOR}" + echo "[François Chastanet](https://github.com/fchastanet)" + # ------------------------------------------ + # sourceFile section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-binary.yaml" + # ------------------------------------------ + # license section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}LICENSE: ${__RESET_COLOR}" + echo "MIT License" + # ------------------------------------------ + # copyright section + # ------------------------------------------ + Array::wrap2 ' ' 76 0 "$(copyrightCallback)" } -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run + +beforeParseCallback + +gitIsAncestorOfCommandParse "$@" +MAIN_FUNCTION_NAME="main" +main() { + +runVerboseIfNeeded() { + ( + # shellcheck disable=SC2154 + if [[ "${optionTraceVerbose}" = "1" ]]; then + set -x + fi + "$@" + ) +} +# shellcheck disable=SC2154 +if ! runVerboseIfNeeded git cat-file -t "${refArg}" &>/dev/null; then + Log::displayError "Ref ${refArg} does not exists at all" + exit 1 +fi +# shellcheck disable=SC2154 +if ! runVerboseIfNeeded git cat-file -t "${claimedBranchArg}" &>/dev/null; then + Log::displayError "Ref ${claimedBranchArg} does not exists at all" + exit 1 +fi + +declare refCommit claimedBranchCommit +refCommit="$(git rev-parse "${refArg}")" || { + Log::displayError "Ref ${refArg} is not convertible to commit sha" + exit 1 +} + +claimedBranchCommit="$(git rev-parse "${claimedBranchArg}")" || { + Log::displayError "Ref ${claimedBranchArg} is not convertible to commit sha" + exit 1 +} + +declare revParse mergeBase +# shellcheck disable=SC2154 +mergeBase="$(runVerboseIfNeeded git merge-base "${refCommit}" "${claimedBranchCommit}")" +revParse="$(runVerboseIfNeeded git rev-parse --verify "${refCommit}")" + +if [[ -z "${mergeBase}" || "${mergeBase}" != "${revParse}" ]]; then + Log::displayError "Commit ${refArg} is not an ancestor of branch ${claimedBranchArg}" + exit 2 fi } -facade_main_gitIsAncestorOfsh "$@" +# if file is sourced avoid calling main function +# shellcheck disable=SC2178 +BASH_SOURCE=".$0" # cannot be changed in bash +# shellcheck disable=SC2128 +if test ".$0" == ".${BASH_SOURCE}"; then + if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then + main "$@" &>/dev/null + else + main "$@" + fi +fi diff --git a/src/_binaries/Docker/cli/cli-options.sh b/src/_binaries/Docker/cli/cli-options.sh index ee9b169c..453cb457 100755 --- a/src/_binaries/Docker/cli/cli-options.sh +++ b/src/_binaries/Docker/cli/cli-options.sh @@ -5,6 +5,7 @@ declare containerArg="default" declare finalUserArg="${defaultUserArg}" declare finalCommandArg=("${defaultCommandArg[@]}") +declare defaultConfiguration="default" declare defaultUserArg="root" declare -a defaultCommandArg=("//bin/sh") declare PROFILES_DIR @@ -35,7 +36,7 @@ initConf() { PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/cliProfiles" HOME_PROFILES_DIR="${HOME}/.bash-tools/cliProfiles" # load default conf file - Conf::load "cliProfiles" "default" + Conf::load "cliProfiles" "${defaultConfiguration}" } optionHelpCallback() { @@ -68,36 +69,37 @@ longDescriptionFunction() { } containerArgHelpFunction() { - Conf::load "cliProfiles" "default" - echo " Container should be the name of a profile from profile list," - echo " check containers list below." - echo " If not provided, it will load the container specified in default configuration." - # shellcheck disable=SC2154 - echo -e " Default configuration: ${__HELP_OPTION_COLOR}${containerArg}${__HELP_NORMAL}" + Conf::load "cliProfiles" "${defaultConfiguration}" + echo -e " Container should be the name of a profile from profile list," + echo -e " check containers list below." + echo + echo -e " If no value provided, it will load the container" + echo -e " specified in ${__HELP_OPTION_COLOR}${defaultConfiguration}${__HELP_NORMAL} configuration." # shellcheck disable=SC2154 - echo -e " Default container: ${__HELP_OPTION_COLOR}${finalContainerArg}${__HELP_NORMAL}" + echo -e " Default: ${__HELP_OPTION_COLOR}${finalContainerArg}${__HELP_NORMAL}" + echo } userArgHelpFunction() { - Conf::load "cliProfiles" "default" - echo " user to connect on this container" $'\n' + Conf::load "cliProfiles" "${defaultConfiguration}" + echo -e " user to connect on this container" + echo + echo -e " If no value provided, it will load the user" + echo -e " specified in ${__HELP_OPTION_COLOR}${defaultConfiguration}${__HELP_NORMAL} configuration." # shellcheck disable=SC2154 - echo -e " Default user: ${__HELP_OPTION_COLOR}${finalUserArg}${__HELP_NORMAL}" - echo " loaded from profile selected as first arg" - echo " or deduced from default configuration." $'\n' - echo -e " Default configuration: ${__HELP_OPTION_COLOR}${containerArg}${__HELP_NORMAL}" $'\n' - echo " if first arg is not a profile" + echo -e " Default: ${__HELP_OPTION_COLOR}${finalUserArg}${__HELP_NORMAL}" + echo } commandArgHelpFunction() { - Conf::load "cliProfiles" "default" - echo "The command to execute" $'\n' + Conf::load "cliProfiles" "${defaultConfiguration}" + echo -e " The command to execute" + echo + echo -e " If no value provided, it will load the command" + echo -e " specified in ${__HELP_OPTION_COLOR}${defaultConfiguration}${__HELP_NORMAL} configuration." # shellcheck disable=SC2154 - echo -e "Default command: ${__HELP_OPTION_COLOR}${finalCommandArg[*]}${__HELP_NORMAL}" - echo " loaded from profile selected as first arg" - echo " or deduced from default configuration." - echo -e "Default configuration: ${__HELP_OPTION_COLOR}${containerArg}${__HELP_NORMAL}" $'\n' - echo "if first arg is not a profile" + echo -e " Default: ${__HELP_OPTION_COLOR}${finalCommandArg[*]}${__HELP_NORMAL}" + echo } # shellcheck disable=SC2317 # if function is overridden diff --git a/src/_binaries/Docker/cli/testsData/cli.help.txt b/src/_binaries/Docker/cli/testsData/cli.help.txt index a0b1dcd2..7839be46 100644 --- a/src/_binaries/Docker/cli/testsData/cli.help.txt +++ b/src/_binaries/Docker/cli/testsData/cli.help.txt @@ -12,28 +12,25 @@ [container {single}] Container should be the name of a profile from profile list, check containers list below. - If not provided, it will load the container specified in default configuration. - Default configuration:  - Default container: project-apache2 + + If no value provided, it will load the container + specified in default configuration. + Default: project-apache2 + [user {single}] user to connect on this container - Default user: www-data - loaded from profile selected as first arg - or deduced from default configuration. - - Default configuration:  + If no value provided, it will load the user + specified in default configuration. + Default: www-data - if first arg is not a profile [command {single}] -The command to execute + The command to execute -Default command: /bin/bash - loaded from profile selected as first arg - or deduced from default configuration. -Default configuration:  + If no value provided, it will load the command + specified in default configuration. + Default: /bin/bash -if first arg is not a profile GLOBAL OPTIONS: --help, -h {single} diff --git a/src/_binaries/Git/gitIsAncestorOf.options.tpl b/src/_binaries/Git/gitIsAncestorOf.options.tpl deleted file mode 100644 index 5de5e65d..00000000 --- a/src/_binaries/Git/gitIsAncestorOf.options.tpl +++ /dev/null @@ -1,42 +0,0 @@ -% - -declare commandFunctionName="gitIsAncestorOfCommand" -declare help="check if commit is inside a given branch" -declare longDescription=''' -${__HELP_TITLE}EXIT CODES:${__HELP_NORMAL} -${__HELP_OPTION_COLOR}1${__HELP_NORMAL}: if commit does not exists -${__HELP_OPTION_COLOR}2${__HELP_NORMAL}: if commit is not included in given branch -''' -% - -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.base.tpl)" - -% -# shellcheck source=/dev/null -source <( - containerArgHelpCallback() { :; } - Options::generateArg \ - --help "the branch in which the commit will be searched" \ - --min 1 \ - --max 1 \ - --name "claimedBranch" \ - --variable-name "claimedBranchArg" \ - --function-name claimedBranchArgFunction - - userArgHelpCallback() { :; } - Options::generateArg \ - --help "the commit oid to check" \ - --min 1 \ - --max 1 \ - --name "commit" \ - --variable-name "commitArg" \ - --function-name commitArgFunction -) -options+=( - claimedBranchArgFunction - commitArgFunction -) -Options::generateCommand "${options[@]}" -% - -<% ${commandFunctionName} %> parse "${BASH_FRAMEWORK_ARGV[@]}" diff --git a/src/_binaries/Git/gitIsAncestorOf.sh b/src/_binaries/Git/gitIsAncestorOf.sh deleted file mode 100755 index 86020d89..00000000 --- a/src/_binaries/Git/gitIsAncestorOf.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bash -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/gitIsAncestorOf -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE -# shellcheck disable=SC2034 - -declare copyrightBeginYear="2020" -declare claimedBranchArg="" -declare commitArg="" - -.INCLUDE "$(dynamicTemplateDir _binaries/Git/gitIsAncestorOf.options.tpl)" - -# @require Linux::requireExecutedAsUser -run() { - - if ! git cat-file -t "${commitArg}" &>/dev/null; then - Log::displayError "Commit ${commitArg} does not exists at all" - exit 1 - fi - - merge_base="$(git merge-base "${commitArg}" "${claimedBranchArg}")" - if [[ -z "${merge_base}" || "${merge_base}" != "$(git rev-parse --verify "${commitArg}")" ]]; then - Log::displayError "Commit ${commitArg} is not an ancestor of branch ${claimedBranchArg}" - exit 2 - fi -} - -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run -fi diff --git a/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-binary.yaml b/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-binary.yaml new file mode 100644 index 00000000..fc52edc3 --- /dev/null +++ b/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-binary.yaml @@ -0,0 +1,40 @@ +extends: + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" + +vars: + SRC_FILE_PATH: src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-binary.yaml + +compilerConfig: + targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/gitIsAncestorOf" + relativeRootDirBasedOnTargetDir: .. + srcDirs: + - ${BASH_TOOLS_ROOT_DIR}/src +binData: + commands: + default: + functionName: gitIsAncestorOfCommand + version: "3.0" + copyrightBeginYear: 2020 + commandName: gitIsAncestorOf + beforeParseCallbacks: + - beforeParseCallback + definitionFiles: + 20: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-options.sh + mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-main.sh + help: check if ref is inside a given branch. + longDescription: longDescriptionFunction + args: + - type: String + min: 1 + max: 1 + help: The branch in which the ref will be searched. + name: claimedBranch + variableName: claimedBranchArg + - type: String + min: 1 + max: 1 + help: The ref to check. + name: ref + variableName: refArg diff --git a/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-main.sh b/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-main.sh new file mode 100755 index 00000000..4c124d7f --- /dev/null +++ b/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-main.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash + +runVerboseIfNeeded() { + ( + # shellcheck disable=SC2154 + if [[ "${optionTraceVerbose}" = "1" ]]; then + set -x + fi + "$@" + ) +} +# shellcheck disable=SC2154 +if ! runVerboseIfNeeded git cat-file -t "${refArg}" &>/dev/null; then + Log::displayError "Ref ${refArg} does not exists at all" + exit 1 +fi +# shellcheck disable=SC2154 +if ! runVerboseIfNeeded git cat-file -t "${claimedBranchArg}" &>/dev/null; then + Log::displayError "Ref ${claimedBranchArg} does not exists at all" + exit 1 +fi + +declare refCommit claimedBranchCommit +refCommit="$(git rev-parse "${refArg}")" || { + Log::displayError "Ref ${refArg} is not convertible to commit sha" + exit 1 +} + +claimedBranchCommit="$(git rev-parse "${claimedBranchArg}")" || { + Log::displayError "Ref ${claimedBranchArg} is not convertible to commit sha" + exit 1 +} + +declare revParse mergeBase +# shellcheck disable=SC2154 +mergeBase="$(runVerboseIfNeeded git merge-base "${refCommit}" "${claimedBranchCommit}")" +revParse="$(runVerboseIfNeeded git rev-parse --verify "${refCommit}")" + +if [[ -z "${mergeBase}" || "${mergeBase}" != "${revParse}" ]]; then + Log::displayError "Commit ${refArg} is not an ancestor of branch ${claimedBranchArg}" + exit 2 +fi diff --git a/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-options.sh b/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-options.sh new file mode 100755 index 00000000..ed9ed160 --- /dev/null +++ b/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-options.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +longDescriptionFunction() { + echo -e " ${__HELP_TITLE}EXIT CODES:${__HELP_NORMAL}" + echo -e " ${__HELP_OPTION_COLOR}1${__HELP_NORMAL}: if commit does not exists" + echo -e " ${__HELP_OPTION_COLOR}2${__HELP_NORMAL}: if ref is not convertible to commit oid" + echo -e " ${__HELP_OPTION_COLOR}3${__HELP_NORMAL}: if commit is not included in given branch" +} + +optionHelpCallback() { + gitIsAncestorOfCommandHelp + exit 0 +} diff --git a/src/_binaries/Git/gitIsAncestorOf.bats b/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf.bats similarity index 88% rename from src/_binaries/Git/gitIsAncestorOf.bats rename to src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf.bats index fccb5199..06a86e19 100755 --- a/src/_binaries/Git/gitIsAncestorOf.bats +++ b/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf.bats @@ -1,7 +1,7 @@ #!/usr/bin/env bash # shellcheck source=src/batsHeaders.sh -source "$(cd "${BATS_TEST_DIRNAME}/../.." && pwd)/batsHeaders.sh" +source "$(cd "${BATS_TEST_DIRNAME}/../../.." && pwd)/batsHeaders.sh" load "${FRAMEWORK_ROOT_DIR}/src/_standalone/Bats/assert_lines_count.sh" diff --git a/src/_binaries/Git/gitIsAncestorOf/testsData/gitIsAncestorOf.help.txt b/src/_binaries/Git/gitIsAncestorOf/testsData/gitIsAncestorOf.help.txt new file mode 100644 index 00000000..054c5c23 --- /dev/null +++ b/src/_binaries/Git/gitIsAncestorOf/testsData/gitIsAncestorOf.help.txt @@ -0,0 +1,67 @@ +SYNOPSIS: + check if ref is inside a given branch. + +USAGE: gitIsAncestorOf [OPTIONS] [ARGUMENTS] +USAGE: gitIsAncestorOf [--help|-h] [--config] + [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] + [--env-file ] [--log-level ] [--log-file ] + [--display-level ] [--no-color] [--theme ] [--version] + [--quiet|-q] + +ARGUMENTS: + claimedBranch {single} (mandatory) + The branch in which the ref will be searched. + ref {single} (mandatory) + The ref to check. + +GLOBAL OPTIONS: + --help, -h {single} + Displays this command help + --config {single} + Displays configuration + --bash-framework-config  {single} + Use alternate bash framework configuration. + --verbose, -v {single} + Info level verbose mode (alias of --display-level INFO) + -vv {single} + Debug level verbose mode (alias of --display-level DEBUG) + -vvv {single} + Trace level verbose mode (alias of --display-level TRACE) + --env-file  {list} (optional) + Load the specified env file (deprecated, please use --bash-framework-con + fig option instead) + --log-level  {single} + Set log level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --log-file  {single} + Set log file + --display-level  {single} + Set display level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --no-color {single} + Produce monochrome output. alias of --theme noColor. + --theme  {single} + Choose color theme - default-force means colors will be produced even if + command is piped. + Possible values: default, default-force, noColor + Default value: default + --version {single} + Print version information and quit. + --quiet, -q {single} + Quiet mode, doesn't display any output. + + +DESCRIPTION: + EXIT CODES: + 1: if commit does not exists + 2: if ref is not convertible to commit oid + 3: if commit is not included in given branch + +VERSION: 3.0 + +AUTHOR: [François Chastanet](https://github.com/fchastanet) + +SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-binary.yaml + +LICENSE: MIT License +Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Git/testsData/gitIsAncestorOf.help.txt b/src/_binaries/Git/testsData/gitIsAncestorOf.help.txt deleted file mode 100644 index ad1ff9b6..00000000 --- a/src/_binaries/Git/testsData/gitIsAncestorOf.help.txt +++ /dev/null @@ -1,65 +0,0 @@ -DESCRIPTION: check if commit is inside a given branch -USAGE: gitIsAncestorOf [OPTIONS] [ARGUMENTS] -USAGE: gitIsAncestorOf [--bash-framework-config ] - [--config] [--verbose|-v] [-vv] [-vvv] [--env-file ] [--no-color] - [--theme ] [--help|-h] [--version] [--quiet|-q] [--log-level ] - [--log-file ] [--display-level ] - -ARGUMENTS: - claimedBranch {single} (mandatory) - the branch in which the commit will be searched - commit {single} (mandatory) - the commit oid to check - -GLOBAL OPTIONS: - --bash-framework-config  {single} - use alternate bash framework configuration. - --config {single} - Display configuration - --verbose, -v {single} - info level verbose mode (alias of --display-level INFO) - -vv {single} - debug level verbose mode (alias of --display-level DEBUG) - -vvv {single} - trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-config - option instead) - --no-color {single} - Produce monochrome output. alias of --theme noColor. - --theme  {single} - choose color theme - default-force means colors will be produced even if com - mand is piped - Default value: default - Possible values: default|default-force|noColor - --help, -h {single} - Display this command help - --version {single} - Print version information and quit - --quiet, -q {single} - quiet mode, doesn't display any output - --log-level  {single} - Set log level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - --log-file  {single} - Set log file - --display-level  {single} - set display level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - -EXIT CODES: -1: if commit does not exists -2: if commit is not included in given branch - -VERSION: 1.0 - -AUTHOR: -[François Chastanet](https://github.com/fchastanet) - -SOURCE FILE: -https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Git/gitIsAncestorOf.sh - -LICENSE: -MIT License - -Copyright (c) 2020-now François Chastanet From d9f80f102bdbfb5238add38d642ca0233303951f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Wed, 4 Sep 2024 00:26:02 +0200 Subject: [PATCH 14/24] compiled gitIsBranch using go compiler --- bin/gitIsAncestorOf | 42 +- bin/gitIsBranch | 2006 ++++++++++------- src/BashTools/runVerboseIfNeeded.bats | 38 + src/BashTools/runVerboseIfNeeded.sh | 21 + .../gitIsAncestorOf/gitIsAncestorOf-main.sh | 21 +- src/_binaries/Git/gitIsBranch.options.tpl | 28 - src/_binaries/Git/gitIsBranch.sh | 25 - .../Git/gitIsBranch/gitIsBranch-binary.yaml | 34 + .../Git/gitIsBranch/gitIsBranch-main.sh | 11 + .../Git/gitIsBranch/gitIsBranch-options.sh | 13 + .../Git/gitIsBranch/gitIsBranch.bats | 22 + .../testsData/gitIsBranch.help.txt | 0 12 files changed, 1326 insertions(+), 935 deletions(-) create mode 100755 src/BashTools/runVerboseIfNeeded.bats create mode 100755 src/BashTools/runVerboseIfNeeded.sh delete mode 100644 src/_binaries/Git/gitIsBranch.options.tpl delete mode 100755 src/_binaries/Git/gitIsBranch.sh create mode 100644 src/_binaries/Git/gitIsBranch/gitIsBranch-binary.yaml create mode 100755 src/_binaries/Git/gitIsBranch/gitIsBranch-main.sh create mode 100755 src/_binaries/Git/gitIsBranch/gitIsBranch-options.sh create mode 100755 src/_binaries/Git/gitIsBranch/gitIsBranch.bats create mode 100644 src/_binaries/Git/gitIsBranch/testsData/gitIsBranch.help.txt diff --git a/bin/gitIsAncestorOf b/bin/gitIsAncestorOf index 6aa15e73..8e07039d 100755 --- a/bin/gitIsAncestorOf +++ b/bin/gitIsAncestorOf @@ -338,6 +338,27 @@ Bash::handlePipelineFailure() { } +# @description run command specified +# @arg $@ array:String[] the command to run +# @env optionTraceVerbose int - if 1 displays the command specified before running it +# @env optionRedirectCmdOutputs String - if set redirect command outputs to file specified +# @exitcode command's exit code +BashTools::runVerboseIfNeeded() { + # shellcheck disable=SC2154 + if [[ "${optionTraceVerbose}" = "1" ]]; then + echo >&2 "+ $*" + fi + ( + # shellcheck disable=SC2154 + if [[ -n "${optionRedirectCmdOutputs:-}" ]]; then + exec >"${optionRedirectCmdOutputs:-}" + exec 2>"${optionRedirectCmdOutputs:-}" + fi + "$@" + ) +} + + # @description check if all requirements are satisfied # to execute dbImport commands Db::checkRequirements() { @@ -1776,41 +1797,32 @@ gitIsAncestorOfCommandParse "$@" MAIN_FUNCTION_NAME="main" main() { -runVerboseIfNeeded() { - ( - # shellcheck disable=SC2154 - if [[ "${optionTraceVerbose}" = "1" ]]; then - set -x - fi - "$@" - ) -} # shellcheck disable=SC2154 -if ! runVerboseIfNeeded git cat-file -t "${refArg}" &>/dev/null; then +if ! optionRedirectCmdOutputs=/dev/null BashTools::runVerboseIfNeeded git cat-file -t "${refArg}"; then Log::displayError "Ref ${refArg} does not exists at all" exit 1 fi # shellcheck disable=SC2154 -if ! runVerboseIfNeeded git cat-file -t "${claimedBranchArg}" &>/dev/null; then +if ! optionRedirectCmdOutputs=/dev/null BashTools::runVerboseIfNeeded git cat-file -t "${claimedBranchArg}"; then Log::displayError "Ref ${claimedBranchArg} does not exists at all" exit 1 fi declare refCommit claimedBranchCommit -refCommit="$(git rev-parse "${refArg}")" || { +refCommit="$(BashTools::runVerboseIfNeeded git rev-parse "${refArg}")" || { Log::displayError "Ref ${refArg} is not convertible to commit sha" exit 1 } -claimedBranchCommit="$(git rev-parse "${claimedBranchArg}")" || { +claimedBranchCommit="$(BashTools::runVerboseIfNeeded git rev-parse "${claimedBranchArg}")" || { Log::displayError "Ref ${claimedBranchArg} is not convertible to commit sha" exit 1 } declare revParse mergeBase # shellcheck disable=SC2154 -mergeBase="$(runVerboseIfNeeded git merge-base "${refCommit}" "${claimedBranchCommit}")" -revParse="$(runVerboseIfNeeded git rev-parse --verify "${refCommit}")" +mergeBase="$(BashTools::runVerboseIfNeeded git merge-base "${refCommit}" "${claimedBranchCommit}")" +revParse="$(BashTools::runVerboseIfNeeded git rev-parse --verify "${refCommit}")" if [[ -z "${mergeBase}" || "${mergeBase}" != "${revParse}" ]]; then Log::displayError "Commit ${refArg} is not an ancestor of branch ${claimedBranchArg}" diff --git a/bin/gitIsBranch b/bin/gitIsBranch index 6a7c3c20..c88fa789 100755 --- a/bin/gitIsBranch +++ b/bin/gitIsBranch @@ -1,13 +1,12 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FACADE FROM https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Git/gitIsBranch.sh +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Git/gitIsBranch/gitIsBranch-binary.yaml # DO NOT EDIT IT # @generated ############################################################################### # shellcheck disable=SC2288,SC2034 -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/gitIsBranch -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE + + # ensure that no user aliases could interfere with # commands used in this script @@ -62,13 +61,6 @@ interruptManagement() { kill -s INT "$$" } trap interruptManagement INT -SCRIPT_NAME=${0##*/} -REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" -if [[ -n "${EMBED_CURRENT_DIR}" ]]; then - CURRENT_DIR="${EMBED_CURRENT_DIR}" -else - CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" -fi ################################################ # Temp dir management @@ -102,6 +94,20 @@ cleanOnExit() { } trap cleanOnExit EXIT HUP QUIT ABRT TERM + +SCRIPT_NAME=${0##*/} +REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" +if [[ -n "${EMBED_CURRENT_DIR}" ]]; then + CURRENT_DIR="${EMBED_CURRENT_DIR}" +else + CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" +fi +FRAMEWORK_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" +FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" +FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + # @description Log namespace provides 2 kind of functions # - Log::display* allows to display given message with # given display level @@ -132,247 +138,6 @@ export __VERBOSE_LEVEL_DEBUG=2 # @description verbose level info export __VERBOSE_LEVEL_TRACE=3 -# @description Display message using info color (bg light blue/fg white) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayInfo() { - local type="${2:-INFO}" - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logInfo "$1" "${type}" -} - -# @description Display message using debug color (gray) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayDebug() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then - Log::computeDuration - echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logDebug "$1" -} - -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logWarning "$1" -} - -# @description Display message using error color (red) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayError() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then - Log::computeDuration - echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logError "$1" -} - -# @description load colors theme constants -# @warning if tty not opened, noColor theme will be chosen -# @arg $1 theme:String the theme to use (default, noColor) -# @arg $@ args:String[] -# @set __ERROR_COLOR String indicate error status -# @set __INFO_COLOR String indicate info status -# @set __SUCCESS_COLOR String indicate success status -# @set __WARNING_COLOR String indicate warning status -# @set __SKIPPED_COLOR String indicate skipped status -# @set __DEBUG_COLOR String indicate debug status -# @set __HELP_COLOR String indicate help status -# @set __TEST_COLOR String not used -# @set __TEST_ERROR_COLOR String not used -# @set __HELP_TITLE_COLOR String used to display help title in help strings -# @set __HELP_OPTION_COLOR String used to display highlight options in help strings -# -# @set __RESET_COLOR String reset default color -# -# @set __HELP_EXAMPLE String to remove -# @set __HELP_TITLE String to remove -# @set __HELP_NORMAL String to remove -# shellcheck disable=SC2034 -UI::theme() { - local theme="${1-default}" - if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then - theme="noColor" - fi - case "${theme}" in - default | default-force) - theme="default" - ;; - noColor) ;; - *) - Log::fatal "invalid theme provided" - ;; - esac - if [[ "${theme}" = "default" ]]; then - BASH_FRAMEWORK_THEME="default" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='\e[31m' # Red - __INFO_COLOR='\e[44m' # white on lightBlue - __SUCCESS_COLOR='\e[32m' # Green - __WARNING_COLOR='\e[33m' # Yellow - __SKIPPED_COLOR='\e[33m' # Yellow - __DEBUG_COLOR='\e[37m' # Gray - __HELP_COLOR='\e[7;49;33m' # Black on Gold - __TEST_COLOR='\e[100m' # Light magenta - __TEST_ERROR_COLOR='\e[41m' # white on red - __HELP_TITLE_COLOR="\e[1;37m" # Bold - __HELP_OPTION_COLOR="\e[1;34m" # Blue - # Internal: reset color - __RESET_COLOR='\e[0m' # Reset Color - # shellcheck disable=SC2155,SC2034 - __HELP_EXAMPLE="$(echo -e "\e[2;97m")" - # shellcheck disable=SC2155,SC2034 - __HELP_TITLE="$(echo -e "\e[1;37m")" - # shellcheck disable=SC2155,SC2034 - __HELP_NORMAL="$(echo -e "\033[0m")" - else - BASH_FRAMEWORK_THEME="noColor" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='' - __INFO_COLOR='' - __SUCCESS_COLOR='' - __WARNING_COLOR='' - __SKIPPED_COLOR='' - __DEBUG_COLOR='' - __HELP_COLOR='' - __TEST_COLOR='' - __TEST_ERROR_COLOR='' - __HELP_TITLE_COLOR='' - __HELP_OPTION_COLOR='' - # Internal: reset color - __RESET_COLOR='' - __HELP_EXAMPLE='' - __HELP_TITLE='' - __HELP_NORMAL='' - fi -} - -# @description draw a line with the character passed in parameter repeated depending on terminal width -# @arg $1 character:String character to use as separator (default value #) -UI::drawLine() { - local character="${1:-#}" - local -i width=${COLUMNS:-0} - if ((width == 0)) && [[ -t 1 ]]; then - width=$(tput cols) - fi - if ((width == 0)); then - width=80 - fi - printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") - echo -} - -# @description Display message using error color (red) and exit immediately with error status 1 -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::fatal() { - Log::computeDuration - echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - Log::logFatal "$1" - exit 1 -} - -# @description create a temp file using default TMPDIR variable -# initialized in _includes/_commonHeader.sh -# @env TMPDIR String (default value /tmp) -# @arg $1 templateName:String template name to use(optional) -Framework::createTempFile() { - mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" -} - -# @description ensure env files are loaded -# @arg $@ list of default files to load at the end -# @exitcode 1 if one of env files fails to load -# @stderr diagnostics information is displayed -# shellcheck disable=SC2120 -Env::requireLoad() { - local -a defaultFiles=("$@") - # get list of possible config files - local -a configFiles=() - if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then - # BASH_FRAMEWORK_ENV_FILES is an array - configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") - fi - local localFrameworkConfigFile - localFrameworkConfigFile="$(pwd)/.framework-config" - if [[ -f "${localFrameworkConfigFile}" ]]; then - configFiles+=("${localFrameworkConfigFile}") - fi - if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then - configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") - fi - configFiles+=("${optionEnvFiles[@]}") - configFiles+=("${defaultFiles[@]}") - - for file in "${configFiles[@]}"; do - # shellcheck source=/.framework-config - CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { - Log::displayError "while loading config file: ${file}" - return 1 - } - done -} - -# @description activate or not Log::display* and Log::log* functions -# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL -# environment variables loaded by Env::requireLoad -# try to create log file and rotate it if necessary -# @noargs -# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable -# @env BASH_FRAMEWORK_DISPLAY_LEVEL int -# @env BASH_FRAMEWORK_LOG_LEVEL int -# @env BASH_FRAMEWORK_LOG_FILE String -# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 -# @exitcode 0 always successful -# @stderr diagnostics information about log file is displayed -# @require Env::requireLoad -# @require UI::requireTheme -Log::requireLoad() { - if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - export BASH_FRAMEWORK_LOG_LEVEL - fi - - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then - if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 - fi - elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - fi - - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - # will always be created even if not in info level - Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" - if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then - Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" - fi - fi -} # @description concatenate each element of an array with a separator # but wrapping text when line length is more than provided argument @@ -502,124 +267,161 @@ Array::wrap2() { ) | sed -E -e 's/[[:blank:]]+$//' } -bashToolsDefaultConfigTemplate="${bashToolsDefaultConfigTemplate:-$( - cat <<'EOF' -# shellcheck disable=SC2034 -# Default settings -# you can override these settings by creating ${HOME}/.bash-tools/.env file - -### -### DISPLAY Level -### minimum level of the messages that will be displayed on screen -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} - -### -### DISPLAY duration -### 0: no duration is displayed on the messages -### 1: duration between previous message and current is displayed -### with the message -### -DISPLAY_DURATION=${DISPLAY_DURATION:0} - -### -### Log to file -### -### all log messages will be redirected to log file specified -### this same path will be used inside and outside of the container -### -BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} - -### -### LOG Level -### minimum level of the messages that will be logged into LOG_FILE -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} - -# absolute directory containing db import sql dumps -DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} - -# garbage collect all files for which modification is greater than eg: 30 days (+30) -# each time an existing file is used by dbImport/dbImportTable -# the file modification time is set to now -DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} - -# absolute directory containing dbScripts used by dbScriptAllDatabases -SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} - -# ----------------------------------------------------- -# AWS Parameters -# ----------------------------------------------------- -S3_BASE_URL=${S3_BASE_URL:-} - -# ----------------------------------------------------- -# Postman Parameters -# ----------------------------------------------------- -POSTMAN_API_KEY= -EOF -)}" - -# @description loads ~/.bash-tools/.env if available -# if not creates it from a default template -# else check if new options need to be added -BashTools::Conf::requireLoad() { - local envFile="${HOME}/.bash-tools/.env" - if [[ ! -f "${envFile}" ]]; then - mkdir -p "${HOME}/.bash-tools" - ( - echo "#!/usr/bin/env bash" - echo "${bashToolsDefaultConfigTemplate}" - ) >"${envFile}" - Log::displayInfo "Configuration file '${envFile}' created" - else - if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then - ( - echo '# -----------------------------------------------------' - echo '# Postman Parameters' - echo '# -----------------------------------------------------' - echo 'POSTMAN_API_KEY=' - ) >>"${envFile}" +# @description check if command specified exists or return 1 +# with error and message if not +# +# @arg $1 commandName:String on which existence must be checked +# @arg $2 helpIfNotExists:String a help command to display if the command does not exist +# +# @exitcode 1 if the command specified does not exist +# @stderr diagnostic information + help if second argument is provided +Assert::commandExists() { + local commandName="$1" + local helpIfNotExists="$2" + + "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { + Log::displayError "${commandName} is not installed, please install it" + if [[ -n "${helpIfNotExists}" ]]; then + Log::displayInfo "${helpIfNotExists}" fi - fi - # shellcheck source=/conf/.env - source "${envFile}" || { - Log::displayError "impossible to load '${envFile}'" - exit 1 + return 1 } + return 0 } -# @description ensure COMMAND_BIN_DIR env var is set -# and PATH correctly prepared + +# @description check if tty (interactive mode) is active # @noargs -# @set COMMAND_BIN_DIR string the directory where to find this command -# @set PATH string add directory where to find this command binary -Compiler::Facade::requireCommandBinDir() { - COMMAND_BIN_DIR="${CURRENT_DIR}" - Env::pathPrepend "${COMMAND_BIN_DIR}" +# @exitcode 1 if tty not active +# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive +# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive +Assert::tty() { + if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then + return 1 + fi + if [[ "${INTERACTIVE:-0}" = "1" ]]; then + return 0 + fi + tty -s } -# @description ensure running user is not root -# @exitcode 1 if current user is root + +# @description ignore exit code 141 from simple command pipes +# @example use with: +# local resultingStatus=0 +# local -a originalPipeStatus=() +# cmd1 | cmd2 || Bash::handlePipelineFailure resultingStatus originalPipeStatus || true +# [[ "${resultingStatus}" = "0" ]] +# @arg $1 resultingStatusCode:&int (passed by reference) (optional) resulting status code +# @arg $2 originalStatus:int[] (passed by reference) (optional) copy of original PIPESTATUS array +# @env PIPESTATUS assuming that this function is called like in the example provided +# @see https://unix.stackexchange.com/a/709880/582856 +Bash::handlePipelineFailure() { + local -a pipeStatusBackup=("${PIPESTATUS[@]}") + local -n handlePipelineFailure_resultingStatusCode=$1 + local -n handlePipelineFailure_originalStatus=$2 + # shellcheck disable=SC2034 + handlePipelineFailure_originalStatus=("${pipeStatusBackup[@]}") + handlePipelineFailure_resultingStatusCode=0 + local statusCode + for statusCode in "${pipeStatusBackup[@]}"; do + if ((statusCode == 141)); then + return 0 + elif ((statusCode > 0)); then + # shellcheck disable=SC2034 + handlePipelineFailure_resultingStatusCode="${statusCode}" + break + fi + done + return "${handlePipelineFailure_resultingStatusCode}" +} + + +# @description run command specified +# @arg $@ array:String[] the command to run +# @env optionTraceVerbose int - if 1 displays the command specified before running it +# @env optionRedirectCmdOutputs String - if set redirect command outputs to file specified +# @exitcode command's exit code +BashTools::runVerboseIfNeeded() { + # shellcheck disable=SC2154 + if [[ "${optionTraceVerbose}" = "1" ]]; then + echo >&2 "+ $*" + fi + ( + # shellcheck disable=SC2154 + if [[ -n "${optionRedirectCmdOutputs:-}" ]]; then + exec >"${optionRedirectCmdOutputs:-}" + exec 2>"${optionRedirectCmdOutputs:-}" + fi + "$@" + ) +} + + +# @description check if all requirements are satisfied +# to execute dbImport commands +Db::checkRequirements() { + if [[ "${SKIP_REQUIREMENTS_CHECKS:-0}" = "1" ]]; then + return 0 + fi + local -i failures=0 + echo + Assert::commandExists mysql "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqldump "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists pv "sudo apt-get install -y pv" || ((++failures)) + Assert::commandExists gawk "sudo apt-get install -y gawk" || ((++failures)) + Assert::commandExists awk "sudo apt-get install -y gawk" || ((++failures)) + Version::checkMinimal "gawk" "--version" "5.0.1" || ((++failures)) + return "${failures}" +} + + +# @description ensure env files are loaded +# @arg $@ list of default files to load at the end +# @exitcode 1 if one of env files fails to load # @stderr diagnostics information is displayed -Linux::requireExecutedAsUser() { - if [[ "$(id -u)" = "0" ]]; then - Log::fatal "this script should be executed as normal user" +# shellcheck disable=SC2120 +Env::requireLoad() { + export REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 + + local -a defaultFiles=("$@") + # get list of possible config files + local -a configFiles=() + if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then + # BASH_FRAMEWORK_ENV_FILES is an array + configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") + fi + local localFrameworkConfigFile + localFrameworkConfigFile="$(pwd)/.framework-config" + if [[ -f "${localFrameworkConfigFile}" ]]; then + configFiles+=("${localFrameworkConfigFile}") fi + if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then + configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") + fi + configFiles+=("${optionEnvFiles[@]}") + configFiles+=("${defaultFiles[@]}") + + for file in "${configFiles[@]}"; do + # shellcheck source=/.framework-config + CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { + Log::displayError "while loading config file: ${file}" + return 1 + } + done +} + + +# @description create a temp file using default TMPDIR variable +# @env TMPDIR String (default value /tmp) +# @arg $1 templateName:String template name to use(optional) +Framework::createTempFile() { + mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" } + declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" @@ -658,14 +460,72 @@ Log::computeDuration() { fi } -# @description log message to file + +# @description Display message using debug color (gray) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayDebug() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then + Log::computeDuration + echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logDebug "$1" +} + + +# @description Display message using error color (red) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayError() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then + Log::computeDuration + echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logError "$1" +} + + +# @description Display message using info color (bg light blue/fg white) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayInfo() { + local type="${2:-INFO}" + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logInfo "$1" "${type}" +} + + +# @description Display message using warning color (yellow) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayWarning() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then + Log::computeDuration + echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logWarning "$1" +} + + +# @description Display message using error color (red) and exit immediately with error status 1 # @arg $1 message:String the message to display -Log::logInfo() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then - Log::logMessage "${2:-INFO}" "$1" - fi +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::fatal() { + Log::computeDuration + echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + Log::logFatal "$1" + exit 1 } + # @description log message to file # @arg $1 message:String the message to display Log::logDebug() { @@ -674,13 +534,6 @@ Log::logDebug() { fi } -# @description log message to file -# @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" - fi -} # @description log message to file # @arg $1 message:String the message to display @@ -690,20 +543,6 @@ Log::logError() { fi } -# @description check if tty (interactive mode) is active -# @noargs -# @exitcode 1 if tty not active -# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive -# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive -Assert::tty() { - if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then - return 1 - fi - if [[ "${INTERACTIVE:-0}" = "1" ]]; then - return 0 - fi - tty -s -} # @description log message to file # @arg $1 message:String the message to display @@ -711,6 +550,16 @@ Log::logFatal() { Log::logMessage "${2:-FATAL}" "$1" } + +# @description log message to file +# @arg $1 message:String the message to display +Log::logInfo() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then + Log::logMessage "${2:-INFO}" "$1" + fi +} + + # @description Internal: common log message # @example text # [date]|[levelMsg]|message @@ -724,9 +573,18 @@ Log::logFatal() { # @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty # @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages # @stderr diagnostics information is displayed -# @require Env::requireLoad -# @require Log::requireLoad Log::logMessage() { + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Log::requireLoad has not been loaded" + exit 1 + fi + local levelMsg="$1" local msg="$2" local date @@ -738,6 +596,74 @@ Log::logMessage() { fi } + +# @description log message to file +# @arg $1 message:String the message to display +Log::logWarning() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then + Log::logMessage "${2:-WARNING}" "$1" + fi +} + + +# @description activate or not Log::display* and Log::log* functions +# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL +# environment variables loaded by Env::requireLoad +# try to create log file and rotate it if necessary +# @noargs +# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable +# @env BASH_FRAMEWORK_DISPLAY_LEVEL int +# @env BASH_FRAMEWORK_LOG_LEVEL int +# @env BASH_FRAMEWORK_LOG_FILE String +# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 +# @exitcode 0 always successful +# @stderr diagnostics information about log file is displayed +Log::requireLoad() { + export REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement UI::requireTheme has not been loaded" + exit 1 + fi + + if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + export BASH_FRAMEWORK_LOG_LEVEL + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then + if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 + fi + elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + # will always be created even if not in info level + Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" + if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then + Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + fi + fi +} + + # @description To be called before logging in the log file # @arg $1 file:string log file name # @arg $2 maxLogFilesCount:int maximum number of log files @@ -760,69 +686,222 @@ Log::rotate() { fi } -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" - fi - done + +# @description draw a line with the character passed in parameter repeated depending on terminal width +# @arg $1 character:String character to use as separator (default value #) +UI::drawLine() { + local character="${1:-#}" + local -i width=${COLUMNS:-0} + if ((width == 0)) && [[ -t 1 ]]; then + width=$(tput cols) + fi + if ((width == 0)); then + width=80 + fi + printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") + echo } + # @description load color theme # @noargs # @env BASH_FRAMEWORK_THEME String theme to use # @env LOAD_THEME int 0 to avoid loading theme # @exitcode 0 always successful UI::requireTheme() { + export REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + if [[ "${LOAD_THEME:-1}" = "1" ]]; then UI::theme "${BASH_FRAMEWORK_THEME-default}" fi } -# FUNCTIONS - -facade_main_gitIsBranchsh() { -BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" -if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then - FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" -else - # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR - FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" -fi -FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" -FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" -# @require BashTools::Conf::requireLoad -if [[ -f "${HOME}/.bash-tools/.env" ]]; then - export BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") -fi -# REQUIRES -Env::requireLoad -UI::requireTheme -Log::requireLoad -BashTools::Conf::requireLoad -Compiler::Facade::requireCommandBinDir -Linux::requireExecutedAsUser - -# @require Compiler::Facade::requireCommandBinDir +# @description load colors theme constants +# @warning if tty not opened, noColor theme will be chosen +# @arg $1 theme:String the theme to use (default, noColor) +# @arg $@ args:String[] +# @set __ERROR_COLOR String indicate error status +# @set __INFO_COLOR String indicate info status +# @set __SUCCESS_COLOR String indicate success status +# @set __WARNING_COLOR String indicate warning status +# @set __SKIPPED_COLOR String indicate skipped status +# @set __DEBUG_COLOR String indicate debug status +# @set __HELP_COLOR String indicate help status +# @set __TEST_COLOR String not used +# @set __TEST_ERROR_COLOR String not used +# @set __HELP_TITLE_COLOR String used to display help title in help strings +# @set __HELP_OPTION_COLOR String used to display highlight options in help strings +# +# @set __RESET_COLOR String reset default color +# +# @set __HELP_EXAMPLE String to remove +# @set __HELP_TITLE String to remove +# @set __HELP_NORMAL String to remove # shellcheck disable=SC2034 +UI::theme() { + local theme="${1-default}" + if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then + theme="noColor" + fi + case "${theme}" in + default | default-force) + theme="default" + ;; + noColor) ;; + *) + Log::fatal "invalid theme provided" + ;; + esac + if [[ "${theme}" = "default" ]]; then + BASH_FRAMEWORK_THEME="default" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='\e[31m' # Red + __INFO_COLOR='\e[44m' # white on lightBlue + __SUCCESS_COLOR='\e[32m' # Green + __WARNING_COLOR='\e[33m' # Yellow + __SKIPPED_COLOR='\e[33m' # Yellow + __DEBUG_COLOR='\e[37m' # Gray + __HELP_COLOR='\e[7;49;33m' # Black on Gold + __TEST_COLOR='\e[100m' # Light magenta + __TEST_ERROR_COLOR='\e[41m' # white on red + __HELP_TITLE_COLOR="\e[1;37m" # Bold + __HELP_OPTION_COLOR="\e[1;34m" # Blue + # Internal: reset color + __RESET_COLOR='\e[0m' # Reset Color + # shellcheck disable=SC2155,SC2034 + __HELP_EXAMPLE="$(echo -e "\e[2;97m")" + # shellcheck disable=SC2155,SC2034 + __HELP_TITLE="$(echo -e "\e[1;37m")" + # shellcheck disable=SC2155,SC2034 + __HELP_NORMAL="$(echo -e "\033[0m")" + else + BASH_FRAMEWORK_THEME="noColor" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='' + __INFO_COLOR='' + __SUCCESS_COLOR='' + __WARNING_COLOR='' + __SKIPPED_COLOR='' + __DEBUG_COLOR='' + __HELP_COLOR='' + __TEST_COLOR='' + __TEST_ERROR_COLOR='' + __HELP_TITLE_COLOR='' + __HELP_OPTION_COLOR='' + # Internal: reset color + __RESET_COLOR='' + __HELP_EXAMPLE='' + __HELP_TITLE='' + __HELP_NORMAL='' + fi +} + + +# @description Check that command version is greater than expected minimal version +# display warning if command version greater than expected minimal version +# display error if command version less than expected minimal version and exit 1 +# @arg $1 commandName:String command path +# @arg $2 argVersion:String command line parameters to launch to get command version +# @arg $3 minimalVersion:String expected minimal command version +# @arg $4 parseVersionCallback:Function +# @arg $5 help:String optional help message to display if command does not exist +# @exitcode 0 if command version greater or equal to expected minimal version +# @exitcode 1 if command version less than expected minimal version +# @exitcode 2 if command does not exist +Version::checkMinimal() { + local commandName="$1" + local argVersion="$2" + local minimalVersion="$3" + local parseVersionCallback=${4:-Version::parse} + local help="${5:-}" + + Assert::commandExists "${commandName}" "${help}" || return 2 + + # shellcheck disable=SC2034 + local status=0 + # shellcheck disable=SC2034 + local -a pipeStatus=() + local version + version="$("${commandName}" "${argVersion}" 2>&1 | ${parseVersionCallback} || Bash::handlePipelineFailure status pipeStatus)" + + Log::displayDebug "check ${commandName} version ${version} against minimal ${minimalVersion}" + + Version::compare "${version}" "${minimalVersion}" || { + local result=$? + if [[ "${result}" = "1" ]]; then + Log::displayInfo "${commandName} version is ${version} greater than ${minimalVersion}" + elif [[ "${result}" = "2" ]]; then + Log::displayError "${commandName} minimal version is ${minimalVersion}, your version is ${version}" + return 1 + fi + return 0 + } + +} + + +# @description compare 2 version numbers +# @arg $1 version1:String version 1 +# @arg $2 version2:String version 2 +# @exitcode 0 if equal +# @exitcode 1 if version1 > version2 +# @exitcode 2 else +Version::compare() { + if [[ "$1" = "$2" ]]; then + return 0 + fi + local IFS=. + # shellcheck disable=2206 + local i ver1=($1) ver2=($2) + # fill empty fields in ver1 with zeros + for ((i = ${#ver1[@]}; i < ${#ver2[@]}; i++)); do + ver1[i]=0 + done + for ((i = 0; i < ${#ver1[@]}; i++)); do + if [[ -z "${ver2[i]+unset}" ]] || [[ -z ${ver2[i]} ]]; then + # fill empty fields in ver2 with zeros + ver2[i]=0 + fi + if ((10#${ver1[i]} > 10#${ver2[i]})); then + return 1 + fi + if ((10#${ver1[i]} < 10#${ver2[i]})); then + return 2 + fi + done + return 0 +} + + +# @description filter to keep only version number from a string +# @arg $@ files:String[] the files to filter +# @exitcode * if one of the filter command fails +# @stdin you can use stdin as alternative to files argument +# @stdout the filtered content +# shellcheck disable=SC2120 +Version::parse() { + # match anything, print(p), exit on first match(Q) + sed -En \ + -e 's/\x1b\[[0-9;]*[mGKHF]//g' \ + -e 's/[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/' \ + -e '//{p;Q}' \ + "$@" +} +# FUNCTIONS -declare copyrightBeginYear="2020" -declare branchNameArg="" declare -a BASH_FRAMEWORK_ARGV_FILTERED=() +beforeParseCallback() { + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + copyrightCallback() { - if [[ -z "${copyrightBeginYear}" ]]; then - copyrightBeginYear="$(date +%Y)" - fi - echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" + # + echo "Copyright (c) 2020-now François Chastanet" } # shellcheck disable=SC2317 # if function is overridden @@ -854,13 +933,14 @@ updateArgListQuietCallback() { :; } # shellcheck disable=SC2317 # if function is overridden optionHelpCallback() { - gitIsBranchCommand help + Log::displayError "optionHelpCallback needs to be overridden" exit 0 } # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { - echo "${SCRIPT_NAME} version 1.0" + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version 3.0" exit 0 } @@ -878,21 +958,22 @@ optionEnvFileCallback() { optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_INFO} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionDebugVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_DEBUG} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionTraceVerboseCallback() { + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vvv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_TRACE} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } getLevel() { @@ -916,6 +997,7 @@ getLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -937,6 +1019,7 @@ getVerboseLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -947,7 +1030,7 @@ optionDisplayLevelCallback() { logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -956,19 +1039,20 @@ optionLogLevelCallback() { local logLevel verboseLevel logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionLogFileCallback() { local logFile="$2" - echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionQuietCallback() { - echo "BASH_FRAMEWORK_QUIET_MODE=1" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_QUIET_MODE=1" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -999,6 +1083,7 @@ optionBashFrameworkConfigCallback() { defaultFrameworkConfig="$( cat <<'EOF' + # copied from src/_includes/.framework-config.default # shellcheck disable=SC2034 @@ -1016,7 +1101,7 @@ NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-conf # describe the files that are allowed to not have an associated bats file BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" # describe the files that are allowed to not have a function matching the filename -FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|\.tpl$|/testsData/|^manualTests/|\.bats$}" +FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|/testsData/|^manualTests/|\.bats$}" # Source directories if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then FRAMEWORK_SRC_DIRS=( @@ -1032,6 +1117,7 @@ BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" + EOF )" @@ -1040,7 +1126,7 @@ overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" commandOptionParseFinished() { # load default template framework config defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" - echo "${defaultFrameworkConfig}" > "${defaultEnvFile}" + echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") if [[ -f "${envFile}" ]]; then files+=("${envFile}") @@ -1058,434 +1144,650 @@ commandOptionParseFinished() { fi } -gitIsBranchCommand() { - local options_parse_cmd="$1" - shift || true - if [[ "${options_parse_cmd}" = "parse" ]]; then - local -i options_parse_optionParsedCountOptionBashFrameworkConfig - ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true - optionConfig="0" - local -i options_parse_optionParsedCountOptionConfig - ((options_parse_optionParsedCountOptionConfig = 0)) || true - optionInfoVerbose="0" - local -i options_parse_optionParsedCountOptionInfoVerbose - ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true - optionDebugVerbose="0" - local -i options_parse_optionParsedCountOptionDebugVerbose - ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true - optionTraceVerbose="0" - local -i options_parse_optionParsedCountOptionTraceVerbose - ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionNoColor="0" - local -i options_parse_optionParsedCountOptionNoColor - ((options_parse_optionParsedCountOptionNoColor = 0)) || true - optionTheme="default" - local -i options_parse_optionParsedCountOptionTheme - ((options_parse_optionParsedCountOptionTheme = 0)) || true - optionHelp="0" - local -i options_parse_optionParsedCountOptionHelp - ((options_parse_optionParsedCountOptionHelp = 0)) || true - optionVersion="0" - local -i options_parse_optionParsedCountOptionVersion - ((options_parse_optionParsedCountOptionVersion = 0)) || true - optionQuiet="0" - local -i options_parse_optionParsedCountOptionQuiet - ((options_parse_optionParsedCountOptionQuiet = 0)) || true - local -i options_parse_optionParsedCountOptionLogLevel - ((options_parse_optionParsedCountOptionLogLevel = 0)) || true - local -i options_parse_optionParsedCountOptionLogFile - ((options_parse_optionParsedCountOptionLogFile = 0)) || true - local -i options_parse_optionParsedCountOptionDisplayLevel - ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true - local -i options_parse_argParsedCountBranchNameArg - ((options_parse_argParsedCountBranchNameArg = 0)) || true - # shellcheck disable=SC2034 - local -i options_parse_parsedArgIndex=0 - while (($# > 0)); do - local options_parse_arg="$1" - local argOptDefaultBehavior=0 - case "${options_parse_arg}" in - # Option 1/14 - # Option optionBashFrameworkConfig --bash-framework-config variableType String min 0 max 1 authorizedValues '' regexp '' - --bash-framework-config) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) - # shellcheck disable=SC2034 - optionBashFrameworkConfig="$1" - optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" - ;; - # Option 2/14 - # Option optionConfig --config variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --config) - # shellcheck disable=SC2034 - optionConfig="1" - if ((options_parse_optionParsedCountOptionConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionConfig)) - ;; - # Option 3/14 - # Option optionInfoVerbose --verbose|-v variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --verbose | -v) - # shellcheck disable=SC2034 - optionInfoVerbose="1" - if ((options_parse_optionParsedCountOptionInfoVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionInfoVerbose)) - optionInfoVerboseCallback "${options_parse_arg}" - updateArgListInfoVerboseCallback "${options_parse_arg}" - ;; - # Option 4/14 - # Option optionDebugVerbose -vv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vv) - # shellcheck disable=SC2034 - optionDebugVerbose="1" - if ((options_parse_optionParsedCountOptionDebugVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDebugVerbose)) - optionDebugVerboseCallback "${options_parse_arg}" - updateArgListDebugVerboseCallback "${options_parse_arg}" - ;; - # Option 5/14 - # Option optionTraceVerbose -vvv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vvv) - # shellcheck disable=SC2034 - optionTraceVerbose="1" - if ((options_parse_optionParsedCountOptionTraceVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTraceVerbose)) - optionTraceVerboseCallback "${options_parse_arg}" - updateArgListTraceVerboseCallback "${options_parse_arg}" - ;; - # Option 6/14 - # Option optionEnvFiles --env-file variableType StringArray min 0 max -1 authorizedValues '' regexp '' - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - ;; - # Option 7/14 - # Option optionNoColor --no-color variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --no-color) - # shellcheck disable=SC2034 - optionNoColor="1" - if ((options_parse_optionParsedCountOptionNoColor >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionNoColor)) - optionNoColorCallback "${options_parse_arg}" - updateArgListNoColorCallback "${options_parse_arg}" - ;; - # Option 8/14 - # Option optionTheme --theme variableType String min 0 max 1 authorizedValues 'default|default-force|noColor' regexp '' - --theme) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default|default-force|noColor)" - return 1 - fi - if ((options_parse_optionParsedCountOptionTheme >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTheme)) - # shellcheck disable=SC2034 - optionTheme="$1" - optionThemeCallback "${options_parse_arg}" "${optionTheme}" - updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" - ;; - # Option 9/14 - # Option optionHelp --help|-h variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --help | -h) - # shellcheck disable=SC2034 - optionHelp="1" - if ((options_parse_optionParsedCountOptionHelp >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionHelp)) - optionHelpCallback "${options_parse_arg}" - ;; - # Option 10/14 - # Option optionVersion --version variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --version) - # shellcheck disable=SC2034 - optionVersion="1" - if ((options_parse_optionParsedCountOptionVersion >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionVersion)) - optionVersionCallback "${options_parse_arg}" - ;; - # Option 11/14 - # Option optionQuiet --quiet|-q variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --quiet | -q) - # shellcheck disable=SC2034 - optionQuiet="1" - if ((options_parse_optionParsedCountOptionQuiet >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionQuiet)) - optionQuietCallback "${options_parse_arg}" - updateArgListQuietCallback "${options_parse_arg}" - ;; - # Option 12/14 - # Option optionLogLevel --log-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --log-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogLevel)) - # shellcheck disable=SC2034 - optionLogLevel="$1" - optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - ;; - # Option 13/14 - # Option optionLogFile --log-file variableType String min 0 max 1 authorizedValues '' regexp '' - --log-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogFile >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogFile)) - # shellcheck disable=SC2034 - optionLogFile="$1" - optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" - updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" - ;; - # Option 14/14 - # Option optionDisplayLevel --display-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --display-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionDisplayLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + +longDescriptionFunction() { + echo -e " ${__HELP_TITLE}EXIT CODES:${__HELP_NORMAL}" + echo -e " ${__HELP_OPTION_COLOR}1${__HELP_NORMAL}: if commit does not exists" + echo -e " ${__HELP_OPTION_COLOR}2${__HELP_NORMAL}: if ref is not convertible to commit oid" + echo -e " ${__HELP_OPTION_COLOR}3${__HELP_NORMAL}: if commit is not included in given branch" +} + +optionHelpCallback() { + gitIsBranchCommandHelp + exit 0 +} + + + +optionVersionCallback() { + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version 3.0" + Db::checkRequirements + exit 0 +} + + +# ------------------------------------------ +# Command gitIsBranchCommand +# ------------------------------------------ + +# options variables initialization +declare optionHelp="0" +declare optionConfig="0" +declare optionBashFrameworkConfig="" +declare optionInfoVerbose="0" +declare optionDebugVerbose="0" +declare optionTraceVerbose="0" +declare -a optionEnvFiles=() +declare optionLogLevel="" +declare optionLogFile="" +declare optionDisplayLevel="" +declare optionNoColor="0" +declare optionTheme="default" +declare optionVersion="0" +declare optionQuiet="0" +# arguments variables initialization +declare branchNameArg="" +# @description parse command options and arguments for gitIsBranchCommand +gitIsBranchCommandParse() { + Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" + Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" + optionHelp="0" + local -i options_parse_optionParsedCountOptionHelp + ((options_parse_optionParsedCountOptionHelp = 0)) || true + optionConfig="0" + local -i options_parse_optionParsedCountOptionConfig + ((options_parse_optionParsedCountOptionConfig = 0)) || true + optionBashFrameworkConfig="" + local -i options_parse_optionParsedCountOptionBashFrameworkConfig + ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true + optionInfoVerbose="0" + local -i options_parse_optionParsedCountOptionInfoVerbose + ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true + optionDebugVerbose="0" + local -i options_parse_optionParsedCountOptionDebugVerbose + ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true + optionTraceVerbose="0" + local -i options_parse_optionParsedCountOptionTraceVerbose + ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true + + optionLogLevel="" + local -i options_parse_optionParsedCountOptionLogLevel + ((options_parse_optionParsedCountOptionLogLevel = 0)) || true + optionLogFile="" + local -i options_parse_optionParsedCountOptionLogFile + ((options_parse_optionParsedCountOptionLogFile = 0)) || true + optionDisplayLevel="" + local -i options_parse_optionParsedCountOptionDisplayLevel + ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true + optionNoColor="0" + local -i options_parse_optionParsedCountOptionNoColor + ((options_parse_optionParsedCountOptionNoColor = 0)) || true + optionTheme="default" + local -i options_parse_optionParsedCountOptionTheme + ((options_parse_optionParsedCountOptionTheme = 0)) || true + optionVersion="0" + local -i options_parse_optionParsedCountOptionVersion + ((options_parse_optionParsedCountOptionVersion = 0)) || true + optionQuiet="0" + local -i options_parse_optionParsedCountOptionQuiet + ((options_parse_optionParsedCountOptionQuiet = 0)) || true + + branchNameArg="" + local -i options_parse_argParsedCountBranchNameArg + ((options_parse_argParsedCountBranchNameArg = 0)) || true + + + # shellcheck disable=SC2034 + local -i options_parse_parsedArgIndex=0 + while (($# > 0)); do + local options_parse_arg="$1" + local argOptDefaultBehavior=0 + case "${options_parse_arg}" in + # Option 1/14 + # optionHelp alts --help|-h + # type: Boolean min 0 max 1 + --help | -h) + # shellcheck disable=SC2034 + optionHelp="1" + + if ((options_parse_optionParsedCountOptionHelp >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionHelp)) + optionHelpCallback "${options_parse_arg}" "${optionHelp}" + + ;; + + # Option 2/14 + # optionConfig alts --config + # type: Boolean min 0 max 1 + --config) + # shellcheck disable=SC2034 + optionConfig="1" + + if ((options_parse_optionParsedCountOptionConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionConfig)) + ;; + + # Option 3/14 + # optionBashFrameworkConfig alts --bash-framework-config + # type: String min 0 max 1 + --bash-framework-config) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) + # shellcheck disable=SC2034 + optionBashFrameworkConfig="$1" + optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" + + ;; + + # Option 4/14 + # optionInfoVerbose alts --verbose|-v + # type: Boolean min 0 max 1 + --verbose | -v) + # shellcheck disable=SC2034 + optionInfoVerbose="1" + + if ((options_parse_optionParsedCountOptionInfoVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionInfoVerbose)) + optionInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + updateArgListInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + ;; + + # Option 5/14 + # optionDebugVerbose alts -vv + # type: Boolean min 0 max 1 + -vv) + # shellcheck disable=SC2034 + optionDebugVerbose="1" + + if ((options_parse_optionParsedCountOptionDebugVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDebugVerbose)) + optionDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + updateArgListDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + ;; + + # Option 6/14 + # optionTraceVerbose alts -vvv + # type: Boolean min 0 max 1 + -vvv) + # shellcheck disable=SC2034 + optionTraceVerbose="1" + + if ((options_parse_optionParsedCountOptionTraceVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTraceVerbose)) + optionTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + updateArgListTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + ;; + + # Option 7/14 + # optionEnvFiles alts --env-file + # type: StringArray min 0 max -1 + --env-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + ((++options_parse_optionParsedCountOptionEnvFiles)) + optionEnvFiles+=("$1") + optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + ;; + + # Option 8/14 + # optionLogLevel alts --log-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --log-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogLevel)) + # shellcheck disable=SC2034 + optionLogLevel="$1" + optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + ;; + + # Option 9/14 + # optionLogFile alts --log-file + # type: String min 0 max 1 + --log-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogFile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogFile)) + # shellcheck disable=SC2034 + optionLogFile="$1" + optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + ;; + + # Option 10/14 + # optionDisplayLevel alts --display-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --display-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionDisplayLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDisplayLevel)) + # shellcheck disable=SC2034 + optionDisplayLevel="$1" + optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + ;; + + # Option 11/14 + # optionNoColor alts --no-color + # type: Boolean min 0 max 1 + --no-color) + # shellcheck disable=SC2034 + optionNoColor="1" + + if ((options_parse_optionParsedCountOptionNoColor >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionNoColor)) + optionNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + updateArgListNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + ;; + + # Option 12/14 + # optionTheme alts --theme + # type: String min 0 max 1 + # authorizedValues: default|default-force|noColor + --theme) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ default|default-force|noColor ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionTheme >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTheme)) + # shellcheck disable=SC2034 + optionTheme="$1" + optionThemeCallback "${options_parse_arg}" "${optionTheme}" + + updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" + + ;; + + # Option 13/14 + # optionVersion alts --version + # type: Boolean min 0 max 1 + --version) + # shellcheck disable=SC2034 + optionVersion="1" + + if ((options_parse_optionParsedCountOptionVersion >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionVersion)) + optionVersionCallback "${options_parse_arg}" "${optionVersion}" + + ;; + + # Option 14/14 + # optionQuiet alts --quiet|-q + # type: Boolean min 0 max 1 + --quiet | -q) + # shellcheck disable=SC2034 + optionQuiet="1" + + if ((options_parse_optionParsedCountOptionQuiet >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionQuiet)) + optionQuietCallback "${options_parse_arg}" "${optionQuiet}" + + updateArgListQuietCallback "${options_parse_arg}" "${optionQuiet}" + + ;; + + -*) + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" + return 1 + fi + ;; + *) + ((minParsedArgIndex0 = 0)) || true + ((maxParsedArgIndex0 = 0)) || true + ((minParsedArgIndex1 = minParsedArgIndex0 + 1)) || true + ((maxParsedArgIndex1 = maxParsedArgIndex0 + 1)) || true + ((incrementArg = 1 )) + if ((0)); then + # Technical if - never reached + : + + # Argument 1/1 - branchNameArg + # Argument branchNameArg min 1 max 1 + # Argument branchNameArg authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && + options_parse_parsedArgIndex < maxParsedArgIndex1 )); then + if ((options_parse_argParsedCountBranchNameArg >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument branchName - Maximum number of argument occurrences reached(1)" return 1 fi - ((++options_parse_optionParsedCountOptionDisplayLevel)) + ((++options_parse_argParsedCountBranchNameArg)) # shellcheck disable=SC2034 - optionDisplayLevel="$1" - optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - ;; - -*) + branchNameArg="${options_parse_arg}" + + + # else too much args + else + + if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" + # too much args and no unknownArgumentCallbacks configured + Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" return 1 fi - ;; - *) - if ((0)); then - # Technical if - never reached - : - # Argument 1/1 - # Argument branchNameArg min 1 max 1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 0 && options_parse_parsedArgIndex < 1)); then - if ((options_parse_argParsedCountBranchNameArg >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument branchName - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountBranchNameArg)) - # shellcheck disable=SC2034 - branchNameArg="${options_parse_arg}" - else - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" - return 1 - fi - fi + + fi + if ((incrementArg == 1)); then ((++options_parse_parsedArgIndex)) - ;; - esac - shift || true - done - if ((options_parse_argParsedCountBranchNameArg < 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument 'branchName' should be provided at least 1 time(s)" - return 1 - fi - commandOptionParseFinished - Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" - Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" - elif [[ "${options_parse_cmd}" = "help" ]]; then - Array::wrap2 " " 80 0 "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" "show an error if branchName is not a known branch" - echo + fi + ;; + esac + shift || true + done - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "${SCRIPT_NAME}" "[OPTIONS]" "[ARGUMENTS]")" - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ - "${SCRIPT_NAME}" \ - "[--bash-framework-config ]" "[--config]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--no-color]" "[--theme ]" "[--help|-h]" "[--version]" "[--quiet|-q]" "[--log-level ]" "[--log-file ]" "[--display-level ]")" - echo - echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}branchName${__HELP_NORMAL} {single} (mandatory)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(the\ branch\ name\ to\ check) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(use\ alternate\ bash\ framework\ configuration.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ configuration) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(info\ level\ verbose\ mode\ \(alias\ of\ --display-level\ INFO\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(debug\ level\ verbose\ mode\ \(alias\ of\ --display-level\ DEBUG\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(trace\ level\ verbose\ mode\ \(alias\ of\ --display-level\ TRACE\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Load\ the\ specified\ env\ file\ \(deprecated\,\ please\ use\ --bash-framework-config\ option\ instead\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Produce\ monochrome\ output.\ alias\ of\ --theme\ noColor.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' - echo ' Possible values: default|default-force|noColor' - echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ this\ command\ help) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Print\ version\ information\ and\ quit) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(quiet\ mode\,\ doesn\'t\ display\ any\ output) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ file) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(set\ display\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo - echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo '1.0' - echo - echo -e "${__HELP_TITLE_COLOR}AUTHOR:${__RESET_COLOR}" - echo '[François Chastanet](https://github.com/fchastanet)' - echo - echo -e "${__HELP_TITLE_COLOR}SOURCE FILE:${__RESET_COLOR}" - echo 'https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Git/gitIsBranch.sh' - echo - echo -e "${__HELP_TITLE_COLOR}LICENSE:${__RESET_COLOR}" - echo 'MIT License' - echo - Array::wrap2 ' ' 76 4 "$(copyrightCallback)" - else - Log::displayError "Command ${SCRIPT_NAME} - Option command invalid: '${options_parse_cmd}'" + + + + + + + + + + + + + + if ((options_parse_argParsedCountBranchNameArg < 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument 'branchName' should be provided at least 1 time(s)" return 1 - fi + fi || return $? + + commandOptionParseFinished + } -gitIsBranchCommand parse "${BASH_FRAMEWORK_ARGV[@]}" +# @description display command options and arguments help for gitIsBranchCommand +gitIsBranchCommandHelp() { + echo -e "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" + Array::wrap2 ' ' 76 4 " " "Show an error if branchName is not a known branch." -# @require Linux::requireExecutedAsUser -run() { - # check various branch hierarchies, adjust as needed - # shellcheck disable=SC2154 - git show-ref --verify refs/heads/"${branchNameArg}" || - git show-ref --verify refs/remotes/"${branchNameArg}" || - Log::fatal "not a branch name: ${branchNameArg}" + echo + echo + + # ------------------------------------------ + # usage section + # ------------------------------------------ + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "gitIsBranch [OPTIONS] [ARGUMENTS]" + echo + # ------------------------------------------ + # usage/options section + # ------------------------------------------ + optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" + ) + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ + "gitIsBranch" "${optionsAltList[@]}" + echo + + # ------------------------------------------ + # usage/arguments section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" + + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}branchName${__HELP_NORMAL} {single} (mandatory) + " + Array::wrap2 ' ' 76 4 " " "The branch name to check." + echo + # ------------------------------------------ + # options section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays this command help" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays configuration" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" + Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log file" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set display level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." + echo + Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" + echo + + Array::wrap2 ' ' 76 6 " Default value: " "default" + echo + + echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Print version information and quit." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." + echo + + + # ------------------------------------------ + # longDescription section + # ------------------------------------------ + echo + echo + echo -e "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" + longDescriptionFunction + # ------------------------------------------ + # version section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" + echo "3.0" + # ------------------------------------------ + # author section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}AUTHOR: ${__RESET_COLOR}" + echo "[François Chastanet](https://github.com/fchastanet)" + # ------------------------------------------ + # sourceFile section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Git/gitIsBranch/gitIsBranch-binary.yaml" + # ------------------------------------------ + # license section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}LICENSE: ${__RESET_COLOR}" + echo "MIT License" + # ------------------------------------------ + # copyright section + # ------------------------------------------ + Array::wrap2 ' ' 76 0 "$(copyrightCallback)" } -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run + +beforeParseCallback + +gitIsBranchCommandParse "$@" +MAIN_FUNCTION_NAME="main" +main() { + +# shellcheck disable=SC2154 + +declare optionRedirectCmdOutputs="" +if [[ "${optionTraceVerbose}" != "1" ]]; then + # shellcheck disable=SC2034 # used by BashTools::runVerboseIfNeeded + optionRedirectCmdOutputs="/dev/null" fi +BashTools::runVerboseIfNeeded git show-ref --verify refs/heads/"${branchNameArg}" || + BashTools::runVerboseIfNeeded git show-ref --verify refs/remotes/"${branchNameArg}" || + Log::fatal "not a branch name: ${branchNameArg}" } -facade_main_gitIsBranchsh "$@" +# if file is sourced avoid calling main function +# shellcheck disable=SC2178 +BASH_SOURCE=".$0" # cannot be changed in bash +# shellcheck disable=SC2128 +if test ".$0" == ".${BASH_SOURCE}"; then + if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then + main "$@" &>/dev/null + else + main "$@" + fi +fi diff --git a/src/BashTools/runVerboseIfNeeded.bats b/src/BashTools/runVerboseIfNeeded.bats new file mode 100755 index 00000000..95b22bad --- /dev/null +++ b/src/BashTools/runVerboseIfNeeded.bats @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +# shellcheck source=src/batsHeaders.sh +source "$(cd "${BATS_TEST_DIRNAME}/.." && pwd)/batsHeaders.sh" +# shellcheck source=src/BashTools/runVerboseIfNeeded.sh +source "${rootDir}/src/BashTools/runVerboseIfNeeded.sh" + +teardown() { + unset optionTraceVerbose +} + +function BashTools::runVerboseIfNeeded::noTrace { #@test + run BashTools::runVerboseIfNeeded echo "coucou" + assert_output "coucou" + assert_success +} + +function BashTools::runVerboseIfNeeded::trace { #@test + export optionTraceVerbose="1" + cmdTest() { + optionTraceVerbose="1" BashTools::runVerboseIfNeeded echo "coucou" + } + run cmdTest + assert_lines_count 2 + assert_line --index 0 '+ echo coucou' + assert_line --index 1 "coucou" + assert_success +} + +function BashTools::runVerboseIfNeeded::redirectCmdOutputs { #@test + export + cmdTest() { + optionTraceVerbose="1" optionRedirectCmdOutputs="/dev/null" BashTools::runVerboseIfNeeded echo "coucou" + } + run cmdTest + assert_output '+ echo coucou' + assert_success +} diff --git a/src/BashTools/runVerboseIfNeeded.sh b/src/BashTools/runVerboseIfNeeded.sh new file mode 100755 index 00000000..e3ce2f33 --- /dev/null +++ b/src/BashTools/runVerboseIfNeeded.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# @description run command specified +# @arg $@ array:String[] the command to run +# @env optionTraceVerbose int - if 1 displays the command specified before running it +# @env optionRedirectCmdOutputs String - if set redirect command outputs to file specified +# @exitcode command's exit code +BashTools::runVerboseIfNeeded() { + # shellcheck disable=SC2154 + if [[ "${optionTraceVerbose}" = "1" ]]; then + echo >&2 "+ $*" + fi + ( + # shellcheck disable=SC2154 + if [[ -n "${optionRedirectCmdOutputs:-}" ]]; then + exec >"${optionRedirectCmdOutputs:-}" + exec 2>"${optionRedirectCmdOutputs:-}" + fi + "$@" + ) +} diff --git a/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-main.sh b/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-main.sh index 4c124d7f..848888d0 100755 --- a/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-main.sh +++ b/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-main.sh @@ -1,40 +1,31 @@ #!/usr/bin/env bash -runVerboseIfNeeded() { - ( - # shellcheck disable=SC2154 - if [[ "${optionTraceVerbose}" = "1" ]]; then - set -x - fi - "$@" - ) -} # shellcheck disable=SC2154 -if ! runVerboseIfNeeded git cat-file -t "${refArg}" &>/dev/null; then +if ! optionRedirectCmdOutputs=/dev/null BashTools::runVerboseIfNeeded git cat-file -t "${refArg}"; then Log::displayError "Ref ${refArg} does not exists at all" exit 1 fi # shellcheck disable=SC2154 -if ! runVerboseIfNeeded git cat-file -t "${claimedBranchArg}" &>/dev/null; then +if ! optionRedirectCmdOutputs=/dev/null BashTools::runVerboseIfNeeded git cat-file -t "${claimedBranchArg}"; then Log::displayError "Ref ${claimedBranchArg} does not exists at all" exit 1 fi declare refCommit claimedBranchCommit -refCommit="$(git rev-parse "${refArg}")" || { +refCommit="$(BashTools::runVerboseIfNeeded git rev-parse "${refArg}")" || { Log::displayError "Ref ${refArg} is not convertible to commit sha" exit 1 } -claimedBranchCommit="$(git rev-parse "${claimedBranchArg}")" || { +claimedBranchCommit="$(BashTools::runVerboseIfNeeded git rev-parse "${claimedBranchArg}")" || { Log::displayError "Ref ${claimedBranchArg} is not convertible to commit sha" exit 1 } declare revParse mergeBase # shellcheck disable=SC2154 -mergeBase="$(runVerboseIfNeeded git merge-base "${refCommit}" "${claimedBranchCommit}")" -revParse="$(runVerboseIfNeeded git rev-parse --verify "${refCommit}")" +mergeBase="$(BashTools::runVerboseIfNeeded git merge-base "${refCommit}" "${claimedBranchCommit}")" +revParse="$(BashTools::runVerboseIfNeeded git rev-parse --verify "${refCommit}")" if [[ -z "${mergeBase}" || "${mergeBase}" != "${revParse}" ]]; then Log::displayError "Commit ${refArg} is not an ancestor of branch ${claimedBranchArg}" diff --git a/src/_binaries/Git/gitIsBranch.options.tpl b/src/_binaries/Git/gitIsBranch.options.tpl deleted file mode 100644 index 283f794e..00000000 --- a/src/_binaries/Git/gitIsBranch.options.tpl +++ /dev/null @@ -1,28 +0,0 @@ -% -declare versionNumber="1.0" -declare commandFunctionName="gitIsBranchCommand" -declare help="show an error if branchName is not a known branch" -% - -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.base.tpl)" - -% -# shellcheck source=/dev/null -source <( - containerArgHelpCallback() { :; } - Options::generateArg \ - --help "the branch name to check" \ - --min 1 \ - --max 1 \ - --name "branchName" \ - --variable-name "branchNameArg" \ - --function-name branchNameArgFunction - -) -options+=( - branchNameArgFunction -) -Options::generateCommand "${options[@]}" -% - -<% ${commandFunctionName} %> parse "${BASH_FRAMEWORK_ARGV[@]}" diff --git a/src/_binaries/Git/gitIsBranch.sh b/src/_binaries/Git/gitIsBranch.sh deleted file mode 100755 index dd10bb03..00000000 --- a/src/_binaries/Git/gitIsBranch.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/gitIsBranch -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE -# shellcheck disable=SC2034 - -declare copyrightBeginYear="2020" -declare branchNameArg="" - -.INCLUDE "$(dynamicTemplateDir _binaries/Git/gitIsBranch.options.tpl)" - -# @require Linux::requireExecutedAsUser -run() { - # check various branch hierarchies, adjust as needed - # shellcheck disable=SC2154 - git show-ref --verify refs/heads/"${branchNameArg}" || - git show-ref --verify refs/remotes/"${branchNameArg}" || - Log::fatal "not a branch name: ${branchNameArg}" -} - -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run -fi diff --git a/src/_binaries/Git/gitIsBranch/gitIsBranch-binary.yaml b/src/_binaries/Git/gitIsBranch/gitIsBranch-binary.yaml new file mode 100644 index 00000000..00074e3a --- /dev/null +++ b/src/_binaries/Git/gitIsBranch/gitIsBranch-binary.yaml @@ -0,0 +1,34 @@ +extends: + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" + +vars: + SRC_FILE_PATH: src/_binaries/Git/gitIsBranch/gitIsBranch-binary.yaml + +compilerConfig: + targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/gitIsBranch" + relativeRootDirBasedOnTargetDir: .. + srcDirs: + - ${BASH_TOOLS_ROOT_DIR}/src +binData: + commands: + default: + functionName: gitIsBranchCommand + version: "3.0" + copyrightBeginYear: 2020 + commandName: gitIsBranch + beforeParseCallbacks: + - beforeParseCallback + definitionFiles: + 20: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Git/gitIsBranch/gitIsBranch-options.sh + mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Git/gitIsBranch/gitIsBranch-main.sh + help: Show an error if branchName is not a known branch. + longDescription: longDescriptionFunction + args: + - type: String + min: 1 + max: 1 + help: The branch name to check. + name: branchName + variableName: branchNameArg diff --git a/src/_binaries/Git/gitIsBranch/gitIsBranch-main.sh b/src/_binaries/Git/gitIsBranch/gitIsBranch-main.sh new file mode 100755 index 00000000..b2683963 --- /dev/null +++ b/src/_binaries/Git/gitIsBranch/gitIsBranch-main.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2154 + +declare optionRedirectCmdOutputs="" +if [[ "${optionTraceVerbose}" != "1" ]]; then + # shellcheck disable=SC2034 # used by BashTools::runVerboseIfNeeded + optionRedirectCmdOutputs="/dev/null" +fi +BashTools::runVerboseIfNeeded git show-ref --verify refs/heads/"${branchNameArg}" || + BashTools::runVerboseIfNeeded git show-ref --verify refs/remotes/"${branchNameArg}" || + Log::fatal "not a branch name: ${branchNameArg}" diff --git a/src/_binaries/Git/gitIsBranch/gitIsBranch-options.sh b/src/_binaries/Git/gitIsBranch/gitIsBranch-options.sh new file mode 100755 index 00000000..f216eff4 --- /dev/null +++ b/src/_binaries/Git/gitIsBranch/gitIsBranch-options.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +longDescriptionFunction() { + echo -e " ${__HELP_TITLE}EXIT CODES:${__HELP_NORMAL}" + echo -e " ${__HELP_OPTION_COLOR}1${__HELP_NORMAL}: if commit does not exists" + echo -e " ${__HELP_OPTION_COLOR}2${__HELP_NORMAL}: if ref is not convertible to commit oid" + echo -e " ${__HELP_OPTION_COLOR}3${__HELP_NORMAL}: if commit is not included in given branch" +} + +optionHelpCallback() { + gitIsBranchCommandHelp + exit 0 +} diff --git a/src/_binaries/Git/gitIsBranch/gitIsBranch.bats b/src/_binaries/Git/gitIsBranch/gitIsBranch.bats new file mode 100755 index 00000000..83564465 --- /dev/null +++ b/src/_binaries/Git/gitIsBranch/gitIsBranch.bats @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +# shellcheck source=src/batsHeaders.sh +source "$(cd "${BATS_TEST_DIRNAME}/../../.." && pwd)/batsHeaders.sh" + +load "${FRAMEWORK_ROOT_DIR}/src/_standalone/Bats/assert_lines_count.sh" + +setup() { + export TMPDIR="${BATS_TEST_TMPDIR}" + export HOME="${BATS_TEST_TMPDIR}/home" + export BASH_FRAMEWORK_ENV_FILEPATH="${BATS_TEST_DIRNAME}/testsData/.env" + mkdir -p "${HOME}/.bash-tools" + cp "${rootDir}/conf/.env" "${HOME}/.bash-tools/.env" +} + +teardown() { + unstub_all +} + +function Git::gitIsBranch::display_help { #@test + testCommand "${binDir}/gitIsBranch" gitIsBranch.help.txt +} diff --git a/src/_binaries/Git/gitIsBranch/testsData/gitIsBranch.help.txt b/src/_binaries/Git/gitIsBranch/testsData/gitIsBranch.help.txt new file mode 100644 index 00000000..e69de29b From d7cb81506dd7e09fb3cd98c09602ea9570f89a81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Wed, 4 Sep 2024 23:42:49 +0200 Subject: [PATCH 15/24] compiled gitRenameBranch using go compiler --- bin/gitIsAncestorOf | 147 +- bin/gitIsBranch | 152 +- bin/gitRenameBranch | 2714 ++++++++++------- src/BashTools/runVerboseIfNeeded.bats | 8 +- src/BashTools/runVerboseIfNeeded.sh | 4 +- .../gitIsAncestorOf-binary.yaml | 1 + .../Git/gitIsBranch/gitIsBranch-binary.yaml | 1 + .../Git/gitIsBranch/gitIsBranch-main.sh | 5 - .../testsData/gitIsBranch.help.txt | 65 + src/_binaries/Git/gitRenameBranch.options.tpl | 89 - src/_binaries/Git/gitRenameBranch.sh | 88 - .../gitRenameBranch-binary.yaml | 70 + .../gitRenameBranch/gitRenameBranch-main.sh | 69 + .../gitRenameBranch-options.sh | 36 + .../gitRenameBranch.bats | 99 +- .../Git/gitRenameBranch/testsData/.env | 52 + .../testsData/gitRenameBranch.help.txt | 86 + src/_binaries/Git/gitToolsDefaultOptions.sh | 19 + src/_binaries/Git/gitToolsDefaultOptions.yaml | 7 + .../Git/testsData/gitRenameBranch.help.txt | 86 - src/_binaries/Postman/postmanCli.bats | 2 +- 21 files changed, 2359 insertions(+), 1441 deletions(-) delete mode 100644 src/_binaries/Git/gitRenameBranch.options.tpl delete mode 100755 src/_binaries/Git/gitRenameBranch.sh create mode 100644 src/_binaries/Git/gitRenameBranch/gitRenameBranch-binary.yaml create mode 100755 src/_binaries/Git/gitRenameBranch/gitRenameBranch-main.sh create mode 100755 src/_binaries/Git/gitRenameBranch/gitRenameBranch-options.sh rename src/_binaries/Git/{ => gitRenameBranch}/gitRenameBranch.bats (63%) create mode 100755 src/_binaries/Git/gitRenameBranch/testsData/.env create mode 100644 src/_binaries/Git/gitRenameBranch/testsData/gitRenameBranch.help.txt create mode 100755 src/_binaries/Git/gitToolsDefaultOptions.sh create mode 100644 src/_binaries/Git/gitToolsDefaultOptions.yaml delete mode 100644 src/_binaries/Git/testsData/gitRenameBranch.help.txt diff --git a/bin/gitIsAncestorOf b/bin/gitIsAncestorOf index 8e07039d..10986f44 100755 --- a/bin/gitIsAncestorOf +++ b/bin/gitIsAncestorOf @@ -338,14 +338,74 @@ Bash::handlePipelineFailure() { } +# @description loads ~/.bash-tools/.env if available +# if not creates it from a default template +# else check if new options need to be added +BashTools::Conf::requireLoad() { +Linux::requireTarCommand +Compiler::Embed::extractFileFromBase64 \ + "${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" \ + "IyEvdXNyL2Jpbi9lbnYgYmFzaAojIHNoZWxsY2hlY2sgZGlzYWJsZT1TQzIwMzQKCiMgRGVmYXVsdCBzZXR0aW5ncwojIHlvdSBjYW4gb3ZlcnJpZGUgdGhlc2Ugc2V0dGluZ3MgYnkgY3JlYXRpbmcgJHtIT01FfS8uYmFzaC10b29scy8uZW52IGZpbGUKCiMjIwojIyMgRElTUExBWSBMZXZlbAojIyMgbWluaW11bSBsZXZlbCBvZiB0aGUgbWVzc2FnZXMgdGhhdCB3aWxsIGJlIGRpc3BsYXllZCBvbiBzY3JlZW4KIyMjCiMjIyAwOiBOTyBMT0cKIyMjIDE6IEVSUk9SCiMjIyAyOiBXQVJOSU5HCiMjIyAzOiBJTkZPCiMjIyA0OiBERUJVRwojIyMKQkFTSF9GUkFNRVdPUktfRElTUExBWV9MRVZFTD0ke0JBU0hfRlJBTUVXT1JLX0RJU1BMQVlfTEVWRUw6LTN9CgojIyMKIyMjIERJU1BMQVkgZHVyYXRpb24KIyMjIDA6IG5vIGR1cmF0aW9uIGlzIGRpc3BsYXllZCBvbiB0aGUgbWVzc2FnZXMKIyMjIDE6IGR1cmF0aW9uIGJldHdlZW4gcHJldmlvdXMgbWVzc2FnZSBhbmQgY3VycmVudCBpcyBkaXNwbGF5ZWQKIyMjIHdpdGggdGhlIG1lc3NhZ2UKIyMjCkRJU1BMQVlfRFVSQVRJT049JHtESVNQTEFZX0RVUkFUSU9OOjB9CgojIyMKIyMjIExvZyB0byBmaWxlCiMjIwojIyMgYWxsIGxvZyBtZXNzYWdlcyB3aWxsIGJlIHJlZGlyZWN0ZWQgdG8gbG9nIGZpbGUgc3BlY2lmaWVkCiMjIyB0aGlzIHNhbWUgcGF0aCB3aWxsIGJlIHVzZWQgaW5zaWRlIGFuZCBvdXRzaWRlIG9mIHRoZSBjb250YWluZXIKIyMjCkJBU0hfRlJBTUVXT1JLX0xPR19GSUxFPSR7QkFTSF9GUkFNRVdPUktfTE9HX0ZJTEU6LSR7RlJBTUVXT1JLX1JPT1RfRElSfS9sb2dzL2Jhc2gubG9nfQoKIyMjCiMjIyBMT0cgTGV2ZWwKIyMjIG1pbmltdW0gbGV2ZWwgb2YgdGhlIG1lc3NhZ2VzIHRoYXQgd2lsbCBiZSBsb2dnZWQgaW50byBMT0dfRklMRQojIyMKIyMjIDA6IE5PIExPRwojIyMgMTogRVJST1IKIyMjIDI6IFdBUk5JTkcKIyMjIDM6IElORk8KIyMjIDQ6IERFQlVHCiMjIwpCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw9JHtCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw6LTB9CgojIGFic29sdXRlIGRpcmVjdG9yeSBjb250YWluaW5nIGRiIGltcG9ydCBzcWwgZHVtcHMKREJfSU1QT1JUX0RVTVBfRElSPSR7REJfSU1QT1JUX0RVTVBfRElSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2RiSW1wb3J0RHVtcHN9CgojIGdhcmJhZ2UgY29sbGVjdCBhbGwgZmlsZXMgZm9yIHdoaWNoIG1vZGlmaWNhdGlvbiBpcyBncmVhdGVyIHRoYW4gZWc6IDMwIGRheXMgKCszMCkKIyBlYWNoIHRpbWUgYW4gZXhpc3RpbmcgZmlsZSBpcyB1c2VkIGJ5IGRiSW1wb3J0L2RiSW1wb3J0VGFibGUKIyB0aGUgZmlsZSBtb2RpZmljYXRpb24gdGltZSBpcyBzZXQgdG8gbm93CkRCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUz0ke0RCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUzotKzMwfQoKIyBhYnNvbHV0ZSBkaXJlY3RvcnkgY29udGFpbmluZyBkYlNjcmlwdHMgdXNlZCBieSBkYlNjcmlwdEFsbERhdGFiYXNlcwpTQ1JJUFRTX0ZPTERFUj0ke1NDUklQVFNfRk9MREVSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2NvbmYvZGJTY3JpcHRzfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEFXUyBQYXJhbWV0ZXJzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KUzNfQkFTRV9VUkw9JHtTM19CQVNFX1VSTDotfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvc3RtYW4gUGFyYW1ldGVycwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClBPU1RNQU5fQVBJX0tFWT0K" \ + "755" + +declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" + + BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" + if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then + FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" + else + # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR + FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" + fi + # shellcheck disable=SC2034 + FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" + # shellcheck disable=SC2034 + FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + + if [[ -f "${HOME}/.bash-tools/.env" ]]; then + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") + fi + + local envFile="${HOME}/.bash-tools/.env" + if [[ ! -f "${envFile}" ]]; then + mkdir -p "${HOME}/.bash-tools" + ( + echo "#!/usr/bin/env bash" + # shellcheck disable=SC2154 + echo "${embed_file_bashToolsDefaultConfigTemplate}" + ) >"${envFile}" + Log::displayInfo "Configuration file '${envFile}' created" + else + if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then + ( + echo '# -----------------------------------------------------' + echo '# Postman Parameters' + echo '# -----------------------------------------------------' + echo 'POSTMAN_API_KEY=' + ) >>"${envFile}" + fi + fi + # shellcheck source=/conf/.env + source "${envFile}" || { + Log::displayError "impossible to load '${envFile}'" + exit 1 + } +} + + # @description run command specified # @arg $@ array:String[] the command to run -# @env optionTraceVerbose int - if 1 displays the command specified before running it +# @env optionInfoVerbose int - if 1 displays the command specified before running it # @env optionRedirectCmdOutputs String - if set redirect command outputs to file specified # @exitcode command's exit code BashTools::runVerboseIfNeeded() { # shellcheck disable=SC2154 - if [[ "${optionTraceVerbose}" = "1" ]]; then + if [[ "${optionInfoVerbose}" = "1" ]]; then echo >&2 "+ $*" fi ( @@ -359,6 +419,36 @@ BashTools::runVerboseIfNeeded() { } +# @description convert base64 encoded back to target file +# if target file is executable prepend dir of target +# file to PATH to make binary available everywhere +# it is advised to include in the path of the target file +# the md5sum of the binFile +# +# @arg $1 targetFile:String the file to write +# @arg $2 binFileBase64:String the base64 encoded file +# @arg $3 fileMode:String the chmod to set on the file +# @set PATH String prepend target embedded file binary directory to PATH variable if binary executable +Compiler::Embed::extractFileFromBase64() { + local targetFile="$1" + local binFileBase64="$2" + local fileMode="${3:-+x}" + local targetDir="${targetFile%/*}" + + if [[ ! -f "${targetFile}" ]]; then + if [[ ! -d "${targetDir}" ]]; then + mkdir -p "${targetDir}" + fi + base64 -d >"${targetFile}" <<<"${binFileBase64}" + chmod "${fileMode}" "${targetFile}" + fi + + if [[ -x "${targetFile}" ]]; then + Env::pathPrepend "${targetDir}" + fi +} + + # @description check if all requirements are satisfied # to execute dbImport commands Db::checkRequirements() { @@ -378,6 +468,19 @@ Db::checkRequirements() { } +# @description prepend directories to the PATH environment variable +# @arg $@ args:String[] list of directories to prepend +# @set PATH update PATH with the directories prepended +Env::pathPrepend() { + local arg + for arg in "$@"; do + if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then + PATH="$(realpath "${arg}"):${PATH}" + fi + done +} + + # @description ensure env files are loaded # @arg $@ list of default files to load at the end # @exitcode 1 if one of env files fails to load @@ -422,6 +525,24 @@ Framework::createTempFile() { } +# @description ensure running user is not root +# @exitcode 1 if current user is root +# @stderr diagnostics information is displayed +Linux::requireExecutedAsUser() { + if [[ "$(id -u)" = "0" ]]; then + Log::fatal "this script should be executed as normal user" + fi +} + + +# @description ensure command tar is available +# @exitcode 1 if tar command not available +# @stderr diagnostics information is displayed +Linux::requireTarCommand() { + Assert::commandExists tar +} + + declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" @@ -1146,6 +1267,26 @@ commandOptionParseFinished() { +beforeParseCallback() { + Linux::requireExecutedAsUser + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + +declare optionRedirectCmdOutputs="" +optionRedirectCmdOutputs() { + export optionTraceVerbose + # shellcheck disable=SC2154 + if [[ "${optionTraceVerbose}" != "1" ]]; then + # shellcheck disable=SC2034 # used by BashTools::runVerboseIfNeeded + optionRedirectCmdOutputs="/dev/null" + fi +} + + + longDescriptionFunction() { echo -e " ${__HELP_TITLE}EXIT CODES:${__HELP_NORMAL}" echo -e " ${__HELP_OPTION_COLOR}1${__HELP_NORMAL}: if commit does not exists" @@ -1620,7 +1761,9 @@ gitIsAncestorOfCommandParse() { return 1 fi || return $? + commandOptionParseFinished + optionRedirectCmdOutputs } diff --git a/bin/gitIsBranch b/bin/gitIsBranch index c88fa789..cd5f8a09 100755 --- a/bin/gitIsBranch +++ b/bin/gitIsBranch @@ -338,14 +338,74 @@ Bash::handlePipelineFailure() { } +# @description loads ~/.bash-tools/.env if available +# if not creates it from a default template +# else check if new options need to be added +BashTools::Conf::requireLoad() { +Linux::requireTarCommand +Compiler::Embed::extractFileFromBase64 \ + "${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" \ + "IyEvdXNyL2Jpbi9lbnYgYmFzaAojIHNoZWxsY2hlY2sgZGlzYWJsZT1TQzIwMzQKCiMgRGVmYXVsdCBzZXR0aW5ncwojIHlvdSBjYW4gb3ZlcnJpZGUgdGhlc2Ugc2V0dGluZ3MgYnkgY3JlYXRpbmcgJHtIT01FfS8uYmFzaC10b29scy8uZW52IGZpbGUKCiMjIwojIyMgRElTUExBWSBMZXZlbAojIyMgbWluaW11bSBsZXZlbCBvZiB0aGUgbWVzc2FnZXMgdGhhdCB3aWxsIGJlIGRpc3BsYXllZCBvbiBzY3JlZW4KIyMjCiMjIyAwOiBOTyBMT0cKIyMjIDE6IEVSUk9SCiMjIyAyOiBXQVJOSU5HCiMjIyAzOiBJTkZPCiMjIyA0OiBERUJVRwojIyMKQkFTSF9GUkFNRVdPUktfRElTUExBWV9MRVZFTD0ke0JBU0hfRlJBTUVXT1JLX0RJU1BMQVlfTEVWRUw6LTN9CgojIyMKIyMjIERJU1BMQVkgZHVyYXRpb24KIyMjIDA6IG5vIGR1cmF0aW9uIGlzIGRpc3BsYXllZCBvbiB0aGUgbWVzc2FnZXMKIyMjIDE6IGR1cmF0aW9uIGJldHdlZW4gcHJldmlvdXMgbWVzc2FnZSBhbmQgY3VycmVudCBpcyBkaXNwbGF5ZWQKIyMjIHdpdGggdGhlIG1lc3NhZ2UKIyMjCkRJU1BMQVlfRFVSQVRJT049JHtESVNQTEFZX0RVUkFUSU9OOjB9CgojIyMKIyMjIExvZyB0byBmaWxlCiMjIwojIyMgYWxsIGxvZyBtZXNzYWdlcyB3aWxsIGJlIHJlZGlyZWN0ZWQgdG8gbG9nIGZpbGUgc3BlY2lmaWVkCiMjIyB0aGlzIHNhbWUgcGF0aCB3aWxsIGJlIHVzZWQgaW5zaWRlIGFuZCBvdXRzaWRlIG9mIHRoZSBjb250YWluZXIKIyMjCkJBU0hfRlJBTUVXT1JLX0xPR19GSUxFPSR7QkFTSF9GUkFNRVdPUktfTE9HX0ZJTEU6LSR7RlJBTUVXT1JLX1JPT1RfRElSfS9sb2dzL2Jhc2gubG9nfQoKIyMjCiMjIyBMT0cgTGV2ZWwKIyMjIG1pbmltdW0gbGV2ZWwgb2YgdGhlIG1lc3NhZ2VzIHRoYXQgd2lsbCBiZSBsb2dnZWQgaW50byBMT0dfRklMRQojIyMKIyMjIDA6IE5PIExPRwojIyMgMTogRVJST1IKIyMjIDI6IFdBUk5JTkcKIyMjIDM6IElORk8KIyMjIDQ6IERFQlVHCiMjIwpCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw9JHtCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw6LTB9CgojIGFic29sdXRlIGRpcmVjdG9yeSBjb250YWluaW5nIGRiIGltcG9ydCBzcWwgZHVtcHMKREJfSU1QT1JUX0RVTVBfRElSPSR7REJfSU1QT1JUX0RVTVBfRElSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2RiSW1wb3J0RHVtcHN9CgojIGdhcmJhZ2UgY29sbGVjdCBhbGwgZmlsZXMgZm9yIHdoaWNoIG1vZGlmaWNhdGlvbiBpcyBncmVhdGVyIHRoYW4gZWc6IDMwIGRheXMgKCszMCkKIyBlYWNoIHRpbWUgYW4gZXhpc3RpbmcgZmlsZSBpcyB1c2VkIGJ5IGRiSW1wb3J0L2RiSW1wb3J0VGFibGUKIyB0aGUgZmlsZSBtb2RpZmljYXRpb24gdGltZSBpcyBzZXQgdG8gbm93CkRCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUz0ke0RCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUzotKzMwfQoKIyBhYnNvbHV0ZSBkaXJlY3RvcnkgY29udGFpbmluZyBkYlNjcmlwdHMgdXNlZCBieSBkYlNjcmlwdEFsbERhdGFiYXNlcwpTQ1JJUFRTX0ZPTERFUj0ke1NDUklQVFNfRk9MREVSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2NvbmYvZGJTY3JpcHRzfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEFXUyBQYXJhbWV0ZXJzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KUzNfQkFTRV9VUkw9JHtTM19CQVNFX1VSTDotfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvc3RtYW4gUGFyYW1ldGVycwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClBPU1RNQU5fQVBJX0tFWT0K" \ + "755" + +declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" + + BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" + if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then + FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" + else + # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR + FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" + fi + # shellcheck disable=SC2034 + FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" + # shellcheck disable=SC2034 + FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + + if [[ -f "${HOME}/.bash-tools/.env" ]]; then + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") + fi + + local envFile="${HOME}/.bash-tools/.env" + if [[ ! -f "${envFile}" ]]; then + mkdir -p "${HOME}/.bash-tools" + ( + echo "#!/usr/bin/env bash" + # shellcheck disable=SC2154 + echo "${embed_file_bashToolsDefaultConfigTemplate}" + ) >"${envFile}" + Log::displayInfo "Configuration file '${envFile}' created" + else + if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then + ( + echo '# -----------------------------------------------------' + echo '# Postman Parameters' + echo '# -----------------------------------------------------' + echo 'POSTMAN_API_KEY=' + ) >>"${envFile}" + fi + fi + # shellcheck source=/conf/.env + source "${envFile}" || { + Log::displayError "impossible to load '${envFile}'" + exit 1 + } +} + + # @description run command specified # @arg $@ array:String[] the command to run -# @env optionTraceVerbose int - if 1 displays the command specified before running it +# @env optionInfoVerbose int - if 1 displays the command specified before running it # @env optionRedirectCmdOutputs String - if set redirect command outputs to file specified # @exitcode command's exit code BashTools::runVerboseIfNeeded() { # shellcheck disable=SC2154 - if [[ "${optionTraceVerbose}" = "1" ]]; then + if [[ "${optionInfoVerbose}" = "1" ]]; then echo >&2 "+ $*" fi ( @@ -359,6 +419,36 @@ BashTools::runVerboseIfNeeded() { } +# @description convert base64 encoded back to target file +# if target file is executable prepend dir of target +# file to PATH to make binary available everywhere +# it is advised to include in the path of the target file +# the md5sum of the binFile +# +# @arg $1 targetFile:String the file to write +# @arg $2 binFileBase64:String the base64 encoded file +# @arg $3 fileMode:String the chmod to set on the file +# @set PATH String prepend target embedded file binary directory to PATH variable if binary executable +Compiler::Embed::extractFileFromBase64() { + local targetFile="$1" + local binFileBase64="$2" + local fileMode="${3:-+x}" + local targetDir="${targetFile%/*}" + + if [[ ! -f "${targetFile}" ]]; then + if [[ ! -d "${targetDir}" ]]; then + mkdir -p "${targetDir}" + fi + base64 -d >"${targetFile}" <<<"${binFileBase64}" + chmod "${fileMode}" "${targetFile}" + fi + + if [[ -x "${targetFile}" ]]; then + Env::pathPrepend "${targetDir}" + fi +} + + # @description check if all requirements are satisfied # to execute dbImport commands Db::checkRequirements() { @@ -378,6 +468,19 @@ Db::checkRequirements() { } +# @description prepend directories to the PATH environment variable +# @arg $@ args:String[] list of directories to prepend +# @set PATH update PATH with the directories prepended +Env::pathPrepend() { + local arg + for arg in "$@"; do + if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then + PATH="$(realpath "${arg}"):${PATH}" + fi + done +} + + # @description ensure env files are loaded # @arg $@ list of default files to load at the end # @exitcode 1 if one of env files fails to load @@ -422,6 +525,24 @@ Framework::createTempFile() { } +# @description ensure running user is not root +# @exitcode 1 if current user is root +# @stderr diagnostics information is displayed +Linux::requireExecutedAsUser() { + if [[ "$(id -u)" = "0" ]]; then + Log::fatal "this script should be executed as normal user" + fi +} + + +# @description ensure command tar is available +# @exitcode 1 if tar command not available +# @stderr diagnostics information is displayed +Linux::requireTarCommand() { + Assert::commandExists tar +} + + declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" @@ -1146,6 +1267,26 @@ commandOptionParseFinished() { +beforeParseCallback() { + Linux::requireExecutedAsUser + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + +declare optionRedirectCmdOutputs="" +optionRedirectCmdOutputs() { + export optionTraceVerbose + # shellcheck disable=SC2154 + if [[ "${optionTraceVerbose}" != "1" ]]; then + # shellcheck disable=SC2034 # used by BashTools::runVerboseIfNeeded + optionRedirectCmdOutputs="/dev/null" + fi +} + + + longDescriptionFunction() { echo -e " ${__HELP_TITLE}EXIT CODES:${__HELP_NORMAL}" echo -e " ${__HELP_OPTION_COLOR}1${__HELP_NORMAL}: if commit does not exists" @@ -1594,7 +1735,9 @@ gitIsBranchCommandParse() { return 1 fi || return $? + commandOptionParseFinished + optionRedirectCmdOutputs } @@ -1769,11 +1912,6 @@ main() { # shellcheck disable=SC2154 -declare optionRedirectCmdOutputs="" -if [[ "${optionTraceVerbose}" != "1" ]]; then - # shellcheck disable=SC2034 # used by BashTools::runVerboseIfNeeded - optionRedirectCmdOutputs="/dev/null" -fi BashTools::runVerboseIfNeeded git show-ref --verify refs/heads/"${branchNameArg}" || BashTools::runVerboseIfNeeded git show-ref --verify refs/remotes/"${branchNameArg}" || Log::fatal "not a branch name: ${branchNameArg}" diff --git a/bin/gitRenameBranch b/bin/gitRenameBranch index b9026f1c..08da8777 100755 --- a/bin/gitRenameBranch +++ b/bin/gitRenameBranch @@ -1,13 +1,12 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FACADE FROM https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Git/gitRenameBranch.sh +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Git/gitRenameBranch/gitRenameBranch-binary.yaml # DO NOT EDIT IT # @generated ############################################################################### # shellcheck disable=SC2288,SC2034 -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/gitRenameBranch -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE + + # ensure that no user aliases could interfere with # commands used in this script @@ -62,13 +61,6 @@ interruptManagement() { kill -s INT "$$" } trap interruptManagement INT -SCRIPT_NAME=${0##*/} -REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" -if [[ -n "${EMBED_CURRENT_DIR}" ]]; then - CURRENT_DIR="${EMBED_CURRENT_DIR}" -else - CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" -fi ################################################ # Temp dir management @@ -102,6 +94,20 @@ cleanOnExit() { } trap cleanOnExit EXIT HUP QUIT ABRT TERM + +SCRIPT_NAME=${0##*/} +REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" +if [[ -n "${EMBED_CURRENT_DIR}" ]]; then + CURRENT_DIR="${EMBED_CURRENT_DIR}" +else + CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" +fi +FRAMEWORK_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" +FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" +FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + # @description Log namespace provides 2 kind of functions # - Log::display* allows to display given message with # given display level @@ -132,175 +138,357 @@ export __VERBOSE_LEVEL_DEBUG=2 # @description verbose level info export __VERBOSE_LEVEL_TRACE=3 -# @description Display message using info color (bg light blue/fg white) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayInfo() { - local type="${2:-INFO}" - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description concatenate each element of an array with a separator +# but wrapping text when line length is more than provided argument +# The algorithm will try not to cut the array element if it can. +# - if an arg can be placed on current line it will be, +# otherwise current line is printed and arg is added to the new +# current line +# - Empty arg is interpreted as a new line. +# - Add \r to arg in order to force break line and avoid following +# arg to be concatenated with current arg. +# +# @arg $1 glue:String +# @arg $2 maxLineLength:int +# @arg $3 indentNextLine:int +# @arg $@ array:String[] +Array::wrap2() { + local glue="${1-}" + local -i glueLength="${#glue}" + shift || true + local -i maxLineLength=$1 + shift || true + local -i indentNextLine=$1 + shift || true + local indentStr="" + if ((indentNextLine > 0)); then + indentStr="$(head -c "${indentNextLine}" 0)); do + arg="$1" + shift || true + + # replace tab by 2 spaces + arg="${arg//$'\t'/ }" + # remove trailing spaces + arg="${arg%[[:blank:]]}" + if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then + printCurrentLine + ((previousLineEmpty = 1)) + continue + else + if ((previousLineEmpty == 1)); then + printCurrentLine + fi + ((previousLineEmpty = 0)) || true + fi + # convert eol to args + mapfile -t additionalLines <<<"${arg}" + if ((${#additionalLines[@]} > 1)); then + set -- "${additionalLines[@]}" "$@" + continue + fi + + ((argLength = ${#arg})) || true + + # empty arg + if ((argLength == 0)); then + if ((isNewline == 0)); then + # isNewline = 0 means currentLine is not empty + printCurrentLine + fi + continue + fi + + if ((isNewline == 0)); then + glueLength="${#glue}" + else + glueLength="0" + fi + if ((currentLineLength + argLength + glueLength > maxLineLength)); then + if ((argLength + glueLength > maxLineLength)); then + # arg is too long to even fit on one line + # we have to split the arg on current and next line + local -i remainingLineLength + ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) + appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" + printCurrentLine + arg="${arg:${remainingLineLength}}" + # remove leading spaces + arg="${arg##[[:blank:]]}" + + set -- "${arg}" "$@" + else + # the arg can fit on next line + printCurrentLine + appendToCurrentLine "${arg}" "${argLength}" + fi + else + appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" + fi + done + if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then + printCurrentLine + fi + ) | sed -E -e 's/[[:blank:]]+$//' } -# @description Display message using debug color (gray) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayDebug() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then - Log::computeDuration - echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description check if command specified exists or return 1 +# with error and message if not +# +# @arg $1 commandName:String on which existence must be checked +# @arg $2 helpIfNotExists:String a help command to display if the command does not exist +# +# @exitcode 1 if the command specified does not exist +# @stderr diagnostic information + help if second argument is provided +Assert::commandExists() { + local commandName="$1" + local helpIfNotExists="$2" + + "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { + Log::displayError "${commandName} is not installed, please install it" + if [[ -n "${helpIfNotExists}" ]]; then + Log::displayInfo "${helpIfNotExists}" + fi + return 1 + } + return 0 +} + + +# @description check if tty (interactive mode) is active +# @noargs +# @exitcode 1 if tty not active +# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive +# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive +Assert::tty() { + if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then + return 1 fi - Log::logDebug "$1" + if [[ "${INTERACTIVE:-0}" = "1" ]]; then + return 0 + fi + tty -s } -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description ignore exit code 141 from simple command pipes +# @example use with: +# local resultingStatus=0 +# local -a originalPipeStatus=() +# cmd1 | cmd2 || Bash::handlePipelineFailure resultingStatus originalPipeStatus || true +# [[ "${resultingStatus}" = "0" ]] +# @arg $1 resultingStatusCode:&int (passed by reference) (optional) resulting status code +# @arg $2 originalStatus:int[] (passed by reference) (optional) copy of original PIPESTATUS array +# @env PIPESTATUS assuming that this function is called like in the example provided +# @see https://unix.stackexchange.com/a/709880/582856 +Bash::handlePipelineFailure() { + local -a pipeStatusBackup=("${PIPESTATUS[@]}") + local -n handlePipelineFailure_resultingStatusCode=$1 + local -n handlePipelineFailure_originalStatus=$2 + # shellcheck disable=SC2034 + handlePipelineFailure_originalStatus=("${pipeStatusBackup[@]}") + handlePipelineFailure_resultingStatusCode=0 + local statusCode + for statusCode in "${pipeStatusBackup[@]}"; do + if ((statusCode == 141)); then + return 0 + elif ((statusCode > 0)); then + # shellcheck disable=SC2034 + handlePipelineFailure_resultingStatusCode="${statusCode}" + break + fi + done + return "${handlePipelineFailure_resultingStatusCode}" +} + + +# @description loads ~/.bash-tools/.env if available +# if not creates it from a default template +# else check if new options need to be added +BashTools::Conf::requireLoad() { +Linux::requireTarCommand +Compiler::Embed::extractFileFromBase64 \ + "${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" \ + "IyEvdXNyL2Jpbi9lbnYgYmFzaAojIHNoZWxsY2hlY2sgZGlzYWJsZT1TQzIwMzQKCiMgRGVmYXVsdCBzZXR0aW5ncwojIHlvdSBjYW4gb3ZlcnJpZGUgdGhlc2Ugc2V0dGluZ3MgYnkgY3JlYXRpbmcgJHtIT01FfS8uYmFzaC10b29scy8uZW52IGZpbGUKCiMjIwojIyMgRElTUExBWSBMZXZlbAojIyMgbWluaW11bSBsZXZlbCBvZiB0aGUgbWVzc2FnZXMgdGhhdCB3aWxsIGJlIGRpc3BsYXllZCBvbiBzY3JlZW4KIyMjCiMjIyAwOiBOTyBMT0cKIyMjIDE6IEVSUk9SCiMjIyAyOiBXQVJOSU5HCiMjIyAzOiBJTkZPCiMjIyA0OiBERUJVRwojIyMKQkFTSF9GUkFNRVdPUktfRElTUExBWV9MRVZFTD0ke0JBU0hfRlJBTUVXT1JLX0RJU1BMQVlfTEVWRUw6LTN9CgojIyMKIyMjIERJU1BMQVkgZHVyYXRpb24KIyMjIDA6IG5vIGR1cmF0aW9uIGlzIGRpc3BsYXllZCBvbiB0aGUgbWVzc2FnZXMKIyMjIDE6IGR1cmF0aW9uIGJldHdlZW4gcHJldmlvdXMgbWVzc2FnZSBhbmQgY3VycmVudCBpcyBkaXNwbGF5ZWQKIyMjIHdpdGggdGhlIG1lc3NhZ2UKIyMjCkRJU1BMQVlfRFVSQVRJT049JHtESVNQTEFZX0RVUkFUSU9OOjB9CgojIyMKIyMjIExvZyB0byBmaWxlCiMjIwojIyMgYWxsIGxvZyBtZXNzYWdlcyB3aWxsIGJlIHJlZGlyZWN0ZWQgdG8gbG9nIGZpbGUgc3BlY2lmaWVkCiMjIyB0aGlzIHNhbWUgcGF0aCB3aWxsIGJlIHVzZWQgaW5zaWRlIGFuZCBvdXRzaWRlIG9mIHRoZSBjb250YWluZXIKIyMjCkJBU0hfRlJBTUVXT1JLX0xPR19GSUxFPSR7QkFTSF9GUkFNRVdPUktfTE9HX0ZJTEU6LSR7RlJBTUVXT1JLX1JPT1RfRElSfS9sb2dzL2Jhc2gubG9nfQoKIyMjCiMjIyBMT0cgTGV2ZWwKIyMjIG1pbmltdW0gbGV2ZWwgb2YgdGhlIG1lc3NhZ2VzIHRoYXQgd2lsbCBiZSBsb2dnZWQgaW50byBMT0dfRklMRQojIyMKIyMjIDA6IE5PIExPRwojIyMgMTogRVJST1IKIyMjIDI6IFdBUk5JTkcKIyMjIDM6IElORk8KIyMjIDQ6IERFQlVHCiMjIwpCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw9JHtCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw6LTB9CgojIGFic29sdXRlIGRpcmVjdG9yeSBjb250YWluaW5nIGRiIGltcG9ydCBzcWwgZHVtcHMKREJfSU1QT1JUX0RVTVBfRElSPSR7REJfSU1QT1JUX0RVTVBfRElSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2RiSW1wb3J0RHVtcHN9CgojIGdhcmJhZ2UgY29sbGVjdCBhbGwgZmlsZXMgZm9yIHdoaWNoIG1vZGlmaWNhdGlvbiBpcyBncmVhdGVyIHRoYW4gZWc6IDMwIGRheXMgKCszMCkKIyBlYWNoIHRpbWUgYW4gZXhpc3RpbmcgZmlsZSBpcyB1c2VkIGJ5IGRiSW1wb3J0L2RiSW1wb3J0VGFibGUKIyB0aGUgZmlsZSBtb2RpZmljYXRpb24gdGltZSBpcyBzZXQgdG8gbm93CkRCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUz0ke0RCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUzotKzMwfQoKIyBhYnNvbHV0ZSBkaXJlY3RvcnkgY29udGFpbmluZyBkYlNjcmlwdHMgdXNlZCBieSBkYlNjcmlwdEFsbERhdGFiYXNlcwpTQ1JJUFRTX0ZPTERFUj0ke1NDUklQVFNfRk9MREVSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2NvbmYvZGJTY3JpcHRzfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEFXUyBQYXJhbWV0ZXJzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KUzNfQkFTRV9VUkw9JHtTM19CQVNFX1VSTDotfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvc3RtYW4gUGFyYW1ldGVycwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClBPU1RNQU5fQVBJX0tFWT0K" \ + "755" + +declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" + + BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" + if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then + FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" + else + # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR + FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" fi - Log::logWarning "$1" + # shellcheck disable=SC2034 + FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" + # shellcheck disable=SC2034 + FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + + if [[ -f "${HOME}/.bash-tools/.env" ]]; then + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") + fi + + local envFile="${HOME}/.bash-tools/.env" + if [[ ! -f "${envFile}" ]]; then + mkdir -p "${HOME}/.bash-tools" + ( + echo "#!/usr/bin/env bash" + # shellcheck disable=SC2154 + echo "${embed_file_bashToolsDefaultConfigTemplate}" + ) >"${envFile}" + Log::displayInfo "Configuration file '${envFile}' created" + else + if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then + ( + echo '# -----------------------------------------------------' + echo '# Postman Parameters' + echo '# -----------------------------------------------------' + echo 'POSTMAN_API_KEY=' + ) >>"${envFile}" + fi + fi + # shellcheck source=/conf/.env + source "${envFile}" || { + Log::displayError "impossible to load '${envFile}'" + exit 1 + } } -# @description Display message using error color (red) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayError() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then - Log::computeDuration - echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description run command specified +# @arg $@ array:String[] the command to run +# @env optionInfoVerbose int - if 1 displays the command specified before running it +# @env optionRedirectCmdOutputs String - if set redirect command outputs to file specified +# @exitcode command's exit code +BashTools::runVerboseIfNeeded() { + # shellcheck disable=SC2154 + if [[ "${optionInfoVerbose}" = "1" ]]; then + echo >&2 "+ $*" fi - Log::logError "$1" + ( + # shellcheck disable=SC2154 + if [[ -n "${optionRedirectCmdOutputs:-}" ]]; then + exec >"${optionRedirectCmdOutputs:-}" + exec 2>"${optionRedirectCmdOutputs:-}" + fi + "$@" + ) } -# @description load colors theme constants -# @warning if tty not opened, noColor theme will be chosen -# @arg $1 theme:String the theme to use (default, noColor) -# @arg $@ args:String[] -# @set __ERROR_COLOR String indicate error status -# @set __INFO_COLOR String indicate info status -# @set __SUCCESS_COLOR String indicate success status -# @set __WARNING_COLOR String indicate warning status -# @set __SKIPPED_COLOR String indicate skipped status -# @set __DEBUG_COLOR String indicate debug status -# @set __HELP_COLOR String indicate help status -# @set __TEST_COLOR String not used -# @set __TEST_ERROR_COLOR String not used -# @set __HELP_TITLE_COLOR String used to display help title in help strings -# @set __HELP_OPTION_COLOR String used to display highlight options in help strings -# -# @set __RESET_COLOR String reset default color + +# @description convert base64 encoded back to target file +# if target file is executable prepend dir of target +# file to PATH to make binary available everywhere +# it is advised to include in the path of the target file +# the md5sum of the binFile # -# @set __HELP_EXAMPLE String to remove -# @set __HELP_TITLE String to remove -# @set __HELP_NORMAL String to remove -# shellcheck disable=SC2034 -UI::theme() { - local theme="${1-default}" - if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then - theme="noColor" +# @arg $1 targetFile:String the file to write +# @arg $2 binFileBase64:String the base64 encoded file +# @arg $3 fileMode:String the chmod to set on the file +# @set PATH String prepend target embedded file binary directory to PATH variable if binary executable +Compiler::Embed::extractFileFromBase64() { + local targetFile="$1" + local binFileBase64="$2" + local fileMode="${3:-+x}" + local targetDir="${targetFile%/*}" + + if [[ ! -f "${targetFile}" ]]; then + if [[ ! -d "${targetDir}" ]]; then + mkdir -p "${targetDir}" + fi + base64 -d >"${targetFile}" <<<"${binFileBase64}" + chmod "${fileMode}" "${targetFile}" fi - case "${theme}" in - default | default-force) - theme="default" - ;; - noColor) ;; - *) - Log::fatal "invalid theme provided" - ;; - esac - if [[ "${theme}" = "default" ]]; then - BASH_FRAMEWORK_THEME="default" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='\e[31m' # Red - __INFO_COLOR='\e[44m' # white on lightBlue - __SUCCESS_COLOR='\e[32m' # Green - __WARNING_COLOR='\e[33m' # Yellow - __SKIPPED_COLOR='\e[33m' # Yellow - __DEBUG_COLOR='\e[37m' # Gray - __HELP_COLOR='\e[7;49;33m' # Black on Gold - __TEST_COLOR='\e[100m' # Light magenta - __TEST_ERROR_COLOR='\e[41m' # white on red - __HELP_TITLE_COLOR="\e[1;37m" # Bold - __HELP_OPTION_COLOR="\e[1;34m" # Blue - # Internal: reset color - __RESET_COLOR='\e[0m' # Reset Color - # shellcheck disable=SC2155,SC2034 - __HELP_EXAMPLE="$(echo -e "\e[2;97m")" - # shellcheck disable=SC2155,SC2034 - __HELP_TITLE="$(echo -e "\e[1;37m")" - # shellcheck disable=SC2155,SC2034 - __HELP_NORMAL="$(echo -e "\033[0m")" - else - BASH_FRAMEWORK_THEME="noColor" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='' - __INFO_COLOR='' - __SUCCESS_COLOR='' - __WARNING_COLOR='' - __SKIPPED_COLOR='' - __DEBUG_COLOR='' - __HELP_COLOR='' - __TEST_COLOR='' - __TEST_ERROR_COLOR='' - __HELP_TITLE_COLOR='' - __HELP_OPTION_COLOR='' - # Internal: reset color - __RESET_COLOR='' - __HELP_EXAMPLE='' - __HELP_TITLE='' - __HELP_NORMAL='' + + if [[ -x "${targetFile}" ]]; then + Env::pathPrepend "${targetDir}" fi } -# @description draw a line with the character passed in parameter repeated depending on terminal width -# @arg $1 character:String character to use as separator (default value #) -UI::drawLine() { - local character="${1:-#}" - local -i width=${COLUMNS:-0} - if ((width == 0)) && [[ -t 1 ]]; then - width=$(tput cols) - fi - if ((width == 0)); then - width=80 + +# @description check if all requirements are satisfied +# to execute dbImport commands +Db::checkRequirements() { + if [[ "${SKIP_REQUIREMENTS_CHECKS:-0}" = "1" ]]; then + return 0 fi - printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") + local -i failures=0 echo + Assert::commandExists mysql "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqldump "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists pv "sudo apt-get install -y pv" || ((++failures)) + Assert::commandExists gawk "sudo apt-get install -y gawk" || ((++failures)) + Assert::commandExists awk "sudo apt-get install -y gawk" || ((++failures)) + Version::checkMinimal "gawk" "--version" "5.0.1" || ((++failures)) + return "${failures}" } -# @description Display message using error color (red) and exit immediately with error status 1 -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::fatal() { - Log::computeDuration - echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - Log::logFatal "$1" - exit 1 -} -# @description create a temp file using default TMPDIR variable -# initialized in _includes/_commonHeader.sh -# @env TMPDIR String (default value /tmp) -# @arg $1 templateName:String template name to use(optional) -Framework::createTempFile() { - mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" +# @description prepend directories to the PATH environment variable +# @arg $@ args:String[] list of directories to prepend +# @set PATH update PATH with the directories prepended +Env::pathPrepend() { + local arg + for arg in "$@"; do + if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then + PATH="$(realpath "${arg}"):${PATH}" + fi + done } + # @description ensure env files are loaded # @arg $@ list of default files to load at the end # @exitcode 1 if one of env files fails to load # @stderr diagnostics information is displayed # shellcheck disable=SC2120 Env::requireLoad() { + export REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 + local -a defaultFiles=("$@") # get list of possible config files local -a configFiles=() @@ -328,381 +516,137 @@ Env::requireLoad() { done } -# @description activate or not Log::display* and Log::log* functions -# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL -# environment variables loaded by Env::requireLoad -# try to create log file and rotate it if necessary -# @noargs -# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable -# @env BASH_FRAMEWORK_DISPLAY_LEVEL int -# @env BASH_FRAMEWORK_LOG_LEVEL int -# @env BASH_FRAMEWORK_LOG_FILE String -# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 -# @exitcode 0 always successful -# @stderr diagnostics information about log file is displayed -# @require Env::requireLoad -# @require UI::requireTheme -Log::requireLoad() { - if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - export BASH_FRAMEWORK_LOG_LEVEL - fi - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then - if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 - fi - elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - fi +# @description create a temp file using default TMPDIR variable +# @env TMPDIR String (default value /tmp) +# @arg $1 templateName:String template name to use(optional) +Framework::createTempFile() { + mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" +} - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - # will always be created even if not in info level - Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" - if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then - Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" - fi + +# @description ensure running user is not root +# @exitcode 1 if current user is root +# @stderr diagnostics information is displayed +Linux::requireExecutedAsUser() { + if [[ "$(id -u)" = "0" ]]; then + Log::fatal "this script should be executed as normal user" fi } -# @description concatenate each element of an array with a separator -# but wrapping text when line length is more than provided argument -# The algorithm will try not to cut the array element if it can. -# - if an arg can be placed on current line it will be, -# otherwise current line is printed and arg is added to the new -# current line -# - Empty arg is interpreted as a new line. -# - Add \r to arg in order to force break line and avoid following -# arg to be concatenated with current arg. -# -# @arg $1 glue:String -# @arg $2 maxLineLength:int -# @arg $3 indentNextLine:int -# @arg $@ array:String[] -Array::wrap2() { - local glue="${1-}" - local -i glueLength="${#glue}" - shift || true - local -i maxLineLength=$1 - shift || true - local -i indentNextLine=$1 - shift || true - local indentStr="" - if ((indentNextLine > 0)); then - indentStr="$(head -c "${indentNextLine}" 0)); do - arg="$1" - shift || true - - # replace tab by 2 spaces - arg="${arg//$'\t'/ }" - # remove trailing spaces - arg="${arg%[[:blank:]]}" - if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then - printCurrentLine - ((previousLineEmpty = 1)) - continue - else - if ((previousLineEmpty == 1)); then - printCurrentLine - fi - ((previousLineEmpty = 0)) || true - fi - # convert eol to args - mapfile -t additionalLines <<<"${arg}" - if ((${#additionalLines[@]} > 1)); then - set -- "${additionalLines[@]}" "$@" - continue - fi - - ((argLength = ${#arg})) || true - - # empty arg - if ((argLength == 0)); then - if ((isNewline == 0)); then - # isNewline = 0 means currentLine is not empty - printCurrentLine - fi - continue - fi - - if ((isNewline == 0)); then - glueLength="${#glue}" - else - glueLength="0" - fi - if ((currentLineLength + argLength + glueLength > maxLineLength)); then - if ((argLength + glueLength > maxLineLength)); then - # arg is too long to even fit on one line - # we have to split the arg on current and next line - local -i remainingLineLength - ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) - appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" - printCurrentLine - arg="${arg:${remainingLineLength}}" - # remove leading spaces - arg="${arg##[[:blank:]]}" - - set -- "${arg}" "$@" - else - # the arg can fit on next line - printCurrentLine - appendToCurrentLine "${arg}" "${argLength}" - fi - else - appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" - fi - done - if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then - printCurrentLine - fi - ) | sed -E -e 's/[[:blank:]]+$//' -} - -# @description check if tty (interactive mode) is active -# @noargs -# @exitcode 1 if tty not active -# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive -# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive -Assert::tty() { - if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then - return 1 - fi - if [[ "${INTERACTIVE:-0}" = "1" ]]; then - return 0 +# @description Display message using debug color (gray) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayDebug() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then + Log::computeDuration + echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi - tty -s -} - -# @description Ask user to enter y or n, retry until answer is correct -# @arg $1 message:String message to display before asking -# @stdout displays message
[msg arg $1] (y or n)?
-# @stdout if characters entered different than [yYnN] displays "Invalid answer" and continue to ask -# @exitcode 0 if yes -# @exitcode 1 else -UI::askYesNo() { - while true; do - read -p "$1 (y or n)? " -n 1 -r - echo # move to a new line - case ${REPLY} in - [yY]) return 0 ;; - [nN]) return 1 ;; - *) - read -r -N 10000000 -t '0.01' || true # empty stdin in case of control characters - # \\r to go back to the beginning of the line - Log::displayError "\\r invalid answer " - ;; - esac - done + Log::logDebug "$1" } -bashToolsDefaultConfigTemplate="${bashToolsDefaultConfigTemplate:-$( - cat <<'EOF' -# shellcheck disable=SC2034 - -# Default settings -# you can override these settings by creating ${HOME}/.bash-tools/.env file - -### -### DISPLAY Level -### minimum level of the messages that will be displayed on screen -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} - -### -### DISPLAY duration -### 0: no duration is displayed on the messages -### 1: duration between previous message and current is displayed -### with the message -### -DISPLAY_DURATION=${DISPLAY_DURATION:0} - -### -### Log to file -### -### all log messages will be redirected to log file specified -### this same path will be used inside and outside of the container -### -BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} - -### -### LOG Level -### minimum level of the messages that will be logged into LOG_FILE -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} - -# absolute directory containing db import sql dumps -DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} - -# garbage collect all files for which modification is greater than eg: 30 days (+30) -# each time an existing file is used by dbImport/dbImportTable -# the file modification time is set to now -DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} - -# absolute directory containing dbScripts used by dbScriptAllDatabases -SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} - -# ----------------------------------------------------- -# AWS Parameters -# ----------------------------------------------------- -S3_BASE_URL=${S3_BASE_URL:-} - -# ----------------------------------------------------- -# Postman Parameters -# ----------------------------------------------------- -POSTMAN_API_KEY= -EOF -)}" -# @description loads ~/.bash-tools/.env if available -# if not creates it from a default template -# else check if new options need to be added -BashTools::Conf::requireLoad() { - local envFile="${HOME}/.bash-tools/.env" - if [[ ! -f "${envFile}" ]]; then - mkdir -p "${HOME}/.bash-tools" - ( - echo "#!/usr/bin/env bash" - echo "${bashToolsDefaultConfigTemplate}" - ) >"${envFile}" - Log::displayInfo "Configuration file '${envFile}' created" - else - if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then - ( - echo '# -----------------------------------------------------' - echo '# Postman Parameters' - echo '# -----------------------------------------------------' - echo 'POSTMAN_API_KEY=' - ) >>"${envFile}" - fi +# @description Display message using error color (red) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayError() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then + Log::computeDuration + echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi - # shellcheck source=/conf/.env - source "${envFile}" || { - Log::displayError "impossible to load '${envFile}'" - exit 1 - } + Log::logError "$1" } -# @description ensure COMMAND_BIN_DIR env var is set -# and PATH correctly prepared -# @noargs -# @set COMMAND_BIN_DIR string the directory where to find this command -# @set PATH string add directory where to find this command binary -Compiler::Facade::requireCommandBinDir() { - COMMAND_BIN_DIR="${CURRENT_DIR}" - Env::pathPrepend "${COMMAND_BIN_DIR}" -} -# @description ensure running user is not root -# @exitcode 1 if current user is root -# @stderr diagnostics information is displayed -Linux::requireExecutedAsUser() { - if [[ "$(id -u)" = "0" ]]; then - Log::fatal "this script should be executed as normal user" +# @description Display message using info color (bg light blue/fg white) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayInfo() { + local type="${2:-INFO}" + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logInfo "$1" "${type}" } -declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR -FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" -LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" -LOG_LAST_LOG_DATE_INIT=1 -LOG_LAST_DURATION_STR="" -# @description compute duration since last call to this function -# the result is set in following env variables. -# in ss.sss (seconds followed by milliseconds precision 3 decimals) -# @noargs +# @description Display message using warning color (yellow) +# @arg $1 message:String the message to display # @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @set LOG_LAST_LOG_DATE_INIT int (default 1) set to 0 at first call, allows to detect reference log -# @set LOG_LAST_DURATION_STR String the last duration displayed -# @set LOG_LAST_LOG_DATE String the last log date that will be used to compute next diff -Log::computeDuration() { - if ((${DISPLAY_DURATION:-0} == 1)); then - local -i duration=0 - local -i delta=0 - local -i currentLogDate - currentLogDate="${EPOCHREALTIME/[^0-9]/}" - if ((LOG_LAST_LOG_DATE_INIT == 1)); then - LOG_LAST_LOG_DATE_INIT=0 - LOG_LAST_DURATION_STR="Ref" - else - duration=$(((currentLogDate - FIRST_LOG_DATE) / 1000000)) - delta=$(((currentLogDate - LOG_LAST_LOG_DATE) / 1000000)) - LOG_LAST_DURATION_STR="${duration}s/+${delta}s" - fi - LOG_LAST_LOG_DATE="${currentLogDate}" - # shellcheck disable=SC2034 - local microSeconds="${EPOCHREALTIME#*.}" - LOG_LAST_DURATION_STR="$(printf '%(%T)T.%03.0f\n' "${EPOCHSECONDS}" "${microSeconds:0:3}")(${LOG_LAST_DURATION_STR}) - " - else - # shellcheck disable=SC2034 - LOG_LAST_DURATION_STR="" +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayWarning() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then + Log::computeDuration + echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logWarning "$1" } -# @description log message to file + +# @description Display message using error color (red) and exit immediately with error status 1 # @arg $1 message:String the message to display -Log::logInfo() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then - Log::logMessage "${2:-INFO}" "$1" - fi +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::fatal() { + Log::computeDuration + echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + Log::logFatal "$1" + exit 1 } + # @description log message to file # @arg $1 message:String the message to display Log::logDebug() { @@ -711,13 +655,6 @@ Log::logDebug() { fi } -# @description log message to file -# @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" - fi -} # @description log message to file # @arg $1 message:String the message to display @@ -727,12 +664,23 @@ Log::logError() { fi } + # @description log message to file # @arg $1 message:String the message to display Log::logFatal() { Log::logMessage "${2:-FATAL}" "$1" } + +# @description log message to file +# @arg $1 message:String the message to display +Log::logInfo() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then + Log::logMessage "${2:-INFO}" "$1" + fi +} + + # @description Internal: common log message # @example text # [date]|[levelMsg]|message @@ -746,9 +694,18 @@ Log::logFatal() { # @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty # @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages # @stderr diagnostics information is displayed -# @require Env::requireLoad -# @require Log::requireLoad Log::logMessage() { + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Log::requireLoad has not been loaded" + exit 1 + fi + local levelMsg="$1" local msg="$2" local date @@ -760,14 +717,82 @@ Log::logMessage() { fi } -# @description To be called before logging in the log file -# @arg $1 file:string log file name -# @arg $2 maxLogFilesCount:int maximum number of log files -Log::rotate() { - local file="$1" - local maxLogFilesCount="${2:-5}" - if [[ ! -f "${file}" ]]; then +# @description log message to file +# @arg $1 message:String the message to display +Log::logWarning() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then + Log::logMessage "${2:-WARNING}" "$1" + fi +} + + +# @description activate or not Log::display* and Log::log* functions +# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL +# environment variables loaded by Env::requireLoad +# try to create log file and rotate it if necessary +# @noargs +# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable +# @env BASH_FRAMEWORK_DISPLAY_LEVEL int +# @env BASH_FRAMEWORK_LOG_LEVEL int +# @env BASH_FRAMEWORK_LOG_FILE String +# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 +# @exitcode 0 always successful +# @stderr diagnostics information about log file is displayed +Log::requireLoad() { + export REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement UI::requireTheme has not been loaded" + exit 1 + fi + + if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + export BASH_FRAMEWORK_LOG_LEVEL + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then + if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 + fi + elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + # will always be created even if not in info level + Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" + if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then + Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + fi + fi +} + + +# @description To be called before logging in the log file +# @arg $1 file:string log file name +# @arg $2 maxLogFilesCount:int maximum number of log files +Log::rotate() { + local file="$1" + local maxLogFilesCount="${2:-5}" + + if [[ ! -f "${file}" ]]; then Log::displayDebug "Log file ${file} doesn't exist yet" return 0 fi @@ -782,76 +807,245 @@ Log::rotate() { fi } -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" - fi + +# @description Ask user to enter y or n, retry until answer is correct +# @arg $1 message:String message to display before asking +# @stdout displays message
[msg arg $1] (y or n)?
+# @stdout if characters entered different than [yYnN] displays "Invalid answer" and continue to ask +# @exitcode 0 if yes +# @exitcode 1 else +UI::askYesNo() { + while true; do + read -p "$1 (y or n)? " -n 1 -r + echo # move to a new line + case ${REPLY} in + [yY]) return 0 ;; + [nN]) return 1 ;; + *) + read -r -N 10000000 -t '0.01' || true # empty stdin in case of control characters + # \\r to go back to the beginning of the line + Log::displayError "\\r invalid answer " + ;; + esac done } + +# @description draw a line with the character passed in parameter repeated depending on terminal width +# @arg $1 character:String character to use as separator (default value #) +UI::drawLine() { + local character="${1:-#}" + local -i width=${COLUMNS:-0} + if ((width == 0)) && [[ -t 1 ]]; then + width=$(tput cols) + fi + if ((width == 0)); then + width=80 + fi + printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") + echo +} + + # @description load color theme # @noargs # @env BASH_FRAMEWORK_THEME String theme to use # @env LOAD_THEME int 0 to avoid loading theme # @exitcode 0 always successful UI::requireTheme() { + export REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + if [[ "${LOAD_THEME:-1}" = "1" ]]; then UI::theme "${BASH_FRAMEWORK_THEME-default}" fi } -# FUNCTIONS - -facade_main_gitRenameBranchsh() { -BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" -if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then - FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" -else - # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR - FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" -fi -FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" -FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" -# @require BashTools::Conf::requireLoad -if [[ -f "${HOME}/.bash-tools/.env" ]]; then - export BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") -fi -# REQUIRES -Env::requireLoad -UI::requireTheme -Log::requireLoad -BashTools::Conf::requireLoad -Compiler::Facade::requireCommandBinDir -Linux::requireExecutedAsUser - -# @require Compiler::Facade::requireCommandBinDir +# @description load colors theme constants +# @warning if tty not opened, noColor theme will be chosen +# @arg $1 theme:String the theme to use (default, noColor) +# @arg $@ args:String[] +# @set __ERROR_COLOR String indicate error status +# @set __INFO_COLOR String indicate info status +# @set __SUCCESS_COLOR String indicate success status +# @set __WARNING_COLOR String indicate warning status +# @set __SKIPPED_COLOR String indicate skipped status +# @set __DEBUG_COLOR String indicate debug status +# @set __HELP_COLOR String indicate help status +# @set __TEST_COLOR String not used +# @set __TEST_ERROR_COLOR String not used +# @set __HELP_TITLE_COLOR String used to display help title in help strings +# @set __HELP_OPTION_COLOR String used to display highlight options in help strings +# +# @set __RESET_COLOR String reset default color +# +# @set __HELP_EXAMPLE String to remove +# @set __HELP_TITLE String to remove +# @set __HELP_NORMAL String to remove # shellcheck disable=SC2034 +UI::theme() { + local theme="${1-default}" + if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then + theme="noColor" + fi + case "${theme}" in + default | default-force) + theme="default" + ;; + noColor) ;; + *) + Log::fatal "invalid theme provided" + ;; + esac + if [[ "${theme}" = "default" ]]; then + BASH_FRAMEWORK_THEME="default" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='\e[31m' # Red + __INFO_COLOR='\e[44m' # white on lightBlue + __SUCCESS_COLOR='\e[32m' # Green + __WARNING_COLOR='\e[33m' # Yellow + __SKIPPED_COLOR='\e[33m' # Yellow + __DEBUG_COLOR='\e[37m' # Gray + __HELP_COLOR='\e[7;49;33m' # Black on Gold + __TEST_COLOR='\e[100m' # Light magenta + __TEST_ERROR_COLOR='\e[41m' # white on red + __HELP_TITLE_COLOR="\e[1;37m" # Bold + __HELP_OPTION_COLOR="\e[1;34m" # Blue + # Internal: reset color + __RESET_COLOR='\e[0m' # Reset Color + # shellcheck disable=SC2155,SC2034 + __HELP_EXAMPLE="$(echo -e "\e[2;97m")" + # shellcheck disable=SC2155,SC2034 + __HELP_TITLE="$(echo -e "\e[1;37m")" + # shellcheck disable=SC2155,SC2034 + __HELP_NORMAL="$(echo -e "\033[0m")" + else + BASH_FRAMEWORK_THEME="noColor" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='' + __INFO_COLOR='' + __SUCCESS_COLOR='' + __WARNING_COLOR='' + __SKIPPED_COLOR='' + __DEBUG_COLOR='' + __HELP_COLOR='' + __TEST_COLOR='' + __TEST_ERROR_COLOR='' + __HELP_TITLE_COLOR='' + __HELP_OPTION_COLOR='' + # Internal: reset color + __RESET_COLOR='' + __HELP_EXAMPLE='' + __HELP_TITLE='' + __HELP_NORMAL='' + fi +} -# variables values -declare optionPush="0" -declare optionDelete="0" -declare optionAssumeYes="0" -declare newBranchNameArg="" -declare oldBranchNameArg="" -# other values -declare copyrightBeginYear="2020" +# @description Check that command version is greater than expected minimal version +# display warning if command version greater than expected minimal version +# display error if command version less than expected minimal version and exit 1 +# @arg $1 commandName:String command path +# @arg $2 argVersion:String command line parameters to launch to get command version +# @arg $3 minimalVersion:String expected minimal command version +# @arg $4 parseVersionCallback:Function +# @arg $5 help:String optional help message to display if command does not exist +# @exitcode 0 if command version greater or equal to expected minimal version +# @exitcode 1 if command version less than expected minimal version +# @exitcode 2 if command does not exist +Version::checkMinimal() { + local commandName="$1" + local argVersion="$2" + local minimalVersion="$3" + local parseVersionCallback=${4:-Version::parse} + local help="${5:-}" + + Assert::commandExists "${commandName}" "${help}" || return 2 + + # shellcheck disable=SC2034 + local status=0 + # shellcheck disable=SC2034 + local -a pipeStatus=() + local version + version="$("${commandName}" "${argVersion}" 2>&1 | ${parseVersionCallback} || Bash::handlePipelineFailure status pipeStatus)" + + Log::displayDebug "check ${commandName} version ${version} against minimal ${minimalVersion}" + + Version::compare "${version}" "${minimalVersion}" || { + local result=$? + if [[ "${result}" = "1" ]]; then + Log::displayInfo "${commandName} version is ${version} greater than ${minimalVersion}" + elif [[ "${result}" = "2" ]]; then + Log::displayError "${commandName} minimal version is ${minimalVersion}, your version is ${version}" + return 1 + fi + return 0 + } + +} + + +# @description compare 2 version numbers +# @arg $1 version1:String version 1 +# @arg $2 version2:String version 2 +# @exitcode 0 if equal +# @exitcode 1 if version1 > version2 +# @exitcode 2 else +Version::compare() { + if [[ "$1" = "$2" ]]; then + return 0 + fi + local IFS=. + # shellcheck disable=2206 + local i ver1=($1) ver2=($2) + # fill empty fields in ver1 with zeros + for ((i = ${#ver1[@]}; i < ${#ver2[@]}; i++)); do + ver1[i]=0 + done + for ((i = 0; i < ${#ver1[@]}; i++)); do + if [[ -z "${ver2[i]+unset}" ]] || [[ -z ${ver2[i]} ]]; then + # fill empty fields in ver2 with zeros + ver2[i]=0 + fi + if ((10#${ver1[i]} > 10#${ver2[i]})); then + return 1 + fi + if ((10#${ver1[i]} < 10#${ver2[i]})); then + return 2 + fi + done + return 0 +} + + +# @description filter to keep only version number from a string +# @arg $@ files:String[] the files to filter +# @exitcode * if one of the filter command fails +# @stdin you can use stdin as alternative to files argument +# @stdout the filtered content +# shellcheck disable=SC2120 +Version::parse() { + # match anything, print(p), exit on first match(Q) + sed -En \ + -e 's/\x1b\[[0-9;]*[mGKHF]//g' \ + -e 's/[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/' \ + -e '//{p;Q}' \ + "$@" +} +# FUNCTIONS + declare -a BASH_FRAMEWORK_ARGV_FILTERED=() +beforeParseCallback() { + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + copyrightCallback() { - if [[ -z "${copyrightBeginYear}" ]]; then - copyrightBeginYear="$(date +%Y)" - fi - echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" + # + echo "Copyright (c) 2020-now François Chastanet" } # shellcheck disable=SC2317 # if function is overridden @@ -883,13 +1077,14 @@ updateArgListQuietCallback() { :; } # shellcheck disable=SC2317 # if function is overridden optionHelpCallback() { - gitRenameBranchCommand help + Log::displayError "optionHelpCallback needs to be overridden" exit 0 } # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { - echo "${SCRIPT_NAME} version 1.0" + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version 3.0" exit 0 } @@ -907,21 +1102,22 @@ optionEnvFileCallback() { optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_INFO} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionDebugVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_DEBUG} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionTraceVerboseCallback() { + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vvv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_TRACE} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } getLevel() { @@ -945,6 +1141,7 @@ getLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -966,6 +1163,7 @@ getVerboseLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -976,7 +1174,7 @@ optionDisplayLevelCallback() { logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -985,19 +1183,20 @@ optionLogLevelCallback() { local logLevel verboseLevel logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionLogFileCallback() { local logFile="$2" - echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionQuietCallback() { - echo "BASH_FRAMEWORK_QUIET_MODE=1" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_QUIET_MODE=1" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1028,6 +1227,7 @@ optionBashFrameworkConfigCallback() { defaultFrameworkConfig="$( cat <<'EOF' + # copied from src/_includes/.framework-config.default # shellcheck disable=SC2034 @@ -1045,7 +1245,7 @@ NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-conf # describe the files that are allowed to not have an associated bats file BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" # describe the files that are allowed to not have a function matching the filename -FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|\.tpl$|/testsData/|^manualTests/|\.bats$}" +FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|/testsData/|^manualTests/|\.bats$}" # Source directories if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then FRAMEWORK_SRC_DIRS=( @@ -1061,6 +1261,7 @@ BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" + EOF )" @@ -1069,7 +1270,7 @@ overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" commandOptionParseFinished() { # load default template framework config defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" - echo "${defaultFrameworkConfig}" > "${defaultEnvFile}" + echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") if [[ -f "${envFile}" ]]; then files+=("${envFile}") @@ -1087,593 +1288,856 @@ commandOptionParseFinished() { fi } -gitRenameBranchCommand() { - local options_parse_cmd="$1" - shift || true - if [[ "${options_parse_cmd}" = "parse" ]]; then - optionAssumeYes="0" - local -i options_parse_optionParsedCountOptionAssumeYes - ((options_parse_optionParsedCountOptionAssumeYes = 0)) || true - optionPush="0" - local -i options_parse_optionParsedCountOptionPush - ((options_parse_optionParsedCountOptionPush = 0)) || true - optionDelete="0" - local -i options_parse_optionParsedCountOptionDelete - ((options_parse_optionParsedCountOptionDelete = 0)) || true - local -i options_parse_optionParsedCountOptionBashFrameworkConfig - ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true - optionConfig="0" - local -i options_parse_optionParsedCountOptionConfig - ((options_parse_optionParsedCountOptionConfig = 0)) || true - optionInfoVerbose="0" - local -i options_parse_optionParsedCountOptionInfoVerbose - ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true - optionDebugVerbose="0" - local -i options_parse_optionParsedCountOptionDebugVerbose - ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true - optionTraceVerbose="0" - local -i options_parse_optionParsedCountOptionTraceVerbose - ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionNoColor="0" - local -i options_parse_optionParsedCountOptionNoColor - ((options_parse_optionParsedCountOptionNoColor = 0)) || true - optionTheme="default" - local -i options_parse_optionParsedCountOptionTheme - ((options_parse_optionParsedCountOptionTheme = 0)) || true - optionHelp="0" - local -i options_parse_optionParsedCountOptionHelp - ((options_parse_optionParsedCountOptionHelp = 0)) || true - optionVersion="0" - local -i options_parse_optionParsedCountOptionVersion - ((options_parse_optionParsedCountOptionVersion = 0)) || true - optionQuiet="0" - local -i options_parse_optionParsedCountOptionQuiet - ((options_parse_optionParsedCountOptionQuiet = 0)) || true - local -i options_parse_optionParsedCountOptionLogLevel - ((options_parse_optionParsedCountOptionLogLevel = 0)) || true - local -i options_parse_optionParsedCountOptionLogFile - ((options_parse_optionParsedCountOptionLogFile = 0)) || true - local -i options_parse_optionParsedCountOptionDisplayLevel - ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true - local -i options_parse_argParsedCountNewBranchNameArg - ((options_parse_argParsedCountNewBranchNameArg = 0)) || true - local -i options_parse_argParsedCountOldBranchNameArg - ((options_parse_argParsedCountOldBranchNameArg = 0)) || true - # shellcheck disable=SC2034 - local -i options_parse_parsedArgIndex=0 - while (($# > 0)); do - local options_parse_arg="$1" - local argOptDefaultBehavior=0 - case "${options_parse_arg}" in - # Option 1/17 - # Option optionAssumeYes --assume-yes|--yes|-y variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --assume-yes | --yes | -y) - # shellcheck disable=SC2034 - optionAssumeYes="1" - if ((options_parse_optionParsedCountOptionAssumeYes >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionAssumeYes)) - ;; - # Option 2/17 - # Option optionPush --push|-p variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --push | -p) - # shellcheck disable=SC2034 - optionPush="1" - if ((options_parse_optionParsedCountOptionPush >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionPush)) - ;; - # Option 3/17 - # Option optionDelete --delete|-d variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --delete | -d) - # shellcheck disable=SC2034 - optionDelete="1" - if ((options_parse_optionParsedCountOptionDelete >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDelete)) - ;; - # Option 4/17 - # Option optionBashFrameworkConfig --bash-framework-config variableType String min 0 max 1 authorizedValues '' regexp '' - --bash-framework-config) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) - # shellcheck disable=SC2034 - optionBashFrameworkConfig="$1" - optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" - ;; - # Option 5/17 - # Option optionConfig --config variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --config) - # shellcheck disable=SC2034 - optionConfig="1" - if ((options_parse_optionParsedCountOptionConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionConfig)) - ;; - # Option 6/17 - # Option optionInfoVerbose --verbose|-v variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --verbose | -v) - # shellcheck disable=SC2034 - optionInfoVerbose="1" - if ((options_parse_optionParsedCountOptionInfoVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionInfoVerbose)) - optionInfoVerboseCallback "${options_parse_arg}" - updateArgListInfoVerboseCallback "${options_parse_arg}" - ;; - # Option 7/17 - # Option optionDebugVerbose -vv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vv) - # shellcheck disable=SC2034 - optionDebugVerbose="1" - if ((options_parse_optionParsedCountOptionDebugVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDebugVerbose)) - optionDebugVerboseCallback "${options_parse_arg}" - updateArgListDebugVerboseCallback "${options_parse_arg}" - ;; - # Option 8/17 - # Option optionTraceVerbose -vvv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vvv) - # shellcheck disable=SC2034 - optionTraceVerbose="1" - if ((options_parse_optionParsedCountOptionTraceVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTraceVerbose)) - optionTraceVerboseCallback "${options_parse_arg}" - updateArgListTraceVerboseCallback "${options_parse_arg}" - ;; - # Option 9/17 - # Option optionEnvFiles --env-file variableType StringArray min 0 max -1 authorizedValues '' regexp '' - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - ;; - # Option 10/17 - # Option optionNoColor --no-color variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --no-color) - # shellcheck disable=SC2034 - optionNoColor="1" - if ((options_parse_optionParsedCountOptionNoColor >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionNoColor)) - optionNoColorCallback "${options_parse_arg}" - updateArgListNoColorCallback "${options_parse_arg}" - ;; - # Option 11/17 - # Option optionTheme --theme variableType String min 0 max 1 authorizedValues 'default|default-force|noColor' regexp '' - --theme) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default|default-force|noColor)" - return 1 - fi - if ((options_parse_optionParsedCountOptionTheme >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTheme)) - # shellcheck disable=SC2034 - optionTheme="$1" - optionThemeCallback "${options_parse_arg}" "${optionTheme}" - updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" - ;; - # Option 12/17 - # Option optionHelp --help|-h variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --help | -h) - # shellcheck disable=SC2034 - optionHelp="1" - if ((options_parse_optionParsedCountOptionHelp >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionHelp)) - optionHelpCallback "${options_parse_arg}" - ;; - # Option 13/17 - # Option optionVersion --version variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --version) - # shellcheck disable=SC2034 - optionVersion="1" - if ((options_parse_optionParsedCountOptionVersion >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionVersion)) - optionVersionCallback "${options_parse_arg}" - ;; - # Option 14/17 - # Option optionQuiet --quiet|-q variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --quiet | -q) - # shellcheck disable=SC2034 - optionQuiet="1" - if ((options_parse_optionParsedCountOptionQuiet >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionQuiet)) - optionQuietCallback "${options_parse_arg}" - updateArgListQuietCallback "${options_parse_arg}" - ;; - # Option 15/17 - # Option optionLogLevel --log-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --log-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogLevel)) - # shellcheck disable=SC2034 - optionLogLevel="$1" - optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - ;; - # Option 16/17 - # Option optionLogFile --log-file variableType String min 0 max 1 authorizedValues '' regexp '' - --log-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogFile >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + +beforeParseCallback() { + Linux::requireExecutedAsUser + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + +declare optionRedirectCmdOutputs="" +optionRedirectCmdOutputs() { + export optionTraceVerbose + # shellcheck disable=SC2154 + if [[ "${optionTraceVerbose}" != "1" ]]; then + # shellcheck disable=SC2034 # used by BashTools::runVerboseIfNeeded + optionRedirectCmdOutputs="/dev/null" + fi +} + + + +longDescriptionFunction() { + echo -e " ${__HELP_TITLE}EXIT CODES:${__HELP_NORMAL}" + echo -e " ${__HELP_OPTION_COLOR}1${__HELP_NORMAL} : if current directory is not a git repository" + echo -e " or if invalid or missing arguments" + echo -e " ${__HELP_OPTION_COLOR}2${__HELP_NORMAL} : if impossible to compute current branch name" + echo -e " ${__HELP_OPTION_COLOR}3${__HELP_NORMAL} : master/main branch not supported by this command," + echo -e " please do it manually" + echo -e " ${__HELP_OPTION_COLOR}5${__HELP_NORMAL} : New and old branch names are the same" + echo -e " ${__HELP_OPTION_COLOR}6${__HELP_NORMAL} : You can use this tool in non interactive mode only" + echo -e " if --assume-yes option is provided" + echo -e " ${__HELP_OPTION_COLOR}7${__HELP_NORMAL} : if failed to rename local branch" + echo -e " ${__HELP_OPTION_COLOR}8${__HELP_NORMAL} : if remote branch deletion failed" + echo -e " ${__HELP_OPTION_COLOR}9${__HELP_NORMAL} : if failed to push the new branch" + echo +} + +optionHelpCallback() { + gitRenameBranchCommandHelp + exit 0 +} + +assumeYesHelpFunction() { + echo " Do not ask for confirmation (use with caution)." + echo ' Automatic yes to prompts; assume "y" as answer to all prompts' + echo ' and run non-interactively.' +} + +commandCallback() { + # shellcheck disable=SC2154 + if ! Assert::tty && [[ "${optionAssumeYes}" != "1" ]]; then + Log::displayError "You can use this tool in non interactive mode only if --assume-yes option is provided" + exit 6 + fi +} + + + +optionVersionCallback() { + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version 3.0" + Db::checkRequirements + exit 0 +} + + +# ------------------------------------------ +# Command gitRenameBranchCommand +# ------------------------------------------ + +# options variables initialization +declare optionHelp="0" +declare optionConfig="0" +declare optionBashFrameworkConfig="" +declare optionInfoVerbose="0" +declare optionDebugVerbose="0" +declare optionTraceVerbose="0" +declare -a optionEnvFiles=() +declare optionLogLevel="" +declare optionLogFile="" +declare optionDisplayLevel="" +declare optionNoColor="0" +declare optionTheme="default" +declare optionVersion="0" +declare optionQuiet="0" +declare optionAssumeYes="0" +declare optionPush="0" +declare optionDelete="0" +# arguments variables initialization +declare newBranchNameArg="" +declare oldBranchNameArg="" +# @description parse command options and arguments for gitRenameBranchCommand +gitRenameBranchCommandParse() { + Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" + Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" + optionHelp="0" + local -i options_parse_optionParsedCountOptionHelp + ((options_parse_optionParsedCountOptionHelp = 0)) || true + optionConfig="0" + local -i options_parse_optionParsedCountOptionConfig + ((options_parse_optionParsedCountOptionConfig = 0)) || true + optionBashFrameworkConfig="" + local -i options_parse_optionParsedCountOptionBashFrameworkConfig + ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true + optionInfoVerbose="0" + local -i options_parse_optionParsedCountOptionInfoVerbose + ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true + optionDebugVerbose="0" + local -i options_parse_optionParsedCountOptionDebugVerbose + ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true + optionTraceVerbose="0" + local -i options_parse_optionParsedCountOptionTraceVerbose + ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true + + optionLogLevel="" + local -i options_parse_optionParsedCountOptionLogLevel + ((options_parse_optionParsedCountOptionLogLevel = 0)) || true + optionLogFile="" + local -i options_parse_optionParsedCountOptionLogFile + ((options_parse_optionParsedCountOptionLogFile = 0)) || true + optionDisplayLevel="" + local -i options_parse_optionParsedCountOptionDisplayLevel + ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true + optionNoColor="0" + local -i options_parse_optionParsedCountOptionNoColor + ((options_parse_optionParsedCountOptionNoColor = 0)) || true + optionTheme="default" + local -i options_parse_optionParsedCountOptionTheme + ((options_parse_optionParsedCountOptionTheme = 0)) || true + optionVersion="0" + local -i options_parse_optionParsedCountOptionVersion + ((options_parse_optionParsedCountOptionVersion = 0)) || true + optionQuiet="0" + local -i options_parse_optionParsedCountOptionQuiet + ((options_parse_optionParsedCountOptionQuiet = 0)) || true + optionAssumeYes="0" + local -i options_parse_optionParsedCountOptionAssumeYes + ((options_parse_optionParsedCountOptionAssumeYes = 0)) || true + optionPush="0" + local -i options_parse_optionParsedCountOptionPush + ((options_parse_optionParsedCountOptionPush = 0)) || true + optionDelete="0" + local -i options_parse_optionParsedCountOptionDelete + ((options_parse_optionParsedCountOptionDelete = 0)) || true + + newBranchNameArg="" + local -i options_parse_argParsedCountNewBranchNameArg + ((options_parse_argParsedCountNewBranchNameArg = 0)) || true + + oldBranchNameArg="" + local -i options_parse_argParsedCountOldBranchNameArg + ((options_parse_argParsedCountOldBranchNameArg = 0)) || true + + + # shellcheck disable=SC2034 + local -i options_parse_parsedArgIndex=0 + while (($# > 0)); do + local options_parse_arg="$1" + local argOptDefaultBehavior=0 + case "${options_parse_arg}" in + # Option 1/17 + # optionHelp alts --help|-h + # type: Boolean min 0 max 1 + --help | -h) + # shellcheck disable=SC2034 + optionHelp="1" + + if ((options_parse_optionParsedCountOptionHelp >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionHelp)) + optionHelpCallback "${options_parse_arg}" "${optionHelp}" + + ;; + + # Option 2/17 + # optionConfig alts --config + # type: Boolean min 0 max 1 + --config) + # shellcheck disable=SC2034 + optionConfig="1" + + if ((options_parse_optionParsedCountOptionConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionConfig)) + ;; + + # Option 3/17 + # optionBashFrameworkConfig alts --bash-framework-config + # type: String min 0 max 1 + --bash-framework-config) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) + # shellcheck disable=SC2034 + optionBashFrameworkConfig="$1" + optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" + + ;; + + # Option 4/17 + # optionInfoVerbose alts --verbose|-v + # type: Boolean min 0 max 1 + --verbose | -v) + # shellcheck disable=SC2034 + optionInfoVerbose="1" + + if ((options_parse_optionParsedCountOptionInfoVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionInfoVerbose)) + optionInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + updateArgListInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + ;; + + # Option 5/17 + # optionDebugVerbose alts -vv + # type: Boolean min 0 max 1 + -vv) + # shellcheck disable=SC2034 + optionDebugVerbose="1" + + if ((options_parse_optionParsedCountOptionDebugVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDebugVerbose)) + optionDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + updateArgListDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + ;; + + # Option 6/17 + # optionTraceVerbose alts -vvv + # type: Boolean min 0 max 1 + -vvv) + # shellcheck disable=SC2034 + optionTraceVerbose="1" + + if ((options_parse_optionParsedCountOptionTraceVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTraceVerbose)) + optionTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + updateArgListTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + ;; + + # Option 7/17 + # optionEnvFiles alts --env-file + # type: StringArray min 0 max -1 + --env-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + ((++options_parse_optionParsedCountOptionEnvFiles)) + optionEnvFiles+=("$1") + optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + ;; + + # Option 8/17 + # optionLogLevel alts --log-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --log-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogLevel)) + # shellcheck disable=SC2034 + optionLogLevel="$1" + optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + ;; + + # Option 9/17 + # optionLogFile alts --log-file + # type: String min 0 max 1 + --log-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogFile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogFile)) + # shellcheck disable=SC2034 + optionLogFile="$1" + optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + ;; + + # Option 10/17 + # optionDisplayLevel alts --display-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --display-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionDisplayLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDisplayLevel)) + # shellcheck disable=SC2034 + optionDisplayLevel="$1" + optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + ;; + + # Option 11/17 + # optionNoColor alts --no-color + # type: Boolean min 0 max 1 + --no-color) + # shellcheck disable=SC2034 + optionNoColor="1" + + if ((options_parse_optionParsedCountOptionNoColor >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionNoColor)) + optionNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + updateArgListNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + ;; + + # Option 12/17 + # optionTheme alts --theme + # type: String min 0 max 1 + # authorizedValues: default|default-force|noColor + --theme) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ default|default-force|noColor ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionTheme >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTheme)) + # shellcheck disable=SC2034 + optionTheme="$1" + optionThemeCallback "${options_parse_arg}" "${optionTheme}" + + updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" + + ;; + + # Option 13/17 + # optionVersion alts --version + # type: Boolean min 0 max 1 + --version) + # shellcheck disable=SC2034 + optionVersion="1" + + if ((options_parse_optionParsedCountOptionVersion >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionVersion)) + optionVersionCallback "${options_parse_arg}" "${optionVersion}" + + ;; + + # Option 14/17 + # optionQuiet alts --quiet|-q + # type: Boolean min 0 max 1 + --quiet | -q) + # shellcheck disable=SC2034 + optionQuiet="1" + + if ((options_parse_optionParsedCountOptionQuiet >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionQuiet)) + optionQuietCallback "${options_parse_arg}" "${optionQuiet}" + + updateArgListQuietCallback "${options_parse_arg}" "${optionQuiet}" + + ;; + + # Option 15/17 + # optionAssumeYes alts --assume-yes|--yes|-y + # type: Boolean min 0 max 1 + --assume-yes | --yes | -y) + # shellcheck disable=SC2034 + optionAssumeYes="1" + + if ((options_parse_optionParsedCountOptionAssumeYes >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionAssumeYes)) + ;; + + # Option 16/17 + # optionPush alts --push|-p + # type: Boolean min 0 max 1 + --push | -p) + # shellcheck disable=SC2034 + optionPush="1" + + if ((options_parse_optionParsedCountOptionPush >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionPush)) + ;; + + # Option 17/17 + # optionDelete alts --delete|-d + # type: Boolean min 0 max 1 + --delete | -d) + # shellcheck disable=SC2034 + optionDelete="1" + + if ((options_parse_optionParsedCountOptionDelete >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDelete)) + ;; + + -*) + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" + return 1 + fi + ;; + *) + ((minParsedArgIndex0 = 0)) || true + ((maxParsedArgIndex0 = 0)) || true + ((minParsedArgIndex1 = minParsedArgIndex0 + 1)) || true + ((maxParsedArgIndex1 = maxParsedArgIndex0 + 1)) || true + ((minParsedArgIndex2 = minParsedArgIndex1 + 0)) || true + ((maxParsedArgIndex2 = maxParsedArgIndex1 + 1)) || true + ((incrementArg = 1 )) + if ((0)); then + # Technical if - never reached + : + + # Argument 1/2 - newBranchNameArg + # Argument newBranchNameArg min 1 max 1 + # Argument newBranchNameArg authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && + options_parse_parsedArgIndex < maxParsedArgIndex1 )); then + if ((options_parse_argParsedCountNewBranchNameArg >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument newBranchName - Maximum number of argument occurrences reached(1)" return 1 fi - ((++options_parse_optionParsedCountOptionLogFile)) + ((++options_parse_argParsedCountNewBranchNameArg)) # shellcheck disable=SC2034 - optionLogFile="$1" - optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" - updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" - ;; - # Option 17/17 - # Option optionDisplayLevel --display-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --display-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionDisplayLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + newBranchNameArg="${options_parse_arg}" + + + # Argument 2/2 - oldBranchNameArg + # Argument oldBranchNameArg min 0 max 1 + # Argument oldBranchNameArg authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex1 && + options_parse_parsedArgIndex < maxParsedArgIndex2 )); then + if ((options_parse_argParsedCountOldBranchNameArg >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument oldBranchName - Maximum number of argument occurrences reached(1)" return 1 fi - ((++options_parse_optionParsedCountOptionDisplayLevel)) + ((++options_parse_argParsedCountOldBranchNameArg)) # shellcheck disable=SC2034 - optionDisplayLevel="$1" - optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - ;; - -*) + oldBranchNameArg="${options_parse_arg}" + + + # else too much args + else + + if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" + # too much args and no unknownArgumentCallbacks configured + Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" return 1 fi - ;; - *) - if ((0)); then - # Technical if - never reached - : - # Argument 1/2 - # Argument newBranchNameArg min 1 max 1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 0 && options_parse_parsedArgIndex < 1)); then - if ((options_parse_argParsedCountNewBranchNameArg >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument newBranchName - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountNewBranchNameArg)) - # shellcheck disable=SC2034 - newBranchNameArg="${options_parse_arg}" - # Argument 2/2 - # Argument oldBranchNameArg min 0 max 1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 1 && options_parse_parsedArgIndex < 2)); then - if ((options_parse_argParsedCountOldBranchNameArg >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument oldBranchName - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountOldBranchNameArg)) - # shellcheck disable=SC2034 - oldBranchNameArg="${options_parse_arg}" - else - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" - return 1 - fi - fi + + fi + if ((incrementArg == 1)); then ((++options_parse_parsedArgIndex)) - ;; - esac - shift || true - done - if ((options_parse_argParsedCountNewBranchNameArg < 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument 'newBranchName' should be provided at least 1 time(s)" - return 1 - fi - commandOptionParseFinished - commandCallback - Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" - Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" - elif [[ "${options_parse_cmd}" = "help" ]]; then - Array::wrap2 " " 80 0 "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" "rename git local branch, push new branch and delete old branch" - echo + fi + ;; + esac + shift || true + done - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "${SCRIPT_NAME}" "[OPTIONS]" "[ARGUMENTS]")" - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ - "${SCRIPT_NAME}" \ - "[--assume-yes|--yes|-y]" "[--push|-p]" "[--delete|-d]" "[--bash-framework-config ]" "[--config]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--no-color]" "[--theme ]" "[--help|-h]" "[--version]" "[--quiet|-q]" "[--log-level ]" "[--log-file ]" "[--display-level ]")" - echo - echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}newBranchName${__HELP_NORMAL} {single} (mandatory)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(the\ branch\ name\ to\ check) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " [${__HELP_OPTION_COLOR}oldBranchName${__HELP_NORMAL} {single}]" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(the\ name\ of\ the\ old\ branch\ if\ not\ current\ one) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--assume-yes${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}--yes${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-y${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054,SC2206 - mapfile -t helpArray < <(assumeYesHelpCallback) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--push${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-p${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(push\ the\ new\ branch) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--delete${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-d${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(delete\ the\ old\ remote\ branch) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(use\ alternate\ bash\ framework\ configuration.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ configuration) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(info\ level\ verbose\ mode\ \(alias\ of\ --display-level\ INFO\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(debug\ level\ verbose\ mode\ \(alias\ of\ --display-level\ DEBUG\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(trace\ level\ verbose\ mode\ \(alias\ of\ --display-level\ TRACE\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Load\ the\ specified\ env\ file\ \(deprecated\,\ please\ use\ --bash-framework-config\ option\ instead\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Produce\ monochrome\ output.\ alias\ of\ --theme\ noColor.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' - echo ' Possible values: default|default-force|noColor' - echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ this\ command\ help) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Print\ version\ information\ and\ quit) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(quiet\ mode\,\ doesn\'t\ display\ any\ output) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ file) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(set\ display\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e """ -${__HELP_TITLE}EXIT CODES:${__HELP_NORMAL} -${__HELP_OPTION_COLOR}1${__HELP_NORMAL} : if current directory is not a git repository - or if invalid or missing arguments -${__HELP_OPTION_COLOR}2${__HELP_NORMAL} : if impossible to compute current branch name -${__HELP_OPTION_COLOR}3${__HELP_NORMAL} : master/main branch not supported by this command, - please do it manually -${__HELP_OPTION_COLOR}5${__HELP_NORMAL} : New and old branch names are the same -${__HELP_OPTION_COLOR}6${__HELP_NORMAL} : You can use this tool in non interactive mode only - if --assume-yes option is provided -${__HELP_OPTION_COLOR}7${__HELP_NORMAL} : if failed to rename local branch -${__HELP_OPTION_COLOR}8${__HELP_NORMAL} : if remote branch deletion failed -${__HELP_OPTION_COLOR}9${__HELP_NORMAL} : if failed to push the new branch""" - echo - echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo '1.0' - echo - echo -e "${__HELP_TITLE_COLOR}AUTHOR:${__RESET_COLOR}" - echo '[François Chastanet](https://github.com/fchastanet)' - echo - echo -e "${__HELP_TITLE_COLOR}SOURCE FILE:${__RESET_COLOR}" - echo 'https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Git/gitRenameBranch.sh' - echo - echo -e "${__HELP_TITLE_COLOR}LICENSE:${__RESET_COLOR}" - echo 'MIT License' - echo - Array::wrap2 ' ' 76 4 "$(copyrightCallback)" - else - Log::displayError "Command ${SCRIPT_NAME} - Option command invalid: '${options_parse_cmd}'" + + + + + + + + + + + + + + + + + if ((options_parse_argParsedCountNewBranchNameArg < 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument 'newBranchName' should be provided at least 1 time(s)" return 1 - fi -} + fi || return $? + + + + commandCallback + commandOptionParseFinished + optionRedirectCmdOutputs -assumeYesHelpCallback() { - echo "do not ask for confirmation (use with caution)" - echo ' Automatic yes to prompts; assume "y" as answer to all prompts' - echo ' and run non-interactively.' } -commandCallback() { - if ! Assert::tty && [[ "${optionAssumeYes}" != "1" ]]; then - Log::displayError "You can use this tool in non interactive mode only if --assume-yes option is provided" - exit 6 - fi +# @description display command options and arguments help for gitRenameBranchCommand +gitRenameBranchCommandHelp() { + echo -e "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" + Array::wrap2 ' ' 76 4 " " "Rename git local branch, push new branch and delete old branch." + + echo + echo + + # ------------------------------------------ + # usage section + # ------------------------------------------ + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "gitRenameBranch [OPTIONS] [ARGUMENTS]" + echo + # ------------------------------------------ + # usage/options section + # ------------------------------------------ + optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--assume-yes|--yes|-y]" "[--push|-p]" "[--delete|-d]" + ) + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ + "gitRenameBranch" "${optionsAltList[@]}" + echo + + # ------------------------------------------ + # usage/arguments section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" + + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}newBranchName${__HELP_NORMAL} {single} (mandatory) + " + Array::wrap2 ' ' 76 4 " " "The new branch name." + echo + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}oldBranchName${__HELP_NORMAL} {single}] + " + Array::wrap2 ' ' 76 4 " " "The name of the old branch if not current one." + echo + # ------------------------------------------ + # options section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays this command help" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays configuration" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" + Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log file" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set display level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." + echo + Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" + echo + + Array::wrap2 ' ' 76 6 " Default value: " "default" + echo + + echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Print version information and quit." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." + echo + + + echo + echo -e "${__HELP_TITLE_COLOR}OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--assume-yes${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}--yes${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-y${__HELP_NORMAL} {single}" + assumeYesHelpFunction + + + + + echo -e " ${__HELP_OPTION_COLOR}--push${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-p${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Push the new branch." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--delete${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-d${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Delete the old remote branch." + echo + + + # ------------------------------------------ + # longDescription section + # ------------------------------------------ + echo + echo + echo -e "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" + longDescriptionFunction + # ------------------------------------------ + # version section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" + echo "3.0" + # ------------------------------------------ + # author section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}AUTHOR: ${__RESET_COLOR}" + echo "[François Chastanet](https://github.com/fchastanet)" + # ------------------------------------------ + # sourceFile section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Git/gitRenameBranch/gitRenameBranch-binary.yaml" + # ------------------------------------------ + # license section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}LICENSE: ${__RESET_COLOR}" + echo "MIT License" + # ------------------------------------------ + # copyright section + # ------------------------------------------ + Array::wrap2 ' ' 76 0 "$(copyrightCallback)" } -gitRenameBranchCommand parse "${BASH_FRAMEWORK_ARGV[@]}" -# @require Linux::requireExecutedAsUser -run() { - local -a cmd=() - if ! git rev-parse --git-dir >/dev/null 2>&1; then - Log::displayError "not a git repository (or any of the parent directories)" - exit 1 - fi +beforeParseCallback + +gitRenameBranchCommandParse "$@" +MAIN_FUNCTION_NAME="main" +main() { + +# shellcheck disable=SC2154 +declare -a cmd=() +if ! optionRedirectCmdOutputs=/dev/null BashTools::runVerboseIfNeeded \ + git rev-parse --git-dir 2>&1; then + Log::displayError "not a git repository (or any of the parent directories)" + exit 1 +fi + +if [[ -z "${oldBranchNameArg}" ]]; then + oldBranchNameArg="$( + optionRedirectCmdOutputs="" BashTools::runVerboseIfNeeded git branch --show-current + )" if [[ -z "${oldBranchNameArg}" ]]; then - oldBranchNameArg="$(git branch --show-current)" - if [[ -z "${oldBranchNameArg}" ]]; then - Log::displayError "Impossible to compute current branch name" - exit 2 - fi + Log::displayError "Impossible to compute current branch name" + exit 2 fi +fi - if [[ "${oldBranchNameArg}" =~ ^(master|main)$ || "${newBranchNameArg}" =~ ^(master|main)$ ]]; then - Log::displayError "master/main branch not supported by this command, please do it manually" - exit 3 - fi +if [[ "${oldBranchNameArg}" =~ ^(master|main)$ || + "${newBranchNameArg}" =~ ^(master|main)$ ]]; then + Log::displayError "master/main branch not supported by this command, please do it manually" + exit 3 +fi - if [[ -z "${newBranchNameArg}" ]]; then - Log::displayError "new branch name not provided" - exit 4 - fi +if [[ -z "${newBranchNameArg}" ]]; then + Log::displayError "new branch name not provided" + exit 4 +fi - if [[ "${oldBranchNameArg}" = "${newBranchNameArg}" ]]; then - Log::displayError "New and old branch names are the same" - exit 5 - fi +if [[ "${oldBranchNameArg}" = "${newBranchNameArg}" ]]; then + Log::displayError "New and old branch names are the same" + exit 5 +fi - Log::displayInfo "Renaming branch locally from ${oldBranchNameArg} to ${newBranchNameArg}" - declare -a cmd=() - cmd=(git branch -m "${oldBranchNameArg}" "${newBranchNameArg}") - Log::displayDebug "Running '${cmd[*]}'" - if ! "${cmd[@]}"; then - Log::displayError "Failed to rename local branch ${oldBranchNameArg} to ${newBranchNameArg}" - exit 7 - fi +Log::displayInfo "Renaming branch locally from ${oldBranchNameArg} to ${newBranchNameArg}" +declare -a cmd=() +cmd=(git branch -m "${oldBranchNameArg}" "${newBranchNameArg}") +Log::displayDebug "Running '${cmd[*]}'" +if ! BashTools::runVerboseIfNeeded "${cmd[@]}"; then + Log::displayError "Failed to rename local branch ${oldBranchNameArg} to ${newBranchNameArg}" + exit 7 +fi - if [[ "${optionDelete}" = "1" ]]; then - if [[ "${optionAssumeYes}" = "1" ]] || UI::askYesNo "Remove eventual old remote branch ${oldBranchNameArg}"; then - Log::displayInfo "Removing eventual old remote branch ${oldBranchNameArg}" - cmd=(git push origin ":${oldBranchNameArg}") - Log::displayDebug "Running '${cmd[*]}'" - if ! "${cmd[@]}"; then - Log::displayError "Failed to delete remote branch ${oldBranchNameArg}" - exit 8 - fi +if [[ "${optionDelete}" = "1" ]]; then + if [[ "${optionAssumeYes}" = "1" ]] || + UI::askYesNo "Remove eventual old remote branch ${oldBranchNameArg}"; then + Log::displayInfo "Removing eventual old remote branch ${oldBranchNameArg}" + cmd=(git push origin ":${oldBranchNameArg}") + Log::displayDebug "Running '${cmd[*]}'" + if ! BashTools::runVerboseIfNeeded "${cmd[@]}"; then + Log::displayError "Failed to delete remote branch ${oldBranchNameArg}" + exit 8 fi fi +fi - if [[ "${optionPush}" = "1" ]]; then - if [[ "${optionAssumeYes}" = "1" ]] || UI::askYesNo "Push new branch name ${newBranchNameArg}"; then - Log::displayInfo "Pushing new branch name ${newBranchNameArg}" - cmd=(git push --set-upstream origin "${newBranchNameArg}") - Log::displayDebug "Running '${cmd[*]}'" - if ! "${cmd[@]}"; then - Log::displayError "Failed to push the new branch ${newBranchNameArg}" - exit 9 - fi +if [[ "${optionPush}" = "1" ]]; then + if [[ "${optionAssumeYes}" = "1" ]] || UI::askYesNo "Push new branch name ${newBranchNameArg}"; then + Log::displayInfo "Pushing new branch name ${newBranchNameArg}" + cmd=(git push --set-upstream origin "${newBranchNameArg}") + Log::displayDebug "Running '${cmd[*]}'" + if ! BashTools::runVerboseIfNeeded "${cmd[@]}"; then + Log::displayError "Failed to push the new branch ${newBranchNameArg}" + exit 9 fi fi -} - -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run fi } -facade_main_gitRenameBranchsh "$@" +# if file is sourced avoid calling main function +# shellcheck disable=SC2178 +BASH_SOURCE=".$0" # cannot be changed in bash +# shellcheck disable=SC2128 +if test ".$0" == ".${BASH_SOURCE}"; then + if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then + main "$@" &>/dev/null + else + main "$@" + fi +fi diff --git a/src/BashTools/runVerboseIfNeeded.bats b/src/BashTools/runVerboseIfNeeded.bats index 95b22bad..e677c5e8 100755 --- a/src/BashTools/runVerboseIfNeeded.bats +++ b/src/BashTools/runVerboseIfNeeded.bats @@ -6,7 +6,7 @@ source "$(cd "${BATS_TEST_DIRNAME}/.." && pwd)/batsHeaders.sh" source "${rootDir}/src/BashTools/runVerboseIfNeeded.sh" teardown() { - unset optionTraceVerbose + unset optionInfoVerbose } function BashTools::runVerboseIfNeeded::noTrace { #@test @@ -16,9 +16,9 @@ function BashTools::runVerboseIfNeeded::noTrace { #@test } function BashTools::runVerboseIfNeeded::trace { #@test - export optionTraceVerbose="1" + export optionInfoVerbose="1" cmdTest() { - optionTraceVerbose="1" BashTools::runVerboseIfNeeded echo "coucou" + optionInfoVerbose="1" BashTools::runVerboseIfNeeded echo "coucou" } run cmdTest assert_lines_count 2 @@ -30,7 +30,7 @@ function BashTools::runVerboseIfNeeded::trace { #@test function BashTools::runVerboseIfNeeded::redirectCmdOutputs { #@test export cmdTest() { - optionTraceVerbose="1" optionRedirectCmdOutputs="/dev/null" BashTools::runVerboseIfNeeded echo "coucou" + optionInfoVerbose="1" optionRedirectCmdOutputs="/dev/null" BashTools::runVerboseIfNeeded echo "coucou" } run cmdTest assert_output '+ echo coucou' diff --git a/src/BashTools/runVerboseIfNeeded.sh b/src/BashTools/runVerboseIfNeeded.sh index e3ce2f33..93ce352c 100755 --- a/src/BashTools/runVerboseIfNeeded.sh +++ b/src/BashTools/runVerboseIfNeeded.sh @@ -2,12 +2,12 @@ # @description run command specified # @arg $@ array:String[] the command to run -# @env optionTraceVerbose int - if 1 displays the command specified before running it +# @env optionInfoVerbose int - if 1 displays the command specified before running it # @env optionRedirectCmdOutputs String - if set redirect command outputs to file specified # @exitcode command's exit code BashTools::runVerboseIfNeeded() { # shellcheck disable=SC2154 - if [[ "${optionTraceVerbose}" = "1" ]]; then + if [[ "${optionInfoVerbose}" = "1" ]]; then echo >&2 "+ $*" fi ( diff --git a/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-binary.yaml b/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-binary.yaml index fc52edc3..e64ec961 100644 --- a/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-binary.yaml +++ b/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-binary.yaml @@ -1,4 +1,5 @@ extends: + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/Git/gitToolsDefaultOptions.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" diff --git a/src/_binaries/Git/gitIsBranch/gitIsBranch-binary.yaml b/src/_binaries/Git/gitIsBranch/gitIsBranch-binary.yaml index 00074e3a..743d2325 100644 --- a/src/_binaries/Git/gitIsBranch/gitIsBranch-binary.yaml +++ b/src/_binaries/Git/gitIsBranch/gitIsBranch-binary.yaml @@ -1,4 +1,5 @@ extends: + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/Git/gitToolsDefaultOptions.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" diff --git a/src/_binaries/Git/gitIsBranch/gitIsBranch-main.sh b/src/_binaries/Git/gitIsBranch/gitIsBranch-main.sh index b2683963..cc06ca1c 100755 --- a/src/_binaries/Git/gitIsBranch/gitIsBranch-main.sh +++ b/src/_binaries/Git/gitIsBranch/gitIsBranch-main.sh @@ -1,11 +1,6 @@ #!/usr/bin/env bash # shellcheck disable=SC2154 -declare optionRedirectCmdOutputs="" -if [[ "${optionTraceVerbose}" != "1" ]]; then - # shellcheck disable=SC2034 # used by BashTools::runVerboseIfNeeded - optionRedirectCmdOutputs="/dev/null" -fi BashTools::runVerboseIfNeeded git show-ref --verify refs/heads/"${branchNameArg}" || BashTools::runVerboseIfNeeded git show-ref --verify refs/remotes/"${branchNameArg}" || Log::fatal "not a branch name: ${branchNameArg}" diff --git a/src/_binaries/Git/gitIsBranch/testsData/gitIsBranch.help.txt b/src/_binaries/Git/gitIsBranch/testsData/gitIsBranch.help.txt index e69de29b..a844954f 100644 --- a/src/_binaries/Git/gitIsBranch/testsData/gitIsBranch.help.txt +++ b/src/_binaries/Git/gitIsBranch/testsData/gitIsBranch.help.txt @@ -0,0 +1,65 @@ +SYNOPSIS: + Show an error if branchName is not a known branch. + +USAGE: gitIsBranch [OPTIONS] [ARGUMENTS] +USAGE: gitIsBranch [--help|-h] [--config] + [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] + [--env-file ] [--log-level ] [--log-file ] + [--display-level ] [--no-color] [--theme ] [--version] + [--quiet|-q] + +ARGUMENTS: + branchName {single} (mandatory) + The branch name to check. + +GLOBAL OPTIONS: + --help, -h {single} + Displays this command help + --config {single} + Displays configuration + --bash-framework-config  {single} + Use alternate bash framework configuration. + --verbose, -v {single} + Info level verbose mode (alias of --display-level INFO) + -vv {single} + Debug level verbose mode (alias of --display-level DEBUG) + -vvv {single} + Trace level verbose mode (alias of --display-level TRACE) + --env-file  {list} (optional) + Load the specified env file (deprecated, please use --bash-framework-con + fig option instead) + --log-level  {single} + Set log level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --log-file  {single} + Set log file + --display-level  {single} + Set display level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --no-color {single} + Produce monochrome output. alias of --theme noColor. + --theme  {single} + Choose color theme - default-force means colors will be produced even if + command is piped. + Possible values: default, default-force, noColor + Default value: default + --version {single} + Print version information and quit. + --quiet, -q {single} + Quiet mode, doesn't display any output. + + +DESCRIPTION: + EXIT CODES: + 1: if commit does not exists + 2: if ref is not convertible to commit oid + 3: if commit is not included in given branch + +VERSION: 3.0 + +AUTHOR: [François Chastanet](https://github.com/fchastanet) + +SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Git/gitIsBranch/gitIsBranch-binary.yaml + +LICENSE: MIT License +Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Git/gitRenameBranch.options.tpl b/src/_binaries/Git/gitRenameBranch.options.tpl deleted file mode 100644 index bbabcfcf..00000000 --- a/src/_binaries/Git/gitRenameBranch.options.tpl +++ /dev/null @@ -1,89 +0,0 @@ -% -declare versionNumber="1.0" -declare commandFunctionName="gitRenameBranchCommand" -declare help="rename git local branch, push new branch and delete old branch" -declare longDescription=''' -${__HELP_TITLE}EXIT CODES:${__HELP_NORMAL} -${__HELP_OPTION_COLOR}1${__HELP_NORMAL} : if current directory is not a git repository - or if invalid or missing arguments -${__HELP_OPTION_COLOR}2${__HELP_NORMAL} : if impossible to compute current branch name -${__HELP_OPTION_COLOR}3${__HELP_NORMAL} : master/main branch not supported by this command, - please do it manually -${__HELP_OPTION_COLOR}5${__HELP_NORMAL} : New and old branch names are the same -${__HELP_OPTION_COLOR}6${__HELP_NORMAL} : You can use this tool in non interactive mode only - if --assume-yes option is provided -${__HELP_OPTION_COLOR}7${__HELP_NORMAL} : if failed to rename local branch -${__HELP_OPTION_COLOR}8${__HELP_NORMAL} : if remote branch deletion failed -${__HELP_OPTION_COLOR}9${__HELP_NORMAL} : if failed to push the new branch''' -% - -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.base.tpl)" - -% -# shellcheck source=/dev/null -source <( - Options::generateArg \ - --help "the branch name to check" \ - --min 1 \ - --max 1 \ - --name "newBranchName" \ - --variable-name "newBranchNameArg" \ - --function-name newBranchNameArgFunction - - Options::generateArg \ - --help "the name of the old branch if not current one" \ - --min 0 \ - --max 1 \ - --name "oldBranchName" \ - --variable-name "oldBranchNameArg" \ - --function-name oldBranchNameArgFunction - - assumeYesHelpCallback() { :; } - # shellcheck disable=SC2116 - Options::generateOption \ - --help assumeYesHelpCallback \ - --alt "--assume-yes" \ - --alt "--yes" \ - --alt "-y" \ - --variable-name "optionAssumeYes" \ - --function-name optionAssumeYesFunction - - Options::generateOption \ - --help "push the new branch" \ - --alt "--push" \ - --alt "-p" \ - --variable-name "optionPush" \ - --function-name optionPushFunction - - Options::generateOption \ - --help "delete the old remote branch" \ - --alt "--delete" \ - --alt "-d" \ - --variable-name "optionDelete" \ - --function-name optionDeleteFunction -) -options+=( - newBranchNameArgFunction - oldBranchNameArgFunction - optionAssumeYesFunction - optionPushFunction - optionDeleteFunction - --callback commandCallback -) -Options::generateCommand "${options[@]}" -% - -assumeYesHelpCallback() { - echo "do not ask for confirmation (use with caution)" - echo ' Automatic yes to prompts; assume "y" as answer to all prompts' - echo ' and run non-interactively.' -} - -commandCallback() { - if ! Assert::tty && [[ "${optionAssumeYes}" != "1" ]]; then - Log::displayError "You can use this tool in non interactive mode only if --assume-yes option is provided" - exit 6 - fi -} - -<% ${commandFunctionName} %> parse "${BASH_FRAMEWORK_ARGV[@]}" diff --git a/src/_binaries/Git/gitRenameBranch.sh b/src/_binaries/Git/gitRenameBranch.sh deleted file mode 100755 index 17487f57..00000000 --- a/src/_binaries/Git/gitRenameBranch.sh +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env bash -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/gitRenameBranch -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE -# shellcheck disable=SC2034 - -# variables values -declare optionPush="0" -declare optionDelete="0" -declare optionAssumeYes="0" -declare newBranchNameArg="" -declare oldBranchNameArg="" - -# other values -declare copyrightBeginYear="2020" - -.INCLUDE "$(dynamicTemplateDir _binaries/Git/gitRenameBranch.options.tpl)" - -# @require Linux::requireExecutedAsUser -run() { - local -a cmd=() - if ! git rev-parse --git-dir >/dev/null 2>&1; then - Log::displayError "not a git repository (or any of the parent directories)" - exit 1 - fi - - if [[ -z "${oldBranchNameArg}" ]]; then - oldBranchNameArg="$(git branch --show-current)" - if [[ -z "${oldBranchNameArg}" ]]; then - Log::displayError "Impossible to compute current branch name" - exit 2 - fi - fi - - if [[ "${oldBranchNameArg}" =~ ^(master|main)$ || "${newBranchNameArg}" =~ ^(master|main)$ ]]; then - Log::displayError "master/main branch not supported by this command, please do it manually" - exit 3 - fi - - if [[ -z "${newBranchNameArg}" ]]; then - Log::displayError "new branch name not provided" - exit 4 - fi - - if [[ "${oldBranchNameArg}" = "${newBranchNameArg}" ]]; then - Log::displayError "New and old branch names are the same" - exit 5 - fi - - Log::displayInfo "Renaming branch locally from ${oldBranchNameArg} to ${newBranchNameArg}" - declare -a cmd=() - cmd=(git branch -m "${oldBranchNameArg}" "${newBranchNameArg}") - Log::displayDebug "Running '${cmd[*]}'" - if ! "${cmd[@]}"; then - Log::displayError "Failed to rename local branch ${oldBranchNameArg} to ${newBranchNameArg}" - exit 7 - fi - - if [[ "${optionDelete}" = "1" ]]; then - if [[ "${optionAssumeYes}" = "1" ]] || UI::askYesNo "Remove eventual old remote branch ${oldBranchNameArg}"; then - Log::displayInfo "Removing eventual old remote branch ${oldBranchNameArg}" - cmd=(git push origin ":${oldBranchNameArg}") - Log::displayDebug "Running '${cmd[*]}'" - if ! "${cmd[@]}"; then - Log::displayError "Failed to delete remote branch ${oldBranchNameArg}" - exit 8 - fi - fi - fi - - if [[ "${optionPush}" = "1" ]]; then - if [[ "${optionAssumeYes}" = "1" ]] || UI::askYesNo "Push new branch name ${newBranchNameArg}"; then - Log::displayInfo "Pushing new branch name ${newBranchNameArg}" - cmd=(git push --set-upstream origin "${newBranchNameArg}") - Log::displayDebug "Running '${cmd[*]}'" - if ! "${cmd[@]}"; then - Log::displayError "Failed to push the new branch ${newBranchNameArg}" - exit 9 - fi - fi - fi -} - -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run -fi diff --git a/src/_binaries/Git/gitRenameBranch/gitRenameBranch-binary.yaml b/src/_binaries/Git/gitRenameBranch/gitRenameBranch-binary.yaml new file mode 100644 index 00000000..d31c5e3e --- /dev/null +++ b/src/_binaries/Git/gitRenameBranch/gitRenameBranch-binary.yaml @@ -0,0 +1,70 @@ +extends: + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/Git/gitToolsDefaultOptions.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" + +vars: + SRC_FILE_PATH: src/_binaries/Git/gitRenameBranch/gitRenameBranch-binary.yaml + +compilerConfig: + targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/gitRenameBranch" + relativeRootDirBasedOnTargetDir: .. + srcDirs: + - ${BASH_TOOLS_ROOT_DIR}/src +binData: + commands: + default: + functionName: gitRenameBranchCommand + version: "3.0" + copyrightBeginYear: 2020 + commandName: gitRenameBranch + beforeParseCallbacks: + - beforeParseCallback + callbacks: + - commandCallback + definitionFiles: + 20: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Git/gitRenameBranch/gitRenameBranch-options.sh + mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Git/gitRenameBranch/gitRenameBranch-main.sh + help: Rename git local branch, push new branch and delete old branch. + longDescription: longDescriptionFunction + args: + - type: String + min: 1 + max: 1 + help: The new branch name. + name: newBranchName + variableName: newBranchNameArg + + - type: String + min: 0 + max: 1 + help: The name of the old branch if not current one. + name: oldBranchName + variableName: oldBranchNameArg + + options: + - alts: + - --assume-yes + - --yes + - -y + group: OptionsGroup + type: Boolean + help: assumeYesHelpFunction + variableName: optionAssumeYes + + - alts: + - --push + - -p + group: OptionsGroup + type: Boolean + help: Push the new branch. + variableName: optionPush + + - alts: + - --delete + - -d + group: OptionsGroup + type: Boolean + help: Delete the old remote branch. + variableName: optionDelete diff --git a/src/_binaries/Git/gitRenameBranch/gitRenameBranch-main.sh b/src/_binaries/Git/gitRenameBranch/gitRenameBranch-main.sh new file mode 100755 index 00000000..65bba7f2 --- /dev/null +++ b/src/_binaries/Git/gitRenameBranch/gitRenameBranch-main.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2154 + +declare -a cmd=() +if ! optionRedirectCmdOutputs=/dev/null BashTools::runVerboseIfNeeded \ + git rev-parse --git-dir 2>&1; then + Log::displayError "not a git repository (or any of the parent directories)" + exit 1 +fi + +if [[ -z "${oldBranchNameArg}" ]]; then + oldBranchNameArg="$( + optionRedirectCmdOutputs="" BashTools::runVerboseIfNeeded git branch --show-current + )" + if [[ -z "${oldBranchNameArg}" ]]; then + Log::displayError "Impossible to compute current branch name" + exit 2 + fi +fi + +if [[ "${oldBranchNameArg}" =~ ^(master|main)$ || + "${newBranchNameArg}" =~ ^(master|main)$ ]]; then + Log::displayError "master/main branch not supported by this command, please do it manually" + exit 3 +fi + +if [[ -z "${newBranchNameArg}" ]]; then + Log::displayError "new branch name not provided" + exit 4 +fi + +if [[ "${oldBranchNameArg}" = "${newBranchNameArg}" ]]; then + Log::displayError "New and old branch names are the same" + exit 5 +fi + +Log::displayInfo "Renaming branch locally from ${oldBranchNameArg} to ${newBranchNameArg}" +declare -a cmd=() +cmd=(git branch -m "${oldBranchNameArg}" "${newBranchNameArg}") +Log::displayDebug "Running '${cmd[*]}'" +if ! BashTools::runVerboseIfNeeded "${cmd[@]}"; then + Log::displayError "Failed to rename local branch ${oldBranchNameArg} to ${newBranchNameArg}" + exit 7 +fi + +if [[ "${optionDelete}" = "1" ]]; then + if [[ "${optionAssumeYes}" = "1" ]] || + UI::askYesNo "Remove eventual old remote branch ${oldBranchNameArg}"; then + Log::displayInfo "Removing eventual old remote branch ${oldBranchNameArg}" + cmd=(git push origin ":${oldBranchNameArg}") + Log::displayDebug "Running '${cmd[*]}'" + if ! BashTools::runVerboseIfNeeded "${cmd[@]}"; then + Log::displayError "Failed to delete remote branch ${oldBranchNameArg}" + exit 8 + fi + fi +fi + +if [[ "${optionPush}" = "1" ]]; then + if [[ "${optionAssumeYes}" = "1" ]] || UI::askYesNo "Push new branch name ${newBranchNameArg}"; then + Log::displayInfo "Pushing new branch name ${newBranchNameArg}" + cmd=(git push --set-upstream origin "${newBranchNameArg}") + Log::displayDebug "Running '${cmd[*]}'" + if ! BashTools::runVerboseIfNeeded "${cmd[@]}"; then + Log::displayError "Failed to push the new branch ${newBranchNameArg}" + exit 9 + fi + fi +fi diff --git a/src/_binaries/Git/gitRenameBranch/gitRenameBranch-options.sh b/src/_binaries/Git/gitRenameBranch/gitRenameBranch-options.sh new file mode 100755 index 00000000..68d74a22 --- /dev/null +++ b/src/_binaries/Git/gitRenameBranch/gitRenameBranch-options.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +longDescriptionFunction() { + echo -e " ${__HELP_TITLE}EXIT CODES:${__HELP_NORMAL}" + echo -e " ${__HELP_OPTION_COLOR}1${__HELP_NORMAL} : if current directory is not a git repository" + echo -e " or if invalid or missing arguments" + echo -e " ${__HELP_OPTION_COLOR}2${__HELP_NORMAL} : if impossible to compute current branch name" + echo -e " ${__HELP_OPTION_COLOR}3${__HELP_NORMAL} : master/main branch not supported by this command," + echo -e " please do it manually" + echo -e " ${__HELP_OPTION_COLOR}5${__HELP_NORMAL} : New and old branch names are the same" + echo -e " ${__HELP_OPTION_COLOR}6${__HELP_NORMAL} : You can use this tool in non interactive mode only" + echo -e " if --assume-yes option is provided" + echo -e " ${__HELP_OPTION_COLOR}7${__HELP_NORMAL} : if failed to rename local branch" + echo -e " ${__HELP_OPTION_COLOR}8${__HELP_NORMAL} : if remote branch deletion failed" + echo -e " ${__HELP_OPTION_COLOR}9${__HELP_NORMAL} : if failed to push the new branch" + echo +} + +optionHelpCallback() { + gitRenameBranchCommandHelp + exit 0 +} + +assumeYesHelpFunction() { + echo " Do not ask for confirmation (use with caution)." + echo ' Automatic yes to prompts; assume "y" as answer to all prompts' + echo ' and run non-interactively.' +} + +commandCallback() { + # shellcheck disable=SC2154 + if ! Assert::tty && [[ "${optionAssumeYes}" != "1" ]]; then + Log::displayError "You can use this tool in non interactive mode only if --assume-yes option is provided" + exit 6 + fi +} diff --git a/src/_binaries/Git/gitRenameBranch.bats b/src/_binaries/Git/gitRenameBranch/gitRenameBranch.bats similarity index 63% rename from src/_binaries/Git/gitRenameBranch.bats rename to src/_binaries/Git/gitRenameBranch/gitRenameBranch.bats index 038d6caf..54f2feab 100755 --- a/src/_binaries/Git/gitRenameBranch.bats +++ b/src/_binaries/Git/gitRenameBranch/gitRenameBranch.bats @@ -1,7 +1,7 @@ #!/usr/bin/env bash # shellcheck source=src/batsHeaders.sh -source "$(cd "${BATS_TEST_DIRNAME}/../.." && pwd)/batsHeaders.sh" +source "$(cd "${BATS_TEST_DIRNAME}/../../.." && pwd)/batsHeaders.sh" load "${FRAMEWORK_ROOT_DIR}/src/_standalone/Bats/assert_lines_count.sh" @@ -48,11 +48,15 @@ function Git::gitRenameBranch::not_a_git_repository { #@test assert_output --partial "ERROR - not a git repository (or any of the parent directories)" } -function Git::gitRenameBranch::master_branch_not_supported { #@test +function Git::gitRenameBranch::master_branch_not_supported_checkout_master { #@test git checkout master - run "${binDir}/gitRenameBranch" --verbose master 2>&1 + run "${binDir}/gitRenameBranch" --verbose master -vvv 2>&1 assert_failure 3 - assert_output --partial "ERROR - master/main branch not supported by this command, please do it manually" + assert_lines_count 4 + assert_line --index 0 "+ git rev-parse --git-dir" + assert_line --index 1 "+ git branch --show-current" + assert_line --index 2 --partial "ERROR - master/main branch not supported by this command, please do it manually" + assert_line --index 3 --partial "DEBUG - KEEP_TEMP_FILES=0 removing temp files" } function Git::gitRenameBranch::main_branch_not_supported { #@test @@ -102,9 +106,13 @@ function Git::gitRenameBranch::rename_local_and_push_branch { #@test run "${binDir}/gitRenameBranch" newBranch --push --verbose 2>&1 <<<'y' assert_success - assert_line -n 0 --partial "INFO - Renaming branch locally from oldName to newBranch" - assert_line -n 1 --partial "INFO - Pushing new branch name newBranch" - assert_lines_count 2 + assert_lines_count 6 + assert_line -n 0 "+ git rev-parse --git-dir" + assert_line -n 1 "+ git branch --show-current" + assert_line -n 2 --partial "INFO - Renaming branch locally from oldName to newBranch" + assert_line -n 3 "+ git branch -m oldName newBranch" + assert_line -n 4 --partial "INFO - Pushing new branch name newBranch" + assert_line -n 5 "+ git push --set-upstream origin newBranch" } function Git::gitRenameBranch::rename_local_push_delete_remote_branch { #@test @@ -117,10 +125,15 @@ function Git::gitRenameBranch::rename_local_push_delete_remote_branch { #@test run "${binDir}/gitRenameBranch" newBranch --push --delete --verbose 2>&1 <<<'yy' assert_success - assert_line -n 0 --partial "INFO - Renaming branch locally from oldName to newBranch" - assert_line -n 1 --partial "INFO - Removing eventual old remote branch oldName" - assert_line -n 2 --partial "INFO - Pushing new branch name newBranch" - assert_lines_count 3 + assert_lines_count 8 + assert_line -n 0 "+ git rev-parse --git-dir" + assert_line -n 1 "+ git branch --show-current" + assert_line -n 2 --partial "INFO - Renaming branch locally from oldName to newBranch" + assert_line -n 3 "+ git branch -m oldName newBranch" + assert_line -n 4 --partial "INFO - Removing eventual old remote branch oldName" + assert_line -n 5 "+ git push origin :oldName" + assert_line -n 6 --partial "INFO - Pushing new branch name newBranch" + assert_line -n 7 "+ git push --set-upstream origin newBranch" } function Git::gitRenameBranch::rename_local_and_delete_remote_branch { #@test @@ -131,10 +144,12 @@ function Git::gitRenameBranch::rename_local_and_delete_remote_branch { #@test run "${binDir}/gitRenameBranch" newBranch oldName --delete --verbose 2>&1 <<<'y' assert_success - - assert_line -n 0 --partial "INFO - Renaming branch locally from oldName to newBranch" - assert_line -n 1 --partial "INFO - Removing eventual old remote branch oldName" - assert_lines_count 2 + assert_lines_count 5 + assert_line -n 0 "+ git rev-parse --git-dir" + assert_line -n 1 --partial "INFO - Renaming branch locally from oldName to newBranch" + assert_line -n 2 "+ git branch -m oldName newBranch" + assert_line -n 3 --partial "INFO - Removing eventual old remote branch oldName" + assert_line -n 4 "+ git push origin :oldName" } function Git::gitRenameBranch::rename_local_and_delete_remote_branch_without_oldName { #@test @@ -147,9 +162,13 @@ function Git::gitRenameBranch::rename_local_and_delete_remote_branch_without_old run "${binDir}/gitRenameBranch" newBranch --delete --verbose 2>&1 <<<'y' assert_success - assert_line -n 0 --partial "INFO - Renaming branch locally from oldName to newBranch" - assert_line -n 1 --partial "INFO - Removing eventual old remote branch oldName" - assert_lines_count 2 + assert_lines_count 6 + assert_line -n 0 "+ git rev-parse --git-dir" + assert_line -n 1 "+ git branch --show-current" + assert_line -n 2 --partial "INFO - Renaming branch locally from oldName to newBranch" + assert_line -n 3 "+ git branch -m oldName newBranch" + assert_line -n 4 --partial "INFO - Removing eventual old remote branch oldName" + assert_line -n 5 "+ git push origin :oldName" } function Git::gitRenameBranch::rename_local_and_push_branch_assume_yes { #@test @@ -162,9 +181,13 @@ function Git::gitRenameBranch::rename_local_and_push_branch_assume_yes { #@test run "${binDir}/gitRenameBranch" newBranch --push --assume-yes --verbose 2>&1 assert_success - assert_line -n 0 --partial "INFO - Renaming branch locally from oldName to newBranch" - assert_line -n 1 --partial "INFO - Pushing new branch name newBranch" - assert_lines_count 2 + assert_lines_count 6 + assert_line -n 0 "+ git rev-parse --git-dir" + assert_line -n 1 "+ git branch --show-current" + assert_line -n 2 --partial "INFO - Renaming branch locally from oldName to newBranch" + assert_line -n 3 "+ git branch -m oldName newBranch" + assert_line -n 4 --partial "INFO - Pushing new branch name newBranch" + assert_line -n 5 "+ git push --set-upstream origin newBranch" } function Git::gitRenameBranch::rename_local_push_delete_remote_branch_assume_yes { #@test @@ -178,10 +201,15 @@ function Git::gitRenameBranch::rename_local_push_delete_remote_branch_assume_yes run "${binDir}/gitRenameBranch" newBranch --push --delete --assume-yes --verbose 2>&1 <<<'yy' assert_success - assert_line -n 0 --partial "INFO - Renaming branch locally from oldName to newBranch" - assert_line -n 1 --partial "INFO - Removing eventual old remote branch oldName" - assert_line -n 2 --partial "INFO - Pushing new branch name newBranch" - assert_lines_count 3 + assert_lines_count 8 + assert_line -n 0 "+ git rev-parse --git-dir" + assert_line -n 1 "+ git branch --show-current" + assert_line -n 2 --partial "INFO - Renaming branch locally from oldName to newBranch" + assert_line -n 3 "+ git branch -m oldName newBranch" + assert_line -n 4 --partial "INFO - Removing eventual old remote branch oldName" + assert_line -n 5 "+ git push origin :oldName" + assert_line -n 6 --partial "INFO - Pushing new branch name newBranch" + assert_line -n 7 "+ git push --set-upstream origin newBranch" } function Git::gitRenameBranch::rename_local_and_delete_remote_branch_assume_yes { #@test @@ -191,11 +219,13 @@ function Git::gitRenameBranch::rename_local_and_delete_remote_branch_assume_yes 'push origin :oldName : echo "git push origin :oldName"' run "${binDir}/gitRenameBranch" newBranch oldName --delete --assume-yes --verbose 2>&1 <<<'y' - assert_success - assert_lines_count 4 - assert_line -n 1 "git branch -m oldName newBranch" - assert_line -n 3 "git push origin :oldName" + assert_lines_count 5 + assert_line -n 0 "+ git rev-parse --git-dir" + assert_line -n 1 --partial "INFO - Renaming branch locally from oldName to newBranch" + assert_line -n 2 "+ git branch -m oldName newBranch" + assert_line -n 3 --partial "INFO - Removing eventual old remote branch oldName" + assert_line -n 4 "+ git push origin :oldName" } function Git::gitRenameBranch::rename_local_and_delete_remote_branch_without_oldName_assume_yes { #@test @@ -208,7 +238,12 @@ function Git::gitRenameBranch::rename_local_and_delete_remote_branch_without_old run "${binDir}/gitRenameBranch" newBranch --delete --assume-yes --verbose 2>&1 <<<'y' assert_success - assert_lines_count 4 - assert_line -n 1 "git branch -m oldName newBranch" - assert_line -n 3 "git push origin :oldName" + assert_lines_count 6 + assert_line -n 0 "+ git rev-parse --git-dir" + assert_line -n 1 "+ git branch --show-current" + assert_line -n 2 --partial "INFO - Renaming branch locally from oldName to newBranch" + assert_line -n 3 "+ git branch -m oldName newBranch" + assert_line -n 4 --partial "INFO - Removing eventual old remote branch oldName" + assert_line -n 5 "+ git push origin :oldName" + } diff --git a/src/_binaries/Git/gitRenameBranch/testsData/.env b/src/_binaries/Git/gitRenameBranch/testsData/.env new file mode 100755 index 00000000..24e891d2 --- /dev/null +++ b/src/_binaries/Git/gitRenameBranch/testsData/.env @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2034 + +# Default settings +# you can override these settings by creating ${HOME}/.bash-tools/.env file +### +### LOG Level +### minimum level of the messages that will be logged into LOG_FILE +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_LOG_LEVEL=0 + +### +### DISPLAY Level +### minimum level of the messages that will be displayed on screen +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} + +### +### Log to file +### +### all log messages will be redirected to log file specified +### this same path will be used inside and outside of the container +### +BASH_FRAMEWORK_LOG_FILE= + +# absolute directory containing db import sql dumps +DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} + +# garbage collect all files for which modification is greater than eg: 30 days (+30) +# each time an existing file is used by dbImport/dbImportTable +# the file modification time is set to now +DB_IMPORT_GARBAGE_COLLECT_DAYS=+30 + +# absolute directory containing dbScripts used by dbScriptAllDatabases +SCRIPTS_FOLDER=${HOME}/.bash-tools/conf/dbScripts + +# ----------------------------------------------------- +# AWS Parameters +# ----------------------------------------------------- +S3_BASE_URL=s3://s3server/exports diff --git a/src/_binaries/Git/gitRenameBranch/testsData/gitRenameBranch.help.txt b/src/_binaries/Git/gitRenameBranch/testsData/gitRenameBranch.help.txt new file mode 100644 index 00000000..c14be34d --- /dev/null +++ b/src/_binaries/Git/gitRenameBranch/testsData/gitRenameBranch.help.txt @@ -0,0 +1,86 @@ +SYNOPSIS: + Rename git local branch, push new branch and delete old branch. + +USAGE: gitRenameBranch [OPTIONS] [ARGUMENTS] +USAGE: gitRenameBranch [--help|-h] [--config] + [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] + [--env-file ] [--log-level ] [--log-file ] + [--display-level ] [--no-color] [--theme ] [--version] + [--quiet|-q] [--assume-yes|--yes|-y] [--push|-p] [--delete|-d] + +ARGUMENTS: + newBranchName {single} (mandatory) + The new branch name. + [oldBranchName {single}] + The name of the old branch if not current one. + +GLOBAL OPTIONS: + --help, -h {single} + Displays this command help + --config {single} + Displays configuration + --bash-framework-config  {single} + Use alternate bash framework configuration. + --verbose, -v {single} + Info level verbose mode (alias of --display-level INFO) + -vv {single} + Debug level verbose mode (alias of --display-level DEBUG) + -vvv {single} + Trace level verbose mode (alias of --display-level TRACE) + --env-file  {list} (optional) + Load the specified env file (deprecated, please use --bash-framework-con + fig option instead) + --log-level  {single} + Set log level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --log-file  {single} + Set log file + --display-level  {single} + Set display level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --no-color {single} + Produce monochrome output. alias of --theme noColor. + --theme  {single} + Choose color theme - default-force means colors will be produced even if + command is piped. + Possible values: default, default-force, noColor + Default value: default + --version {single} + Print version information and quit. + --quiet, -q {single} + Quiet mode, doesn't display any output. + +OPTIONS: + --assume-yes, --yes, -y {single} + Do not ask for confirmation (use with caution). + Automatic yes to prompts; assume "y" as answer to all prompts + and run non-interactively. + --push, -p {single} + Push the new branch. + --delete, -d {single} + Delete the old remote branch. + + +DESCRIPTION: + EXIT CODES: + 1 : if current directory is not a git repository + or if invalid or missing arguments + 2 : if impossible to compute current branch name + 3 : master/main branch not supported by this command, + please do it manually + 5 : New and old branch names are the same + 6 : You can use this tool in non interactive mode only + if --assume-yes option is provided + 7 : if failed to rename local branch + 8 : if remote branch deletion failed + 9 : if failed to push the new branch + + +VERSION: 3.0 + +AUTHOR: [François Chastanet](https://github.com/fchastanet) + +SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Git/gitRenameBranch/gitRenameBranch-binary.yaml + +LICENSE: MIT License +Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Git/gitToolsDefaultOptions.sh b/src/_binaries/Git/gitToolsDefaultOptions.sh new file mode 100755 index 00000000..210e4067 --- /dev/null +++ b/src/_binaries/Git/gitToolsDefaultOptions.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +beforeParseCallback() { + Linux::requireExecutedAsUser + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + +declare optionRedirectCmdOutputs="" +optionRedirectCmdOutputs() { + export optionTraceVerbose + # shellcheck disable=SC2154 + if [[ "${optionTraceVerbose}" != "1" ]]; then + # shellcheck disable=SC2034 # used by BashTools::runVerboseIfNeeded + optionRedirectCmdOutputs="/dev/null" + fi +} diff --git a/src/_binaries/Git/gitToolsDefaultOptions.yaml b/src/_binaries/Git/gitToolsDefaultOptions.yaml new file mode 100644 index 00000000..4ce58af0 --- /dev/null +++ b/src/_binaries/Git/gitToolsDefaultOptions.yaml @@ -0,0 +1,7 @@ +binData: + commands: + default: + definitionFiles: + 19: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Git/gitToolsDefaultOptions.sh + callbacks: + - optionRedirectCmdOutputs diff --git a/src/_binaries/Git/testsData/gitRenameBranch.help.txt b/src/_binaries/Git/testsData/gitRenameBranch.help.txt deleted file mode 100644 index 022b6bdc..00000000 --- a/src/_binaries/Git/testsData/gitRenameBranch.help.txt +++ /dev/null @@ -1,86 +0,0 @@ -DESCRIPTION: -rename git local branch, push new branch and delete old branch -USAGE: gitRenameBranch [OPTIONS] [ARGUMENTS] -USAGE: gitRenameBranch [--assume-yes|--yes|-y] [--push|-p] - [--delete|-d] [--bash-framework-config ] [--config] [--verbose|-v] - [-vv] [-vvv] [--env-file ] [--no-color] [--theme ] [--help|-h] - [--version] [--quiet|-q] [--log-level ] [--log-file ] - [--display-level ] - -ARGUMENTS: - newBranchName {single} (mandatory) - the branch name to check - [oldBranchName {single}] - the name of the old branch if not current one - -OPTIONS: - --assume-yes, --yes, -y {single} - do not ask for confirmation (use with caution) - Automatic yes to prompts; assume "y" as answer to all prompts - and run non-interactively. - --push, -p {single} - push the new branch - --delete, -d {single} - delete the old remote branch - -GLOBAL OPTIONS: - --bash-framework-config  {single} - use alternate bash framework configuration. - --config {single} - Display configuration - --verbose, -v {single} - info level verbose mode (alias of --display-level INFO) - -vv {single} - debug level verbose mode (alias of --display-level DEBUG) - -vvv {single} - trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-config - option instead) - --no-color {single} - Produce monochrome output. alias of --theme noColor. - --theme  {single} - choose color theme - default-force means colors will be produced even if com - mand is piped - Default value: default - Possible values: default|default-force|noColor - --help, -h {single} - Display this command help - --version {single} - Print version information and quit - --quiet, -q {single} - quiet mode, doesn't display any output - --log-level  {single} - Set log level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - --log-file  {single} - Set log file - --display-level  {single} - set display level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - -EXIT CODES: -1 : if current directory is not a git repository - or if invalid or missing arguments -2 : if impossible to compute current branch name -3 : master/main branch not supported by this command, - please do it manually -5 : New and old branch names are the same -6 : You can use this tool in non interactive mode only - if --assume-yes option is provided -7 : if failed to rename local branch -8 : if remote branch deletion failed -9 : if failed to push the new branch - -VERSION: 1.0 - -AUTHOR: -[François Chastanet](https://github.com/fchastanet) - -SOURCE FILE: -https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Git/gitRenameBranch.sh - -LICENSE: -MIT License - -Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Postman/postmanCli.bats b/src/_binaries/Postman/postmanCli.bats index 17142809..58e334c1 100755 --- a/src/_binaries/Postman/postmanCli.bats +++ b/src/_binaries/Postman/postmanCli.bats @@ -31,7 +31,7 @@ function PostmanCli::config { #@test assert_line --index 6 "BASH_FRAMEWORK_LOG_FILE = \"${TMPDIR}/logFile\"" assert_line --index 7 'BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION = "5"' assert_line --index 8 'BASH_FRAMEWORK_LOG_LEVEL = "0"' - assert_line --index 9 'BASH_FRAMEWORK_THEME = "noColor"' + assert_line --index 9 'BASH_FRAMEWORK_THEME = "default"' assert_line --index 10 "--------------------------------------------------------------------------------" assert_line --index 11 'POSTMAN_API_KEY = ...(truncated)' assert_lines_count 12 From bb01c30406025987c3bd2820155a454acd91f285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Sat, 7 Sep 2024 08:07:01 +0200 Subject: [PATCH 16/24] compiled upgradeGithubRelease using go compiler --- bin/upgradeGithubRelease | 3050 +++++++++-------- src/_binaries/Git/testsData/.env | 52 - .../testsData/upgradeGithubRelease.help.txt | 130 - .../Git/upgradeGithubRelease.help.txt | 34 - .../Git/upgradeGithubRelease.options.tpl | 120 - src/_binaries/Git/upgradeGithubRelease.sh | 47 - .../testsData/upgradeGithubRelease.help.txt | 115 + .../testsData/upgradeGithubRelease_bin | 0 .../upgradeGithubRelease-binary.yaml | 79 + .../upgradeGithubRelease-main.sh | 20 + .../upgradeGithubRelease-options.sh | 89 + .../upgradeGithubRelease.bats | 2 +- 12 files changed, 1987 insertions(+), 1751 deletions(-) delete mode 100755 src/_binaries/Git/testsData/.env delete mode 100644 src/_binaries/Git/testsData/upgradeGithubRelease.help.txt delete mode 100644 src/_binaries/Git/upgradeGithubRelease.help.txt delete mode 100644 src/_binaries/Git/upgradeGithubRelease.options.tpl delete mode 100755 src/_binaries/Git/upgradeGithubRelease.sh create mode 100644 src/_binaries/Github/upgradeGithubRelease/testsData/upgradeGithubRelease.help.txt rename src/_binaries/{Git => Github/upgradeGithubRelease}/testsData/upgradeGithubRelease_bin (100%) create mode 100644 src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease-binary.yaml create mode 100755 src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease-main.sh create mode 100755 src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease-options.sh rename src/_binaries/{Git => Github/upgradeGithubRelease}/upgradeGithubRelease.bats (99%) diff --git a/bin/upgradeGithubRelease b/bin/upgradeGithubRelease index 6b64b222..be737dbe 100755 --- a/bin/upgradeGithubRelease +++ b/bin/upgradeGithubRelease @@ -1,13 +1,12 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FACADE FROM https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Git/upgradeGithubRelease.sh +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Git/upgradeGithubRelease/upgradeGithubRelease-binary.yaml # DO NOT EDIT IT # @generated ############################################################################### # shellcheck disable=SC2288,SC2034 -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/upgradeGithubRelease -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE + + # ensure that no user aliases could interfere with # commands used in this script @@ -62,13 +61,6 @@ interruptManagement() { kill -s INT "$$" } trap interruptManagement INT -SCRIPT_NAME=${0##*/} -REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" -if [[ -n "${EMBED_CURRENT_DIR}" ]]; then - CURRENT_DIR="${EMBED_CURRENT_DIR}" -else - CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" -fi ################################################ # Temp dir management @@ -102,6 +94,20 @@ cleanOnExit() { } trap cleanOnExit EXIT HUP QUIT ABRT TERM + +SCRIPT_NAME=${0##*/} +REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" +if [[ -n "${EMBED_CURRENT_DIR}" ]]; then + CURRENT_DIR="${EMBED_CURRENT_DIR}" +else + CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" +fi +FRAMEWORK_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" +FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" +FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + # @description Log namespace provides 2 kind of functions # - Log::display* allows to display given message with # given display level @@ -132,175 +138,275 @@ export __VERBOSE_LEVEL_DEBUG=2 # @description verbose level info export __VERBOSE_LEVEL_TRACE=3 -# @description Display message using info color (bg light blue/fg white) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayInfo() { - local type="${2:-INFO}" - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description concatenate each element of an array with a separator +# but wrapping text when line length is more than provided argument +# The algorithm will try not to cut the array element if it can. +# - if an arg can be placed on current line it will be, +# otherwise current line is printed and arg is added to the new +# current line +# - Empty arg is interpreted as a new line. +# - Add \r to arg in order to force break line and avoid following +# arg to be concatenated with current arg. +# +# @arg $1 glue:String +# @arg $2 maxLineLength:int +# @arg $3 indentNextLine:int +# @arg $@ array:String[] +Array::wrap2() { + local glue="${1-}" + local -i glueLength="${#glue}" + shift || true + local -i maxLineLength=$1 + shift || true + local -i indentNextLine=$1 + shift || true + local indentStr="" + if ((indentNextLine > 0)); then + indentStr="$(head -c "${indentNextLine}" 0)); do + arg="$1" + shift || true + + # replace tab by 2 spaces + arg="${arg//$'\t'/ }" + # remove trailing spaces + arg="${arg%[[:blank:]]}" + if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then + printCurrentLine + ((previousLineEmpty = 1)) + continue + else + if ((previousLineEmpty == 1)); then + printCurrentLine + fi + ((previousLineEmpty = 0)) || true + fi + # convert eol to args + mapfile -t additionalLines <<<"${arg}" + if ((${#additionalLines[@]} > 1)); then + set -- "${additionalLines[@]}" "$@" + continue + fi + + ((argLength = ${#arg})) || true + + # empty arg + if ((argLength == 0)); then + if ((isNewline == 0)); then + # isNewline = 0 means currentLine is not empty + printCurrentLine + fi + continue + fi + + if ((isNewline == 0)); then + glueLength="${#glue}" + else + glueLength="0" + fi + if ((currentLineLength + argLength + glueLength > maxLineLength)); then + if ((argLength + glueLength > maxLineLength)); then + # arg is too long to even fit on one line + # we have to split the arg on current and next line + local -i remainingLineLength + ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) + appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" + printCurrentLine + arg="${arg:${remainingLineLength}}" + # remove leading spaces + arg="${arg##[[:blank:]]}" + + set -- "${arg}" "$@" + else + # the arg can fit on next line + printCurrentLine + appendToCurrentLine "${arg}" "${argLength}" + fi + else + appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" + fi + done + if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then + printCurrentLine + fi + ) | sed -E -e 's/[[:blank:]]+$//' } -# @description Display message using debug color (gray) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayDebug() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then - Log::computeDuration - echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logDebug "$1" + +# @description check if command specified exists or return 1 +# with error and message if not +# +# @arg $1 commandName:String on which existence must be checked +# @arg $2 helpIfNotExists:String a help command to display if the command does not exist +# +# @exitcode 1 if the command specified does not exist +# @stderr diagnostic information + help if second argument is provided +Assert::commandExists() { + local commandName="$1" + local helpIfNotExists="$2" + + "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { + Log::displayError "${commandName} is not installed, please install it" + if [[ -n "${helpIfNotExists}" ]]; then + Log::displayInfo "${helpIfNotExists}" + fi + return 1 + } + return 0 } -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# Checks if file can be created in folder +# + +# @description Checks if file can be created in folder +# The file does not need to exist +# @arg $1 file:String +# @exitcode 1 if file is not a valid path +# @exitcode 2 if file parent's dir is not writable +# @exitcode 3 if existing file is not writable +# @see Assert::validPath +Assert::fileWritable() { + local file="$1" + + Assert::validPath "${file}" || return 1 + if [[ -f "${file}" ]]; then + [[ -w "${file}" ]] || return 3 + else + [[ -w "${file%/*}" ]] || return 2 fi - Log::logWarning "$1" + } -# @description Display message using error color (red) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayError() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then - Log::computeDuration - echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description check if tty (interactive mode) is active +# @noargs +# @exitcode 1 if tty not active +# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive +# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive +Assert::tty() { + if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then + return 1 fi - Log::logError "$1" + if [[ "${INTERACTIVE:-0}" = "1" ]]; then + return 0 + fi + tty -s } -# @description load colors theme constants -# @warning if tty not opened, noColor theme will be chosen -# @arg $1 theme:String the theme to use (default, noColor) -# @arg $@ args:String[] -# @set __ERROR_COLOR String indicate error status -# @set __INFO_COLOR String indicate info status -# @set __SUCCESS_COLOR String indicate success status -# @set __WARNING_COLOR String indicate warning status -# @set __SKIPPED_COLOR String indicate skipped status -# @set __DEBUG_COLOR String indicate debug status -# @set __HELP_COLOR String indicate help status -# @set __TEST_COLOR String not used -# @set __TEST_ERROR_COLOR String not used -# @set __HELP_TITLE_COLOR String used to display help title in help strings -# @set __HELP_OPTION_COLOR String used to display highlight options in help strings -# -# @set __RESET_COLOR String reset default color + +# @description check if argument is a valid linux path +# invalid path are those with: +# - invalid characters +# - component beginning by a - (because could be considered as a command's option) +# - not beginning with a slash +# - relative # -# @set __HELP_EXAMPLE String to remove -# @set __HELP_TITLE String to remove -# @set __HELP_NORMAL String to remove -# shellcheck disable=SC2034 -UI::theme() { - local theme="${1-default}" - if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then - theme="noColor" - fi - case "${theme}" in - default | default-force) - theme="default" - ;; - noColor) ;; - *) - Log::fatal "invalid theme provided" - ;; - esac - if [[ "${theme}" = "default" ]]; then - BASH_FRAMEWORK_THEME="default" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='\e[31m' # Red - __INFO_COLOR='\e[44m' # white on lightBlue - __SUCCESS_COLOR='\e[32m' # Green - __WARNING_COLOR='\e[33m' # Yellow - __SKIPPED_COLOR='\e[33m' # Yellow - __DEBUG_COLOR='\e[37m' # Gray - __HELP_COLOR='\e[7;49;33m' # Black on Gold - __TEST_COLOR='\e[100m' # Light magenta - __TEST_ERROR_COLOR='\e[41m' # white on red - __HELP_TITLE_COLOR="\e[1;37m" # Bold - __HELP_OPTION_COLOR="\e[1;34m" # Blue - # Internal: reset color - __RESET_COLOR='\e[0m' # Reset Color - # shellcheck disable=SC2155,SC2034 - __HELP_EXAMPLE="$(echo -e "\e[2;97m")" - # shellcheck disable=SC2155,SC2034 - __HELP_TITLE="$(echo -e "\e[1;37m")" - # shellcheck disable=SC2155,SC2034 - __HELP_NORMAL="$(echo -e "\033[0m")" - else - BASH_FRAMEWORK_THEME="noColor" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='' - __INFO_COLOR='' - __SUCCESS_COLOR='' - __WARNING_COLOR='' - __SKIPPED_COLOR='' - __DEBUG_COLOR='' - __HELP_COLOR='' - __TEST_COLOR='' - __TEST_ERROR_COLOR='' - __HELP_TITLE_COLOR='' - __HELP_OPTION_COLOR='' - # Internal: reset color - __RESET_COLOR='' - __HELP_EXAMPLE='' - __HELP_TITLE='' - __HELP_NORMAL='' - fi -} +# @arg $1 path:string path that needs to be checked +# @exitcode 1 if path is invalid +# @see https://regex101.com/r/afLrmM/2 +# @see Assert::validPosixPath if you need more restrictive check +Assert::validPath() { + local path="$1" -# @description draw a line with the character passed in parameter repeated depending on terminal width -# @arg $1 character:String character to use as separator (default value #) -UI::drawLine() { - local character="${1:-#}" - local -i width=${COLUMNS:-0} - if ((width == 0)) && [[ -t 1 ]]; then - width=$(tput cols) - fi - if ((width == 0)); then - width=80 - fi - printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") - echo + [[ "${path}" =~ ^\/$|^(\/[.a-zA-Z_0-9][.a-zA-Z_0-9-]*)+$ ]] && + [[ ! "${path}" =~ (\/\.\.)|(\.\.\/)|^\.$|^\.\.$ ]] # avoid relative } -# @description Display message using error color (red) and exit immediately with error status 1 -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::fatal() { - Log::computeDuration - echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - Log::logFatal "$1" - exit 1 + +# @description ignore exit code 141 from simple command pipes +# @example use with: +# local resultingStatus=0 +# local -a originalPipeStatus=() +# cmd1 | cmd2 || Bash::handlePipelineFailure resultingStatus originalPipeStatus || true +# [[ "${resultingStatus}" = "0" ]] +# @arg $1 resultingStatusCode:&int (passed by reference) (optional) resulting status code +# @arg $2 originalStatus:int[] (passed by reference) (optional) copy of original PIPESTATUS array +# @env PIPESTATUS assuming that this function is called like in the example provided +# @see https://unix.stackexchange.com/a/709880/582856 +Bash::handlePipelineFailure() { + local -a pipeStatusBackup=("${PIPESTATUS[@]}") + local -n handlePipelineFailure_resultingStatusCode=$1 + local -n handlePipelineFailure_originalStatus=$2 + # shellcheck disable=SC2034 + handlePipelineFailure_originalStatus=("${pipeStatusBackup[@]}") + handlePipelineFailure_resultingStatusCode=0 + local statusCode + for statusCode in "${pipeStatusBackup[@]}"; do + if ((statusCode == 141)); then + return 0 + elif ((statusCode > 0)); then + # shellcheck disable=SC2034 + handlePipelineFailure_resultingStatusCode="${statusCode}" + break + fi + done + return "${handlePipelineFailure_resultingStatusCode}" } -# @description create a temp file using default TMPDIR variable -# initialized in _includes/_commonHeader.sh -# @env TMPDIR String (default value /tmp) -# @arg $1 templateName:String template name to use(optional) -Framework::createTempFile() { - mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" + +# @description check if all requirements are satisfied +# to execute dbImport commands +Db::checkRequirements() { + if [[ "${SKIP_REQUIREMENTS_CHECKS:-0}" = "1" ]]; then + return 0 + fi + local -i failures=0 + echo + Assert::commandExists mysql "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqldump "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists pv "sudo apt-get install -y pv" || ((++failures)) + Assert::commandExists gawk "sudo apt-get install -y gawk" || ((++failures)) + Assert::commandExists awk "sudo apt-get install -y gawk" || ((++failures)) + Version::checkMinimal "gawk" "--version" "5.0.1" || ((++failures)) + return "${failures}" } + # @description ensure env files are loaded # @arg $@ list of default files to load at the end # @exitcode 1 if one of env files fails to load # @stderr diagnostics information is displayed # shellcheck disable=SC2120 Env::requireLoad() { + export REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 + local -a defaultFiles=("$@") # get list of possible config files local -a configFiles=() @@ -328,234 +434,87 @@ Env::requireLoad() { done } -# @description activate or not Log::display* and Log::log* functions -# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL -# environment variables loaded by Env::requireLoad -# try to create log file and rotate it if necessary -# @noargs -# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable -# @env BASH_FRAMEWORK_DISPLAY_LEVEL int -# @env BASH_FRAMEWORK_LOG_LEVEL int -# @env BASH_FRAMEWORK_LOG_FILE String -# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 -# @exitcode 0 always successful -# @stderr diagnostics information about log file is displayed -# @require Env::requireLoad -# @require UI::requireTheme -Log::requireLoad() { - if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - export BASH_FRAMEWORK_LOG_LEVEL - fi - - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then - if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 - fi - elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - fi - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - # will always be created even if not in info level - Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" - if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then - Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" - fi - fi +# @description create a temp file using default TMPDIR variable +# @env TMPDIR String (default value /tmp) +# @arg $1 templateName:String template name to use(optional) +Framework::createTempFile() { + mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" } -# @description concatenate each element of an array with a separator -# but wrapping text when line length is more than provided argument -# The algorithm will try not to cut the array element if it can. -# - if an arg can be placed on current line it will be, -# otherwise current line is printed and arg is added to the new -# current line -# - Empty arg is interpreted as a new line. -# - Add \r to arg in order to force break line and avoid following -# arg to be concatenated with current arg. -# -# @arg $1 glue:String -# @arg $2 maxLineLength:int -# @arg $3 indentNextLine:int -# @arg $@ array:String[] -Array::wrap2() { - local glue="${1-}" - local -i glueLength="${#glue}" - shift || true - local -i maxLineLength=$1 - shift || true - local -i indentNextLine=$1 - shift || true - local indentStr="" - if ((indentNextLine > 0)); then - indentStr="$(head -c "${indentNextLine}" 0)); do - arg="$1" - shift || true - - # replace tab by 2 spaces - arg="${arg//$'\t'/ }" - # remove trailing spaces - arg="${arg%[[:blank:]]}" - if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then - printCurrentLine - ((previousLineEmpty = 1)) - continue - else - if ((previousLineEmpty == 1)); then - printCurrentLine - fi - ((previousLineEmpty = 0)) || true - fi - # convert eol to args - mapfile -t additionalLines <<<"${arg}" - if ((${#additionalLines[@]} > 1)); then - set -- "${additionalLines[@]}" "$@" - continue - fi - - ((argLength = ${#arg})) || true - - # empty arg - if ((argLength == 0)); then - if ((isNewline == 0)); then - # isNewline = 0 means currentLine is not empty - printCurrentLine - fi - continue - fi - - if ((isNewline == 0)); then - glueLength="${#glue}" - else - glueLength="0" - fi - if ((currentLineLength + argLength + glueLength > maxLineLength)); then - if ((argLength + glueLength > maxLineLength)); then - # arg is too long to even fit on one line - # we have to split the arg on current and next line - local -i remainingLineLength - ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) - appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" - printCurrentLine - arg="${arg:${remainingLineLength}}" - # remove leading spaces - arg="${arg##[[:blank:]]}" - - set -- "${arg}" "$@" - else - # the arg can fit on next line - printCurrentLine - appendToCurrentLine "${arg}" "${argLength}" - fi - else - appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" - fi - done - if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then - printCurrentLine - fi - ) | sed -E -e 's/[[:blank:]]+$//' -} - -# @description check if argument is a valid linux path -# invalid path are those with: -# - invalid characters -# - component beginning by a - (because could be considered as a command's option) -# - not beginning with a slash -# - relative -# -# @arg $1 path:string path that needs to be checked -# @exitcode 1 if path is invalid -# @see https://regex101.com/r/afLrmM/2 -# @see Assert::validPosixPath if you need more restrictive check -Assert::validPath() { - local path="$1" - - [[ "${path}" =~ ^\/$|^(\/[.a-zA-Z_0-9][.a-zA-Z_0-9-]*)+$ ]] && - [[ ! "${path}" =~ (\/\.\.)|(\.\.\/)|^\.$|^\.\.$ ]] # avoid relative +# @description github repository eg: kubernetes-sigs/kind +# @arg $1 githubUrl:String eg: https://github.com/kubernetes-sigs/kind/releases/download/@latestVersion@/kind-linux-amd64 +# @exitcode 1 if no matching repo found in provided url, 0 otherwise +# @stdout the repo in the form owner/repo +Github::extractRepoFromGithubUrl() { + local githubUrl="$1" + local result + result="$(sed -n -E 's#^https://github.com/([^/]+/[^/]+)/.*$#\1#p' <<<"${githubUrl}")" + if [[ -z "${result}" ]]; then + return 1 + fi + echo "${result}" } -# Checks if file can be created in folder -# - -# @description Checks if file can be created in folder -# The file does not need to exist -# @arg $1 file:String -# @exitcode 1 if file is not a valid path -# @exitcode 2 if file parent's dir is not writable -# @exitcode 3 if existing file is not writable -# @see Assert::validPath -Assert::fileWritable() { - local file="$1" - Assert::validPath "${file}" || return 1 - if [[ -f "${file}" ]]; then - [[ -w "${file}" ]] || return 3 - else - [[ -w "${file%/*}" ]] || return 2 - fi +# @description check if specified release software version exists in github +# @arg $1 releaseUrl:String eg: https://github.com/kubernetes-sigs/kind/releases/download/v1.0.0/kind-linux-amd64 +# @exitcode 1 on failure +# @exitcode 0 if release version exists +# @env CURL_CONNECT_TIMEOUT number of seconds before giving up host connection +Github::isReleaseVersionExist() { + local releaseUrl="$1" + curl \ + -L \ + --connect-timeout "${CURL_CONNECT_TIMEOUT:-5}" \ + -o /dev/null \ + --silent \ + --head \ + --fail \ + "${releaseUrl}" } -# @description filter to keep only version number from a string -# @arg $@ files:String[] the files to filter -# @exitcode * if one of the filter command fails -# @stdin you can use stdin as alternative to files argument -# @stdout the filtered content -# shellcheck disable=SC2120 -Version::parse() { - # match anything, print(p), exit on first match(Q) - sed -En \ - -e 's/\x1b\[[0-9;]*[mGKHF]//g' \ - -e 's/[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/' \ - -e '//{p;Q}' \ - "$@" -} # @description upgrade given binary to latest github release using retry # @@ -598,152 +557,18 @@ Github::upgradeRelease() { "${EXACT_VERSION:-}" } -# @description intermediate callback that is used by Github::upgradeRelease -# or Github::installRelease -# if installCallback is not set, it allows to: -# - copy the downloaded file to the right target file -# - and set the execution bit -# else -# installCallback is called with newSoftware, targetFile, version arguments -# fi -# @warning do not use this function as callback for Github::upgradeRelease or Github::installRelease, as it would result to an infinite loop -# @arg $1 newSoftware:String the downloaded software file -# @arg $2 targetFile:String where we want to copy the file -# @arg $3 version:String the version that has been downloaded -# @arg $4 installCallback:Function (optional) the callback to call with 3 first arguments -# @env SUDO String allows to use custom sudo prefix command -# @exitcode * on failure -# @see Github::upgradeRelease -# @see Github::installRelease -# @internal -Github::defaultInstall() { - local newSoftware="$1" - local targetFile="$2" - local version="$3" - local installCallback=$4 - # shellcheck disable=SC2086 - if ! ${SUDO:-} test -d "${targetFile%/*}"; then - ${SUDO:-} mkdir -p "${targetFile%/*}" - fi - if [[ "$(type -t "${installCallback}")" = "function" ]]; then - ${installCallback} "${newSoftware}" "${targetFile}" "${version}" - else - ${SUDO:-} mv "${newSoftware}" "${targetFile}" - ${SUDO:-} chmod +x "${targetFile}" - hash -r - ${SUDO:-} rm -f "${newSoftware}" || true - Log::displaySuccess "Version ${version} installed in ${targetFile}" - fi -} -bashToolsDefaultConfigTemplate="${bashToolsDefaultConfigTemplate:-$( - cat <<'EOF' -# shellcheck disable=SC2034 +# @description ensure command jq is available +# @exitcode 1 if jq command not available +# @stderr diagnostics information is displayed +Linux::requireJqCommand() { + export REQUIRE_FUNCTION_LINUX_REQUIRE_JQ_COMMAND_LOADED=1 -# Default settings -# you can override these settings by creating ${HOME}/.bash-tools/.env file - -### -### DISPLAY Level -### minimum level of the messages that will be displayed on screen -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} - -### -### DISPLAY duration -### 0: no duration is displayed on the messages -### 1: duration between previous message and current is displayed -### with the message -### -DISPLAY_DURATION=${DISPLAY_DURATION:0} - -### -### Log to file -### -### all log messages will be redirected to log file specified -### this same path will be used inside and outside of the container -### -BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} - -### -### LOG Level -### minimum level of the messages that will be logged into LOG_FILE -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} - -# absolute directory containing db import sql dumps -DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} - -# garbage collect all files for which modification is greater than eg: 30 days (+30) -# each time an existing file is used by dbImport/dbImportTable -# the file modification time is set to now -DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} - -# absolute directory containing dbScripts used by dbScriptAllDatabases -SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} - -# ----------------------------------------------------- -# AWS Parameters -# ----------------------------------------------------- -S3_BASE_URL=${S3_BASE_URL:-} - -# ----------------------------------------------------- -# Postman Parameters -# ----------------------------------------------------- -POSTMAN_API_KEY= -EOF -)}" - -# @description loads ~/.bash-tools/.env if available -# if not creates it from a default template -# else check if new options need to be added -BashTools::Conf::requireLoad() { - local envFile="${HOME}/.bash-tools/.env" - if [[ ! -f "${envFile}" ]]; then - mkdir -p "${HOME}/.bash-tools" - ( - echo "#!/usr/bin/env bash" - echo "${bashToolsDefaultConfigTemplate}" - ) >"${envFile}" - Log::displayInfo "Configuration file '${envFile}' created" - else - if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then - ( - echo '# -----------------------------------------------------' - echo '# Postman Parameters' - echo '# -----------------------------------------------------' - echo 'POSTMAN_API_KEY=' - ) >>"${envFile}" - fi + if [[ "${SKIP_REQUIRE_JQ:-0}" = "0" && "${SKIP_REQUIRES:-0}" = "0" ]]; then + Assert::commandExists jq fi - # shellcheck source=/conf/.env - source "${envFile}" || { - Log::displayError "impossible to load '${envFile}'" - exit 1 - } } -# @description ensure COMMAND_BIN_DIR env var is set -# and PATH correctly prepared -# @noargs -# @set COMMAND_BIN_DIR string the directory where to find this command -# @set PATH string add directory where to find this command binary -Compiler::Facade::requireCommandBinDir() { - COMMAND_BIN_DIR="${CURRENT_DIR}" - Env::pathPrepend "${COMMAND_BIN_DIR}" -} declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" @@ -783,59 +608,119 @@ Log::computeDuration() { fi } -# @description log message to file + +# @description Display message using debug color (gray) # @arg $1 message:String the message to display -Log::logInfo() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then - Log::logMessage "${2:-INFO}" "$1" +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayDebug() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then + Log::computeDuration + echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logDebug "$1" } -# @description log message to file + +# @description Display message using error color (red) # @arg $1 message:String the message to display -Log::logDebug() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then - Log::logMessage "${2:-DEBUG}" "$1" +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayError() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then + Log::computeDuration + echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logError "$1" } -# @description log message to file + +# @description Display message using info color (bg light blue/fg white) # @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayInfo() { + local type="${2:-INFO}" + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logInfo "$1" "${type}" } -# @description log message to file + +# @description Display message using success color (bg green/fg white) # @arg $1 message:String the message to display -Log::logError() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_ERROR)); then - Log::logMessage "${2:-ERROR}" "$1" +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displaySuccess() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__SUCCESS_COLOR}SUCCESS - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logSuccess "$1" } -# @description check if tty (interactive mode) is active -# @noargs -# @exitcode 1 if tty not active -# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive -# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive -Assert::tty() { - if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then - return 1 - fi - if [[ "${INTERACTIVE:-0}" = "1" ]]; then - return 0 + +# @description Display message using warning color (yellow) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayWarning() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then + Log::computeDuration + echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logWarning "$1" +} + + +# @description Display message using error color (red) and exit immediately with error status 1 +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::fatal() { + Log::computeDuration + echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + Log::logFatal "$1" + exit 1 +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logDebug() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then + Log::logMessage "${2:-DEBUG}" "$1" + fi +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logError() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_ERROR)); then + Log::logMessage "${2:-ERROR}" "$1" fi - tty -s } + # @description log message to file # @arg $1 message:String the message to display Log::logFatal() { Log::logMessage "${2:-FATAL}" "$1" } + +# @description log message to file +# @arg $1 message:String the message to display +Log::logInfo() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then + Log::logMessage "${2:-INFO}" "$1" + fi +} + + # @description Internal: common log message # @example text # [date]|[levelMsg]|message @@ -849,9 +734,18 @@ Log::logFatal() { # @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty # @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages # @stderr diagnostics information is displayed -# @require Env::requireLoad -# @require Log::requireLoad Log::logMessage() { + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Log::requireLoad has not been loaded" + exit 1 + fi + local levelMsg="$1" local msg="$2" local date @@ -863,6 +757,83 @@ Log::logMessage() { fi } + +# @description log message to file +# @arg $1 message:String the message to display +Log::logSuccess() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then + Log::logMessage "${2:-SUCCESS}" "$1" + fi +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logWarning() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then + Log::logMessage "${2:-WARNING}" "$1" + fi +} + + +# @description activate or not Log::display* and Log::log* functions +# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL +# environment variables loaded by Env::requireLoad +# try to create log file and rotate it if necessary +# @noargs +# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable +# @env BASH_FRAMEWORK_DISPLAY_LEVEL int +# @env BASH_FRAMEWORK_LOG_LEVEL int +# @env BASH_FRAMEWORK_LOG_FILE String +# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 +# @exitcode 0 always successful +# @stderr diagnostics information about log file is displayed +Log::requireLoad() { + export REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement UI::requireTheme has not been loaded" + exit 1 + fi + + if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + export BASH_FRAMEWORK_LOG_LEVEL + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then + if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 + fi + elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + # will always be created even if not in info level + Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" + if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then + Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + fi + fi +} + + # @description To be called before logging in the log file # @arg $1 file:string log file name # @arg $2 maxLogFilesCount:int maximum number of log files @@ -885,127 +856,71 @@ Log::rotate() { fi } -# @description extract software version number -# @arg $1 command:String the command that will be called with --version parameter -# @arg $2 argVersion:String allows to override default --version parameter -Version::getCommandVersionFromPlainText() { - local command="$1" - local argVersion="${2:---version}" - "${command}" "${argVersion}" 2>&1 | - Version::parse # keep only version numbers -} - -# @description github repository eg: kubernetes-sigs/kind -# @arg $1 githubUrl:String eg: https://github.com/kubernetes-sigs/kind/releases/download/@latestVersion@/kind-linux-amd64 -# @exitcode 1 if no matching repo found in provided url, 0 otherwise -# @stdout the repo in the form owner/repo -Github::extractRepoFromGithubUrl() { - local githubUrl="$1" - local result - result="$(sed -n -E 's#^https://github.com/([^/]+/[^/]+)/.*$#\1#p' <<<"${githubUrl}")" - if [[ -z "${result}" ]]; then - return 1 - fi - echo "${result}" -} -# @description extract version number from github api -# @noargs -# @stdin json result of github API -# @exitcode 1 if jq or Version::parse fails -# @stdout the version parsed -# @require Linux::requireJqCommand -Version::githubApiExtractVersion() { - jq -r ".tag_name" +# @description Retry a command 5 times with a delay of 15 seconds between each attempt +# @arg $@ command:String[] the command to run +# @exitcode 0 on success +# @exitcode 1 if max retries count reached +# @env RETRY_MAX_RETRY int max retries +# @env RETRY_DELAY_BETWEEN_RETRIES int delay between attempts +Retry::default() { + Retry::parameterized "${RETRY_MAX_RETRY:-5}" "${RETRY_DELAY_BETWEEN_RETRIES:-15}" "" "$@" } -# @description upgrade given binary to latest release using retry -# -# releasesUrl argument : the placeholder @latestVersion@ will be replaced by the latest release version -# @arg $1 targetFile:String target binary file (eg: /usr/local/bin/kind) -# @arg $2 releasesUrl:String url on which we can query all available versions (eg: "https://go.dev/dl/?mode=json") -# @arg $3 downloadReleaseUrl:String url from which the software will be downloaded (eg: https://storage.googleapis.com/golang/go@latestVersion@.linux-amd64.tar.gz) -# @arg $4 softVersionArg:String parameter to add to existing command to compute current version -# @arg $5 exactVersion:String if you want to retrieve a specific version instead of the latest -# @stdout log messages about retry, install, upgrade -# @env FILTER_LAST_VERSION_CALLBACK a callback to filter the latest version from releasesUrl -# @env SOFT_VERSION_CALLBACK a callback to execute command version -# @env PARSE_VERSION_CALLBACK a callback to parse the version of the existing command -# @env INSTALL_CALLBACK a callback to install the software downloaded -# @env CURL_CONNECT_TIMEOUT number of seconds before giving up host connection -Web::upgradeRelease() { - local targetFile="$1" - local releasesUrl="$2" - local downloadReleaseUrl="$3" - local softVersionArg="${4:---version}" - local exactVersion="${5:-}" - # options from env variables - local filterLastVersionCallback="${FILTER_LAST_VERSION_CALLBACK:-Version::parse}" - local softVersionCallback="${SOFT_VERSION_CALLBACK:-Version::getCommandVersionFromPlainText}" - local installCallback="${INSTALL_CALLBACK:-}" - local latestVersion - latestVersion="$(Web::getReleases "${releasesUrl}" | ${filterLastVersionCallback})" || { - Log::displayError "latest version not found on ${releasesUrl}" - return 1 - } - Log::displayInfo "Latest version found is ${latestVersion}" - local currentVersion="not existing" - if [[ -f "${targetFile}" ]]; then - currentVersion="$(${softVersionCallback} "${targetFile}" "${softVersionArg}" 2>&1 || true)" - fi - if [[ -z "${exactVersion}" ]]; then - exactVersion="${latestVersion}" - fi - local url="${downloadReleaseUrl//@latestVersion@/${exactVersion}}" - if [[ -n "${exactVersion}" ]] && ! Github::isReleaseVersionExist "${url}"; then - Log::displayError "${targetFile} version ${exactVersion} doesn't exist on github" +# @description Retry a command several times depending on parameters +# @arg $1 maxRetries:int $1 max retries +# @arg $2 delay:int between attempt +# @arg $3 message:String to display to describe the attempt +# @arg $@ rest of parameters, the command to run +# @exitcode 0 on success +# @exitcode 1 if max retries count reached +# @exitcode 2 if maxRetries invalid value +Retry::parameterized() { + local maxRetries=$1 + shift || true + local delayBetweenTries=$1 + shift || true + local message="$1" + shift || true + local retriesCount=1 + if [[ "${maxRetries}" -lt 1 ]]; then + Log::displayError "invalid maxRetry value" return 2 fi - if [[ "${currentVersion}" = "${exactVersion}" ]]; then - Log::displayInfo "${targetFile} version ${exactVersion} already installed" - else - if [[ -z "${currentVersion}" ]]; then - Log::displayInfo "Installing ${targetFile} with version ${exactVersion}" + + while true; do + Log::displayInfo "Attempt ${retriesCount}/${maxRetries}: ${message}" + if "$@"; then + break + elif [[ "${retriesCount}" -lt "${maxRetries}" ]]; then + Log::displayDebug "Command failed. Wait for ${delayBetweenTries} seconds" + ((retriesCount++)) + sleep "${delayBetweenTries}" else - Log::displayInfo "Upgrading ${targetFile} from version ${currentVersion} to ${exactVersion}" + Log::displayError "The command has failed after ${retriesCount} attempts." + return 1 fi - Log::displayInfo "Using url ${url}" - newSoftware=$(mktemp -p "${TMPDIR:-/tmp}" -t web.newSoftware.XXXX) - Retry::default curl \ - -L \ - --connect-timeout "${CURL_CONNECT_TIMEOUT:-5}" \ - -o "${newSoftware}" \ - --fail \ - "${url}" + done + return 0 +} - Github::defaultInstall "${newSoftware}" "${targetFile}" "${exactVersion}" "${installCallback}" - fi -} -# @description Display message using success color (bg green/fg white) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displaySuccess() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__SUCCESS_COLOR}SUCCESS - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 +# @description draw a line with the character passed in parameter repeated depending on terminal width +# @arg $1 character:String character to use as separator (default value #) +UI::drawLine() { + local character="${1:-#}" + local -i width=${COLUMNS:-0} + if ((width == 0)) && [[ -t 1 ]]; then + width=$(tput cols) fi - Log::logSuccess "$1" + if ((width == 0)); then + width=80 + fi + printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") + echo } -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" - fi - done -} # @description load color theme # @noargs @@ -1013,180 +928,307 @@ Env::pathPrepend() { # @env LOAD_THEME int 0 to avoid loading theme # @exitcode 0 always successful UI::requireTheme() { + export REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + if [[ "${LOAD_THEME:-1}" = "1" ]]; then UI::theme "${BASH_FRAMEWORK_THEME-default}" fi } -# @description Retrieve the latest version number of a web release -# @arg $1 releaseListUrl:String the url from which version list can be retrieved -# @stdout log messages about retry -# @env CURL_CONNECT_TIMEOUT number of seconds before giving up host connection -Web::getReleases() { - local releaseListUrl="$1" - # Get latest release from GitHub api - Retry::parameterized "${RETRY_MAX_RETRY:-5}" "${RETRY_DELAY_BETWEEN_RETRIES:-15}" "Retrieving release versions list ..." curl \ - -L \ - --connect-timeout "${CURL_CONNECT_TIMEOUT:-5}" \ - --fail \ - --silent \ - "${releaseListUrl}" + +# @description load colors theme constants +# @warning if tty not opened, noColor theme will be chosen +# @arg $1 theme:String the theme to use (default, noColor) +# @arg $@ args:String[] +# @set __ERROR_COLOR String indicate error status +# @set __INFO_COLOR String indicate info status +# @set __SUCCESS_COLOR String indicate success status +# @set __WARNING_COLOR String indicate warning status +# @set __SKIPPED_COLOR String indicate skipped status +# @set __DEBUG_COLOR String indicate debug status +# @set __HELP_COLOR String indicate help status +# @set __TEST_COLOR String not used +# @set __TEST_ERROR_COLOR String not used +# @set __HELP_TITLE_COLOR String used to display help title in help strings +# @set __HELP_OPTION_COLOR String used to display highlight options in help strings +# +# @set __RESET_COLOR String reset default color +# +# @set __HELP_EXAMPLE String to remove +# @set __HELP_TITLE String to remove +# @set __HELP_NORMAL String to remove +# shellcheck disable=SC2034 +UI::theme() { + local theme="${1-default}" + if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then + theme="noColor" + fi + case "${theme}" in + default | default-force) + theme="default" + ;; + noColor) ;; + *) + Log::fatal "invalid theme provided" + ;; + esac + if [[ "${theme}" = "default" ]]; then + BASH_FRAMEWORK_THEME="default" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='\e[31m' # Red + __INFO_COLOR='\e[44m' # white on lightBlue + __SUCCESS_COLOR='\e[32m' # Green + __WARNING_COLOR='\e[33m' # Yellow + __SKIPPED_COLOR='\e[33m' # Yellow + __DEBUG_COLOR='\e[37m' # Gray + __HELP_COLOR='\e[7;49;33m' # Black on Gold + __TEST_COLOR='\e[100m' # Light magenta + __TEST_ERROR_COLOR='\e[41m' # white on red + __HELP_TITLE_COLOR="\e[1;37m" # Bold + __HELP_OPTION_COLOR="\e[1;34m" # Blue + # Internal: reset color + __RESET_COLOR='\e[0m' # Reset Color + # shellcheck disable=SC2155,SC2034 + __HELP_EXAMPLE="$(echo -e "\e[2;97m")" + # shellcheck disable=SC2155,SC2034 + __HELP_TITLE="$(echo -e "\e[1;37m")" + # shellcheck disable=SC2155,SC2034 + __HELP_NORMAL="$(echo -e "\033[0m")" + else + BASH_FRAMEWORK_THEME="noColor" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='' + __INFO_COLOR='' + __SUCCESS_COLOR='' + __WARNING_COLOR='' + __SKIPPED_COLOR='' + __DEBUG_COLOR='' + __HELP_COLOR='' + __TEST_COLOR='' + __TEST_ERROR_COLOR='' + __HELP_TITLE_COLOR='' + __HELP_OPTION_COLOR='' + # Internal: reset color + __RESET_COLOR='' + __HELP_EXAMPLE='' + __HELP_TITLE='' + __HELP_NORMAL='' + fi } -# @description check if specified release software version exists in github -# @arg $1 releaseUrl:String eg: https://github.com/kubernetes-sigs/kind/releases/download/v1.0.0/kind-linux-amd64 -# @exitcode 1 on failure -# @exitcode 0 if release version exists -# @env CURL_CONNECT_TIMEOUT number of seconds before giving up host connection -Github::isReleaseVersionExist() { - local releaseUrl="$1" - curl \ - -L \ - --connect-timeout "${CURL_CONNECT_TIMEOUT:-5}" \ - -o /dev/null \ - --silent \ - --head \ - --fail \ - "${releaseUrl}" -} +# @description Check that command version is greater than expected minimal version +# display warning if command version greater than expected minimal version +# display error if command version less than expected minimal version and exit 1 +# @arg $1 commandName:String command path +# @arg $2 argVersion:String command line parameters to launch to get command version +# @arg $3 minimalVersion:String expected minimal command version +# @arg $4 parseVersionCallback:Function +# @arg $5 help:String optional help message to display if command does not exist +# @exitcode 0 if command version greater or equal to expected minimal version +# @exitcode 1 if command version less than expected minimal version +# @exitcode 2 if command does not exist +Version::checkMinimal() { + local commandName="$1" + local argVersion="$2" + local minimalVersion="$3" + local parseVersionCallback=${4:-Version::parse} + local help="${5:-}" -# @description Retry a command 5 times with a delay of 15 seconds between each attempt -# @arg $@ command:String[] the command to run -# @exitcode 0 on success -# @exitcode 1 if max retries count reached -# @env RETRY_MAX_RETRY int max retries -# @env RETRY_DELAY_BETWEEN_RETRIES int delay between attempts -Retry::default() { - Retry::parameterized "${RETRY_MAX_RETRY:-5}" "${RETRY_DELAY_BETWEEN_RETRIES:-15}" "" "$@" -} + Assert::commandExists "${commandName}" "${help}" || return 2 -# @description log message to file -# @arg $1 message:String the message to display -Log::logSuccess() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then - Log::logMessage "${2:-SUCCESS}" "$1" - fi -} + # shellcheck disable=SC2034 + local status=0 + # shellcheck disable=SC2034 + local -a pipeStatus=() + local version + version="$("${commandName}" "${argVersion}" 2>&1 | ${parseVersionCallback} || Bash::handlePipelineFailure status pipeStatus)" + + Log::displayDebug "check ${commandName} version ${version} against minimal ${minimalVersion}" + + Version::compare "${version}" "${minimalVersion}" || { + local result=$? + if [[ "${result}" = "1" ]]; then + Log::displayInfo "${commandName} version is ${version} greater than ${minimalVersion}" + elif [[ "${result}" = "2" ]]; then + Log::displayError "${commandName} minimal version is ${minimalVersion}, your version is ${version}" + return 1 + fi + return 0 + } -# @description ensure command jq is available -# @exitcode 1 if jq command not available -# @stderr diagnostics information is displayed -Linux::requireJqCommand() { - if [[ "${SKIP_REQUIRE_JQ:-0}" = "0" && "${SKIP_REQUIRES:-0}" = "0" ]]; then - Assert::commandExists jq - fi } -# @description Retry a command several times depending on parameters -# @arg $1 maxRetries:int $1 max retries -# @arg $2 delay:int between attempt -# @arg $3 message:String to display to describe the attempt -# @arg $@ rest of parameters, the command to run -# @exitcode 0 on success -# @exitcode 1 if max retries count reached -# @exitcode 2 if maxRetries invalid value -Retry::parameterized() { - local maxRetries=$1 - shift || true - local delayBetweenTries=$1 - shift || true - local message="$1" - shift || true - local retriesCount=1 - if [[ "${maxRetries}" -lt 1 ]]; then - Log::displayError "invalid maxRetry value" - return 2 - fi - while true; do - Log::displayInfo "Attempt ${retriesCount}/${maxRetries}: ${message}" - if "$@"; then - break - elif [[ "${retriesCount}" -lt "${maxRetries}" ]]; then - Log::displayDebug "Command failed. Wait for ${delayBetweenTries} seconds" - ((retriesCount++)) - sleep "${delayBetweenTries}" - else - Log::displayError "The command has failed after ${retriesCount} attempts." +# @description compare 2 version numbers +# @arg $1 version1:String version 1 +# @arg $2 version2:String version 2 +# @exitcode 0 if equal +# @exitcode 1 if version1 > version2 +# @exitcode 2 else +Version::compare() { + if [[ "$1" = "$2" ]]; then + return 0 + fi + local IFS=. + # shellcheck disable=2206 + local i ver1=($1) ver2=($2) + # fill empty fields in ver1 with zeros + for ((i = ${#ver1[@]}; i < ${#ver2[@]}; i++)); do + ver1[i]=0 + done + for ((i = 0; i < ${#ver1[@]}; i++)); do + if [[ -z "${ver2[i]+unset}" ]] || [[ -z ${ver2[i]} ]]; then + # fill empty fields in ver2 with zeros + ver2[i]=0 + fi + if ((10#${ver1[i]} > 10#${ver2[i]})); then return 1 fi + if ((10#${ver1[i]} < 10#${ver2[i]})); then + return 2 + fi done return 0 } -# @description check if command specified exists or return 1 -# with error and message if not -# -# @arg $1 commandName:String on which existence must be checked -# @arg $2 helpIfNotExists:String a help command to display if the command does not exist -# -# @exitcode 1 if the command specified does not exist -# @stderr diagnostic information + help if second argument is provided -Assert::commandExists() { - local commandName="$1" - local helpIfNotExists="$2" - "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { - Log::displayError "${commandName} is not installed, please install it" - if [[ -n "${helpIfNotExists}" ]]; then - Log::displayInfo "${helpIfNotExists}" - fi - return 1 - } - return 0 +# @description extract software version number +# @arg $1 command:String the command that will be called with --version parameter +# @arg $2 argVersion:String allows to override default --version parameter +Version::getCommandVersionFromPlainText() { + local command="$1" + local argVersion="${2:---version}" + "${command}" "${argVersion}" 2>&1 | + Version::parse # keep only version numbers } -# FUNCTIONS - -facade_main_upgradeGithubReleasesh() { -BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" -if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then - FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" -else - # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR - FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" -fi -FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" -FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" - -# @require BashTools::Conf::requireLoad -if [[ -f "${HOME}/.bash-tools/.env" ]]; then - export BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") -fi -# REQUIRES -Env::requireLoad -UI::requireTheme -Log::requireLoad -Linux::requireJqCommand -BashTools::Conf::requireLoad -Compiler::Facade::requireCommandBinDir - -# @require Compiler::Facade::requireCommandBinDir -# shellcheck disable=SC2034 -# default -declare defaultVersionArg="--version" +# @description extract version number from github api +# @noargs +# @stdin json result of github API +# @exitcode 1 if jq or Version::parse fails +# @stdout the version parsed +Version::githubApiExtractVersion() { -# option values -declare targetFileArg="" -declare githubUrlPatternArg="" -declare optionVersionArg="${defaultVersionArg}" -declare optionCurrentVersion="" -declare optionExactVersion="" + if [[ "${REQUIRE_FUNCTION_LINUX_REQUIRE_JQ_COMMAND_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Linux::requireJqCommand has not been loaded" + exit 1 + fi -# other values -declare copyrightBeginYear="2020" + jq -r ".tag_name" +} -declare -a BASH_FRAMEWORK_ARGV_FILTERED=() -copyrightCallback() { - if [[ -z "${copyrightBeginYear}" ]]; then - copyrightBeginYear="$(date +%Y)" - fi - echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" +# @description filter to keep only version number from a string +# @arg $@ files:String[] the files to filter +# @exitcode * if one of the filter command fails +# @stdin you can use stdin as alternative to files argument +# @stdout the filtered content +# shellcheck disable=SC2120 +Version::parse() { + # match anything, print(p), exit on first match(Q) + sed -En \ + -e 's/\x1b\[[0-9;]*[mGKHF]//g' \ + -e 's/[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/' \ + -e '//{p;Q}' \ + "$@" +} + + +# @description Retrieve the latest version number of a web release +# @arg $1 releaseListUrl:String the url from which version list can be retrieved +# @stdout log messages about retry +# @env CURL_CONNECT_TIMEOUT number of seconds before giving up host connection +Web::getReleases() { + local releaseListUrl="$1" + # Get latest release from GitHub api + Retry::parameterized "${RETRY_MAX_RETRY:-5}" "${RETRY_DELAY_BETWEEN_RETRIES:-15}" "Retrieving release versions list ..." curl \ + -L \ + --connect-timeout "${CURL_CONNECT_TIMEOUT:-5}" \ + --fail \ + --silent \ + "${releaseListUrl}" +} + + +# @description upgrade given binary to latest release using retry +# +# releasesUrl argument : the placeholder @latestVersion@ will be replaced by the latest release version +# @arg $1 targetFile:String target binary file (eg: /usr/local/bin/kind) +# @arg $2 releasesUrl:String url on which we can query all available versions (eg: "https://go.dev/dl/?mode=json") +# @arg $3 downloadReleaseUrl:String url from which the software will be downloaded (eg: https://storage.googleapis.com/golang/go@latestVersion@.linux-amd64.tar.gz) +# @arg $4 softVersionArg:String parameter to add to existing command to compute current version +# @arg $5 exactVersion:String if you want to retrieve a specific version instead of the latest +# @stdout log messages about retry, install, upgrade +# @env FILTER_LAST_VERSION_CALLBACK a callback to filter the latest version from releasesUrl +# @env SOFT_VERSION_CALLBACK a callback to execute command version +# @env PARSE_VERSION_CALLBACK a callback to parse the version of the existing command +# @env INSTALL_CALLBACK a callback to install the software downloaded +# @env CURL_CONNECT_TIMEOUT number of seconds before giving up host connection +Web::upgradeRelease() { + local targetFile="$1" + local releasesUrl="$2" + local downloadReleaseUrl="$3" + local softVersionArg="${4:---version}" + local exactVersion="${5:-}" + # options from env variables + local filterLastVersionCallback="${FILTER_LAST_VERSION_CALLBACK:-Version::parse}" + local softVersionCallback="${SOFT_VERSION_CALLBACK:-Version::getCommandVersionFromPlainText}" + local installCallback="${INSTALL_CALLBACK:-}" + local latestVersion + latestVersion="$(Web::getReleases "${releasesUrl}" | ${filterLastVersionCallback})" || { + Log::displayError "latest version not found on ${releasesUrl}" + return 1 + } + Log::displayInfo "Latest version found is ${latestVersion}" + + local currentVersion="not existing" + if [[ -f "${targetFile}" ]]; then + currentVersion="$(${softVersionCallback} "${targetFile}" "${softVersionArg}" 2>&1 || true)" + fi + if [[ -z "${exactVersion}" ]]; then + exactVersion="${latestVersion}" + fi + local url="${downloadReleaseUrl//@latestVersion@/${exactVersion}}" + if [[ -n "${exactVersion}" ]] && ! Github::isReleaseVersionExist "${url}"; then + Log::displayError "${targetFile} version ${exactVersion} doesn't exist on github" + return 2 + fi + if [[ "${currentVersion}" = "${exactVersion}" ]]; then + Log::displayInfo "${targetFile} version ${exactVersion} already installed" + else + if [[ -z "${currentVersion}" ]]; then + Log::displayInfo "Installing ${targetFile} with version ${exactVersion}" + else + Log::displayInfo "Upgrading ${targetFile} from version ${currentVersion} to ${exactVersion}" + fi + Log::displayInfo "Using url ${url}" + newSoftware=$(mktemp -p "${TMPDIR:-/tmp}" -t web.newSoftware.XXXX) + Retry::default curl \ + -L \ + --connect-timeout "${CURL_CONNECT_TIMEOUT:-5}" \ + -o "${newSoftware}" \ + --fail \ + "${url}" + + Github::defaultInstall "${newSoftware}" "${targetFile}" "${exactVersion}" "${installCallback}" + fi +} +# FUNCTIONS + + +declare -a BASH_FRAMEWORK_ARGV_FILTERED=() + +beforeParseCallback() { + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + +copyrightCallback() { + # + echo "Copyright (c) 2020-now François Chastanet" } # shellcheck disable=SC2317 # if function is overridden @@ -1218,13 +1260,14 @@ updateArgListQuietCallback() { :; } # shellcheck disable=SC2317 # if function is overridden optionHelpCallback() { - upgradeGithubReleaseCommand help + Log::displayError "optionHelpCallback needs to be overridden" exit 0 } # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { - echo "${SCRIPT_NAME} version 2.0" + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version 3.0" exit 0 } @@ -1242,21 +1285,22 @@ optionEnvFileCallback() { optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_INFO} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionDebugVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_DEBUG} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionTraceVerboseCallback() { + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vvv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_TRACE} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } getLevel() { @@ -1280,6 +1324,7 @@ getLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -1301,6 +1346,7 @@ getVerboseLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -1311,7 +1357,7 @@ optionDisplayLevelCallback() { logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1320,19 +1366,20 @@ optionLogLevelCallback() { local logLevel verboseLevel logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionLogFileCallback() { local logFile="$2" - echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionQuietCallback() { - echo "BASH_FRAMEWORK_QUIET_MODE=1" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_QUIET_MODE=1" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1363,6 +1410,7 @@ optionBashFrameworkConfigCallback() { defaultFrameworkConfig="$( cat <<'EOF' + # copied from src/_includes/.framework-config.default # shellcheck disable=SC2034 @@ -1380,7 +1428,7 @@ NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-conf # describe the files that are allowed to not have an associated bats file BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" # describe the files that are allowed to not have a function matching the filename -FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|\.tpl$|/testsData/|^manualTests/|\.bats$}" +FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|/testsData/|^manualTests/|\.bats$}" # Source directories if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then FRAMEWORK_SRC_DIRS=( @@ -1396,6 +1444,7 @@ BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" + EOF )" @@ -1404,7 +1453,7 @@ overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" commandOptionParseFinished() { # load default template framework config defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" - echo "${defaultFrameworkConfig}" > "${defaultEnvFile}" + echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") if [[ -f "${envFile}" ]]; then files+=("${envFile}") @@ -1422,548 +1471,79 @@ commandOptionParseFinished() { fi } -upgradeGithubReleaseCommand() { - local options_parse_cmd="$1" - shift || true - if [[ "${options_parse_cmd}" = "parse" ]]; then - optionVersionArg="--version" - local -i options_parse_optionParsedCountOptionVersionArg - ((options_parse_optionParsedCountOptionVersionArg = 0)) || true - local -i options_parse_optionParsedCountOptionCurrentVersion - ((options_parse_optionParsedCountOptionCurrentVersion = 0)) || true - local -i options_parse_optionParsedCountOptionExactVersion - ((options_parse_optionParsedCountOptionExactVersion = 0)) || true - local -i options_parse_optionParsedCountOptionBashFrameworkConfig - ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true - optionConfig="0" - local -i options_parse_optionParsedCountOptionConfig - ((options_parse_optionParsedCountOptionConfig = 0)) || true - optionInfoVerbose="0" - local -i options_parse_optionParsedCountOptionInfoVerbose - ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true - optionDebugVerbose="0" - local -i options_parse_optionParsedCountOptionDebugVerbose - ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true - optionTraceVerbose="0" - local -i options_parse_optionParsedCountOptionTraceVerbose - ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionNoColor="0" - local -i options_parse_optionParsedCountOptionNoColor - ((options_parse_optionParsedCountOptionNoColor = 0)) || true - optionTheme="default" - local -i options_parse_optionParsedCountOptionTheme - ((options_parse_optionParsedCountOptionTheme = 0)) || true - optionHelp="0" - local -i options_parse_optionParsedCountOptionHelp - ((options_parse_optionParsedCountOptionHelp = 0)) || true - optionVersion="0" - local -i options_parse_optionParsedCountOptionVersion - ((options_parse_optionParsedCountOptionVersion = 0)) || true - optionQuiet="0" - local -i options_parse_optionParsedCountOptionQuiet - ((options_parse_optionParsedCountOptionQuiet = 0)) || true - local -i options_parse_optionParsedCountOptionLogLevel - ((options_parse_optionParsedCountOptionLogLevel = 0)) || true - local -i options_parse_optionParsedCountOptionLogFile - ((options_parse_optionParsedCountOptionLogFile = 0)) || true - local -i options_parse_optionParsedCountOptionDisplayLevel - ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true - local -i options_parse_argParsedCountTargetFileArg - ((options_parse_argParsedCountTargetFileArg = 0)) || true - local -i options_parse_argParsedCountGithubUrlPatternArg - ((options_parse_argParsedCountGithubUrlPatternArg = 0)) || true - # shellcheck disable=SC2034 - local -i options_parse_parsedArgIndex=0 - while (($# > 0)); do - local options_parse_arg="$1" - local argOptDefaultBehavior=0 - case "${options_parse_arg}" in - # Option 1/17 - # Option optionVersionArg --version-arg variableType String min 0 max 1 authorizedValues '' regexp '' - --version-arg) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionVersionArg >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionVersionArg)) - # shellcheck disable=SC2034 - optionVersionArg="$1" - ;; - # Option 2/17 - # Option optionCurrentVersion --current-version|-c variableType String min 0 max 1 authorizedValues '' regexp '' - --current-version | -c) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionCurrentVersion >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionCurrentVersion)) - # shellcheck disable=SC2034 - optionCurrentVersion="$1" - ;; - # Option 3/17 - # Option optionExactVersion --exact-version|-e variableType String min 0 max 1 authorizedValues '' regexp '' - --exact-version | -e) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionExactVersion >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionExactVersion)) - # shellcheck disable=SC2034 - optionExactVersion="$1" - ;; - # Option 4/17 - # Option optionBashFrameworkConfig --bash-framework-config variableType String min 0 max 1 authorizedValues '' regexp '' - --bash-framework-config) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) - # shellcheck disable=SC2034 - optionBashFrameworkConfig="$1" - optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" - ;; - # Option 5/17 - # Option optionConfig --config variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --config) - # shellcheck disable=SC2034 - optionConfig="1" - if ((options_parse_optionParsedCountOptionConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionConfig)) - ;; - # Option 6/17 - # Option optionInfoVerbose --verbose|-v variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --verbose | -v) - # shellcheck disable=SC2034 - optionInfoVerbose="1" - if ((options_parse_optionParsedCountOptionInfoVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionInfoVerbose)) - optionInfoVerboseCallback "${options_parse_arg}" - updateArgListInfoVerboseCallback "${options_parse_arg}" - ;; - # Option 7/17 - # Option optionDebugVerbose -vv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vv) - # shellcheck disable=SC2034 - optionDebugVerbose="1" - if ((options_parse_optionParsedCountOptionDebugVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDebugVerbose)) - optionDebugVerboseCallback "${options_parse_arg}" - updateArgListDebugVerboseCallback "${options_parse_arg}" - ;; - # Option 8/17 - # Option optionTraceVerbose -vvv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vvv) - # shellcheck disable=SC2034 - optionTraceVerbose="1" - if ((options_parse_optionParsedCountOptionTraceVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTraceVerbose)) - optionTraceVerboseCallback "${options_parse_arg}" - updateArgListTraceVerboseCallback "${options_parse_arg}" - ;; - # Option 9/17 - # Option optionEnvFiles --env-file variableType StringArray min 0 max -1 authorizedValues '' regexp '' - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - ;; - # Option 10/17 - # Option optionNoColor --no-color variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --no-color) - # shellcheck disable=SC2034 - optionNoColor="1" - if ((options_parse_optionParsedCountOptionNoColor >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionNoColor)) - optionNoColorCallback "${options_parse_arg}" - updateArgListNoColorCallback "${options_parse_arg}" - ;; - # Option 11/17 - # Option optionTheme --theme variableType String min 0 max 1 authorizedValues 'default|default-force|noColor' regexp '' - --theme) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default|default-force|noColor)" - return 1 - fi - if ((options_parse_optionParsedCountOptionTheme >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTheme)) - # shellcheck disable=SC2034 - optionTheme="$1" - optionThemeCallback "${options_parse_arg}" "${optionTheme}" - updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" - ;; - # Option 12/17 - # Option optionHelp --help|-h variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --help | -h) - # shellcheck disable=SC2034 - optionHelp="1" - if ((options_parse_optionParsedCountOptionHelp >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionHelp)) - optionHelpCallback "${options_parse_arg}" - ;; - # Option 13/17 - # Option optionVersion --version variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --version) - # shellcheck disable=SC2034 - optionVersion="1" - if ((options_parse_optionParsedCountOptionVersion >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionVersion)) - optionVersionCallback "${options_parse_arg}" - ;; - # Option 14/17 - # Option optionQuiet --quiet|-q variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --quiet | -q) - # shellcheck disable=SC2034 - optionQuiet="1" - if ((options_parse_optionParsedCountOptionQuiet >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionQuiet)) - optionQuietCallback "${options_parse_arg}" - updateArgListQuietCallback "${options_parse_arg}" - ;; - # Option 15/17 - # Option optionLogLevel --log-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --log-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogLevel)) - # shellcheck disable=SC2034 - optionLogLevel="$1" - optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - ;; - # Option 16/17 - # Option optionLogFile --log-file variableType String min 0 max 1 authorizedValues '' regexp '' - --log-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogFile >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogFile)) - # shellcheck disable=SC2034 - optionLogFile="$1" - optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" - updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" - ;; - # Option 17/17 - # Option optionDisplayLevel --display-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --display-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionDisplayLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDisplayLevel)) - # shellcheck disable=SC2034 - optionDisplayLevel="$1" - optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - ;; - -*) - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" - return 1 - fi - ;; - *) - if ((0)); then - # Technical if - never reached - : - # Argument 1/2 - # Argument targetFileArg min 1 max 1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 0 && options_parse_parsedArgIndex < 1)); then - if ((options_parse_argParsedCountTargetFileArg >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument targetFile - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountTargetFileArg)) - # shellcheck disable=SC2034 - targetFileArg="${options_parse_arg}" - targetFileArgCallback "${targetFileArg}" -- "${@:2}" - # Argument 2/2 - # Argument githubUrlPatternArg min 1 max 1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 1 && options_parse_parsedArgIndex < 2)); then - if ((options_parse_argParsedCountGithubUrlPatternArg >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument githubUrlPattern - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountGithubUrlPatternArg)) - # shellcheck disable=SC2034 - githubUrlPatternArg="${options_parse_arg}" - githubUrlPatternArgCallback "${githubUrlPatternArg}" -- "${@:2}" - else - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" - return 1 - fi - fi - ((++options_parse_parsedArgIndex)) - ;; - esac - shift || true - done - if ((options_parse_argParsedCountTargetFileArg < 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument 'targetFile' should be provided at least 1 time(s)" - return 1 - fi - if ((options_parse_argParsedCountGithubUrlPatternArg < 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument 'githubUrlPattern' should be provided at least 1 time(s)" - return 1 - fi - commandOptionParseFinished - Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" - Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" - elif [[ "${options_parse_cmd}" = "help" ]]; then - Array::wrap2 " " 80 0 "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" "retrieve latest binary release from github and install it" - echo - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "${SCRIPT_NAME}" "[OPTIONS]" "[ARGUMENTS]")" - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ - "${SCRIPT_NAME}" \ - "[--version-arg ]" "[--current-version|-c ]" "[--exact-version|-e ]" "[--bash-framework-config ]" "[--config]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--no-color]" "[--theme ]" "[--help|-h]" "[--version]" "[--quiet|-q]" "[--log-level ]" "[--log-file ]" "[--display-level ]")" - echo - echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}targetFile${__HELP_NORMAL} {single} (mandatory)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=($'the binary downloaded will be written to this file path.\n\n Ensure the path is writable.') - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}githubUrlPattern${__HELP_NORMAL} {single} (mandatory)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=($'the url pattern to use to download the binary, see examples below.\n\n @version@ is template variable that will be replaced by the latest \n\n version tag found on github.') - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}VERSION MANAGEMENT:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--version-arg ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=($'The argument that will be provided to the currently installed binary\n\n to check the version of the software. \n\n See options constraints below.') - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: --version' - echo -e " ${__HELP_OPTION_COLOR}--current-version${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-c ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=($'Sometimes the command to retrieve the version is complicated. \n\n Some command needs you to parse json or other commands provides multiple \n\n sub command versions. In this case you can provide the version you \n\n currently have, see examples below. \n\n See options constraints below.') - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--exact-version${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-e ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=($'if provided and currently installed binary is not this exactVersion,\n\n This exact version of the binary will be installed.\n\n See options constraints below.') - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(use\ alternate\ bash\ framework\ configuration.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ configuration) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(info\ level\ verbose\ mode\ \(alias\ of\ --display-level\ INFO\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(debug\ level\ verbose\ mode\ \(alias\ of\ --display-level\ DEBUG\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(trace\ level\ verbose\ mode\ \(alias\ of\ --display-level\ TRACE\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Load\ the\ specified\ env\ file\ \(deprecated\,\ please\ use\ --bash-framework-config\ option\ instead\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Produce\ monochrome\ output.\ alias\ of\ --theme\ noColor.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' - echo ' Possible values: default|default-force|noColor' - echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ this\ command\ help) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Print\ version\ information\ and\ quit) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(quiet\ mode\,\ doesn\'t\ display\ any\ output) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ file) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(set\ display\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e """ -${__HELP_TITLE}OPTIONS CONSTRAINTS:${__HELP_NORMAL} - -${__HELP_OPTION_COLOR}--current-version${__HELP_NORMAL}|${__HELP_OPTION_COLOR}-c${__HELP_NORMAL} and ${__HELP_OPTION_COLOR}--version-arg${__HELP_NORMAL} are mutually exclusive, you cannot use both argument at the same time. - -${__HELP_TITLE}GITHUB TEMPLATE URLS EXAMPLES:${__HELP_NORMAL} - -Simple ones(Sometimes @version@ template variable has to be specified twice):${__HELP_EXAMPLE} -\"https://github.com/hadolint/hadolint/releases/download/v@version@/hadolint-Linux-x86_64\" -\"https://github.com/koalaman/shellcheck/releases/download/v@version@/shellcheck-v@version@.linux.x86_64.tar.xz\" -\"https://github.com/sharkdp/fd/releases/download/v@version@/fd_@version@_amd64.deb\" -\"https://github.com/sharkdp/bat/releases/download/v@version@/bat_@version@_amd64.deb\" -\"https://github.com/kubernetes-sigs/kind/releases/download/v@version@/kind-linux-amd64\" -\"https://github.com/kubernetes/minikube/releases/download/v@version@/minikube-linux-amd64\" -\"https://github.com/plantuml/plantuml/releases/download/v@version@/plantuml-@version@.jar\" -\"https://github.com/Versent/saml2aws/releases/download/v@version@/saml2aws_@version@_linux_amd64.tar.gz\" -${__HELP_NORMAL} -If you want to add a condition on architecture(linux, windows, x86, 64/32 bits):${__HELP_EXAMPLE} -\"https://github.com/docker/compose/releases/download/v@version@/docker-compose-\$(uname -s | tr \"[:upper:]\" \"[:lower:]\")-\$(uname -m)\" -\"https://github.com/docker/docker-credential-helpers/releases/download/v@version@/docker-credential-wincred-v@version@.windows-\$(dpkg --print-architecture).exe\" -\"https://github.com/Blacksmoke16/oq/releases/download/v@version@/oq-v@version@-\$(uname -s)-\$(uname -m)\" -${__HELP_NORMAL} -${__HELP_TITLE}COMMAND EXAMPLES:${__HELP_NORMAL} -Download docker-compose latest version -${__HELP_EXAMPLE}upgradeGithubRelease /usr/local/bin/docker-compose \\ - \"https://github.com/docker/compose/releases/download/v@version@/docker-compose-\$(uname -s | tr \"[:upper:]\" \"[:lower:]\")-\$(uname -m)\"${__HELP_NORMAL} - -Download oq specific version -${__HELP_EXAMPLE}upgradeGithubRelease /usr/local/bin/oq --exact-version 1.3.4 \\ - \"https://github.com/Blacksmoke16/oq/releases/download/v@version@/oq-v@version@-\$(uname -s)-\$(uname -m)\"${__HELP_NORMAL} - -Download oq specific version correctly retrieving the oq version and not the jq one -${__HELP_EXAMPLE}upgradeGithubRelease /usr/local/bin/oq --exact-version 1.3.4 --version-arg '-V | grep oq:' \\ - \"https://github.com/Blacksmoke16/oq/releases/download/v@version@/oq-v@version@-\$(uname -s)-\$(uname -m)\"${__HELP_NORMAL}""" - echo - echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo '2.0' - echo - echo -e "${__HELP_TITLE_COLOR}AUTHOR:${__RESET_COLOR}" - echo '[François Chastanet](https://github.com/fchastanet)' - echo - echo -e "${__HELP_TITLE_COLOR}SOURCE FILE:${__RESET_COLOR}" - echo 'https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Git/upgradeGithubRelease.sh' - echo - echo -e "${__HELP_TITLE_COLOR}LICENSE:${__RESET_COLOR}" - echo 'MIT License' - echo - Array::wrap2 ' ' 76 4 "$(copyrightCallback)" - else - Log::displayError "Command ${SCRIPT_NAME} - Option command invalid: '${options_parse_cmd}'" - return 1 - fi +specificRequirements() { + Linux::requireJqCommand +} + +longDescriptionFunction() { + echo -e " ${__HELP_TITLE}GITHUB TEMPLATE URLS EXAMPLES:${__HELP_NORMAL}" + echo + echo -e " Simple ones(Sometimes @version@ template variable has to be specified twice):${__HELP_EXAMPLE}" + echo -e ' "https://github.com/hadolint/hadolint/releases/download/v@version@/hadolint-Linux-x86_64"' + echo -e ' "https://github.com/koalaman/shellcheck/releases/download/v@version@/shellcheck-v@version@.linux.x86_64.tar.xz"' + echo -e ' "https://github.com/sharkdp/fd/releases/download/v@version@/fd_@version@_amd64.deb"' + echo -e ' "https://github.com/sharkdp/bat/releases/download/v@version@/bat_@version@_amd64.deb"' + echo -e ' "https://github.com/kubernetes-sigs/kind/releases/download/v@version@/kind-linux-amd64"' + echo -e ' "https://github.com/kubernetes/minikube/releases/download/v@version@/minikube-linux-amd64"' + echo -e ' "https://github.com/plantuml/plantuml/releases/download/v@version@/plantuml-@version@.jar"' + echo -e ' "https://github.com/Versent/saml2aws/releases/download/v@version@/saml2aws_@version@_linux_amd64.tar.gz"' + echo -e "${__HELP_NORMAL}" + echo -e " If you want to add a condition on architecture(linux, windows, x86, 64/32 bits):${__HELP_EXAMPLE}" + # shellcheck disable=SC2016 + echo -e ' "https://github.com/docker/compose/releases/download/v@version@/docker-compose-$(uname -s | tr "[:upper:]" "[:lower:]")-$(uname -m)"' + # shellcheck disable=SC2016 + echo -e ' "https://github.com/docker/docker-credential-helpers/releases/download/v@version@/docker-credential-wincred-v@version@.windows-$(dpkg --print-architecture).exe"' + # shellcheck disable=SC2016 + echo -e ' "https://github.com/Blacksmoke16/oq/releases/download/v@version@/oq-v@version@-$(uname -s)-$(uname -m)"' + echo -e "${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}COMMAND EXAMPLES:${__HELP_NORMAL}" + echo -e "${__HELP_NORMAL}" + echo -e " Download docker-compose latest version${__HELP_EXAMPLE}" + echo -e " upgradeGithubRelease /usr/local/bin/docker-compose \\" + # shellcheck disable=SC2016 + echo -e ' "https://github.com/docker/compose/releases/download/v@version@/docker-compose-$(uname -s | tr "[:upper:]" "[:lower:]")-$(uname -m)"' + echo -e "${__HELP_NORMAL}" + echo -e " Download oq specific version${__HELP_EXAMPLE}" + echo -e " upgradeGithubRelease /usr/local/bin/oq --exact-version 1.3.4 \\" + # shellcheck disable=SC2016 + echo -e ' "https://github.com/Blacksmoke16/oq/releases/download/v@version@/oq-v@version@-$(uname -s)-$(uname -m)"' + echo -e "${__HELP_NORMAL}" + echo -e " Download oq specific version correctly retrieving the oq version and not the jq one${__HELP_EXAMPLE}" + echo -e " upgradeGithubRelease /usr/local/bin/oq --exact-version 1.3.4 --version-arg '-V | grep oq:' \\" + # shellcheck disable=SC2016 + echo -e ' "https://github.com/Blacksmoke16/oq/releases/download/v@version@/oq-v@version@-$(uname -s)-$(uname -m)"' + echo -e "${__HELP_NORMAL}" +} + +githubUrlPatternHelpFunction() { + echo ' The url pattern to use to download the binary, see examples below.' + echo ' @version@ is template variable that will be replaced by the latest' + echo ' version tag found on github.' +} + +optionCurrentVersionHelpFunction() { + echo ' Sometimes the command to retrieve the version is complicated.' + echo ' Some command needs you to parse json or other commands' + echo ' that provides multiple sub command versions.' + echo ' In this case you can provide the version you currently have.' + echo ' See options constraints and examples below.' +} + +exactVersionHelpFunction() { + echo ' If provided and currently installed binary is not this exactVersion,' + echo ' this exact version of the binary will be installed.' + echo ' See options constraints below.' +} + +optionHelpCallback() { + upgradeGithubReleaseCommandHelp + exit 0 } githubUrlPatternArgCallback() { + # shellcheck disable=SC2154 if [[ ! "${githubUrlPatternArg}" =~ ^https://github.com/ ]]; then Log::fatal "Invalid githubUrlPattern ${githubUrlPatternArg} provided, it should begin with https://github.com/" fi @@ -1981,35 +1561,771 @@ targetFileArgCallback() { fi } -upgradeGithubReleaseCommand parse "${BASH_FRAMEWORK_ARGV[@]}" -run() { - computeCurrentCommandVersion() { - if [[ -n "${optionCurrentVersion}" ]]; then - echo "${optionCurrentVersion}" - return 0 - fi - if [[ -n "${optionVersionArg}" ]]; then - "${targetFileArg}" "${optionVersionArg}" 2>&1 | Version::parse || return 3 - fi - } +optionVersionCallback() { + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version 3.0" + Db::checkRequirements + exit 0 +} + + +# ------------------------------------------ +# Command upgradeGithubReleaseCommand +# ------------------------------------------ + +# options variables initialization +declare optionHelp="0" +declare optionConfig="0" +declare optionBashFrameworkConfig="" +declare optionInfoVerbose="0" +declare optionDebugVerbose="0" +declare optionTraceVerbose="0" +declare -a optionEnvFiles=() +declare optionLogLevel="" +declare optionLogFile="" +declare optionDisplayLevel="" +declare optionNoColor="0" +declare optionTheme="default" +declare optionVersion="0" +declare optionQuiet="0" +declare optionVersionArg="--version" +declare optionCurrentVersion="" +declare optionExactVersion="" +# arguments variables initialization +declare targetFileArg="" +declare githubUrlPatternArg="" +# @description parse command options and arguments for upgradeGithubReleaseCommand +upgradeGithubReleaseCommandParse() { + Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" + Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" + optionHelp="0" + local -i options_parse_optionParsedCountOptionHelp + ((options_parse_optionParsedCountOptionHelp = 0)) || true + optionConfig="0" + local -i options_parse_optionParsedCountOptionConfig + ((options_parse_optionParsedCountOptionConfig = 0)) || true + optionBashFrameworkConfig="" + local -i options_parse_optionParsedCountOptionBashFrameworkConfig + ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true + optionInfoVerbose="0" + local -i options_parse_optionParsedCountOptionInfoVerbose + ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true + optionDebugVerbose="0" + local -i options_parse_optionParsedCountOptionDebugVerbose + ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true + optionTraceVerbose="0" + local -i options_parse_optionParsedCountOptionTraceVerbose + ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true + + optionLogLevel="" + local -i options_parse_optionParsedCountOptionLogLevel + ((options_parse_optionParsedCountOptionLogLevel = 0)) || true + optionLogFile="" + local -i options_parse_optionParsedCountOptionLogFile + ((options_parse_optionParsedCountOptionLogFile = 0)) || true + optionDisplayLevel="" + local -i options_parse_optionParsedCountOptionDisplayLevel + ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true + optionNoColor="0" + local -i options_parse_optionParsedCountOptionNoColor + ((options_parse_optionParsedCountOptionNoColor = 0)) || true + optionTheme="default" + local -i options_parse_optionParsedCountOptionTheme + ((options_parse_optionParsedCountOptionTheme = 0)) || true + optionVersion="0" + local -i options_parse_optionParsedCountOptionVersion + ((options_parse_optionParsedCountOptionVersion = 0)) || true + optionQuiet="0" + local -i options_parse_optionParsedCountOptionQuiet + ((options_parse_optionParsedCountOptionQuiet = 0)) || true + optionVersionArg="--version" + local -i options_parse_optionParsedCountOptionVersionArg + ((options_parse_optionParsedCountOptionVersionArg = 0)) || true + optionCurrentVersion="" + local -i options_parse_optionParsedCountOptionCurrentVersion + ((options_parse_optionParsedCountOptionCurrentVersion = 0)) || true + optionExactVersion="" + local -i options_parse_optionParsedCountOptionExactVersion + ((options_parse_optionParsedCountOptionExactVersion = 0)) || true + + targetFileArg="" + local -i options_parse_argParsedCountTargetFileArg + ((options_parse_argParsedCountTargetFileArg = 0)) || true + + githubUrlPatternArg="" + local -i options_parse_argParsedCountGithubUrlPatternArg + ((options_parse_argParsedCountGithubUrlPatternArg = 0)) || true + + + # shellcheck disable=SC2034 + local -i options_parse_parsedArgIndex=0 + while (($# > 0)); do + local options_parse_arg="$1" + local argOptDefaultBehavior=0 + case "${options_parse_arg}" in + # Option 1/17 + # optionHelp alts --help|-h + # type: Boolean min 0 max 1 + --help | -h) + # shellcheck disable=SC2034 + optionHelp="1" + + if ((options_parse_optionParsedCountOptionHelp >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionHelp)) + optionHelpCallback "${options_parse_arg}" "${optionHelp}" + + ;; + + # Option 2/17 + # optionConfig alts --config + # type: Boolean min 0 max 1 + --config) + # shellcheck disable=SC2034 + optionConfig="1" + + if ((options_parse_optionParsedCountOptionConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionConfig)) + ;; + + # Option 3/17 + # optionBashFrameworkConfig alts --bash-framework-config + # type: String min 0 max 1 + --bash-framework-config) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) + # shellcheck disable=SC2034 + optionBashFrameworkConfig="$1" + optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" + + ;; + + # Option 4/17 + # optionInfoVerbose alts --verbose|-v + # type: Boolean min 0 max 1 + --verbose | -v) + # shellcheck disable=SC2034 + optionInfoVerbose="1" + + if ((options_parse_optionParsedCountOptionInfoVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionInfoVerbose)) + optionInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + updateArgListInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + ;; + + # Option 5/17 + # optionDebugVerbose alts -vv + # type: Boolean min 0 max 1 + -vv) + # shellcheck disable=SC2034 + optionDebugVerbose="1" + + if ((options_parse_optionParsedCountOptionDebugVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDebugVerbose)) + optionDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + updateArgListDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + ;; + + # Option 6/17 + # optionTraceVerbose alts -vvv + # type: Boolean min 0 max 1 + -vvv) + # shellcheck disable=SC2034 + optionTraceVerbose="1" + + if ((options_parse_optionParsedCountOptionTraceVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTraceVerbose)) + optionTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + updateArgListTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + ;; + + # Option 7/17 + # optionEnvFiles alts --env-file + # type: StringArray min 0 max -1 + --env-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + ((++options_parse_optionParsedCountOptionEnvFiles)) + optionEnvFiles+=("$1") + optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + ;; + + # Option 8/17 + # optionLogLevel alts --log-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --log-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogLevel)) + # shellcheck disable=SC2034 + optionLogLevel="$1" + optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + ;; + + # Option 9/17 + # optionLogFile alts --log-file + # type: String min 0 max 1 + --log-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogFile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogFile)) + # shellcheck disable=SC2034 + optionLogFile="$1" + optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + ;; + + # Option 10/17 + # optionDisplayLevel alts --display-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --display-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionDisplayLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDisplayLevel)) + # shellcheck disable=SC2034 + optionDisplayLevel="$1" + optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + ;; + + # Option 11/17 + # optionNoColor alts --no-color + # type: Boolean min 0 max 1 + --no-color) + # shellcheck disable=SC2034 + optionNoColor="1" + + if ((options_parse_optionParsedCountOptionNoColor >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionNoColor)) + optionNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + updateArgListNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + ;; + + # Option 12/17 + # optionTheme alts --theme + # type: String min 0 max 1 + # authorizedValues: default|default-force|noColor + --theme) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ default|default-force|noColor ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionTheme >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTheme)) + # shellcheck disable=SC2034 + optionTheme="$1" + optionThemeCallback "${options_parse_arg}" "${optionTheme}" + + updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" + + ;; + + # Option 13/17 + # optionVersion alts --version + # type: Boolean min 0 max 1 + --version) + # shellcheck disable=SC2034 + optionVersion="1" + + if ((options_parse_optionParsedCountOptionVersion >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionVersion)) + optionVersionCallback "${options_parse_arg}" "${optionVersion}" + + ;; + + # Option 14/17 + # optionQuiet alts --quiet|-q + # type: Boolean min 0 max 1 + --quiet | -q) + # shellcheck disable=SC2034 + optionQuiet="1" + + if ((options_parse_optionParsedCountOptionQuiet >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionQuiet)) + optionQuietCallback "${options_parse_arg}" "${optionQuiet}" + + updateArgListQuietCallback "${options_parse_arg}" "${optionQuiet}" + + ;; + + # Option 15/17 + # optionVersionArg alts --version-arg + # type: String min 0 max 1 + --version-arg) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionVersionArg >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionVersionArg)) + # shellcheck disable=SC2034 + optionVersionArg="$1" + ;; + + # Option 16/17 + # optionCurrentVersion alts --current-version|-c + # type: String min 0 max 1 + --current-version | -c) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionCurrentVersion >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionCurrentVersion)) + # shellcheck disable=SC2034 + optionCurrentVersion="$1" + ;; + + # Option 17/17 + # optionExactVersion alts --exact-version|-e + # type: String min 0 max 1 + --exact-version | -e) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionExactVersion >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionExactVersion)) + # shellcheck disable=SC2034 + optionExactVersion="$1" + ;; + + -*) + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" + return 1 + fi + ;; + *) + ((minParsedArgIndex0 = 0)) || true + ((maxParsedArgIndex0 = 0)) || true + ((minParsedArgIndex1 = minParsedArgIndex0 + 1)) || true + ((maxParsedArgIndex1 = maxParsedArgIndex0 + 1)) || true + ((minParsedArgIndex2 = minParsedArgIndex1 + 1)) || true + ((maxParsedArgIndex2 = maxParsedArgIndex1 + 1)) || true + ((incrementArg = 1 )) + if ((0)); then + # Technical if - never reached + : + + # Argument 1/2 - targetFileArg + # Argument targetFileArg min 1 max 1 + # Argument targetFileArg authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && + options_parse_parsedArgIndex < maxParsedArgIndex1 )); then + if ((options_parse_argParsedCountTargetFileArg >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument targetFile - Maximum number of argument occurrences reached(1)" + return 1 + fi + ((++options_parse_argParsedCountTargetFileArg)) + # shellcheck disable=SC2034 + targetFileArg="${options_parse_arg}" + targetFileArgCallback "${targetFileArg}" -- "${@:2}" + + + # Argument 2/2 - githubUrlPatternArg + # Argument githubUrlPatternArg min 1 max 1 + # Argument githubUrlPatternArg authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex1 && + options_parse_parsedArgIndex < maxParsedArgIndex2 )); then + if ((options_parse_argParsedCountGithubUrlPatternArg >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument githubUrlPattern - Maximum number of argument occurrences reached(1)" + return 1 + fi + ((++options_parse_argParsedCountGithubUrlPatternArg)) + # shellcheck disable=SC2034 + githubUrlPatternArg="${options_parse_arg}" + githubUrlPatternArgCallback "${githubUrlPatternArg}" -- "${@:2}" + + + # else too much args + else + + + if [[ "${argOptDefaultBehavior}" = "0" ]]; then + # too much args and no unknownArgumentCallbacks configured + Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" + return 1 + fi + + fi + if ((incrementArg == 1)); then + ((++options_parse_parsedArgIndex)) + fi + ;; + esac + shift || true + done + + + + + + + + + + + + + + + + + + if ((options_parse_argParsedCountTargetFileArg < 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument 'targetFile' should be provided at least 1 time(s)" + return 1 + fi + + if ((options_parse_argParsedCountGithubUrlPatternArg < 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument 'githubUrlPattern' should be provided at least 1 time(s)" + return 1 + fi || return $? + + commandOptionParseFinished - EXACT_VERSION="${optionExactVersion}" \ - Github::upgradeRelease \ - "${targetFileArg}" \ - "${githubUrlPatternArg}" \ - "${optionVersionArg}" \ - computeCurrentCommandVersion \ - Github::defaultInstall } -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run -fi +# @description display command options and arguments help for upgradeGithubReleaseCommand +upgradeGithubReleaseCommandHelp() { + echo -e "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" + Array::wrap2 ' ' 76 4 " " "Retrieve latest binary release from github and install it." + + echo + echo + + # ------------------------------------------ + # usage section + # ------------------------------------------ + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "upgradeGithubRelease [OPTIONS] [ARGUMENTS]" + echo + # ------------------------------------------ + # usage/options section + # ------------------------------------------ + optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--version-arg ]" "[--current-version|-c ]" "[--exact-version|-e ]" + ) + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ + "upgradeGithubRelease" "${optionsAltList[@]}" + echo + + # ------------------------------------------ + # usage/arguments section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" + + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}targetFile${__HELP_NORMAL} {single} (mandatory) + " + Array::wrap2 ' ' 76 4 " " "The binary downloaded will be written to this file path. Ensure the path is writable." + echo + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}githubUrlPattern${__HELP_NORMAL} {single} (mandatory) + " + githubUrlPatternHelpFunction + + # ------------------------------------------ + # options section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays this command help" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays configuration" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" + Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log file" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set display level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." + echo + Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" + echo + + Array::wrap2 ' ' 76 6 " Default value: " "default" + echo + + echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Print version information and quit." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." + echo + + + echo + echo -e "${__HELP_TITLE_COLOR}VERSION MANAGEMENT:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--version-arg ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "The argument that will be provided to the currently installed binary" "to check the version of the software. See options constraints below." "" + echo + + + Array::wrap2 ' ' 76 6 " Default value: " "--version" + echo + + echo -e " ${__HELP_OPTION_COLOR}--current-version${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-c ${__HELP_NORMAL} {single}" + optionCurrentVersionHelpFunction + + + + + echo -e " ${__HELP_OPTION_COLOR}--exact-version${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-e ${__HELP_NORMAL} {single}" + exactVersionHelpFunction + + + + # ------------------------------------------ + # longDescription section + # ------------------------------------------ + echo + echo + echo -e "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" + longDescriptionFunction + # ------------------------------------------ + # version section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" + echo "3.0" + # ------------------------------------------ + # author section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}AUTHOR: ${__RESET_COLOR}" + echo "[François Chastanet](https://github.com/fchastanet)" + # ------------------------------------------ + # sourceFile section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Git/upgradeGithubRelease/upgradeGithubRelease-binary.yaml" + # ------------------------------------------ + # license section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}LICENSE: ${__RESET_COLOR}" + echo "MIT License" + # ------------------------------------------ + # copyright section + # ------------------------------------------ + Array::wrap2 ' ' 76 0 "$(copyrightCallback)" +} + + +beforeParseCallback +specificRequirements + +upgradeGithubReleaseCommandParse "$@" +MAIN_FUNCTION_NAME="main" +main() { + +# shellcheck disable=SC2154 + +computeCurrentCommandVersion() { + if [[ -n "${optionCurrentVersion}" ]]; then + echo "${optionCurrentVersion}" + return 0 + fi + if [[ -n "${optionVersionArg}" ]]; then + "${targetFileArg}" "${optionVersionArg}" 2>&1 | Version::parse || return 3 + fi +} + +EXACT_VERSION="${optionExactVersion}" \ + Github::upgradeRelease \ + "${targetFileArg}" \ + "${githubUrlPatternArg}" \ + "${optionVersionArg}" \ + computeCurrentCommandVersion \ + Github::defaultInstall } -facade_main_upgradeGithubReleasesh "$@" +# if file is sourced avoid calling main function +# shellcheck disable=SC2178 +BASH_SOURCE=".$0" # cannot be changed in bash +# shellcheck disable=SC2128 +if test ".$0" == ".${BASH_SOURCE}"; then + if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then + main "$@" &>/dev/null + else + main "$@" + fi +fi diff --git a/src/_binaries/Git/testsData/.env b/src/_binaries/Git/testsData/.env deleted file mode 100755 index 24e891d2..00000000 --- a/src/_binaries/Git/testsData/.env +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env bash -# shellcheck disable=SC2034 - -# Default settings -# you can override these settings by creating ${HOME}/.bash-tools/.env file -### -### LOG Level -### minimum level of the messages that will be logged into LOG_FILE -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_LOG_LEVEL=0 - -### -### DISPLAY Level -### minimum level of the messages that will be displayed on screen -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} - -### -### Log to file -### -### all log messages will be redirected to log file specified -### this same path will be used inside and outside of the container -### -BASH_FRAMEWORK_LOG_FILE= - -# absolute directory containing db import sql dumps -DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} - -# garbage collect all files for which modification is greater than eg: 30 days (+30) -# each time an existing file is used by dbImport/dbImportTable -# the file modification time is set to now -DB_IMPORT_GARBAGE_COLLECT_DAYS=+30 - -# absolute directory containing dbScripts used by dbScriptAllDatabases -SCRIPTS_FOLDER=${HOME}/.bash-tools/conf/dbScripts - -# ----------------------------------------------------- -# AWS Parameters -# ----------------------------------------------------- -S3_BASE_URL=s3://s3server/exports diff --git a/src/_binaries/Git/testsData/upgradeGithubRelease.help.txt b/src/_binaries/Git/testsData/upgradeGithubRelease.help.txt deleted file mode 100644 index 69b50024..00000000 --- a/src/_binaries/Git/testsData/upgradeGithubRelease.help.txt +++ /dev/null @@ -1,130 +0,0 @@ -DESCRIPTION: -retrieve latest binary release from github and install it -USAGE: upgradeGithubRelease [OPTIONS] [ARGUMENTS] -USAGE: upgradeGithubRelease [--version-arg ] - [--current-version|-c ] [--exact-version|-e ] - [--bash-framework-config ] [--config] [--verbose|-v] [-vv] [-vvv] - [--env-file ] [--no-color] [--theme ] [--help|-h] [--version] - [--quiet|-q] [--log-level ] [--log-file ] - [--display-level ] - -ARGUMENTS: - targetFile {single} (mandatory) - the binary downloaded will be written to this file path. - - Ensure the path is writable. - githubUrlPattern {single} (mandatory) - the url pattern to use to download the binary, see examples below. - - @version@ is template variable that will be replaced by the latest - - version tag found on github. - -VERSION MANAGEMENT: - --version-arg  {single} - The argument that will be provided to the currently installed binary - - to check the version of the software. - - See options constraints below. - Default value: --version - --current-version, -c  {single} - Sometimes the command to retrieve the version is complicated. - - Some command needs you to parse json or other commands provides mu - ltiple - - sub command versions. In this case you can provide the version you - - currently have, see examples below. - - See options constraints below. - --exact-version, -e  {single} - if provided and currently installed binary is not this exactVersion, - - This exact version of the binary will be installed. - - See options constraints below. - -GLOBAL OPTIONS: - --bash-framework-config  {single} - use alternate bash framework configuration. - --config {single} - Display configuration - --verbose, -v {single} - info level verbose mode (alias of --display-level INFO) - -vv {single} - debug level verbose mode (alias of --display-level DEBUG) - -vvv {single} - trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-config - option instead) - --no-color {single} - Produce monochrome output. alias of --theme noColor. - --theme  {single} - choose color theme - default-force means colors will be produced even if com - mand is piped - Default value: default - Possible values: default|default-force|noColor - --help, -h {single} - Display this command help - --version {single} - Print version information and quit - --quiet, -q {single} - quiet mode, doesn't display any output - --log-level  {single} - Set log level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - --log-file  {single} - Set log file - --display-level  {single} - set display level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - -OPTIONS CONSTRAINTS: - ---current-version|-c and --version-arg are mutually exclusive, you cannot use both argument at the same time. - -GITHUB TEMPLATE URLS EXAMPLES: - -Simple ones(Sometimes @version@ template variable has to be specified twice): -"https://github.com/hadolint/hadolint/releases/download/v@version@/hadolint-Linux-x86_64" -"https://github.com/koalaman/shellcheck/releases/download/v@version@/shellcheck-v@version@.linux.x86_64.tar.xz" -"https://github.com/sharkdp/fd/releases/download/v@version@/fd_@version@_amd64.deb" -"https://github.com/sharkdp/bat/releases/download/v@version@/bat_@version@_amd64.deb" -"https://github.com/kubernetes-sigs/kind/releases/download/v@version@/kind-linux-amd64" -"https://github.com/kubernetes/minikube/releases/download/v@version@/minikube-linux-amd64" -"https://github.com/plantuml/plantuml/releases/download/v@version@/plantuml-@version@.jar" -"https://github.com/Versent/saml2aws/releases/download/v@version@/saml2aws_@version@_linux_amd64.tar.gz" - -If you want to add a condition on architecture(linux, windows, x86, 64/32 bits): -"https://github.com/docker/compose/releases/download/v@version@/docker-compose-$(uname -s | tr "[:upper:]" "[:lower:]")-$(uname -m)" -"https://github.com/docker/docker-credential-helpers/releases/download/v@version@/docker-credential-wincred-v@version@.windows-$(dpkg --print-architecture).exe" -"https://github.com/Blacksmoke16/oq/releases/download/v@version@/oq-v@version@-$(uname -s)-$(uname -m)" - -COMMAND EXAMPLES: -Download docker-compose latest version -upgradeGithubRelease /usr/local/bin/docker-compose \ - "https://github.com/docker/compose/releases/download/v@version@/docker-compose-$(uname -s | tr "[:upper:]" "[:lower:]")-$(uname -m)" - -Download oq specific version -upgradeGithubRelease /usr/local/bin/oq --exact-version 1.3.4 \ - "https://github.com/Blacksmoke16/oq/releases/download/v@version@/oq-v@version@-$(uname -s)-$(uname -m)" - -Download oq specific version correctly retrieving the oq version and not the jq one -upgradeGithubRelease /usr/local/bin/oq --exact-version 1.3.4 --version-arg '-V | grep oq:' \ - "https://github.com/Blacksmoke16/oq/releases/download/v@version@/oq-v@version@-$(uname -s)-$(uname -m)" - -VERSION: 2.0 - -AUTHOR: -[François Chastanet](https://github.com/fchastanet) - -SOURCE FILE: -https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Git/upgradeGithubRelease.sh - -LICENSE: -MIT License - -Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Git/upgradeGithubRelease.help.txt b/src/_binaries/Git/upgradeGithubRelease.help.txt deleted file mode 100644 index 3c891ea7..00000000 --- a/src/_binaries/Git/upgradeGithubRelease.help.txt +++ /dev/null @@ -1,34 +0,0 @@ - -${__HELP_TITLE}OPTIONS CONSTRAINTS:${__HELP_NORMAL} - -${__HELP_OPTION_COLOR}--current-version${__HELP_NORMAL}|${__HELP_OPTION_COLOR}-c${__HELP_NORMAL} and ${__HELP_OPTION_COLOR}--version-arg${__HELP_NORMAL} are mutually exclusive, you cannot use both argument at the same time. - -${__HELP_TITLE}GITHUB TEMPLATE URLS EXAMPLES:${__HELP_NORMAL} - -Simple ones(Sometimes @version@ template variable has to be specified twice):${__HELP_EXAMPLE} -\"https://github.com/hadolint/hadolint/releases/download/v@version@/hadolint-Linux-x86_64\" -\"https://github.com/koalaman/shellcheck/releases/download/v@version@/shellcheck-v@version@.linux.x86_64.tar.xz\" -\"https://github.com/sharkdp/fd/releases/download/v@version@/fd_@version@_amd64.deb\" -\"https://github.com/sharkdp/bat/releases/download/v@version@/bat_@version@_amd64.deb\" -\"https://github.com/kubernetes-sigs/kind/releases/download/v@version@/kind-linux-amd64\" -\"https://github.com/kubernetes/minikube/releases/download/v@version@/minikube-linux-amd64\" -\"https://github.com/plantuml/plantuml/releases/download/v@version@/plantuml-@version@.jar\" -\"https://github.com/Versent/saml2aws/releases/download/v@version@/saml2aws_@version@_linux_amd64.tar.gz\" -${__HELP_NORMAL} -If you want to add a condition on architecture(linux, windows, x86, 64/32 bits):${__HELP_EXAMPLE} -\"https://github.com/docker/compose/releases/download/v@version@/docker-compose-\$(uname -s | tr \"[:upper:]\" \"[:lower:]\")-\$(uname -m)\" -\"https://github.com/docker/docker-credential-helpers/releases/download/v@version@/docker-credential-wincred-v@version@.windows-\$(dpkg --print-architecture).exe\" -\"https://github.com/Blacksmoke16/oq/releases/download/v@version@/oq-v@version@-\$(uname -s)-\$(uname -m)\" -${__HELP_NORMAL} -${__HELP_TITLE}COMMAND EXAMPLES:${__HELP_NORMAL} -Download docker-compose latest version -${__HELP_EXAMPLE}upgradeGithubRelease /usr/local/bin/docker-compose \\ - \"https://github.com/docker/compose/releases/download/v@version@/docker-compose-\$(uname -s | tr \"[:upper:]\" \"[:lower:]\")-\$(uname -m)\"${__HELP_NORMAL} - -Download oq specific version -${__HELP_EXAMPLE}upgradeGithubRelease /usr/local/bin/oq --exact-version 1.3.4 \\ - \"https://github.com/Blacksmoke16/oq/releases/download/v@version@/oq-v@version@-\$(uname -s)-\$(uname -m)\"${__HELP_NORMAL} - -Download oq specific version correctly retrieving the oq version and not the jq one -${__HELP_EXAMPLE}upgradeGithubRelease /usr/local/bin/oq --exact-version 1.3.4 --version-arg '-V | grep oq:' \\ - \"https://github.com/Blacksmoke16/oq/releases/download/v@version@/oq-v@version@-\$(uname -s)-\$(uname -m)\"${__HELP_NORMAL} diff --git a/src/_binaries/Git/upgradeGithubRelease.options.tpl b/src/_binaries/Git/upgradeGithubRelease.options.tpl deleted file mode 100644 index a03d3126..00000000 --- a/src/_binaries/Git/upgradeGithubRelease.options.tpl +++ /dev/null @@ -1,120 +0,0 @@ -% -declare versionNumber="2.0" -declare commandFunctionName="upgradeGithubReleaseCommand" -declare copyrightBeginYear="2023" -declare help="retrieve latest binary release from github and install it" -# shellcheck disable=SC2016 -declare longDescription -longDescription="$( -% -.INCLUDE "$(dynamicSrcFile "_binaries/Git/upgradeGithubRelease.help.txt")" -% -)" - -# TODO find a way to not duplicate this info -declare defaultVersionArg="--version" -% - -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.base.tpl)" - -% -# shellcheck source=/dev/null -source <( - targetFileArgCallback() { :; } - Options::generateArg \ - --help $'the binary downloaded will be written to this file path.\n - Ensure the path is writable.' \ - --min 1 \ - --max 1 \ - --name "targetFile" \ - --variable-name "targetFileArg" \ - --callback targetFileArgCallback \ - --function-name targetFileArgFunction - - githubUrlPatternArgCallback() { :; } - # shellcheck disable=SC2116 - Options::generateArg \ - --help $'the url pattern to use to download the binary, see examples below.\n - @version@ is template variable that will be replaced by the latest \n - version tag found on github.' \ - --min 1 \ - --max 1 \ - --name "githubUrlPattern" \ - --variable-name "githubUrlPatternArg" \ - --callback githubUrlPatternArgCallback \ - --function-name githubUrlPatternArgFunction - - Options::generateGroup \ - --title "VERSION MANAGEMENT:" \ - --function-name groupVersionManagementFunction - - # shellcheck disable=SC2116,SC2016 - Options::generateOption \ - --help-value-name "versionArg" \ - --default-value "${defaultVersionArg}" \ - --help $'The argument that will be provided to the currently installed binary\n - to check the version of the software. \n - See options constraints below.' \ - --group groupVersionManagementFunction \ - --alt "--version-arg" \ - --variable-type "String" \ - --variable-name "optionVersionArg" \ - --function-name optionVersionArgFunction - - # shellcheck disable=SC2116 - Options::generateOption \ - --help-value-name "currentVersion" \ - --help $'Sometimes the command to retrieve the version is complicated. \n - Some command needs you to parse json or other commands provides multiple \n - sub command versions. In this case you can provide the version you \n - currently have, see examples below. \n - See options constraints below.' \ - --group groupVersionManagementFunction \ - --alt "--current-version" \ - --alt "-c" \ - --variable-type "String" \ - --variable-name "optionCurrentVersion" \ - --function-name optionCurrentVersionFunction - - # shellcheck disable=SC2116,SC2016 - Options::generateOption \ - --help-value-name "exactVersion" \ - --help $'if provided and currently installed binary is not this exactVersion,\n - This exact version of the binary will be installed.\n - See options constraints below.' \ - --group groupVersionManagementFunction \ - --alt "--exact-version" \ - --alt "-e" \ - --variable-type "String" \ - --variable-name "optionExactVersion" \ - --function-name optionExactVersionFunction -) -options+=( - targetFileArgFunction - githubUrlPatternArgFunction - optionVersionArgFunction - optionCurrentVersionFunction - optionExactVersionFunction -) -Options::generateCommand "${options[@]}" -% - -githubUrlPatternArgCallback() { - if [[ ! "${githubUrlPatternArg}" =~ ^https://github.com/ ]]; then - Log::fatal "Invalid githubUrlPattern ${githubUrlPatternArg} provided, it should begin with https://github.com/" - fi -} - -targetFileArgCallback() { - if [[ "${targetFileArg:0:1}" != "/" ]]; then - targetFileArg="$(pwd)/${targetFileArg}" - fi - if ! Assert::validPath "${targetFileArg}"; then - Log::fatal "File ${targetFileArg} is not a valid path" - fi - if ! Assert::fileWritable "${targetFileArg}"; then - Log::fatal "File ${targetFileArg} is not writable" - fi -} - -<% ${commandFunctionName} %> parse "${BASH_FRAMEWORK_ARGV[@]}" diff --git a/src/_binaries/Git/upgradeGithubRelease.sh b/src/_binaries/Git/upgradeGithubRelease.sh deleted file mode 100755 index 6b7e3850..00000000 --- a/src/_binaries/Git/upgradeGithubRelease.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/upgradeGithubRelease -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE -# shellcheck disable=SC2034 - -# default -declare defaultVersionArg="--version" - -# option values -declare targetFileArg="" -declare githubUrlPatternArg="" -declare optionVersionArg="${defaultVersionArg}" -declare optionCurrentVersion="" -declare optionExactVersion="" - -# other values -declare copyrightBeginYear="2020" - -.INCLUDE "$(dynamicTemplateDir _binaries/Git/upgradeGithubRelease.options.tpl)" - -run() { - - computeCurrentCommandVersion() { - if [[ -n "${optionCurrentVersion}" ]]; then - echo "${optionCurrentVersion}" - return 0 - fi - if [[ -n "${optionVersionArg}" ]]; then - "${targetFileArg}" "${optionVersionArg}" 2>&1 | Version::parse || return 3 - fi - } - - EXACT_VERSION="${optionExactVersion}" \ - Github::upgradeRelease \ - "${targetFileArg}" \ - "${githubUrlPatternArg}" \ - "${optionVersionArg}" \ - computeCurrentCommandVersion \ - Github::defaultInstall -} - -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run -fi diff --git a/src/_binaries/Github/upgradeGithubRelease/testsData/upgradeGithubRelease.help.txt b/src/_binaries/Github/upgradeGithubRelease/testsData/upgradeGithubRelease.help.txt new file mode 100644 index 00000000..0917fa79 --- /dev/null +++ b/src/_binaries/Github/upgradeGithubRelease/testsData/upgradeGithubRelease.help.txt @@ -0,0 +1,115 @@ +SYNOPSIS: + Retrieve latest binary release from github and install it. + +USAGE: upgradeGithubRelease [OPTIONS] [ARGUMENTS] +USAGE: upgradeGithubRelease [--help|-h] [--config] + [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] + [--env-file ] [--log-level ] [--log-file ] + [--display-level ] [--no-color] [--theme ] [--version] + [--quiet|-q] [--version-arg ] + [--current-version|-c ] [--exact-version|-e ] + +ARGUMENTS: + targetFile {single} (mandatory) + The binary downloaded will be written to this file path. Ensure the path + is writable. + githubUrlPattern {single} (mandatory) + The url pattern to use to download the binary, see examples below. + @version@ is template variable that will be replaced by the latest + version tag found on github. + +GLOBAL OPTIONS: + --help, -h {single} + Displays this command help + --config {single} + Displays configuration + --bash-framework-config  {single} + Use alternate bash framework configuration. + --verbose, -v {single} + Info level verbose mode (alias of --display-level INFO) + -vv {single} + Debug level verbose mode (alias of --display-level DEBUG) + -vvv {single} + Trace level verbose mode (alias of --display-level TRACE) + --env-file  {list} (optional) + Load the specified env file (deprecated, please use --bash-framework-con + fig option instead) + --log-level  {single} + Set log level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --log-file  {single} + Set log file + --display-level  {single} + Set display level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --no-color {single} + Produce monochrome output. alias of --theme noColor. + --theme  {single} + Choose color theme - default-force means colors will be produced even if + command is piped. + Possible values: default, default-force, noColor + Default value: default + --version {single} + Print version information and quit. + --quiet, -q {single} + Quiet mode, doesn't display any output. + +VERSION MANAGEMENT: + --version-arg  {single} + The argument that will be provided to the currently installed binary + to check the version of the software. See options constraints below. + + Default value: --version + --current-version, -c  {single} + Sometimes the command to retrieve the version is complicated. + Some command needs you to parse json or other commands + that provides multiple sub command versions. + In this case you can provide the version you currently have. + See options constraints and examples below. + --exact-version, -e  {single} + If provided and currently installed binary is not this exactVersion, + this exact version of the binary will be installed. + See options constraints below. + + +DESCRIPTION: + GITHUB TEMPLATE URLS EXAMPLES: + + Simple ones(Sometimes @version@ template variable has to be specified twice): + "https://github.com/hadolint/hadolint/releases/download/v@version@/hadolint-Linux-x86_64" + "https://github.com/koalaman/shellcheck/releases/download/v@version@/shellcheck-v@version@.linux.x86_64.tar.xz" + "https://github.com/sharkdp/fd/releases/download/v@version@/fd_@version@_amd64.deb" + "https://github.com/sharkdp/bat/releases/download/v@version@/bat_@version@_amd64.deb" + "https://github.com/kubernetes-sigs/kind/releases/download/v@version@/kind-linux-amd64" + "https://github.com/kubernetes/minikube/releases/download/v@version@/minikube-linux-amd64" + "https://github.com/plantuml/plantuml/releases/download/v@version@/plantuml-@version@.jar" + "https://github.com/Versent/saml2aws/releases/download/v@version@/saml2aws_@version@_linux_amd64.tar.gz" + + If you want to add a condition on architecture(linux, windows, x86, 64/32 bits): + "https://github.com/docker/compose/releases/download/v@version@/docker-compose-$(uname -s | tr "[:upper:]" "[:lower:]")-$(uname -m)" + "https://github.com/docker/docker-credential-helpers/releases/download/v@version@/docker-credential-wincred-v@version@.windows-$(dpkg --print-architecture).exe" + "https://github.com/Blacksmoke16/oq/releases/download/v@version@/oq-v@version@-$(uname -s)-$(uname -m)" + + COMMAND EXAMPLES: + + Download docker-compose latest version + upgradeGithubRelease /usr/local/bin/docker-compose \ + "https://github.com/docker/compose/releases/download/v@version@/docker-compose-$(uname -s | tr "[:upper:]" "[:lower:]")-$(uname -m)" + + Download oq specific version + upgradeGithubRelease /usr/local/bin/oq --exact-version 1.3.4 \ + "https://github.com/Blacksmoke16/oq/releases/download/v@version@/oq-v@version@-$(uname -s)-$(uname -m)" + + Download oq specific version correctly retrieving the oq version and not the jq one + upgradeGithubRelease /usr/local/bin/oq --exact-version 1.3.4 --version-arg '-V | grep oq:' \ + "https://github.com/Blacksmoke16/oq/releases/download/v@version@/oq-v@version@-$(uname -s)-$(uname -m)" + + +VERSION: 3.0 + +AUTHOR: [François Chastanet](https://github.com/fchastanet) + +SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Git/upgradeGithubRelease/upgradeGithubRelease-binary.yaml + +LICENSE: MIT License +Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Git/testsData/upgradeGithubRelease_bin b/src/_binaries/Github/upgradeGithubRelease/testsData/upgradeGithubRelease_bin similarity index 100% rename from src/_binaries/Git/testsData/upgradeGithubRelease_bin rename to src/_binaries/Github/upgradeGithubRelease/testsData/upgradeGithubRelease_bin diff --git a/src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease-binary.yaml b/src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease-binary.yaml new file mode 100644 index 00000000..b0edc529 --- /dev/null +++ b/src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease-binary.yaml @@ -0,0 +1,79 @@ +extends: + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" + +vars: + SRC_FILE_PATH: src/_binaries/Git/upgradeGithubRelease/upgradeGithubRelease-binary.yaml + +compilerConfig: + targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/upgradeGithubRelease" + relativeRootDirBasedOnTargetDir: .. + srcDirs: + - ${BASH_TOOLS_ROOT_DIR}/src +binData: + commands: + default: + functionName: upgradeGithubReleaseCommand + version: "3.0" + copyrightBeginYear: 2020 + commandName: upgradeGithubRelease + beforeParseCallbacks: + - beforeParseCallback + - specificRequirements + definitionFiles: + 20: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease-options.sh + mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease-main.sh + help: Retrieve latest binary release from github and install it. + longDescription: longDescriptionFunction + args: + - type: String + min: 1 + max: 1 + help: + The binary downloaded will be written to this file path. Ensure the + path is writable. + name: targetFile + callbacks: + - targetFileArgCallback + variableName: targetFileArg + - type: String + min: 1 + max: 1 + help: githubUrlPatternHelpFunction + name: githubUrlPattern + callbacks: + - githubUrlPatternArgCallback + variableName: githubUrlPatternArg + optionGroups: + groupVersionManagementFunction: + title: "VERSION MANAGEMENT:" + options: + - variableName: optionVersionArg + group: groupVersionManagementFunction + type: String + help: | + The argument that will be provided to the currently installed binary + to check the version of the software. See options constraints below. + helpValueName: versionArg + defaultValue: "--version" + alts: + - --version-arg + + - variableName: optionCurrentVersion + group: groupVersionManagementFunction + type: String + help: optionCurrentVersionHelpFunction + helpValueName: currentVersion + alts: + - --current-version + - -c + + - variableName: optionExactVersion + group: groupVersionManagementFunction + type: String + help: exactVersionHelpFunction + helpValueName: exactVersion + alts: + - --exact-version + - -e diff --git a/src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease-main.sh b/src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease-main.sh new file mode 100755 index 00000000..5d7295be --- /dev/null +++ b/src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease-main.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2154 + +computeCurrentCommandVersion() { + if [[ -n "${optionCurrentVersion}" ]]; then + echo "${optionCurrentVersion}" + return 0 + fi + if [[ -n "${optionVersionArg}" ]]; then + "${targetFileArg}" "${optionVersionArg}" 2>&1 | Version::parse || return 3 + fi +} + +EXACT_VERSION="${optionExactVersion}" \ + Github::upgradeRelease \ + "${targetFileArg}" \ + "${githubUrlPatternArg}" \ + "${optionVersionArg}" \ + computeCurrentCommandVersion \ + Github::defaultInstall diff --git a/src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease-options.sh b/src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease-options.sh new file mode 100755 index 00000000..d49e9e82 --- /dev/null +++ b/src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease-options.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash + +specificRequirements() { + Linux::requireJqCommand +} + +longDescriptionFunction() { + echo -e " ${__HELP_TITLE}GITHUB TEMPLATE URLS EXAMPLES:${__HELP_NORMAL}" + echo + echo -e " Simple ones(Sometimes @version@ template variable has to be specified twice):${__HELP_EXAMPLE}" + echo -e ' "https://github.com/hadolint/hadolint/releases/download/v@version@/hadolint-Linux-x86_64"' + echo -e ' "https://github.com/koalaman/shellcheck/releases/download/v@version@/shellcheck-v@version@.linux.x86_64.tar.xz"' + echo -e ' "https://github.com/sharkdp/fd/releases/download/v@version@/fd_@version@_amd64.deb"' + echo -e ' "https://github.com/sharkdp/bat/releases/download/v@version@/bat_@version@_amd64.deb"' + echo -e ' "https://github.com/kubernetes-sigs/kind/releases/download/v@version@/kind-linux-amd64"' + echo -e ' "https://github.com/kubernetes/minikube/releases/download/v@version@/minikube-linux-amd64"' + echo -e ' "https://github.com/plantuml/plantuml/releases/download/v@version@/plantuml-@version@.jar"' + echo -e ' "https://github.com/Versent/saml2aws/releases/download/v@version@/saml2aws_@version@_linux_amd64.tar.gz"' + echo -e "${__HELP_NORMAL}" + echo -e " If you want to add a condition on architecture(linux, windows, x86, 64/32 bits):${__HELP_EXAMPLE}" + # shellcheck disable=SC2016 + echo -e ' "https://github.com/docker/compose/releases/download/v@version@/docker-compose-$(uname -s | tr "[:upper:]" "[:lower:]")-$(uname -m)"' + # shellcheck disable=SC2016 + echo -e ' "https://github.com/docker/docker-credential-helpers/releases/download/v@version@/docker-credential-wincred-v@version@.windows-$(dpkg --print-architecture).exe"' + # shellcheck disable=SC2016 + echo -e ' "https://github.com/Blacksmoke16/oq/releases/download/v@version@/oq-v@version@-$(uname -s)-$(uname -m)"' + echo -e "${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}COMMAND EXAMPLES:${__HELP_NORMAL}" + echo -e "${__HELP_NORMAL}" + echo -e " Download docker-compose latest version${__HELP_EXAMPLE}" + echo -e " upgradeGithubRelease /usr/local/bin/docker-compose \\" + # shellcheck disable=SC2016 + echo -e ' "https://github.com/docker/compose/releases/download/v@version@/docker-compose-$(uname -s | tr "[:upper:]" "[:lower:]")-$(uname -m)"' + echo -e "${__HELP_NORMAL}" + echo -e " Download oq specific version${__HELP_EXAMPLE}" + echo -e " upgradeGithubRelease /usr/local/bin/oq --exact-version 1.3.4 \\" + # shellcheck disable=SC2016 + echo -e ' "https://github.com/Blacksmoke16/oq/releases/download/v@version@/oq-v@version@-$(uname -s)-$(uname -m)"' + echo -e "${__HELP_NORMAL}" + echo -e " Download oq specific version correctly retrieving the oq version and not the jq one${__HELP_EXAMPLE}" + echo -e " upgradeGithubRelease /usr/local/bin/oq --exact-version 1.3.4 --version-arg '-V | grep oq:' \\" + # shellcheck disable=SC2016 + echo -e ' "https://github.com/Blacksmoke16/oq/releases/download/v@version@/oq-v@version@-$(uname -s)-$(uname -m)"' + echo -e "${__HELP_NORMAL}" +} + +githubUrlPatternHelpFunction() { + echo ' The url pattern to use to download the binary, see examples below.' + echo ' @version@ is template variable that will be replaced by the latest' + echo ' version tag found on github.' +} + +optionCurrentVersionHelpFunction() { + echo ' Sometimes the command to retrieve the version is complicated.' + echo ' Some command needs you to parse json or other commands' + echo ' that provides multiple sub command versions.' + echo ' In this case you can provide the version you currently have.' + echo ' See options constraints and examples below.' +} + +exactVersionHelpFunction() { + echo ' If provided and currently installed binary is not this exactVersion,' + echo ' this exact version of the binary will be installed.' + echo ' See options constraints below.' +} + +optionHelpCallback() { + upgradeGithubReleaseCommandHelp + exit 0 +} + +githubUrlPatternArgCallback() { + # shellcheck disable=SC2154 + if [[ ! "${githubUrlPatternArg}" =~ ^https://github.com/ ]]; then + Log::fatal "Invalid githubUrlPattern ${githubUrlPatternArg} provided, it should begin with https://github.com/" + fi +} + +targetFileArgCallback() { + if [[ "${targetFileArg:0:1}" != "/" ]]; then + targetFileArg="$(pwd)/${targetFileArg}" + fi + if ! Assert::validPath "${targetFileArg}"; then + Log::fatal "File ${targetFileArg} is not a valid path" + fi + if ! Assert::fileWritable "${targetFileArg}"; then + Log::fatal "File ${targetFileArg} is not writable" + fi +} diff --git a/src/_binaries/Git/upgradeGithubRelease.bats b/src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease.bats similarity index 99% rename from src/_binaries/Git/upgradeGithubRelease.bats rename to src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease.bats index 4c3b130f..d739e202 100755 --- a/src/_binaries/Git/upgradeGithubRelease.bats +++ b/src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease.bats @@ -1,7 +1,7 @@ #!/usr/bin/env bash # shellcheck source=src/batsHeaders.sh -source "$(cd "${BATS_TEST_DIRNAME}/../.." && pwd)/batsHeaders.sh" +source "$(cd "${BATS_TEST_DIRNAME}/../../.." && pwd)/batsHeaders.sh" load "${FRAMEWORK_ROOT_DIR}/src/_standalone/Bats/assert_lines_count.sh" From 0ab09ec03670f34d7e8a4b53a434dfd280df160f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Sat, 7 Sep 2024 10:58:22 +0200 Subject: [PATCH 17/24] compiled waitForIt using go compiler --- bin/gitIsAncestorOf | 2 +- bin/gitIsBranch | 2 +- bin/gitRenameBranch | 2 +- bin/waitForIt | 2922 +++++++++-------- src/BashTools/runVerboseIfNeeded.bats | 7 +- src/BashTools/runVerboseIfNeeded.sh | 2 +- src/_binaries/Utils/waitForIt.options.tpl | 118 - src/_binaries/Utils/waitForIt.sh | 192 -- .../waitForIt/testsData/waitForIt.help.txt | 94 + .../Utils/waitForIt/waitForIt-binary.yaml | 93 + .../Utils/waitForIt/waitForIt-main.sh | 165 + .../Utils/waitForIt/waitForIt-options.sh | 61 + .../Utils/{ => waitForIt}/waitForIt.bats | 92 +- .../commandDefinitions/optionsTimeout.sh | 8 + .../commandDefinitions/optionsTimeout.yaml | 21 + src/_binaries/options/options.timeout.tpl | 26 - 16 files changed, 2129 insertions(+), 1678 deletions(-) delete mode 100644 src/_binaries/Utils/waitForIt.options.tpl delete mode 100755 src/_binaries/Utils/waitForIt.sh create mode 100644 src/_binaries/Utils/waitForIt/testsData/waitForIt.help.txt create mode 100644 src/_binaries/Utils/waitForIt/waitForIt-binary.yaml create mode 100755 src/_binaries/Utils/waitForIt/waitForIt-main.sh create mode 100755 src/_binaries/Utils/waitForIt/waitForIt-options.sh rename src/_binaries/Utils/{ => waitForIt}/waitForIt.bats (67%) create mode 100755 src/_binaries/commandDefinitions/optionsTimeout.sh create mode 100644 src/_binaries/commandDefinitions/optionsTimeout.yaml delete mode 100644 src/_binaries/options/options.timeout.tpl diff --git a/bin/gitIsAncestorOf b/bin/gitIsAncestorOf index 10986f44..b5abf0e9 100755 --- a/bin/gitIsAncestorOf +++ b/bin/gitIsAncestorOf @@ -405,7 +405,7 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp # @exitcode command's exit code BashTools::runVerboseIfNeeded() { # shellcheck disable=SC2154 - if [[ "${optionInfoVerbose}" = "1" ]]; then + if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_INFO)); then echo >&2 "+ $*" fi ( diff --git a/bin/gitIsBranch b/bin/gitIsBranch index cd5f8a09..b9f09656 100755 --- a/bin/gitIsBranch +++ b/bin/gitIsBranch @@ -405,7 +405,7 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp # @exitcode command's exit code BashTools::runVerboseIfNeeded() { # shellcheck disable=SC2154 - if [[ "${optionInfoVerbose}" = "1" ]]; then + if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_INFO)); then echo >&2 "+ $*" fi ( diff --git a/bin/gitRenameBranch b/bin/gitRenameBranch index 08da8777..cac3a882 100755 --- a/bin/gitRenameBranch +++ b/bin/gitRenameBranch @@ -405,7 +405,7 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp # @exitcode command's exit code BashTools::runVerboseIfNeeded() { # shellcheck disable=SC2154 - if [[ "${optionInfoVerbose}" = "1" ]]; then + if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_INFO)); then echo >&2 "+ $*" fi ( diff --git a/bin/waitForIt b/bin/waitForIt index 8d463fa4..e9734453 100755 --- a/bin/waitForIt +++ b/bin/waitForIt @@ -1,13 +1,12 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FACADE FROM https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Utils/waitForIt.sh +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Utils/waitForIt/waitForIt-binary.yaml # DO NOT EDIT IT # @generated ############################################################################### # shellcheck disable=SC2288,SC2034 -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/waitForIt -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE + + # ensure that no user aliases could interfere with # commands used in this script @@ -62,13 +61,6 @@ interruptManagement() { kill -s INT "$$" } trap interruptManagement INT -SCRIPT_NAME=${0##*/} -REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" -if [[ -n "${EMBED_CURRENT_DIR}" ]]; then - CURRENT_DIR="${EMBED_CURRENT_DIR}" -else - CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" -fi ################################################ # Temp dir management @@ -102,6 +94,20 @@ cleanOnExit() { } trap cleanOnExit EXIT HUP QUIT ABRT TERM + +SCRIPT_NAME=${0##*/} +REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" +if [[ -n "${EMBED_CURRENT_DIR}" ]]; then + CURRENT_DIR="${EMBED_CURRENT_DIR}" +else + CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" +fi +FRAMEWORK_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" +FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" +FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + # @description Log namespace provides 2 kind of functions # - Log::display* allows to display given message with # given display level @@ -132,168 +138,262 @@ export __VERBOSE_LEVEL_DEBUG=2 # @description verbose level info export __VERBOSE_LEVEL_TRACE=3 -# @description Display message using info color (bg light blue/fg white) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayInfo() { - local type="${2:-INFO}" - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description check if an element is contained in an array +# +# @arg $1 needle:String +# @arg $@ array:String[] +# @exitcode 0 if found +# @exitcode 1 otherwise +# @example +# Array::contains "${libPath}" "${__BASH_FRAMEWORK_IMPORTED_FILES[@]}" +Array::contains() { + local element + for element in "${@:2}"; do + [[ "${element}" = "$1" ]] && return 0 + done + return 1 +} + + +# @description concatenate each element of an array with a separator +# but wrapping text when line length is more than provided argument +# The algorithm will try not to cut the array element if it can. +# - if an arg can be placed on current line it will be, +# otherwise current line is printed and arg is added to the new +# current line +# - Empty arg is interpreted as a new line. +# - Add \r to arg in order to force break line and avoid following +# arg to be concatenated with current arg. +# +# @arg $1 glue:String +# @arg $2 maxLineLength:int +# @arg $3 indentNextLine:int +# @arg $@ array:String[] +Array::wrap2() { + local glue="${1-}" + local -i glueLength="${#glue}" + shift || true + local -i maxLineLength=$1 + shift || true + local -i indentNextLine=$1 + shift || true + local indentStr="" + if ((indentNextLine > 0)); then + indentStr="$(head -c "${indentNextLine}" 0)); do + arg="$1" + shift || true + + # replace tab by 2 spaces + arg="${arg//$'\t'/ }" + # remove trailing spaces + arg="${arg%[[:blank:]]}" + if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then + printCurrentLine + ((previousLineEmpty = 1)) + continue + else + if ((previousLineEmpty == 1)); then + printCurrentLine + fi + ((previousLineEmpty = 0)) || true + fi + # convert eol to args + mapfile -t additionalLines <<<"${arg}" + if ((${#additionalLines[@]} > 1)); then + set -- "${additionalLines[@]}" "$@" + continue + fi + + ((argLength = ${#arg})) || true + + # empty arg + if ((argLength == 0)); then + if ((isNewline == 0)); then + # isNewline = 0 means currentLine is not empty + printCurrentLine + fi + continue + fi + + if ((isNewline == 0)); then + glueLength="${#glue}" + else + glueLength="0" + fi + if ((currentLineLength + argLength + glueLength > maxLineLength)); then + if ((argLength + glueLength > maxLineLength)); then + # arg is too long to even fit on one line + # we have to split the arg on current and next line + local -i remainingLineLength + ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) + appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" + printCurrentLine + arg="${arg:${remainingLineLength}}" + # remove leading spaces + arg="${arg##[[:blank:]]}" + + set -- "${arg}" "$@" + else + # the arg can fit on next line + printCurrentLine + appendToCurrentLine "${arg}" "${argLength}" + fi + else + appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" + fi + done + if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then + printCurrentLine + fi + ) | sed -E -e 's/[[:blank:]]+$//' } -# @description Display message using debug color (gray) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayDebug() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then - Log::computeDuration - echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description check if command specified exists or return 1 +# with error and message if not +# +# @arg $1 commandName:String on which existence must be checked +# @arg $2 helpIfNotExists:String a help command to display if the command does not exist +# +# @exitcode 1 if the command specified does not exist +# @stderr diagnostic information + help if second argument is provided +Assert::commandExists() { + local commandName="$1" + local helpIfNotExists="$2" + + "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { + Log::displayError "${commandName} is not installed, please install it" + if [[ -n "${helpIfNotExists}" ]]; then + Log::displayInfo "${helpIfNotExists}" + fi + return 1 + } + return 0 +} + + +# @description check if tty (interactive mode) is active +# @noargs +# @exitcode 1 if tty not active +# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive +# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive +Assert::tty() { + if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then + return 1 fi - Log::logDebug "$1" + if [[ "${INTERACTIVE:-0}" = "1" ]]; then + return 0 + fi + tty -s } -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description ignore exit code 141 from simple command pipes +# @example use with: +# local resultingStatus=0 +# local -a originalPipeStatus=() +# cmd1 | cmd2 || Bash::handlePipelineFailure resultingStatus originalPipeStatus || true +# [[ "${resultingStatus}" = "0" ]] +# @arg $1 resultingStatusCode:&int (passed by reference) (optional) resulting status code +# @arg $2 originalStatus:int[] (passed by reference) (optional) copy of original PIPESTATUS array +# @env PIPESTATUS assuming that this function is called like in the example provided +# @see https://unix.stackexchange.com/a/709880/582856 +Bash::handlePipelineFailure() { + local -a pipeStatusBackup=("${PIPESTATUS[@]}") + local -n handlePipelineFailure_resultingStatusCode=$1 + local -n handlePipelineFailure_originalStatus=$2 + # shellcheck disable=SC2034 + handlePipelineFailure_originalStatus=("${pipeStatusBackup[@]}") + handlePipelineFailure_resultingStatusCode=0 + local statusCode + for statusCode in "${pipeStatusBackup[@]}"; do + if ((statusCode == 141)); then + return 0 + elif ((statusCode > 0)); then + # shellcheck disable=SC2034 + handlePipelineFailure_resultingStatusCode="${statusCode}" + break + fi + done + return "${handlePipelineFailure_resultingStatusCode}" +} + + +# @description run command specified +# @arg $@ array:String[] the command to run +# @env optionInfoVerbose int - if 1 displays the command specified before running it +# @env optionRedirectCmdOutputs String - if set redirect command outputs to file specified +# @exitcode command's exit code +BashTools::runVerboseIfNeeded() { + # shellcheck disable=SC2154 + if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_INFO)); then + echo >&2 "+ $*" fi - Log::logWarning "$1" + ( + # shellcheck disable=SC2154 + if [[ -n "${optionRedirectCmdOutputs:-}" ]]; then + exec >"${optionRedirectCmdOutputs:-}" + exec 2>"${optionRedirectCmdOutputs:-}" + fi + "$@" + ) } -# @description Display message using error color (red) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayError() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then - Log::computeDuration - echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description check if all requirements are satisfied +# to execute dbImport commands +Db::checkRequirements() { + if [[ "${SKIP_REQUIREMENTS_CHECKS:-0}" = "1" ]]; then + return 0 fi - Log::logError "$1" + local -i failures=0 + echo + Assert::commandExists mysql "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqldump "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists pv "sudo apt-get install -y pv" || ((++failures)) + Assert::commandExists gawk "sudo apt-get install -y gawk" || ((++failures)) + Assert::commandExists awk "sudo apt-get install -y gawk" || ((++failures)) + Version::checkMinimal "gawk" "--version" "5.0.1" || ((++failures)) + return "${failures}" } -# @description load colors theme constants -# @warning if tty not opened, noColor theme will be chosen -# @arg $1 theme:String the theme to use (default, noColor) -# @arg $@ args:String[] -# @set __ERROR_COLOR String indicate error status -# @set __INFO_COLOR String indicate info status -# @set __SUCCESS_COLOR String indicate success status -# @set __WARNING_COLOR String indicate warning status -# @set __SKIPPED_COLOR String indicate skipped status -# @set __DEBUG_COLOR String indicate debug status -# @set __HELP_COLOR String indicate help status -# @set __TEST_COLOR String not used -# @set __TEST_ERROR_COLOR String not used -# @set __HELP_TITLE_COLOR String used to display help title in help strings -# @set __HELP_OPTION_COLOR String used to display highlight options in help strings -# -# @set __RESET_COLOR String reset default color -# -# @set __HELP_EXAMPLE String to remove -# @set __HELP_TITLE String to remove -# @set __HELP_NORMAL String to remove -# shellcheck disable=SC2034 -UI::theme() { - local theme="${1-default}" - if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then - theme="noColor" - fi - case "${theme}" in - default | default-force) - theme="default" - ;; - noColor) ;; - *) - Log::fatal "invalid theme provided" - ;; - esac - if [[ "${theme}" = "default" ]]; then - BASH_FRAMEWORK_THEME="default" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='\e[31m' # Red - __INFO_COLOR='\e[44m' # white on lightBlue - __SUCCESS_COLOR='\e[32m' # Green - __WARNING_COLOR='\e[33m' # Yellow - __SKIPPED_COLOR='\e[33m' # Yellow - __DEBUG_COLOR='\e[37m' # Gray - __HELP_COLOR='\e[7;49;33m' # Black on Gold - __TEST_COLOR='\e[100m' # Light magenta - __TEST_ERROR_COLOR='\e[41m' # white on red - __HELP_TITLE_COLOR="\e[1;37m" # Bold - __HELP_OPTION_COLOR="\e[1;34m" # Blue - # Internal: reset color - __RESET_COLOR='\e[0m' # Reset Color - # shellcheck disable=SC2155,SC2034 - __HELP_EXAMPLE="$(echo -e "\e[2;97m")" - # shellcheck disable=SC2155,SC2034 - __HELP_TITLE="$(echo -e "\e[1;37m")" - # shellcheck disable=SC2155,SC2034 - __HELP_NORMAL="$(echo -e "\033[0m")" - else - BASH_FRAMEWORK_THEME="noColor" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='' - __INFO_COLOR='' - __SUCCESS_COLOR='' - __WARNING_COLOR='' - __SKIPPED_COLOR='' - __DEBUG_COLOR='' - __HELP_COLOR='' - __TEST_COLOR='' - __TEST_ERROR_COLOR='' - __HELP_TITLE_COLOR='' - __HELP_OPTION_COLOR='' - # Internal: reset color - __RESET_COLOR='' - __HELP_EXAMPLE='' - __HELP_TITLE='' - __HELP_NORMAL='' - fi -} - -# @description draw a line with the character passed in parameter repeated depending on terminal width -# @arg $1 character:String character to use as separator (default value #) -UI::drawLine() { - local character="${1:-#}" - local -i width=${COLUMNS:-0} - if ((width == 0)) && [[ -t 1 ]]; then - width=$(tput cols) - fi - if ((width == 0)); then - width=80 - fi - printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") - echo -} - -# @description Display message using error color (red) and exit immediately with error status 1 -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::fatal() { - Log::computeDuration - echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - Log::logFatal "$1" - exit 1 -} - -# @description create a temp file using default TMPDIR variable -# initialized in _includes/_commonHeader.sh -# @env TMPDIR String (default value /tmp) -# @arg $1 templateName:String template name to use(optional) -Framework::createTempFile() { - mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" -} # @description ensure env files are loaded # @arg $@ list of default files to load at the end @@ -301,6 +401,8 @@ Framework::createTempFile() { # @stderr diagnostics information is displayed # shellcheck disable=SC2120 Env::requireLoad() { + export REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 + local -a defaultFiles=("$@") # get list of possible config files local -a configFiles=() @@ -328,326 +430,14 @@ Env::requireLoad() { done } -# @description activate or not Log::display* and Log::log* functions -# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL -# environment variables loaded by Env::requireLoad -# try to create log file and rotate it if necessary -# @noargs -# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable -# @env BASH_FRAMEWORK_DISPLAY_LEVEL int -# @env BASH_FRAMEWORK_LOG_LEVEL int -# @env BASH_FRAMEWORK_LOG_FILE String -# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 -# @exitcode 0 always successful -# @stderr diagnostics information about log file is displayed -# @require Env::requireLoad -# @require UI::requireTheme -Log::requireLoad() { - if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - export BASH_FRAMEWORK_LOG_LEVEL - fi - - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then - if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 - fi - elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - fi - - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - # will always be created even if not in info level - Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" - if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then - Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" - fi - fi -} - -# @description concatenate each element of an array with a separator -# but wrapping text when line length is more than provided argument -# The algorithm will try not to cut the array element if it can. -# - if an arg can be placed on current line it will be, -# otherwise current line is printed and arg is added to the new -# current line -# - Empty arg is interpreted as a new line. -# - Add \r to arg in order to force break line and avoid following -# arg to be concatenated with current arg. -# -# @arg $1 glue:String -# @arg $2 maxLineLength:int -# @arg $3 indentNextLine:int -# @arg $@ array:String[] -Array::wrap2() { - local glue="${1-}" - local -i glueLength="${#glue}" - shift || true - local -i maxLineLength=$1 - shift || true - local -i indentNextLine=$1 - shift || true - local indentStr="" - if ((indentNextLine > 0)); then - indentStr="$(head -c "${indentNextLine}" 0)); do - arg="$1" - shift || true - - # replace tab by 2 spaces - arg="${arg//$'\t'/ }" - # remove trailing spaces - arg="${arg%[[:blank:]]}" - if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then - printCurrentLine - ((previousLineEmpty = 1)) - continue - else - if ((previousLineEmpty == 1)); then - printCurrentLine - fi - ((previousLineEmpty = 0)) || true - fi - # convert eol to args - mapfile -t additionalLines <<<"${arg}" - if ((${#additionalLines[@]} > 1)); then - set -- "${additionalLines[@]}" "$@" - continue - fi - - ((argLength = ${#arg})) || true - - # empty arg - if ((argLength == 0)); then - if ((isNewline == 0)); then - # isNewline = 0 means currentLine is not empty - printCurrentLine - fi - continue - fi - - if ((isNewline == 0)); then - glueLength="${#glue}" - else - glueLength="0" - fi - if ((currentLineLength + argLength + glueLength > maxLineLength)); then - if ((argLength + glueLength > maxLineLength)); then - # arg is too long to even fit on one line - # we have to split the arg on current and next line - local -i remainingLineLength - ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) - appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" - printCurrentLine - arg="${arg:${remainingLineLength}}" - # remove leading spaces - arg="${arg##[[:blank:]]}" - - set -- "${arg}" "$@" - else - # the arg can fit on next line - printCurrentLine - appendToCurrentLine "${arg}" "${argLength}" - fi - else - appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" - fi - done - if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then - printCurrentLine - fi - ) | sed -E -e 's/[[:blank:]]+$//' -} - -# @description check if an element is contained in an array -# -# @arg $1 needle:String -# @arg $@ array:String[] -# @exitcode 0 if found -# @exitcode 1 otherwise -# @example -# Array::contains "${libPath}" "${__BASH_FRAMEWORK_IMPORTED_FILES[@]}" -Array::contains() { - local element - for element in "${@:2}"; do - [[ "${element}" = "$1" ]] && return 0 - done - return 1 -} - -# @description check if command specified exists or return 1 -# with error and message if not -# -# @arg $1 commandName:String on which existence must be checked -# @arg $2 helpIfNotExists:String a help command to display if the command does not exist -# -# @exitcode 1 if the command specified does not exist -# @stderr diagnostic information + help if second argument is provided -Assert::commandExists() { - local commandName="$1" - local helpIfNotExists="$2" - - "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { - Log::displayError "${commandName} is not installed, please install it" - if [[ -n "${helpIfNotExists}" ]]; then - Log::displayInfo "${helpIfNotExists}" - fi - return 1 - } - return 0 -} - -bashToolsDefaultConfigTemplate="${bashToolsDefaultConfigTemplate:-$( - cat <<'EOF' -# shellcheck disable=SC2034 - -# Default settings -# you can override these settings by creating ${HOME}/.bash-tools/.env file - -### -### DISPLAY Level -### minimum level of the messages that will be displayed on screen -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} - -### -### DISPLAY duration -### 0: no duration is displayed on the messages -### 1: duration between previous message and current is displayed -### with the message -### -DISPLAY_DURATION=${DISPLAY_DURATION:0} - -### -### Log to file -### -### all log messages will be redirected to log file specified -### this same path will be used inside and outside of the container -### -BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} - -### -### LOG Level -### minimum level of the messages that will be logged into LOG_FILE -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} - -# absolute directory containing db import sql dumps -DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} - -# garbage collect all files for which modification is greater than eg: 30 days (+30) -# each time an existing file is used by dbImport/dbImportTable -# the file modification time is set to now -DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} - -# absolute directory containing dbScripts used by dbScriptAllDatabases -SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} - -# ----------------------------------------------------- -# AWS Parameters -# ----------------------------------------------------- -S3_BASE_URL=${S3_BASE_URL:-} - -# ----------------------------------------------------- -# Postman Parameters -# ----------------------------------------------------- -POSTMAN_API_KEY= -EOF -)}" - -# @description loads ~/.bash-tools/.env if available -# if not creates it from a default template -# else check if new options need to be added -BashTools::Conf::requireLoad() { - local envFile="${HOME}/.bash-tools/.env" - if [[ ! -f "${envFile}" ]]; then - mkdir -p "${HOME}/.bash-tools" - ( - echo "#!/usr/bin/env bash" - echo "${bashToolsDefaultConfigTemplate}" - ) >"${envFile}" - Log::displayInfo "Configuration file '${envFile}' created" - else - if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then - ( - echo '# -----------------------------------------------------' - echo '# Postman Parameters' - echo '# -----------------------------------------------------' - echo 'POSTMAN_API_KEY=' - ) >>"${envFile}" - fi - fi - # shellcheck source=/conf/.env - source "${envFile}" || { - Log::displayError "impossible to load '${envFile}'" - exit 1 - } +# @description create a temp file using default TMPDIR variable +# @env TMPDIR String (default value /tmp) +# @arg $1 templateName:String template name to use(optional) +Framework::createTempFile() { + mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" } -# @description ensure COMMAND_BIN_DIR env var is set -# and PATH correctly prepared -# @noargs -# @set COMMAND_BIN_DIR string the directory where to find this command -# @set PATH string add directory where to find this command binary -Compiler::Facade::requireCommandBinDir() { - COMMAND_BIN_DIR="${CURRENT_DIR}" - Env::pathPrepend "${COMMAND_BIN_DIR}" -} declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" @@ -687,14 +477,72 @@ Log::computeDuration() { fi } -# @description log message to file + +# @description Display message using debug color (gray) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayDebug() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then + Log::computeDuration + echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logDebug "$1" +} + + +# @description Display message using error color (red) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayError() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then + Log::computeDuration + echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logError "$1" +} + + +# @description Display message using info color (bg light blue/fg white) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayInfo() { + local type="${2:-INFO}" + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logInfo "$1" "${type}" +} + + +# @description Display message using warning color (yellow) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayWarning() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then + Log::computeDuration + echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logWarning "$1" +} + + +# @description Display message using error color (red) and exit immediately with error status 1 # @arg $1 message:String the message to display -Log::logInfo() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then - Log::logMessage "${2:-INFO}" "$1" - fi +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::fatal() { + Log::computeDuration + echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + Log::logFatal "$1" + exit 1 } + # @description log message to file # @arg $1 message:String the message to display Log::logDebug() { @@ -703,13 +551,6 @@ Log::logDebug() { fi } -# @description log message to file -# @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" - fi -} # @description log message to file # @arg $1 message:String the message to display @@ -719,20 +560,6 @@ Log::logError() { fi } -# @description check if tty (interactive mode) is active -# @noargs -# @exitcode 1 if tty not active -# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive -# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive -Assert::tty() { - if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then - return 1 - fi - if [[ "${INTERACTIVE:-0}" = "1" ]]; then - return 0 - fi - tty -s -} # @description log message to file # @arg $1 message:String the message to display @@ -740,6 +567,16 @@ Log::logFatal() { Log::logMessage "${2:-FATAL}" "$1" } + +# @description log message to file +# @arg $1 message:String the message to display +Log::logInfo() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then + Log::logMessage "${2:-INFO}" "$1" + fi +} + + # @description Internal: common log message # @example text # [date]|[levelMsg]|message @@ -753,9 +590,18 @@ Log::logFatal() { # @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty # @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages # @stderr diagnostics information is displayed -# @require Env::requireLoad -# @require Log::requireLoad Log::logMessage() { + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Log::requireLoad has not been loaded" + exit 1 + fi + local levelMsg="$1" local msg="$2" local date @@ -767,105 +613,312 @@ Log::logMessage() { fi } -# @description To be called before logging in the log file -# @arg $1 file:string log file name -# @arg $2 maxLogFilesCount:int maximum number of log files -Log::rotate() { - local file="$1" - local maxLogFilesCount="${2:-5}" - if [[ ! -f "${file}" ]]; then - Log::displayDebug "Log file ${file} doesn't exist yet" +# @description log message to file +# @arg $1 message:String the message to display +Log::logWarning() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then + Log::logMessage "${2:-WARNING}" "$1" + fi +} + + +# @description activate or not Log::display* and Log::log* functions +# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL +# environment variables loaded by Env::requireLoad +# try to create log file and rotate it if necessary +# @noargs +# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable +# @env BASH_FRAMEWORK_DISPLAY_LEVEL int +# @env BASH_FRAMEWORK_LOG_LEVEL int +# @env BASH_FRAMEWORK_LOG_FILE String +# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 +# @exitcode 0 always successful +# @stderr diagnostics information about log file is displayed +Log::requireLoad() { + export REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement UI::requireTheme has not been loaded" + exit 1 + fi + + if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + export BASH_FRAMEWORK_LOG_LEVEL + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then + if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 + fi + elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + # will always be created even if not in info level + Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" + if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then + Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + fi + fi +} + + +# @description To be called before logging in the log file +# @arg $1 file:string log file name +# @arg $2 maxLogFilesCount:int maximum number of log files +Log::rotate() { + local file="$1" + local maxLogFilesCount="${2:-5}" + + if [[ ! -f "${file}" ]]; then + Log::displayDebug "Log file ${file} doesn't exist yet" + return 0 + fi + local i + for ((i = maxLogFilesCount - 1; i > 0; i--)); do + Log::displayInfo "Log rotation ${file}.${i} to ${file}.$((i + 1))" + mv "${file}."{"${i}","$((i + 1))"} &>/dev/null || true + done + if cp "${file}" "${file}.1" &>/dev/null; then + echo >"${file}" # reset log file + Log::displayInfo "Log rotation ${file} to ${file}.1" + fi +} + + +# @description draw a line with the character passed in parameter repeated depending on terminal width +# @arg $1 character:String character to use as separator (default value #) +UI::drawLine() { + local character="${1:-#}" + local -i width=${COLUMNS:-0} + if ((width == 0)) && [[ -t 1 ]]; then + width=$(tput cols) + fi + if ((width == 0)); then + width=80 + fi + printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") + echo +} + + +# @description load color theme +# @noargs +# @env BASH_FRAMEWORK_THEME String theme to use +# @env LOAD_THEME int 0 to avoid loading theme +# @exitcode 0 always successful +UI::requireTheme() { + export REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + + if [[ "${LOAD_THEME:-1}" = "1" ]]; then + UI::theme "${BASH_FRAMEWORK_THEME-default}" + fi +} + + +# @description load colors theme constants +# @warning if tty not opened, noColor theme will be chosen +# @arg $1 theme:String the theme to use (default, noColor) +# @arg $@ args:String[] +# @set __ERROR_COLOR String indicate error status +# @set __INFO_COLOR String indicate info status +# @set __SUCCESS_COLOR String indicate success status +# @set __WARNING_COLOR String indicate warning status +# @set __SKIPPED_COLOR String indicate skipped status +# @set __DEBUG_COLOR String indicate debug status +# @set __HELP_COLOR String indicate help status +# @set __TEST_COLOR String not used +# @set __TEST_ERROR_COLOR String not used +# @set __HELP_TITLE_COLOR String used to display help title in help strings +# @set __HELP_OPTION_COLOR String used to display highlight options in help strings +# +# @set __RESET_COLOR String reset default color +# +# @set __HELP_EXAMPLE String to remove +# @set __HELP_TITLE String to remove +# @set __HELP_NORMAL String to remove +# shellcheck disable=SC2034 +UI::theme() { + local theme="${1-default}" + if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then + theme="noColor" + fi + case "${theme}" in + default | default-force) + theme="default" + ;; + noColor) ;; + *) + Log::fatal "invalid theme provided" + ;; + esac + if [[ "${theme}" = "default" ]]; then + BASH_FRAMEWORK_THEME="default" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='\e[31m' # Red + __INFO_COLOR='\e[44m' # white on lightBlue + __SUCCESS_COLOR='\e[32m' # Green + __WARNING_COLOR='\e[33m' # Yellow + __SKIPPED_COLOR='\e[33m' # Yellow + __DEBUG_COLOR='\e[37m' # Gray + __HELP_COLOR='\e[7;49;33m' # Black on Gold + __TEST_COLOR='\e[100m' # Light magenta + __TEST_ERROR_COLOR='\e[41m' # white on red + __HELP_TITLE_COLOR="\e[1;37m" # Bold + __HELP_OPTION_COLOR="\e[1;34m" # Blue + # Internal: reset color + __RESET_COLOR='\e[0m' # Reset Color + # shellcheck disable=SC2155,SC2034 + __HELP_EXAMPLE="$(echo -e "\e[2;97m")" + # shellcheck disable=SC2155,SC2034 + __HELP_TITLE="$(echo -e "\e[1;37m")" + # shellcheck disable=SC2155,SC2034 + __HELP_NORMAL="$(echo -e "\033[0m")" + else + BASH_FRAMEWORK_THEME="noColor" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='' + __INFO_COLOR='' + __SUCCESS_COLOR='' + __WARNING_COLOR='' + __SKIPPED_COLOR='' + __DEBUG_COLOR='' + __HELP_COLOR='' + __TEST_COLOR='' + __TEST_ERROR_COLOR='' + __HELP_TITLE_COLOR='' + __HELP_OPTION_COLOR='' + # Internal: reset color + __RESET_COLOR='' + __HELP_EXAMPLE='' + __HELP_TITLE='' + __HELP_NORMAL='' + fi +} + + +# @description Check that command version is greater than expected minimal version +# display warning if command version greater than expected minimal version +# display error if command version less than expected minimal version and exit 1 +# @arg $1 commandName:String command path +# @arg $2 argVersion:String command line parameters to launch to get command version +# @arg $3 minimalVersion:String expected minimal command version +# @arg $4 parseVersionCallback:Function +# @arg $5 help:String optional help message to display if command does not exist +# @exitcode 0 if command version greater or equal to expected minimal version +# @exitcode 1 if command version less than expected minimal version +# @exitcode 2 if command does not exist +Version::checkMinimal() { + local commandName="$1" + local argVersion="$2" + local minimalVersion="$3" + local parseVersionCallback=${4:-Version::parse} + local help="${5:-}" + + Assert::commandExists "${commandName}" "${help}" || return 2 + + # shellcheck disable=SC2034 + local status=0 + # shellcheck disable=SC2034 + local -a pipeStatus=() + local version + version="$("${commandName}" "${argVersion}" 2>&1 | ${parseVersionCallback} || Bash::handlePipelineFailure status pipeStatus)" + + Log::displayDebug "check ${commandName} version ${version} against minimal ${minimalVersion}" + + Version::compare "${version}" "${minimalVersion}" || { + local result=$? + if [[ "${result}" = "1" ]]; then + Log::displayInfo "${commandName} version is ${version} greater than ${minimalVersion}" + elif [[ "${result}" = "2" ]]; then + Log::displayError "${commandName} minimal version is ${minimalVersion}, your version is ${version}" + return 1 + fi return 0 - fi - local i - for ((i = maxLogFilesCount - 1; i > 0; i--)); do - Log::displayInfo "Log rotation ${file}.${i} to ${file}.$((i + 1))" - mv "${file}."{"${i}","$((i + 1))"} &>/dev/null || true - done - if cp "${file}" "${file}.1" &>/dev/null; then - echo >"${file}" # reset log file - Log::displayInfo "Log rotation ${file} to ${file}.1" - fi + } + } -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" + +# @description compare 2 version numbers +# @arg $1 version1:String version 1 +# @arg $2 version2:String version 2 +# @exitcode 0 if equal +# @exitcode 1 if version1 > version2 +# @exitcode 2 else +Version::compare() { + if [[ "$1" = "$2" ]]; then + return 0 + fi + local IFS=. + # shellcheck disable=2206 + local i ver1=($1) ver2=($2) + # fill empty fields in ver1 with zeros + for ((i = ${#ver1[@]}; i < ${#ver2[@]}; i++)); do + ver1[i]=0 + done + for ((i = 0; i < ${#ver1[@]}; i++)); do + if [[ -z "${ver2[i]+unset}" ]] || [[ -z ${ver2[i]} ]]; then + # fill empty fields in ver2 with zeros + ver2[i]=0 + fi + if ((10#${ver1[i]} > 10#${ver2[i]})); then + return 1 + fi + if ((10#${ver1[i]} < 10#${ver2[i]})); then + return 2 fi done + return 0 } -# @description load color theme -# @noargs -# @env BASH_FRAMEWORK_THEME String theme to use -# @env LOAD_THEME int 0 to avoid loading theme -# @exitcode 0 always successful -UI::requireTheme() { - if [[ "${LOAD_THEME:-1}" = "1" ]]; then - UI::theme "${BASH_FRAMEWORK_THEME-default}" - fi -} +# @description filter to keep only version number from a string +# @arg $@ files:String[] the files to filter +# @exitcode * if one of the filter command fails +# @stdin you can use stdin as alternative to files argument +# @stdout the filtered content +# shellcheck disable=SC2120 +Version::parse() { + # match anything, print(p), exit on first match(Q) + sed -En \ + -e 's/\x1b\[[0-9;]*[mGKHF]//g' \ + -e 's/[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/' \ + -e '//{p;Q}' \ + "$@" +} # FUNCTIONS -facade_main_waitForItsh() { -BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" -if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then - FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" -else - # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR - FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" -fi -FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" -FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" - -# @require BashTools::Conf::requireLoad -if [[ -f "${HOME}/.bash-tools/.env" ]]; then - export BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") -fi -# REQUIRES -Env::requireLoad -UI::requireTheme -Log::requireLoad -BashTools::Conf::requireLoad -Compiler::Facade::requireCommandBinDir - -# @require Compiler::Facade::requireCommandBinDir -# shellcheck disable=SC2034 - -# option values -declare -a commandArgs=() -declare optionHostOrIp="" -declare optionPort="" -declare optionStrict="0" -declare optionTimeout="15" -declare optionAlgo="" -declare -a availableAlgos=( - "timeoutV1WithNc" - "timeoutV2WithNc" - "whileLoopWithNc" - "timeoutV1WithTcp" - "timeoutV2WithTcp" - "whileLoopWithTcp" -) -# other values -declare copyrightBeginYear="2020" declare -a BASH_FRAMEWORK_ARGV_FILTERED=() +beforeParseCallback() { + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + copyrightCallback() { - if [[ -z "${copyrightBeginYear}" ]]; then - copyrightBeginYear="$(date +%Y)" - fi - echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" + # + echo "Copyright (c) 2020-now François Chastanet" } # shellcheck disable=SC2317 # if function is overridden @@ -897,13 +950,14 @@ updateArgListQuietCallback() { :; } # shellcheck disable=SC2317 # if function is overridden optionHelpCallback() { - waitForItCommand help + Log::displayError "optionHelpCallback needs to be overridden" exit 0 } # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { - echo "${SCRIPT_NAME} version 2.0" + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version 3.0" exit 0 } @@ -921,21 +975,22 @@ optionEnvFileCallback() { optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_INFO} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionDebugVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_DEBUG} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionTraceVerboseCallback() { + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vvv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_TRACE} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } getLevel() { @@ -959,6 +1014,7 @@ getLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -980,6 +1036,7 @@ getVerboseLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -990,7 +1047,7 @@ optionDisplayLevelCallback() { logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -999,19 +1056,20 @@ optionLogLevelCallback() { local logLevel verboseLevel logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionLogFileCallback() { local logFile="$2" - echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionQuietCallback() { - echo "BASH_FRAMEWORK_QUIET_MODE=1" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_QUIET_MODE=1" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1042,6 +1100,7 @@ optionBashFrameworkConfigCallback() { defaultFrameworkConfig="$( cat <<'EOF' + # copied from src/_includes/.framework-config.default # shellcheck disable=SC2034 @@ -1059,7 +1118,7 @@ NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-conf # describe the files that are allowed to not have an associated bats file BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" # describe the files that are allowed to not have a function matching the filename -FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|\.tpl$|/testsData/|^manualTests/|\.bats$}" +FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|/testsData/|^manualTests/|\.bats$}" # Source directories if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then FRAMEWORK_SRC_DIRS=( @@ -1075,6 +1134,7 @@ BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" + EOF )" @@ -1083,7 +1143,7 @@ overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" commandOptionParseFinished() { # load default template framework config defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" - echo "${defaultFrameworkConfig}" > "${defaultEnvFile}" + echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") if [[ -f "${envFile}" ]]; then files+=("${envFile}") @@ -1101,741 +1161,1027 @@ commandOptionParseFinished() { fi } + + +declare -a availableAlgos=( + "timeoutV1WithNc" + "timeoutV2WithNc" + "whileLoopWithNc" + "timeoutV1WithTcp" + "timeoutV2WithTcp" + "whileLoopWithTcp" +) + +longDescriptionFunction() { + echo -e " ${__HELP_TITLE}EXIT STATUS CODES:${__HELP_NORMAL}" + echo -e " ${__HELP_OPTION_COLOR}0${__HELP_NORMAL}: the host/port is available" + echo -e " ${__HELP_OPTION_COLOR}1${__HELP_NORMAL}: indicates host/port is not available or argument error" + echo -e " ${__HELP_OPTION_COLOR}2${__HELP_NORMAL}: timeout reached" + echo + echo -e " ${__HELP_TITLE}AVAILABLE ALGORITHMS:${__HELP_NORMAL}" + echo -e " ${__HELP_OPTION_COLOR}timeoutV1WithNc${__HELP_NORMAL}: previous version of timeout command with --timeout option, base command nc" + echo -e " ${__HELP_OPTION_COLOR}timeoutV2WithNc${__HELP_NORMAL}: newer version of timeout command using timeout as argument, base command nc" + echo -e " ${__HELP_OPTION_COLOR}whileLoopWithNc${__HELP_NORMAL}: timeout command simulated using while loop, base command nc" + echo -e " ${__HELP_OPTION_COLOR}timeoutV1WithTcp${__HELP_NORMAL}: previous version of timeout command with --timeout option" + echo -e " ${__HELP_OPTION_COLOR}timeoutV2WithTcp${__HELP_NORMAL}: newer version of timeout command using timeout as argument" + echo -e " ${__HELP_OPTION_COLOR}whileLoopWithTcp${__HELP_NORMAL}: timeout command simulated using while loop, base command tcp" +} + +algorithmHelpFunction() { + echo " Algorithm to use Check algorithms list below." + echo " Default: automatic selection based on commands availability and timeout option value." +} + +optionHelpCallback() { + waitForItCommandHelp + exit 0 +} + +# shellcheck disable=SC2317 # if function is overridden +unknownOption() { + commandArgs+=("$1") +} + +portArgCallback() { + # shellcheck disable=SC2154 + if [[ ! "${portArg}" =~ ^[0-9]+$ ]] || ((portArg == 0)); then + Log::fatal "${SCRIPT_NAME} - invalid port argument - must be greater than to 0" + fi +} + +optionAlgoCallback() { + # shellcheck disable=SC2154 + if ! Array::contains "${optionAlgo}" "${availableAlgos[@]}"; then + Log::fatal "${SCRIPT_NAME} - invalid algorithm option '${optionAlgo}'" + fi +} + +commandCallback() { + # shellcheck disable=SC2154 + if [[ "${hostOrIpArg}" = "" || "${portArg}" = "" ]]; then + Log::fatal "${SCRIPT_NAME} - you need to provide a host and port to test." + fi +} + + + optionTimeoutCallback() { + # shellcheck disable=SC2154 if [[ ! "${optionTimeout}" =~ ^[0-9]+$ ]]; then Log::fatal "${SCRIPT_NAME} - invalid timeout option - must be greater or equal to 0" fi } -waitForItCommand() { - local options_parse_cmd="$1" - shift || true - if [[ "${options_parse_cmd}" = "parse" ]]; then - optionTimeout="15" - local -i options_parse_optionParsedCountOptionTimeout - ((options_parse_optionParsedCountOptionTimeout = 0)) || true - local -i options_parse_optionParsedCountOptionHostOrIp - ((options_parse_optionParsedCountOptionHostOrIp = 0)) || true - local -i options_parse_optionParsedCountOptionPort - ((options_parse_optionParsedCountOptionPort = 0)) || true - local -i options_parse_optionParsedCountOptionAlgo - ((options_parse_optionParsedCountOptionAlgo = 0)) || true - optionStrict="0" - local -i options_parse_optionParsedCountOptionStrict - ((options_parse_optionParsedCountOptionStrict = 0)) || true - local -i options_parse_optionParsedCountOptionBashFrameworkConfig - ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true - optionConfig="0" - local -i options_parse_optionParsedCountOptionConfig - ((options_parse_optionParsedCountOptionConfig = 0)) || true - optionInfoVerbose="0" - local -i options_parse_optionParsedCountOptionInfoVerbose - ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true - optionDebugVerbose="0" - local -i options_parse_optionParsedCountOptionDebugVerbose - ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true - optionTraceVerbose="0" - local -i options_parse_optionParsedCountOptionTraceVerbose - ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionNoColor="0" - local -i options_parse_optionParsedCountOptionNoColor - ((options_parse_optionParsedCountOptionNoColor = 0)) || true - optionTheme="default" - local -i options_parse_optionParsedCountOptionTheme - ((options_parse_optionParsedCountOptionTheme = 0)) || true - optionHelp="0" - local -i options_parse_optionParsedCountOptionHelp - ((options_parse_optionParsedCountOptionHelp = 0)) || true - optionVersion="0" - local -i options_parse_optionParsedCountOptionVersion - ((options_parse_optionParsedCountOptionVersion = 0)) || true - optionQuiet="0" - local -i options_parse_optionParsedCountOptionQuiet - ((options_parse_optionParsedCountOptionQuiet = 0)) || true - local -i options_parse_optionParsedCountOptionLogLevel - ((options_parse_optionParsedCountOptionLogLevel = 0)) || true - local -i options_parse_optionParsedCountOptionLogFile - ((options_parse_optionParsedCountOptionLogFile = 0)) || true - local -i options_parse_optionParsedCountOptionDisplayLevel - ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true - local -i options_parse_argParsedCountCommandArgs - ((options_parse_argParsedCountCommandArgs = 0)) || true - # shellcheck disable=SC2034 - local -i options_parse_parsedArgIndex=0 - while (($# > 0)); do - local options_parse_arg="$1" - local argOptDefaultBehavior=0 - case "${options_parse_arg}" in - # Option 1/19 - # Option optionTimeout --timeout|-t variableType String min 0 max 1 authorizedValues '' regexp '' - --timeout | -t) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionTimeout >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTimeout)) - # shellcheck disable=SC2034 - optionTimeout="$1" - optionTimeoutCallback "${options_parse_arg}" "${optionTimeout}" - ;; - # Option 2/19 - # Option optionHostOrIp --host|-i variableType String min 1 max 1 authorizedValues '' regexp '' - --host | -i) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionHostOrIp >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionHostOrIp)) - # shellcheck disable=SC2034 - optionHostOrIp="$1" - ;; - # Option 3/19 - # Option optionPort --port|-p variableType String min 1 max 1 authorizedValues '' regexp '' - --port | -p) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionPort >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionPort)) - # shellcheck disable=SC2034 - optionPort="$1" - optionPortCallback "${options_parse_arg}" "${optionPort}" - ;; - # Option 4/19 - # Option optionAlgo --algorithm|--algo variableType String min 0 max 1 authorizedValues '' regexp '' - --algorithm | --algo) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionAlgo >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionAlgo)) - # shellcheck disable=SC2034 - optionAlgo="$1" - optionAlgoCallback "${options_parse_arg}" "${optionAlgo}" - ;; - # Option 5/19 - # Option optionStrict --exec-command-on-success-only|--strict|-s variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --exec-command-on-success-only | --strict | -s) - # shellcheck disable=SC2034 - optionStrict="1" - if ((options_parse_optionParsedCountOptionStrict >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionStrict)) - ;; - # Option 6/19 - # Option optionBashFrameworkConfig --bash-framework-config variableType String min 0 max 1 authorizedValues '' regexp '' - --bash-framework-config) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) - # shellcheck disable=SC2034 - optionBashFrameworkConfig="$1" - optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" - ;; - # Option 7/19 - # Option optionConfig --config variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --config) - # shellcheck disable=SC2034 - optionConfig="1" - if ((options_parse_optionParsedCountOptionConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionConfig)) - ;; - # Option 8/19 - # Option optionInfoVerbose --verbose|-v variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --verbose | -v) - # shellcheck disable=SC2034 - optionInfoVerbose="1" - if ((options_parse_optionParsedCountOptionInfoVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionInfoVerbose)) - optionInfoVerboseCallback "${options_parse_arg}" - updateArgListInfoVerboseCallback "${options_parse_arg}" - ;; - # Option 9/19 - # Option optionDebugVerbose -vv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vv) - # shellcheck disable=SC2034 - optionDebugVerbose="1" - if ((options_parse_optionParsedCountOptionDebugVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDebugVerbose)) - optionDebugVerboseCallback "${options_parse_arg}" - updateArgListDebugVerboseCallback "${options_parse_arg}" - ;; - # Option 10/19 - # Option optionTraceVerbose -vvv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vvv) - # shellcheck disable=SC2034 - optionTraceVerbose="1" - if ((options_parse_optionParsedCountOptionTraceVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTraceVerbose)) - optionTraceVerboseCallback "${options_parse_arg}" - updateArgListTraceVerboseCallback "${options_parse_arg}" - ;; - # Option 11/19 - # Option optionEnvFiles --env-file variableType StringArray min 0 max -1 authorizedValues '' regexp '' - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - ;; - # Option 12/19 - # Option optionNoColor --no-color variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --no-color) - # shellcheck disable=SC2034 - optionNoColor="1" - if ((options_parse_optionParsedCountOptionNoColor >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionNoColor)) - optionNoColorCallback "${options_parse_arg}" - updateArgListNoColorCallback "${options_parse_arg}" - ;; - # Option 13/19 - # Option optionTheme --theme variableType String min 0 max 1 authorizedValues 'default|default-force|noColor' regexp '' - --theme) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default|default-force|noColor)" - return 1 - fi - if ((options_parse_optionParsedCountOptionTheme >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTheme)) - # shellcheck disable=SC2034 - optionTheme="$1" - optionThemeCallback "${options_parse_arg}" "${optionTheme}" - updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" - ;; - # Option 14/19 - # Option optionHelp --help|-h variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --help | -h) - # shellcheck disable=SC2034 - optionHelp="1" - if ((options_parse_optionParsedCountOptionHelp >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionHelp)) - optionHelpCallback "${options_parse_arg}" - ;; - # Option 15/19 - # Option optionVersion --version variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --version) - # shellcheck disable=SC2034 - optionVersion="1" - if ((options_parse_optionParsedCountOptionVersion >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + +optionVersionCallback() { + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version 3.0" + Db::checkRequirements + exit 0 +} + + +# ------------------------------------------ +# Command waitForItCommand +# ------------------------------------------ + +# options variables initialization +declare optionTimeout="15" +declare optionHelp="0" +declare optionConfig="0" +declare optionBashFrameworkConfig="" +declare optionInfoVerbose="0" +declare optionDebugVerbose="0" +declare optionTraceVerbose="0" +declare -a optionEnvFiles=() +declare optionLogLevel="" +declare optionLogFile="" +declare optionDisplayLevel="" +declare optionNoColor="0" +declare optionTheme="default" +declare optionVersion="0" +declare optionQuiet="0" +declare optionAlgo="" +declare optionStrict="0" +declare optionLegacy="0" +# arguments variables initialization +declare hostOrIpArg="" +declare portArg="" +declare -a commandArgs=() +# @description parse command options and arguments for waitForItCommand +waitForItCommandParse() { + Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" + Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" + optionTimeout="15" + local -i options_parse_optionParsedCountOptionTimeout + ((options_parse_optionParsedCountOptionTimeout = 0)) || true + optionHelp="0" + local -i options_parse_optionParsedCountOptionHelp + ((options_parse_optionParsedCountOptionHelp = 0)) || true + optionConfig="0" + local -i options_parse_optionParsedCountOptionConfig + ((options_parse_optionParsedCountOptionConfig = 0)) || true + optionBashFrameworkConfig="" + local -i options_parse_optionParsedCountOptionBashFrameworkConfig + ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true + optionInfoVerbose="0" + local -i options_parse_optionParsedCountOptionInfoVerbose + ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true + optionDebugVerbose="0" + local -i options_parse_optionParsedCountOptionDebugVerbose + ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true + optionTraceVerbose="0" + local -i options_parse_optionParsedCountOptionTraceVerbose + ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true + + optionLogLevel="" + local -i options_parse_optionParsedCountOptionLogLevel + ((options_parse_optionParsedCountOptionLogLevel = 0)) || true + optionLogFile="" + local -i options_parse_optionParsedCountOptionLogFile + ((options_parse_optionParsedCountOptionLogFile = 0)) || true + optionDisplayLevel="" + local -i options_parse_optionParsedCountOptionDisplayLevel + ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true + optionNoColor="0" + local -i options_parse_optionParsedCountOptionNoColor + ((options_parse_optionParsedCountOptionNoColor = 0)) || true + optionTheme="default" + local -i options_parse_optionParsedCountOptionTheme + ((options_parse_optionParsedCountOptionTheme = 0)) || true + optionVersion="0" + local -i options_parse_optionParsedCountOptionVersion + ((options_parse_optionParsedCountOptionVersion = 0)) || true + optionQuiet="0" + local -i options_parse_optionParsedCountOptionQuiet + ((options_parse_optionParsedCountOptionQuiet = 0)) || true + optionAlgo="" + local -i options_parse_optionParsedCountOptionAlgo + ((options_parse_optionParsedCountOptionAlgo = 0)) || true + optionStrict="0" + local -i options_parse_optionParsedCountOptionStrict + ((options_parse_optionParsedCountOptionStrict = 0)) || true + optionLegacy="0" + local -i options_parse_optionParsedCountOptionLegacy + ((options_parse_optionParsedCountOptionLegacy = 0)) || true + + hostOrIpArg="" + local -i options_parse_argParsedCountHostOrIpArg + ((options_parse_argParsedCountHostOrIpArg = 0)) || true + + portArg="" + local -i options_parse_argParsedCountPortArg + ((options_parse_argParsedCountPortArg = 0)) || true + + commandArgs=() + + + # shellcheck disable=SC2034 + local -i options_parse_parsedArgIndex=0 + while (($# > 0)); do + local options_parse_arg="$1" + local argOptDefaultBehavior=0 + case "${options_parse_arg}" in + # Option 1/18 + # optionTimeout alts --timeout|-t + # type: String min 0 max 1 + --timeout | -t) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionTimeout >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTimeout)) + # shellcheck disable=SC2034 + optionTimeout="$1" + optionTimeoutCallback "${options_parse_arg}" "${optionTimeout}" + + ;; + + # Option 2/18 + # optionHelp alts --help|-h + # type: Boolean min 0 max 1 + --help | -h) + # shellcheck disable=SC2034 + optionHelp="1" + + if ((options_parse_optionParsedCountOptionHelp >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionHelp)) + optionHelpCallback "${options_parse_arg}" "${optionHelp}" + + ;; + + # Option 3/18 + # optionConfig alts --config + # type: Boolean min 0 max 1 + --config) + # shellcheck disable=SC2034 + optionConfig="1" + + if ((options_parse_optionParsedCountOptionConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionConfig)) + ;; + + # Option 4/18 + # optionBashFrameworkConfig alts --bash-framework-config + # type: String min 0 max 1 + --bash-framework-config) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) + # shellcheck disable=SC2034 + optionBashFrameworkConfig="$1" + optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" + + ;; + + # Option 5/18 + # optionInfoVerbose alts --verbose|-v + # type: Boolean min 0 max 1 + --verbose | -v) + # shellcheck disable=SC2034 + optionInfoVerbose="1" + + if ((options_parse_optionParsedCountOptionInfoVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionInfoVerbose)) + optionInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + updateArgListInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + ;; + + # Option 6/18 + # optionDebugVerbose alts -vv + # type: Boolean min 0 max 1 + -vv) + # shellcheck disable=SC2034 + optionDebugVerbose="1" + + if ((options_parse_optionParsedCountOptionDebugVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDebugVerbose)) + optionDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + updateArgListDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + ;; + + # Option 7/18 + # optionTraceVerbose alts -vvv + # type: Boolean min 0 max 1 + -vvv) + # shellcheck disable=SC2034 + optionTraceVerbose="1" + + if ((options_parse_optionParsedCountOptionTraceVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTraceVerbose)) + optionTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + updateArgListTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + ;; + + # Option 8/18 + # optionEnvFiles alts --env-file + # type: StringArray min 0 max -1 + --env-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + ((++options_parse_optionParsedCountOptionEnvFiles)) + optionEnvFiles+=("$1") + optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + ;; + + # Option 9/18 + # optionLogLevel alts --log-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --log-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogLevel)) + # shellcheck disable=SC2034 + optionLogLevel="$1" + optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + ;; + + # Option 10/18 + # optionLogFile alts --log-file + # type: String min 0 max 1 + --log-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogFile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogFile)) + # shellcheck disable=SC2034 + optionLogFile="$1" + optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + ;; + + # Option 11/18 + # optionDisplayLevel alts --display-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --display-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionDisplayLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDisplayLevel)) + # shellcheck disable=SC2034 + optionDisplayLevel="$1" + optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + ;; + + # Option 12/18 + # optionNoColor alts --no-color + # type: Boolean min 0 max 1 + --no-color) + # shellcheck disable=SC2034 + optionNoColor="1" + + if ((options_parse_optionParsedCountOptionNoColor >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionNoColor)) + optionNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + updateArgListNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + ;; + + # Option 13/18 + # optionTheme alts --theme + # type: String min 0 max 1 + # authorizedValues: default|default-force|noColor + --theme) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ default|default-force|noColor ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionTheme >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTheme)) + # shellcheck disable=SC2034 + optionTheme="$1" + optionThemeCallback "${options_parse_arg}" "${optionTheme}" + + updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" + + ;; + + # Option 14/18 + # optionVersion alts --version + # type: Boolean min 0 max 1 + --version) + # shellcheck disable=SC2034 + optionVersion="1" + + if ((options_parse_optionParsedCountOptionVersion >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionVersion)) + optionVersionCallback "${options_parse_arg}" "${optionVersion}" + + ;; + + # Option 15/18 + # optionQuiet alts --quiet|-q + # type: Boolean min 0 max 1 + --quiet | -q) + # shellcheck disable=SC2034 + optionQuiet="1" + + if ((options_parse_optionParsedCountOptionQuiet >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionQuiet)) + optionQuietCallback "${options_parse_arg}" "${optionQuiet}" + + updateArgListQuietCallback "${options_parse_arg}" "${optionQuiet}" + + ;; + + # Option 16/18 + # optionAlgo alts --algorithm|--algo + # type: String min 0 max 1 + --algorithm | --algo) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionAlgo >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionAlgo)) + # shellcheck disable=SC2034 + optionAlgo="$1" + optionAlgoCallback "${options_parse_arg}" "${optionAlgo}" + + ;; + + # Option 17/18 + # optionStrict alts --exec-command-on-success-only|--strict|-s + # type: Boolean min 0 max 1 + --exec-command-on-success-only | --strict | -s) + # shellcheck disable=SC2034 + optionStrict="1" + + if ((options_parse_optionParsedCountOptionStrict >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionStrict)) + ;; + + # Option 18/18 + # optionLegacy alts --user-nc + # type: Boolean min 0 max 1 + --user-nc) + # shellcheck disable=SC2034 + optionLegacy="1" + + if ((options_parse_optionParsedCountOptionLegacy >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLegacy)) + ;; + + -*) + unknownOption "${options_parse_arg}" || argOptDefaultBehavior=$? + ;; + *) + ((minParsedArgIndex0 = 0)) || true + ((maxParsedArgIndex0 = 0)) || true + ((minParsedArgIndex1 = minParsedArgIndex0 + 1)) || true + ((maxParsedArgIndex1 = maxParsedArgIndex0 + 1)) || true + ((minParsedArgIndex2 = minParsedArgIndex1 + 1)) || true + ((maxParsedArgIndex2 = maxParsedArgIndex1 + 1)) || true + ((minParsedArgIndex3 = minParsedArgIndex2 + 0)) || true + ((maxParsedArgIndex3 = maxParsedArgIndex2)) || true + ((incrementArg = 1 )) + if ((0)); then + # Technical if - never reached + : + + # Argument 1/3 - hostOrIpArg + # Argument hostOrIpArg min 1 max 1 + # Argument hostOrIpArg authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && + options_parse_parsedArgIndex < maxParsedArgIndex1 )); then + if ((options_parse_argParsedCountHostOrIpArg >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument hostOrIp - Maximum number of argument occurrences reached(1)" return 1 fi - ((++options_parse_optionParsedCountOptionVersion)) - optionVersionCallback "${options_parse_arg}" - ;; - # Option 16/19 - # Option optionQuiet --quiet|-q variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --quiet | -q) + ((++options_parse_argParsedCountHostOrIpArg)) # shellcheck disable=SC2034 - optionQuiet="1" - if ((options_parse_optionParsedCountOptionQuiet >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionQuiet)) - optionQuietCallback "${options_parse_arg}" - updateArgListQuietCallback "${options_parse_arg}" - ;; - # Option 17/19 - # Option optionLogLevel --log-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --log-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + hostOrIpArg="${options_parse_arg}" + + + # Argument 2/3 - portArg + # Argument portArg min 1 max 1 + # Argument portArg authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex1 && + options_parse_parsedArgIndex < maxParsedArgIndex2 )); then + if ((options_parse_argParsedCountPortArg >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument port - Maximum number of argument occurrences reached(1)" return 1 fi - ((++options_parse_optionParsedCountOptionLogLevel)) + ((++options_parse_argParsedCountPortArg)) # shellcheck disable=SC2034 - optionLogLevel="$1" - optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - ;; - # Option 18/19 - # Option optionLogFile --log-file variableType String min 0 max 1 authorizedValues '' regexp '' - --log-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogFile >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogFile)) + portArg="${options_parse_arg}" + portArgCallback "${portArg}" -- "${@:2}" + + + # Argument 3/3 - commandArgs + # Argument commandArgs min 0 max -1 + # Argument commandArgs authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex2 )); then + ((++options_parse_argParsedCountCommandArgs)) # shellcheck disable=SC2034 - optionLogFile="$1" - optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" - updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" - ;; - # Option 19/19 - # Option optionDisplayLevel --display-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --display-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionDisplayLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDisplayLevel)) # shellcheck disable=SC2034 - optionDisplayLevel="$1" - optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - ;; - -*) - unknownOption "${options_parse_arg}" - ;; - *) - if ((0)); then - # Technical if - never reached - : - # Argument 1/1 - # Argument commandArgs min 0 max -1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 0)); then - ((++options_parse_argParsedCountCommandArgs)) - # shellcheck disable=SC2034 - commandArgs+=("${options_parse_arg}") - else - unknownOption "${options_parse_arg}" - fi + commandArgs+=("${options_parse_arg}") + + + # else too much args + else + + + # no arg configured, call unknownArgumentCallback + + # shellcheck disable=SC2317 + unknownOption "${options_parse_arg}" + + fi + if ((incrementArg == 1)); then ((++options_parse_parsedArgIndex)) - ;; - esac - shift || true - done - if ((options_parse_optionParsedCountOptionHostOrIp < 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option '--host' should be provided at least 1 time(s)" - return 1 - fi - if ((options_parse_optionParsedCountOptionPort < 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option '--port' should be provided at least 1 time(s)" - return 1 - fi - commandOptionParseFinished - commandCallback - Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" - Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" - elif [[ "${options_parse_cmd}" = "help" ]]; then - Array::wrap2 " " 80 0 "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" "wait for host:port to be available" - echo + fi + ;; + esac + shift || true + done - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "${SCRIPT_NAME}" "[OPTIONS]" "[ARGUMENTS]")" - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ - "${SCRIPT_NAME}" \ - "[--timeout|-t ]" "--host|-i " "--port|-p " "[--algorithm|--algo ]" "[--exec-command-on-success-only|--strict|-s]" "[--bash-framework-config ]" "[--config]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--no-color]" "[--theme ]" "[--help|-h]" "[--version]" "[--quiet|-q]" "[--log-level ]" "[--log-file ]" "[--display-level ]")" - echo - echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - echo -e " [${__HELP_OPTION_COLOR}commandArgs${__HELP_NORMAL} {list} (optional)]" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Execute\ command\ with\ args\ after\ the\ test\ finishes\ or\ exit\ with\ status\ code\ if\ no\ command\ provided.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--timeout${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-t ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Timeout\ in\ seconds\,\ zero\ for\ no\ timeout.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: 15' - echo -e " ${__HELP_OPTION_COLOR}--host${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-i ${__HELP_NORMAL} {single} (mandatory)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Host\ or\ IP\ under\ test.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--port${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-p ${__HELP_NORMAL} {single} (mandatory)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(TCP\ port\ under\ test.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--algorithm${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}--algo ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=($'Algorithm to use Check algorithms list below. \n (default: automatic selection based on commands availability and timeout option value).') - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--exec-command-on-success-only${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}--strict${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-s${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Only\ execute\ sub-command\ if\ the\ test\ succeeds.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(use\ alternate\ bash\ framework\ configuration.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ configuration) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(info\ level\ verbose\ mode\ \(alias\ of\ --display-level\ INFO\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(debug\ level\ verbose\ mode\ \(alias\ of\ --display-level\ DEBUG\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(trace\ level\ verbose\ mode\ \(alias\ of\ --display-level\ TRACE\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Load\ the\ specified\ env\ file\ \(deprecated\,\ please\ use\ --bash-framework-config\ option\ instead\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Produce\ monochrome\ output.\ alias\ of\ --theme\ noColor.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' - echo ' Possible values: default|default-force|noColor' - echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ this\ command\ help) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Print\ version\ information\ and\ quit) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(quiet\ mode\,\ doesn\'t\ display\ any\ output) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ file) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(set\ display\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e """ -${__HELP_TITLE}EXIT STATUS CODES:${__HELP_NORMAL} -${__HELP_OPTION_COLOR}0${__HELP_NORMAL}: the host/port is available -${__HELP_OPTION_COLOR}1${__HELP_NORMAL}: indicates host/port is not available or argument error -${__HELP_OPTION_COLOR}2${__HELP_NORMAL}: timeout reached - -${__HELP_TITLE}AVAILABLE ALGORITHMS:${__HELP_NORMAL} -${__HELP_OPTION_COLOR}timeoutV1WithNc${__HELP_NORMAL}: previous version of timeout command with --timeout option, base command nc -${__HELP_OPTION_COLOR}timeoutV2WithNc${__HELP_NORMAL}: newer version of timeout command using timeout as argument, base command nc -${__HELP_OPTION_COLOR}whileLoopWithNc${__HELP_NORMAL}: timeout command simulated using while loop, base command nc -${__HELP_OPTION_COLOR}timeoutV1WithTcp${__HELP_NORMAL}: previous version of timeout command with --timeout option -${__HELP_OPTION_COLOR}timeoutV2WithTcp${__HELP_NORMAL}: newer version of timeout command using timeout as argument -${__HELP_OPTION_COLOR}whileLoopWithTcp${__HELP_NORMAL}: timeout command simulated using while loop, base command tcp""" - echo - echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo '2.0' - echo - echo -e "${__HELP_TITLE_COLOR}AUTHOR:${__RESET_COLOR}" - echo '[François Chastanet](https://github.com/fchastanet)' - echo - echo -e "${__HELP_TITLE_COLOR}SOURCE FILE:${__RESET_COLOR}" - echo 'https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Utils/waitForIt.sh' - echo - echo -e "${__HELP_TITLE_COLOR}LICENSE:${__RESET_COLOR}" - echo 'MIT License' - echo - Array::wrap2 ' ' 76 4 "$(copyrightCallback)" - else - Log::displayError "Command ${SCRIPT_NAME} - Option command invalid: '${options_parse_cmd}'" + + + + + + + + + + + + + + + + + + if ((options_parse_argParsedCountHostOrIpArg < 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument 'hostOrIp' should be provided at least 1 time(s)" return 1 fi -} -# shellcheck disable=SC2317 # if function is overridden -unknownOption() { - commandArgs+=("$1") -} + if ((options_parse_argParsedCountPortArg < 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument 'port' should be provided at least 1 time(s)" + return 1 + fi || return $? -optionPortCallback() { - if [[ ! "${optionPort}" =~ ^[0-9]+$ ]] || (( optionPort == 0 )); then - Log::fatal "${SCRIPT_NAME} - invalid port option - must be greater than to 0" - fi -} -optionAlgoCallback() { - if ! Array::contains "${optionAlgo}" "${availableAlgos[@]}"; then - Log::fatal "${SCRIPT_NAME} - invalid algorithm '${optionAlgo}'" - fi -} + commandCallback + commandOptionParseFinished -commandCallback() { - if [[ "${optionHostOrIp}" = "" || "${optionPort}" = "" ]]; then - Log::fatal "${SCRIPT_NAME} - you need to provide a host and port to test." - fi } -waitForItCommand parse "${BASH_FRAMEWORK_ARGV[@]}" +# @description display command options and arguments help for waitForItCommand +waitForItCommandHelp() { + echo -e "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" + Array::wrap2 ' ' 76 4 " " "Wait for host:port to be available." -# Use this script to test if a given TCP host/port are available -# https://github.com/vishnubob/wait-for-it + echo + echo -run() { - # shellcheck disable=SC2317 - usingTcp() { - # couldn't find another way to mock this part - if [[ -n "${WAIT_FOR_IT_MOCKED_TCP:-}" ]]; then - "${WAIT_FOR_IT_MOCKED_TCP}" "/dev/tcp/${optionHostOrIp}/${optionPort}" 2>&1 - else - echo >"/dev/tcp/${optionHostOrIp}/${optionPort}" 2>&1 - fi - } + # ------------------------------------------ + # usage section + # ------------------------------------------ + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "waitForIt [OPTIONS] [ARGUMENTS]" + echo + # ------------------------------------------ + # usage/options section + # ------------------------------------------ + optionsAltList=("[--timeout|-t ]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--algorithm|--algo ]" "[--exec-command-on-success-only|--strict|-s]" "[--user-nc]" + ) + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ + "waitForIt" "${optionsAltList[@]}" + echo - # shellcheck disable=SC2317 - usingNc() { - nc -z "${optionHostOrIp}" "${optionPort}" -w 1 2>&1 - } + # ------------------------------------------ + # usage/arguments section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - whileLoop() { - local commandToUse="$1" - local reportTimeout="${2:-0}" - if ! Array::contains "${commandToUse}" "usingTcp" "usingNc"; then - Log::fatal "${SCRIPT_NAME} - can't call command ${commandToUse} in child mode" - fi + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}hostOrIp${__HELP_NORMAL} {single} (mandatory) + " + Array::wrap2 ' ' 76 4 " " "Host or IP under test." + echo + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}port${__HELP_NORMAL} {single} (mandatory) + " + Array::wrap2 ' ' 76 4 " " "TCP port under test." + echo + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}commandArgs${__HELP_NORMAL} {list} (optional)] + " + Array::wrap2 ' ' 76 4 " " "Execute command with args after the test finishes or exit with status code if no command provided." + echo + # ------------------------------------------ + # options section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}TIMEOUT OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--timeout${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-t ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Timeout in seconds, zero for no timeout." + echo - local -i start_ts=${SECONDS} - while true; do - if "${commandToUse}"; then - Log::displayInfo "${SCRIPT_NAME} - ${optionHostOrIp}:${optionPort} is available after $((SECONDS - start_ts)) seconds" - break - fi - if ((optionTimeout != 0 && SECONDS - start_ts >= optionTimeout)); then - if [[ "${reportTimeout}" = "1" ]]; then - Log::displayError "${SCRIPT_NAME} - timeout for ${optionHostOrIp}:${optionPort} occurred after $((SECONDS - start_ts)) seconds" - fi - return 2 - fi - sleep 1 - done - return 0 - } - # shellcheck disable=SC2317 - timeoutCommand() { - local timeoutVersion="$1" - local commandToUse="$2" - local result - local -i start_ts=${SECONDS} + Array::wrap2 ' ' 76 6 " Default value: " "15" + echo + echo + echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays this command help" + echo - if ! Array::contains "${commandToUse}" "usingTcp" "usingNc"; then - Log::fatal "${SCRIPT_NAME} - can't call command ${commandToUse} in timeout mode" - fi - # compute timeout command - local -a timeoutCmd=(timeout) - if [[ "${timeoutVersion}" = "v1" ]]; then - # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 - timeoutCmd+=("-t") - fi - timeoutCmd+=( - "${optionTimeout}" - "$0" - "${ORIGINAL_BASH_FRAMEWORK_ARGV[@]}" - ) - WAIT_FOR_IT_TIMEOUT_CHILD_ALGO="${commandToUse}" "${timeoutCmd[@]}" & - - local pid=$! - # shellcheck disable=2064 - trap "kill -INT -${pid}" INT - wait "${pid}" - result=$? - if [[ "${result}" != "0" ]]; then - Log::displayError "${SCRIPT_NAME} - timeout for ${optionHostOrIp}:${optionPort} occurred after $((SECONDS - start_ts)) seconds" - fi - return "${result}" - } - # -------------------------------------- - # ALGORITHMS - # shellcheck disable=SC2317 - timeoutV1WithNc() { - timeoutCommand "v1" "usingNc" - } - # shellcheck disable=SC2317 - timeoutV2WithNc() { - timeoutCommand "v2" "usingNc" - } - # shellcheck disable=SC2317 - whileLoopWithNc() { - whileLoop "usingNc" "1" - } - # shellcheck disable=SC2317 - timeoutV1WithTcp() { - timeoutCommand "v1" "usingTcp" - } - # shellcheck disable=SC2317 - timeoutV2WithTcp() { - timeoutCommand "v2" "usingTcp" - } - # shellcheck disable=SC2317 - whileLoopWithTcp() { - whileLoop "usingTcp" "1" - } - # -------------------------------------- + echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays configuration" + echo - algorithmAutomaticSelection() { - if Array::contains "${optionAlgo}" "${availableAlgos[@]}"; then - echo "${optionAlgo}" - return 0 - fi - local command="WithTcp" - if Assert::commandExists nc &>/dev/null; then - # nc has the -w option allowing for timeout - command="WithNc" - fi - if ((optionTimeout > 0)); then - if Assert::commandExists timeout &>/dev/null; then - if timeout --help 2>&1 | grep -q -E -e '--timeout '; then - echo "timeoutV1${command}" - else - echo "timeoutV2${command}" - fi - fi - return 0 - fi - echo "whileLoop${command}" - } + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" + Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log file" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set display level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." + echo + Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" + echo + + Array::wrap2 ' ' 76 6 " Default value: " "default" + echo + + echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Print version information and quit." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." + echo + + + echo + echo -e "${__HELP_TITLE_COLOR}OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--algorithm${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}--algo ${__HELP_NORMAL} {single}" + algorithmHelpFunction + + + + + echo -e " ${__HELP_OPTION_COLOR}--exec-command-on-success-only${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}--strict${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-s${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Only execute sub-command if the test succeeds." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--user-nc${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Legacy mode using nc command or while loop (uses timeout command by default)." + echo + + + # ------------------------------------------ + # longDescription section + # ------------------------------------------ + echo + echo + echo -e "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" + longDescriptionFunction + # ------------------------------------------ + # version section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" + echo "3.0" + # ------------------------------------------ + # author section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}AUTHOR: ${__RESET_COLOR}" + echo "[François Chastanet](https://github.com/fchastanet)" + # ------------------------------------------ + # sourceFile section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Utils/waitForIt/waitForIt-binary.yaml" + # ------------------------------------------ + # license section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}LICENSE: ${__RESET_COLOR}" + echo "MIT License" + # ------------------------------------------ + # copyright section + # ------------------------------------------ + Array::wrap2 ' ' 76 0 "$(copyrightCallback)" +} + - local result="0" - if [[ -n "${WAIT_FOR_IT_TIMEOUT_CHILD_ALGO:-}" ]]; then - # parent process is executing timeout with current child process - # call algo nc or tcp inside whileLoop - whileLoop "${WAIT_FOR_IT_TIMEOUT_CHILD_ALGO}" "0" || result=$? +beforeParseCallback + +waitForItCommandParse "$@" +MAIN_FUNCTION_NAME="main" +main() { + +# shellcheck disable=SC2154 + +# Use this script to test if a given TCP host/port are available +# https://github.com/vishnubob/wait-for-it +# shellcheck disable=SC2317 +usingTcp() { + # couldn't find another way to mock this part + if [[ -n "${WAIT_FOR_IT_MOCKED_TCP:-}" ]]; then + "${WAIT_FOR_IT_MOCKED_TCP}" "/dev/tcp/${hostOrIpArg}/${portArg}" else - local algo="${optionAlgo}" - if [[ -z "${algo}" ]]; then - algo=$(algorithmAutomaticSelection) - fi - Log::displayInfo "${SCRIPT_NAME} - using algorithm ${algo}" - if ((optionTimeout > 0)); then - Log::displayInfo "${SCRIPT_NAME} - waiting ${optionTimeout} seconds for ${optionHostOrIp}:${optionPort}" - else - Log::displayInfo "${SCRIPT_NAME} - waiting for ${optionHostOrIp}:${optionPort} without a timeout" + echo >"/dev/tcp/${hostOrIpArg}/${portArg}" + fi +} + +# shellcheck disable=SC2317 +usingNc() { + local -a ncCmd=(nc -z "${hostOrIpArg}" "${portArg}" -w 1) + if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_INFO)); then + ncCmd+=(-v) + fi + + BashTools::runVerboseIfNeeded "${ncCmd[@]}" +} + +whileLoop() { + local commandToUse="$1" + local reportTimeout="${2:-0}" + if ! Array::contains "${commandToUse}" "usingTcp" "usingNc"; then + Log::fatal "${SCRIPT_NAME} - can't call command ${commandToUse} in child mode" + fi + + local -i start_ts=${SECONDS} + while true; do + if "${commandToUse}"; then + Log::displayInfo "${SCRIPT_NAME} - ${hostOrIpArg}:${portArg} is available after $((SECONDS - start_ts)) seconds" + break fi - "${algo}" || result=$? - # when timed out, call command if any - if [[ -n "${commandArgs+x}" && "${commandArgs[*]}" != "" ]]; then - if [[ "${result}" != "0" && "${optionStrict}" = "1" ]]; then - Log::displayError "${SCRIPT_NAME} - failed to connect - strict mode - command not executed" - exit "${result}" + if ((optionTimeout != 0 && SECONDS - start_ts >= optionTimeout)); then + if [[ "${reportTimeout}" = "1" ]]; then + Log::displayError "${SCRIPT_NAME} - timeout for ${hostOrIpArg}:${portArg} occurred after $((SECONDS - start_ts)) seconds" fi - exec "${commandArgs[@]}" + return 2 fi + sleep 1 + done + return 0 +} + +# shellcheck disable=SC2317 +timeoutCommand() { + local timeoutVersion="$1" + local commandToUse="$2" + local result + local -i start_ts=${SECONDS} + + if ! Array::contains "${commandToUse}" "usingTcp" "usingNc"; then + Log::fatal "${SCRIPT_NAME} - can't call command ${commandToUse} in timeout mode" + fi + + # compute timeout command + local -a timeoutCmd=(timeout) + if [[ "${timeoutVersion}" = "v1" ]]; then + # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 + timeoutCmd+=("-t") + fi + timeoutCmd+=( + "${optionTimeout}" + "$0" + "${ORIGINAL_BASH_FRAMEWORK_ARGV[@]}" + ) + WAIT_FOR_IT_TIMEOUT_CHILD_ALGO="${commandToUse}" "${timeoutCmd[@]}" & + + local pid=$! + # shellcheck disable=2064 + trap "kill -INT -${pid}" INT + wait "${pid}" + result=$? + if [[ "${result}" != "0" ]]; then + Log::displayError "${SCRIPT_NAME} - timeout for ${hostOrIpArg}:${portArg} occurred after $((SECONDS - start_ts)) seconds" + fi + return "${result}" +} + +# -------------------------------------- +# ALGORITHMS +# shellcheck disable=SC2317 +timeoutV1WithNc() { + timeoutCommand "v1" "usingNc" +} +# shellcheck disable=SC2317 +timeoutV2WithNc() { + timeoutCommand "v2" "usingNc" +} +# shellcheck disable=SC2317 +whileLoopWithNc() { + whileLoop "usingNc" "1" +} +# shellcheck disable=SC2317 +timeoutV1WithTcp() { + timeoutCommand "v1" "usingTcp" +} +# shellcheck disable=SC2317 +timeoutV2WithTcp() { + timeoutCommand "v2" "usingTcp" +} +# shellcheck disable=SC2317 +whileLoopWithTcp() { + whileLoop "usingTcp" "1" +} +# -------------------------------------- + +algorithmAutomaticSelection() { + if Array::contains "${optionAlgo}" "${availableAlgos[@]}"; then + echo "${optionAlgo}" + return 0 fi - exit "${result}" + local command="WithTcp" + if Assert::commandExists nc &>/dev/null; then + # nc has the -w option allowing for timeout + command="WithNc" + fi + + if ((optionTimeout > 0)); then + if Assert::commandExists timeout &>/dev/null; then + if timeout --help 2>&1 | grep -q -E -e '--timeout '; then + echo "timeoutV1${command}" + else + echo "timeoutV2${command}" + fi + fi + return 0 + fi + echo "whileLoop${command}" } -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null +declare result="0" +if [[ -n "${WAIT_FOR_IT_TIMEOUT_CHILD_ALGO:-}" ]]; then + # parent process is executing timeout with current child process + # call algo nc or tcp inside whileLoop + whileLoop "${WAIT_FOR_IT_TIMEOUT_CHILD_ALGO}" "0" || result=$? else - run + declare algo="${optionAlgo}" + if [[ -z "${algo}" ]]; then + algo=$(algorithmAutomaticSelection) + fi + Log::displayInfo "${SCRIPT_NAME} - using algorithm ${algo}" + if ((optionTimeout > 0)); then + Log::displayInfo "${SCRIPT_NAME} - waiting ${optionTimeout} seconds for ${hostOrIpArg}:${portArg}" + else + Log::displayInfo "${SCRIPT_NAME} - waiting for ${hostOrIpArg}:${portArg} without a timeout" + fi + "${algo}" || result=$? + # when timed out, call command if any + if [[ -n "${commandArgs+x}" && "${commandArgs[*]}" != "" ]]; then + if [[ "${result}" != "0" && "${optionStrict}" = "1" ]]; then + Log::displayError "${SCRIPT_NAME} - failed to connect - strict mode - command not executed" + exit "${result}" + fi + exec "${commandArgs[@]}" + fi fi +exit "${result}" + } -facade_main_waitForItsh "$@" +# if file is sourced avoid calling main function +# shellcheck disable=SC2178 +BASH_SOURCE=".$0" # cannot be changed in bash +# shellcheck disable=SC2128 +if test ".$0" == ".${BASH_SOURCE}"; then + if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then + main "$@" &>/dev/null + else + main "$@" + fi +fi diff --git a/src/BashTools/runVerboseIfNeeded.bats b/src/BashTools/runVerboseIfNeeded.bats index e677c5e8..eb60e784 100755 --- a/src/BashTools/runVerboseIfNeeded.bats +++ b/src/BashTools/runVerboseIfNeeded.bats @@ -16,9 +16,8 @@ function BashTools::runVerboseIfNeeded::noTrace { #@test } function BashTools::runVerboseIfNeeded::trace { #@test - export optionInfoVerbose="1" cmdTest() { - optionInfoVerbose="1" BashTools::runVerboseIfNeeded echo "coucou" + BASH_FRAMEWORK_ARGS_VERBOSE=__VERBOSE_LEVEL_INFO BashTools::runVerboseIfNeeded echo "coucou" } run cmdTest assert_lines_count 2 @@ -28,9 +27,9 @@ function BashTools::runVerboseIfNeeded::trace { #@test } function BashTools::runVerboseIfNeeded::redirectCmdOutputs { #@test - export + export BASH_FRAMEWORK_ARGS_VERBOSE cmdTest() { - optionInfoVerbose="1" optionRedirectCmdOutputs="/dev/null" BashTools::runVerboseIfNeeded echo "coucou" + BASH_FRAMEWORK_ARGS_VERBOSE=__VERBOSE_LEVEL_INFO optionRedirectCmdOutputs="/dev/null" BashTools::runVerboseIfNeeded echo "coucou" } run cmdTest assert_output '+ echo coucou' diff --git a/src/BashTools/runVerboseIfNeeded.sh b/src/BashTools/runVerboseIfNeeded.sh index 93ce352c..5d1cb6d4 100755 --- a/src/BashTools/runVerboseIfNeeded.sh +++ b/src/BashTools/runVerboseIfNeeded.sh @@ -7,7 +7,7 @@ # @exitcode command's exit code BashTools::runVerboseIfNeeded() { # shellcheck disable=SC2154 - if [[ "${optionInfoVerbose}" = "1" ]]; then + if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_INFO)); then echo >&2 "+ $*" fi ( diff --git a/src/_binaries/Utils/waitForIt.options.tpl b/src/_binaries/Utils/waitForIt.options.tpl deleted file mode 100644 index f33fcee4..00000000 --- a/src/_binaries/Utils/waitForIt.options.tpl +++ /dev/null @@ -1,118 +0,0 @@ -% -declare versionNumber="2.0" -declare commandFunctionName="waitForItCommand" -declare help="wait for host:port to be available" -# shellcheck disable=SC2016 -declare longDescription=''' -${__HELP_TITLE}EXIT STATUS CODES:${__HELP_NORMAL} -${__HELP_OPTION_COLOR}0${__HELP_NORMAL}: the host/port is available -${__HELP_OPTION_COLOR}1${__HELP_NORMAL}: indicates host/port is not available or argument error -${__HELP_OPTION_COLOR}2${__HELP_NORMAL}: timeout reached - -${__HELP_TITLE}AVAILABLE ALGORITHMS:${__HELP_NORMAL} -${__HELP_OPTION_COLOR}timeoutV1WithNc${__HELP_NORMAL}: previous version of timeout command with --timeout option, base command nc -${__HELP_OPTION_COLOR}timeoutV2WithNc${__HELP_NORMAL}: newer version of timeout command using timeout as argument, base command nc -${__HELP_OPTION_COLOR}whileLoopWithNc${__HELP_NORMAL}: timeout command simulated using while loop, base command nc -${__HELP_OPTION_COLOR}timeoutV1WithTcp${__HELP_NORMAL}: previous version of timeout command with --timeout option -${__HELP_OPTION_COLOR}timeoutV2WithTcp${__HELP_NORMAL}: newer version of timeout command using timeout as argument -${__HELP_OPTION_COLOR}whileLoopWithTcp${__HELP_NORMAL}: timeout command simulated using while loop, base command tcp -''' -% -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.base.tpl)" -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.timeout.tpl)" -% -# shellcheck source=/dev/null -source <( - Options::generateArg \ - --help "Execute command with args after the test finishes or exit with status code if no command provided." \ - --min 0 \ - --max -1 \ - --name "commandArgs" \ - --variable-name "commandArgs" \ - --function-name commandArgsFunction - - Options::generateOption \ - --help-value-name "hostOrIp" \ - --help "Host or IP under test." \ - --alt "--host" \ - --alt "-i" \ - --mandatory \ - --variable-type "String" \ - --variable-name "optionHostOrIp" \ - --function-name optionHostOrIpFunction - - Options::generateOption \ - --help-value-name "port" \ - --help "TCP port under test." \ - --alt "--port" \ - --alt "-p" \ - --mandatory \ - --variable-type "String" \ - --variable-name "optionPort" \ - --function-name optionPortFunction \ - --callback optionPortCallback - - Options::generateOption \ - --help-value-name "algorithm" \ - --help "$(echo \ - "Algorithm to use Check algorithms list below." $'\n' \ - "(default: automatic selection based on commands availability and timeout option value)." \ - )" \ - --alt "--algorithm" \ - --alt "--algo" \ - --variable-type "String" \ - --variable-name "optionAlgo" \ - --function-name optionAlgoFunction \ - --callback optionAlgoCallback - - Options::generateOption \ - --help "Only execute sub-command if the test succeeds." \ - --alt "--exec-command-on-success-only" \ - --alt "--strict" \ - --alt "-s" \ - --variable-name "optionStrict" \ - --function-name optionStrictFunction - - Options::generateOption \ - --help "legacy mode using nc command or while loop (uses timeout command by default)." \ - --alt "--user-nc" \ - --variable-name "optionLegacy" \ - --function-name optionLegacyFunction -) -options+=( - --unknown-option-callback unknownOption - --unknown-argument-callback unknownOption - --callback commandCallback - commandArgsFunction - optionHostOrIpFunction - optionPortFunction - optionAlgoFunction - optionStrictFunction -) -Options::generateCommand "${options[@]}" -% - -# shellcheck disable=SC2317 # if function is overridden -unknownOption() { - commandArgs+=("$1") -} - -optionPortCallback() { - if [[ ! "${optionPort}" =~ ^[0-9]+$ ]] || (( optionPort == 0 )); then - Log::fatal "${SCRIPT_NAME} - invalid port option - must be greater than to 0" - fi -} - -optionAlgoCallback() { - if ! Array::contains "${optionAlgo}" "${availableAlgos[@]}"; then - Log::fatal "${SCRIPT_NAME} - invalid algorithm '${optionAlgo}'" - fi -} - -commandCallback() { - if [[ "${optionHostOrIp}" = "" || "${optionPort}" = "" ]]; then - Log::fatal "${SCRIPT_NAME} - you need to provide a host and port to test." - fi -} - -<% ${commandFunctionName} %> parse "${BASH_FRAMEWORK_ARGV[@]}" diff --git a/src/_binaries/Utils/waitForIt.sh b/src/_binaries/Utils/waitForIt.sh deleted file mode 100755 index 11ff8264..00000000 --- a/src/_binaries/Utils/waitForIt.sh +++ /dev/null @@ -1,192 +0,0 @@ -#!/usr/bin/env bash -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/waitForIt -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE -# shellcheck disable=SC2034 - -# option values -declare -a commandArgs=() -declare optionHostOrIp="" -declare optionPort="" -declare optionStrict="0" -declare optionTimeout="15" -declare optionAlgo="" -declare -a availableAlgos=( - "timeoutV1WithNc" - "timeoutV2WithNc" - "whileLoopWithNc" - "timeoutV1WithTcp" - "timeoutV2WithTcp" - "whileLoopWithTcp" -) -# other values -declare copyrightBeginYear="2020" - -.INCLUDE "$(dynamicTemplateDir _binaries/Utils/waitForIt.options.tpl)" - -# Use this script to test if a given TCP host/port are available -# https://github.com/vishnubob/wait-for-it - -run() { - # shellcheck disable=SC2317 - usingTcp() { - # couldn't find another way to mock this part - if [[ -n "${WAIT_FOR_IT_MOCKED_TCP:-}" ]]; then - "${WAIT_FOR_IT_MOCKED_TCP}" "/dev/tcp/${optionHostOrIp}/${optionPort}" 2>&1 - else - echo >"/dev/tcp/${optionHostOrIp}/${optionPort}" 2>&1 - fi - } - - # shellcheck disable=SC2317 - usingNc() { - nc -z "${optionHostOrIp}" "${optionPort}" -w 1 2>&1 - } - - whileLoop() { - local commandToUse="$1" - local reportTimeout="${2:-0}" - if ! Array::contains "${commandToUse}" "usingTcp" "usingNc"; then - Log::fatal "${SCRIPT_NAME} - can't call command ${commandToUse} in child mode" - fi - - local -i start_ts=${SECONDS} - while true; do - if "${commandToUse}"; then - Log::displayInfo "${SCRIPT_NAME} - ${optionHostOrIp}:${optionPort} is available after $((SECONDS - start_ts)) seconds" - break - fi - if ((optionTimeout != 0 && SECONDS - start_ts >= optionTimeout)); then - if [[ "${reportTimeout}" = "1" ]]; then - Log::displayError "${SCRIPT_NAME} - timeout for ${optionHostOrIp}:${optionPort} occurred after $((SECONDS - start_ts)) seconds" - fi - return 2 - fi - sleep 1 - done - return 0 - } - - # shellcheck disable=SC2317 - timeoutCommand() { - local timeoutVersion="$1" - local commandToUse="$2" - local result - local -i start_ts=${SECONDS} - - if ! Array::contains "${commandToUse}" "usingTcp" "usingNc"; then - Log::fatal "${SCRIPT_NAME} - can't call command ${commandToUse} in timeout mode" - fi - - # compute timeout command - local -a timeoutCmd=(timeout) - if [[ "${timeoutVersion}" = "v1" ]]; then - # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 - timeoutCmd+=("-t") - fi - timeoutCmd+=( - "${optionTimeout}" - "$0" - "${ORIGINAL_BASH_FRAMEWORK_ARGV[@]}" - ) - WAIT_FOR_IT_TIMEOUT_CHILD_ALGO="${commandToUse}" "${timeoutCmd[@]}" & - - local pid=$! - # shellcheck disable=2064 - trap "kill -INT -${pid}" INT - wait "${pid}" - result=$? - if [[ "${result}" != "0" ]]; then - Log::displayError "${SCRIPT_NAME} - timeout for ${optionHostOrIp}:${optionPort} occurred after $((SECONDS - start_ts)) seconds" - fi - return "${result}" - } - - # -------------------------------------- - # ALGORITHMS - # shellcheck disable=SC2317 - timeoutV1WithNc() { - timeoutCommand "v1" "usingNc" - } - # shellcheck disable=SC2317 - timeoutV2WithNc() { - timeoutCommand "v2" "usingNc" - } - # shellcheck disable=SC2317 - whileLoopWithNc() { - whileLoop "usingNc" "1" - } - # shellcheck disable=SC2317 - timeoutV1WithTcp() { - timeoutCommand "v1" "usingTcp" - } - # shellcheck disable=SC2317 - timeoutV2WithTcp() { - timeoutCommand "v2" "usingTcp" - } - # shellcheck disable=SC2317 - whileLoopWithTcp() { - whileLoop "usingTcp" "1" - } - # -------------------------------------- - - algorithmAutomaticSelection() { - if Array::contains "${optionAlgo}" "${availableAlgos[@]}"; then - echo "${optionAlgo}" - return 0 - fi - - local command="WithTcp" - if Assert::commandExists nc &>/dev/null; then - # nc has the -w option allowing for timeout - command="WithNc" - fi - - if ((optionTimeout > 0)); then - if Assert::commandExists timeout &>/dev/null; then - if timeout --help 2>&1 | grep -q -E -e '--timeout '; then - echo "timeoutV1${command}" - else - echo "timeoutV2${command}" - fi - fi - return 0 - fi - echo "whileLoop${command}" - } - - local result="0" - if [[ -n "${WAIT_FOR_IT_TIMEOUT_CHILD_ALGO:-}" ]]; then - # parent process is executing timeout with current child process - # call algo nc or tcp inside whileLoop - whileLoop "${WAIT_FOR_IT_TIMEOUT_CHILD_ALGO}" "0" || result=$? - else - local algo="${optionAlgo}" - if [[ -z "${algo}" ]]; then - algo=$(algorithmAutomaticSelection) - fi - Log::displayInfo "${SCRIPT_NAME} - using algorithm ${algo}" - if ((optionTimeout > 0)); then - Log::displayInfo "${SCRIPT_NAME} - waiting ${optionTimeout} seconds for ${optionHostOrIp}:${optionPort}" - else - Log::displayInfo "${SCRIPT_NAME} - waiting for ${optionHostOrIp}:${optionPort} without a timeout" - fi - "${algo}" || result=$? - # when timed out, call command if any - if [[ -n "${commandArgs+x}" && "${commandArgs[*]}" != "" ]]; then - if [[ "${result}" != "0" && "${optionStrict}" = "1" ]]; then - Log::displayError "${SCRIPT_NAME} - failed to connect - strict mode - command not executed" - exit "${result}" - fi - exec "${commandArgs[@]}" - fi - fi - - exit "${result}" -} - -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run -fi diff --git a/src/_binaries/Utils/waitForIt/testsData/waitForIt.help.txt b/src/_binaries/Utils/waitForIt/testsData/waitForIt.help.txt new file mode 100644 index 00000000..55c86a8d --- /dev/null +++ b/src/_binaries/Utils/waitForIt/testsData/waitForIt.help.txt @@ -0,0 +1,94 @@ +SYNOPSIS: + Wait for host:port to be available. + +USAGE: waitForIt [OPTIONS] [ARGUMENTS] +USAGE: waitForIt [--timeout|-t ] [--help|-h] [--config] + [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] + [--env-file ] [--log-level ] [--log-file ] + [--display-level ] [--no-color] [--theme ] [--version] + [--quiet|-q] [--algorithm|--algo ] + [--exec-command-on-success-only|--strict|-s] [--user-nc] + +ARGUMENTS: + hostOrIp {single} (mandatory) + Host or IP under test. + port {single} (mandatory) + TCP port under test. + [commandArgs {list} (optional)] + Execute command with args after the test finishes or exit with status co + de if no command provided. + +TIMEOUT OPTIONS: + --timeout, -t  {single} + Timeout in seconds, zero for no timeout. + Default value: 15 + +GLOBAL OPTIONS: + --help, -h {single} + Displays this command help + --config {single} + Displays configuration + --bash-framework-config  {single} + Use alternate bash framework configuration. + --verbose, -v {single} + Info level verbose mode (alias of --display-level INFO) + -vv {single} + Debug level verbose mode (alias of --display-level DEBUG) + -vvv {single} + Trace level verbose mode (alias of --display-level TRACE) + --env-file  {list} (optional) + Load the specified env file (deprecated, please use --bash-framework-con + fig option instead) + --log-level  {single} + Set log level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --log-file  {single} + Set log file + --display-level  {single} + Set display level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --no-color {single} + Produce monochrome output. alias of --theme noColor. + --theme  {single} + Choose color theme - default-force means colors will be produced even if + command is piped. + Possible values: default, default-force, noColor + Default value: default + --version {single} + Print version information and quit. + --quiet, -q {single} + Quiet mode, doesn't display any output. + +OPTIONS: + --algorithm, --algo  {single} + Algorithm to use Check algorithms list below. + Default: automatic selection based on commands availability and timeout option value. + --exec-command-on-success-only, --strict, -s {single} + Only execute sub-command if the test succeeds. + --user-nc {single} + Legacy mode using nc command or while loop (uses timeout command by defa + ult). + + +DESCRIPTION: + EXIT STATUS CODES: + 0: the host/port is available + 1: indicates host/port is not available or argument error + 2: timeout reached + + AVAILABLE ALGORITHMS: + timeoutV1WithNc: previous version of timeout command with --timeout option, base command nc + timeoutV2WithNc: newer version of timeout command using timeout as argument, base command nc + whileLoopWithNc: timeout command simulated using while loop, base command nc + timeoutV1WithTcp: previous version of timeout command with --timeout option + timeoutV2WithTcp: newer version of timeout command using timeout as argument + whileLoopWithTcp: timeout command simulated using while loop, base command tcp + +VERSION: 3.0 + +AUTHOR: [François Chastanet](https://github.com/fchastanet) + +SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Utils/waitForIt/waitForIt-binary.yaml + +LICENSE: MIT License +Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Utils/waitForIt/waitForIt-binary.yaml b/src/_binaries/Utils/waitForIt/waitForIt-binary.yaml new file mode 100644 index 00000000..af2279c7 --- /dev/null +++ b/src/_binaries/Utils/waitForIt/waitForIt-binary.yaml @@ -0,0 +1,93 @@ +extends: + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsTimeout.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" + +vars: + SRC_FILE_PATH: src/_binaries/Utils/waitForIt/waitForIt-binary.yaml + +compilerConfig: + targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/waitForIt" + relativeRootDirBasedOnTargetDir: .. + srcDirs: + - ${BASH_TOOLS_ROOT_DIR}/src +binData: + commands: + default: + functionName: waitForItCommand + version: "3.0" + copyrightBeginYear: 2020 + commandName: waitForIt + beforeParseCallbacks: + - beforeParseCallback + unknownOptionCallbacks: + - unknownOption + unknownArgumentCallbacks: + - unknownOption + callbacks: + - commandCallback + definitionFiles: + 20: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Utils/waitForIt/waitForIt-options.sh + mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Utils/waitForIt/waitForIt-main.sh + help: Wait for host:port to be available. + longDescription: longDescriptionFunction + args: + - type: String + min: 1 + max: 1 + help: Host or IP under test. + name: hostOrIp + variableName: hostOrIpArg + + - type: String + min: 1 + max: 1 + help: TCP port under test. + name: port + variableName: portArg + callbacks: + - portArgCallback + + - type: StringArray + min: 0 + max: -1 + help: + Execute command with args after the test finishes or exit with + status code if no command provided. + name: commandArgs + variableName: commandArgs + + optionGroups: + waitForItOptionGroup: + title: "OPTIONS:" + + options: + - variableName: optionAlgo + type: String + group: waitForItOptionGroup + help: algorithmHelpFunction + helpValueName: algorithm + callbacks: + - optionAlgoCallback + alts: + - --algorithm + - --algo + + - variableName: optionStrict + type: Boolean + group: waitForItOptionGroup + help: Only execute sub-command if the test succeeds. + alts: + - --exec-command-on-success-only + - --strict + - -s + + - variableName: optionLegacy + type: Boolean + group: waitForItOptionGroup + help: + Legacy mode using nc command or while loop (uses timeout command by + default). + alts: + - --user-nc diff --git a/src/_binaries/Utils/waitForIt/waitForIt-main.sh b/src/_binaries/Utils/waitForIt/waitForIt-main.sh new file mode 100755 index 00000000..ee63f3cd --- /dev/null +++ b/src/_binaries/Utils/waitForIt/waitForIt-main.sh @@ -0,0 +1,165 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2154 + +# Use this script to test if a given TCP host/port are available +# https://github.com/vishnubob/wait-for-it +# shellcheck disable=SC2317 +usingTcp() { + # couldn't find another way to mock this part + if [[ -n "${WAIT_FOR_IT_MOCKED_TCP:-}" ]]; then + "${WAIT_FOR_IT_MOCKED_TCP}" "/dev/tcp/${hostOrIpArg}/${portArg}" + else + echo >"/dev/tcp/${hostOrIpArg}/${portArg}" + fi +} + +# shellcheck disable=SC2317 +usingNc() { + local -a ncCmd=(nc -z "${hostOrIpArg}" "${portArg}" -w 1) + if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_INFO)); then + ncCmd+=(-v) + fi + + BashTools::runVerboseIfNeeded "${ncCmd[@]}" +} + +whileLoop() { + local commandToUse="$1" + local reportTimeout="${2:-0}" + if ! Array::contains "${commandToUse}" "usingTcp" "usingNc"; then + Log::fatal "${SCRIPT_NAME} - can't call command ${commandToUse} in child mode" + fi + + local -i start_ts=${SECONDS} + while true; do + if "${commandToUse}"; then + Log::displayInfo "${SCRIPT_NAME} - ${hostOrIpArg}:${portArg} is available after $((SECONDS - start_ts)) seconds" + break + fi + if ((optionTimeout != 0 && SECONDS - start_ts >= optionTimeout)); then + if [[ "${reportTimeout}" = "1" ]]; then + Log::displayError "${SCRIPT_NAME} - timeout for ${hostOrIpArg}:${portArg} occurred after $((SECONDS - start_ts)) seconds" + fi + return 2 + fi + sleep 1 + done + return 0 +} + +# shellcheck disable=SC2317 +timeoutCommand() { + local timeoutVersion="$1" + local commandToUse="$2" + local result + local -i start_ts=${SECONDS} + + if ! Array::contains "${commandToUse}" "usingTcp" "usingNc"; then + Log::fatal "${SCRIPT_NAME} - can't call command ${commandToUse} in timeout mode" + fi + + # compute timeout command + local -a timeoutCmd=(timeout) + if [[ "${timeoutVersion}" = "v1" ]]; then + # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 + timeoutCmd+=("-t") + fi + timeoutCmd+=( + "${optionTimeout}" + "$0" + "${ORIGINAL_BASH_FRAMEWORK_ARGV[@]}" + ) + WAIT_FOR_IT_TIMEOUT_CHILD_ALGO="${commandToUse}" "${timeoutCmd[@]}" & + + local pid=$! + # shellcheck disable=2064 + trap "kill -INT -${pid}" INT + wait "${pid}" + result=$? + if [[ "${result}" != "0" ]]; then + Log::displayError "${SCRIPT_NAME} - timeout for ${hostOrIpArg}:${portArg} occurred after $((SECONDS - start_ts)) seconds" + fi + return "${result}" +} + +# -------------------------------------- +# ALGORITHMS +# shellcheck disable=SC2317 +timeoutV1WithNc() { + timeoutCommand "v1" "usingNc" +} +# shellcheck disable=SC2317 +timeoutV2WithNc() { + timeoutCommand "v2" "usingNc" +} +# shellcheck disable=SC2317 +whileLoopWithNc() { + whileLoop "usingNc" "1" +} +# shellcheck disable=SC2317 +timeoutV1WithTcp() { + timeoutCommand "v1" "usingTcp" +} +# shellcheck disable=SC2317 +timeoutV2WithTcp() { + timeoutCommand "v2" "usingTcp" +} +# shellcheck disable=SC2317 +whileLoopWithTcp() { + whileLoop "usingTcp" "1" +} +# -------------------------------------- + +algorithmAutomaticSelection() { + if Array::contains "${optionAlgo}" "${availableAlgos[@]}"; then + echo "${optionAlgo}" + return 0 + fi + + local command="WithTcp" + if Assert::commandExists nc &>/dev/null; then + # nc has the -w option allowing for timeout + command="WithNc" + fi + + if ((optionTimeout > 0)); then + if Assert::commandExists timeout &>/dev/null; then + if timeout --help 2>&1 | grep -q -E -e '--timeout '; then + echo "timeoutV1${command}" + else + echo "timeoutV2${command}" + fi + fi + return 0 + fi + echo "whileLoop${command}" +} + +declare result="0" +if [[ -n "${WAIT_FOR_IT_TIMEOUT_CHILD_ALGO:-}" ]]; then + # parent process is executing timeout with current child process + # call algo nc or tcp inside whileLoop + whileLoop "${WAIT_FOR_IT_TIMEOUT_CHILD_ALGO}" "0" || result=$? +else + declare algo="${optionAlgo}" + if [[ -z "${algo}" ]]; then + algo=$(algorithmAutomaticSelection) + fi + Log::displayInfo "${SCRIPT_NAME} - using algorithm ${algo}" + if ((optionTimeout > 0)); then + Log::displayInfo "${SCRIPT_NAME} - waiting ${optionTimeout} seconds for ${hostOrIpArg}:${portArg}" + else + Log::displayInfo "${SCRIPT_NAME} - waiting for ${hostOrIpArg}:${portArg} without a timeout" + fi + "${algo}" || result=$? + # when timed out, call command if any + if [[ -n "${commandArgs+x}" && "${commandArgs[*]}" != "" ]]; then + if [[ "${result}" != "0" && "${optionStrict}" = "1" ]]; then + Log::displayError "${SCRIPT_NAME} - failed to connect - strict mode - command not executed" + exit "${result}" + fi + exec "${commandArgs[@]}" + fi +fi + +exit "${result}" diff --git a/src/_binaries/Utils/waitForIt/waitForIt-options.sh b/src/_binaries/Utils/waitForIt/waitForIt-options.sh new file mode 100755 index 00000000..c6645b44 --- /dev/null +++ b/src/_binaries/Utils/waitForIt/waitForIt-options.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash + +declare -a availableAlgos=( + "timeoutV1WithNc" + "timeoutV2WithNc" + "whileLoopWithNc" + "timeoutV1WithTcp" + "timeoutV2WithTcp" + "whileLoopWithTcp" +) + +longDescriptionFunction() { + echo -e " ${__HELP_TITLE}EXIT STATUS CODES:${__HELP_NORMAL}" + echo -e " ${__HELP_OPTION_COLOR}0${__HELP_NORMAL}: the host/port is available" + echo -e " ${__HELP_OPTION_COLOR}1${__HELP_NORMAL}: indicates host/port is not available or argument error" + echo -e " ${__HELP_OPTION_COLOR}2${__HELP_NORMAL}: timeout reached" + echo + echo -e " ${__HELP_TITLE}AVAILABLE ALGORITHMS:${__HELP_NORMAL}" + echo -e " ${__HELP_OPTION_COLOR}timeoutV1WithNc${__HELP_NORMAL}: previous version of timeout command with --timeout option, base command nc" + echo -e " ${__HELP_OPTION_COLOR}timeoutV2WithNc${__HELP_NORMAL}: newer version of timeout command using timeout as argument, base command nc" + echo -e " ${__HELP_OPTION_COLOR}whileLoopWithNc${__HELP_NORMAL}: timeout command simulated using while loop, base command nc" + echo -e " ${__HELP_OPTION_COLOR}timeoutV1WithTcp${__HELP_NORMAL}: previous version of timeout command with --timeout option" + echo -e " ${__HELP_OPTION_COLOR}timeoutV2WithTcp${__HELP_NORMAL}: newer version of timeout command using timeout as argument" + echo -e " ${__HELP_OPTION_COLOR}whileLoopWithTcp${__HELP_NORMAL}: timeout command simulated using while loop, base command tcp" +} + +algorithmHelpFunction() { + echo " Algorithm to use Check algorithms list below." + echo " Default: automatic selection based on commands availability and timeout option value." +} + +optionHelpCallback() { + waitForItCommandHelp + exit 0 +} + +# shellcheck disable=SC2317 # if function is overridden +unknownOption() { + commandArgs+=("$1") +} + +portArgCallback() { + # shellcheck disable=SC2154 + if [[ ! "${portArg}" =~ ^[0-9]+$ ]] || ((portArg == 0)); then + Log::fatal "${SCRIPT_NAME} - invalid port argument - must be greater than to 0" + fi +} + +optionAlgoCallback() { + # shellcheck disable=SC2154 + if ! Array::contains "${optionAlgo}" "${availableAlgos[@]}"; then + Log::fatal "${SCRIPT_NAME} - invalid algorithm option '${optionAlgo}'" + fi +} + +commandCallback() { + # shellcheck disable=SC2154 + if [[ "${hostOrIpArg}" = "" || "${portArg}" = "" ]]; then + Log::fatal "${SCRIPT_NAME} - you need to provide a host and port to test." + fi +} diff --git a/src/_binaries/Utils/waitForIt.bats b/src/_binaries/Utils/waitForIt/waitForIt.bats similarity index 67% rename from src/_binaries/Utils/waitForIt.bats rename to src/_binaries/Utils/waitForIt/waitForIt.bats index 583886a1..afaf8bd8 100755 --- a/src/_binaries/Utils/waitForIt.bats +++ b/src/_binaries/Utils/waitForIt/waitForIt.bats @@ -1,7 +1,7 @@ #!/usr/bin/env bash # shellcheck source=src/batsHeaders.sh -source "$(cd "${BATS_TEST_DIRNAME}/../.." && pwd)/batsHeaders.sh" +source "$(cd "${BATS_TEST_DIRNAME}/../../.." && pwd)/batsHeaders.sh" setup() { export TMPDIR="${BATS_TEST_TMPDIR}" @@ -27,27 +27,27 @@ function Utils::waitForIt::noArgs { #@test assert_failure 1 assert_lines_count 1 - assert_output --partial "ERROR - Command waitForIt - Option '--host' should be provided at least 1 time(s)" + assert_output --partial "ERROR - Command waitForIt - Argument 'hostOrIp' should be provided at least 1 time(s)" } function Utils::waitForIt::missingPort { #@test - run "${binDir}/waitForIt" --host localhost 2>&1 + run "${binDir}/waitForIt" localhost 2>&1 assert_failure 1 assert_lines_count 1 - assert_output --partial "ERROR - Command waitForIt - Option '--port' should be provided at least 1 time(s)" + assert_output --partial "ERROR - Command waitForIt - Argument 'port' should be provided at least 1 time(s)" } function Utils::waitForIt::missingHost { #@test - run "${binDir}/waitForIt" --port 8888 2>&1 + run "${binDir}/waitForIt" 8888 2>&1 assert_failure 1 assert_lines_count 1 - assert_output --partial "ERROR - Command waitForIt - Option '--host' should be provided at least 1 time(s)" + assert_output --partial "ERROR - Command waitForIt - Argument 'port' should be provided at least 1 time(s)" } function Utils::waitForIt::invalidTimeout { #@test - run "${binDir}/waitForIt" --host localhost --port 8888 --timeout invalid 2>&1 + run "${binDir}/waitForIt" localhost 8888 --timeout invalid 2>&1 assert_failure 1 assert_lines_count 1 @@ -55,17 +55,17 @@ function Utils::waitForIt::invalidTimeout { #@test } function Utils::waitForIt::invalidAlgo { #@test - run "${binDir}/waitForIt" --host localhost --port 8888 --algo invalid 2>&1 + run "${binDir}/waitForIt" localhost 8888 --algo invalid 2>&1 assert_failure 1 assert_lines_count 1 - assert_output --partial "FATAL - waitForIt - invalid algorithm 'invalid'" + assert_output --partial "FATAL - waitForIt - invalid algorithm option 'invalid'" } function Utils::waitForIt::algo::timeoutV1WithNc::WithoutCommand { #@test - stub timeout "-t 1 ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --algo timeoutV1WithNc : ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --algo timeoutV1WithNc" + stub timeout "-t 1 ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV1WithNc : ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV1WithNc" stub nc "-z localhost 8888 -w 1 : exit 0" - run "${binDir}/waitForIt" --host localhost --port 8888 --timeout 1 --algo timeoutV1WithNc 2>&1 + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo timeoutV1WithNc 2>&1 assert_success assert_line --index 0 --partial "INFO - waitForIt - using algorithm timeoutV1WithNc" @@ -76,10 +76,10 @@ function Utils::waitForIt::algo::timeoutV1WithNc::WithoutCommand { #@test } function Utils::waitForIt::algo::timeoutV1WithNc::ExecCommand { #@test - stub timeout "-t 1 ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --algo timeoutV1WithNc echo success : \ - ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --algo timeoutV1WithNc echo success" + stub timeout "-t 1 ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV1WithNc echo success : \ + ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV1WithNc echo success" stub nc "-z localhost 8888 -w 1 : exit 0" - run "${binDir}/waitForIt" --host localhost --port 8888 --timeout 1 --algo timeoutV1WithNc echo "success" 2>&1 + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo timeoutV1WithNc echo "success" 2>&1 assert_success assert_line --index 0 --partial "INFO - waitForIt - using algorithm timeoutV1WithNc" @@ -98,8 +98,8 @@ function Utils::waitForIt::algo::timeoutV1WithNc::NoCommandExecutedIfFailed { #@ chmod +x "${HOME}/bin/nc" stub timeout \ - "-t 1 ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --strict --algo timeoutV1WithNc echo success : ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --strict --algo timeoutV1WithNc echo success" - run "${binDir}/waitForIt" --host localhost --port 8888 --timeout 1 --strict --algo timeoutV1WithNc echo "success" 2>&1 + "-t 1 ${binDir}/waitForIt localhost 8888 --timeout 1 --strict --algo timeoutV1WithNc echo success : ${binDir}/waitForIt localhost 8888 --timeout 1 --strict --algo timeoutV1WithNc echo success" + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --strict --algo timeoutV1WithNc echo "success" 2>&1 assert_failure 2 assert_line --index 0 --partial "INFO - waitForIt - using algorithm timeoutV1WithNc" @@ -111,9 +111,9 @@ function Utils::waitForIt::algo::timeoutV1WithNc::NoCommandExecutedIfFailed { #@ } function Utils::waitForIt::algo::timeoutV2WithNc::WithoutCommand { #@test - stub timeout "1 ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --algo timeoutV2WithNc : ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --algo timeoutV2WithNc" + stub timeout "1 ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV2WithNc : ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV2WithNc" stub nc "-z localhost 8888 -w 1 : exit 0" - run "${binDir}/waitForIt" --host localhost --port 8888 --timeout 1 --algo timeoutV2WithNc 2>&1 + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo timeoutV2WithNc 2>&1 assert_success assert_line --index 0 --partial "INFO - waitForIt - using algorithm timeoutV2WithNc" @@ -123,10 +123,10 @@ function Utils::waitForIt::algo::timeoutV2WithNc::WithoutCommand { #@test } function Utils::waitForIt::algo::timeoutV2WithNc::ExecCommand { #@test - stub timeout "1 ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --algo timeoutV2WithNc echo success : \ - ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --algo timeoutV2WithNc echo success" + stub timeout "1 ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV2WithNc echo success : \ + ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV2WithNc echo success" stub nc "-z localhost 8888 -w 1 : exit 0" - run "${binDir}/waitForIt" --host localhost --port 8888 --timeout 1 --algo timeoutV2WithNc echo "success" 2>&1 + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo timeoutV2WithNc echo "success" 2>&1 assert_success assert_line --index 0 --partial "INFO - waitForIt - using algorithm timeoutV2WithNc" @@ -144,8 +144,8 @@ function Utils::waitForIt::algo::timeoutV2WithNc::NoCommandExecutedIfFailed { #@ chmod +x "${HOME}/bin/nc" stub timeout \ - "1 ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --strict --algo timeoutV2WithNc echo success : ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --strict --algo timeoutV2WithNc echo success" - run "${binDir}/waitForIt" --host localhost --port 8888 --timeout 1 --strict --algo timeoutV2WithNc echo "success" 2>&1 + "1 ${binDir}/waitForIt localhost 8888 --timeout 1 --strict --algo timeoutV2WithNc echo success : ${binDir}/waitForIt localhost 8888 --timeout 1 --strict --algo timeoutV2WithNc echo success" + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --strict --algo timeoutV2WithNc echo "success" 2>&1 assert_failure 2 assert_line --index 0 --partial "INFO - waitForIt - using algorithm timeoutV2WithNc" @@ -164,8 +164,8 @@ function Utils::waitForIt::algo::timeoutV1WithTcp::WithoutCommand { #@test echo "mocked $*" } export -f mockedTcp - stub timeout "-t 1 ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --algo timeoutV1WithTcp : ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --algo timeoutV1WithTcp" - run "${binDir}/waitForIt" --host localhost --port 8888 --timeout 1 --algo timeoutV1WithTcp 2>&1 + stub timeout "-t 1 ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV1WithTcp : ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV1WithTcp" + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo timeoutV1WithTcp 2>&1 assert_success assert_line --index 0 --partial "INFO - waitForIt - using algorithm timeoutV1WithTcp" @@ -182,9 +182,9 @@ function Utils::waitForIt::algo::timeoutV1WithTcp::ExecCommand { #@test echo "mocked $*" } export -f mockedTcp - stub timeout "-t 1 ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --algo timeoutV1WithTcp echo success : \ - ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --algo timeoutV1WithTcp echo success" - run "${binDir}/waitForIt" --host localhost --port 8888 --timeout 1 --algo timeoutV1WithTcp echo "success" 2>&1 + stub timeout "-t 1 ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV1WithTcp echo success : \ + ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV1WithTcp echo success" + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo timeoutV1WithTcp echo "success" 2>&1 assert_success assert_line --index 0 --partial "INFO - waitForIt - using algorithm timeoutV1WithTcp" @@ -203,8 +203,8 @@ function Utils::waitForIt::algo::timeoutV1WithTcp::NoCommandExecutedIfFailed { # } export -f mockedTcp stub timeout \ - "-t 1 ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --strict --algo timeoutV1WithTcp echo success : ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --strict --algo timeoutV1WithTcp echo success" - run "${binDir}/waitForIt" --host localhost --port 8888 --timeout 1 --strict --algo timeoutV1WithTcp echo "success" 2>&1 + "-t 1 ${binDir}/waitForIt localhost 8888 --timeout 1 --strict --algo timeoutV1WithTcp echo success : ${binDir}/waitForIt localhost 8888 --timeout 1 --strict --algo timeoutV1WithTcp echo success" + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --strict --algo timeoutV1WithTcp echo "success" 2>&1 assert_failure 2 assert_line --index 0 --partial "INFO - waitForIt - using algorithm timeoutV1WithTcp" @@ -221,8 +221,8 @@ function Utils::waitForIt::algo::timeoutV2WithTcp::WithoutCommand { #@test return 0 } export -f mockedTcp - stub timeout "1 ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --algo timeoutV2WithTcp : ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --algo timeoutV2WithTcp" - run "${binDir}/waitForIt" --host localhost --port 8888 --timeout 1 --algo timeoutV2WithTcp 2>&1 + stub timeout "1 ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV2WithTcp : ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV2WithTcp" + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo timeoutV2WithTcp 2>&1 assert_success assert_line --index 0 --partial "INFO - waitForIt - using algorithm timeoutV2WithTcp" @@ -238,9 +238,9 @@ function Utils::waitForIt::algo::timeoutV2WithTcp::ExecCommand { #@test return 0 } export -f mockedTcp - stub timeout "1 ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --algo timeoutV2WithTcp echo success : \ - ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --algo timeoutV2WithTcp echo success" - run "${binDir}/waitForIt" --host localhost --port 8888 --timeout 1 --algo timeoutV2WithTcp echo "success" 2>&1 + stub timeout "1 ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV2WithTcp echo success : \ + ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV2WithTcp echo success" + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo timeoutV2WithTcp echo "success" 2>&1 assert_success assert_line --index 0 --partial "INFO - waitForIt - using algorithm timeoutV2WithTcp" @@ -258,8 +258,8 @@ function Utils::waitForIt::algo::timeoutV2WithTcp::NoCommandExecutedIfFailed { # } export -f mockedTcp stub timeout \ - "1 ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --strict --algo timeoutV2WithTcp echo success : ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --strict --algo timeoutV2WithTcp echo success" - run "${binDir}/waitForIt" --host localhost --port 8888 --timeout 1 --strict --algo timeoutV2WithTcp echo "success" 2>&1 + "1 ${binDir}/waitForIt localhost 8888 --timeout 1 --strict --algo timeoutV2WithTcp echo success : ${binDir}/waitForIt localhost 8888 --timeout 1 --strict --algo timeoutV2WithTcp echo success" + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --strict --algo timeoutV2WithTcp echo "success" 2>&1 assert_failure 2 assert_line --index 0 --partial "INFO - waitForIt - using algorithm timeoutV2WithTcp" @@ -278,8 +278,8 @@ function Utils::waitForIt::algo::whileLoopWithTcp::WithoutCommand { #@test echo "mocked $*" } export -f mockedTcp - stub timeout "-t 1 ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --algo whileLoopWithTcp : ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --algo whileLoopWithTcp" - run "${binDir}/waitForIt" --host localhost --port 8888 --timeout 1 --algo whileLoopWithTcp 2>&1 + stub timeout "-t 1 ${binDir}/waitForIt localhost 8888 --timeout 1 --algo whileLoopWithTcp : ${binDir}/waitForIt localhost 8888 --timeout 1 --algo whileLoopWithTcp" + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo whileLoopWithTcp 2>&1 assert_success assert_line --index 0 --partial "INFO - waitForIt - using algorithm whileLoopWithTcp" @@ -296,9 +296,9 @@ function Utils::waitForIt::algo::whileLoopWithTcp::ExecCommand { #@test echo "mocked $*" } export -f mockedTcp - stub timeout "-t 1 ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --algo whileLoopWithTcp echo success : \ - ${binDir}/waitForIt --host localhost --port 8888 --timeout 1 --algo whileLoopWithTcp echo success" - run "${binDir}/waitForIt" --host localhost --port 8888 --timeout 1 --algo whileLoopWithTcp echo "success" 2>&1 + stub timeout "-t 1 ${binDir}/waitForIt localhost 8888 --timeout 1 --algo whileLoopWithTcp echo success : \ + ${binDir}/waitForIt localhost 8888 --timeout 1 --algo whileLoopWithTcp echo success" + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo whileLoopWithTcp echo "success" 2>&1 assert_success assert_line --index 0 --partial "INFO - waitForIt - using algorithm whileLoopWithTcp" @@ -316,7 +316,7 @@ function Utils::waitForIt::algo::whileLoopWithTcp::NoCommandExecutedIfFailed { # return 1 } export -f mockedTcp - run "${binDir}/waitForIt" --host localhost --port 8888 --timeout 1 --strict --algo whileLoopWithTcp echo "success" 2>&1 + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --strict --algo whileLoopWithTcp echo "success" 2>&1 assert_failure 2 assert_line --index 0 --partial "INFO - waitForIt - using algorithm whileLoopWithTcp" @@ -332,7 +332,7 @@ function Utils::waitForIt::algo::whileLoopWithNc::WithoutCommand { #@test echo 'exit 0' ) >"${HOME}/bin/nc" chmod +x "${HOME}/bin/nc" - run "${binDir}/waitForIt" --host localhost --port 8888 --timeout 1 --algo whileLoopWithNc 2>&1 + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo whileLoopWithNc 2>&1 assert_success assert_line --index 0 --partial "INFO - waitForIt - using algorithm whileLoopWithNc" @@ -347,7 +347,7 @@ function Utils::waitForIt::algo::whileLoopWithNc::ExecCommand { #@test echo 'exit 0' ) >"${HOME}/bin/nc" chmod +x "${HOME}/bin/nc" - run "${binDir}/waitForIt" --host localhost --port 8888 --timeout 1 --algo whileLoopWithNc echo "success" 2>&1 + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo whileLoopWithNc echo "success" 2>&1 assert_success assert_line --index 0 --partial "INFO - waitForIt - using algorithm whileLoopWithNc" @@ -363,7 +363,7 @@ function Utils::waitForIt::algo::whileLoopWithNc::NoCommandExecutedIfFailed { #@ echo 'exit 1' ) >"${HOME}/bin/nc" chmod +x "${HOME}/bin/nc" - run "${binDir}/waitForIt" --host localhost --port 8888 --timeout 1 --strict --algo whileLoopWithNc echo "success" 2>&1 + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --strict --algo whileLoopWithNc echo "success" 2>&1 assert_failure 2 assert_line --index 0 --partial "INFO - waitForIt - using algorithm whileLoopWithNc" diff --git a/src/_binaries/commandDefinitions/optionsTimeout.sh b/src/_binaries/commandDefinitions/optionsTimeout.sh new file mode 100755 index 00000000..a8a3093b --- /dev/null +++ b/src/_binaries/commandDefinitions/optionsTimeout.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +optionTimeoutCallback() { + # shellcheck disable=SC2154 + if [[ ! "${optionTimeout}" =~ ^[0-9]+$ ]]; then + Log::fatal "${SCRIPT_NAME} - invalid timeout option - must be greater or equal to 0" + fi +} diff --git a/src/_binaries/commandDefinitions/optionsTimeout.yaml b/src/_binaries/commandDefinitions/optionsTimeout.yaml new file mode 100644 index 00000000..eba7f017 --- /dev/null +++ b/src/_binaries/commandDefinitions/optionsTimeout.yaml @@ -0,0 +1,21 @@ +--- +binData: + commands: + default: + definitionFiles: + 27: "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsTimeout.sh" + optionGroups: + groupTimeoutOptions: + title: "TIMEOUT OPTIONS:" + options: + - variableName: optionTimeout + group: groupTimeoutOptions + type: String + help: Timeout in seconds, zero for no timeout. + helpValueName: timeout + defaultValue: 15 + callbacks: + - optionTimeoutCallback + alts: + - --timeout + - -t diff --git a/src/_binaries/options/options.timeout.tpl b/src/_binaries/options/options.timeout.tpl deleted file mode 100644 index 75d0c12f..00000000 --- a/src/_binaries/options/options.timeout.tpl +++ /dev/null @@ -1,26 +0,0 @@ -% -declare defaultTimeout="15" -# shellcheck source=/dev/null -source <( - optionTimeoutCallback() { :; } - Options::generateOption \ - --help-value-name "timeout" \ - --help "Timeout in seconds, zero for no timeout." \ - --default-value "${defaultTimeout}" \ - --alt "--timeout" \ - --alt "-t" \ - --variable-type "String" \ - --variable-name "optionTimeout" \ - --function-name optionTimeoutFunction \ - --callback optionTimeoutCallback -) -options+=( - optionTimeoutFunction -) -% - -optionTimeoutCallback() { - if [[ ! "${optionTimeout}" =~ ^[0-9]+$ ]]; then - Log::fatal "${SCRIPT_NAME} - invalid timeout option - must be greater or equal to 0" - fi -} From 2bd43f7d97dc47dffc69c060cd021bb917bdf809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Sat, 7 Sep 2024 19:44:23 +0200 Subject: [PATCH 18/24] compiled waitForMysql using go compiler --- bin/waitForIt | 82 +- bin/waitForMysql | 2611 ++++++++++------- .../Utils/testsData/waitForIt.help.txt | 90 - .../Utils/testsData/waitForMysql.help.txt | 76 - .../waitForIt/testsData/waitForIt.help.txt | 10 +- .../Utils/waitForIt/waitForIt-binary.yaml | 9 - .../Utils/waitForIt/waitForIt-main.sh | 4 +- src/_binaries/Utils/waitForIt/waitForIt.bats | 63 +- src/_binaries/Utils/waitForMysql.options.tpl | 59 - src/_binaries/Utils/waitForMysql.sh | 47 - .../testsData/waitForMysql.help.txt | 82 + .../waitForMysql/waitForMysql-binary.yaml | 71 + .../Utils/waitForMysql/waitForMysql-main.sh | 46 + .../waitForMysql/waitForMysql-options.sh | 25 + .../{ => waitForMysql}/waitForMysql.bats | 18 +- .../commandDefinitions/optionsTimeout.yaml | 9 + 16 files changed, 1823 insertions(+), 1479 deletions(-) delete mode 100644 src/_binaries/Utils/testsData/waitForIt.help.txt delete mode 100644 src/_binaries/Utils/testsData/waitForMysql.help.txt delete mode 100644 src/_binaries/Utils/waitForMysql.options.tpl delete mode 100755 src/_binaries/Utils/waitForMysql.sh create mode 100644 src/_binaries/Utils/waitForMysql/testsData/waitForMysql.help.txt create mode 100644 src/_binaries/Utils/waitForMysql/waitForMysql-binary.yaml create mode 100755 src/_binaries/Utils/waitForMysql/waitForMysql-main.sh create mode 100755 src/_binaries/Utils/waitForMysql/waitForMysql-options.sh rename src/_binaries/Utils/{ => waitForMysql}/waitForMysql.bats (80%) diff --git a/bin/waitForIt b/bin/waitForIt index e9734453..a286f4dd 100755 --- a/bin/waitForIt +++ b/bin/waitForIt @@ -1248,6 +1248,7 @@ optionVersionCallback() { # options variables initialization declare optionTimeout="15" +declare optionExecIfTimedOut="0" declare optionHelp="0" declare optionConfig="0" declare optionBashFrameworkConfig="" @@ -1263,7 +1264,6 @@ declare optionTheme="default" declare optionVersion="0" declare optionQuiet="0" declare optionAlgo="" -declare optionStrict="0" declare optionLegacy="0" # arguments variables initialization declare hostOrIpArg="" @@ -1276,6 +1276,9 @@ waitForItCommandParse() { optionTimeout="15" local -i options_parse_optionParsedCountOptionTimeout ((options_parse_optionParsedCountOptionTimeout = 0)) || true + optionExecIfTimedOut="0" + local -i options_parse_optionParsedCountOptionExecIfTimedOut + ((options_parse_optionParsedCountOptionExecIfTimedOut = 0)) || true optionHelp="0" local -i options_parse_optionParsedCountOptionHelp ((options_parse_optionParsedCountOptionHelp = 0)) || true @@ -1319,9 +1322,6 @@ waitForItCommandParse() { optionAlgo="" local -i options_parse_optionParsedCountOptionAlgo ((options_parse_optionParsedCountOptionAlgo = 0)) || true - optionStrict="0" - local -i options_parse_optionParsedCountOptionStrict - ((options_parse_optionParsedCountOptionStrict = 0)) || true optionLegacy="0" local -i options_parse_optionParsedCountOptionLegacy ((options_parse_optionParsedCountOptionLegacy = 0)) || true @@ -1365,6 +1365,20 @@ waitForItCommandParse() { ;; # Option 2/18 + # optionExecIfTimedOut alts --exec-command-on-timeout|--lax|-l + # type: Boolean min 0 max 1 + --exec-command-on-timeout | --lax | -l) + # shellcheck disable=SC2034 + optionExecIfTimedOut="1" + + if ((options_parse_optionParsedCountOptionExecIfTimedOut >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionExecIfTimedOut)) + ;; + + # Option 3/18 # optionHelp alts --help|-h # type: Boolean min 0 max 1 --help | -h) @@ -1380,7 +1394,7 @@ waitForItCommandParse() { ;; - # Option 3/18 + # Option 4/18 # optionConfig alts --config # type: Boolean min 0 max 1 --config) @@ -1394,7 +1408,7 @@ waitForItCommandParse() { ((++options_parse_optionParsedCountOptionConfig)) ;; - # Option 4/18 + # Option 5/18 # optionBashFrameworkConfig alts --bash-framework-config # type: String min 0 max 1 --bash-framework-config) @@ -1415,7 +1429,7 @@ waitForItCommandParse() { ;; - # Option 5/18 + # Option 6/18 # optionInfoVerbose alts --verbose|-v # type: Boolean min 0 max 1 --verbose | -v) @@ -1433,7 +1447,7 @@ waitForItCommandParse() { ;; - # Option 6/18 + # Option 7/18 # optionDebugVerbose alts -vv # type: Boolean min 0 max 1 -vv) @@ -1451,7 +1465,7 @@ waitForItCommandParse() { ;; - # Option 7/18 + # Option 8/18 # optionTraceVerbose alts -vvv # type: Boolean min 0 max 1 -vvv) @@ -1469,7 +1483,7 @@ waitForItCommandParse() { ;; - # Option 8/18 + # Option 9/18 # optionEnvFiles alts --env-file # type: StringArray min 0 max -1 --env-file) @@ -1487,7 +1501,7 @@ waitForItCommandParse() { ;; - # Option 9/18 + # Option 10/18 # optionLogLevel alts --log-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1515,7 +1529,7 @@ waitForItCommandParse() { ;; - # Option 10/18 + # Option 11/18 # optionLogFile alts --log-file # type: String min 0 max 1 --log-file) @@ -1538,7 +1552,7 @@ waitForItCommandParse() { ;; - # Option 11/18 + # Option 12/18 # optionDisplayLevel alts --display-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1566,7 +1580,7 @@ waitForItCommandParse() { ;; - # Option 12/18 + # Option 13/18 # optionNoColor alts --no-color # type: Boolean min 0 max 1 --no-color) @@ -1584,7 +1598,7 @@ waitForItCommandParse() { ;; - # Option 13/18 + # Option 14/18 # optionTheme alts --theme # type: String min 0 max 1 # authorizedValues: default|default-force|noColor @@ -1612,7 +1626,7 @@ waitForItCommandParse() { ;; - # Option 14/18 + # Option 15/18 # optionVersion alts --version # type: Boolean min 0 max 1 --version) @@ -1628,7 +1642,7 @@ waitForItCommandParse() { ;; - # Option 15/18 + # Option 16/18 # optionQuiet alts --quiet|-q # type: Boolean min 0 max 1 --quiet | -q) @@ -1646,7 +1660,7 @@ waitForItCommandParse() { ;; - # Option 16/18 + # Option 17/18 # optionAlgo alts --algorithm|--algo # type: String min 0 max 1 --algorithm | --algo) @@ -1667,20 +1681,6 @@ waitForItCommandParse() { ;; - # Option 17/18 - # optionStrict alts --exec-command-on-success-only|--strict|-s - # type: Boolean min 0 max 1 - --exec-command-on-success-only | --strict | -s) - # shellcheck disable=SC2034 - optionStrict="1" - - if ((options_parse_optionParsedCountOptionStrict >= 1 )); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionStrict)) - ;; - # Option 18/18 # optionLegacy alts --user-nc # type: Boolean min 0 max 1 @@ -1818,7 +1818,7 @@ waitForItCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--timeout|-t ]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--algorithm|--algo ]" "[--exec-command-on-success-only|--strict|-s]" "[--user-nc]" + optionsAltList=("[--timeout|-t ]" "[--exec-command-on-timeout|--lax|-l]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--algorithm|--algo ]" "[--user-nc]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "waitForIt" "${optionsAltList[@]}" @@ -1854,6 +1854,12 @@ waitForItCommandHelp() { Array::wrap2 ' ' 76 6 " Default value: " "15" echo + + echo -e " ${__HELP_OPTION_COLOR}--exec-command-on-timeout${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}--lax${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-l${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Execute sub-command even if timeout is reached." + echo + + echo echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" @@ -1952,12 +1958,6 @@ waitForItCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--exec-command-on-success-only${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}--strict${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-s${__HELP_NORMAL} {single}" - Array::wrap2 ' ' 76 4 " " "Only execute sub-command if the test succeeds." - echo - - - echo -e " ${__HELP_OPTION_COLOR}--user-nc${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Legacy mode using nc command or while loop (uses timeout command by default)." echo @@ -2046,7 +2046,7 @@ whileLoop() { fi if ((optionTimeout != 0 && SECONDS - start_ts >= optionTimeout)); then if [[ "${reportTimeout}" = "1" ]]; then - Log::displayError "${SCRIPT_NAME} - timeout for ${hostOrIpArg}:${portArg} occurred after $((SECONDS - start_ts)) seconds" + Log::displayError "${SCRIPT_NAME} - timeout for ${hostOrIpArg}:${portArg} occurred after $((SECONDS - start_ts)) seconds > ${optionTimeout}" fi return 2 fi @@ -2162,7 +2162,7 @@ else "${algo}" || result=$? # when timed out, call command if any if [[ -n "${commandArgs+x}" && "${commandArgs[*]}" != "" ]]; then - if [[ "${result}" != "0" && "${optionStrict}" = "1" ]]; then + if [[ "${result}" != "0" && "${optionExecIfTimedOut}" = "0" ]]; then Log::displayError "${SCRIPT_NAME} - failed to connect - strict mode - command not executed" exit "${result}" fi diff --git a/bin/waitForMysql b/bin/waitForMysql index e3406742..db71355d 100755 --- a/bin/waitForMysql +++ b/bin/waitForMysql @@ -1,13 +1,12 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FACADE FROM https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Utils/waitForMysql.sh +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Utils/waitForMysql/waitForMysql-binary.yaml # DO NOT EDIT IT # @generated ############################################################################### # shellcheck disable=SC2288,SC2034 -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/waitForMysql -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE + + # ensure that no user aliases could interfere with # commands used in this script @@ -62,13 +61,6 @@ interruptManagement() { kill -s INT "$$" } trap interruptManagement INT -SCRIPT_NAME=${0##*/} -REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" -if [[ -n "${EMBED_CURRENT_DIR}" ]]; then - CURRENT_DIR="${EMBED_CURRENT_DIR}" -else - CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" -fi ################################################ # Temp dir management @@ -102,6 +94,20 @@ cleanOnExit() { } trap cleanOnExit EXIT HUP QUIT ABRT TERM + +SCRIPT_NAME=${0##*/} +REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" +if [[ -n "${EMBED_CURRENT_DIR}" ]]; then + CURRENT_DIR="${EMBED_CURRENT_DIR}" +else + CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" +fi +FRAMEWORK_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" +FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" +FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + # @description Log namespace provides 2 kind of functions # - Log::display* allows to display given message with # given display level @@ -132,168 +138,245 @@ export __VERBOSE_LEVEL_DEBUG=2 # @description verbose level info export __VERBOSE_LEVEL_TRACE=3 -# @description Display message using info color (bg light blue/fg white) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayInfo() { - local type="${2:-INFO}" - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description concatenate each element of an array with a separator +# but wrapping text when line length is more than provided argument +# The algorithm will try not to cut the array element if it can. +# - if an arg can be placed on current line it will be, +# otherwise current line is printed and arg is added to the new +# current line +# - Empty arg is interpreted as a new line. +# - Add \r to arg in order to force break line and avoid following +# arg to be concatenated with current arg. +# +# @arg $1 glue:String +# @arg $2 maxLineLength:int +# @arg $3 indentNextLine:int +# @arg $@ array:String[] +Array::wrap2() { + local glue="${1-}" + local -i glueLength="${#glue}" + shift || true + local -i maxLineLength=$1 + shift || true + local -i indentNextLine=$1 + shift || true + local indentStr="" + if ((indentNextLine > 0)); then + indentStr="$(head -c "${indentNextLine}" 0)); do + arg="$1" + shift || true + + # replace tab by 2 spaces + arg="${arg//$'\t'/ }" + # remove trailing spaces + arg="${arg%[[:blank:]]}" + if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then + printCurrentLine + ((previousLineEmpty = 1)) + continue + else + if ((previousLineEmpty == 1)); then + printCurrentLine + fi + ((previousLineEmpty = 0)) || true + fi + # convert eol to args + mapfile -t additionalLines <<<"${arg}" + if ((${#additionalLines[@]} > 1)); then + set -- "${additionalLines[@]}" "$@" + continue + fi + + ((argLength = ${#arg})) || true + + # empty arg + if ((argLength == 0)); then + if ((isNewline == 0)); then + # isNewline = 0 means currentLine is not empty + printCurrentLine + fi + continue + fi + + if ((isNewline == 0)); then + glueLength="${#glue}" + else + glueLength="0" + fi + if ((currentLineLength + argLength + glueLength > maxLineLength)); then + if ((argLength + glueLength > maxLineLength)); then + # arg is too long to even fit on one line + # we have to split the arg on current and next line + local -i remainingLineLength + ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) + appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" + printCurrentLine + arg="${arg:${remainingLineLength}}" + # remove leading spaces + arg="${arg##[[:blank:]]}" + + set -- "${arg}" "$@" + else + # the arg can fit on next line + printCurrentLine + appendToCurrentLine "${arg}" "${argLength}" + fi + else + appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" + fi + done + if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then + printCurrentLine + fi + ) | sed -E -e 's/[[:blank:]]+$//' } -# @description Display message using debug color (gray) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayDebug() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then - Log::computeDuration - echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description check if command specified exists or return 1 +# with error and message if not +# +# @arg $1 commandName:String on which existence must be checked +# @arg $2 helpIfNotExists:String a help command to display if the command does not exist +# +# @exitcode 1 if the command specified does not exist +# @stderr diagnostic information + help if second argument is provided +Assert::commandExists() { + local commandName="$1" + local helpIfNotExists="$2" + + "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { + Log::displayError "${commandName} is not installed, please install it" + if [[ -n "${helpIfNotExists}" ]]; then + Log::displayInfo "${helpIfNotExists}" + fi + return 1 + } + return 0 +} + + +# @description check if tty (interactive mode) is active +# @noargs +# @exitcode 1 if tty not active +# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive +# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive +Assert::tty() { + if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then + return 1 fi - Log::logDebug "$1" + if [[ "${INTERACTIVE:-0}" = "1" ]]; then + return 0 + fi + tty -s } -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description ignore exit code 141 from simple command pipes +# @example use with: +# local resultingStatus=0 +# local -a originalPipeStatus=() +# cmd1 | cmd2 || Bash::handlePipelineFailure resultingStatus originalPipeStatus || true +# [[ "${resultingStatus}" = "0" ]] +# @arg $1 resultingStatusCode:&int (passed by reference) (optional) resulting status code +# @arg $2 originalStatus:int[] (passed by reference) (optional) copy of original PIPESTATUS array +# @env PIPESTATUS assuming that this function is called like in the example provided +# @see https://unix.stackexchange.com/a/709880/582856 +Bash::handlePipelineFailure() { + local -a pipeStatusBackup=("${PIPESTATUS[@]}") + local -n handlePipelineFailure_resultingStatusCode=$1 + local -n handlePipelineFailure_originalStatus=$2 + # shellcheck disable=SC2034 + handlePipelineFailure_originalStatus=("${pipeStatusBackup[@]}") + handlePipelineFailure_resultingStatusCode=0 + local statusCode + for statusCode in "${pipeStatusBackup[@]}"; do + if ((statusCode == 141)); then + return 0 + elif ((statusCode > 0)); then + # shellcheck disable=SC2034 + handlePipelineFailure_resultingStatusCode="${statusCode}" + break + fi + done + return "${handlePipelineFailure_resultingStatusCode}" +} + + +# @description run command specified +# @arg $@ array:String[] the command to run +# @env optionInfoVerbose int - if 1 displays the command specified before running it +# @env optionRedirectCmdOutputs String - if set redirect command outputs to file specified +# @exitcode command's exit code +BashTools::runVerboseIfNeeded() { + # shellcheck disable=SC2154 + if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_INFO)); then + echo >&2 "+ $*" fi - Log::logWarning "$1" + ( + # shellcheck disable=SC2154 + if [[ -n "${optionRedirectCmdOutputs:-}" ]]; then + exec >"${optionRedirectCmdOutputs:-}" + exec 2>"${optionRedirectCmdOutputs:-}" + fi + "$@" + ) } -# @description Display message using error color (red) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayError() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then - Log::computeDuration - echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description check if all requirements are satisfied +# to execute dbImport commands +Db::checkRequirements() { + if [[ "${SKIP_REQUIREMENTS_CHECKS:-0}" = "1" ]]; then + return 0 fi - Log::logError "$1" + local -i failures=0 + echo + Assert::commandExists mysql "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqldump "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists pv "sudo apt-get install -y pv" || ((++failures)) + Assert::commandExists gawk "sudo apt-get install -y gawk" || ((++failures)) + Assert::commandExists awk "sudo apt-get install -y gawk" || ((++failures)) + Version::checkMinimal "gawk" "--version" "5.0.1" || ((++failures)) + return "${failures}" } -# @description load colors theme constants -# @warning if tty not opened, noColor theme will be chosen -# @arg $1 theme:String the theme to use (default, noColor) -# @arg $@ args:String[] -# @set __ERROR_COLOR String indicate error status -# @set __INFO_COLOR String indicate info status -# @set __SUCCESS_COLOR String indicate success status -# @set __WARNING_COLOR String indicate warning status -# @set __SKIPPED_COLOR String indicate skipped status -# @set __DEBUG_COLOR String indicate debug status -# @set __HELP_COLOR String indicate help status -# @set __TEST_COLOR String not used -# @set __TEST_ERROR_COLOR String not used -# @set __HELP_TITLE_COLOR String used to display help title in help strings -# @set __HELP_OPTION_COLOR String used to display highlight options in help strings -# -# @set __RESET_COLOR String reset default color -# -# @set __HELP_EXAMPLE String to remove -# @set __HELP_TITLE String to remove -# @set __HELP_NORMAL String to remove -# shellcheck disable=SC2034 -UI::theme() { - local theme="${1-default}" - if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then - theme="noColor" - fi - case "${theme}" in - default | default-force) - theme="default" - ;; - noColor) ;; - *) - Log::fatal "invalid theme provided" - ;; - esac - if [[ "${theme}" = "default" ]]; then - BASH_FRAMEWORK_THEME="default" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='\e[31m' # Red - __INFO_COLOR='\e[44m' # white on lightBlue - __SUCCESS_COLOR='\e[32m' # Green - __WARNING_COLOR='\e[33m' # Yellow - __SKIPPED_COLOR='\e[33m' # Yellow - __DEBUG_COLOR='\e[37m' # Gray - __HELP_COLOR='\e[7;49;33m' # Black on Gold - __TEST_COLOR='\e[100m' # Light magenta - __TEST_ERROR_COLOR='\e[41m' # white on red - __HELP_TITLE_COLOR="\e[1;37m" # Bold - __HELP_OPTION_COLOR="\e[1;34m" # Blue - # Internal: reset color - __RESET_COLOR='\e[0m' # Reset Color - # shellcheck disable=SC2155,SC2034 - __HELP_EXAMPLE="$(echo -e "\e[2;97m")" - # shellcheck disable=SC2155,SC2034 - __HELP_TITLE="$(echo -e "\e[1;37m")" - # shellcheck disable=SC2155,SC2034 - __HELP_NORMAL="$(echo -e "\033[0m")" - else - BASH_FRAMEWORK_THEME="noColor" - # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting - __ERROR_COLOR='' - __INFO_COLOR='' - __SUCCESS_COLOR='' - __WARNING_COLOR='' - __SKIPPED_COLOR='' - __DEBUG_COLOR='' - __HELP_COLOR='' - __TEST_COLOR='' - __TEST_ERROR_COLOR='' - __HELP_TITLE_COLOR='' - __HELP_OPTION_COLOR='' - # Internal: reset color - __RESET_COLOR='' - __HELP_EXAMPLE='' - __HELP_TITLE='' - __HELP_NORMAL='' - fi -} - -# @description draw a line with the character passed in parameter repeated depending on terminal width -# @arg $1 character:String character to use as separator (default value #) -UI::drawLine() { - local character="${1:-#}" - local -i width=${COLUMNS:-0} - if ((width == 0)) && [[ -t 1 ]]; then - width=$(tput cols) - fi - if ((width == 0)); then - width=80 - fi - printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") - echo -} - -# @description Display message using error color (red) and exit immediately with error status 1 -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::fatal() { - Log::computeDuration - echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - Log::logFatal "$1" - exit 1 -} - -# @description create a temp file using default TMPDIR variable -# initialized in _includes/_commonHeader.sh -# @env TMPDIR String (default value /tmp) -# @arg $1 templateName:String template name to use(optional) -Framework::createTempFile() { - mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" -} # @description ensure env files are loaded # @arg $@ list of default files to load at the end @@ -301,6 +384,8 @@ Framework::createTempFile() { # @stderr diagnostics information is displayed # shellcheck disable=SC2120 Env::requireLoad() { + export REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 + local -a defaultFiles=("$@") # get list of possible config files local -a configFiles=() @@ -328,357 +413,119 @@ Env::requireLoad() { done } -# @description activate or not Log::display* and Log::log* functions -# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL -# environment variables loaded by Env::requireLoad -# try to create log file and rotate it if necessary -# @noargs -# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable -# @env BASH_FRAMEWORK_DISPLAY_LEVEL int -# @env BASH_FRAMEWORK_LOG_LEVEL int -# @env BASH_FRAMEWORK_LOG_FILE String -# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 -# @exitcode 0 always successful -# @stderr diagnostics information about log file is displayed -# @require Env::requireLoad -# @require UI::requireTheme -Log::requireLoad() { - if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - export BASH_FRAMEWORK_LOG_LEVEL - fi - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then - if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 - fi - elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - fi +# @description create a temp file using default TMPDIR variable +# @env TMPDIR String (default value /tmp) +# @arg $1 templateName:String template name to use(optional) +Framework::createTempFile() { + mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" +} - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - # will always be created even if not in info level - Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" - if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then - Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + +declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR +FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" +LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" +LOG_LAST_LOG_DATE_INIT=1 +LOG_LAST_DURATION_STR="" + +# @description compute duration since last call to this function +# the result is set in following env variables. +# in ss.sss (seconds followed by milliseconds precision 3 decimals) +# @noargs +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @set LOG_LAST_LOG_DATE_INIT int (default 1) set to 0 at first call, allows to detect reference log +# @set LOG_LAST_DURATION_STR String the last duration displayed +# @set LOG_LAST_LOG_DATE String the last log date that will be used to compute next diff +Log::computeDuration() { + if ((${DISPLAY_DURATION:-0} == 1)); then + local -i duration=0 + local -i delta=0 + local -i currentLogDate + currentLogDate="${EPOCHREALTIME/[^0-9]/}" + if ((LOG_LAST_LOG_DATE_INIT == 1)); then + LOG_LAST_LOG_DATE_INIT=0 + LOG_LAST_DURATION_STR="Ref" + else + duration=$(((currentLogDate - FIRST_LOG_DATE) / 1000000)) + delta=$(((currentLogDate - LOG_LAST_LOG_DATE) / 1000000)) + LOG_LAST_DURATION_STR="${duration}s/+${delta}s" fi + LOG_LAST_LOG_DATE="${currentLogDate}" + # shellcheck disable=SC2034 + local microSeconds="${EPOCHREALTIME#*.}" + LOG_LAST_DURATION_STR="$(printf '%(%T)T.%03.0f\n' "${EPOCHSECONDS}" "${microSeconds:0:3}")(${LOG_LAST_DURATION_STR}) - " + else + # shellcheck disable=SC2034 + LOG_LAST_DURATION_STR="" fi } -# @description concatenate each element of an array with a separator -# but wrapping text when line length is more than provided argument -# The algorithm will try not to cut the array element if it can. -# - if an arg can be placed on current line it will be, -# otherwise current line is printed and arg is added to the new -# current line -# - Empty arg is interpreted as a new line. -# - Add \r to arg in order to force break line and avoid following -# arg to be concatenated with current arg. -# -# @arg $1 glue:String -# @arg $2 maxLineLength:int -# @arg $3 indentNextLine:int -# @arg $@ array:String[] -Array::wrap2() { - local glue="${1-}" - local -i glueLength="${#glue}" - shift || true - local -i maxLineLength=$1 - shift || true - local -i indentNextLine=$1 - shift || true - local indentStr="" - if ((indentNextLine > 0)); then - indentStr="$(head -c "${indentNextLine}" = __LEVEL_DEBUG)); then + Log::computeDuration + echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logDebug "$1" +} - printCurrentLine() { - if ((isNewline == 0)) || ((previousLineEmpty == 1)); then - echo - fi - ((isNewline = 1)) - echo -en "${indentStr}" - ((currentLineLength = indentNextLine)) || true - } - appendToCurrentLine() { - local text="$1" - local -i length=$2 - ((currentLineLength += length)) || true - ((isNewline = 0)) || true - if [[ "${text: -1}" = $'\r' ]]; then - text="${text:0:-1}" - echo -en "${text%%+([[:blank:]])}" - printCurrentLine - else - echo -en "${text%%+([[:blank:]])}" - fi - } - ( - local currentLine - local -i currentLineLength=0 isNewline=1 argLength=0 - local -a additionalLines - local -i previousLineEmpty=0 - local arg="" +# @description Display message using error color (red) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayError() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then + Log::computeDuration + echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logError "$1" +} - while (($# > 0)); do - arg="$1" - shift || true - # replace tab by 2 spaces - arg="${arg//$'\t'/ }" - # remove trailing spaces - arg="${arg%[[:blank:]]}" - if [[ "${arg}" = $'\n' || -z "${arg}" ]]; then - printCurrentLine - ((previousLineEmpty = 1)) - continue - else - if ((previousLineEmpty == 1)); then - printCurrentLine - fi - ((previousLineEmpty = 0)) || true - fi - # convert eol to args - mapfile -t additionalLines <<<"${arg}" - if ((${#additionalLines[@]} > 1)); then - set -- "${additionalLines[@]}" "$@" - continue - fi - - ((argLength = ${#arg})) || true - - # empty arg - if ((argLength == 0)); then - if ((isNewline == 0)); then - # isNewline = 0 means currentLine is not empty - printCurrentLine - fi - continue - fi - - if ((isNewline == 0)); then - glueLength="${#glue}" - else - glueLength="0" - fi - if ((currentLineLength + argLength + glueLength > maxLineLength)); then - if ((argLength + glueLength > maxLineLength)); then - # arg is too long to even fit on one line - # we have to split the arg on current and next line - local -i remainingLineLength - ((remainingLineLength = maxLineLength - currentLineLength - glueLength)) - appendToCurrentLine "${glue:0:${glueLength}}${arg:0:${remainingLineLength}}" "$((glueLength + remainingLineLength))" - printCurrentLine - arg="${arg:${remainingLineLength}}" - # remove leading spaces - arg="${arg##[[:blank:]]}" - - set -- "${arg}" "$@" - else - # the arg can fit on next line - printCurrentLine - appendToCurrentLine "${arg}" "${argLength}" - fi - else - appendToCurrentLine "${glue:0:${glueLength}}${arg}" "$((glueLength + argLength))" - fi - done - if [[ "${currentLine}" != "" ]] && [[ ! "${currentLine}" =~ ^[\ \t]+$ ]]; then - printCurrentLine - fi - ) | sed -E -e 's/[[:blank:]]+$//' -} - -# @description check if command specified exists or return 1 -# with error and message if not -# -# @arg $1 commandName:String on which existence must be checked -# @arg $2 helpIfNotExists:String a help command to display if the command does not exist -# -# @exitcode 1 if the command specified does not exist -# @stderr diagnostic information + help if second argument is provided -Assert::commandExists() { - local commandName="$1" - local helpIfNotExists="$2" - - "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { - Log::displayError "${commandName} is not installed, please install it" - if [[ -n "${helpIfNotExists}" ]]; then - Log::displayInfo "${helpIfNotExists}" - fi - return 1 - } - return 0 -} - -bashToolsDefaultConfigTemplate="${bashToolsDefaultConfigTemplate:-$( - cat <<'EOF' -# shellcheck disable=SC2034 - -# Default settings -# you can override these settings by creating ${HOME}/.bash-tools/.env file - -### -### DISPLAY Level -### minimum level of the messages that will be displayed on screen -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} - -### -### DISPLAY duration -### 0: no duration is displayed on the messages -### 1: duration between previous message and current is displayed -### with the message -### -DISPLAY_DURATION=${DISPLAY_DURATION:0} - -### -### Log to file -### -### all log messages will be redirected to log file specified -### this same path will be used inside and outside of the container -### -BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} - -### -### LOG Level -### minimum level of the messages that will be logged into LOG_FILE -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} - -# absolute directory containing db import sql dumps -DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} - -# garbage collect all files for which modification is greater than eg: 30 days (+30) -# each time an existing file is used by dbImport/dbImportTable -# the file modification time is set to now -DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} - -# absolute directory containing dbScripts used by dbScriptAllDatabases -SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} - -# ----------------------------------------------------- -# AWS Parameters -# ----------------------------------------------------- -S3_BASE_URL=${S3_BASE_URL:-} - -# ----------------------------------------------------- -# Postman Parameters -# ----------------------------------------------------- -POSTMAN_API_KEY= -EOF -)}" - -# @description loads ~/.bash-tools/.env if available -# if not creates it from a default template -# else check if new options need to be added -BashTools::Conf::requireLoad() { - local envFile="${HOME}/.bash-tools/.env" - if [[ ! -f "${envFile}" ]]; then - mkdir -p "${HOME}/.bash-tools" - ( - echo "#!/usr/bin/env bash" - echo "${bashToolsDefaultConfigTemplate}" - ) >"${envFile}" - Log::displayInfo "Configuration file '${envFile}' created" - else - if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then - ( - echo '# -----------------------------------------------------' - echo '# Postman Parameters' - echo '# -----------------------------------------------------' - echo 'POSTMAN_API_KEY=' - ) >>"${envFile}" - fi +# @description Display message using info color (bg light blue/fg white) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayInfo() { + local type="${2:-INFO}" + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi - # shellcheck source=/conf/.env - source "${envFile}" || { - Log::displayError "impossible to load '${envFile}'" - exit 1 - } -} - -# @description ensure COMMAND_BIN_DIR env var is set -# and PATH correctly prepared -# @noargs -# @set COMMAND_BIN_DIR string the directory where to find this command -# @set PATH string add directory where to find this command binary -Compiler::Facade::requireCommandBinDir() { - COMMAND_BIN_DIR="${CURRENT_DIR}" - Env::pathPrepend "${COMMAND_BIN_DIR}" + Log::logInfo "$1" "${type}" } -declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR -FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" -LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" -LOG_LAST_LOG_DATE_INIT=1 -LOG_LAST_DURATION_STR="" -# @description compute duration since last call to this function -# the result is set in following env variables. -# in ss.sss (seconds followed by milliseconds precision 3 decimals) -# @noargs +# @description Display message using warning color (yellow) +# @arg $1 message:String the message to display # @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @set LOG_LAST_LOG_DATE_INIT int (default 1) set to 0 at first call, allows to detect reference log -# @set LOG_LAST_DURATION_STR String the last duration displayed -# @set LOG_LAST_LOG_DATE String the last log date that will be used to compute next diff -Log::computeDuration() { - if ((${DISPLAY_DURATION:-0} == 1)); then - local -i duration=0 - local -i delta=0 - local -i currentLogDate - currentLogDate="${EPOCHREALTIME/[^0-9]/}" - if ((LOG_LAST_LOG_DATE_INIT == 1)); then - LOG_LAST_LOG_DATE_INIT=0 - LOG_LAST_DURATION_STR="Ref" - else - duration=$(((currentLogDate - FIRST_LOG_DATE) / 1000000)) - delta=$(((currentLogDate - LOG_LAST_LOG_DATE) / 1000000)) - LOG_LAST_DURATION_STR="${duration}s/+${delta}s" - fi - LOG_LAST_LOG_DATE="${currentLogDate}" - # shellcheck disable=SC2034 - local microSeconds="${EPOCHREALTIME#*.}" - LOG_LAST_DURATION_STR="$(printf '%(%T)T.%03.0f\n' "${EPOCHSECONDS}" "${microSeconds:0:3}")(${LOG_LAST_DURATION_STR}) - " - else - # shellcheck disable=SC2034 - LOG_LAST_DURATION_STR="" +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayWarning() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then + Log::computeDuration + echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logWarning "$1" } -# @description log message to file + +# @description Display message using error color (red) and exit immediately with error status 1 # @arg $1 message:String the message to display -Log::logInfo() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then - Log::logMessage "${2:-INFO}" "$1" - fi +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::fatal() { + Log::computeDuration + echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + Log::logFatal "$1" + exit 1 } + # @description log message to file # @arg $1 message:String the message to display Log::logDebug() { @@ -687,13 +534,6 @@ Log::logDebug() { fi } -# @description log message to file -# @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" - fi -} # @description log message to file # @arg $1 message:String the message to display @@ -703,20 +543,6 @@ Log::logError() { fi } -# @description check if tty (interactive mode) is active -# @noargs -# @exitcode 1 if tty not active -# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive -# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive -Assert::tty() { - if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then - return 1 - fi - if [[ "${INTERACTIVE:-0}" = "1" ]]; then - return 0 - fi - tty -s -} # @description log message to file # @arg $1 message:String the message to display @@ -724,6 +550,16 @@ Log::logFatal() { Log::logMessage "${2:-FATAL}" "$1" } + +# @description log message to file +# @arg $1 message:String the message to display +Log::logInfo() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then + Log::logMessage "${2:-INFO}" "$1" + fi +} + + # @description Internal: common log message # @example text # [date]|[levelMsg]|message @@ -737,9 +573,18 @@ Log::logFatal() { # @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty # @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages # @stderr diagnostics information is displayed -# @require Env::requireLoad -# @require Log::requireLoad Log::logMessage() { + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Log::requireLoad has not been loaded" + exit 1 + fi + local levelMsg="$1" local msg="$2" local date @@ -751,98 +596,312 @@ Log::logMessage() { fi } -# @description To be called before logging in the log file -# @arg $1 file:string log file name -# @arg $2 maxLogFilesCount:int maximum number of log files -Log::rotate() { - local file="$1" - local maxLogFilesCount="${2:-5}" - if [[ ! -f "${file}" ]]; then - Log::displayDebug "Log file ${file} doesn't exist yet" - return 0 - fi - local i - for ((i = maxLogFilesCount - 1; i > 0; i--)); do - Log::displayInfo "Log rotation ${file}.${i} to ${file}.$((i + 1))" - mv "${file}."{"${i}","$((i + 1))"} &>/dev/null || true - done - if cp "${file}" "${file}.1" &>/dev/null; then - echo >"${file}" # reset log file - Log::displayInfo "Log rotation ${file} to ${file}.1" +# @description log message to file +# @arg $1 message:String the message to display +Log::logWarning() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then + Log::logMessage "${2:-WARNING}" "$1" fi } -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" - fi - done -} -# @description load color theme +# @description activate or not Log::display* and Log::log* functions +# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL +# environment variables loaded by Env::requireLoad +# try to create log file and rotate it if necessary # @noargs -# @env BASH_FRAMEWORK_THEME String theme to use -# @env LOAD_THEME int 0 to avoid loading theme +# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable +# @env BASH_FRAMEWORK_DISPLAY_LEVEL int +# @env BASH_FRAMEWORK_LOG_LEVEL int +# @env BASH_FRAMEWORK_LOG_FILE String +# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 # @exitcode 0 always successful -UI::requireTheme() { - if [[ "${LOAD_THEME:-1}" = "1" ]]; then - UI::theme "${BASH_FRAMEWORK_THEME-default}" - fi -} +# @stderr diagnostics information about log file is displayed +Log::requireLoad() { + export REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 -# FUNCTIONS -facade_main_waitForMysqlsh() { -BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" -if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then - FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" -else - # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR - FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" -fi -FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" -FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi -# @require BashTools::Conf::requireLoad -if [[ -f "${HOME}/.bash-tools/.env" ]]; then - export BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") -fi -# REQUIRES -Env::requireLoad -UI::requireTheme -Log::requireLoad -BashTools::Conf::requireLoad -Compiler::Facade::requireCommandBinDir - -# @require Compiler::Facade::requireCommandBinDir -# shellcheck disable=SC2034 + if [[ "${REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement UI::requireTheme has not been loaded" + exit 1 + fi + + if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + export BASH_FRAMEWORK_LOG_LEVEL + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then + if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 + fi + elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + # will always be created even if not in info level + Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" + if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then + Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + fi + fi +} + + +# @description To be called before logging in the log file +# @arg $1 file:string log file name +# @arg $2 maxLogFilesCount:int maximum number of log files +Log::rotate() { + local file="$1" + local maxLogFilesCount="${2:-5}" + + if [[ ! -f "${file}" ]]; then + Log::displayDebug "Log file ${file} doesn't exist yet" + return 0 + fi + local i + for ((i = maxLogFilesCount - 1; i > 0; i--)); do + Log::displayInfo "Log rotation ${file}.${i} to ${file}.$((i + 1))" + mv "${file}."{"${i}","$((i + 1))"} &>/dev/null || true + done + if cp "${file}" "${file}.1" &>/dev/null; then + echo >"${file}" # reset log file + Log::displayInfo "Log rotation ${file} to ${file}.1" + fi +} + + +# @description draw a line with the character passed in parameter repeated depending on terminal width +# @arg $1 character:String character to use as separator (default value #) +UI::drawLine() { + local character="${1:-#}" + local -i width=${COLUMNS:-0} + if ((width == 0)) && [[ -t 1 ]]; then + width=$(tput cols) + fi + if ((width == 0)); then + width=80 + fi + printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") + echo +} + + +# @description load color theme +# @noargs +# @env BASH_FRAMEWORK_THEME String theme to use +# @env LOAD_THEME int 0 to avoid loading theme +# @exitcode 0 always successful +UI::requireTheme() { + export REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 + + if [[ "${LOAD_THEME:-1}" = "1" ]]; then + UI::theme "${BASH_FRAMEWORK_THEME-default}" + fi +} + + +# @description load colors theme constants +# @warning if tty not opened, noColor theme will be chosen +# @arg $1 theme:String the theme to use (default, noColor) +# @arg $@ args:String[] +# @set __ERROR_COLOR String indicate error status +# @set __INFO_COLOR String indicate info status +# @set __SUCCESS_COLOR String indicate success status +# @set __WARNING_COLOR String indicate warning status +# @set __SKIPPED_COLOR String indicate skipped status +# @set __DEBUG_COLOR String indicate debug status +# @set __HELP_COLOR String indicate help status +# @set __TEST_COLOR String not used +# @set __TEST_ERROR_COLOR String not used +# @set __HELP_TITLE_COLOR String used to display help title in help strings +# @set __HELP_OPTION_COLOR String used to display highlight options in help strings +# +# @set __RESET_COLOR String reset default color +# +# @set __HELP_EXAMPLE String to remove +# @set __HELP_TITLE String to remove +# @set __HELP_NORMAL String to remove +# shellcheck disable=SC2034 +UI::theme() { + local theme="${1-default}" + if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then + theme="noColor" + fi + case "${theme}" in + default | default-force) + theme="default" + ;; + noColor) ;; + *) + Log::fatal "invalid theme provided" + ;; + esac + if [[ "${theme}" = "default" ]]; then + BASH_FRAMEWORK_THEME="default" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='\e[31m' # Red + __INFO_COLOR='\e[44m' # white on lightBlue + __SUCCESS_COLOR='\e[32m' # Green + __WARNING_COLOR='\e[33m' # Yellow + __SKIPPED_COLOR='\e[33m' # Yellow + __DEBUG_COLOR='\e[37m' # Gray + __HELP_COLOR='\e[7;49;33m' # Black on Gold + __TEST_COLOR='\e[100m' # Light magenta + __TEST_ERROR_COLOR='\e[41m' # white on red + __HELP_TITLE_COLOR="\e[1;37m" # Bold + __HELP_OPTION_COLOR="\e[1;34m" # Blue + # Internal: reset color + __RESET_COLOR='\e[0m' # Reset Color + # shellcheck disable=SC2155,SC2034 + __HELP_EXAMPLE="$(echo -e "\e[2;97m")" + # shellcheck disable=SC2155,SC2034 + __HELP_TITLE="$(echo -e "\e[1;37m")" + # shellcheck disable=SC2155,SC2034 + __HELP_NORMAL="$(echo -e "\033[0m")" + else + BASH_FRAMEWORK_THEME="noColor" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='' + __INFO_COLOR='' + __SUCCESS_COLOR='' + __WARNING_COLOR='' + __SKIPPED_COLOR='' + __DEBUG_COLOR='' + __HELP_COLOR='' + __TEST_COLOR='' + __TEST_ERROR_COLOR='' + __HELP_TITLE_COLOR='' + __HELP_OPTION_COLOR='' + # Internal: reset color + __RESET_COLOR='' + __HELP_EXAMPLE='' + __HELP_TITLE='' + __HELP_NORMAL='' + fi +} + + +# @description Check that command version is greater than expected minimal version +# display warning if command version greater than expected minimal version +# display error if command version less than expected minimal version and exit 1 +# @arg $1 commandName:String command path +# @arg $2 argVersion:String command line parameters to launch to get command version +# @arg $3 minimalVersion:String expected minimal command version +# @arg $4 parseVersionCallback:Function +# @arg $5 help:String optional help message to display if command does not exist +# @exitcode 0 if command version greater or equal to expected minimal version +# @exitcode 1 if command version less than expected minimal version +# @exitcode 2 if command does not exist +Version::checkMinimal() { + local commandName="$1" + local argVersion="$2" + local minimalVersion="$3" + local parseVersionCallback=${4:-Version::parse} + local help="${5:-}" + + Assert::commandExists "${commandName}" "${help}" || return 2 + + # shellcheck disable=SC2034 + local status=0 + # shellcheck disable=SC2034 + local -a pipeStatus=() + local version + version="$("${commandName}" "${argVersion}" 2>&1 | ${parseVersionCallback} || Bash::handlePipelineFailure status pipeStatus)" + + Log::displayDebug "check ${commandName} version ${version} against minimal ${minimalVersion}" + + Version::compare "${version}" "${minimalVersion}" || { + local result=$? + if [[ "${result}" = "1" ]]; then + Log::displayInfo "${commandName} version is ${version} greater than ${minimalVersion}" + elif [[ "${result}" = "2" ]]; then + Log::displayError "${commandName} minimal version is ${minimalVersion}, your version is ${version}" + return 1 + fi + return 0 + } + +} + + +# @description compare 2 version numbers +# @arg $1 version1:String version 1 +# @arg $2 version2:String version 2 +# @exitcode 0 if equal +# @exitcode 1 if version1 > version2 +# @exitcode 2 else +Version::compare() { + if [[ "$1" = "$2" ]]; then + return 0 + fi + local IFS=. + # shellcheck disable=2206 + local i ver1=($1) ver2=($2) + # fill empty fields in ver1 with zeros + for ((i = ${#ver1[@]}; i < ${#ver2[@]}; i++)); do + ver1[i]=0 + done + for ((i = 0; i < ${#ver1[@]}; i++)); do + if [[ -z "${ver2[i]+unset}" ]] || [[ -z ${ver2[i]} ]]; then + # fill empty fields in ver2 with zeros + ver2[i]=0 + fi + if ((10#${ver1[i]} > 10#${ver2[i]})); then + return 1 + fi + if ((10#${ver1[i]} < 10#${ver2[i]})); then + return 2 + fi + done + return 0 +} + + +# @description filter to keep only version number from a string +# @arg $@ files:String[] the files to filter +# @exitcode * if one of the filter command fails +# @stdin you can use stdin as alternative to files argument +# @stdout the filtered content +# shellcheck disable=SC2120 +Version::parse() { + # match anything, print(p), exit on first match(Q) + sed -En \ + -e 's/\x1b\[[0-9;]*[mGKHF]//g' \ + -e 's/[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/' \ + -e '//{p;Q}' \ + "$@" +} +# FUNCTIONS -# default values -declare defaultTimeout="15" -# option values -declare optionTimeout="${defaultTimeout}" -declare mysqlHostArg="" -declare mysqlPortArg="" -declare mysqlUserArg="" -declare mysqlPasswordArg="" -# default values -declare copyrightBeginYear="2020" declare -a BASH_FRAMEWORK_ARGV_FILTERED=() +beforeParseCallback() { + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + copyrightCallback() { - if [[ -z "${copyrightBeginYear}" ]]; then - copyrightBeginYear="$(date +%Y)" - fi - echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" + # + echo "Copyright (c) 2020-now François Chastanet" } # shellcheck disable=SC2317 # if function is overridden @@ -874,13 +933,14 @@ updateArgListQuietCallback() { :; } # shellcheck disable=SC2317 # if function is overridden optionHelpCallback() { - waitForMysqlCommand help + Log::displayError "optionHelpCallback needs to be overridden" exit 0 } # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { - echo "${SCRIPT_NAME} version 2.0" + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version 3.0" exit 0 } @@ -898,21 +958,22 @@ optionEnvFileCallback() { optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_INFO} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionDebugVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_DEBUG} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionTraceVerboseCallback() { + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vvv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_TRACE} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } getLevel() { @@ -936,6 +997,7 @@ getLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -957,6 +1019,7 @@ getVerboseLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -967,7 +1030,7 @@ optionDisplayLevelCallback() { logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -976,19 +1039,20 @@ optionLogLevelCallback() { local logLevel verboseLevel logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionLogFileCallback() { local logFile="$2" - echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionQuietCallback() { - echo "BASH_FRAMEWORK_QUIET_MODE=1" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_QUIET_MODE=1" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1019,6 +1083,7 @@ optionBashFrameworkConfigCallback() { defaultFrameworkConfig="$( cat <<'EOF' + # copied from src/_includes/.framework-config.default # shellcheck disable=SC2034 @@ -1036,7 +1101,7 @@ NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-conf # describe the files that are allowed to not have an associated bats file BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" # describe the files that are allowed to not have a function matching the filename -FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|\.tpl$|/testsData/|^manualTests/|\.bats$}" +FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|/testsData/|^manualTests/|\.bats$}" # Source directories if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then FRAMEWORK_SRC_DIRS=( @@ -1052,6 +1117,7 @@ BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" + EOF )" @@ -1060,7 +1126,7 @@ overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" commandOptionParseFinished() { # load default template framework config defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" - echo "${defaultFrameworkConfig}" > "${defaultEnvFile}" + echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") if [[ -f "${envFile}" ]]; then files+=("${envFile}") @@ -1078,557 +1144,872 @@ commandOptionParseFinished() { fi } + + +longDescriptionFunction() { + echo -e " ${__HELP_TITLE}EXIT STATUS CODES:${__HELP_NORMAL}" + echo -e " ${__HELP_OPTION_COLOR}0${__HELP_NORMAL}: mysql is available" + echo -e " ${__HELP_OPTION_COLOR}1${__HELP_NORMAL}: indicates mysql is not available or argument error" + echo -e " ${__HELP_OPTION_COLOR}2${__HELP_NORMAL}: timeout reached" +} + +optionHelpCallback() { + waitForMysqlCommandHelp + exit 0 +} + +mysqlPortArgCallback() { + # shellcheck disable=SC2154 + if [[ ! "${mysqlPortArg}" =~ ^[0-9]+$ ]] || ((mysqlPortArg == 0)); then + Log::fatal "${SCRIPT_NAME} - invalid port option - must be greater than to 0" + fi +} + +# shellcheck disable=SC2317 # if function is overridden +unknownOption() { + commandArgs+=("$1") +} + + + optionTimeoutCallback() { + # shellcheck disable=SC2154 if [[ ! "${optionTimeout}" =~ ^[0-9]+$ ]]; then Log::fatal "${SCRIPT_NAME} - invalid timeout option - must be greater or equal to 0" fi } -waitForMysqlCommand() { - local options_parse_cmd="$1" - shift || true - if [[ "${options_parse_cmd}" = "parse" ]]; then - optionTimeout="15" - local -i options_parse_optionParsedCountOptionTimeout - ((options_parse_optionParsedCountOptionTimeout = 0)) || true - local -i options_parse_optionParsedCountOptionBashFrameworkConfig - ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true - optionConfig="0" - local -i options_parse_optionParsedCountOptionConfig - ((options_parse_optionParsedCountOptionConfig = 0)) || true - optionInfoVerbose="0" - local -i options_parse_optionParsedCountOptionInfoVerbose - ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true - optionDebugVerbose="0" - local -i options_parse_optionParsedCountOptionDebugVerbose - ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true - optionTraceVerbose="0" - local -i options_parse_optionParsedCountOptionTraceVerbose - ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionNoColor="0" - local -i options_parse_optionParsedCountOptionNoColor - ((options_parse_optionParsedCountOptionNoColor = 0)) || true - optionTheme="default" - local -i options_parse_optionParsedCountOptionTheme - ((options_parse_optionParsedCountOptionTheme = 0)) || true - optionHelp="0" - local -i options_parse_optionParsedCountOptionHelp - ((options_parse_optionParsedCountOptionHelp = 0)) || true - optionVersion="0" - local -i options_parse_optionParsedCountOptionVersion - ((options_parse_optionParsedCountOptionVersion = 0)) || true - optionQuiet="0" - local -i options_parse_optionParsedCountOptionQuiet - ((options_parse_optionParsedCountOptionQuiet = 0)) || true - local -i options_parse_optionParsedCountOptionLogLevel - ((options_parse_optionParsedCountOptionLogLevel = 0)) || true - local -i options_parse_optionParsedCountOptionLogFile - ((options_parse_optionParsedCountOptionLogFile = 0)) || true - local -i options_parse_optionParsedCountOptionDisplayLevel - ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true - local -i options_parse_argParsedCountMysqlHostArg - ((options_parse_argParsedCountMysqlHostArg = 0)) || true - local -i options_parse_argParsedCountMysqlPortArg - ((options_parse_argParsedCountMysqlPortArg = 0)) || true - local -i options_parse_argParsedCountMysqlUserArg - ((options_parse_argParsedCountMysqlUserArg = 0)) || true - local -i options_parse_argParsedCountMysqlPasswordArg - ((options_parse_argParsedCountMysqlPasswordArg = 0)) || true - # shellcheck disable=SC2034 - local -i options_parse_parsedArgIndex=0 - while (($# > 0)); do - local options_parse_arg="$1" - local argOptDefaultBehavior=0 - case "${options_parse_arg}" in - # Option 1/15 - # Option optionTimeout --timeout|-t variableType String min 0 max 1 authorizedValues '' regexp '' - --timeout | -t) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionTimeout >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTimeout)) - # shellcheck disable=SC2034 - optionTimeout="$1" - optionTimeoutCallback "${options_parse_arg}" "${optionTimeout}" - ;; - # Option 2/15 - # Option optionBashFrameworkConfig --bash-framework-config variableType String min 0 max 1 authorizedValues '' regexp '' - --bash-framework-config) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) - # shellcheck disable=SC2034 - optionBashFrameworkConfig="$1" - optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" - ;; - # Option 3/15 - # Option optionConfig --config variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --config) - # shellcheck disable=SC2034 - optionConfig="1" - if ((options_parse_optionParsedCountOptionConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionConfig)) - ;; - # Option 4/15 - # Option optionInfoVerbose --verbose|-v variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --verbose | -v) - # shellcheck disable=SC2034 - optionInfoVerbose="1" - if ((options_parse_optionParsedCountOptionInfoVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionInfoVerbose)) - optionInfoVerboseCallback "${options_parse_arg}" - updateArgListInfoVerboseCallback "${options_parse_arg}" - ;; - # Option 5/15 - # Option optionDebugVerbose -vv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vv) - # shellcheck disable=SC2034 - optionDebugVerbose="1" - if ((options_parse_optionParsedCountOptionDebugVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDebugVerbose)) - optionDebugVerboseCallback "${options_parse_arg}" - updateArgListDebugVerboseCallback "${options_parse_arg}" - ;; - # Option 6/15 - # Option optionTraceVerbose -vvv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vvv) - # shellcheck disable=SC2034 - optionTraceVerbose="1" - if ((options_parse_optionParsedCountOptionTraceVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTraceVerbose)) - optionTraceVerboseCallback "${options_parse_arg}" - updateArgListTraceVerboseCallback "${options_parse_arg}" - ;; - # Option 7/15 - # Option optionEnvFiles --env-file variableType StringArray min 0 max -1 authorizedValues '' regexp '' - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - ;; - # Option 8/15 - # Option optionNoColor --no-color variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --no-color) - # shellcheck disable=SC2034 - optionNoColor="1" - if ((options_parse_optionParsedCountOptionNoColor >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionNoColor)) - optionNoColorCallback "${options_parse_arg}" - updateArgListNoColorCallback "${options_parse_arg}" - ;; - # Option 9/15 - # Option optionTheme --theme variableType String min 0 max 1 authorizedValues 'default|default-force|noColor' regexp '' - --theme) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default|default-force|noColor)" - return 1 - fi - if ((options_parse_optionParsedCountOptionTheme >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + +optionVersionCallback() { + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version 3.0" + Db::checkRequirements + exit 0 +} + + +# ------------------------------------------ +# Command waitForMysqlCommand +# ------------------------------------------ + +# options variables initialization +declare optionTimeout="15" +declare optionExecIfTimedOut="0" +declare optionHelp="0" +declare optionConfig="0" +declare optionBashFrameworkConfig="" +declare optionInfoVerbose="0" +declare optionDebugVerbose="0" +declare optionTraceVerbose="0" +declare -a optionEnvFiles=() +declare optionLogLevel="" +declare optionLogFile="" +declare optionDisplayLevel="" +declare optionNoColor="0" +declare optionTheme="default" +declare optionVersion="0" +declare optionQuiet="0" +# arguments variables initialization +declare mysqlHostArg="" +declare mysqlPortArg="" +declare mysqlUserArg="" +declare mysqlPasswordArg="" +declare -a commandArgs=() +# @description parse command options and arguments for waitForMysqlCommand +waitForMysqlCommandParse() { + Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" + Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" + optionTimeout="15" + local -i options_parse_optionParsedCountOptionTimeout + ((options_parse_optionParsedCountOptionTimeout = 0)) || true + optionExecIfTimedOut="0" + local -i options_parse_optionParsedCountOptionExecIfTimedOut + ((options_parse_optionParsedCountOptionExecIfTimedOut = 0)) || true + optionHelp="0" + local -i options_parse_optionParsedCountOptionHelp + ((options_parse_optionParsedCountOptionHelp = 0)) || true + optionConfig="0" + local -i options_parse_optionParsedCountOptionConfig + ((options_parse_optionParsedCountOptionConfig = 0)) || true + optionBashFrameworkConfig="" + local -i options_parse_optionParsedCountOptionBashFrameworkConfig + ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true + optionInfoVerbose="0" + local -i options_parse_optionParsedCountOptionInfoVerbose + ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true + optionDebugVerbose="0" + local -i options_parse_optionParsedCountOptionDebugVerbose + ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true + optionTraceVerbose="0" + local -i options_parse_optionParsedCountOptionTraceVerbose + ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true + + optionLogLevel="" + local -i options_parse_optionParsedCountOptionLogLevel + ((options_parse_optionParsedCountOptionLogLevel = 0)) || true + optionLogFile="" + local -i options_parse_optionParsedCountOptionLogFile + ((options_parse_optionParsedCountOptionLogFile = 0)) || true + optionDisplayLevel="" + local -i options_parse_optionParsedCountOptionDisplayLevel + ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true + optionNoColor="0" + local -i options_parse_optionParsedCountOptionNoColor + ((options_parse_optionParsedCountOptionNoColor = 0)) || true + optionTheme="default" + local -i options_parse_optionParsedCountOptionTheme + ((options_parse_optionParsedCountOptionTheme = 0)) || true + optionVersion="0" + local -i options_parse_optionParsedCountOptionVersion + ((options_parse_optionParsedCountOptionVersion = 0)) || true + optionQuiet="0" + local -i options_parse_optionParsedCountOptionQuiet + ((options_parse_optionParsedCountOptionQuiet = 0)) || true + + mysqlHostArg="" + local -i options_parse_argParsedCountMysqlHostArg + ((options_parse_argParsedCountMysqlHostArg = 0)) || true + + mysqlPortArg="" + local -i options_parse_argParsedCountMysqlPortArg + ((options_parse_argParsedCountMysqlPortArg = 0)) || true + + mysqlUserArg="" + local -i options_parse_argParsedCountMysqlUserArg + ((options_parse_argParsedCountMysqlUserArg = 0)) || true + + mysqlPasswordArg="" + local -i options_parse_argParsedCountMysqlPasswordArg + ((options_parse_argParsedCountMysqlPasswordArg = 0)) || true + + commandArgs=() + + + # shellcheck disable=SC2034 + local -i options_parse_parsedArgIndex=0 + while (($# > 0)); do + local options_parse_arg="$1" + local argOptDefaultBehavior=0 + case "${options_parse_arg}" in + # Option 1/16 + # optionTimeout alts --timeout|-t + # type: String min 0 max 1 + --timeout | -t) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionTimeout >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTimeout)) + # shellcheck disable=SC2034 + optionTimeout="$1" + optionTimeoutCallback "${options_parse_arg}" "${optionTimeout}" + + ;; + + # Option 2/16 + # optionExecIfTimedOut alts --exec-command-on-timeout|--lax|-l + # type: Boolean min 0 max 1 + --exec-command-on-timeout | --lax | -l) + # shellcheck disable=SC2034 + optionExecIfTimedOut="1" + + if ((options_parse_optionParsedCountOptionExecIfTimedOut >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionExecIfTimedOut)) + ;; + + # Option 3/16 + # optionHelp alts --help|-h + # type: Boolean min 0 max 1 + --help | -h) + # shellcheck disable=SC2034 + optionHelp="1" + + if ((options_parse_optionParsedCountOptionHelp >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionHelp)) + optionHelpCallback "${options_parse_arg}" "${optionHelp}" + + ;; + + # Option 4/16 + # optionConfig alts --config + # type: Boolean min 0 max 1 + --config) + # shellcheck disable=SC2034 + optionConfig="1" + + if ((options_parse_optionParsedCountOptionConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionConfig)) + ;; + + # Option 5/16 + # optionBashFrameworkConfig alts --bash-framework-config + # type: String min 0 max 1 + --bash-framework-config) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) + # shellcheck disable=SC2034 + optionBashFrameworkConfig="$1" + optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" + + ;; + + # Option 6/16 + # optionInfoVerbose alts --verbose|-v + # type: Boolean min 0 max 1 + --verbose | -v) + # shellcheck disable=SC2034 + optionInfoVerbose="1" + + if ((options_parse_optionParsedCountOptionInfoVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionInfoVerbose)) + optionInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + updateArgListInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + ;; + + # Option 7/16 + # optionDebugVerbose alts -vv + # type: Boolean min 0 max 1 + -vv) + # shellcheck disable=SC2034 + optionDebugVerbose="1" + + if ((options_parse_optionParsedCountOptionDebugVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDebugVerbose)) + optionDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + updateArgListDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + ;; + + # Option 8/16 + # optionTraceVerbose alts -vvv + # type: Boolean min 0 max 1 + -vvv) + # shellcheck disable=SC2034 + optionTraceVerbose="1" + + if ((options_parse_optionParsedCountOptionTraceVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTraceVerbose)) + optionTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + updateArgListTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + ;; + + # Option 9/16 + # optionEnvFiles alts --env-file + # type: StringArray min 0 max -1 + --env-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + ((++options_parse_optionParsedCountOptionEnvFiles)) + optionEnvFiles+=("$1") + optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + ;; + + # Option 10/16 + # optionLogLevel alts --log-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --log-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogLevel)) + # shellcheck disable=SC2034 + optionLogLevel="$1" + optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + ;; + + # Option 11/16 + # optionLogFile alts --log-file + # type: String min 0 max 1 + --log-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogFile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogFile)) + # shellcheck disable=SC2034 + optionLogFile="$1" + optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + ;; + + # Option 12/16 + # optionDisplayLevel alts --display-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --display-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionDisplayLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDisplayLevel)) + # shellcheck disable=SC2034 + optionDisplayLevel="$1" + optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + ;; + + # Option 13/16 + # optionNoColor alts --no-color + # type: Boolean min 0 max 1 + --no-color) + # shellcheck disable=SC2034 + optionNoColor="1" + + if ((options_parse_optionParsedCountOptionNoColor >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionNoColor)) + optionNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + updateArgListNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + ;; + + # Option 14/16 + # optionTheme alts --theme + # type: String min 0 max 1 + # authorizedValues: default|default-force|noColor + --theme) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ default|default-force|noColor ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionTheme >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTheme)) + # shellcheck disable=SC2034 + optionTheme="$1" + optionThemeCallback "${options_parse_arg}" "${optionTheme}" + + updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" + + ;; + + # Option 15/16 + # optionVersion alts --version + # type: Boolean min 0 max 1 + --version) + # shellcheck disable=SC2034 + optionVersion="1" + + if ((options_parse_optionParsedCountOptionVersion >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionVersion)) + optionVersionCallback "${options_parse_arg}" "${optionVersion}" + + ;; + + # Option 16/16 + # optionQuiet alts --quiet|-q + # type: Boolean min 0 max 1 + --quiet | -q) + # shellcheck disable=SC2034 + optionQuiet="1" + + if ((options_parse_optionParsedCountOptionQuiet >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionQuiet)) + optionQuietCallback "${options_parse_arg}" "${optionQuiet}" + + updateArgListQuietCallback "${options_parse_arg}" "${optionQuiet}" + + ;; + + -*) + unknownOption "${options_parse_arg}" || argOptDefaultBehavior=$? + ;; + *) + ((minParsedArgIndex0 = 0)) || true + ((maxParsedArgIndex0 = 0)) || true + ((minParsedArgIndex1 = minParsedArgIndex0 + 1)) || true + ((maxParsedArgIndex1 = maxParsedArgIndex0 + 1)) || true + ((minParsedArgIndex2 = minParsedArgIndex1 + 1)) || true + ((maxParsedArgIndex2 = maxParsedArgIndex1 + 1)) || true + ((minParsedArgIndex3 = minParsedArgIndex2 + 1)) || true + ((maxParsedArgIndex3 = maxParsedArgIndex2 + 1)) || true + ((minParsedArgIndex4 = minParsedArgIndex3 + 1)) || true + ((maxParsedArgIndex4 = maxParsedArgIndex3 + 1)) || true + ((minParsedArgIndex5 = minParsedArgIndex4 + 0)) || true + ((maxParsedArgIndex5 = maxParsedArgIndex4)) || true + ((incrementArg = 1 )) + if ((0)); then + # Technical if - never reached + : + + # Argument 1/5 - mysqlHostArg + # Argument mysqlHostArg min 1 max 1 + # Argument mysqlHostArg authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && + options_parse_parsedArgIndex < maxParsedArgIndex1 )); then + if ((options_parse_argParsedCountMysqlHostArg >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument mysqlHost - Maximum number of argument occurrences reached(1)" return 1 fi - ((++options_parse_optionParsedCountOptionTheme)) - # shellcheck disable=SC2034 - optionTheme="$1" - optionThemeCallback "${options_parse_arg}" "${optionTheme}" - updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" - ;; - # Option 10/15 - # Option optionHelp --help|-h variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --help | -h) + ((++options_parse_argParsedCountMysqlHostArg)) # shellcheck disable=SC2034 - optionHelp="1" - if ((options_parse_optionParsedCountOptionHelp >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + mysqlHostArg="${options_parse_arg}" + + + # Argument 2/5 - mysqlPortArg + # Argument mysqlPortArg min 1 max 1 + # Argument mysqlPortArg authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex1 && + options_parse_parsedArgIndex < maxParsedArgIndex2 )); then + if ((options_parse_argParsedCountMysqlPortArg >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument mysqlPort - Maximum number of argument occurrences reached(1)" return 1 fi - ((++options_parse_optionParsedCountOptionHelp)) - optionHelpCallback "${options_parse_arg}" - ;; - # Option 11/15 - # Option optionVersion --version variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --version) + ((++options_parse_argParsedCountMysqlPortArg)) # shellcheck disable=SC2034 - optionVersion="1" - if ((options_parse_optionParsedCountOptionVersion >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + mysqlPortArg="${options_parse_arg}" + mysqlPortArgCallback "${mysqlPortArg}" -- "${@:2}" + + + # Argument 3/5 - mysqlUserArg + # Argument mysqlUserArg min 1 max 1 + # Argument mysqlUserArg authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex2 && + options_parse_parsedArgIndex < maxParsedArgIndex3 )); then + if ((options_parse_argParsedCountMysqlUserArg >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument mysqlUser - Maximum number of argument occurrences reached(1)" return 1 fi - ((++options_parse_optionParsedCountOptionVersion)) - optionVersionCallback "${options_parse_arg}" - ;; - # Option 12/15 - # Option optionQuiet --quiet|-q variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --quiet | -q) + ((++options_parse_argParsedCountMysqlUserArg)) # shellcheck disable=SC2034 - optionQuiet="1" - if ((options_parse_optionParsedCountOptionQuiet >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionQuiet)) - optionQuietCallback "${options_parse_arg}" - updateArgListQuietCallback "${options_parse_arg}" - ;; - # Option 13/15 - # Option optionLogLevel --log-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --log-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + mysqlUserArg="${options_parse_arg}" + + + # Argument 4/5 - mysqlPasswordArg + # Argument mysqlPasswordArg min 1 max 1 + # Argument mysqlPasswordArg authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex3 && + options_parse_parsedArgIndex < maxParsedArgIndex4 )); then + if ((options_parse_argParsedCountMysqlPasswordArg >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument mysqlPassword - Maximum number of argument occurrences reached(1)" return 1 fi - ((++options_parse_optionParsedCountOptionLogLevel)) + ((++options_parse_argParsedCountMysqlPasswordArg)) # shellcheck disable=SC2034 - optionLogLevel="$1" - optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - ;; - # Option 14/15 - # Option optionLogFile --log-file variableType String min 0 max 1 authorizedValues '' regexp '' - --log-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogFile >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogFile)) + mysqlPasswordArg="${options_parse_arg}" + + + # Argument 5/5 - commandArgs + # Argument commandArgs min 0 max -1 + # Argument commandArgs authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex4 )); then + ((++options_parse_argParsedCountCommandArgs)) # shellcheck disable=SC2034 - optionLogFile="$1" - optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" - updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" - ;; - # Option 15/15 - # Option optionDisplayLevel --display-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --display-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionDisplayLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDisplayLevel)) # shellcheck disable=SC2034 - optionDisplayLevel="$1" - optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - ;; - -*) - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" - return 1 - fi - ;; - *) - if ((0)); then - # Technical if - never reached - : - # Argument 1/4 - # Argument mysqlHostArg min 1 max 1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 0 && options_parse_parsedArgIndex < 1)); then - if ((options_parse_argParsedCountMysqlHostArg >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument mysqlHost - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountMysqlHostArg)) - # shellcheck disable=SC2034 - mysqlHostArg="${options_parse_arg}" - # Argument 2/4 - # Argument mysqlPortArg min 1 max 1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 1 && options_parse_parsedArgIndex < 2)); then - if ((options_parse_argParsedCountMysqlPortArg >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument mysqlPort - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountMysqlPortArg)) - # shellcheck disable=SC2034 - mysqlPortArg="${options_parse_arg}" - mysqlPortArgCallback "${mysqlPortArg}" -- "${@:2}" - # Argument 3/4 - # Argument mysqlUserArg min 1 max 1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 2 && options_parse_parsedArgIndex < 3)); then - if ((options_parse_argParsedCountMysqlUserArg >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument mysqlUserArg - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountMysqlUserArg)) - # shellcheck disable=SC2034 - mysqlUserArg="${options_parse_arg}" - # Argument 4/4 - # Argument mysqlPasswordArg min 1 max 1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 3 && options_parse_parsedArgIndex < 4)); then - if ((options_parse_argParsedCountMysqlPasswordArg >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument mysqlPasswordArg - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountMysqlPasswordArg)) - # shellcheck disable=SC2034 - mysqlPasswordArg="${options_parse_arg}" - else - if [[ "${argOptDefaultBehavior}" = "0" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Argument - too much arguments provided: $*" - return 1 - fi - fi + commandArgs+=("${options_parse_arg}") + + + # else too much args + else + + + # no arg configured, call unknownArgumentCallback + + # shellcheck disable=SC2317 + unknownOption "${options_parse_arg}" + + fi + if ((incrementArg == 1)); then ((++options_parse_parsedArgIndex)) - ;; - esac - shift || true - done - if ((options_parse_argParsedCountMysqlHostArg < 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument 'mysqlHost' should be provided at least 1 time(s)" - return 1 - fi - if ((options_parse_argParsedCountMysqlPortArg < 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument 'mysqlPort' should be provided at least 1 time(s)" - return 1 - fi - if ((options_parse_argParsedCountMysqlUserArg < 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument 'mysqlUserArg' should be provided at least 1 time(s)" - return 1 - fi - if ((options_parse_argParsedCountMysqlPasswordArg < 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument 'mysqlPasswordArg' should be provided at least 1 time(s)" - return 1 - fi - commandOptionParseFinished - Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" - Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" - elif [[ "${options_parse_cmd}" = "help" ]]; then - Array::wrap2 " " 80 0 "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" "wait for mysql to be ready" - echo + fi + ;; + esac + shift || true + done - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "${SCRIPT_NAME}" "[OPTIONS]" "[ARGUMENTS]")" - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ - "${SCRIPT_NAME}" \ - "[--timeout|-t ]" "[--bash-framework-config ]" "[--config]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--no-color]" "[--theme ]" "[--help|-h]" "[--version]" "[--quiet|-q]" "[--log-level ]" "[--log-file ]" "[--display-level ]")" - echo - echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}mysqlHost${__HELP_NORMAL} {single} (mandatory)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Mysql\ host\ name) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}mysqlPort${__HELP_NORMAL} {single} (mandatory)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Mysql\ port) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}mysqlUserArg${__HELP_NORMAL} {single} (mandatory)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Mysql\ user\ name) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}mysqlPasswordArg${__HELP_NORMAL} {single} (mandatory)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Mysql\ password) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--timeout${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-t ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Timeout\ in\ seconds\,\ zero\ for\ no\ timeout.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: 15' - echo - echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(use\ alternate\ bash\ framework\ configuration.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ configuration) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(info\ level\ verbose\ mode\ \(alias\ of\ --display-level\ INFO\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(debug\ level\ verbose\ mode\ \(alias\ of\ --display-level\ DEBUG\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(trace\ level\ verbose\ mode\ \(alias\ of\ --display-level\ TRACE\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Load\ the\ specified\ env\ file\ \(deprecated\,\ please\ use\ --bash-framework-config\ option\ instead\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Produce\ monochrome\ output.\ alias\ of\ --theme\ noColor.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' - echo ' Possible values: default|default-force|noColor' - echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ this\ command\ help) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Print\ version\ information\ and\ quit) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(quiet\ mode\,\ doesn\'t\ display\ any\ output) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ file) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(set\ display\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e """ -${__HELP_TITLE}EXIT STATUS CODES:${__HELP_NORMAL} -${__HELP_OPTION_COLOR}0${__HELP_NORMAL}: mysql is available -${__HELP_OPTION_COLOR}1${__HELP_NORMAL}: indicates mysql is not available or argument error -${__HELP_OPTION_COLOR}2${__HELP_NORMAL}: timeout reached""" + + + + + + + + + + + + + + + + if ((options_parse_argParsedCountMysqlHostArg < 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument 'mysqlHost' should be provided at least 1 time(s)" + return 1 + fi + + if ((options_parse_argParsedCountMysqlPortArg < 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument 'mysqlPort' should be provided at least 1 time(s)" + return 1 + fi + + if ((options_parse_argParsedCountMysqlUserArg < 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument 'mysqlUser' should be provided at least 1 time(s)" + return 1 + fi + + if ((options_parse_argParsedCountMysqlPasswordArg < 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument 'mysqlPassword' should be provided at least 1 time(s)" + return 1 + fi || return $? + + commandOptionParseFinished + +} + +# @description display command options and arguments help for waitForMysqlCommand +waitForMysqlCommandHelp() { + echo -e "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" + Array::wrap2 ' ' 76 4 " " "Wait for mysql to be ready." + + echo + echo + + # ------------------------------------------ + # usage section + # ------------------------------------------ + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "waitForMysql [OPTIONS] [ARGUMENTS]" + echo + # ------------------------------------------ + # usage/options section + # ------------------------------------------ + optionsAltList=("[--timeout|-t ]" "[--exec-command-on-timeout|--lax|-l]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" + ) + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ + "waitForMysql" "${optionsAltList[@]}" + echo + + # ------------------------------------------ + # usage/arguments section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" + + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}mysqlHost${__HELP_NORMAL} {single} (mandatory) + " + Array::wrap2 ' ' 76 4 " " "Mysql host name." echo - echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo '2.0' + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}mysqlPort${__HELP_NORMAL} {single} (mandatory) + " + Array::wrap2 ' ' 76 4 " " "Mysql port." echo - echo -e "${__HELP_TITLE_COLOR}AUTHOR:${__RESET_COLOR}" - echo '[François Chastanet](https://github.com/fchastanet)' + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}mysqlUser${__HELP_NORMAL} {single} (mandatory) + " + Array::wrap2 ' ' 76 4 " " "Mysql user name." echo - echo -e "${__HELP_TITLE_COLOR}SOURCE FILE:${__RESET_COLOR}" - echo 'https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Utils/waitForMysql.sh' + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}mysqlPassword${__HELP_NORMAL} {single} (mandatory) + " + Array::wrap2 ' ' 76 4 " " "Mysql user password." echo - echo -e "${__HELP_TITLE_COLOR}LICENSE:${__RESET_COLOR}" - echo 'MIT License' + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}commandArgs${__HELP_NORMAL} {list} (optional)] + " + Array::wrap2 ' ' 76 4 " " "Execute command with args after the test finishes or exit with status code if no command provided." echo - Array::wrap2 ' ' 76 4 "$(copyrightCallback)" - else - Log::displayError "Command ${SCRIPT_NAME} - Option command invalid: '${options_parse_cmd}'" - return 1 - fi -} + # ------------------------------------------ + # options section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}TIMEOUT OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--timeout${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-t ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Timeout in seconds, zero for no timeout." + echo -mysqlPortArgCallback() { - if [[ ! "${mysqlPortArg}" =~ ^[0-9]+$ ]] || (( mysqlPortArg == 0 )); then - Log::fatal "${SCRIPT_NAME} - invalid port option - must be greater than to 0" - fi + + Array::wrap2 ' ' 76 6 " Default value: " "15" + echo + + echo -e " ${__HELP_OPTION_COLOR}--exec-command-on-timeout${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}--lax${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-l${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Execute sub-command even if timeout is reached." + echo + + + echo + echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays this command help" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays configuration" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" + Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log file" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set display level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." + echo + Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" + echo + + Array::wrap2 ' ' 76 6 " Default value: " "default" + echo + + echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Print version information and quit." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." + echo + + + # ------------------------------------------ + # longDescription section + # ------------------------------------------ + echo + echo + echo -e "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" + longDescriptionFunction + # ------------------------------------------ + # version section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" + echo "3.0" + # ------------------------------------------ + # author section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}AUTHOR: ${__RESET_COLOR}" + echo "[François Chastanet](https://github.com/fchastanet)" + # ------------------------------------------ + # sourceFile section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Utils/waitForMysql/waitForMysql-binary.yaml" + # ------------------------------------------ + # license section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}LICENSE: ${__RESET_COLOR}" + echo "MIT License" + # ------------------------------------------ + # copyright section + # ------------------------------------------ + Array::wrap2 ' ' 76 0 "$(copyrightCallback)" } -waitForMysqlCommand parse "${BASH_FRAMEWORK_ARGV[@]}" - -run() { - Assert::commandExists "mysql" - Log::displayInfo "Waiting for mysql" - local -i start_ts=${SECONDS} - (printf >&2 ".") - until (echo "select 1" | mysql \ - -h"${mysqlHostArg}" \ - -P"${mysqlPortArg}" \ - -u"${mysqlUserArg}" \ - -p"${mysqlPasswordArg}" &>/dev/null); do + +beforeParseCallback + +waitForMysqlCommandParse "$@" +MAIN_FUNCTION_NAME="main" +main() { + +# shellcheck disable=SC2154 + +Assert::commandExists "mysql" +Log::displayInfo "Waiting for mysql" +declare -i start_ts=${SECONDS} +declare status=0 +declare -i triesCount=0 +while true; do + ((++triesCount)) + if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_INFO)); then + Log::displayInfo "Try connecting to mysql host ${mysqlHostArg}:${mysqlPortArg} - Try ${triesCount}" + else (printf >&2 ".") - if ((optionTimeout != 0 && SECONDS - start_ts >= optionTimeout)); then - (echo >&2 "") - Log::displayError "${SCRIPT_NAME} - timeout for ${mysqlHostArg}:${mysqlPortArg} occurred after $((SECONDS - start_ts)) seconds" - return 2 - fi - sleep 1 - done + fi + BashTools::runVerboseIfNeeded mysql -h"${mysqlHostArg}" -P"${mysqlPortArg}" -u"${mysqlUserArg}" \ + -p"${mysqlPasswordArg}" &>/dev/null <<<"SELECT 1" || status=$? + if [[ "${status}" = "0" ]]; then + break + fi + if ((optionTimeout != 0 && SECONDS - start_ts >= optionTimeout)); then + (echo >&2 "") + Log::displayError "${SCRIPT_NAME} - timeout for ${mysqlHostArg}:${mysqlPortArg} occurred after $((SECONDS - start_ts)) seconds" + status=2 + break + fi + sleep 1 +done +if ((BASH_FRAMEWORK_ARGS_VERBOSE < __VERBOSE_LEVEL_INFO)); then (echo >&2 "") +fi +if [[ "${status}" = "0" ]]; then Log::displayInfo "mysql ready" -} +fi -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run +# when timed out, call command if any +if [[ -n "${commandArgs+x}" && "${commandArgs[*]}" != "" ]]; then + if [[ "${status}" != "0" && "${optionExecIfTimedOut}" = "0" ]]; then + Log::displayError "${SCRIPT_NAME} - failed to connect - strict mode - command not executed" + exit "${status}" + fi + exec "${commandArgs[@]}" fi +exit "${status}" + } -facade_main_waitForMysqlsh "$@" +# if file is sourced avoid calling main function +# shellcheck disable=SC2178 +BASH_SOURCE=".$0" # cannot be changed in bash +# shellcheck disable=SC2128 +if test ".$0" == ".${BASH_SOURCE}"; then + if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then + main "$@" &>/dev/null + else + main "$@" + fi +fi diff --git a/src/_binaries/Utils/testsData/waitForIt.help.txt b/src/_binaries/Utils/testsData/waitForIt.help.txt deleted file mode 100644 index e0939d63..00000000 --- a/src/_binaries/Utils/testsData/waitForIt.help.txt +++ /dev/null @@ -1,90 +0,0 @@ -DESCRIPTION: wait for host:port to be available -USAGE: waitForIt [OPTIONS] [ARGUMENTS] -USAGE: waitForIt [--timeout|-t ] --host|-i - --port|-p [--algorithm|--algo ] - [--exec-command-on-success-only|--strict|-s] - [--bash-framework-config ] [--config] [--verbose|-v] [-vv] [-vvv] - [--env-file ] [--no-color] [--theme ] [--help|-h] [--version] - [--quiet|-q] [--log-level ] [--log-file ] - [--display-level ] - -ARGUMENTS: - [commandArgs {list} (optional)] - Execute command with args after the test finishes or exit with status code i - f no command provided. - -OPTIONS: - --timeout, -t  {single} - Timeout in seconds, zero for no timeout. - Default value: 15 - --host, -i  {single} (mandatory) - Host or IP under test. - --port, -p  {single} (mandatory) - TCP port under test. - --algorithm, --algo  {single} - Algorithm to use Check algorithms list below. (default: automatic selection - based on commands availability and timeout option value). - --exec-command-on-success-only, --strict, -s {single} - Only execute sub-command if the test succeeds. - -GLOBAL OPTIONS: - --bash-framework-config  {single} - use alternate bash framework configuration. - --config {single} - Display configuration - --verbose, -v {single} - info level verbose mode (alias of --display-level INFO) - -vv {single} - debug level verbose mode (alias of --display-level DEBUG) - -vvv {single} - trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-config - option instead) - --no-color {single} - Produce monochrome output. alias of --theme noColor. - --theme  {single} - choose color theme - default-force means colors will be produced even if com - mand is piped - Default value: default - Possible values: default|default-force|noColor - --help, -h {single} - Display this command help - --version {single} - Print version information and quit - --quiet, -q {single} - quiet mode, doesn't display any output - --log-level  {single} - Set log level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - --log-file  {single} - Set log file - --display-level  {single} - set display level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - -EXIT STATUS CODES: -0: the host/port is available -1: indicates host/port is not available or argument error -2: timeout reached - -AVAILABLE ALGORITHMS: -timeoutV1WithNc: previous version of timeout command with --timeout option, base command nc -timeoutV2WithNc: newer version of timeout command using timeout as argument, base command nc -whileLoopWithNc: timeout command simulated using while loop, base command nc -timeoutV1WithTcp: previous version of timeout command with --timeout option -timeoutV2WithTcp: newer version of timeout command using timeout as argument -whileLoopWithTcp: timeout command simulated using while loop, base command tcp - -VERSION: 2.0 - -AUTHOR: -[François Chastanet](https://github.com/fchastanet) - -SOURCE FILE: -https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Utils/waitForIt.sh - -LICENSE: -MIT License - -Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Utils/testsData/waitForMysql.help.txt b/src/_binaries/Utils/testsData/waitForMysql.help.txt deleted file mode 100644 index 7e5a037c..00000000 --- a/src/_binaries/Utils/testsData/waitForMysql.help.txt +++ /dev/null @@ -1,76 +0,0 @@ -DESCRIPTION: wait for mysql to be ready -USAGE: waitForMysql [OPTIONS] [ARGUMENTS] -USAGE: waitForMysql [--timeout|-t ] - [--bash-framework-config ] [--config] [--verbose|-v] [-vv] [-vvv] - [--env-file ] [--no-color] [--theme ] [--help|-h] [--version] - [--quiet|-q] [--log-level ] [--log-file ] - [--display-level ] - -ARGUMENTS: - mysqlHost {single} (mandatory) - Mysql host name - mysqlPort {single} (mandatory) - Mysql port - mysqlUserArg {single} (mandatory) - Mysql user name - mysqlPasswordArg {single} (mandatory) - Mysql password - -OPTIONS: - --timeout, -t  {single} - Timeout in seconds, zero for no timeout. - Default value: 15 - -GLOBAL OPTIONS: - --bash-framework-config  {single} - use alternate bash framework configuration. - --config {single} - Display configuration - --verbose, -v {single} - info level verbose mode (alias of --display-level INFO) - -vv {single} - debug level verbose mode (alias of --display-level DEBUG) - -vvv {single} - trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-config - option instead) - --no-color {single} - Produce monochrome output. alias of --theme noColor. - --theme  {single} - choose color theme - default-force means colors will be produced even if com - mand is piped - Default value: default - Possible values: default|default-force|noColor - --help, -h {single} - Display this command help - --version {single} - Print version information and quit - --quiet, -q {single} - quiet mode, doesn't display any output - --log-level  {single} - Set log level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - --log-file  {single} - Set log file - --display-level  {single} - set display level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - -EXIT STATUS CODES: -0: mysql is available -1: indicates mysql is not available or argument error -2: timeout reached - -VERSION: 2.0 - -AUTHOR: -[François Chastanet](https://github.com/fchastanet) - -SOURCE FILE: -https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Utils/waitForMysql.sh - -LICENSE: -MIT License - -Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Utils/waitForIt/testsData/waitForIt.help.txt b/src/_binaries/Utils/waitForIt/testsData/waitForIt.help.txt index 55c86a8d..c1cda629 100644 --- a/src/_binaries/Utils/waitForIt/testsData/waitForIt.help.txt +++ b/src/_binaries/Utils/waitForIt/testsData/waitForIt.help.txt @@ -2,12 +2,12 @@ Wait for host:port to be available. USAGE: waitForIt [OPTIONS] [ARGUMENTS] -USAGE: waitForIt [--timeout|-t ] [--help|-h] [--config] +USAGE: waitForIt [--timeout|-t ] + [--exec-command-on-timeout|--lax|-l] [--help|-h] [--config] [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] [--env-file ] [--log-level ] [--log-file ] [--display-level ] [--no-color] [--theme ] [--version] - [--quiet|-q] [--algorithm|--algo ] - [--exec-command-on-success-only|--strict|-s] [--user-nc] + [--quiet|-q] [--algorithm|--algo ] [--user-nc] ARGUMENTS: hostOrIp {single} (mandatory) @@ -22,6 +22,8 @@ --timeout, -t  {single} Timeout in seconds, zero for no timeout. Default value: 15 + --exec-command-on-timeout, --lax, -l {single} + Execute sub-command even if timeout is reached. GLOBAL OPTIONS: --help, -h {single} @@ -63,8 +65,6 @@ --algorithm, --algo  {single} Algorithm to use Check algorithms list below. Default: automatic selection based on commands availability and timeout option value. - --exec-command-on-success-only, --strict, -s {single} - Only execute sub-command if the test succeeds. --user-nc {single} Legacy mode using nc command or while loop (uses timeout command by defa ult). diff --git a/src/_binaries/Utils/waitForIt/waitForIt-binary.yaml b/src/_binaries/Utils/waitForIt/waitForIt-binary.yaml index af2279c7..4ea5411f 100644 --- a/src/_binaries/Utils/waitForIt/waitForIt-binary.yaml +++ b/src/_binaries/Utils/waitForIt/waitForIt-binary.yaml @@ -74,15 +74,6 @@ binData: - --algorithm - --algo - - variableName: optionStrict - type: Boolean - group: waitForItOptionGroup - help: Only execute sub-command if the test succeeds. - alts: - - --exec-command-on-success-only - - --strict - - -s - - variableName: optionLegacy type: Boolean group: waitForItOptionGroup diff --git a/src/_binaries/Utils/waitForIt/waitForIt-main.sh b/src/_binaries/Utils/waitForIt/waitForIt-main.sh index ee63f3cd..71322547 100755 --- a/src/_binaries/Utils/waitForIt/waitForIt-main.sh +++ b/src/_binaries/Utils/waitForIt/waitForIt-main.sh @@ -38,7 +38,7 @@ whileLoop() { fi if ((optionTimeout != 0 && SECONDS - start_ts >= optionTimeout)); then if [[ "${reportTimeout}" = "1" ]]; then - Log::displayError "${SCRIPT_NAME} - timeout for ${hostOrIpArg}:${portArg} occurred after $((SECONDS - start_ts)) seconds" + Log::displayError "${SCRIPT_NAME} - timeout for ${hostOrIpArg}:${portArg} occurred after $((SECONDS - start_ts)) seconds > ${optionTimeout}" fi return 2 fi @@ -154,7 +154,7 @@ else "${algo}" || result=$? # when timed out, call command if any if [[ -n "${commandArgs+x}" && "${commandArgs[*]}" != "" ]]; then - if [[ "${result}" != "0" && "${optionStrict}" = "1" ]]; then + if [[ "${result}" != "0" && "${optionExecIfTimedOut}" = "0" ]]; then Log::displayError "${SCRIPT_NAME} - failed to connect - strict mode - command not executed" exit "${result}" fi diff --git a/src/_binaries/Utils/waitForIt/waitForIt.bats b/src/_binaries/Utils/waitForIt/waitForIt.bats index afaf8bd8..f32340ea 100755 --- a/src/_binaries/Utils/waitForIt/waitForIt.bats +++ b/src/_binaries/Utils/waitForIt/waitForIt.bats @@ -98,8 +98,8 @@ function Utils::waitForIt::algo::timeoutV1WithNc::NoCommandExecutedIfFailed { #@ chmod +x "${HOME}/bin/nc" stub timeout \ - "-t 1 ${binDir}/waitForIt localhost 8888 --timeout 1 --strict --algo timeoutV1WithNc echo success : ${binDir}/waitForIt localhost 8888 --timeout 1 --strict --algo timeoutV1WithNc echo success" - run "${binDir}/waitForIt" localhost 8888 --timeout 1 --strict --algo timeoutV1WithNc echo "success" 2>&1 + "-t 1 ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV1WithNc echo success : ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV1WithNc echo success" + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo timeoutV1WithNc echo "success" 2>&1 assert_failure 2 assert_line --index 0 --partial "INFO - waitForIt - using algorithm timeoutV1WithNc" @@ -111,9 +111,9 @@ function Utils::waitForIt::algo::timeoutV1WithNc::NoCommandExecutedIfFailed { #@ } function Utils::waitForIt::algo::timeoutV2WithNc::WithoutCommand { #@test - stub timeout "1 ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV2WithNc : ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV2WithNc" + stub timeout "1 ${binDir}/waitForIt localhost 8888 --timeout 1 --lax --algo timeoutV2WithNc : ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV2WithNc" stub nc "-z localhost 8888 -w 1 : exit 0" - run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo timeoutV2WithNc 2>&1 + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --lax --algo timeoutV2WithNc 2>&1 assert_success assert_line --index 0 --partial "INFO - waitForIt - using algorithm timeoutV2WithNc" @@ -123,10 +123,10 @@ function Utils::waitForIt::algo::timeoutV2WithNc::WithoutCommand { #@test } function Utils::waitForIt::algo::timeoutV2WithNc::ExecCommand { #@test - stub timeout "1 ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV2WithNc echo success : \ + stub timeout "1 ${binDir}/waitForIt localhost 8888 --timeout 1 --lax --algo timeoutV2WithNc echo success : \ ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV2WithNc echo success" stub nc "-z localhost 8888 -w 1 : exit 0" - run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo timeoutV2WithNc echo "success" 2>&1 + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --lax --algo timeoutV2WithNc echo "success" 2>&1 assert_success assert_line --index 0 --partial "INFO - waitForIt - using algorithm timeoutV2WithNc" @@ -144,8 +144,8 @@ function Utils::waitForIt::algo::timeoutV2WithNc::NoCommandExecutedIfFailed { #@ chmod +x "${HOME}/bin/nc" stub timeout \ - "1 ${binDir}/waitForIt localhost 8888 --timeout 1 --strict --algo timeoutV2WithNc echo success : ${binDir}/waitForIt localhost 8888 --timeout 1 --strict --algo timeoutV2WithNc echo success" - run "${binDir}/waitForIt" localhost 8888 --timeout 1 --strict --algo timeoutV2WithNc echo "success" 2>&1 + "1 ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV2WithNc echo success : ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV2WithNc echo success" + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo timeoutV2WithNc echo "success" 2>&1 assert_failure 2 assert_line --index 0 --partial "INFO - waitForIt - using algorithm timeoutV2WithNc" @@ -164,8 +164,8 @@ function Utils::waitForIt::algo::timeoutV1WithTcp::WithoutCommand { #@test echo "mocked $*" } export -f mockedTcp - stub timeout "-t 1 ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV1WithTcp : ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV1WithTcp" - run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo timeoutV1WithTcp 2>&1 + stub timeout "-t 1 ${binDir}/waitForIt localhost 8888 --timeout 1 --lax --algo timeoutV1WithTcp : ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV1WithTcp" + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --lax --algo timeoutV1WithTcp 2>&1 assert_success assert_line --index 0 --partial "INFO - waitForIt - using algorithm timeoutV1WithTcp" @@ -182,9 +182,9 @@ function Utils::waitForIt::algo::timeoutV1WithTcp::ExecCommand { #@test echo "mocked $*" } export -f mockedTcp - stub timeout "-t 1 ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV1WithTcp echo success : \ - ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV1WithTcp echo success" - run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo timeoutV1WithTcp echo "success" 2>&1 + stub timeout "-t 1 ${binDir}/waitForIt localhost 8888 --timeout 1 --lax --algo timeoutV1WithTcp echo success : \ + ${binDir}/waitForIt localhost 8888 --timeout 1 --lax --algo timeoutV1WithTcp echo success" + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --lax --algo timeoutV1WithTcp echo "success" 2>&1 assert_success assert_line --index 0 --partial "INFO - waitForIt - using algorithm timeoutV1WithTcp" @@ -203,8 +203,8 @@ function Utils::waitForIt::algo::timeoutV1WithTcp::NoCommandExecutedIfFailed { # } export -f mockedTcp stub timeout \ - "-t 1 ${binDir}/waitForIt localhost 8888 --timeout 1 --strict --algo timeoutV1WithTcp echo success : ${binDir}/waitForIt localhost 8888 --timeout 1 --strict --algo timeoutV1WithTcp echo success" - run "${binDir}/waitForIt" localhost 8888 --timeout 1 --strict --algo timeoutV1WithTcp echo "success" 2>&1 + "-t 1 ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV1WithTcp echo success : ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV1WithTcp echo success" + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo timeoutV1WithTcp echo "success" 2>&1 assert_failure 2 assert_line --index 0 --partial "INFO - waitForIt - using algorithm timeoutV1WithTcp" @@ -221,8 +221,8 @@ function Utils::waitForIt::algo::timeoutV2WithTcp::WithoutCommand { #@test return 0 } export -f mockedTcp - stub timeout "1 ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV2WithTcp : ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV2WithTcp" - run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo timeoutV2WithTcp 2>&1 + stub timeout "1 ${binDir}/waitForIt localhost 8888 --timeout 1 --lax --algo timeoutV2WithTcp : ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV2WithTcp" + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --lax --algo timeoutV2WithTcp 2>&1 assert_success assert_line --index 0 --partial "INFO - waitForIt - using algorithm timeoutV2WithTcp" @@ -238,9 +238,9 @@ function Utils::waitForIt::algo::timeoutV2WithTcp::ExecCommand { #@test return 0 } export -f mockedTcp - stub timeout "1 ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV2WithTcp echo success : \ - ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV2WithTcp echo success" - run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo timeoutV2WithTcp echo "success" 2>&1 + stub timeout "1 ${binDir}/waitForIt localhost 8888 --timeout 1 --lax --algo timeoutV2WithTcp echo success : \ + ${binDir}/waitForIt localhost 8888 --timeout 1 --lax --algo timeoutV2WithTcp echo success" + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --lax --algo timeoutV2WithTcp echo "success" 2>&1 assert_success assert_line --index 0 --partial "INFO - waitForIt - using algorithm timeoutV2WithTcp" @@ -258,9 +258,8 @@ function Utils::waitForIt::algo::timeoutV2WithTcp::NoCommandExecutedIfFailed { # } export -f mockedTcp stub timeout \ - "1 ${binDir}/waitForIt localhost 8888 --timeout 1 --strict --algo timeoutV2WithTcp echo success : ${binDir}/waitForIt localhost 8888 --timeout 1 --strict --algo timeoutV2WithTcp echo success" - run "${binDir}/waitForIt" localhost 8888 --timeout 1 --strict --algo timeoutV2WithTcp echo "success" 2>&1 - + "1 ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV2WithTcp echo success : ${binDir}/waitForIt localhost 8888 --timeout 1 --algo timeoutV2WithTcp echo success" + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo timeoutV2WithTcp echo "success" 2>&1 assert_failure 2 assert_line --index 0 --partial "INFO - waitForIt - using algorithm timeoutV2WithTcp" assert_line --index 1 --partial "INFO - waitForIt - waiting 1 seconds for localhost:8888" @@ -278,8 +277,8 @@ function Utils::waitForIt::algo::whileLoopWithTcp::WithoutCommand { #@test echo "mocked $*" } export -f mockedTcp - stub timeout "-t 1 ${binDir}/waitForIt localhost 8888 --timeout 1 --algo whileLoopWithTcp : ${binDir}/waitForIt localhost 8888 --timeout 1 --algo whileLoopWithTcp" - run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo whileLoopWithTcp 2>&1 + stub timeout "-t 1 ${binDir}/waitForIt localhost 8888 --timeout 1 --lax --algo whileLoopWithTcp : ${binDir}/waitForIt localhost 8888 --timeout 1 --algo whileLoopWithTcp" + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --lax --algo whileLoopWithTcp 2>&1 assert_success assert_line --index 0 --partial "INFO - waitForIt - using algorithm whileLoopWithTcp" @@ -296,9 +295,9 @@ function Utils::waitForIt::algo::whileLoopWithTcp::ExecCommand { #@test echo "mocked $*" } export -f mockedTcp - stub timeout "-t 1 ${binDir}/waitForIt localhost 8888 --timeout 1 --algo whileLoopWithTcp echo success : \ - ${binDir}/waitForIt localhost 8888 --timeout 1 --algo whileLoopWithTcp echo success" - run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo whileLoopWithTcp echo "success" 2>&1 + stub timeout "-t 1 ${binDir}/waitForIt localhost 8888 --timeout 1 --lax --algo whileLoopWithTcp echo success : \ + ${binDir}/waitForIt localhost 8888 --timeout 1 --lax --algo whileLoopWithTcp echo success" + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --lax --algo whileLoopWithTcp echo "success" 2>&1 assert_success assert_line --index 0 --partial "INFO - waitForIt - using algorithm whileLoopWithTcp" @@ -316,7 +315,7 @@ function Utils::waitForIt::algo::whileLoopWithTcp::NoCommandExecutedIfFailed { # return 1 } export -f mockedTcp - run "${binDir}/waitForIt" localhost 8888 --timeout 1 --strict --algo whileLoopWithTcp echo "success" 2>&1 + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo whileLoopWithTcp echo "success" 2>&1 assert_failure 2 assert_line --index 0 --partial "INFO - waitForIt - using algorithm whileLoopWithTcp" @@ -332,7 +331,7 @@ function Utils::waitForIt::algo::whileLoopWithNc::WithoutCommand { #@test echo 'exit 0' ) >"${HOME}/bin/nc" chmod +x "${HOME}/bin/nc" - run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo whileLoopWithNc 2>&1 + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --lax --algo whileLoopWithNc 2>&1 assert_success assert_line --index 0 --partial "INFO - waitForIt - using algorithm whileLoopWithNc" @@ -347,7 +346,7 @@ function Utils::waitForIt::algo::whileLoopWithNc::ExecCommand { #@test echo 'exit 0' ) >"${HOME}/bin/nc" chmod +x "${HOME}/bin/nc" - run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo whileLoopWithNc echo "success" 2>&1 + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --lax --algo whileLoopWithNc echo "success" 2>&1 assert_success assert_line --index 0 --partial "INFO - waitForIt - using algorithm whileLoopWithNc" @@ -363,7 +362,7 @@ function Utils::waitForIt::algo::whileLoopWithNc::NoCommandExecutedIfFailed { #@ echo 'exit 1' ) >"${HOME}/bin/nc" chmod +x "${HOME}/bin/nc" - run "${binDir}/waitForIt" localhost 8888 --timeout 1 --strict --algo whileLoopWithNc echo "success" 2>&1 + run "${binDir}/waitForIt" localhost 8888 --timeout 1 --algo whileLoopWithNc echo "success" 2>&1 assert_failure 2 assert_line --index 0 --partial "INFO - waitForIt - using algorithm whileLoopWithNc" diff --git a/src/_binaries/Utils/waitForMysql.options.tpl b/src/_binaries/Utils/waitForMysql.options.tpl deleted file mode 100644 index 889d34f8..00000000 --- a/src/_binaries/Utils/waitForMysql.options.tpl +++ /dev/null @@ -1,59 +0,0 @@ -% -declare versionNumber="2.0" -declare commandFunctionName="waitForMysqlCommand" -declare help="wait for mysql to be ready" -# shellcheck disable=SC2016 -declare longDescription=''' -${__HELP_TITLE}EXIT STATUS CODES:${__HELP_NORMAL} -${__HELP_OPTION_COLOR}0${__HELP_NORMAL}: mysql is available -${__HELP_OPTION_COLOR}1${__HELP_NORMAL}: indicates mysql is not available or argument error -${__HELP_OPTION_COLOR}2${__HELP_NORMAL}: timeout reached -''' -% -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.base.tpl)" -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.timeout.tpl)" -% -# shellcheck source=/dev/null -source <( - Options::generateArg \ - --help "Mysql host name" \ - --name "mysqlHost" \ - --variable-name "mysqlHostArg" \ - --function-name mysqlHostArgFunction - - mysqlPortArgCallback() { :; } - Options::generateArg \ - --help "Mysql port" \ - --name "mysqlPort" \ - --variable-name "mysqlPortArg" \ - --callback mysqlPortArgCallback \ - --function-name mysqlPortArgFunction - - Options::generateArg \ - --help "Mysql user name" \ - --name "mysqlUserArg" \ - --variable-name "mysqlUserArg" \ - --function-name mysqlUserArgFunction - - Options::generateArg \ - --help "Mysql password" \ - --name "mysqlPasswordArg" \ - --variable-name "mysqlPasswordArg" \ - --function-name mysqlPasswordArgFunction -) -options+=( - mysqlHostArgFunction - mysqlPortArgFunction - mysqlUserArgFunction - mysqlPasswordArgFunction -) -Options::generateCommand "${options[@]}" -% - -mysqlPortArgCallback() { - if [[ ! "${mysqlPortArg}" =~ ^[0-9]+$ ]] || (( mysqlPortArg == 0 )); then - Log::fatal "${SCRIPT_NAME} - invalid port option - must be greater than to 0" - fi -} - -<% ${commandFunctionName} %> parse "${BASH_FRAMEWORK_ARGV[@]}" diff --git a/src/_binaries/Utils/waitForMysql.sh b/src/_binaries/Utils/waitForMysql.sh deleted file mode 100755 index aad4c9c8..00000000 --- a/src/_binaries/Utils/waitForMysql.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/waitForMysql -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE -# shellcheck disable=SC2034 - -# default values -declare defaultTimeout="15" -# option values -declare optionTimeout="${defaultTimeout}" -declare mysqlHostArg="" -declare mysqlPortArg="" -declare mysqlUserArg="" -declare mysqlPasswordArg="" -# default values -declare copyrightBeginYear="2020" - -.INCLUDE "$(dynamicTemplateDir _binaries/Utils/waitForMysql.options.tpl)" - -run() { - Assert::commandExists "mysql" - Log::displayInfo "Waiting for mysql" - local -i start_ts=${SECONDS} - (printf >&2 ".") - until (echo "select 1" | mysql \ - -h"${mysqlHostArg}" \ - -P"${mysqlPortArg}" \ - -u"${mysqlUserArg}" \ - -p"${mysqlPasswordArg}" &>/dev/null); do - (printf >&2 ".") - if ((optionTimeout != 0 && SECONDS - start_ts >= optionTimeout)); then - (echo >&2 "") - Log::displayError "${SCRIPT_NAME} - timeout for ${mysqlHostArg}:${mysqlPortArg} occurred after $((SECONDS - start_ts)) seconds" - return 2 - fi - sleep 1 - done - - (echo >&2 "") - Log::displayInfo "mysql ready" -} - -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run &>/dev/null -else - run -fi diff --git a/src/_binaries/Utils/waitForMysql/testsData/waitForMysql.help.txt b/src/_binaries/Utils/waitForMysql/testsData/waitForMysql.help.txt new file mode 100644 index 00000000..a9e6e3b9 --- /dev/null +++ b/src/_binaries/Utils/waitForMysql/testsData/waitForMysql.help.txt @@ -0,0 +1,82 @@ +SYNOPSIS: + Wait for mysql to be ready. + +USAGE: waitForMysql [OPTIONS] [ARGUMENTS] +USAGE: waitForMysql [--timeout|-t ] + [--exec-command-on-timeout|--lax|-l] [--help|-h] [--config] + [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] + [--env-file ] [--log-level ] [--log-file ] + [--display-level ] [--no-color] [--theme ] [--version] + [--quiet|-q] + +ARGUMENTS: + mysqlHost {single} (mandatory) + Mysql host name. + mysqlPort {single} (mandatory) + Mysql port. + mysqlUser {single} (mandatory) + Mysql user name. + mysqlPassword {single} (mandatory) + Mysql user password. + [commandArgs {list} (optional)] + Execute command with args after the test finishes or exit with status co + de if no command provided. + +TIMEOUT OPTIONS: + --timeout, -t  {single} + Timeout in seconds, zero for no timeout. + Default value: 15 + --exec-command-on-timeout, --lax, -l {single} + Execute sub-command even if timeout is reached. + +GLOBAL OPTIONS: + --help, -h {single} + Displays this command help + --config {single} + Displays configuration + --bash-framework-config  {single} + Use alternate bash framework configuration. + --verbose, -v {single} + Info level verbose mode (alias of --display-level INFO) + -vv {single} + Debug level verbose mode (alias of --display-level DEBUG) + -vvv {single} + Trace level verbose mode (alias of --display-level TRACE) + --env-file  {list} (optional) + Load the specified env file (deprecated, please use --bash-framework-con + fig option instead) + --log-level  {single} + Set log level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --log-file  {single} + Set log file + --display-level  {single} + Set display level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --no-color {single} + Produce monochrome output. alias of --theme noColor. + --theme  {single} + Choose color theme - default-force means colors will be produced even if + command is piped. + Possible values: default, default-force, noColor + Default value: default + --version {single} + Print version information and quit. + --quiet, -q {single} + Quiet mode, doesn't display any output. + + +DESCRIPTION: + EXIT STATUS CODES: + 0: mysql is available + 1: indicates mysql is not available or argument error + 2: timeout reached + +VERSION: 3.0 + +AUTHOR: [François Chastanet](https://github.com/fchastanet) + +SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Utils/waitForMysql/waitForMysql-binary.yaml + +LICENSE: MIT License +Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Utils/waitForMysql/waitForMysql-binary.yaml b/src/_binaries/Utils/waitForMysql/waitForMysql-binary.yaml new file mode 100644 index 00000000..eabfb55b --- /dev/null +++ b/src/_binaries/Utils/waitForMysql/waitForMysql-binary.yaml @@ -0,0 +1,71 @@ +extends: + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsTimeout.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" + +vars: + SRC_FILE_PATH: src/_binaries/Utils/waitForMysql/waitForMysql-binary.yaml + +compilerConfig: + targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/waitForMysql" + relativeRootDirBasedOnTargetDir: .. + srcDirs: + - ${BASH_TOOLS_ROOT_DIR}/src +binData: + commands: + default: + functionName: waitForMysqlCommand + version: "3.0" + copyrightBeginYear: 2020 + commandName: waitForMysql + beforeParseCallbacks: + - beforeParseCallback + unknownOptionCallbacks: + - unknownOption + unknownArgumentCallbacks: + - unknownOption + definitionFiles: + 20: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Utils/waitForMysql/waitForMysql-options.sh + mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Utils/waitForMysql/waitForMysql-main.sh + help: Wait for mysql to be ready. + longDescription: longDescriptionFunction + args: + - type: String + min: 1 + max: 1 + help: Mysql host name. + name: mysqlHost + variableName: mysqlHostArg + + - type: String + min: 1 + max: 1 + help: Mysql port. + name: mysqlPort + variableName: mysqlPortArg + callbacks: + - mysqlPortArgCallback + + - type: String + min: 1 + max: 1 + help: Mysql user name. + name: mysqlUser + variableName: mysqlUserArg + + - type: String + min: 1 + max: 1 + help: Mysql user password. + name: mysqlPassword + variableName: mysqlPasswordArg + + - type: StringArray + min: 0 + max: -1 + help: + Execute command with args after the test finishes or exit with + status code if no command provided. + name: commandArgs + variableName: commandArgs diff --git a/src/_binaries/Utils/waitForMysql/waitForMysql-main.sh b/src/_binaries/Utils/waitForMysql/waitForMysql-main.sh new file mode 100755 index 00000000..c74588e4 --- /dev/null +++ b/src/_binaries/Utils/waitForMysql/waitForMysql-main.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2154 + +Assert::commandExists "mysql" +Log::displayInfo "Waiting for mysql" +declare -i start_ts=${SECONDS} +declare status=0 +declare -i triesCount=0 +while true; do + ((++triesCount)) + if ((BASH_FRAMEWORK_ARGS_VERBOSE >= __VERBOSE_LEVEL_INFO)); then + Log::displayInfo "Try connecting to mysql host ${mysqlHostArg}:${mysqlPortArg} - Try ${triesCount}" + else + (printf >&2 ".") + fi + BashTools::runVerboseIfNeeded mysql -h"${mysqlHostArg}" -P"${mysqlPortArg}" -u"${mysqlUserArg}" \ + -p"${mysqlPasswordArg}" &>/dev/null <<<"SELECT 1" || status=$? + if [[ "${status}" = "0" ]]; then + break + fi + if ((optionTimeout != 0 && SECONDS - start_ts >= optionTimeout)); then + (echo >&2 "") + Log::displayError "${SCRIPT_NAME} - timeout for ${mysqlHostArg}:${mysqlPortArg} occurred after $((SECONDS - start_ts)) seconds" + status=2 + break + fi + sleep 1 +done + +if ((BASH_FRAMEWORK_ARGS_VERBOSE < __VERBOSE_LEVEL_INFO)); then + (echo >&2 "") +fi +if [[ "${status}" = "0" ]]; then + Log::displayInfo "mysql ready" +fi + +# when timed out, call command if any +if [[ -n "${commandArgs+x}" && "${commandArgs[*]}" != "" ]]; then + if [[ "${status}" != "0" && "${optionExecIfTimedOut}" = "0" ]]; then + Log::displayError "${SCRIPT_NAME} - failed to connect - strict mode - command not executed" + exit "${status}" + fi + exec "${commandArgs[@]}" +fi + +exit "${status}" diff --git a/src/_binaries/Utils/waitForMysql/waitForMysql-options.sh b/src/_binaries/Utils/waitForMysql/waitForMysql-options.sh new file mode 100755 index 00000000..57f09620 --- /dev/null +++ b/src/_binaries/Utils/waitForMysql/waitForMysql-options.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +longDescriptionFunction() { + echo -e " ${__HELP_TITLE}EXIT STATUS CODES:${__HELP_NORMAL}" + echo -e " ${__HELP_OPTION_COLOR}0${__HELP_NORMAL}: mysql is available" + echo -e " ${__HELP_OPTION_COLOR}1${__HELP_NORMAL}: indicates mysql is not available or argument error" + echo -e " ${__HELP_OPTION_COLOR}2${__HELP_NORMAL}: timeout reached" +} + +optionHelpCallback() { + waitForMysqlCommandHelp + exit 0 +} + +mysqlPortArgCallback() { + # shellcheck disable=SC2154 + if [[ ! "${mysqlPortArg}" =~ ^[0-9]+$ ]] || ((mysqlPortArg == 0)); then + Log::fatal "${SCRIPT_NAME} - invalid port option - must be greater than to 0" + fi +} + +# shellcheck disable=SC2317 # if function is overridden +unknownOption() { + commandArgs+=("$1") +} diff --git a/src/_binaries/Utils/waitForMysql.bats b/src/_binaries/Utils/waitForMysql/waitForMysql.bats similarity index 80% rename from src/_binaries/Utils/waitForMysql.bats rename to src/_binaries/Utils/waitForMysql/waitForMysql.bats index 400e640f..e0c54717 100755 --- a/src/_binaries/Utils/waitForMysql.bats +++ b/src/_binaries/Utils/waitForMysql/waitForMysql.bats @@ -1,7 +1,7 @@ #!/usr/bin/env bash # shellcheck source=src/batsHeaders.sh -source "$(cd "${BATS_TEST_DIRNAME}/../.." && pwd)/batsHeaders.sh" +source "$(cd "${BATS_TEST_DIRNAME}/../../.." && pwd)/batsHeaders.sh" setup() { export TMPDIR="${BATS_TEST_TMPDIR}" @@ -43,7 +43,7 @@ function Utils::waitForMysql::missingUser { #@test assert_failure 1 assert_lines_count 1 - assert_output --partial "ERROR - Command waitForMysql - Argument 'mysqlUserArg' should be provided at least 1 time(s)" + assert_output --partial "ERROR - Command waitForMysql - Argument 'mysqlUser' should be provided at least 1 time(s)" } function Utils::waitForMysql::missingPassword { #@test @@ -51,7 +51,7 @@ function Utils::waitForMysql::missingPassword { #@test assert_failure 1 assert_lines_count 1 - assert_output --partial "ERROR - Command waitForMysql - Argument 'mysqlPasswordArg' should be provided at least 1 time(s)" + assert_output --partial "ERROR - Command waitForMysql - Argument 'mysqlPassword' should be provided at least 1 time(s)" } function Utils::waitForMysql::invalidTimeout { #@test @@ -97,3 +97,15 @@ function Utils::waitForMysql::mysqlNotAvailableAfter1SecondTimeout { #@test assert_line --index 2 --partial "ERROR - waitForMysql - timeout for localhost:3306 occurred after" assert_lines_count 3 } + +function Utils::waitForMysql::mysqlNotAvailableAfter1SecondTimeoutLax { #@test + stub commandExists '-v mysql : exit 0' + export BASH_FRAMEWORK_COMMAND=commandExists + stub mysql '-hlocalhost -P3306 -uuser -ppassword : exit 1' + run "${binDir}/waitForMysql" localhost 3306 user password --timeout 1 --lax 2>&1 + assert_failure 2 + assert_line --index 0 --partial "INFO - Waiting for mysql" + assert_line --index 1 --partial "." + assert_line --index 2 --partial "ERROR - waitForMysql - timeout for localhost:3306 occurred after" + assert_lines_count 3 +} diff --git a/src/_binaries/commandDefinitions/optionsTimeout.yaml b/src/_binaries/commandDefinitions/optionsTimeout.yaml index eba7f017..e3ad4643 100644 --- a/src/_binaries/commandDefinitions/optionsTimeout.yaml +++ b/src/_binaries/commandDefinitions/optionsTimeout.yaml @@ -19,3 +19,12 @@ binData: alts: - --timeout - -t + + - variableName: optionExecIfTimedOut + type: Boolean + group: groupTimeoutOptions + help: Execute sub-command even if timeout is reached. + alts: + - --exec-command-on-timeout + - --lax + - -l From a43bd944a8d05cb50f3c338c8cc04ac2782b1712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Sat, 7 Sep 2024 20:35:11 +0200 Subject: [PATCH 19/24] compiled postmanCli using go compiler --- .jscpd.json | 1 + bin/cli | 1 + bin/dbImport | 2 + bin/dbImportStream | 1 + bin/postmanCli | 3001 +++++++++-------- src/BashTools/runVerboseIfNeeded.bats | 14 +- .../Database/dbImport/dbImport-main.sh | 1 + src/_binaries/Docker/cli/cli-options.sh | 1 + src/_binaries/Postman/command.postmanCli.tpl | 82 - src/_binaries/Postman/postmanCli.sh | 30 - .../Postman/postmanCli/postmanCli-binary.yaml | 66 + .../Postman/postmanCli/postmanCli-main.sh | 15 + .../Postman/postmanCli/postmanCli-options.sh | 59 + .../Postman/{ => postmanCli}/postmanCli.bats | 19 +- .../postmanCli/testsData/postmanCli.help.txt | 76 + .../Postman/testsData/postmanCli.help.txt | 72 - .../commandDefinitions/optionsProfile.sh | 1 + 17 files changed, 1890 insertions(+), 1552 deletions(-) delete mode 100644 src/_binaries/Postman/command.postmanCli.tpl delete mode 100755 src/_binaries/Postman/postmanCli.sh create mode 100644 src/_binaries/Postman/postmanCli/postmanCli-binary.yaml create mode 100755 src/_binaries/Postman/postmanCli/postmanCli-main.sh create mode 100755 src/_binaries/Postman/postmanCli/postmanCli-options.sh rename src/_binaries/Postman/{ => postmanCli}/postmanCli.bats (61%) create mode 100644 src/_binaries/Postman/postmanCli/testsData/postmanCli.help.txt delete mode 100644 src/_binaries/Postman/testsData/postmanCli.help.txt diff --git a/.jscpd.json b/.jscpd.json index 3385a7d4..61171b1b 100644 --- a/.jscpd.json +++ b/.jscpd.json @@ -30,6 +30,7 @@ "conf/postmanCli/GithubAPI/GitHubAPI-01-Basic_no_auth_postman_collection.json", "conf/postmanCli/GithubAPI/GitHubAPI-02-Advanced_with_auth_postman_collection.json", "src/_binaries/DbImport/dbImportProfile.options.tpl", + "src/_binaries/Database/dbImport/dbImport-options.sh", "**/snippets/**" ] } diff --git a/bin/cli b/bin/cli index e0ec39f7..b2c220ca 100755 --- a/bin/cli +++ b/bin/cli @@ -1498,6 +1498,7 @@ commandOptionParseFinished() { # option values +# shellcheck disable=SC2034 declare containerArg="default" declare finalUserArg="${defaultUserArg}" declare finalCommandArg=("${defaultCommandArg[@]}") diff --git a/bin/dbImport b/bin/dbImport index d48c36f4..d5afec12 100755 --- a/bin/dbImport +++ b/bin/dbImport @@ -1808,6 +1808,7 @@ profileOptionHelpFunction() { } initProfileCommandCallback() { + # shellcheck disable=SC2154 if [[ "${optionProfile}" != "default" && -n "${optionTables}" ]]; then Log::fatal "Command ${SCRIPT_NAME} - you cannot use table and profile options at the same time" fi @@ -2823,6 +2824,7 @@ if [[ "${downloadDump}" = "1" ]]; then initializeDefaultTargetMysqlOptions dbFromInstance "${fromDbName}" declare dumpHeader + # shellcheck disable=SC2154 dumpHeader=$(printf "%s\nSET names '%s';\n" "${DUMP_HEADER}" "${optionCharacterSet}") # calculate remote db dump size diff --git a/bin/dbImportStream b/bin/dbImportStream index 7acc6ab9..5729ab43 100755 --- a/bin/dbImportStream +++ b/bin/dbImportStream @@ -1671,6 +1671,7 @@ profileOptionHelpFunction() { } initProfileCommandCallback() { + # shellcheck disable=SC2154 if [[ "${optionProfile}" != "default" && -n "${optionTables}" ]]; then Log::fatal "Command ${SCRIPT_NAME} - you cannot use table and profile options at the same time" fi diff --git a/bin/postmanCli b/bin/postmanCli index b110b402..f51d1323 100755 --- a/bin/postmanCli +++ b/bin/postmanCli @@ -1,13 +1,12 @@ #!/usr/bin/env bash ############################################################################### -# GENERATED FACADE FROM https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Postman/postmanCli.sh +# GENERATED FROM https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Postman/postmanCli/postmanCli-binary.yaml # DO NOT EDIT IT # @generated ############################################################################### # shellcheck disable=SC2288,SC2034 -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/postmanCli -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE + + # ensure that no user aliases could interfere with # commands used in this script @@ -62,13 +61,6 @@ interruptManagement() { kill -s INT "$$" } trap interruptManagement INT -SCRIPT_NAME=${0##*/} -REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" -if [[ -n "${EMBED_CURRENT_DIR}" ]]; then - CURRENT_DIR="${EMBED_CURRENT_DIR}" -else - CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" -fi ################################################ # Temp dir management @@ -102,6 +94,20 @@ cleanOnExit() { } trap cleanOnExit EXIT HUP QUIT ABRT TERM + +SCRIPT_NAME=${0##*/} +REAL_SCRIPT_FILE="$(readlink -e "$(realpath "${BASH_SOURCE[0]}")")" +if [[ -n "${EMBED_CURRENT_DIR}" ]]; then + CURRENT_DIR="${EMBED_CURRENT_DIR}" +else + CURRENT_DIR="${REAL_SCRIPT_FILE%/*}" +fi +FRAMEWORK_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" +FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" +FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" +FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" +FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + # @description Log namespace provides 2 kind of functions # - Log::display* allows to display given message with # given display level @@ -132,310 +138,86 @@ export __VERBOSE_LEVEL_DEBUG=2 # @description verbose level info export __VERBOSE_LEVEL_TRACE=3 -# @description Display message using info color (bg light blue/fg white) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayInfo() { - local type="${2:-INFO}" - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logInfo "$1" "${type}" -} - -# @description Display message using debug color (gray) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayDebug() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then - Log::computeDuration - echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logDebug "$1" -} -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logWarning "$1" +# @description check if an element is contained in an array +# +# @arg $1 needle:String +# @arg $@ array:String[] +# @exitcode 0 if found +# @exitcode 1 otherwise +# @example +# Array::contains "${libPath}" "${__BASH_FRAMEWORK_IMPORTED_FILES[@]}" +Array::contains() { + local element + for element in "${@:2}"; do + [[ "${element}" = "$1" ]] && return 0 + done + return 1 } -# @description Display message using error color (red) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayError() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then - Log::computeDuration - echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logError "$1" -} -# @description load colors theme constants -# @warning if tty not opened, noColor theme will be chosen -# @arg $1 theme:String the theme to use (default, noColor) -# @arg $@ args:String[] -# @set __ERROR_COLOR String indicate error status -# @set __INFO_COLOR String indicate info status -# @set __SUCCESS_COLOR String indicate success status -# @set __WARNING_COLOR String indicate warning status -# @set __SKIPPED_COLOR String indicate skipped status -# @set __DEBUG_COLOR String indicate debug status -# @set __HELP_COLOR String indicate help status -# @set __TEST_COLOR String not used -# @set __TEST_ERROR_COLOR String not used -# @set __HELP_TITLE_COLOR String used to display help title in help strings -# @set __HELP_OPTION_COLOR String used to display highlight options in help strings -# -# @set __RESET_COLOR String reset default color +# @description concatenate each element of an array with a separator +# but wrapping text when line length is more than provided argument +# The algorithm will try not to cut the array element if it can. +# - if an arg can be placed on current line it will be, +# otherwise current line is printed and arg is added to the new +# current line +# - Empty arg is interpreted as a new line. +# - Add \r to arg in order to force break line and avoid following +# arg to be concatenated with current arg. # -# @set __HELP_EXAMPLE String to remove -# @set __HELP_TITLE String to remove -# @set __HELP_NORMAL String to remove -# shellcheck disable=SC2034 -UI::theme() { - local theme="${1-default}" - if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then - theme="noColor" +# @arg $1 glue:String +# @arg $2 maxLineLength:int +# @arg $3 indentNextLine:int +# @arg $@ array:String[] +Array::wrap2() { + local glue="${1-}" + local -i glueLength="${#glue}" + shift || true + local -i maxLineLength=$1 + shift || true + local -i indentNextLine=$1 + shift || true + local indentStr="" + if ((indentNextLine > 0)); then + indentStr="$(head -c "${indentNextLine}" &2 - Log::logFatal "$1" - exit 1 -} + ( + local currentLine + local -i currentLineLength=0 isNewline=1 argLength=0 + local -a additionalLines + local -i previousLineEmpty=0 + local arg="" -# @description create a temp file using default TMPDIR variable -# initialized in _includes/_commonHeader.sh -# @env TMPDIR String (default value /tmp) -# @arg $1 templateName:String template name to use(optional) -Framework::createTempFile() { - mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" -} - -# @description ensure env files are loaded -# @arg $@ list of default files to load at the end -# @exitcode 1 if one of env files fails to load -# @stderr diagnostics information is displayed -# shellcheck disable=SC2120 -Env::requireLoad() { - local -a defaultFiles=("$@") - # get list of possible config files - local -a configFiles=() - if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then - # BASH_FRAMEWORK_ENV_FILES is an array - configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") - fi - local localFrameworkConfigFile - localFrameworkConfigFile="$(pwd)/.framework-config" - if [[ -f "${localFrameworkConfigFile}" ]]; then - configFiles+=("${localFrameworkConfigFile}") - fi - if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then - configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") - fi - configFiles+=("${optionEnvFiles[@]}") - configFiles+=("${defaultFiles[@]}") - - for file in "${configFiles[@]}"; do - # shellcheck source=/.framework-config - CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { - Log::displayError "while loading config file: ${file}" - return 1 - } - done -} - -# @description activate or not Log::display* and Log::log* functions -# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL -# environment variables loaded by Env::requireLoad -# try to create log file and rotate it if necessary -# @noargs -# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable -# @env BASH_FRAMEWORK_DISPLAY_LEVEL int -# @env BASH_FRAMEWORK_LOG_LEVEL int -# @env BASH_FRAMEWORK_LOG_FILE String -# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 -# @exitcode 0 always successful -# @stderr diagnostics information about log file is displayed -# @require Env::requireLoad -# @require UI::requireTheme -Log::requireLoad() { - if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - export BASH_FRAMEWORK_LOG_LEVEL - fi - - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then - if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 - fi - elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then - BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} - echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 - fi - fi - - if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then - # will always be created even if not in info level - Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" - if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then - Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" - fi - fi -} - -# @description concatenate each element of an array with a separator -# but wrapping text when line length is more than provided argument -# The algorithm will try not to cut the array element if it can. -# - if an arg can be placed on current line it will be, -# otherwise current line is printed and arg is added to the new -# current line -# - Empty arg is interpreted as a new line. -# - Add \r to arg in order to force break line and avoid following -# arg to be concatenated with current arg. -# -# @arg $1 glue:String -# @arg $2 maxLineLength:int -# @arg $3 indentNextLine:int -# @arg $@ array:String[] -Array::wrap2() { - local glue="${1-}" - local -i glueLength="${#glue}" - shift || true - local -i maxLineLength=$1 - shift || true - local -i indentNextLine=$1 - shift || true - local indentStr="" - if ((indentNextLine > 0)); then - indentStr="$(head -c "${indentNextLine}" 0)); do - arg="$1" - shift || true + while (($# > 0)); do + arg="$1" + shift || true # replace tab by 2 spaces arg="${arg//$'\t'/ }" @@ -502,284 +284,153 @@ Array::wrap2() { ) | sed -E -e 's/[[:blank:]]+$//' } -# @description validates the model file and checks for file existence -# @arg $1 optionModelFile:String the model file to validate -# @arg $2 mode:Enum(pull|push|config) eg: pull/config don't check for file existence -# @exitcode 1 if file optionModelFile does not exists or invalid -# @stderr diagnostics information is displayed -Postman::Model::validate() { - local modelFile="$1" - local mode="$2" - if ! Array::contains "${mode}" pull push config; then - Log::displayError "invalid mode ${mode}" +# @description check if command specified exists or return 1 +# with error and message if not +# +# @arg $1 commandName:String on which existence must be checked +# @arg $2 helpIfNotExists:String a help command to display if the command does not exist +# +# @exitcode 1 if the command specified does not exist +# @stderr diagnostic information + help if second argument is provided +Assert::commandExists() { + local commandName="$1" + local helpIfNotExists="$2" + + "${BASH_FRAMEWORK_COMMAND:-command}" -v "${commandName}" >/dev/null 2>/dev/null || { + Log::displayError "${commandName} is not installed, please install it" + if [[ -n "${helpIfNotExists}" ]]; then + Log::displayInfo "${helpIfNotExists}" + fi return 1 - fi + } + return 0 +} - # shellcheck disable=SC2154 - if [[ ! -f "${modelFile}" ]]; then - Log::displayError "File ${modelFile} does not exist" - return 1 - fi - if ! jq -cre . &>/dev/null <"${modelFile}"; then - Log::displayError "File '${modelFile}' is not a valid json file" +# @description check if tty (interactive mode) is active +# @noargs +# @exitcode 1 if tty not active +# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive +# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive +Assert::tty() { + if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then return 1 fi - local -i errorCount=0 - - # check name key presence - local name - name="$(jq -cre .name 2>/dev/null <"${modelFile}")" || { - Log::displayError "File '${modelFile}' - missing name property" - ((++errorCount)) - } - if [[ -z "${name}" ]]; then - Log::displayError "File '${modelFile}' name property cannot be empty" - ((++errorCount)) - fi - - # check collections key presence - local expr='.collections | if type=="object" then "yes" else "no" end' - if [[ "$(jq -cre "${expr}" <"${modelFile}")" = "no" ]]; then - Log::displayError "File '${modelFile}' - collections property is missing or is not an object" - ((++errorCount)) - else - local collectionJson - local -i index=0 - # shellcheck disable=SC2030 - jq -cre '.collections | to_entries | map(.value + {key: .key}) | .[]' "${modelFile}" | while IFS=$'\n' read -r collectionJson; do - local collectionFile collectionKey - local status=0 - collectionFile="$(jq -cre .file 2>/dev/null <<<"${collectionJson}")" || status=1 - if [[ "${status}" = 0 ]]; then - collectionKey="$(jq -cre .key 2>/dev/null <<<"${collectionJson}")" || status=1 - else - collectionKey="${index}" - fi - if [[ "${status}" = 1 ]]; then - Log::displayError "File '${modelFile}' - collection ${collectionKey} - missing file property" - ((++errorCount)) - else - local configDirectory - configDirectory="$(Postman::Model::getRelativeConfigDirectory "${modelFile}")" - collectionFile="${configDirectory}/${collectionFile}" - case "${mode}" in - push) - if [[ ! -f "${collectionFile}" ]]; then - Log::displayError "File '${modelFile}' - collection ${collectionKey} - collection file ${collectionFile} does not exists" - ((++errorCount)) - continue - fi - if [[ ! -r "${collectionFile}" ]]; then - Log::displayError "File '${modelFile}' - collection ${collectionKey} - collection file ${collectionFile} is not readable" - ((++errorCount)) - continue - fi - if ! jq -cre . &>/dev/null <"${collectionFile}"; then - Log::displayError "File '${modelFile}' - collection ${collectionKey} - collection file ${collectionFile} is not a valid json file" - ((++errorCount)) - continue - fi - if ! jq -cre .info.name &>/dev/null <"${collectionFile}"; then - Log::displayError "File '${modelFile}' - collection ${collectionKey} - collection file ${collectionFile} does not seem to be a valid collection file" - ((++errorCount)) - continue - fi - ;; - - pull) - if [[ -f "${collectionFile}" && ! -w "${collectionFile}" ]]; then - Log::displayError "File '${modelFile}' - collection ${collectionKey} - collection file ${collectionFile} is not writable" - ((++errorCount)) - continue - fi - if [[ ! -f "${collectionFile}" && ! -w "${configDirectory}" ]]; then - Log::displayError "File '${modelFile}' - collection ${collectionKey} - config directory ${configDirectory} is not writable" - ((++errorCount)) - continue - fi - ;; - *) ;; - # ignore - esac - fi - # TODO environment - ((++index)) - done + if [[ "${INTERACTIVE:-0}" = "1" ]]; then + return 0 fi - - # shellcheck disable=SC2031 - ((errorCount == 0)) + tty -s } -# @description validate arguments before calling Postman::Commands::pullCollections -# @arg $1 modelFile:String model file containing the collections to be pulled -# @arg $@ list of collection references to pull (all if not provided) -# @stderr diagnostic logs -# @exitcode * if one of sub commands fails -Postman::Commands::pullCommand() { - local modelFile="$1" - shift || true - - Postman::Model::validate "${modelFile}" "pull" || return 1 - local -a refs - # shellcheck disable=SC2154 - Postman::Model::getCollectionRefs "${modelFile}" refs || return 1 - if (($# > 0)); then - # shellcheck disable=SC2154 - Postman::Model::checkIfValidCollectionRefs "${modelFile}" refs "$@" || return 1 - refs=("$@") - fi +# @description ignore exit code 141 from simple command pipes +# @example use with: +# local resultingStatus=0 +# local -a originalPipeStatus=() +# cmd1 | cmd2 || Bash::handlePipelineFailure resultingStatus originalPipeStatus || true +# [[ "${resultingStatus}" = "0" ]] +# @arg $1 resultingStatusCode:&int (passed by reference) (optional) resulting status code +# @arg $2 originalStatus:int[] (passed by reference) (optional) copy of original PIPESTATUS array +# @env PIPESTATUS assuming that this function is called like in the example provided +# @see https://unix.stackexchange.com/a/709880/582856 +Bash::handlePipelineFailure() { + local -a pipeStatusBackup=("${PIPESTATUS[@]}") + local -n handlePipelineFailure_resultingStatusCode=$1 + local -n handlePipelineFailure_originalStatus=$2 + # shellcheck disable=SC2034 + handlePipelineFailure_originalStatus=("${pipeStatusBackup[@]}") + handlePipelineFailure_resultingStatusCode=0 + local statusCode + for statusCode in "${pipeStatusBackup[@]}"; do + if ((statusCode == 141)); then + return 0 + elif ((statusCode > 0)); then + # shellcheck disable=SC2034 + handlePipelineFailure_resultingStatusCode="${statusCode}" + break + fi + done + return "${handlePipelineFailure_resultingStatusCode}" +} - if ((${#refs} == 0)); then - Log::displayError "No collection refs to pull" - return 1 - else - Log::displayDebug "Collection refs to pull ${refs[*]}" - Postman::Commands::pullCollections "${modelFile}" "${refs[@]}" || return 1 + +# @description check if all requirements are satisfied +# to execute dbImport commands +Db::checkRequirements() { + if [[ "${SKIP_REQUIREMENTS_CHECKS:-0}" = "1" ]]; then + return 0 fi + local -i failures=0 + echo + Assert::commandExists mysql "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists mysqldump "sudo apt-get install -y mysql-client" || ((++failures)) + Assert::commandExists pv "sudo apt-get install -y pv" || ((++failures)) + Assert::commandExists gawk "sudo apt-get install -y gawk" || ((++failures)) + Assert::commandExists awk "sudo apt-get install -y gawk" || ((++failures)) + Version::checkMinimal "gawk" "--version" "5.0.1" || ((++failures)) + return "${failures}" } -# @description validate arguments before calling Postman::Commands::pushCollections -# @arg $1 modelFile:String model file containing the collections to be pushed -# @arg $@ list of collection references to push (all if not provided) -# @stderr diagnostic logs -# @exitcode * if one of sub commands fails -Postman::Commands::pushCommand() { - local modelFile="$1" - shift || true - Postman::Model::validate "${modelFile}" "push" || return 1 +# @description ensure env files are loaded +# @arg $@ list of default files to load at the end +# @exitcode 1 if one of env files fails to load +# @stderr diagnostics information is displayed +# shellcheck disable=SC2120 +Env::requireLoad() { + export REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED=1 - local -a refs - # shellcheck disable=SC2154 - Postman::Model::getCollectionRefs "${modelFile}" refs || return 1 - if (($# > 0)); then - # shellcheck disable=SC2154 - Postman::Model::checkIfValidCollectionRefs "${modelFile}" refs "$@" || return 1 - refs=("$@") + local -a defaultFiles=("$@") + # get list of possible config files + local -a configFiles=() + if [[ -n "${BASH_FRAMEWORK_ENV_FILES[0]+1}" ]]; then + # BASH_FRAMEWORK_ENV_FILES is an array + configFiles+=("${BASH_FRAMEWORK_ENV_FILES[@]}") fi - - if ((${#refs} == 0)); then - Log::displayError "No collection refs to push" - return 1 - else - Log::displayDebug "Collection refs to push ${refs[*]}" - Postman::Commands::pushCollections "${modelFile}" "${refs[@]}" || return 1 + local localFrameworkConfigFile + localFrameworkConfigFile="$(pwd)/.framework-config" + if [[ -f "${localFrameworkConfigFile}" ]]; then + configFiles+=("${localFrameworkConfigFile}") + fi + if [[ -f "${FRAMEWORK_ROOT_DIR}/.framework-config" ]]; then + configFiles+=("${FRAMEWORK_ROOT_DIR}/.framework-config") fi + configFiles+=("${optionEnvFiles[@]}") + configFiles+=("${defaultFiles[@]}") + + for file in "${configFiles[@]}"; do + # shellcheck source=/.framework-config + CURRENT_LOADED_ENV_FILE="${file}" source "${file}" || { + Log::displayError "while loading config file: ${file}" + return 1 + } + done } -bashToolsDefaultConfigTemplate="${bashToolsDefaultConfigTemplate:-$( - cat <<'EOF' -# shellcheck disable=SC2034 -# Default settings -# you can override these settings by creating ${HOME}/.bash-tools/.env file - -### -### DISPLAY Level -### minimum level of the messages that will be displayed on screen -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} - -### -### DISPLAY duration -### 0: no duration is displayed on the messages -### 1: duration between previous message and current is displayed -### with the message -### -DISPLAY_DURATION=${DISPLAY_DURATION:0} - -### -### Log to file -### -### all log messages will be redirected to log file specified -### this same path will be used inside and outside of the container -### -BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} - -### -### LOG Level -### minimum level of the messages that will be logged into LOG_FILE -### -### 0: NO LOG -### 1: ERROR -### 2: WARNING -### 3: INFO -### 4: DEBUG -### -BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} - -# absolute directory containing db import sql dumps -DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} - -# garbage collect all files for which modification is greater than eg: 30 days (+30) -# each time an existing file is used by dbImport/dbImportTable -# the file modification time is set to now -DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} - -# absolute directory containing dbScripts used by dbScriptAllDatabases -SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} - -# ----------------------------------------------------- -# AWS Parameters -# ----------------------------------------------------- -S3_BASE_URL=${S3_BASE_URL:-} - -# ----------------------------------------------------- -# Postman Parameters -# ----------------------------------------------------- -POSTMAN_API_KEY= -EOF -)}" - -# @description loads ~/.bash-tools/.env if available -# if not creates it from a default template -# else check if new options need to be added -BashTools::Conf::requireLoad() { - local envFile="${HOME}/.bash-tools/.env" - if [[ ! -f "${envFile}" ]]; then - mkdir -p "${HOME}/.bash-tools" - ( - echo "#!/usr/bin/env bash" - echo "${bashToolsDefaultConfigTemplate}" - ) >"${envFile}" - Log::displayInfo "Configuration file '${envFile}' created" - else - if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then - ( - echo '# -----------------------------------------------------' - echo '# Postman Parameters' - echo '# -----------------------------------------------------' - echo 'POSTMAN_API_KEY=' - ) >>"${envFile}" - fi - fi - # shellcheck source=/conf/.env - source "${envFile}" || { - Log::displayError "impossible to load '${envFile}'" - exit 1 - } +# @description print the resolved path relative to DIR +# do not check for path existence +# @arg $1 srcFile:String the file to resolve +# @arg $2 relativeTo:String the directory +# @stdout the resolved path relative to DIR +File::relativeToDir() { + local srcFile="$1" + local relativeTo="$2" + + realpath -m --relative-to="${relativeTo}" "${srcFile}" } -# @description ensure COMMAND_BIN_DIR env var is set -# and PATH correctly prepared -# @noargs -# @set COMMAND_BIN_DIR string the directory where to find this command -# @set PATH string add directory where to find this command binary -Compiler::Facade::requireCommandBinDir() { - COMMAND_BIN_DIR="${CURRENT_DIR}" - Env::pathPrepend "${COMMAND_BIN_DIR}" + +# @description create a temp file using default TMPDIR variable +# @env TMPDIR String (default value /tmp) +# @arg $1 templateName:String template name to use(optional) +Framework::createTempFile() { + mktemp -p "${TMPDIR:-/tmp}" -t "${1:-}.XXXXXXXXXXXX" } + declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" @@ -818,30 +469,94 @@ Log::computeDuration() { fi } -# @description log message to file + +# @description Display message using debug color (gray) # @arg $1 message:String the message to display -Log::logInfo() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then - Log::logMessage "${2:-INFO}" "$1" +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayDebug() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_DEBUG)); then + Log::computeDuration + echo -e "${__DEBUG_COLOR}DEBUG - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logDebug "$1" } -# @description log message to file + +# @description Display message using error color (red) # @arg $1 message:String the message to display -Log::logDebug() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then - Log::logMessage "${2:-DEBUG}" "$1" +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayError() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_ERROR)); then + Log::computeDuration + echo -e "${__ERROR_COLOR}ERROR - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logError "$1" +} + + +# @description Display message using info color (bg light blue/fg white) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayInfo() { + local type="${2:-INFO}" + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__INFO_COLOR}${type} - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logInfo "$1" "${type}" +} + + +# @description Display message using success color (bg green/fg white) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displaySuccess() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then + Log::computeDuration + echo -e "${__SUCCESS_COLOR}SUCCESS - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + fi + Log::logSuccess "$1" +} + + +# @description Display message using warning color (yellow) +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::displayWarning() { + if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then + Log::computeDuration + echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 fi + Log::logWarning "$1" +} + + +# @description Display message using error color (red) and exit immediately with error status 1 +# @arg $1 message:String the message to display +# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs +# @env LOG_CONTEXT String allows to contextualize the log +Log::fatal() { + Log::computeDuration + echo -e "${__ERROR_COLOR}FATAL - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + Log::logFatal "$1" + exit 1 } + # @description log message to file # @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" +Log::logDebug() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_DEBUG)); then + Log::logMessage "${2:-DEBUG}" "$1" fi } + # @description log message to file # @arg $1 message:String the message to display Log::logError() { @@ -850,20 +565,6 @@ Log::logError() { fi } -# @description check if tty (interactive mode) is active -# @noargs -# @exitcode 1 if tty not active -# @env NON_INTERACTIVE if 1 consider as not interactive even if environment is interactive -# @env INTERACTIVE if 1 consider as interactive even if environment is not interactive -Assert::tty() { - if [[ "${NON_INTERACTIVE:-0}" = "1" ]]; then - return 1 - fi - if [[ "${INTERACTIVE:-0}" = "1" ]]; then - return 0 - fi - tty -s -} # @description log message to file # @arg $1 message:String the message to display @@ -871,6 +572,16 @@ Log::logFatal() { Log::logMessage "${2:-FATAL}" "$1" } + +# @description log message to file +# @arg $1 message:String the message to display +Log::logInfo() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then + Log::logMessage "${2:-INFO}" "$1" + fi +} + + # @description Internal: common log message # @example text # [date]|[levelMsg]|message @@ -884,9 +595,18 @@ Log::logFatal() { # @env BASH_FRAMEWORK_LOG_FILE String log file to use, do nothing if empty # @env BASH_FRAMEWORK_LOG_LEVEL int log level log only if > OFF or fatal messages # @stderr diagnostics information is displayed -# @require Env::requireLoad -# @require Log::requireLoad Log::logMessage() { + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Log::requireLoad has not been loaded" + exit 1 + fi + local levelMsg="$1" local msg="$2" local date @@ -898,20 +618,97 @@ Log::logMessage() { fi } -# @description To be called before logging in the log file -# @arg $1 file:string log file name -# @arg $2 maxLogFilesCount:int maximum number of log files -Log::rotate() { - local file="$1" - local maxLogFilesCount="${2:-5}" - if [[ ! -f "${file}" ]]; then - Log::displayDebug "Log file ${file} doesn't exist yet" - return 0 +# @description log message to file +# @arg $1 message:String the message to display +Log::logSuccess() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then + Log::logMessage "${2:-SUCCESS}" "$1" fi - local i - for ((i = maxLogFilesCount - 1; i > 0; i--)); do - Log::displayInfo "Log rotation ${file}.${i} to ${file}.$((i + 1))" +} + + +# @description log message to file +# @arg $1 message:String the message to display +Log::logWarning() { + if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then + Log::logMessage "${2:-WARNING}" "$1" + fi +} + + +# @description activate or not Log::display* and Log::log* functions +# based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL +# environment variables loaded by Env::requireLoad +# try to create log file and rotate it if necessary +# @noargs +# @set BASH_FRAMEWORK_LOG_LEVEL int to OFF level if BASH_FRAMEWORK_LOG_FILE is empty or not writable +# @env BASH_FRAMEWORK_DISPLAY_LEVEL int +# @env BASH_FRAMEWORK_LOG_LEVEL int +# @env BASH_FRAMEWORK_LOG_FILE String +# @env BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION int do log rotation if > 0 +# @exitcode 0 always successful +# @stderr diagnostics information about log file is displayed +Log::requireLoad() { + export REQUIRE_FUNCTION_LOG_REQUIRE_LOAD_LOADED=1 + + + if [[ "${REQUIRE_FUNCTION_ENV_REQUIRE_LOAD_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement Env::requireLoad has not been loaded" + exit 1 + fi + + if [[ "${REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED:-0}" != 1 ]]; then + echo >&2 "Requirement UI::requireTheme has not been loaded" + exit 1 + fi + + if [[ -z "${BASH_FRAMEWORK_LOG_FILE:-}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + export BASH_FRAMEWORK_LOG_LEVEL + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + if [[ ! -f "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + if [[ ! -d "${BASH_FRAMEWORK_LOG_FILE%/*}" ]]; then + if ! mkdir -p "${BASH_FRAMEWORK_LOG_FILE%/*}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - directory ${BASH_FRAMEWORK_LOG_FILE%/*} is not writable${__RESET_COLOR}" >&2 + fi + elif ! touch --no-create "${BASH_FRAMEWORK_LOG_FILE}" 2>/dev/null; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + elif [[ ! -w "${BASH_FRAMEWORK_LOG_FILE}" ]]; then + BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} + echo -e "${__ERROR_COLOR}ERROR - File ${BASH_FRAMEWORK_LOG_FILE} is not writable${__RESET_COLOR}" >&2 + fi + fi + + if ((BASH_FRAMEWORK_LOG_LEVEL > __LEVEL_OFF)); then + # will always be created even if not in info level + Log::logMessage "INFO" "Logging to file ${BASH_FRAMEWORK_LOG_FILE} - Log level ${BASH_FRAMEWORK_LOG_LEVEL}" + if ((BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION > 0)); then + Log::rotate "${BASH_FRAMEWORK_LOG_FILE}" "${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION}" + fi + fi +} + + +# @description To be called before logging in the log file +# @arg $1 file:string log file name +# @arg $2 maxLogFilesCount:int maximum number of log files +Log::rotate() { + local file="$1" + local maxLogFilesCount="${2:-5}" + + if [[ ! -f "${file}" ]]; then + Log::displayDebug "Log file ${file} doesn't exist yet" + return 0 + fi + local i + for ((i = maxLogFilesCount - 1; i > 0; i--)); do + Log::displayInfo "Log rotation ${file}.${i} to ${file}.$((i + 1))" mv "${file}."{"${i}","$((i + 1))"} &>/dev/null || true done if cp "${file}" "${file}.1" &>/dev/null; then @@ -920,72 +717,108 @@ Log::rotate() { fi } -# @description check if an element is contained in an array -# -# @arg $1 needle:String -# @arg $@ array:String[] -# @exitcode 0 if found -# @exitcode 1 otherwise -# @example -# Array::contains "${libPath}" "${__BASH_FRAMEWORK_IMPORTED_FILES[@]}" -Array::contains() { - local element - for element in "${@:2}"; do - [[ "${element}" = "$1" ]] && return 0 - done - return 1 -} -# @description config directory path relative to current execution directory -# @arg $1 configFile:String the config file -# @stdout the parent directory of config file relative to current execution directory -# @example -# executionPath=/home/wsl/bash-tools -# configPath=/home/wsl/bash-tools/conf/postmanCli/openApis.json -# result=conf/postmanCli -Postman::Model::getRelativeConfigDirectory() { - local configFile="$1" - local configDir - configDir="$(cd -- "$(dirname -- "${configFile}")" &>/dev/null && pwd -P)" - File::relativeToDir "${configDir}" "$(pwd -P)" +# @description retrieve the collection id +# associated to the given collection name +# from the postman collection file +# @arg $1 collectionFile:String +# @arg $2 collectionName:String +# @exitcode 1 if error while parsing the collection file +# @exitcode 2 if name not found +# @exitcode 3 if more than one collection matches that name +# @stderr details, on failure +# @stdout the collection id associated to a unique collection name +Postman::Collection::getCollectionIdByName() { + local collectionFile="$1" + local collectionName="$2" + local result + local errorCode="0" + result="$( + jq -cre --arg name "${collectionName}" \ + '.collections[] | select( .name == $name) | .id' 2>&1 <"${collectionFile}" + )" || errorCode="$?" + if [[ "${errorCode}" = "4" ]]; then + Log::displayWarning "collection name '${collectionName}' not found in '${collectionFile}'" + return 2 + elif [[ "${errorCode}" != "0" ]]; then + Log::displayError "Error while parsing '${collectionFile}' - error code ${errorCode} - ${result}" + return 1 + fi + if (($(wc -l <<<"${result}") > 1)); then + Log::displayError "More than one collection match the collection name '${collectionName}', please clean up your postman workspace" + return 3 + fi + echo "${result}" } -# @description get the list of collection references id from given config file -# @arg $1 configFile:String the config file to parse -# @arg $2 getCollectionRefs:&String[] (passed by reference) list of collection -# references -# @exitcode 1 - if jq parsing error, file not found or any other error -# @stderr jq error messages on failure -Postman::Model::getCollectionRefs() { - local configFile="$1" - local -n getCollectionRefs=$2 - # shellcheck disable=SC2034 - jq -cre '.collections | try keys[]' <"${configFile}" | readarray -t getCollectionRefs + +# @description retrieve the name of the collection file +# from the postman collection file +# @arg $1 collectionFile:String +# @exitcode 1 if error while parsing the collection file +# @exitcode * jq exit code, 4 for invalid file +# @stdout the collection name of the collection file +Postman::Collection::getName() { + local collectionFile="$1" + jq -cre '.info.name' <"${collectionFile}" } -# @description check that each collection references passed as parameter -# exists in the model file -# @arg $1 modelFile:String model file in which availableRefs have been retrieved -# @arg $2 availableRefs:&String[] list of known collection references -# @arg $3 modelCollectionRefs:&String[] list of collection references to check -Postman::Model::checkIfValidCollectionRefs() { + +# @description apply callback on each collection specified by modelFile +# #### callback arguments +# - modelFile +# - postmanCollectionsFile +# - collectionRef +# - collectionFile +# - collectionName +# - postmanCollectionId +# - postmanCollectionIdStatus +# @arg $1 modelFile:String model file containing the collections to be processed +# @arg $2 callback:Function callback to apply on each collection selected +# @arg $@ list of collection references to process (all if not provided) +# @stderr diagnostic logs +# @exitcode 2 if no refs specified +# @exitcode * if one of sub commands fails +Postman::Commands::forEachCollection() { local modelFile="$1" - local -n availableRefs=$2 + local callback="$2" shift 2 || true - local -a modelCollectionRefs=("$@") + local -a refs=("$@") + + if ((${#refs[@]} == 0)); then + return 2 + fi + Postman::checkApiKey "${HOME}/.bash-tools/.env" || return 1 + local postmanCollectionsFile + postmanCollectionsFile="$(Framework::createTempFile "postmanCollections")" # shellcheck disable=SC2154 - Log::displayDebug "Checking collection refs using config ${modelFile}" + Log::displayDebug "Retrieving collections from postman in ${postmanCollectionsFile}" + Postman::api getCollections >"${postmanCollectionsFile}" || return 1 - local ref - for ref in "${modelCollectionRefs[@]}"; do - if ! Array::contains "${ref}" "${availableRefs[@]}"; then - Log::displayError "Collection ref '${ref}' is not known in '${modelFile}'" - return 1 - fi + local collectionRef + for collectionRef in "${refs[@]}"; do + local collectionFile collectionName postmanCollectionId + Log::displayDebug "Retrieving collection file from collection reference ${collectionRef}" + collectionFile="$(Postman::Model::getCollectionFileByRef "${modelFile}" "${collectionRef}")" + Log::displayDebug "Retrieving collection name from collection file ${collectionFile}" + collectionName="$(Postman::Collection::getName "${collectionFile}")" + Log::displayDebug "Deducing postman collection id using ${postmanCollectionsFile} and collection name '${collectionName}'" + local postmanCollectionIdStatus="0" + postmanCollectionId="$(Postman::Collection::getCollectionIdByName "${postmanCollectionsFile}" "${collectionName}")" || status=$? + local status=0 + "${callback}" \ + "${modelFile}" "${postmanCollectionsFile}" \ + "${collectionRef}" "${collectionFile}" "${collectionName}" \ + "${postmanCollectionId}" "${postmanCollectionIdStatus}" || status=$? + case "${status}" in + 2 | 0) continue ;; + *) return 1 ;; + esac done } + # @description pull collections specified by modelFile # @arg $1 modelFile:String model file containing the collections to be pulled # @arg $@ list of collection references to pull (all if not provided) @@ -1029,6 +862,37 @@ Postman::Commands::pullCollections() { Postman::Commands::forEachCollection "${modelFile}" pullCollectionsCallback "$@" } + +# @description validate arguments before calling Postman::Commands::pullCollections +# @arg $1 modelFile:String model file containing the collections to be pulled +# @arg $@ list of collection references to pull (all if not provided) +# @stderr diagnostic logs +# @exitcode * if one of sub commands fails +Postman::Commands::pullCommand() { + local modelFile="$1" + shift || true + + Postman::Model::validate "${modelFile}" "pull" || return 1 + + local -a refs + # shellcheck disable=SC2154 + Postman::Model::getCollectionRefs "${modelFile}" refs || return 1 + if (($# > 0)); then + # shellcheck disable=SC2154 + Postman::Model::checkIfValidCollectionRefs "${modelFile}" refs "$@" || return 1 + refs=("$@") + fi + + if ((${#refs} == 0)); then + Log::displayError "No collection refs to pull" + return 1 + else + Log::displayDebug "Collection refs to pull ${refs[*]}" + Postman::Commands::pullCollections "${modelFile}" "${refs[@]}" || return 1 + fi +} + + # @description push collections specified by modelFile # @arg $1 modelFile:String model file containing the collections to be pushed # @arg $@ list of collection references to push (all if not provided) @@ -1069,41 +933,216 @@ Postman::Commands::pushCollections() { Postman::Commands::forEachCollection "${modelFile}" pushCollectionsCallback "$@" } -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" - fi - done -} -# @description load color theme -# @noargs -# @env BASH_FRAMEWORK_THEME String theme to use -# @env LOAD_THEME int 0 to avoid loading theme -# @exitcode 0 always successful -UI::requireTheme() { - if [[ "${LOAD_THEME:-1}" = "1" ]]; then - UI::theme "${BASH_FRAMEWORK_THEME-default}" +# @description validate arguments before calling Postman::Commands::pushCollections +# @arg $1 modelFile:String model file containing the collections to be pushed +# @arg $@ list of collection references to push (all if not provided) +# @stderr diagnostic logs +# @exitcode * if one of sub commands fails +Postman::Commands::pushCommand() { + local modelFile="$1" + shift || true + + Postman::Model::validate "${modelFile}" "push" || return 1 + + local -a refs + # shellcheck disable=SC2154 + Postman::Model::getCollectionRefs "${modelFile}" refs || return 1 + if (($# > 0)); then + # shellcheck disable=SC2154 + Postman::Model::checkIfValidCollectionRefs "${modelFile}" refs "$@" || return 1 + refs=("$@") + fi + + if ((${#refs} == 0)); then + Log::displayError "No collection refs to push" + return 1 + else + Log::displayDebug "Collection refs to push ${refs[*]}" + Postman::Commands::pushCollections "${modelFile}" "${refs[@]}" || return 1 fi } -# @description print the resolved path relative to DIR -# do not check for path existence -# @arg $1 srcFile:String the file to resolve -# @arg $2 relativeTo:String the directory -# @stdout the resolved path relative to DIR -File::relativeToDir() { - local srcFile="$1" - local relativeTo="$2" - realpath -m --relative-to="${relativeTo}" "${srcFile}" +# @description check that each collection references passed as parameter +# exists in the model file +# @arg $1 modelFile:String model file in which availableRefs have been retrieved +# @arg $2 availableRefs:&String[] list of known collection references +# @arg $3 modelCollectionRefs:&String[] list of collection references to check +Postman::Model::checkIfValidCollectionRefs() { + local modelFile="$1" + local -n availableRefs=$2 + shift 2 || true + local -a modelCollectionRefs=("$@") + + # shellcheck disable=SC2154 + Log::displayDebug "Checking collection refs using config ${modelFile}" + + local ref + for ref in "${modelCollectionRefs[@]}"; do + if ! Array::contains "${ref}" "${availableRefs[@]}"; then + Log::displayError "Collection ref '${ref}' is not known in '${modelFile}'" + return 1 + fi + done +} + + +# @description retrieve the file associated to the collection ref +# @arg $1 configFile:String the config file to parse +# @arg $2 ref:String the collection reference to get +# @stdout the file relative to current execution directory +# @exitcode 1 - if jq parsing error, file not found or any other error +Postman::Model::getCollectionFileByRef() { + local configFile="$1" + local ref="$2" + local file + file="$(jq -cre ".collections.${ref}.file" <"${configFile}")" || return 1 + echo "$(Postman::Model::getRelativeConfigDirectory "${configFile}")/${file}" +} + + +# @description get the list of collection references id from given config file +# @arg $1 configFile:String the config file to parse +# @arg $2 getCollectionRefs:&String[] (passed by reference) list of collection +# references +# @exitcode 1 - if jq parsing error, file not found or any other error +# @stderr jq error messages on failure +Postman::Model::getCollectionRefs() { + local configFile="$1" + local -n getCollectionRefs=$2 + # shellcheck disable=SC2034 + jq -cre '.collections | try keys[]' <"${configFile}" | readarray -t getCollectionRefs +} + + +# @description config directory path relative to current execution directory +# @arg $1 configFile:String the config file +# @stdout the parent directory of config file relative to current execution directory +# @example +# executionPath=/home/wsl/bash-tools +# configPath=/home/wsl/bash-tools/conf/postmanCli/openApis.json +# result=conf/postmanCli +Postman::Model::getRelativeConfigDirectory() { + local configFile="$1" + local configDir + configDir="$(cd -- "$(dirname -- "${configFile}")" &>/dev/null && pwd -P)" + File::relativeToDir "${configDir}" "$(pwd -P)" +} + + +# @description validates the model file and checks for file existence +# @arg $1 optionModelFile:String the model file to validate +# @arg $2 mode:Enum(pull|push|config) eg: pull/config don't check for file existence +# @exitcode 1 if file optionModelFile does not exists or invalid +# @stderr diagnostics information is displayed +Postman::Model::validate() { + local modelFile="$1" + local mode="$2" + + if ! Array::contains "${mode}" pull push config; then + Log::displayError "invalid mode ${mode}" + return 1 + fi + + # shellcheck disable=SC2154 + if [[ ! -f "${modelFile}" ]]; then + Log::displayError "File ${modelFile} does not exist" + return 1 + fi + + if ! jq -cre . &>/dev/null <"${modelFile}"; then + Log::displayError "File '${modelFile}' is not a valid json file" + return 1 + fi + local -i errorCount=0 + + # check name key presence + local name + name="$(jq -cre .name 2>/dev/null <"${modelFile}")" || { + Log::displayError "File '${modelFile}' - missing name property" + ((++errorCount)) + } + if [[ -z "${name}" ]]; then + Log::displayError "File '${modelFile}' name property cannot be empty" + ((++errorCount)) + fi + + # check collections key presence + local expr='.collections | if type=="object" then "yes" else "no" end' + if [[ "$(jq -cre "${expr}" <"${modelFile}")" = "no" ]]; then + Log::displayError "File '${modelFile}' - collections property is missing or is not an object" + ((++errorCount)) + else + local collectionJson + local -i index=0 + # shellcheck disable=SC2030 + jq -cre '.collections | to_entries | map(.value + {key: .key}) | .[]' "${modelFile}" | while IFS=$'\n' read -r collectionJson; do + local collectionFile collectionKey + local status=0 + collectionFile="$(jq -cre .file 2>/dev/null <<<"${collectionJson}")" || status=1 + if [[ "${status}" = 0 ]]; then + collectionKey="$(jq -cre .key 2>/dev/null <<<"${collectionJson}")" || status=1 + else + collectionKey="${index}" + fi + if [[ "${status}" = 1 ]]; then + Log::displayError "File '${modelFile}' - collection ${collectionKey} - missing file property" + ((++errorCount)) + else + local configDirectory + configDirectory="$(Postman::Model::getRelativeConfigDirectory "${modelFile}")" + collectionFile="${configDirectory}/${collectionFile}" + case "${mode}" in + push) + if [[ ! -f "${collectionFile}" ]]; then + Log::displayError "File '${modelFile}' - collection ${collectionKey} - collection file ${collectionFile} does not exists" + ((++errorCount)) + continue + fi + if [[ ! -r "${collectionFile}" ]]; then + Log::displayError "File '${modelFile}' - collection ${collectionKey} - collection file ${collectionFile} is not readable" + ((++errorCount)) + continue + fi + if ! jq -cre . &>/dev/null <"${collectionFile}"; then + Log::displayError "File '${modelFile}' - collection ${collectionKey} - collection file ${collectionFile} is not a valid json file" + ((++errorCount)) + continue + fi + if ! jq -cre .info.name &>/dev/null <"${collectionFile}"; then + Log::displayError "File '${modelFile}' - collection ${collectionKey} - collection file ${collectionFile} does not seem to be a valid collection file" + ((++errorCount)) + continue + fi + ;; + + pull) + if [[ -f "${collectionFile}" && ! -w "${collectionFile}" ]]; then + Log::displayError "File '${modelFile}' - collection ${collectionKey} - collection file ${collectionFile} is not writable" + ((++errorCount)) + continue + fi + if [[ ! -f "${collectionFile}" && ! -w "${configDirectory}" ]]; then + Log::displayError "File '${modelFile}' - collection ${collectionKey} - config directory ${configDirectory} is not writable" + ((++errorCount)) + continue + fi + ;; + *) ;; + # ignore + esac + fi + # TODO environment + ((++index)) + done + fi + + # shellcheck disable=SC2031 + ((errorCount == 0)) } + # @description call postman REST api # @arg $1 action:String action to call # @arg $@ args:String[] rest of arguments @@ -1203,101 +1242,20 @@ Postman::api() { esac } -# @description Display message using success color (bg green/fg white) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displaySuccess() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_INFO)); then - Log::computeDuration - echo -e "${__SUCCESS_COLOR}SUCCESS - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 + +# @description check if postman api key is set in .env file +# @arg $1 envFile:String .env file that should contain POSTMAN_API_KEY variable +# @stderr display warning message if postman api key is not filled +# @exitcode 0 always successful +Postman::checkApiKey() { + local envFile="$1" + + if grep -q '^POSTMAN_API_KEY=$' "${envFile}" 2>/dev/null || + ! grep -q '^POSTMAN_API_KEY=' "${envFile}" 2>/dev/null; then + Log::displayWarning "Please update POSTMAN_API_KEY in '${envFile}'" fi - Log::logSuccess "$1" } -# @description apply callback on each collection specified by modelFile -# #### callback arguments -# - modelFile -# - postmanCollectionsFile -# - collectionRef -# - collectionFile -# - collectionName -# - postmanCollectionId -# - postmanCollectionIdStatus -# @arg $1 modelFile:String model file containing the collections to be processed -# @arg $2 callback:Function callback to apply on each collection selected -# @arg $@ list of collection references to process (all if not provided) -# @stderr diagnostic logs -# @exitcode 2 if no refs specified -# @exitcode * if one of sub commands fails -Postman::Commands::forEachCollection() { - local modelFile="$1" - local callback="$2" - shift 2 || true - local -a refs=("$@") - - if ((${#refs[@]} == 0)); then - return 2 - fi - - Postman::checkApiKey "${HOME}/.bash-tools/.env" || return 1 - local postmanCollectionsFile - postmanCollectionsFile="$(Framework::createTempFile "postmanCollections")" - # shellcheck disable=SC2154 - Log::displayDebug "Retrieving collections from postman in ${postmanCollectionsFile}" - Postman::api getCollections >"${postmanCollectionsFile}" || return 1 - - local collectionRef - for collectionRef in "${refs[@]}"; do - local collectionFile collectionName postmanCollectionId - Log::displayDebug "Retrieving collection file from collection reference ${collectionRef}" - collectionFile="$(Postman::Model::getCollectionFileByRef "${modelFile}" "${collectionRef}")" - Log::displayDebug "Retrieving collection name from collection file ${collectionFile}" - collectionName="$(Postman::Collection::getName "${collectionFile}")" - Log::displayDebug "Deducing postman collection id using ${postmanCollectionsFile} and collection name '${collectionName}'" - local postmanCollectionIdStatus="0" - postmanCollectionId="$(Postman::Collection::getCollectionIdByName "${postmanCollectionsFile}" "${collectionName}")" || status=$? - local status=0 - "${callback}" \ - "${modelFile}" "${postmanCollectionsFile}" \ - "${collectionRef}" "${collectionFile}" "${collectionName}" \ - "${postmanCollectionId}" "${postmanCollectionIdStatus}" || status=$? - case "${status}" in - 2 | 0) continue ;; - *) return 1 ;; - esac - done -} - -# @description ignore exit code 141 from simple command pipes -# @example use with: -# local resultingStatus=0 -# local -a originalPipeStatus=() -# cmd1 | cmd2 || Bash::handlePipelineFailure resultingStatus originalPipeStatus || true -# [[ "${resultingStatus}" = "0" ]] -# @arg $1 resultingStatusCode:&int (passed by reference) (optional) resulting status code -# @arg $2 originalStatus:int[] (passed by reference) (optional) copy of original PIPESTATUS array -# @env PIPESTATUS assuming that this function is called like in the example provided -# @see https://unix.stackexchange.com/a/709880/582856 -Bash::handlePipelineFailure() { - local -a pipeStatusBackup=("${PIPESTATUS[@]}") - local -n handlePipelineFailure_resultingStatusCode=$1 - local -n handlePipelineFailure_originalStatus=$2 - # shellcheck disable=SC2034 - handlePipelineFailure_originalStatus=("${pipeStatusBackup[@]}") - handlePipelineFailure_resultingStatusCode=0 - local statusCode - for statusCode in "${pipeStatusBackup[@]}"; do - if ((statusCode == 141)); then - return 0 - elif ((statusCode > 0)); then - # shellcheck disable=SC2034 - handlePipelineFailure_resultingStatusCode="${statusCode}" - break - fi - done - return "${handlePipelineFailure_resultingStatusCode}" -} # @description display curl response only if verbose mode is not off # @arg $1 type:String type of response displayed @@ -1315,124 +1273,227 @@ Postman::displayResponse() { fi } -# @description log message to file -# @arg $1 message:String the message to display -Log::logSuccess() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_INFO)); then - Log::logMessage "${2:-SUCCESS}" "$1" + +# @description draw a line with the character passed in parameter repeated depending on terminal width +# @arg $1 character:String character to use as separator (default value #) +UI::drawLine() { + local character="${1:-#}" + local -i width=${COLUMNS:-0} + if ((width == 0)) && [[ -t 1 ]]; then + width=$(tput cols) + fi + if ((width == 0)); then + width=80 fi + printf -- "${character}%.0s" $(seq "${COLUMNS:-$([[ -t 1 ]] && tput cols || echo '80')}") + echo } -# @description check if postman api key is set in .env file -# @arg $1 envFile:String .env file that should contain POSTMAN_API_KEY variable -# @stderr display warning message if postman api key is not filled + +# @description load color theme +# @noargs +# @env BASH_FRAMEWORK_THEME String theme to use +# @env LOAD_THEME int 0 to avoid loading theme # @exitcode 0 always successful -Postman::checkApiKey() { - local envFile="$1" +UI::requireTheme() { + export REQUIRE_FUNCTION_UI_REQUIRE_THEME_LOADED=1 - if grep -q '^POSTMAN_API_KEY=$' "${envFile}" 2>/dev/null || - ! grep -q '^POSTMAN_API_KEY=' "${envFile}" 2>/dev/null; then - Log::displayWarning "Please update POSTMAN_API_KEY in '${envFile}'" + if [[ "${LOAD_THEME:-1}" = "1" ]]; then + UI::theme "${BASH_FRAMEWORK_THEME-default}" fi } -# @description retrieve the file associated to the collection ref -# @arg $1 configFile:String the config file to parse -# @arg $2 ref:String the collection reference to get -# @stdout the file relative to current execution directory -# @exitcode 1 - if jq parsing error, file not found or any other error -Postman::Model::getCollectionFileByRef() { - local configFile="$1" - local ref="$2" - local file - file="$(jq -cre ".collections.${ref}.file" <"${configFile}")" || return 1 - echo "$(Postman::Model::getRelativeConfigDirectory "${configFile}")/${file}" -} - -# @description retrieve the name of the collection file -# from the postman collection file -# @arg $1 collectionFile:String -# @exitcode 1 if error while parsing the collection file -# @exitcode * jq exit code, 4 for invalid file -# @stdout the collection name of the collection file -Postman::Collection::getName() { - local collectionFile="$1" - jq -cre '.info.name' <"${collectionFile}" -} -# @description retrieve the collection id -# associated to the given collection name -# from the postman collection file -# @arg $1 collectionFile:String -# @arg $2 collectionName:String -# @exitcode 1 if error while parsing the collection file -# @exitcode 2 if name not found -# @exitcode 3 if more than one collection matches that name -# @stderr details, on failure -# @stdout the collection id associated to a unique collection name -Postman::Collection::getCollectionIdByName() { - local collectionFile="$1" - local collectionName="$2" - local result - local errorCode="0" - result="$( - jq -cre --arg name "${collectionName}" \ - '.collections[] | select( .name == $name) | .id' 2>&1 <"${collectionFile}" - )" || errorCode="$?" - if [[ "${errorCode}" = "4" ]]; then - Log::displayWarning "collection name '${collectionName}' not found in '${collectionFile}'" - return 2 - elif [[ "${errorCode}" != "0" ]]; then - Log::displayError "Error while parsing '${collectionFile}' - error code ${errorCode} - ${result}" - return 1 +# @description load colors theme constants +# @warning if tty not opened, noColor theme will be chosen +# @arg $1 theme:String the theme to use (default, noColor) +# @arg $@ args:String[] +# @set __ERROR_COLOR String indicate error status +# @set __INFO_COLOR String indicate info status +# @set __SUCCESS_COLOR String indicate success status +# @set __WARNING_COLOR String indicate warning status +# @set __SKIPPED_COLOR String indicate skipped status +# @set __DEBUG_COLOR String indicate debug status +# @set __HELP_COLOR String indicate help status +# @set __TEST_COLOR String not used +# @set __TEST_ERROR_COLOR String not used +# @set __HELP_TITLE_COLOR String used to display help title in help strings +# @set __HELP_OPTION_COLOR String used to display highlight options in help strings +# +# @set __RESET_COLOR String reset default color +# +# @set __HELP_EXAMPLE String to remove +# @set __HELP_TITLE String to remove +# @set __HELP_NORMAL String to remove +# shellcheck disable=SC2034 +UI::theme() { + local theme="${1-default}" + if [[ ! "${theme}" =~ -force$ ]] && ! Assert::tty; then + theme="noColor" fi - if (($(wc -l <<<"${result}") > 1)); then - Log::displayError "More than one collection match the collection name '${collectionName}', please clean up your postman workspace" - return 3 + case "${theme}" in + default | default-force) + theme="default" + ;; + noColor) ;; + *) + Log::fatal "invalid theme provided" + ;; + esac + if [[ "${theme}" = "default" ]]; then + BASH_FRAMEWORK_THEME="default" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='\e[31m' # Red + __INFO_COLOR='\e[44m' # white on lightBlue + __SUCCESS_COLOR='\e[32m' # Green + __WARNING_COLOR='\e[33m' # Yellow + __SKIPPED_COLOR='\e[33m' # Yellow + __DEBUG_COLOR='\e[37m' # Gray + __HELP_COLOR='\e[7;49;33m' # Black on Gold + __TEST_COLOR='\e[100m' # Light magenta + __TEST_ERROR_COLOR='\e[41m' # white on red + __HELP_TITLE_COLOR="\e[1;37m" # Bold + __HELP_OPTION_COLOR="\e[1;34m" # Blue + # Internal: reset color + __RESET_COLOR='\e[0m' # Reset Color + # shellcheck disable=SC2155,SC2034 + __HELP_EXAMPLE="$(echo -e "\e[2;97m")" + # shellcheck disable=SC2155,SC2034 + __HELP_TITLE="$(echo -e "\e[1;37m")" + # shellcheck disable=SC2155,SC2034 + __HELP_NORMAL="$(echo -e "\033[0m")" + else + BASH_FRAMEWORK_THEME="noColor" + # check colors applicable https://misc.flogisoft.com/bash/tip_colors_and_formatting + __ERROR_COLOR='' + __INFO_COLOR='' + __SUCCESS_COLOR='' + __WARNING_COLOR='' + __SKIPPED_COLOR='' + __DEBUG_COLOR='' + __HELP_COLOR='' + __TEST_COLOR='' + __TEST_ERROR_COLOR='' + __HELP_TITLE_COLOR='' + __HELP_OPTION_COLOR='' + # Internal: reset color + __RESET_COLOR='' + __HELP_EXAMPLE='' + __HELP_TITLE='' + __HELP_NORMAL='' fi - echo "${result}" } -# FUNCTIONS -facade_main_postmanClish() { -BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/.." && pwd -P)" -if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then - FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" -else - # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR - FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" -fi -FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" -FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" -FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" -FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" +# @description Check that command version is greater than expected minimal version +# display warning if command version greater than expected minimal version +# display error if command version less than expected minimal version and exit 1 +# @arg $1 commandName:String command path +# @arg $2 argVersion:String command line parameters to launch to get command version +# @arg $3 minimalVersion:String expected minimal command version +# @arg $4 parseVersionCallback:Function +# @arg $5 help:String optional help message to display if command does not exist +# @exitcode 0 if command version greater or equal to expected minimal version +# @exitcode 1 if command version less than expected minimal version +# @exitcode 2 if command does not exist +Version::checkMinimal() { + local commandName="$1" + local argVersion="$2" + local minimalVersion="$3" + local parseVersionCallback=${4:-Version::parse} + local help="${5:-}" -# @require BashTools::Conf::requireLoad -if [[ -f "${HOME}/.bash-tools/.env" ]]; then - export BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") -fi -# REQUIRES -Env::requireLoad -UI::requireTheme -Log::requireLoad -BashTools::Conf::requireLoad -Compiler::Facade::requireCommandBinDir + Assert::commandExists "${commandName}" "${help}" || return 2 + + # shellcheck disable=SC2034 + local status=0 + # shellcheck disable=SC2034 + local -a pipeStatus=() + local version + version="$("${commandName}" "${argVersion}" 2>&1 | ${parseVersionCallback} || Bash::handlePipelineFailure status pipeStatus)" + + Log::displayDebug "check ${commandName} version ${version} against minimal ${minimalVersion}" + + Version::compare "${version}" "${minimalVersion}" || { + local result=$? + if [[ "${result}" = "1" ]]; then + Log::displayInfo "${commandName} version is ${version} greater than ${minimalVersion}" + elif [[ "${result}" = "2" ]]; then + Log::displayError "${commandName} minimal version is ${minimalVersion}, your version is ${version}" + return 1 + fi + return 0 + } -# @require Compiler::Facade::requireCommandBinDir +} -declare -a BASH_FRAMEWORK_ARGV_FILTERED=() -copyrightCallback() { - if [[ -z "${copyrightBeginYear}" ]]; then - copyrightBeginYear="$(date +%Y)" +# @description compare 2 version numbers +# @arg $1 version1:String version 1 +# @arg $2 version2:String version 2 +# @exitcode 0 if equal +# @exitcode 1 if version1 > version2 +# @exitcode 2 else +Version::compare() { + if [[ "$1" = "$2" ]]; then + return 0 fi - echo "Copyright (c) ${copyrightBeginYear}-now François Chastanet" + local IFS=. + # shellcheck disable=2206 + local i ver1=($1) ver2=($2) + # fill empty fields in ver1 with zeros + for ((i = ${#ver1[@]}; i < ${#ver2[@]}; i++)); do + ver1[i]=0 + done + for ((i = 0; i < ${#ver1[@]}; i++)); do + if [[ -z "${ver2[i]+unset}" ]] || [[ -z ${ver2[i]} ]]; then + # fill empty fields in ver2 with zeros + ver2[i]=0 + fi + if ((10#${ver1[i]} > 10#${ver2[i]})); then + return 1 + fi + if ((10#${ver1[i]} < 10#${ver2[i]})); then + return 2 + fi + done + return 0 } -# shellcheck disable=SC2317 # if function is overridden -updateArgListInfoVerboseCallback() { - BASH_FRAMEWORK_ARGV_FILTERED+=(--verbose) + +# @description filter to keep only version number from a string +# @arg $@ files:String[] the files to filter +# @exitcode * if one of the filter command fails +# @stdin you can use stdin as alternative to files argument +# @stdout the filtered content +# shellcheck disable=SC2120 +Version::parse() { + # match anything, print(p), exit on first match(Q) + sed -En \ + -e 's/\x1b\[[0-9;]*[mGKHF]//g' \ + -e 's/[^0-9]*(([0-9]+\.)*[0-9]+).*/\1/' \ + -e '//{p;Q}' \ + "$@" +} +# FUNCTIONS + + +declare -a BASH_FRAMEWORK_ARGV_FILTERED=() + +beforeParseCallback() { + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + +copyrightCallback() { + # + echo "Copyright (c) 2020-now François Chastanet" +} + +# shellcheck disable=SC2317 # if function is overridden +updateArgListInfoVerboseCallback() { + BASH_FRAMEWORK_ARGV_FILTERED+=(--verbose) } # shellcheck disable=SC2317 # if function is overridden updateArgListDebugVerboseCallback() { @@ -1459,13 +1520,14 @@ updateArgListQuietCallback() { :; } # shellcheck disable=SC2317 # if function is overridden optionHelpCallback() { - postmanCliCommand help + Log::displayError "optionHelpCallback needs to be overridden" exit 0 } # shellcheck disable=SC2317 # if function is overridden optionVersionCallback() { - echo "${SCRIPT_NAME} version 1.0" + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version 3.0" exit 0 } @@ -1483,21 +1545,22 @@ optionEnvFileCallback() { optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_INFO} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_INFO}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionDebugVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_DEBUG} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionTraceVerboseCallback() { + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='-vvv' BASH_FRAMEWORK_ARGS_VERBOSE=${__VERBOSE_LEVEL_TRACE} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${__LEVEL_DEBUG}" >>"${overrideEnvFile}" } getLevel() { @@ -1521,6 +1584,7 @@ getLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -1542,6 +1606,7 @@ getVerboseLevel() { *) Log::displayError "Command ${SCRIPT_NAME} - Invalid level ${level}" return 1 + ;; esac } @@ -1552,7 +1617,7 @@ optionDisplayLevelCallback() { logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_DISPLAY_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1561,19 +1626,20 @@ optionLogLevelCallback() { local logLevel verboseLevel logLevel="$(getLevel "${level}")" verboseLevel="$(getVerboseLevel "${level}")" + # shellcheck disable=SC2034 BASH_FRAMEWORK_ARGS_VERBOSE=${verboseLevel} - echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_LEVEL=${logLevel}" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionLogFileCallback() { local logFile="$2" - echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_LOG_FILE='${logFile}'" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden optionQuietCallback() { - echo "BASH_FRAMEWORK_QUIET_MODE=1" >> "${overrideEnvFile}" + echo "BASH_FRAMEWORK_QUIET_MODE=1" >>"${overrideEnvFile}" } # shellcheck disable=SC2317 # if function is overridden @@ -1604,6 +1670,7 @@ optionBashFrameworkConfigCallback() { defaultFrameworkConfig="$( cat <<'EOF' + # copied from src/_includes/.framework-config.default # shellcheck disable=SC2034 @@ -1621,7 +1688,7 @@ NON_FRAMEWORK_FILES_REGEXP="${NON_FRAMEWORK_FILES_REGEXP:-(^bin/|.framework-conf # describe the files that are allowed to not have an associated bats file BATS_FILE_NOT_NEEDED_REGEXP="${BATS_FILE_NOT_NEEDED_REGEXP:-(^bin/|.framework-config|.bats$|/testsData/|^manualTests/|/_.sh$|/ZZZ.sh$|/__all.sh$|^src/batsHeaders.sh$|^src/_includes)}" # describe the files that are allowed to not have a function matching the filename -FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|\.tpl$|/testsData/|^manualTests/|\.bats$}" +FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP="${FRAMEWORK_FILES_FUNCTION_MATCHING_IGNORE_REGEXP:-^bin/|^\.framework-config$|/testsData/|^manualTests/|\.bats$}" # Source directories if [[ ! -v FRAMEWORK_SRC_DIRS ]]; then FRAMEWORK_SRC_DIRS=( @@ -1637,6 +1704,7 @@ BASH_FRAMEWORK_LOG_LEVEL="${BASH_FRAMEWORK_LOG_LEVEL:-0}" BASH_FRAMEWORK_DISPLAY_LEVEL="${BASH_FRAMEWORK_DISPLAY_LEVEL:-3}" BASH_FRAMEWORK_LOG_FILE="${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/${0##*/}.log}" BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION="${BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION:-5}" + EOF )" @@ -1645,7 +1713,7 @@ overrideEnvFile="$(Framework::createTempFile "overrideEnvFile")" commandOptionParseFinished() { # load default template framework config defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" - echo "${defaultFrameworkConfig}" > "${defaultEnvFile}" + echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") if [[ -f "${envFile}" ]]; then files+=("${envFile}") @@ -1663,456 +1731,35 @@ commandOptionParseFinished() { fi } -postmanCliCommand() { - local options_parse_cmd="$1" - shift || true - if [[ "${options_parse_cmd}" = "parse" ]]; then - local -i options_parse_optionParsedCountOptionPostmanModelConfig - ((options_parse_optionParsedCountOptionPostmanModelConfig = 0)) || true - local -i options_parse_optionParsedCountOptionBashFrameworkConfig - ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true - optionConfig="0" - local -i options_parse_optionParsedCountOptionConfig - ((options_parse_optionParsedCountOptionConfig = 0)) || true - optionInfoVerbose="0" - local -i options_parse_optionParsedCountOptionInfoVerbose - ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true - optionDebugVerbose="0" - local -i options_parse_optionParsedCountOptionDebugVerbose - ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true - optionTraceVerbose="0" - local -i options_parse_optionParsedCountOptionTraceVerbose - ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionNoColor="0" - local -i options_parse_optionParsedCountOptionNoColor - ((options_parse_optionParsedCountOptionNoColor = 0)) || true - optionTheme="default" - local -i options_parse_optionParsedCountOptionTheme - ((options_parse_optionParsedCountOptionTheme = 0)) || true - optionHelp="0" - local -i options_parse_optionParsedCountOptionHelp - ((options_parse_optionParsedCountOptionHelp = 0)) || true - optionVersion="0" - local -i options_parse_optionParsedCountOptionVersion - ((options_parse_optionParsedCountOptionVersion = 0)) || true - optionQuiet="0" - local -i options_parse_optionParsedCountOptionQuiet - ((options_parse_optionParsedCountOptionQuiet = 0)) || true - local -i options_parse_optionParsedCountOptionLogLevel - ((options_parse_optionParsedCountOptionLogLevel = 0)) || true - local -i options_parse_optionParsedCountOptionLogFile - ((options_parse_optionParsedCountOptionLogFile = 0)) || true - local -i options_parse_optionParsedCountOptionDisplayLevel - ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true - local -i options_parse_argParsedCountArgCommand - ((options_parse_argParsedCountArgCommand = 0)) || true - local -i options_parse_argParsedCountCommandArgs - ((options_parse_argParsedCountCommandArgs = 0)) || true - # shellcheck disable=SC2034 - local -i options_parse_parsedArgIndex=0 - while (($# > 0)); do - local options_parse_arg="$1" - local argOptDefaultBehavior=0 - case "${options_parse_arg}" in - # Option 1/15 - # Option optionPostmanModelConfig --postman-model|-m variableType String min 0 max 1 authorizedValues '' regexp '' - --postman-model | -m) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionPostmanModelConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionPostmanModelConfig)) - # shellcheck disable=SC2034 - optionPostmanModelConfig="$1" - ;; - # Option 2/15 - # Option optionBashFrameworkConfig --bash-framework-config variableType String min 0 max 1 authorizedValues '' regexp '' - --bash-framework-config) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) - # shellcheck disable=SC2034 - optionBashFrameworkConfig="$1" - optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" - ;; - # Option 3/15 - # Option optionConfig --config variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --config) - # shellcheck disable=SC2034 - optionConfig="1" - if ((options_parse_optionParsedCountOptionConfig >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionConfig)) - ;; - # Option 4/15 - # Option optionInfoVerbose --verbose|-v variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --verbose | -v) - # shellcheck disable=SC2034 - optionInfoVerbose="1" - if ((options_parse_optionParsedCountOptionInfoVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionInfoVerbose)) - optionInfoVerboseCallback "${options_parse_arg}" - updateArgListInfoVerboseCallback "${options_parse_arg}" - ;; - # Option 5/15 - # Option optionDebugVerbose -vv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vv) - # shellcheck disable=SC2034 - optionDebugVerbose="1" - if ((options_parse_optionParsedCountOptionDebugVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDebugVerbose)) - optionDebugVerboseCallback "${options_parse_arg}" - updateArgListDebugVerboseCallback "${options_parse_arg}" - ;; - # Option 6/15 - # Option optionTraceVerbose -vvv variableType Boolean min 0 max 1 authorizedValues '' regexp '' - -vvv) - # shellcheck disable=SC2034 - optionTraceVerbose="1" - if ((options_parse_optionParsedCountOptionTraceVerbose >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTraceVerbose)) - optionTraceVerboseCallback "${options_parse_arg}" - updateArgListTraceVerboseCallback "${options_parse_arg}" - ;; - # Option 7/15 - # Option optionEnvFiles --env-file variableType StringArray min 0 max -1 authorizedValues '' regexp '' - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - ;; - # Option 8/15 - # Option optionNoColor --no-color variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --no-color) - # shellcheck disable=SC2034 - optionNoColor="1" - if ((options_parse_optionParsedCountOptionNoColor >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionNoColor)) - optionNoColorCallback "${options_parse_arg}" - updateArgListNoColorCallback "${options_parse_arg}" - ;; - # Option 9/15 - # Option optionTheme --theme variableType String min 0 max 1 authorizedValues 'default|default-force|noColor' regexp '' - --theme) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default|default-force|noColor)" - return 1 - fi - if ((options_parse_optionParsedCountOptionTheme >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionTheme)) - # shellcheck disable=SC2034 - optionTheme="$1" - optionThemeCallback "${options_parse_arg}" "${optionTheme}" - updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" - ;; - # Option 10/15 - # Option optionHelp --help|-h variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --help | -h) - # shellcheck disable=SC2034 - optionHelp="1" - if ((options_parse_optionParsedCountOptionHelp >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionHelp)) - optionHelpCallback "${options_parse_arg}" - ;; - # Option 11/15 - # Option optionVersion --version variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --version) - # shellcheck disable=SC2034 - optionVersion="1" - if ((options_parse_optionParsedCountOptionVersion >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionVersion)) - optionVersionCallback "${options_parse_arg}" - ;; - # Option 12/15 - # Option optionQuiet --quiet|-q variableType Boolean min 0 max 1 authorizedValues '' regexp '' - --quiet | -q) - # shellcheck disable=SC2034 - optionQuiet="1" - if ((options_parse_optionParsedCountOptionQuiet >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionQuiet)) - optionQuietCallback "${options_parse_arg}" - updateArgListQuietCallback "${options_parse_arg}" - ;; - # Option 13/15 - # Option optionLogLevel --log-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --log-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogLevel)) - # shellcheck disable=SC2034 - optionLogLevel="$1" - optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" - ;; - # Option 14/15 - # Option optionLogFile --log-file variableType String min 0 max 1 authorizedValues '' regexp '' - --log-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if ((options_parse_optionParsedCountOptionLogFile >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionLogFile)) - # shellcheck disable=SC2034 - optionLogFile="$1" - optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" - updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" - ;; - # Option 15/15 - # Option optionDisplayLevel --display-level variableType String min 0 max 1 authorizedValues 'OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' regexp '' - --display-level) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE)" - return 1 - fi - if ((options_parse_optionParsedCountOptionDisplayLevel >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionDisplayLevel)) - # shellcheck disable=SC2034 - optionDisplayLevel="$1" - optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" - ;; - -*) - unknownOption "${options_parse_arg}" - ;; - *) - if ((0)); then - # Technical if - never reached - : - # Argument 1/2 - # Argument argCommand min 0 max 1 authorizedValues 'pull|push' regexp '' - elif ((options_parse_parsedArgIndex >= 0 && options_parse_parsedArgIndex < 1)); then - if [[ ! "${options_parse_arg}" =~ pull|push ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Argument command - value '${options_parse_arg}' is not part of authorized values(pull|push)" - return 1 - fi - if ((options_parse_argParsedCountArgCommand >= 1)); then - Log::displayError "Command ${SCRIPT_NAME} - Argument command - Maximum number of argument occurrences reached(1)" - return 1 - fi - ((++options_parse_argParsedCountArgCommand)) - # shellcheck disable=SC2034 - argCommand="${options_parse_arg}" - # Argument 2/2 - # Argument commandArgs min 0 max -1 authorizedValues '' regexp '' - elif ((options_parse_parsedArgIndex >= 1)); then - ((++options_parse_argParsedCountCommandArgs)) - # shellcheck disable=SC2034 - commandArgs+=("${options_parse_arg}") - else - unknownOption "${options_parse_arg}" - fi - ((++options_parse_parsedArgIndex)) - ;; - esac - shift || true - done - commandOptionParseFinished - Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" - Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" - elif [[ "${options_parse_cmd}" = "help" ]]; then - Array::wrap2 " " 80 0 "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" "Push/Pull postman collections of all the configured repositories" - echo - - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "${SCRIPT_NAME}" "[OPTIONS]" "[ARGUMENTS]")" - echo -e "$(Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ - "${SCRIPT_NAME}" \ - "[--postman-model|-m ]" "[--bash-framework-config ]" "[--config]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--no-color]" "[--theme ]" "[--help|-h]" "[--version]" "[--quiet|-q]" "[--log-level ]" "[--log-file ]" "[--display-level ]")" - echo - echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - echo -e " [${__HELP_OPTION_COLOR}command${__HELP_NORMAL} {single}]" - local -a helpArray - # shellcheck disable=SC2054,SC2206 - mapfile -t helpArray < <(argCommandHelp) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " [${__HELP_OPTION_COLOR}commandArgs${__HELP_NORMAL} {list} (optional)]" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=($'list of postman collection\'s references to pull or push\r\nor no argument to pull or push all the collections') - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}PUSH/PULL OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--postman-model${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-m ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=($'postmanCli model file to use\r\nDefault value: /postmanCli.collections.json') - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo - echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(use\ alternate\ bash\ framework\ configuration.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ configuration) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(info\ level\ verbose\ mode\ \(alias\ of\ --display-level\ INFO\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(debug\ level\ verbose\ mode\ \(alias\ of\ --display-level\ DEBUG\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(trace\ level\ verbose\ mode\ \(alias\ of\ --display-level\ TRACE\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Load\ the\ specified\ env\ file\ \(deprecated\,\ please\ use\ --bash-framework-config\ option\ instead\)) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Produce\ monochrome\ output.\ alias\ of\ --theme\ noColor.) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(choose\ color\ theme\ -\ default-force\ means\ colors\ will\ be\ produced\ even\ if\ command\ is\ piped) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Default value: default' - echo ' Possible values: default|default-force|noColor' - echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Display\ this\ command\ help) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Print\ version\ information\ and\ quit) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(quiet\ mode\,\ doesn\'t\ display\ any\ output) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(Set\ log\ file) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" - local -a helpArray - # shellcheck disable=SC2054 - helpArray=(set\ display\ level) - echo -e " $(Array::wrap2 " " 76 4 "${helpArray[@]}")" - echo ' Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE' - echo - echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" - echo '1.0' - echo - echo -e "${__HELP_TITLE_COLOR}AUTHOR:${__RESET_COLOR}" - echo '[François Chastanet](https://github.com/fchastanet)' - echo - echo -e "${__HELP_TITLE_COLOR}SOURCE FILE:${__RESET_COLOR}" - echo 'https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Postman/postmanCli.sh' - echo - echo -e "${__HELP_TITLE_COLOR}LICENSE:${__RESET_COLOR}" - echo 'MIT License' - echo - Array::wrap2 ' ' 76 4 "$(copyrightCallback)" - else - Log::displayError "Command ${SCRIPT_NAME} - Option command invalid: '${options_parse_cmd}'" - return 1 - fi + +longDescriptionFunction() { + echo -e " ${__HELP_TITLE}EXIT CODES:${__HELP_NORMAL}" + echo -e " ${__HELP_OPTION_COLOR}1${__HELP_NORMAL}: if commit does not exists" + echo -e " ${__HELP_OPTION_COLOR}2${__HELP_NORMAL}: if ref is not convertible to commit oid" + echo -e " ${__HELP_OPTION_COLOR}3${__HELP_NORMAL}: if commit is not included in given branch" +} + +optionHelpCallback() { + postmanCliCommandHelp + exit 0 } -declare optionPostmanModelConfig="$(pwd -P)/postmanCli.collections.json" -declare copyrightBeginYear="2023" -argCommandHelp() { - echo "${__HELP_OPTION_COLOR}pull${__HELP_NORMAL}" $'\r' - echo " Pull collections from Postman back to repositories." $'\r' - echo "${__HELP_OPTION_COLOR}push${__HELP_NORMAL}" $'\r' - echo ' Push repositories collections to Postman.' +optionPostmanModelConfigHelpFunction() { + echo " postmanCli model file to use" + echo " Default value: /postmanCli.collections.json" +} + +argCommandHelpFunction() { + echo -e " ${__HELP_OPTION_COLOR}pull${__HELP_NORMAL}" + echo -e " Pull collections from Postman back to repositories." + echo -e " ${__HELP_OPTION_COLOR}push${__HELP_NORMAL}" + echo -e ' Push repositories collections to Postman.' +} + +commandArgsHelpFunction() { + echo -e " List of postman collection's references to pull or push" + echo -e " or no argument to pull or push all the collections" } # shellcheck disable=SC2317 # if function is overridden @@ -2120,39 +1767,691 @@ unknownOption() { commandArgs+=("$1") } +postmanCliCommandCallback() { + if [[ -z "${optionPostmanModelConfig}" ]]; then + optionPostmanModelConfig="${CURRENT_DIR}/postmanCli.collections.json" + fi + if [[ ! -f "${optionPostmanModelConfig}" ]]; then + Log::displayError "Please provide a valid postman config file, using --postman-model option." + exit 1 + fi + if [[ "${displayConfig}" = "1" ]]; then + # shellcheck disable=SC2154 + Postman::Model::validate "${optionPostmanModelConfig}" "config" + original_displayConfig + UI::drawLine "-" + printf '%-40s = %s\n' "POSTMAN_API_KEY" "${POSTMAN_API_KEY:0:15}...(truncated)" + exit 0 + fi +} + eval "original_$(declare -f displayConfig | grep -v 'exit 0')" +declare displayConfig=0 displayConfig() { - Postman::Model::validate "${optionPostmanModelConfig}" "config" - original_displayConfig - UI::drawLine "-" - printf '%-40s = %s\n' "POSTMAN_API_KEY" "${POSTMAN_API_KEY:0:15}...(truncated)" - exit 0 + displayConfig=1 } -# call main -postmanCliCommand parse "$@" -run() { + + +optionVersionCallback() { # shellcheck disable=SC2154 - case "${argCommand}" in - pull) - Postman::Commands::pullCommand "${optionPostmanModelConfig}" "${commandArgs[@]}" - ;; - push) - Postman::Commands::pushCommand "${optionPostmanModelConfig}" "${commandArgs[@]}" - ;; - *) - Log::displayError "Invalid command ${argCommand}" - exit 1 - ;; - esac + echo "${SCRIPT_NAME} version 3.0" + Db::checkRequirements + exit 0 } -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run "$@" &>/dev/null -else - run "$@" -fi + +# ------------------------------------------ +# Command postmanCliCommand +# ------------------------------------------ + +# options variables initialization +declare optionHelp="0" +declare optionConfig="0" +declare optionBashFrameworkConfig="" +declare optionInfoVerbose="0" +declare optionDebugVerbose="0" +declare optionTraceVerbose="0" +declare -a optionEnvFiles=() +declare optionLogLevel="" +declare optionLogFile="" +declare optionDisplayLevel="" +declare optionNoColor="0" +declare optionTheme="default" +declare optionVersion="0" +declare optionQuiet="0" +declare optionPostmanModelConfig="" +# arguments variables initialization +declare argCommand="" +declare commandArgs="" +# @description parse command options and arguments for postmanCliCommand +postmanCliCommandParse() { + Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" + Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" + optionHelp="0" + local -i options_parse_optionParsedCountOptionHelp + ((options_parse_optionParsedCountOptionHelp = 0)) || true + optionConfig="0" + local -i options_parse_optionParsedCountOptionConfig + ((options_parse_optionParsedCountOptionConfig = 0)) || true + optionBashFrameworkConfig="" + local -i options_parse_optionParsedCountOptionBashFrameworkConfig + ((options_parse_optionParsedCountOptionBashFrameworkConfig = 0)) || true + optionInfoVerbose="0" + local -i options_parse_optionParsedCountOptionInfoVerbose + ((options_parse_optionParsedCountOptionInfoVerbose = 0)) || true + optionDebugVerbose="0" + local -i options_parse_optionParsedCountOptionDebugVerbose + ((options_parse_optionParsedCountOptionDebugVerbose = 0)) || true + optionTraceVerbose="0" + local -i options_parse_optionParsedCountOptionTraceVerbose + ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true + + optionLogLevel="" + local -i options_parse_optionParsedCountOptionLogLevel + ((options_parse_optionParsedCountOptionLogLevel = 0)) || true + optionLogFile="" + local -i options_parse_optionParsedCountOptionLogFile + ((options_parse_optionParsedCountOptionLogFile = 0)) || true + optionDisplayLevel="" + local -i options_parse_optionParsedCountOptionDisplayLevel + ((options_parse_optionParsedCountOptionDisplayLevel = 0)) || true + optionNoColor="0" + local -i options_parse_optionParsedCountOptionNoColor + ((options_parse_optionParsedCountOptionNoColor = 0)) || true + optionTheme="default" + local -i options_parse_optionParsedCountOptionTheme + ((options_parse_optionParsedCountOptionTheme = 0)) || true + optionVersion="0" + local -i options_parse_optionParsedCountOptionVersion + ((options_parse_optionParsedCountOptionVersion = 0)) || true + optionQuiet="0" + local -i options_parse_optionParsedCountOptionQuiet + ((options_parse_optionParsedCountOptionQuiet = 0)) || true + optionPostmanModelConfig="" + local -i options_parse_optionParsedCountOptionPostmanModelConfig + ((options_parse_optionParsedCountOptionPostmanModelConfig = 0)) || true + + argCommand="" + local -i options_parse_argParsedCountArgCommand + ((options_parse_argParsedCountArgCommand = 0)) || true + + commandArgs="" + + + # shellcheck disable=SC2034 + local -i options_parse_parsedArgIndex=0 + while (($# > 0)); do + local options_parse_arg="$1" + local argOptDefaultBehavior=0 + case "${options_parse_arg}" in + # Option 1/15 + # optionHelp alts --help|-h + # type: Boolean min 0 max 1 + --help | -h) + # shellcheck disable=SC2034 + optionHelp="1" + + if ((options_parse_optionParsedCountOptionHelp >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionHelp)) + optionHelpCallback "${options_parse_arg}" "${optionHelp}" + + ;; + + # Option 2/15 + # optionConfig alts --config + # type: Boolean min 0 max 1 + --config) + # shellcheck disable=SC2034 + optionConfig="1" + + if ((options_parse_optionParsedCountOptionConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionConfig)) + ;; + + # Option 3/15 + # optionBashFrameworkConfig alts --bash-framework-config + # type: String min 0 max 1 + --bash-framework-config) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionBashFrameworkConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionBashFrameworkConfig)) + # shellcheck disable=SC2034 + optionBashFrameworkConfig="$1" + optionBashFrameworkConfigCallback "${options_parse_arg}" "${optionBashFrameworkConfig}" + + ;; + + # Option 4/15 + # optionInfoVerbose alts --verbose|-v + # type: Boolean min 0 max 1 + --verbose | -v) + # shellcheck disable=SC2034 + optionInfoVerbose="1" + + if ((options_parse_optionParsedCountOptionInfoVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionInfoVerbose)) + optionInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + updateArgListInfoVerboseCallback "${options_parse_arg}" "${optionInfoVerbose}" + + ;; + + # Option 5/15 + # optionDebugVerbose alts -vv + # type: Boolean min 0 max 1 + -vv) + # shellcheck disable=SC2034 + optionDebugVerbose="1" + + if ((options_parse_optionParsedCountOptionDebugVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDebugVerbose)) + optionDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + updateArgListDebugVerboseCallback "${options_parse_arg}" "${optionDebugVerbose}" + + ;; + + # Option 6/15 + # optionTraceVerbose alts -vvv + # type: Boolean min 0 max 1 + -vvv) + # shellcheck disable=SC2034 + optionTraceVerbose="1" + + if ((options_parse_optionParsedCountOptionTraceVerbose >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTraceVerbose)) + optionTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + updateArgListTraceVerboseCallback "${options_parse_arg}" "${optionTraceVerbose}" + + ;; + + # Option 7/15 + # optionEnvFiles alts --env-file + # type: StringArray min 0 max -1 + --env-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + ((++options_parse_optionParsedCountOptionEnvFiles)) + optionEnvFiles+=("$1") + optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" + + ;; + + # Option 8/15 + # optionLogLevel alts --log-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --log-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogLevel)) + # shellcheck disable=SC2034 + optionLogLevel="$1" + optionLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + updateArgListLogLevelCallback "${options_parse_arg}" "${optionLogLevel}" + + ;; + + # Option 9/15 + # optionLogFile alts --log-file + # type: String min 0 max 1 + --log-file) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionLogFile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionLogFile)) + # shellcheck disable=SC2034 + optionLogFile="$1" + optionLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + updateArgListLogFileCallback "${options_parse_arg}" "${optionLogFile}" + + ;; + + # Option 10/15 + # optionDisplayLevel alts --display-level + # type: String min 0 max 1 + # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE + --display-level) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionDisplayLevel >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionDisplayLevel)) + # shellcheck disable=SC2034 + optionDisplayLevel="$1" + optionDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + updateArgListDisplayLevelCallback "${options_parse_arg}" "${optionDisplayLevel}" + + ;; + + # Option 11/15 + # optionNoColor alts --no-color + # type: Boolean min 0 max 1 + --no-color) + # shellcheck disable=SC2034 + optionNoColor="1" + + if ((options_parse_optionParsedCountOptionNoColor >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionNoColor)) + optionNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + updateArgListNoColorCallback "${options_parse_arg}" "${optionNoColor}" + + ;; + + # Option 12/15 + # optionTheme alts --theme + # type: String min 0 max 1 + # authorizedValues: default|default-force|noColor + --theme) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + if [[ ! "$1" =~ default|default-force|noColor ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + return 1 + fi + + if ((options_parse_optionParsedCountOptionTheme >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionTheme)) + # shellcheck disable=SC2034 + optionTheme="$1" + optionThemeCallback "${options_parse_arg}" "${optionTheme}" + + updateArgListThemeCallback "${options_parse_arg}" "${optionTheme}" + + ;; + + # Option 13/15 + # optionVersion alts --version + # type: Boolean min 0 max 1 + --version) + # shellcheck disable=SC2034 + optionVersion="1" + + if ((options_parse_optionParsedCountOptionVersion >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionVersion)) + optionVersionCallback "${options_parse_arg}" "${optionVersion}" + + ;; + + # Option 14/15 + # optionQuiet alts --quiet|-q + # type: Boolean min 0 max 1 + --quiet | -q) + # shellcheck disable=SC2034 + optionQuiet="1" + + if ((options_parse_optionParsedCountOptionQuiet >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionQuiet)) + optionQuietCallback "${options_parse_arg}" "${optionQuiet}" + + updateArgListQuietCallback "${options_parse_arg}" "${optionQuiet}" + + ;; + + # Option 15/15 + # optionPostmanModelConfig alts --postman-model|-m + # type: String min 0 max 1 + --postman-model | -m) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionPostmanModelConfig >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionPostmanModelConfig)) + # shellcheck disable=SC2034 + optionPostmanModelConfig="$1" + ;; + + -*) + unknownOption "${options_parse_arg}" || argOptDefaultBehavior=$? + ;; + *) + ((minParsedArgIndex0 = 0)) || true + ((maxParsedArgIndex0 = 0)) || true + ((minParsedArgIndex1 = minParsedArgIndex0 + 0)) || true + ((maxParsedArgIndex1 = maxParsedArgIndex0 + 1)) || true + ((minParsedArgIndex2 = minParsedArgIndex1 + 0)) || true + ((maxParsedArgIndex2 = maxParsedArgIndex1)) || true + ((incrementArg = 1 )) + if ((0)); then + # Technical if - never reached + : + + # Argument 1/2 - argCommand + # Argument argCommand min 0 max 1 + # Argument argCommand authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && + options_parse_parsedArgIndex < maxParsedArgIndex1 )); then + if ((options_parse_argParsedCountArgCommand >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Argument command - Maximum number of argument occurrences reached(1)" + return 1 + fi + ((++options_parse_argParsedCountArgCommand)) + # shellcheck disable=SC2034 + argCommand="${options_parse_arg}" + + + # Argument 2/2 - commandArgs + # Argument commandArgs min 0 max -1 + # Argument commandArgs authorizedValues: + elif (( options_parse_parsedArgIndex >= minParsedArgIndex1 )); then + ((++options_parse_argParsedCountCommandArgs)) + # shellcheck disable=SC2034 + commandArgs="${options_parse_arg}" + + + # else too much args + else + + + # no arg configured, call unknownArgumentCallback + + # shellcheck disable=SC2317 + unknownOption "${options_parse_arg}" + + fi + if ((incrementArg == 1)); then + ((++options_parse_parsedArgIndex)) + fi + ;; + esac + shift || true + done || return $? + + + commandOptionParseFinished + postmanCliCommandCallback } -facade_main_postmanClish "$@" +# @description display command options and arguments help for postmanCliCommand +postmanCliCommandHelp() { + echo -e "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" + Array::wrap2 ' ' 76 4 " " "Push/Pull postman collections of all the configured repositories." + + echo + echo + + # ------------------------------------------ + # usage section + # ------------------------------------------ + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" "postmanCli [OPTIONS] [ARGUMENTS]" + echo + # ------------------------------------------ + # usage/options section + # ------------------------------------------ + optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--postman-model|-m ]" + ) + Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ + "postmanCli" "${optionsAltList[@]}" + echo + + # ------------------------------------------ + # usage/arguments section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" + + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}command${__HELP_NORMAL} {single}] + " + argCommandHelpFunction + + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}ref${__HELP_NORMAL} {list} (optional)] + " + commandArgsHelpFunction + + # ------------------------------------------ + # options section + # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays this command help" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Displays configuration" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" + Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set log file" + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Set display level" + echo + Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" + echo + + + echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." + echo + Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" + echo + + Array::wrap2 ' ' 76 6 " Default value: " "default" + echo + + echo -e " ${__HELP_OPTION_COLOR}--version${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Print version information and quit." + echo + + + + echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" + Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." + echo + + + echo + echo -e "${__HELP_TITLE_COLOR}PUSH/PULL OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--postman-model${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-m ${__HELP_NORMAL} {single}" + optionPostmanModelConfigHelpFunction + + + + # ------------------------------------------ + # longDescription section + # ------------------------------------------ + echo + echo + echo -e "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" + longDescriptionFunction + # ------------------------------------------ + # version section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}VERSION: ${__RESET_COLOR}" + echo "3.0" + # ------------------------------------------ + # author section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}AUTHOR: ${__RESET_COLOR}" + echo "[François Chastanet](https://github.com/fchastanet)" + # ------------------------------------------ + # sourceFile section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}SOURCE FILE: ${__RESET_COLOR}" + echo "https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Postman/postmanCli/postmanCli-binary.yaml" + # ------------------------------------------ + # license section + # ------------------------------------------ + echo + echo -n -e "${__HELP_TITLE_COLOR}LICENSE: ${__RESET_COLOR}" + echo "MIT License" + # ------------------------------------------ + # copyright section + # ------------------------------------------ + Array::wrap2 ' ' 76 0 "$(copyrightCallback)" +} + + +beforeParseCallback + +postmanCliCommandParse "$@" +MAIN_FUNCTION_NAME="main" +main() { + +# shellcheck disable=SC2154 +case "${argCommand}" in + pull) + Postman::Commands::pullCommand "${optionPostmanModelConfig}" "${commandArgs[@]}" + ;; + push) + Postman::Commands::pushCommand "${optionPostmanModelConfig}" "${commandArgs[@]}" + ;; + *) + Log::displayError "Invalid command ${argCommand}" + exit 1 + ;; +esac + +} + +# if file is sourced avoid calling main function +# shellcheck disable=SC2178 +BASH_SOURCE=".$0" # cannot be changed in bash +# shellcheck disable=SC2128 +if test ".$0" == ".${BASH_SOURCE}"; then + if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then + main "$@" &>/dev/null + else + main "$@" + fi +fi diff --git a/src/BashTools/runVerboseIfNeeded.bats b/src/BashTools/runVerboseIfNeeded.bats index eb60e784..2aa45a3c 100755 --- a/src/BashTools/runVerboseIfNeeded.bats +++ b/src/BashTools/runVerboseIfNeeded.bats @@ -10,28 +10,28 @@ teardown() { } function BashTools::runVerboseIfNeeded::noTrace { #@test - run BashTools::runVerboseIfNeeded echo "coucou" - assert_output "coucou" + run BashTools::runVerboseIfNeeded echo "verbose" + assert_output "verbose" assert_success } function BashTools::runVerboseIfNeeded::trace { #@test cmdTest() { - BASH_FRAMEWORK_ARGS_VERBOSE=__VERBOSE_LEVEL_INFO BashTools::runVerboseIfNeeded echo "coucou" + BASH_FRAMEWORK_ARGS_VERBOSE=__VERBOSE_LEVEL_INFO BashTools::runVerboseIfNeeded echo "verbose" } run cmdTest assert_lines_count 2 - assert_line --index 0 '+ echo coucou' - assert_line --index 1 "coucou" + assert_line --index 0 '+ echo verbose' + assert_line --index 1 "verbose" assert_success } function BashTools::runVerboseIfNeeded::redirectCmdOutputs { #@test export BASH_FRAMEWORK_ARGS_VERBOSE cmdTest() { - BASH_FRAMEWORK_ARGS_VERBOSE=__VERBOSE_LEVEL_INFO optionRedirectCmdOutputs="/dev/null" BashTools::runVerboseIfNeeded echo "coucou" + BASH_FRAMEWORK_ARGS_VERBOSE=__VERBOSE_LEVEL_INFO optionRedirectCmdOutputs="/dev/null" BashTools::runVerboseIfNeeded echo "verbose" } run cmdTest - assert_output '+ echo coucou' + assert_output '+ echo verbose' assert_success } diff --git a/src/_binaries/Database/dbImport/dbImport-main.sh b/src/_binaries/Database/dbImport/dbImport-main.sh index 3549c1d1..c0ba6418 100755 --- a/src/_binaries/Database/dbImport/dbImport-main.sh +++ b/src/_binaries/Database/dbImport/dbImport-main.sh @@ -79,6 +79,7 @@ if [[ "${downloadDump}" = "1" ]]; then initializeDefaultTargetMysqlOptions dbFromInstance "${fromDbName}" declare dumpHeader + # shellcheck disable=SC2154 dumpHeader=$(printf "%s\nSET names '%s';\n" "${DUMP_HEADER}" "${optionCharacterSet}") # calculate remote db dump size diff --git a/src/_binaries/Docker/cli/cli-options.sh b/src/_binaries/Docker/cli/cli-options.sh index 453cb457..cc8f297f 100755 --- a/src/_binaries/Docker/cli/cli-options.sh +++ b/src/_binaries/Docker/cli/cli-options.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash # option values +# shellcheck disable=SC2034 declare containerArg="default" declare finalUserArg="${defaultUserArg}" declare finalCommandArg=("${defaultCommandArg[@]}") diff --git a/src/_binaries/Postman/command.postmanCli.tpl b/src/_binaries/Postman/command.postmanCli.tpl deleted file mode 100644 index 95c17fa3..00000000 --- a/src/_binaries/Postman/command.postmanCli.tpl +++ /dev/null @@ -1,82 +0,0 @@ -% -declare versionNumber="1.0" -declare commandFunctionName="postmanCliCommand" -declare help="Push/Pull postman collections of all the configured repositories" -# shellcheck disable=SC2016 -% - -.INCLUDE "$(dynamicTemplateDir _binaries/options/options.base.tpl)" - -% - -# shellcheck source=/dev/null -source <( - Options::generateGroup \ - --title "PUSH/PULL OPTIONS:" \ - --function-name groupPushPullFunction - - # shellcheck disable=SC2016 - Options::generateOption \ - --variable-type String \ - --help $'postmanCli model file to use\r -Default value: /postmanCli.collections.json' \ - --group groupPushPullFunction \ - --alt "--postman-model" \ - --alt "-m" \ - --variable-name "optionPostmanModelConfig" \ - --function-name optionPostmanModelConfigFunction - - argCommandHelp() { :; } - Options::generateArg \ - --variable-name "argCommand" \ - --min 0 \ - --max 1 \ - --name "command" \ - --authorized-values 'pull|push' \ - --help argCommandHelp \ - --function-name argCommandFunction - - Options::generateArg \ - --variable-name "commandArgs" \ - --min 0 \ - --max -1 \ - --name "commandArgs" \ - --help \ - $'list of postman collection\'s references to pull or push\r -or no argument to pull or push all the collections' \ - --function-name commandArgsFunction -) - -options+=( - optionPostmanModelConfigFunction - argCommandFunction - commandArgsFunction - --unknown-option-callback unknownOption - --unknown-argument-callback unknownOption -) - -Options::generateCommand "${options[@]}" -% -declare optionPostmanModelConfig="$(pwd -P)/postmanCli.collections.json" -declare copyrightBeginYear="2024" - -argCommandHelp() { - echo "${__HELP_OPTION_COLOR}pull${__HELP_NORMAL}" $'\r' - echo " Pull collections from Postman back to repositories." $'\r' - echo "${__HELP_OPTION_COLOR}push${__HELP_NORMAL}" $'\r' - echo ' Push repositories collections to Postman.' -} - -# shellcheck disable=SC2317 # if function is overridden -unknownOption() { - commandArgs+=("$1") -} - -eval "original_$(declare -f displayConfig | grep -v 'exit 0')" -displayConfig() { - Postman::Model::validate "${optionPostmanModelConfig}" "config" - original_displayConfig - UI::drawLine "-" - printf '%-40s = %s\n' "POSTMAN_API_KEY" "${POSTMAN_API_KEY:0:15}...(truncated)" - exit 0 -} diff --git a/src/_binaries/Postman/postmanCli.sh b/src/_binaries/Postman/postmanCli.sh deleted file mode 100755 index d547a5bc..00000000 --- a/src/_binaries/Postman/postmanCli.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -# BIN_FILE=${BASH_TOOLS_ROOT_DIR}/bin/postmanCli -# VAR_RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR=.. -# FACADE - -.INCLUDE "$(dynamicTemplateDir _binaries/Postman/command.postmanCli.tpl)" -# call main -postmanCliCommand parse "$@" - -run() { - # shellcheck disable=SC2154 - case "${argCommand}" in - pull) - Postman::Commands::pullCommand "${optionPostmanModelConfig}" "${commandArgs[@]}" - ;; - push) - Postman::Commands::pushCommand "${optionPostmanModelConfig}" "${commandArgs[@]}" - ;; - *) - Log::displayError "Invalid command ${argCommand}" - exit 1 - ;; - esac -} - -if [[ "${BASH_FRAMEWORK_QUIET_MODE:-0}" = "1" ]]; then - run "$@" &>/dev/null -else - run "$@" -fi diff --git a/src/_binaries/Postman/postmanCli/postmanCli-binary.yaml b/src/_binaries/Postman/postmanCli/postmanCli-binary.yaml new file mode 100644 index 00000000..ed68379a --- /dev/null +++ b/src/_binaries/Postman/postmanCli/postmanCli-binary.yaml @@ -0,0 +1,66 @@ +extends: + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" + - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" + +vars: + SRC_FILE_PATH: src/_binaries/Postman/postmanCli/postmanCli-binary.yaml + +compilerConfig: + targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/postmanCli" + relativeRootDirBasedOnTargetDir: .. + srcDirs: + - ${BASH_TOOLS_ROOT_DIR}/src +binData: + commands: + default: + functionName: postmanCliCommand + version: "3.0" + copyrightBeginYear: 2020 + commandName: postmanCli + beforeParseCallbacks: + - beforeParseCallback + unknownOptionCallbacks: + - unknownOption + unknownArgumentCallbacks: + - unknownOption + callbacks: + - postmanCliCommandCallback + definitionFiles: + 20: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Postman/postmanCli/postmanCli-options.sh + mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Postman/postmanCli/postmanCli-main.sh + help: Push/Pull postman collections of all the configured repositories. + longDescription: longDescriptionFunction + args: + - type: String + min: 0 + max: 1 + help: argCommandHelpFunction + authorizedValues: + - value: pull + help: Pull + - value: push + help: Push + name: command + variableName: argCommand + + - type: String + min: 0 + max: -1 + help: commandArgsHelpFunction + name: ref + variableName: commandArgs + + optionGroups: + groupPushPull: + title: "PUSH/PULL OPTIONS:" + + options: + - variableName: optionPostmanModelConfig + type: String + group: groupPushPull + help: optionPostmanModelConfigHelpFunction + helpValueName: configFile + alts: + - --postman-model + - -m diff --git a/src/_binaries/Postman/postmanCli/postmanCli-main.sh b/src/_binaries/Postman/postmanCli/postmanCli-main.sh new file mode 100755 index 00000000..c2d0a606 --- /dev/null +++ b/src/_binaries/Postman/postmanCli/postmanCli-main.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +# shellcheck disable=SC2154 +case "${argCommand}" in + pull) + Postman::Commands::pullCommand "${optionPostmanModelConfig}" "${commandArgs[@]}" + ;; + push) + Postman::Commands::pushCommand "${optionPostmanModelConfig}" "${commandArgs[@]}" + ;; + *) + Log::displayError "Invalid command ${argCommand}" + exit 1 + ;; +esac diff --git a/src/_binaries/Postman/postmanCli/postmanCli-options.sh b/src/_binaries/Postman/postmanCli/postmanCli-options.sh new file mode 100755 index 00000000..0397dfb9 --- /dev/null +++ b/src/_binaries/Postman/postmanCli/postmanCli-options.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +longDescriptionFunction() { + echo -e " ${__HELP_TITLE}EXIT CODES:${__HELP_NORMAL}" + echo -e " ${__HELP_OPTION_COLOR}1${__HELP_NORMAL}: if commit does not exists" + echo -e " ${__HELP_OPTION_COLOR}2${__HELP_NORMAL}: if ref is not convertible to commit oid" + echo -e " ${__HELP_OPTION_COLOR}3${__HELP_NORMAL}: if commit is not included in given branch" +} + +optionHelpCallback() { + postmanCliCommandHelp + exit 0 +} + +optionPostmanModelConfigHelpFunction() { + echo " postmanCli model file to use" + echo " Default value: /postmanCli.collections.json" +} + +argCommandHelpFunction() { + echo -e " ${__HELP_OPTION_COLOR}pull${__HELP_NORMAL}" + echo -e " Pull collections from Postman back to repositories." + echo -e " ${__HELP_OPTION_COLOR}push${__HELP_NORMAL}" + echo -e ' Push repositories collections to Postman.' +} + +commandArgsHelpFunction() { + echo -e " List of postman collection's references to pull or push" + echo -e " or no argument to pull or push all the collections" +} + +# shellcheck disable=SC2317 # if function is overridden +unknownOption() { + commandArgs+=("$1") +} + +postmanCliCommandCallback() { + if [[ -z "${optionPostmanModelConfig}" ]]; then + optionPostmanModelConfig="${CURRENT_DIR}/postmanCli.collections.json" + fi + if [[ ! -f "${optionPostmanModelConfig}" ]]; then + Log::displayError "Please provide a valid postman config file, using --postman-model option." + exit 1 + fi + if [[ "${displayConfig}" = "1" ]]; then + # shellcheck disable=SC2154 + Postman::Model::validate "${optionPostmanModelConfig}" "config" + original_displayConfig + UI::drawLine "-" + printf '%-40s = %s\n' "POSTMAN_API_KEY" "${POSTMAN_API_KEY:0:15}...(truncated)" + exit 0 + fi +} + +eval "original_$(declare -f displayConfig | grep -v 'exit 0')" +declare displayConfig=0 +displayConfig() { + displayConfig=1 +} diff --git a/src/_binaries/Postman/postmanCli.bats b/src/_binaries/Postman/postmanCli/postmanCli.bats similarity index 61% rename from src/_binaries/Postman/postmanCli.bats rename to src/_binaries/Postman/postmanCli/postmanCli.bats index 58e334c1..71ddecfc 100755 --- a/src/_binaries/Postman/postmanCli.bats +++ b/src/_binaries/Postman/postmanCli/postmanCli.bats @@ -1,7 +1,7 @@ #!/usr/bin/env bash # shellcheck source=src/batsHeaders.sh -source "$(cd "${BATS_TEST_DIRNAME}/../.." && pwd)/batsHeaders.sh" +source "$(cd "${BATS_TEST_DIRNAME}/../../.." && pwd)/batsHeaders.sh" setup() { export TMPDIR="${BATS_TEST_TMPDIR}" @@ -21,18 +21,17 @@ function PostmanCli::display_help { #@test function PostmanCli::config { #@test run "${binDir}/postmanCli" --config -m "${rootDir}/conf/postmanCli/openApis.json" - + #assert_output "" assert_line --index 0 "Config" assert_line --index 1 "--------------------------------------------------------------------------------" assert_line --index 2 "BASH_FRAMEWORK_ARGV = ([0]=\"--config\" [1]=\"-m\" [2]=\"${rootDir}/conf/postmanCli/openApis.json\")" assert_line --index 3 "BASH_FRAMEWORK_ARGV_FILTERED = ()" assert_line --index 4 'BASH_FRAMEWORK_DISPLAY_LEVEL = "3"' - assert_line --index 5 "BASH_FRAMEWORK_ENV_FILES = ([0]=\"${HOME}/.bash-tools/.env\")" - assert_line --index 6 "BASH_FRAMEWORK_LOG_FILE = \"${TMPDIR}/logFile\"" - assert_line --index 7 'BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION = "5"' - assert_line --index 8 'BASH_FRAMEWORK_LOG_LEVEL = "0"' - assert_line --index 9 'BASH_FRAMEWORK_THEME = "default"' - assert_line --index 10 "--------------------------------------------------------------------------------" - assert_line --index 11 'POSTMAN_API_KEY = ...(truncated)' - assert_lines_count 12 + assert_line --index 5 "BASH_FRAMEWORK_LOG_FILE = \"${TMPDIR}/logFile\"" + assert_line --index 6 'BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION = "5"' + assert_line --index 7 'BASH_FRAMEWORK_LOG_LEVEL = "0"' + assert_line --index 8 'BASH_FRAMEWORK_THEME = "default"' + assert_line --index 9 "--------------------------------------------------------------------------------" + assert_line --index 10 'POSTMAN_API_KEY = ...(truncated)' + assert_lines_count 11 } diff --git a/src/_binaries/Postman/postmanCli/testsData/postmanCli.help.txt b/src/_binaries/Postman/postmanCli/testsData/postmanCli.help.txt new file mode 100644 index 00000000..78ef2487 --- /dev/null +++ b/src/_binaries/Postman/postmanCli/testsData/postmanCli.help.txt @@ -0,0 +1,76 @@ +SYNOPSIS: + Push/Pull postman collections of all the configured repositories. + +USAGE: postmanCli [OPTIONS] [ARGUMENTS] +USAGE: postmanCli [--help|-h] [--config] + [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] + [--env-file ] [--log-level ] [--log-file ] + [--display-level ] [--no-color] [--theme ] [--version] + [--quiet|-q] [--postman-model|-m ] + +ARGUMENTS: + [command {single}] + pull + Pull collections from Postman back to repositories. + push + Push repositories collections to Postman. + [ref {list} (optional)] + List of postman collection's references to pull or push + or no argument to pull or push all the collections + +GLOBAL OPTIONS: + --help, -h {single} + Displays this command help + --config {single} + Displays configuration + --bash-framework-config  {single} + Use alternate bash framework configuration. + --verbose, -v {single} + Info level verbose mode (alias of --display-level INFO) + -vv {single} + Debug level verbose mode (alias of --display-level DEBUG) + -vvv {single} + Trace level verbose mode (alias of --display-level TRACE) + --env-file  {list} (optional) + Load the specified env file (deprecated, please use --bash-framework-con + fig option instead) + --log-level  {single} + Set log level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --log-file  {single} + Set log file + --display-level  {single} + Set display level + Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + --no-color {single} + Produce monochrome output. alias of --theme noColor. + --theme  {single} + Choose color theme - default-force means colors will be produced even if + command is piped. + Possible values: default, default-force, noColor + Default value: default + --version {single} + Print version information and quit. + --quiet, -q {single} + Quiet mode, doesn't display any output. + +PUSH/PULL OPTIONS: + --postman-model, -m  {single} + postmanCli model file to use + Default value: /postmanCli.collections.json + + +DESCRIPTION: + EXIT CODES: + 1: if commit does not exists + 2: if ref is not convertible to commit oid + 3: if commit is not included in given branch + +VERSION: 3.0 + +AUTHOR: [François Chastanet](https://github.com/fchastanet) + +SOURCE FILE: https://github.com/fchastanet/bash-tools-framework/tree/master/src/_binaries/Postman/postmanCli/postmanCli-binary.yaml + +LICENSE: MIT License +Copyright (c) 2020-now François Chastanet diff --git a/src/_binaries/Postman/testsData/postmanCli.help.txt b/src/_binaries/Postman/testsData/postmanCli.help.txt deleted file mode 100644 index 7165ecd9..00000000 --- a/src/_binaries/Postman/testsData/postmanCli.help.txt +++ /dev/null @@ -1,72 +0,0 @@ -DESCRIPTION: -Push/Pull postman collections of all the configured repositories -USAGE: postmanCli [OPTIONS] [ARGUMENTS] -USAGE: postmanCli [--postman-model|-m ] - [--bash-framework-config ] [--config] [--verbose|-v] [-vv] [-vvv] - [--env-file ] [--no-color] [--theme ] [--help|-h] [--version] - [--quiet|-q] [--log-level ] [--log-file ] - [--display-level ] - -ARGUMENTS: - [command {single}] - pull - Pull collections from Postman back to repositories. - push - Push repositories collections to Postman. - [commandArgs {list} (optional)] - list of postman collection's references to pull or push - or no argument to pull or push all the collections - -PUSH/PULL OPTIONS: - --postman-model, -m  {single} - postmanCli model file to use - Default value: /postmanCli.collections.json - -GLOBAL OPTIONS: - --bash-framework-config  {single} - use alternate bash framework configuration. - --config {single} - Display configuration - --verbose, -v {single} - info level verbose mode (alias of --display-level INFO) - -vv {single} - debug level verbose mode (alias of --display-level DEBUG) - -vvv {single} - trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-config - option instead) - --no-color {single} - Produce monochrome output. alias of --theme noColor. - --theme  {single} - choose color theme - default-force means colors will be produced even if com - mand is piped - Default value: default - Possible values: default|default-force|noColor - --help, -h {single} - Display this command help - --version {single} - Print version information and quit - --quiet, -q {single} - quiet mode, doesn't display any output - --log-level  {single} - Set log level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - --log-file  {single} - Set log file - --display-level  {single} - set display level - Possible values: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE - -VERSION: 1.0 - -AUTHOR: -[François Chastanet](https://github.com/fchastanet) - -SOURCE FILE: -https://github.com/fchastanet/bash-tools/tree/master/src/_binaries/Postman/postmanCli.sh - -LICENSE: -MIT License - -Copyright (c) 2023-now François Chastanet diff --git a/src/_binaries/commandDefinitions/optionsProfile.sh b/src/_binaries/commandDefinitions/optionsProfile.sh index 49db44de..54bbac73 100755 --- a/src/_binaries/commandDefinitions/optionsProfile.sh +++ b/src/_binaries/commandDefinitions/optionsProfile.sh @@ -7,6 +7,7 @@ profileOptionHelpFunction() { } initProfileCommandCallback() { + # shellcheck disable=SC2154 if [[ "${optionProfile}" != "default" && -n "${optionTables}" ]]; then Log::fatal "Command ${SCRIPT_NAME} - you cannot use table and profile options at the same time" fi From 469c1ca80f20e4b4971fb3555ee0444523b854d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Sun, 8 Sep 2024 15:27:07 +0200 Subject: [PATCH 20/24] Refactoring to remove duplicated code - move conf/.env to conf/defaultEnv/.env in order to be able to include the .env file using gtpl it avoids usage of @embed feature removing the need of tar command in BashTools::Conf::requireLoad - added src/_binaries/commandDefinitions/optionsDefault.yaml to set default binary options for all the binaries beforeParseCallback calling defaultBeforeParseCallback - avoid duplicated code by using the new fromDsnOptionLongDescription function - reformatted long description for all Database's binaries - better optionProfile default value management that can be default or empty depending the case - added src/_binaries/commandDefinitions/optionsTables.sh src/_binaries/commandDefinitions/optionsRatio.yaml in order to correctly order the options in help - move requirements to beforeParseCallback - recompiled all binaries to remove deprecated option --env-file --- .pre-commit-config-github.yaml | 6 +- .pre-commit-config.yaml | 6 +- bin/cli | 218 ++++---- bin/dbImport | 304 ++++++---- bin/dbImportProfile | 529 ++++++++++-------- bin/dbImportStream | 278 +++++---- bin/dbQueryAllDatabases | 226 +++++--- bin/dbScriptAllDatabases | 284 +++++----- bin/doc | 204 +++++-- bin/gitIsAncestorOf | 237 ++++---- bin/gitIsBranch | 237 ++++---- bin/gitRenameBranch | 243 ++++---- bin/installRequirements | 202 +++++-- bin/mysql2puml | 226 +++++--- bin/postmanCli | 204 +++++-- bin/upgradeGithubRelease | 231 +++++--- bin/waitForIt | 233 +++++--- bin/waitForMysql | 229 +++++--- conf/dbScripts/extractData | 210 +++---- conf/{ => defaultEnv}/.env | 0 conf/mysql2pumlSkins/default.png | Bin 954 -> 0 bytes conf/mysql2pumlSkins/default.svg | 1 - install | 217 +++++-- src/BashTools/Conf/requireLoad.bats | 8 +- src/BashTools/Conf/requireLoad.sh | 9 +- .../mysql2puml/mysql2puml-binary.yaml | 3 +- .../mysql2puml/testsData/mysql2puml.help.txt | 5 +- .../mysql2puml/testsData/mysql2puml.png | Bin 47049 -> 56573 bytes .../Database/dbImport/dbImport-binary.yaml | 7 +- .../Database/dbImport/dbImport-options.sh | 37 +- .../dbImport/testsData/dbImport.help.txt | 52 +- .../dbImportProfile-binary.yaml | 30 +- .../dbImportProfile/dbImportProfile-main.sh | 6 - .../dbImportProfile-options.sh | 43 +- .../dbImportProfile/dbImportProfile.bats | 3 +- .../testsData/dbImportProfile.help.txt | 79 +-- .../dbImportStream/dbImportStream-binary.yaml | 7 +- .../dbImportStream/dbImportStream-options.sh | 24 +- .../dbImportStream/dbImportStream.bats | 3 +- .../testsData/dbImportProfiles/all.sh | 4 + .../testsData/dbImportProfiles/default.sh | 15 + .../testsData/dbImportProfiles/none.sh | 6 + .../testsData/dbImportStream.help.txt | 49 +- .../dbQueryAllDatabases-binary.yaml | 8 +- .../dbQueryAllDatabases-options.sh | 40 +- .../dbQueryAllDatabases.bats | 7 +- .../testsData/dbQueryAllDatabases.help.txt | 52 +- .../dbScriptAllDatabases-binary.yaml | 11 +- .../dbScriptAllDatabases-options.sh | 43 +- .../dbScriptAllDatabases.bats | 5 +- .../dbScriptOneDatabase.sh | 4 +- .../extractData-binary.yaml | 2 +- .../testsData/dbScriptAllDatabases.help.txt | 56 +- src/_binaries/Docker/cli/cli-binary.yaml | 5 +- src/_binaries/Docker/cli/cli-options.sh | 5 +- src/_binaries/Docker/cli/cli.bats | 1 + .../Docker/cli/testsData/cli.help.txt | 5 +- .../gitIsAncestorOf-binary.yaml | 5 +- .../Git/gitIsAncestorOf/gitIsAncestorOf.bats | 2 +- .../testsData/gitIsAncestorOf.help.txt | 5 +- .../Git/gitIsBranch/gitIsBranch-binary.yaml | 5 +- .../Git/gitIsBranch/gitIsBranch.bats | 2 +- .../testsData/gitIsBranch.help.txt | 5 +- .../gitRenameBranch-binary.yaml | 5 +- .../Git/gitRenameBranch/gitRenameBranch.bats | 2 +- .../testsData/gitRenameBranch.help.txt | 5 +- src/_binaries/Git/gitToolsDefaultOptions.sh | 5 +- .../testsData/upgradeGithubRelease.help.txt | 5 +- .../upgradeGithubRelease-binary.yaml | 5 +- .../upgradeGithubRelease.bats | 2 +- .../Postman/postmanCli/postmanCli-binary.yaml | 5 +- .../Postman/postmanCli/postmanCli.bats | 16 +- .../postmanCli/testsData/postmanCli.help.txt | 5 +- .../waitForIt/testsData/waitForIt.help.txt | 5 +- .../Utils/waitForIt/waitForIt-binary.yaml | 6 +- src/_binaries/Utils/waitForIt/waitForIt.bats | 2 +- .../testsData/waitForMysql.help.txt | 5 +- .../waitForMysql/waitForMysql-binary.yaml | 5 +- .../Utils/waitForMysql/waitForMysql.bats | 2 +- src/_binaries/build/doc/doc-binary.yaml | 2 +- .../build/install/install-binary.yaml | 1 + src/_binaries/build/install/install-main.sh | 7 +- .../build/install/install-options.sh | 6 + .../installRequirements-binary.yaml | 2 +- .../commandDefinitions/optionsDefault.sh | 6 +- .../commandDefinitions/optionsDefault.yaml | 15 + .../commandDefinitions/optionsFromDsn.sh | 17 + .../commandDefinitions/optionsFromDsn.yaml | 2 + .../commandDefinitions/optionsJobs.yaml | 6 +- .../commandDefinitions/optionsMysqlSource.sh | 7 + .../optionsMysqlSource.yaml | 1 + .../optionsMysqlTarget.yaml | 1 + .../commandDefinitions/optionsProfile.sh | 51 +- .../commandDefinitions/optionsProfile.yaml | 15 - .../commandDefinitions/optionsRatio.sh | 10 + .../commandDefinitions/optionsRatio.yaml | 19 + .../commandDefinitions/optionsTables.sh | 8 + .../commandDefinitions/optionsTables.yaml | 21 + src/batsHeaders.sh | 2 +- 99 files changed, 3138 insertions(+), 2324 deletions(-) rename conf/{ => defaultEnv}/.env (100%) delete mode 100644 conf/mysql2pumlSkins/default.png delete mode 100644 conf/mysql2pumlSkins/default.svg create mode 100755 src/_binaries/Database/dbImportStream/testsData/dbImportProfiles/all.sh create mode 100755 src/_binaries/Database/dbImportStream/testsData/dbImportProfiles/default.sh create mode 100755 src/_binaries/Database/dbImportStream/testsData/dbImportProfiles/none.sh create mode 100644 src/_binaries/commandDefinitions/optionsDefault.yaml create mode 100755 src/_binaries/commandDefinitions/optionsFromDsn.sh create mode 100755 src/_binaries/commandDefinitions/optionsRatio.sh create mode 100644 src/_binaries/commandDefinitions/optionsRatio.yaml create mode 100755 src/_binaries/commandDefinitions/optionsTables.sh create mode 100644 src/_binaries/commandDefinitions/optionsTables.yaml diff --git a/.pre-commit-config-github.yaml b/.pre-commit-config-github.yaml index 28050360..f095b61a 100644 --- a/.pre-commit-config-github.yaml +++ b/.pre-commit-config-github.yaml @@ -161,7 +161,7 @@ repos: exclude: /testsData/ - repo: https://github.com/fchastanet/bash-tools-framework - rev: 5.1.0 + rev: 5.1.2 hooks: - id: fixShebangExecutionBit - id: awkLint @@ -192,6 +192,10 @@ repos: WARNING, ] - id: plantuml + exclude: | + (?x)( + ^conf/mysql2pumlSkins/default.puml$ + ) - repo: local hooks: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 43dec14b..7c89af86 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -156,7 +156,7 @@ repos: exclude: /testsData/ - repo: https://github.com/fchastanet/bash-tools-framework - rev: 5.1.0 + rev: 5.1.2 hooks: - id: fixShebangExecutionBit - id: awkLint @@ -187,6 +187,10 @@ repos: WARNING, ] - id: plantuml + exclude: | + (?x)( + ^conf/mysql2pumlSkins/default.puml$ + ) - repo: local hooks: diff --git a/bin/cli b/bin/cli index b2c220ca..75d631b3 100755 --- a/bin/cli +++ b/bin/cli @@ -352,18 +352,79 @@ Bash::handlePipelineFailure() { } +read -r -d '\0' bashToolsDefaultConfigTemplate <<-EOM || true +# +# shellcheck disable=SC2034 + +# Default settings +# you can override these settings by creating ${HOME}/.bash-tools/.env file + +### +### DISPLAY Level +### minimum level of the messages that will be displayed on screen +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} + +### +### DISPLAY duration +### 0: no duration is displayed on the messages +### 1: duration between previous message and current is displayed +### with the message +### +DISPLAY_DURATION=${DISPLAY_DURATION:0} + +### +### Log to file +### +### all log messages will be redirected to log file specified +### this same path will be used inside and outside of the container +### +BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} + +### +### LOG Level +### minimum level of the messages that will be logged into LOG_FILE +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} + +# absolute directory containing db import sql dumps +DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} + +# garbage collect all files for which modification is greater than eg: 30 days (+30) +# each time an existing file is used by dbImport/dbImportTable +# the file modification time is set to now +DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} + +# absolute directory containing dbScripts used by dbScriptAllDatabases +SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} + +# ----------------------------------------------------- +# AWS Parameters +# ----------------------------------------------------- +S3_BASE_URL=${S3_BASE_URL:-} + +# ----------------------------------------------------- +# Postman Parameters +# ----------------------------------------------------- +POSTMAN_API_KEY= +EOM + # @description loads ~/.bash-tools/.env if available # if not creates it from a default template # else check if new options need to be added BashTools::Conf::requireLoad() { -Linux::requireTarCommand -Compiler::Embed::extractFileFromBase64 \ - "${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" \ - "IyEvdXNyL2Jpbi9lbnYgYmFzaAojIHNoZWxsY2hlY2sgZGlzYWJsZT1TQzIwMzQKCiMgRGVmYXVsdCBzZXR0aW5ncwojIHlvdSBjYW4gb3ZlcnJpZGUgdGhlc2Ugc2V0dGluZ3MgYnkgY3JlYXRpbmcgJHtIT01FfS8uYmFzaC10b29scy8uZW52IGZpbGUKCiMjIwojIyMgRElTUExBWSBMZXZlbAojIyMgbWluaW11bSBsZXZlbCBvZiB0aGUgbWVzc2FnZXMgdGhhdCB3aWxsIGJlIGRpc3BsYXllZCBvbiBzY3JlZW4KIyMjCiMjIyAwOiBOTyBMT0cKIyMjIDE6IEVSUk9SCiMjIyAyOiBXQVJOSU5HCiMjIyAzOiBJTkZPCiMjIyA0OiBERUJVRwojIyMKQkFTSF9GUkFNRVdPUktfRElTUExBWV9MRVZFTD0ke0JBU0hfRlJBTUVXT1JLX0RJU1BMQVlfTEVWRUw6LTN9CgojIyMKIyMjIERJU1BMQVkgZHVyYXRpb24KIyMjIDA6IG5vIGR1cmF0aW9uIGlzIGRpc3BsYXllZCBvbiB0aGUgbWVzc2FnZXMKIyMjIDE6IGR1cmF0aW9uIGJldHdlZW4gcHJldmlvdXMgbWVzc2FnZSBhbmQgY3VycmVudCBpcyBkaXNwbGF5ZWQKIyMjIHdpdGggdGhlIG1lc3NhZ2UKIyMjCkRJU1BMQVlfRFVSQVRJT049JHtESVNQTEFZX0RVUkFUSU9OOjB9CgojIyMKIyMjIExvZyB0byBmaWxlCiMjIwojIyMgYWxsIGxvZyBtZXNzYWdlcyB3aWxsIGJlIHJlZGlyZWN0ZWQgdG8gbG9nIGZpbGUgc3BlY2lmaWVkCiMjIyB0aGlzIHNhbWUgcGF0aCB3aWxsIGJlIHVzZWQgaW5zaWRlIGFuZCBvdXRzaWRlIG9mIHRoZSBjb250YWluZXIKIyMjCkJBU0hfRlJBTUVXT1JLX0xPR19GSUxFPSR7QkFTSF9GUkFNRVdPUktfTE9HX0ZJTEU6LSR7RlJBTUVXT1JLX1JPT1RfRElSfS9sb2dzL2Jhc2gubG9nfQoKIyMjCiMjIyBMT0cgTGV2ZWwKIyMjIG1pbmltdW0gbGV2ZWwgb2YgdGhlIG1lc3NhZ2VzIHRoYXQgd2lsbCBiZSBsb2dnZWQgaW50byBMT0dfRklMRQojIyMKIyMjIDA6IE5PIExPRwojIyMgMTogRVJST1IKIyMjIDI6IFdBUk5JTkcKIyMjIDM6IElORk8KIyMjIDQ6IERFQlVHCiMjIwpCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw9JHtCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw6LTB9CgojIGFic29sdXRlIGRpcmVjdG9yeSBjb250YWluaW5nIGRiIGltcG9ydCBzcWwgZHVtcHMKREJfSU1QT1JUX0RVTVBfRElSPSR7REJfSU1QT1JUX0RVTVBfRElSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2RiSW1wb3J0RHVtcHN9CgojIGdhcmJhZ2UgY29sbGVjdCBhbGwgZmlsZXMgZm9yIHdoaWNoIG1vZGlmaWNhdGlvbiBpcyBncmVhdGVyIHRoYW4gZWc6IDMwIGRheXMgKCszMCkKIyBlYWNoIHRpbWUgYW4gZXhpc3RpbmcgZmlsZSBpcyB1c2VkIGJ5IGRiSW1wb3J0L2RiSW1wb3J0VGFibGUKIyB0aGUgZmlsZSBtb2RpZmljYXRpb24gdGltZSBpcyBzZXQgdG8gbm93CkRCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUz0ke0RCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUzotKzMwfQoKIyBhYnNvbHV0ZSBkaXJlY3RvcnkgY29udGFpbmluZyBkYlNjcmlwdHMgdXNlZCBieSBkYlNjcmlwdEFsbERhdGFiYXNlcwpTQ1JJUFRTX0ZPTERFUj0ke1NDUklQVFNfRk9MREVSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2NvbmYvZGJTY3JpcHRzfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEFXUyBQYXJhbWV0ZXJzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KUzNfQkFTRV9VUkw9JHtTM19CQVNFX1VSTDotfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvc3RtYW4gUGFyYW1ldGVycwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClBPU1RNQU5fQVBJX0tFWT0K" \ - "755" - -declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" - BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" @@ -391,7 +452,7 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp ( echo "#!/usr/bin/env bash" # shellcheck disable=SC2154 - echo "${embed_file_bashToolsDefaultConfigTemplate}" + echo "${bashToolsDefaultConfigTemplate}" ) >"${envFile}" Log::displayInfo "Configuration file '${envFile}' created" else @@ -404,7 +465,7 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp ) >>"${envFile}" fi fi - # shellcheck source=/conf/.env + # shellcheck source=/conf/defaultEnv/.env source "${envFile}" || { Log::displayError "impossible to load '${envFile}'" exit 1 @@ -412,36 +473,6 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp } -# @description convert base64 encoded back to target file -# if target file is executable prepend dir of target -# file to PATH to make binary available everywhere -# it is advised to include in the path of the target file -# the md5sum of the binFile -# -# @arg $1 targetFile:String the file to write -# @arg $2 binFileBase64:String the base64 encoded file -# @arg $3 fileMode:String the chmod to set on the file -# @set PATH String prepend target embedded file binary directory to PATH variable if binary executable -Compiler::Embed::extractFileFromBase64() { - local targetFile="$1" - local binFileBase64="$2" - local fileMode="${3:-+x}" - local targetDir="${targetFile%/*}" - - if [[ ! -f "${targetFile}" ]]; then - if [[ ! -d "${targetDir}" ]]; then - mkdir -p "${targetDir}" - fi - base64 -d >"${targetFile}" <<<"${binFileBase64}" - chmod "${fileMode}" "${targetFile}" - fi - - if [[ -x "${targetFile}" ]]; then - Env::pathPrepend "${targetDir}" - fi -} - - # @description get absolute conf file from specified conf folder deduced using these rules # * from absolute file (ignores and ) # * relative to where script is executed (ignores and ) @@ -680,19 +711,6 @@ Db::checkRequirements() { } -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" - fi - done -} - - # @description ensure env files are loaded # @arg $@ list of default files to load at the end # @exitcode 1 if one of env files fails to load @@ -765,14 +783,6 @@ Linux::requireRealpathCommand() { } -# @description ensure command tar is available -# @exitcode 1 if tar command not available -# @stderr diagnostics information is displayed -Linux::requireTarCommand() { - Assert::commandExists tar -} - - declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" @@ -1252,6 +1262,7 @@ beforeParseCallback() { copyrightCallback() { # + # shellcheck disable=SC2155,SC2154,SC2250 echo "Copyright (c) 2020-now François Chastanet" } @@ -1295,16 +1306,6 @@ optionVersionCallback() { exit 0 } -# shellcheck disable=SC2317 # if function is overridden -optionEnvFileCallback() { - local envFile="$2" - Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" - if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" - exit 1 - fi -} - # shellcheck disable=SC2317 # if function is overridden optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' @@ -1479,9 +1480,6 @@ commandOptionParseFinished() { defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi # shellcheck disable=SC2154 if [[ -f "${optionBashFrameworkConfig}" ]]; then files+=("${optionBashFrameworkConfig}") @@ -1497,6 +1495,19 @@ commandOptionParseFinished() { +defaultBeforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + +beforeParseCallback() { + defaultBeforeParseCallback +} + + + # option values # shellcheck disable=SC2034 declare containerArg="default" @@ -1510,10 +1521,7 @@ declare PROFILES_DIR declare HOME_PROFILES_DIR beforeParseCallback() { - BashTools::Conf::requireLoad - Env::requireLoad - UI::requireTheme - Log::requireLoad + defaultBeforeParseCallback Linux::requireRealpathCommand Assert::commandExists docker "check https://docs.docker.com/engine/install/ubuntu/" } @@ -1626,7 +1634,6 @@ declare optionBashFrameworkConfig="" declare optionInfoVerbose="0" declare optionDebugVerbose="0" declare optionTraceVerbose="0" -declare -a optionEnvFiles=() declare optionLogLevel="" declare optionLogFile="" declare optionDisplayLevel="" @@ -1660,7 +1667,6 @@ cliCommandParse() { optionTraceVerbose="0" local -i options_parse_optionParsedCountOptionTraceVerbose ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionLogLevel="" local -i options_parse_optionParsedCountOptionLogLevel ((options_parse_optionParsedCountOptionLogLevel = 0)) || true @@ -1702,7 +1708,7 @@ cliCommandParse() { local options_parse_arg="$1" local argOptDefaultBehavior=0 case "${options_parse_arg}" in - # Option 1/14 + # Option 1/13 # optionHelp alts --help|-h # type: Boolean min 0 max 1 --help | -h) @@ -1718,7 +1724,7 @@ cliCommandParse() { ;; - # Option 2/14 + # Option 2/13 # optionConfig alts --config # type: Boolean min 0 max 1 --config) @@ -1732,7 +1738,7 @@ cliCommandParse() { ((++options_parse_optionParsedCountOptionConfig)) ;; - # Option 3/14 + # Option 3/13 # optionBashFrameworkConfig alts --bash-framework-config # type: String min 0 max 1 --bash-framework-config) @@ -1753,7 +1759,7 @@ cliCommandParse() { ;; - # Option 4/14 + # Option 4/13 # optionInfoVerbose alts --verbose|-v # type: Boolean min 0 max 1 --verbose | -v) @@ -1771,7 +1777,7 @@ cliCommandParse() { ;; - # Option 5/14 + # Option 5/13 # optionDebugVerbose alts -vv # type: Boolean min 0 max 1 -vv) @@ -1789,7 +1795,7 @@ cliCommandParse() { ;; - # Option 6/14 + # Option 6/13 # optionTraceVerbose alts -vvv # type: Boolean min 0 max 1 -vvv) @@ -1807,25 +1813,7 @@ cliCommandParse() { ;; - # Option 7/14 - # optionEnvFiles alts --env-file - # type: StringArray min 0 max -1 - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - ;; - - # Option 8/14 + # Option 7/13 # optionLogLevel alts --log-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1853,7 +1841,7 @@ cliCommandParse() { ;; - # Option 9/14 + # Option 8/13 # optionLogFile alts --log-file # type: String min 0 max 1 --log-file) @@ -1876,7 +1864,7 @@ cliCommandParse() { ;; - # Option 10/14 + # Option 9/13 # optionDisplayLevel alts --display-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1904,7 +1892,7 @@ cliCommandParse() { ;; - # Option 11/14 + # Option 10/13 # optionNoColor alts --no-color # type: Boolean min 0 max 1 --no-color) @@ -1922,7 +1910,7 @@ cliCommandParse() { ;; - # Option 12/14 + # Option 11/13 # optionTheme alts --theme # type: String min 0 max 1 # authorizedValues: default|default-force|noColor @@ -1950,7 +1938,7 @@ cliCommandParse() { ;; - # Option 13/14 + # Option 12/13 # optionVersion alts --version # type: Boolean min 0 max 1 --version) @@ -1966,7 +1954,7 @@ cliCommandParse() { ;; - # Option 14/14 + # Option 13/13 # optionQuiet alts --quiet|-q # type: Boolean min 0 max 1 --quiet | -q) @@ -2081,7 +2069,7 @@ cliCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" + optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "cli" "${optionsAltList[@]}" @@ -2146,12 +2134,6 @@ cliCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" - echo - - - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo diff --git a/bin/dbImport b/bin/dbImport index d5afec12..90a9de58 100755 --- a/bin/dbImport +++ b/bin/dbImport @@ -355,18 +355,79 @@ Bash::handlePipelineFailure() { } +read -r -d '\0' bashToolsDefaultConfigTemplate <<-EOM || true +# +# shellcheck disable=SC2034 + +# Default settings +# you can override these settings by creating ${HOME}/.bash-tools/.env file + +### +### DISPLAY Level +### minimum level of the messages that will be displayed on screen +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} + +### +### DISPLAY duration +### 0: no duration is displayed on the messages +### 1: duration between previous message and current is displayed +### with the message +### +DISPLAY_DURATION=${DISPLAY_DURATION:0} + +### +### Log to file +### +### all log messages will be redirected to log file specified +### this same path will be used inside and outside of the container +### +BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} + +### +### LOG Level +### minimum level of the messages that will be logged into LOG_FILE +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} + +# absolute directory containing db import sql dumps +DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} + +# garbage collect all files for which modification is greater than eg: 30 days (+30) +# each time an existing file is used by dbImport/dbImportTable +# the file modification time is set to now +DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} + +# absolute directory containing dbScripts used by dbScriptAllDatabases +SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} + +# ----------------------------------------------------- +# AWS Parameters +# ----------------------------------------------------- +S3_BASE_URL=${S3_BASE_URL:-} + +# ----------------------------------------------------- +# Postman Parameters +# ----------------------------------------------------- +POSTMAN_API_KEY= +EOM + # @description loads ~/.bash-tools/.env if available # if not creates it from a default template # else check if new options need to be added BashTools::Conf::requireLoad() { -Linux::requireTarCommand -Compiler::Embed::extractFileFromBase64 \ - "${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" \ - "IyEvdXNyL2Jpbi9lbnYgYmFzaAojIHNoZWxsY2hlY2sgZGlzYWJsZT1TQzIwMzQKCiMgRGVmYXVsdCBzZXR0aW5ncwojIHlvdSBjYW4gb3ZlcnJpZGUgdGhlc2Ugc2V0dGluZ3MgYnkgY3JlYXRpbmcgJHtIT01FfS8uYmFzaC10b29scy8uZW52IGZpbGUKCiMjIwojIyMgRElTUExBWSBMZXZlbAojIyMgbWluaW11bSBsZXZlbCBvZiB0aGUgbWVzc2FnZXMgdGhhdCB3aWxsIGJlIGRpc3BsYXllZCBvbiBzY3JlZW4KIyMjCiMjIyAwOiBOTyBMT0cKIyMjIDE6IEVSUk9SCiMjIyAyOiBXQVJOSU5HCiMjIyAzOiBJTkZPCiMjIyA0OiBERUJVRwojIyMKQkFTSF9GUkFNRVdPUktfRElTUExBWV9MRVZFTD0ke0JBU0hfRlJBTUVXT1JLX0RJU1BMQVlfTEVWRUw6LTN9CgojIyMKIyMjIERJU1BMQVkgZHVyYXRpb24KIyMjIDA6IG5vIGR1cmF0aW9uIGlzIGRpc3BsYXllZCBvbiB0aGUgbWVzc2FnZXMKIyMjIDE6IGR1cmF0aW9uIGJldHdlZW4gcHJldmlvdXMgbWVzc2FnZSBhbmQgY3VycmVudCBpcyBkaXNwbGF5ZWQKIyMjIHdpdGggdGhlIG1lc3NhZ2UKIyMjCkRJU1BMQVlfRFVSQVRJT049JHtESVNQTEFZX0RVUkFUSU9OOjB9CgojIyMKIyMjIExvZyB0byBmaWxlCiMjIwojIyMgYWxsIGxvZyBtZXNzYWdlcyB3aWxsIGJlIHJlZGlyZWN0ZWQgdG8gbG9nIGZpbGUgc3BlY2lmaWVkCiMjIyB0aGlzIHNhbWUgcGF0aCB3aWxsIGJlIHVzZWQgaW5zaWRlIGFuZCBvdXRzaWRlIG9mIHRoZSBjb250YWluZXIKIyMjCkJBU0hfRlJBTUVXT1JLX0xPR19GSUxFPSR7QkFTSF9GUkFNRVdPUktfTE9HX0ZJTEU6LSR7RlJBTUVXT1JLX1JPT1RfRElSfS9sb2dzL2Jhc2gubG9nfQoKIyMjCiMjIyBMT0cgTGV2ZWwKIyMjIG1pbmltdW0gbGV2ZWwgb2YgdGhlIG1lc3NhZ2VzIHRoYXQgd2lsbCBiZSBsb2dnZWQgaW50byBMT0dfRklMRQojIyMKIyMjIDA6IE5PIExPRwojIyMgMTogRVJST1IKIyMjIDI6IFdBUk5JTkcKIyMjIDM6IElORk8KIyMjIDQ6IERFQlVHCiMjIwpCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw9JHtCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw6LTB9CgojIGFic29sdXRlIGRpcmVjdG9yeSBjb250YWluaW5nIGRiIGltcG9ydCBzcWwgZHVtcHMKREJfSU1QT1JUX0RVTVBfRElSPSR7REJfSU1QT1JUX0RVTVBfRElSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2RiSW1wb3J0RHVtcHN9CgojIGdhcmJhZ2UgY29sbGVjdCBhbGwgZmlsZXMgZm9yIHdoaWNoIG1vZGlmaWNhdGlvbiBpcyBncmVhdGVyIHRoYW4gZWc6IDMwIGRheXMgKCszMCkKIyBlYWNoIHRpbWUgYW4gZXhpc3RpbmcgZmlsZSBpcyB1c2VkIGJ5IGRiSW1wb3J0L2RiSW1wb3J0VGFibGUKIyB0aGUgZmlsZSBtb2RpZmljYXRpb24gdGltZSBpcyBzZXQgdG8gbm93CkRCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUz0ke0RCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUzotKzMwfQoKIyBhYnNvbHV0ZSBkaXJlY3RvcnkgY29udGFpbmluZyBkYlNjcmlwdHMgdXNlZCBieSBkYlNjcmlwdEFsbERhdGFiYXNlcwpTQ1JJUFRTX0ZPTERFUj0ke1NDUklQVFNfRk9MREVSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2NvbmYvZGJTY3JpcHRzfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEFXUyBQYXJhbWV0ZXJzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KUzNfQkFTRV9VUkw9JHtTM19CQVNFX1VSTDotfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvc3RtYW4gUGFyYW1ldGVycwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClBPU1RNQU5fQVBJX0tFWT0K" \ - "755" - -declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" - BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" @@ -394,7 +455,7 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp ( echo "#!/usr/bin/env bash" # shellcheck disable=SC2154 - echo "${embed_file_bashToolsDefaultConfigTemplate}" + echo "${bashToolsDefaultConfigTemplate}" ) >"${envFile}" Log::displayInfo "Configuration file '${envFile}' created" else @@ -407,7 +468,7 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp ) >>"${envFile}" fi fi - # shellcheck source=/conf/.env + # shellcheck source=/conf/defaultEnv/.env source "${envFile}" || { Log::displayError "impossible to load '${envFile}'" exit 1 @@ -1431,6 +1492,7 @@ beforeParseCallback() { copyrightCallback() { # + # shellcheck disable=SC2155,SC2154,SC2250 echo "Copyright (c) 2020-now François Chastanet" } @@ -1474,16 +1536,6 @@ optionVersionCallback() { exit 0 } -# shellcheck disable=SC2317 # if function is overridden -optionEnvFileCallback() { - local envFile="$2" - Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" - if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" - exit 1 - fi -} - # shellcheck disable=SC2317 # if function is overridden optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' @@ -1658,9 +1710,6 @@ commandOptionParseFinished() { defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi # shellcheck disable=SC2154 if [[ -f "${optionBashFrameworkConfig}" ]]; then files+=("${optionBashFrameworkConfig}") @@ -1676,6 +1725,19 @@ commandOptionParseFinished() { +defaultBeforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + +beforeParseCallback() { + defaultBeforeParseCallback +} + + + # shellcheck disable=SC2034 declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" declare defaultTargetCharacterSet="" @@ -1688,10 +1750,7 @@ declare PROFILES_DIR declare HOME_PROFILES_DIR beforeParseCallback() { - BashTools::Conf::requireLoad - Env::requireLoad - UI::requireTheme - Log::requireLoad + defaultBeforeParseCallback Linux::requireRealpathCommand Linux::requireExecutedAsUser } @@ -1709,32 +1768,16 @@ optionHelpCallback() { } longDescriptionFunction() { - local profilesList="" - local dsnList="" - dsnList="$(Conf::getMergedList "dsn" "env")" - profilesList="$(Conf::getMergedList "dbImportProfiles" "sh" || true)" - - echo -e "${__HELP_TITLE}Default profiles directory:${__HELP_NORMAL}" - echo -e "${PROFILES_DIR-configuration error}" - - echo -e "${__HELP_TITLE}User profiles directory:${__HELP_NORMAL}" - echo -e "${HOME_PROFILES_DIR-configuration error}" - echo -e "Allows to override profiles defined in 'Default profiles directory'" - - echo -e "${__HELP_TITLE}List of available profiles:${__HELP_NORMAL}" - echo -e "${profilesList}" - - echo -e "${__HELP_TITLE}List of available dsn:${__HELP_NORMAL}" - echo -e "${dsnList}" - - echo -e "${__HELP_TITLE}Aws s3 location:${__HELP_NORMAL}" - echo -e "${S3_BASE_URL}" + mysqlSourceLongDescription + echo + profileOptionLongDescription echo - echo -e "${__HELP_TITLE}Example 1: from one database to another one${__HELP_NORMAL}" - echo -e "${__HELP_EXAMPLE}TODO${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}Examples${__HELP_NORMAL}" + echo -e " 1. from one database to another one" + echo -e " ${__HELP_EXAMPLE}dbImport --from-dsn localhost --target-dsn remote fromDb toDb${__HELP_NORMAL}" echo - echo -e "${__HELP_TITLE}Example 2: import from S3${__HELP_NORMAL}" - echo -e "${__HELP_EXAMPLE}TODO${__HELP_NORMAL}" + echo -e " 2. import from S3" + echo -e " ${__HELP_EXAMPLE}dbImport --from-aws awsFile.tar.gz --target-dsn localhost fromDb toDb${__HELP_NORMAL}" Db::checkRequirements } @@ -1801,13 +1844,43 @@ initializeDefaultTargetMysqlOptions() { -profileOptionHelpFunction() { - Array::wrap2 " " 80 4 \ - " The name of the profile to use in order to include or exclude tables." +profileOptionLongDescription() { + local profilesList="" + profilesList="$(Conf::getMergedList "dbImportProfiles" "sh" " - " || true)" + + echo -e " ${__HELP_TITLE}Profiles${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}Default profiles directory:${__HELP_NORMAL}" + echo -e " ${PROFILES_DIR-configuration error}" echo + echo -e " ${__HELP_TITLE}User profiles directory:${__HELP_NORMAL}" + echo -e " ${HOME_PROFILES_DIR-configuration error}" + echo -e ' Allows to override profiles defined in "Default profiles directory"' + echo + echo -e " ${__HELP_TITLE}List of available profiles:${__HELP_NORMAL}" + echo -e "${profilesList}" +} + +profileOptionHelpFunction() { + echo " The name of the profile to use in order to" + echo " include or exclude tables." +} + +initOptionProfileIfNotSet() { + if [[ -z "${optionProfile}" ]]; then + optionProfile="${defaultOptionProfile}" + fi + local -a profilesArray + readarray -t profilesArray < <(Conf::getMergedList "dbImportProfiles" "sh" "" || true) + if ! Array::contains "${optionProfile}" "${profilesArray[@]}"; then + Log::displayError "${SCRIPT_NAME} - invalid profile '${optionProfile}' provided" + return 1 + fi } +declare defaultOptionProfile="default" initProfileCommandCallback() { + initOptionProfileIfNotSet + # shellcheck disable=SC2154 if [[ "${optionProfile}" != "default" && -n "${optionTables}" ]]; then Log::fatal "Command ${SCRIPT_NAME} - you cannot use table and profile options at the same time" @@ -1836,16 +1909,12 @@ initProfileCommandCallback() { Log::displayInfo "${profileMsgInfo}" } -profileOptionCallback() { - local -a profilesArray - readarray -t profilesArray < <(Conf::getMergedList "dbImportProfiles" "sh" "" || true) - if ! Array::contains "$2" "${profilesArray[@]}"; then - Log::displayError "${SCRIPT_NAME} - invalid profile '$2' provided" - return 1 - fi -} + + + optionTablesCallback() { + # shellcheck disable=SC2154 if [[ ! ${optionTables} =~ ^[A-Za-z0-9_]+(,[A-Za-z0-9_]+)*$ ]]; then Log::fatal "Command ${SCRIPT_NAME} - Table list is not valid : ${optionTables}" fi @@ -1853,6 +1922,22 @@ optionTablesCallback() { +fromDsnOptionLongDescription() { + local dsnList="" + dsnList="$(Conf::getMergedList "dsn" "env" " - ")" + + echo -e " ${__HELP_TITLE}Data Source Name (DSN)${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}Default dsn directory:${__HELP_NORMAL}" + echo -e " ${BASH_TOOLS_ROOT_DIR}/conf/dsn" + echo + echo -e " ${__HELP_TITLE}User dsn directory:${__HELP_NORMAL}" + echo -e " ${HOME}/.bash-tools/dsn" + echo -e ' Allows to override dsn defined in "Default dsn directory"' + echo + echo -e " ${__HELP_TITLE}List of available dsn:${__HELP_NORMAL}" + echo -e "${dsnList}" +} + optionVersionCallback() { @@ -1868,6 +1953,13 @@ dsnHelpFunction() { echo " target mysql server" } +mysqlSourceLongDescription() { + fromDsnOptionLongDescription + echo + echo -e " ${__HELP_TITLE}Aws s3 location:${__HELP_NORMAL}" + echo -e " ${S3_BASE_URL}" +} + # ------------------------------------------ # Command dbImportCommand @@ -1877,7 +1969,7 @@ dsnHelpFunction() { declare optionCollationName="" declare optionTargetDsn="default.local" declare optionCharacterSet="" -declare optionProfile="default" +declare optionProfile="" declare optionTables="" declare optionSkipSchema="0" declare optionFromDsn="" @@ -1888,7 +1980,6 @@ declare optionBashFrameworkConfig="" declare optionInfoVerbose="0" declare optionDebugVerbose="0" declare optionTraceVerbose="0" -declare -a optionEnvFiles=() declare optionLogLevel="" declare optionLogFile="" declare optionDisplayLevel="" @@ -1912,7 +2003,7 @@ dbImportCommandParse() { optionCharacterSet="" local -i options_parse_optionParsedCountOptionCharacterSet ((options_parse_optionParsedCountOptionCharacterSet = 0)) || true - optionProfile="default" + optionProfile="" local -i options_parse_optionParsedCountOptionProfile ((options_parse_optionParsedCountOptionProfile = 0)) || true optionTables="" @@ -1945,7 +2036,6 @@ dbImportCommandParse() { optionTraceVerbose="0" local -i options_parse_optionParsedCountOptionTraceVerbose ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionLogLevel="" local -i options_parse_optionParsedCountOptionLogLevel ((options_parse_optionParsedCountOptionLogLevel = 0)) || true @@ -1983,7 +2073,7 @@ dbImportCommandParse() { local options_parse_arg="$1" local argOptDefaultBehavior=0 case "${options_parse_arg}" in - # Option 1/22 + # Option 1/21 # optionCollationName alts --collation-name|-o # type: String min 0 max 1 --collation-name | -o) @@ -2002,7 +2092,7 @@ dbImportCommandParse() { optionCollationName="$1" ;; - # Option 2/22 + # Option 2/21 # optionTargetDsn alts --target-dsn|-t # type: String min 0 max 1 --target-dsn | -t) @@ -2021,7 +2111,7 @@ dbImportCommandParse() { optionTargetDsn="$1" ;; - # Option 3/22 + # Option 3/21 # optionCharacterSet alts --character-set|-c # type: String min 0 max 1 --character-set | -c) @@ -2040,7 +2130,7 @@ dbImportCommandParse() { optionCharacterSet="$1" ;; - # Option 4/22 + # Option 4/21 # optionProfile alts --profile|-p # type: String min 0 max 1 --profile | -p) @@ -2057,11 +2147,9 @@ dbImportCommandParse() { ((++options_parse_optionParsedCountOptionProfile)) # shellcheck disable=SC2034 optionProfile="$1" - profileOptionCallback "${options_parse_arg}" "${optionProfile}" - ;; - # Option 5/22 + # Option 5/21 # optionTables alts --tables # type: String min 0 max 1 --tables) @@ -2082,7 +2170,7 @@ dbImportCommandParse() { ;; - # Option 6/22 + # Option 6/21 # optionSkipSchema alts --skip-schema|-s # type: Boolean min 0 max 1 --skip-schema | -s) @@ -2096,7 +2184,7 @@ dbImportCommandParse() { ((++options_parse_optionParsedCountOptionSkipSchema)) ;; - # Option 7/22 + # Option 7/21 # optionFromDsn alts --from-dsn|-f # type: String min 0 max 1 --from-dsn | -f) @@ -2115,7 +2203,7 @@ dbImportCommandParse() { optionFromDsn="$1" ;; - # Option 8/22 + # Option 8/21 # optionFromAws alts --from-aws|-a # type: String min 0 max 1 --from-aws | -a) @@ -2134,7 +2222,7 @@ dbImportCommandParse() { optionFromAws="$1" ;; - # Option 9/22 + # Option 9/21 # optionHelp alts --help|-h # type: Boolean min 0 max 1 --help | -h) @@ -2150,7 +2238,7 @@ dbImportCommandParse() { ;; - # Option 10/22 + # Option 10/21 # optionConfig alts --config # type: Boolean min 0 max 1 --config) @@ -2164,7 +2252,7 @@ dbImportCommandParse() { ((++options_parse_optionParsedCountOptionConfig)) ;; - # Option 11/22 + # Option 11/21 # optionBashFrameworkConfig alts --bash-framework-config # type: String min 0 max 1 --bash-framework-config) @@ -2185,7 +2273,7 @@ dbImportCommandParse() { ;; - # Option 12/22 + # Option 12/21 # optionInfoVerbose alts --verbose|-v # type: Boolean min 0 max 1 --verbose | -v) @@ -2203,7 +2291,7 @@ dbImportCommandParse() { ;; - # Option 13/22 + # Option 13/21 # optionDebugVerbose alts -vv # type: Boolean min 0 max 1 -vv) @@ -2221,7 +2309,7 @@ dbImportCommandParse() { ;; - # Option 14/22 + # Option 14/21 # optionTraceVerbose alts -vvv # type: Boolean min 0 max 1 -vvv) @@ -2239,25 +2327,7 @@ dbImportCommandParse() { ;; - # Option 15/22 - # optionEnvFiles alts --env-file - # type: StringArray min 0 max -1 - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - ;; - - # Option 16/22 + # Option 15/21 # optionLogLevel alts --log-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -2285,7 +2355,7 @@ dbImportCommandParse() { ;; - # Option 17/22 + # Option 16/21 # optionLogFile alts --log-file # type: String min 0 max 1 --log-file) @@ -2308,7 +2378,7 @@ dbImportCommandParse() { ;; - # Option 18/22 + # Option 17/21 # optionDisplayLevel alts --display-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -2336,7 +2406,7 @@ dbImportCommandParse() { ;; - # Option 19/22 + # Option 18/21 # optionNoColor alts --no-color # type: Boolean min 0 max 1 --no-color) @@ -2354,7 +2424,7 @@ dbImportCommandParse() { ;; - # Option 20/22 + # Option 19/21 # optionTheme alts --theme # type: String min 0 max 1 # authorizedValues: default|default-force|noColor @@ -2382,7 +2452,7 @@ dbImportCommandParse() { ;; - # Option 21/22 + # Option 20/21 # optionVersion alts --version # type: Boolean min 0 max 1 --version) @@ -2398,7 +2468,7 @@ dbImportCommandParse() { ;; - # Option 22/22 + # Option 21/21 # optionQuiet alts --quiet|-q # type: Boolean min 0 max 1 --quiet | -q) @@ -2499,7 +2569,6 @@ dbImportCommandParse() { - if ((options_parse_argParsedCountFromDbName < 1 )); then @@ -2531,7 +2600,7 @@ dbImportCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--collation-name|-o ]" "[--target-dsn|-t ]" "[--character-set|-c ]" "[--profile|-p ]" "[--tables ]" "[--skip-schema|-s]" "[--from-dsn|-f ]" "[--from-aws|-a ]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" + optionsAltList=("[--collation-name|-o ]" "[--target-dsn|-t ]" "[--character-set|-c ]" "[--profile|-p ]" "[--tables ]" "[--skip-schema|-s]" "[--from-dsn|-f ]" "[--from-aws|-a ]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "dbImport" "${optionsAltList[@]}" @@ -2582,11 +2651,9 @@ dbImportCommandHelp() { - Array::wrap2 ' ' 76 6 " Default value: " "default" - echo echo -e " ${__HELP_OPTION_COLOR}--tables ${__HELP_NORMAL} {single}" - Array::wrap2 ' ' 76 4 " " "Import only table specified in the list." "If aws mode, ignore profile option." "" + Array::wrap2 ' ' 76 4 " " "Import only table specified in the list." "If aws mode, ignore profile option." echo @@ -2647,12 +2714,6 @@ dbImportCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" - echo - - - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo @@ -2699,6 +2760,13 @@ dbImportCommandHelp() { echo + # ------------------------------------------ + # longDescription section + # ------------------------------------------ + echo + echo + echo -e "${__HELP_TITLE_COLOR}DESCRIPTION:${__RESET_COLOR}" + longDescriptionFunction # ------------------------------------------ # version section # ------------------------------------------ diff --git a/bin/dbImportProfile b/bin/dbImportProfile index 1818bd57..1dfc9a80 100755 --- a/bin/dbImportProfile +++ b/bin/dbImportProfile @@ -139,6 +139,23 @@ export __VERBOSE_LEVEL_DEBUG=2 export __VERBOSE_LEVEL_TRACE=3 +# @description check if an element is contained in an array +# +# @arg $1 needle:String +# @arg $@ array:String[] +# @exitcode 0 if found +# @exitcode 1 otherwise +# @example +# Array::contains "${libPath}" "${__BASH_FRAMEWORK_IMPORTED_FILES[@]}" +Array::contains() { + local element + for element in "${@:2}"; do + [[ "${element}" = "$1" ]] && return 0 + done + return 1 +} + + # @description concatenate each element of an array with a separator # but wrapping text when line length is more than provided argument # The algorithm will try not to cut the array element if it can. @@ -338,18 +355,79 @@ Bash::handlePipelineFailure() { } +read -r -d '\0' bashToolsDefaultConfigTemplate <<-EOM || true +# +# shellcheck disable=SC2034 + +# Default settings +# you can override these settings by creating ${HOME}/.bash-tools/.env file + +### +### DISPLAY Level +### minimum level of the messages that will be displayed on screen +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} + +### +### DISPLAY duration +### 0: no duration is displayed on the messages +### 1: duration between previous message and current is displayed +### with the message +### +DISPLAY_DURATION=${DISPLAY_DURATION:0} + +### +### Log to file +### +### all log messages will be redirected to log file specified +### this same path will be used inside and outside of the container +### +BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} + +### +### LOG Level +### minimum level of the messages that will be logged into LOG_FILE +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} + +# absolute directory containing db import sql dumps +DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} + +# garbage collect all files for which modification is greater than eg: 30 days (+30) +# each time an existing file is used by dbImport/dbImportTable +# the file modification time is set to now +DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} + +# absolute directory containing dbScripts used by dbScriptAllDatabases +SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} + +# ----------------------------------------------------- +# AWS Parameters +# ----------------------------------------------------- +S3_BASE_URL=${S3_BASE_URL:-} + +# ----------------------------------------------------- +# Postman Parameters +# ----------------------------------------------------- +POSTMAN_API_KEY= +EOM + # @description loads ~/.bash-tools/.env if available # if not creates it from a default template # else check if new options need to be added BashTools::Conf::requireLoad() { -Linux::requireTarCommand -Compiler::Embed::extractFileFromBase64 \ - "${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" \ - "IyEvdXNyL2Jpbi9lbnYgYmFzaAojIHNoZWxsY2hlY2sgZGlzYWJsZT1TQzIwMzQKCiMgRGVmYXVsdCBzZXR0aW5ncwojIHlvdSBjYW4gb3ZlcnJpZGUgdGhlc2Ugc2V0dGluZ3MgYnkgY3JlYXRpbmcgJHtIT01FfS8uYmFzaC10b29scy8uZW52IGZpbGUKCiMjIwojIyMgRElTUExBWSBMZXZlbAojIyMgbWluaW11bSBsZXZlbCBvZiB0aGUgbWVzc2FnZXMgdGhhdCB3aWxsIGJlIGRpc3BsYXllZCBvbiBzY3JlZW4KIyMjCiMjIyAwOiBOTyBMT0cKIyMjIDE6IEVSUk9SCiMjIyAyOiBXQVJOSU5HCiMjIyAzOiBJTkZPCiMjIyA0OiBERUJVRwojIyMKQkFTSF9GUkFNRVdPUktfRElTUExBWV9MRVZFTD0ke0JBU0hfRlJBTUVXT1JLX0RJU1BMQVlfTEVWRUw6LTN9CgojIyMKIyMjIERJU1BMQVkgZHVyYXRpb24KIyMjIDA6IG5vIGR1cmF0aW9uIGlzIGRpc3BsYXllZCBvbiB0aGUgbWVzc2FnZXMKIyMjIDE6IGR1cmF0aW9uIGJldHdlZW4gcHJldmlvdXMgbWVzc2FnZSBhbmQgY3VycmVudCBpcyBkaXNwbGF5ZWQKIyMjIHdpdGggdGhlIG1lc3NhZ2UKIyMjCkRJU1BMQVlfRFVSQVRJT049JHtESVNQTEFZX0RVUkFUSU9OOjB9CgojIyMKIyMjIExvZyB0byBmaWxlCiMjIwojIyMgYWxsIGxvZyBtZXNzYWdlcyB3aWxsIGJlIHJlZGlyZWN0ZWQgdG8gbG9nIGZpbGUgc3BlY2lmaWVkCiMjIyB0aGlzIHNhbWUgcGF0aCB3aWxsIGJlIHVzZWQgaW5zaWRlIGFuZCBvdXRzaWRlIG9mIHRoZSBjb250YWluZXIKIyMjCkJBU0hfRlJBTUVXT1JLX0xPR19GSUxFPSR7QkFTSF9GUkFNRVdPUktfTE9HX0ZJTEU6LSR7RlJBTUVXT1JLX1JPT1RfRElSfS9sb2dzL2Jhc2gubG9nfQoKIyMjCiMjIyBMT0cgTGV2ZWwKIyMjIG1pbmltdW0gbGV2ZWwgb2YgdGhlIG1lc3NhZ2VzIHRoYXQgd2lsbCBiZSBsb2dnZWQgaW50byBMT0dfRklMRQojIyMKIyMjIDA6IE5PIExPRwojIyMgMTogRVJST1IKIyMjIDI6IFdBUk5JTkcKIyMjIDM6IElORk8KIyMjIDQ6IERFQlVHCiMjIwpCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw9JHtCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw6LTB9CgojIGFic29sdXRlIGRpcmVjdG9yeSBjb250YWluaW5nIGRiIGltcG9ydCBzcWwgZHVtcHMKREJfSU1QT1JUX0RVTVBfRElSPSR7REJfSU1QT1JUX0RVTVBfRElSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2RiSW1wb3J0RHVtcHN9CgojIGdhcmJhZ2UgY29sbGVjdCBhbGwgZmlsZXMgZm9yIHdoaWNoIG1vZGlmaWNhdGlvbiBpcyBncmVhdGVyIHRoYW4gZWc6IDMwIGRheXMgKCszMCkKIyBlYWNoIHRpbWUgYW4gZXhpc3RpbmcgZmlsZSBpcyB1c2VkIGJ5IGRiSW1wb3J0L2RiSW1wb3J0VGFibGUKIyB0aGUgZmlsZSBtb2RpZmljYXRpb24gdGltZSBpcyBzZXQgdG8gbm93CkRCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUz0ke0RCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUzotKzMwfQoKIyBhYnNvbHV0ZSBkaXJlY3RvcnkgY29udGFpbmluZyBkYlNjcmlwdHMgdXNlZCBieSBkYlNjcmlwdEFsbERhdGFiYXNlcwpTQ1JJUFRTX0ZPTERFUj0ke1NDUklQVFNfRk9MREVSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2NvbmYvZGJTY3JpcHRzfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEFXUyBQYXJhbWV0ZXJzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KUzNfQkFTRV9VUkw9JHtTM19CQVNFX1VSTDotfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvc3RtYW4gUGFyYW1ldGVycwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClBPU1RNQU5fQVBJX0tFWT0K" \ - "755" - -declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" - BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" @@ -377,7 +455,7 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp ( echo "#!/usr/bin/env bash" # shellcheck disable=SC2154 - echo "${embed_file_bashToolsDefaultConfigTemplate}" + echo "${bashToolsDefaultConfigTemplate}" ) >"${envFile}" Log::displayInfo "Configuration file '${envFile}' created" else @@ -390,7 +468,7 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp ) >>"${envFile}" fi fi - # shellcheck source=/conf/.env + # shellcheck source=/conf/defaultEnv/.env source "${envFile}" || { Log::displayError "impossible to load '${envFile}'" exit 1 @@ -398,36 +476,6 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp } -# @description convert base64 encoded back to target file -# if target file is executable prepend dir of target -# file to PATH to make binary available everywhere -# it is advised to include in the path of the target file -# the md5sum of the binFile -# -# @arg $1 targetFile:String the file to write -# @arg $2 binFileBase64:String the base64 encoded file -# @arg $3 fileMode:String the chmod to set on the file -# @set PATH String prepend target embedded file binary directory to PATH variable if binary executable -Compiler::Embed::extractFileFromBase64() { - local targetFile="$1" - local binFileBase64="$2" - local fileMode="${3:-+x}" - local targetDir="${targetFile%/*}" - - if [[ ! -f "${targetFile}" ]]; then - if [[ ! -d "${targetDir}" ]]; then - mkdir -p "${targetDir}" - fi - base64 -d >"${targetFile}" <<<"${binFileBase64}" - chmod "${fileMode}" "${targetFile}" - fi - - if [[ -x "${targetFile}" ]]; then - Env::pathPrepend "${targetDir}" - fi -} - - # @description get absolute conf file from specified conf folder deduced using these rules # * from absolute file (ignores and ) # * relative to where script is executed (ignores and ) @@ -762,19 +810,6 @@ Db::checkRequirements() { } -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" - fi - done -} - - # @description ensure env files are loaded # @arg $@ list of default files to load at the end # @exitcode 1 if one of env files fails to load @@ -857,14 +892,6 @@ Linux::requireRealpathCommand() { } -# @description ensure command tar is available -# @exitcode 1 if tar command not available -# @stderr diagnostics information is displayed -Linux::requireTarCommand() { - Assert::commandExists tar -} - - declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" @@ -1344,6 +1371,7 @@ beforeParseCallback() { copyrightCallback() { # + # shellcheck disable=SC2155,SC2154,SC2250 echo "Copyright (c) 2021-now François Chastanet" } @@ -1387,16 +1415,6 @@ optionVersionCallback() { exit 0 } -# shellcheck disable=SC2317 # if function is overridden -optionEnvFileCallback() { - local envFile="$2" - Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" - if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" - exit 1 - fi -} - # shellcheck disable=SC2317 # if function is overridden optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' @@ -1571,9 +1589,6 @@ commandOptionParseFinished() { defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi # shellcheck disable=SC2154 if [[ -f "${optionBashFrameworkConfig}" ]]; then files+=("${optionBashFrameworkConfig}") @@ -1588,40 +1603,159 @@ commandOptionParseFinished() { } + +defaultBeforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + +beforeParseCallback() { + defaultBeforeParseCallback +} + + + +profileOptionLongDescription() { + local profilesList="" + profilesList="$(Conf::getMergedList "dbImportProfiles" "sh" " - " || true)" + + echo -e " ${__HELP_TITLE}Profiles${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}Default profiles directory:${__HELP_NORMAL}" + echo -e " ${PROFILES_DIR-configuration error}" + echo + echo -e " ${__HELP_TITLE}User profiles directory:${__HELP_NORMAL}" + echo -e " ${HOME_PROFILES_DIR-configuration error}" + echo -e ' Allows to override profiles defined in "Default profiles directory"' + echo + echo -e " ${__HELP_TITLE}List of available profiles:${__HELP_NORMAL}" + echo -e "${profilesList}" +} + +profileOptionHelpFunction() { + echo " The name of the profile to use in order to" + echo " include or exclude tables." +} + +initOptionProfileIfNotSet() { + if [[ -z "${optionProfile}" ]]; then + optionProfile="${defaultOptionProfile}" + fi + local -a profilesArray + readarray -t profilesArray < <(Conf::getMergedList "dbImportProfiles" "sh" "" || true) + if ! Array::contains "${optionProfile}" "${profilesArray[@]}"; then + Log::displayError "${SCRIPT_NAME} - invalid profile '${optionProfile}' provided" + return 1 + fi +} + +declare defaultOptionProfile="default" +initProfileCommandCallback() { + initOptionProfileIfNotSet + + # shellcheck disable=SC2154 + if [[ "${optionProfile}" != "default" && -n "${optionTables}" ]]; then + Log::fatal "Command ${SCRIPT_NAME} - you cannot use table and profile options at the same time" + fi + + # Profile selection + local profileMsgInfo + # shellcheck disable=SC2154 + if [[ "${optionProfile}" = 'default' && -n "${optionTables}" ]]; then + profileCommandFile=$(Framework::createTempFile "profileCmd.XXXXXXXXXXXX") + profileMsgInfo="only ${optionTables} will be imported" + ( + echo '#!/usr/bin/env bash' + if [[ -n "${optionTables}" ]]; then + echo "${optionTables}" | sed -E 's/([A-Za-z0-9_]+),?/echo "\1"\n/g' + else + # tables option not specified, we will import all tables of the profile + echo 'cat' + fi + ) >"${profileCommandFile}" + else + profileCommandFile="$(Conf::getAbsoluteFile "dbImportProfiles" "${optionProfile}" "sh")" || exit 1 + profileMsgInfo="Using profile ${profileCommandFile}" + fi + chmod +x "${profileCommandFile}" + Log::displayInfo "${profileMsgInfo}" +} + + + +optionRatioHelpFunction() { + echo " define the ratio to use (0 to 100% - default 70)." + echo " - 0 means profile will filter out all the tables." + echo " - 100 means profile will keep all the tables." + echo " Eg: 70 means that tables with size(table+index)" + echo " that are greater than 70% of the max table size" + echo " will be excluded." +} + + + +fromDsnOptionLongDescription() { + local dsnList="" + dsnList="$(Conf::getMergedList "dsn" "env" " - ")" + + echo -e " ${__HELP_TITLE}Data Source Name (DSN)${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}Default dsn directory:${__HELP_NORMAL}" + echo -e " ${BASH_TOOLS_ROOT_DIR}/conf/dsn" + echo + echo -e " ${__HELP_TITLE}User dsn directory:${__HELP_NORMAL}" + echo -e " ${HOME}/.bash-tools/dsn" + echo -e ' Allows to override dsn defined in "Default dsn directory"' + echo + echo -e " ${__HELP_TITLE}List of available dsn:${__HELP_NORMAL}" + echo -e "${dsnList}" +} + + + +optionVersionCallback() { + # shellcheck disable=SC2154 + echo "${SCRIPT_NAME} version 3.0" + Db::checkRequirements + exit 0 +} + + declare defaultFromDsn="default.remote" # shellcheck disable=SC2034 declare PROFILES_DIR declare HOME_PROFILES_DIR +# shellcheck disable=SC2034 +defaultOptionProfile="" + +beforeParseCallback() { + defaultBeforeParseCallback + Linux::requireExecutedAsUser + Linux::requireRealpathCommand + Assert::commandExists mysql "sudo apt-get install -y mysql-client" + Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" +} initConf() { # shellcheck disable=SC2034 PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbImportProfiles" + # shellcheck disable=SC2034 HOME_PROFILES_DIR="${HOME}/.bash-tools/dbImportProfiles" } +initProfileCommandCallback() { + : +} + optionHelpCallback() { dbImportProfileCommandHelp exit 0 } longDescriptionFunction() { - local profilesList="" - local dsnList="" - dsnList="$(Conf::getMergedList "dsn" "env")" - profilesList="$(Conf::getMergedList "dbImportProfiles" "sh" || true)" - - echo -e "${__HELP_TITLE}Default profiles directory:${__HELP_NORMAL}" - echo -e "${PROFILES_DIR-configuration error}" - echo - echo -e "${__HELP_TITLE}User profiles directory:${__HELP_NORMAL}" - echo -e "${HOME_PROFILES_DIR-configuration error}" - echo -e 'Allows to override profiles defined in "Default profiles directory"' + fromDsnOptionLongDescription echo - echo -e "${__HELP_TITLE}List of available profiles:${__HELP_NORMAL}" - echo -e "${profilesList}" - echo - echo -e "${__HELP_TITLE}List of available dsn:${__HELP_NORMAL}" - echo -e "${dsnList}" + profileOptionLongDescription } optionProfileHelpFunction() { @@ -1638,16 +1772,6 @@ optionFromDsnHelpFunction() { echo } -optionRatioHelpFunction() { - Array::wrap2 " " 80 4 \ - " define the ratio to use (0 to 100% - default 70).\n" \ - "- 0 means profile will filter out all the tables\n" \ - "- 100 means profile will keep all the tables.\n" \ - "Eg: 70 means that tables with size(table+index)\n" \ - "that are greater than 70% of the max table size will be excluded." - echo -} - dbImportProfileCommandCallback() { if [[ -z "${fromDbName}" ]]; then Log::fatal "you must provide fromDbName" @@ -1669,29 +1793,13 @@ dbImportProfileCommandCallback() { } - -optionVersionCallback() { - # shellcheck disable=SC2154 - echo "${SCRIPT_NAME} version 3.0" - Db::checkRequirements - exit 0 -} - - - -beforeParseCallback() { - BashTools::Conf::requireLoad - Env::requireLoad - UI::requireTheme - Log::requireLoad -} - - # ------------------------------------------ # Command dbImportProfileCommand # ------------------------------------------ # options variables initialization +declare optionRatio="70" +declare optionProfile="" declare optionFromDsn="" declare optionHelp="0" declare optionConfig="0" @@ -1699,7 +1807,6 @@ declare optionBashFrameworkConfig="" declare optionInfoVerbose="0" declare optionDebugVerbose="0" declare optionTraceVerbose="0" -declare -a optionEnvFiles=() declare optionLogLevel="" declare optionLogFile="" declare optionDisplayLevel="" @@ -1707,14 +1814,18 @@ declare optionNoColor="0" declare optionTheme="default" declare optionVersion="0" declare optionQuiet="0" -declare optionProfile="" -declare optionRatio="70" # arguments variables initialization declare fromDbName="" # @description parse command options and arguments for dbImportProfileCommand dbImportProfileCommandParse() { Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" + optionRatio="70" + local -i options_parse_optionParsedCountOptionRatio + ((options_parse_optionParsedCountOptionRatio = 0)) || true + optionProfile="" + local -i options_parse_optionParsedCountOptionProfile + ((options_parse_optionParsedCountOptionProfile = 0)) || true optionFromDsn="" local -i options_parse_optionParsedCountOptionFromDsn ((options_parse_optionParsedCountOptionFromDsn = 0)) || true @@ -1736,7 +1847,6 @@ dbImportProfileCommandParse() { optionTraceVerbose="0" local -i options_parse_optionParsedCountOptionTraceVerbose ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionLogLevel="" local -i options_parse_optionParsedCountOptionLogLevel ((options_parse_optionParsedCountOptionLogLevel = 0)) || true @@ -1758,12 +1868,6 @@ dbImportProfileCommandParse() { optionQuiet="0" local -i options_parse_optionParsedCountOptionQuiet ((options_parse_optionParsedCountOptionQuiet = 0)) || true - optionProfile="" - local -i options_parse_optionParsedCountOptionProfile - ((options_parse_optionParsedCountOptionProfile = 0)) || true - optionRatio="70" - local -i options_parse_optionParsedCountOptionRatio - ((options_parse_optionParsedCountOptionRatio = 0)) || true fromDbName="" local -i options_parse_argParsedCountFromDbName @@ -1776,7 +1880,45 @@ dbImportProfileCommandParse() { local options_parse_arg="$1" local argOptDefaultBehavior=0 case "${options_parse_arg}" in - # Option 1/17 + # Option 1/16 + # optionRatio alts --ratio|-r + # type: String min 0 max 1 + --ratio | -r) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionRatio >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionRatio)) + # shellcheck disable=SC2034 + optionRatio="$1" + ;; + + # Option 2/16 + # optionProfile alts --profile|-p + # type: String min 0 max 1 + --profile | -p) + shift + if (($# == 0)); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" + return 1 + fi + + if ((options_parse_optionParsedCountOptionProfile >= 1 )); then + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" + return 1 + fi + ((++options_parse_optionParsedCountOptionProfile)) + # shellcheck disable=SC2034 + optionProfile="$1" + ;; + + # Option 3/16 # optionFromDsn alts --from-dsn|-f # type: String min 0 max 1 --from-dsn | -f) @@ -1795,7 +1937,7 @@ dbImportProfileCommandParse() { optionFromDsn="$1" ;; - # Option 2/17 + # Option 4/16 # optionHelp alts --help|-h # type: Boolean min 0 max 1 --help | -h) @@ -1811,7 +1953,7 @@ dbImportProfileCommandParse() { ;; - # Option 3/17 + # Option 5/16 # optionConfig alts --config # type: Boolean min 0 max 1 --config) @@ -1825,7 +1967,7 @@ dbImportProfileCommandParse() { ((++options_parse_optionParsedCountOptionConfig)) ;; - # Option 4/17 + # Option 6/16 # optionBashFrameworkConfig alts --bash-framework-config # type: String min 0 max 1 --bash-framework-config) @@ -1846,7 +1988,7 @@ dbImportProfileCommandParse() { ;; - # Option 5/17 + # Option 7/16 # optionInfoVerbose alts --verbose|-v # type: Boolean min 0 max 1 --verbose | -v) @@ -1864,7 +2006,7 @@ dbImportProfileCommandParse() { ;; - # Option 6/17 + # Option 8/16 # optionDebugVerbose alts -vv # type: Boolean min 0 max 1 -vv) @@ -1882,7 +2024,7 @@ dbImportProfileCommandParse() { ;; - # Option 7/17 + # Option 9/16 # optionTraceVerbose alts -vvv # type: Boolean min 0 max 1 -vvv) @@ -1900,25 +2042,7 @@ dbImportProfileCommandParse() { ;; - # Option 8/17 - # optionEnvFiles alts --env-file - # type: StringArray min 0 max -1 - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - ;; - - # Option 9/17 + # Option 10/16 # optionLogLevel alts --log-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1946,7 +2070,7 @@ dbImportProfileCommandParse() { ;; - # Option 10/17 + # Option 11/16 # optionLogFile alts --log-file # type: String min 0 max 1 --log-file) @@ -1969,7 +2093,7 @@ dbImportProfileCommandParse() { ;; - # Option 11/17 + # Option 12/16 # optionDisplayLevel alts --display-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1997,7 +2121,7 @@ dbImportProfileCommandParse() { ;; - # Option 12/17 + # Option 13/16 # optionNoColor alts --no-color # type: Boolean min 0 max 1 --no-color) @@ -2015,7 +2139,7 @@ dbImportProfileCommandParse() { ;; - # Option 13/17 + # Option 14/16 # optionTheme alts --theme # type: String min 0 max 1 # authorizedValues: default|default-force|noColor @@ -2043,7 +2167,7 @@ dbImportProfileCommandParse() { ;; - # Option 14/17 + # Option 15/16 # optionVersion alts --version # type: Boolean min 0 max 1 --version) @@ -2059,7 +2183,7 @@ dbImportProfileCommandParse() { ;; - # Option 15/17 + # Option 16/16 # optionQuiet alts --quiet|-q # type: Boolean min 0 max 1 --quiet | -q) @@ -2077,44 +2201,6 @@ dbImportProfileCommandParse() { ;; - # Option 16/17 - # optionProfile alts --profile|-p - # type: String min 0 max 1 - --profile | -p) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - if ((options_parse_optionParsedCountOptionProfile >= 1 )); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionProfile)) - # shellcheck disable=SC2034 - optionProfile="$1" - ;; - - # Option 17/17 - # optionRatio alts --ratio|-r - # type: String min 0 max 1 - --ratio | -r) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - if ((options_parse_optionParsedCountOptionRatio >= 1 )); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - Maximum number of option occurrences reached(1)" - return 1 - fi - ((++options_parse_optionParsedCountOptionRatio)) - # shellcheck disable=SC2034 - optionRatio="$1" - ;; - -*) if [[ "${argOptDefaultBehavior}" = "0" ]]; then Log::displayError "Command ${SCRIPT_NAME} - Invalid option ${options_parse_arg}" @@ -2179,14 +2265,15 @@ dbImportProfileCommandParse() { - if ((options_parse_argParsedCountFromDbName < 1 )); then Log::displayError "Command ${SCRIPT_NAME} - Argument 'fromDbName' should be provided at least 1 time(s)" return 1 fi || return $? + commandOptionParseFinished + initProfileCommandCallback dbImportProfileCommandCallback } @@ -2207,7 +2294,7 @@ dbImportProfileCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--from-dsn|-f ]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--profile|-p ]" "[--ratio|-r ]" + optionsAltList=("[--ratio|-r ]" "[--profile|-p ]" "[--from-dsn|-f ]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "dbImportProfile" "${optionsAltList[@]}" @@ -2226,6 +2313,21 @@ dbImportProfileCommandHelp() { # ------------------------------------------ # options section # ------------------------------------------ + echo + echo -e "${__HELP_TITLE_COLOR}PROFILE OPTIONS:${__RESET_COLOR}" + echo -e " ${__HELP_OPTION_COLOR}--ratio${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-r ${__HELP_NORMAL} {single}" + optionRatioHelpFunction + + + + Array::wrap2 ' ' 76 6 " Default value: " "70" + echo + + echo -e " ${__HELP_OPTION_COLOR}--profile${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-p ${__HELP_NORMAL} {single}" + profileOptionHelpFunction + + + echo echo -e "${__HELP_TITLE_COLOR}SOURCE OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--from-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-f ${__HELP_NORMAL} {single}" @@ -2271,12 +2373,6 @@ dbImportProfileCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" - echo - - - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo @@ -2323,21 +2419,6 @@ dbImportProfileCommandHelp() { echo - echo - echo -e "${__HELP_TITLE_COLOR}OPTIONS:${__RESET_COLOR}" - echo -e " ${__HELP_OPTION_COLOR}--profile${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-p ${__HELP_NORMAL} {single}" - optionProfileHelpFunction - - - - - echo -e " ${__HELP_OPTION_COLOR}--ratio${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-r ${__HELP_NORMAL} {single}" - optionRatioHelpFunction - - - - Array::wrap2 ' ' 76 6 " Default value: " "70" - echo # ------------------------------------------ # longDescription section # ------------------------------------------ @@ -2395,12 +2476,6 @@ WHERE ORDER BY maxSize DESC EOM2 -# check dependencies -Linux::requireExecutedAsUser -Linux::requireRealpathCommand -Assert::commandExists mysql "sudo apt-get install -y mysql-client" -Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" - # create db instance declare -Agx dbFromInstance diff --git a/bin/dbImportStream b/bin/dbImportStream index 5729ab43..7c30dc70 100755 --- a/bin/dbImportStream +++ b/bin/dbImportStream @@ -355,18 +355,79 @@ Bash::handlePipelineFailure() { } +read -r -d '\0' bashToolsDefaultConfigTemplate <<-EOM || true +# +# shellcheck disable=SC2034 + +# Default settings +# you can override these settings by creating ${HOME}/.bash-tools/.env file + +### +### DISPLAY Level +### minimum level of the messages that will be displayed on screen +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} + +### +### DISPLAY duration +### 0: no duration is displayed on the messages +### 1: duration between previous message and current is displayed +### with the message +### +DISPLAY_DURATION=${DISPLAY_DURATION:0} + +### +### Log to file +### +### all log messages will be redirected to log file specified +### this same path will be used inside and outside of the container +### +BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} + +### +### LOG Level +### minimum level of the messages that will be logged into LOG_FILE +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} + +# absolute directory containing db import sql dumps +DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} + +# garbage collect all files for which modification is greater than eg: 30 days (+30) +# each time an existing file is used by dbImport/dbImportTable +# the file modification time is set to now +DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} + +# absolute directory containing dbScripts used by dbScriptAllDatabases +SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} + +# ----------------------------------------------------- +# AWS Parameters +# ----------------------------------------------------- +S3_BASE_URL=${S3_BASE_URL:-} + +# ----------------------------------------------------- +# Postman Parameters +# ----------------------------------------------------- +POSTMAN_API_KEY= +EOM + # @description loads ~/.bash-tools/.env if available # if not creates it from a default template # else check if new options need to be added BashTools::Conf::requireLoad() { -Linux::requireTarCommand -Compiler::Embed::extractFileFromBase64 \ - "${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" \ - "IyEvdXNyL2Jpbi9lbnYgYmFzaAojIHNoZWxsY2hlY2sgZGlzYWJsZT1TQzIwMzQKCiMgRGVmYXVsdCBzZXR0aW5ncwojIHlvdSBjYW4gb3ZlcnJpZGUgdGhlc2Ugc2V0dGluZ3MgYnkgY3JlYXRpbmcgJHtIT01FfS8uYmFzaC10b29scy8uZW52IGZpbGUKCiMjIwojIyMgRElTUExBWSBMZXZlbAojIyMgbWluaW11bSBsZXZlbCBvZiB0aGUgbWVzc2FnZXMgdGhhdCB3aWxsIGJlIGRpc3BsYXllZCBvbiBzY3JlZW4KIyMjCiMjIyAwOiBOTyBMT0cKIyMjIDE6IEVSUk9SCiMjIyAyOiBXQVJOSU5HCiMjIyAzOiBJTkZPCiMjIyA0OiBERUJVRwojIyMKQkFTSF9GUkFNRVdPUktfRElTUExBWV9MRVZFTD0ke0JBU0hfRlJBTUVXT1JLX0RJU1BMQVlfTEVWRUw6LTN9CgojIyMKIyMjIERJU1BMQVkgZHVyYXRpb24KIyMjIDA6IG5vIGR1cmF0aW9uIGlzIGRpc3BsYXllZCBvbiB0aGUgbWVzc2FnZXMKIyMjIDE6IGR1cmF0aW9uIGJldHdlZW4gcHJldmlvdXMgbWVzc2FnZSBhbmQgY3VycmVudCBpcyBkaXNwbGF5ZWQKIyMjIHdpdGggdGhlIG1lc3NhZ2UKIyMjCkRJU1BMQVlfRFVSQVRJT049JHtESVNQTEFZX0RVUkFUSU9OOjB9CgojIyMKIyMjIExvZyB0byBmaWxlCiMjIwojIyMgYWxsIGxvZyBtZXNzYWdlcyB3aWxsIGJlIHJlZGlyZWN0ZWQgdG8gbG9nIGZpbGUgc3BlY2lmaWVkCiMjIyB0aGlzIHNhbWUgcGF0aCB3aWxsIGJlIHVzZWQgaW5zaWRlIGFuZCBvdXRzaWRlIG9mIHRoZSBjb250YWluZXIKIyMjCkJBU0hfRlJBTUVXT1JLX0xPR19GSUxFPSR7QkFTSF9GUkFNRVdPUktfTE9HX0ZJTEU6LSR7RlJBTUVXT1JLX1JPT1RfRElSfS9sb2dzL2Jhc2gubG9nfQoKIyMjCiMjIyBMT0cgTGV2ZWwKIyMjIG1pbmltdW0gbGV2ZWwgb2YgdGhlIG1lc3NhZ2VzIHRoYXQgd2lsbCBiZSBsb2dnZWQgaW50byBMT0dfRklMRQojIyMKIyMjIDA6IE5PIExPRwojIyMgMTogRVJST1IKIyMjIDI6IFdBUk5JTkcKIyMjIDM6IElORk8KIyMjIDQ6IERFQlVHCiMjIwpCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw9JHtCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw6LTB9CgojIGFic29sdXRlIGRpcmVjdG9yeSBjb250YWluaW5nIGRiIGltcG9ydCBzcWwgZHVtcHMKREJfSU1QT1JUX0RVTVBfRElSPSR7REJfSU1QT1JUX0RVTVBfRElSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2RiSW1wb3J0RHVtcHN9CgojIGdhcmJhZ2UgY29sbGVjdCBhbGwgZmlsZXMgZm9yIHdoaWNoIG1vZGlmaWNhdGlvbiBpcyBncmVhdGVyIHRoYW4gZWc6IDMwIGRheXMgKCszMCkKIyBlYWNoIHRpbWUgYW4gZXhpc3RpbmcgZmlsZSBpcyB1c2VkIGJ5IGRiSW1wb3J0L2RiSW1wb3J0VGFibGUKIyB0aGUgZmlsZSBtb2RpZmljYXRpb24gdGltZSBpcyBzZXQgdG8gbm93CkRCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUz0ke0RCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUzotKzMwfQoKIyBhYnNvbHV0ZSBkaXJlY3RvcnkgY29udGFpbmluZyBkYlNjcmlwdHMgdXNlZCBieSBkYlNjcmlwdEFsbERhdGFiYXNlcwpTQ1JJUFRTX0ZPTERFUj0ke1NDUklQVFNfRk9MREVSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2NvbmYvZGJTY3JpcHRzfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEFXUyBQYXJhbWV0ZXJzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KUzNfQkFTRV9VUkw9JHtTM19CQVNFX1VSTDotfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvc3RtYW4gUGFyYW1ldGVycwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClBPU1RNQU5fQVBJX0tFWT0K" \ - "755" - -declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" - BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" @@ -394,7 +455,7 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp ( echo "#!/usr/bin/env bash" # shellcheck disable=SC2154 - echo "${embed_file_bashToolsDefaultConfigTemplate}" + echo "${bashToolsDefaultConfigTemplate}" ) >"${envFile}" Log::displayInfo "Configuration file '${envFile}' created" else @@ -407,7 +468,7 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp ) >>"${envFile}" fi fi - # shellcheck source=/conf/.env + # shellcheck source=/conf/defaultEnv/.env source "${envFile}" || { Log::displayError "impossible to load '${envFile}'" exit 1 @@ -1338,6 +1399,7 @@ beforeParseCallback() { copyrightCallback() { # + # shellcheck disable=SC2155,SC2154,SC2250 echo "Copyright (c) 2020-now François Chastanet" } @@ -1381,16 +1443,6 @@ optionVersionCallback() { exit 0 } -# shellcheck disable=SC2317 # if function is overridden -optionEnvFileCallback() { - local envFile="$2" - Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" - if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" - exit 1 - fi -} - # shellcheck disable=SC2317 # if function is overridden optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' @@ -1565,9 +1617,6 @@ commandOptionParseFinished() { defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi # shellcheck disable=SC2154 if [[ -f "${optionBashFrameworkConfig}" ]]; then files+=("${optionBashFrameworkConfig}") @@ -1583,16 +1632,26 @@ commandOptionParseFinished() { +defaultBeforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + +beforeParseCallback() { + defaultBeforeParseCallback +} + + + declare PROFILES_DIR declare HOME_PROFILES_DIR # shellcheck disable=SC2034 declare defaultFromDsn="default.remote" beforeParseCallback() { - BashTools::Conf::requireLoad - Env::requireLoad - UI::requireTheme - Log::requireLoad + defaultBeforeParseCallback Linux::requireExecutedAsUser Linux::requireRealpathCommand } @@ -1600,6 +1659,7 @@ beforeParseCallback() { initConf() { # shellcheck disable=SC2034 PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbImportProfiles" + # shellcheck disable=SC2034 HOME_PROFILES_DIR="${HOME}/.bash-tools/dbImportProfiles" Db::checkRequirements } @@ -1610,23 +1670,9 @@ optionHelpCallback() { } longDescriptionFunction() { - local profilesList="" - local dsnList="" - dsnList="$(Conf::getMergedList "dsn" "env")" - profilesList="$(Conf::getMergedList "dbImportProfiles" "sh" || true)" - - echo -e "${__HELP_TITLE}Default profiles directory:${__HELP_NORMAL}" - echo -e "${PROFILES_DIR-configuration error}" - echo - echo -e "${__HELP_TITLE}User profiles directory:${__HELP_NORMAL}" - echo -e "${HOME_PROFILES_DIR-configuration error}" - echo -e "Allows to override profiles defined in 'Default profiles directory'" + fromDsnOptionLongDescription echo - echo -e "${__HELP_TITLE}List of available profiles:${__HELP_NORMAL}" - echo -e "${profilesList}" - echo - echo -e "${__HELP_TITLE}List of available dsn:${__HELP_NORMAL}" - echo -e "${dsnList}" + profileOptionLongDescription } dbImportStreamCommandCallback() { @@ -1664,13 +1710,43 @@ initializeDefaultTargetMysqlOptions() { -profileOptionHelpFunction() { - Array::wrap2 " " 80 4 \ - " The name of the profile to use in order to include or exclude tables." +profileOptionLongDescription() { + local profilesList="" + profilesList="$(Conf::getMergedList "dbImportProfiles" "sh" " - " || true)" + + echo -e " ${__HELP_TITLE}Profiles${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}Default profiles directory:${__HELP_NORMAL}" + echo -e " ${PROFILES_DIR-configuration error}" echo + echo -e " ${__HELP_TITLE}User profiles directory:${__HELP_NORMAL}" + echo -e " ${HOME_PROFILES_DIR-configuration error}" + echo -e ' Allows to override profiles defined in "Default profiles directory"' + echo + echo -e " ${__HELP_TITLE}List of available profiles:${__HELP_NORMAL}" + echo -e "${profilesList}" +} + +profileOptionHelpFunction() { + echo " The name of the profile to use in order to" + echo " include or exclude tables." } +initOptionProfileIfNotSet() { + if [[ -z "${optionProfile}" ]]; then + optionProfile="${defaultOptionProfile}" + fi + local -a profilesArray + readarray -t profilesArray < <(Conf::getMergedList "dbImportProfiles" "sh" "" || true) + if ! Array::contains "${optionProfile}" "${profilesArray[@]}"; then + Log::displayError "${SCRIPT_NAME} - invalid profile '${optionProfile}' provided" + return 1 + fi +} + +declare defaultOptionProfile="default" initProfileCommandCallback() { + initOptionProfileIfNotSet + # shellcheck disable=SC2154 if [[ "${optionProfile}" != "default" && -n "${optionTables}" ]]; then Log::fatal "Command ${SCRIPT_NAME} - you cannot use table and profile options at the same time" @@ -1699,16 +1775,10 @@ initProfileCommandCallback() { Log::displayInfo "${profileMsgInfo}" } -profileOptionCallback() { - local -a profilesArray - readarray -t profilesArray < <(Conf::getMergedList "dbImportProfiles" "sh" "" || true) - if ! Array::contains "$2" "${profilesArray[@]}"; then - Log::displayError "${SCRIPT_NAME} - invalid profile '$2' provided" - return 1 - fi -} + optionTablesCallback() { + # shellcheck disable=SC2154 if [[ ! ${optionTables} =~ ^[A-Za-z0-9_]+(,[A-Za-z0-9_]+)*$ ]]; then Log::fatal "Command ${SCRIPT_NAME} - Table list is not valid : ${optionTables}" fi @@ -1716,6 +1786,24 @@ optionTablesCallback() { +fromDsnOptionLongDescription() { + local dsnList="" + dsnList="$(Conf::getMergedList "dsn" "env" " - ")" + + echo -e " ${__HELP_TITLE}Data Source Name (DSN)${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}Default dsn directory:${__HELP_NORMAL}" + echo -e " ${BASH_TOOLS_ROOT_DIR}/conf/dsn" + echo + echo -e " ${__HELP_TITLE}User dsn directory:${__HELP_NORMAL}" + echo -e " ${HOME}/.bash-tools/dsn" + echo -e ' Allows to override dsn defined in "Default dsn directory"' + echo + echo -e " ${__HELP_TITLE}List of available dsn:${__HELP_NORMAL}" + echo -e "${dsnList}" +} + + + optionVersionCallback() { # shellcheck disable=SC2154 echo "${SCRIPT_NAME} version 3.0" @@ -1724,21 +1812,12 @@ optionVersionCallback() { } - -beforeParseCallback() { - BashTools::Conf::requireLoad - Env::requireLoad - UI::requireTheme - Log::requireLoad -} - - # ------------------------------------------ # Command dbImportStreamCommand # ------------------------------------------ # options variables initialization -declare optionProfile="default" +declare optionProfile="" declare optionTables="" declare optionTargetDsn="default.local" declare optionCharacterSet="" @@ -1748,7 +1827,6 @@ declare optionBashFrameworkConfig="" declare optionInfoVerbose="0" declare optionDebugVerbose="0" declare optionTraceVerbose="0" -declare -a optionEnvFiles=() declare optionLogLevel="" declare optionLogFile="" declare optionDisplayLevel="" @@ -1763,7 +1841,7 @@ declare argTargetDbName="" dbImportStreamCommandParse() { Log::displayDebug "Command ${SCRIPT_NAME} - parse arguments: ${BASH_FRAMEWORK_ARGV[*]}" Log::displayDebug "Command ${SCRIPT_NAME} - parse filtered arguments: ${BASH_FRAMEWORK_ARGV_FILTERED[*]}" - optionProfile="default" + optionProfile="" local -i options_parse_optionParsedCountOptionProfile ((options_parse_optionParsedCountOptionProfile = 0)) || true optionTables="" @@ -1793,7 +1871,6 @@ dbImportStreamCommandParse() { optionTraceVerbose="0" local -i options_parse_optionParsedCountOptionTraceVerbose ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionLogLevel="" local -i options_parse_optionParsedCountOptionLogLevel ((options_parse_optionParsedCountOptionLogLevel = 0)) || true @@ -1831,7 +1908,7 @@ dbImportStreamCommandParse() { local options_parse_arg="$1" local argOptDefaultBehavior=0 case "${options_parse_arg}" in - # Option 1/18 + # Option 1/17 # optionProfile alts --profile|-p # type: String min 0 max 1 --profile | -p) @@ -1848,11 +1925,9 @@ dbImportStreamCommandParse() { ((++options_parse_optionParsedCountOptionProfile)) # shellcheck disable=SC2034 optionProfile="$1" - profileOptionCallback "${options_parse_arg}" "${optionProfile}" - ;; - # Option 2/18 + # Option 2/17 # optionTables alts --tables # type: String min 0 max 1 --tables) @@ -1873,7 +1948,7 @@ dbImportStreamCommandParse() { ;; - # Option 3/18 + # Option 3/17 # optionTargetDsn alts --target-dsn|-t # type: String min 0 max 1 --target-dsn | -t) @@ -1892,7 +1967,7 @@ dbImportStreamCommandParse() { optionTargetDsn="$1" ;; - # Option 4/18 + # Option 4/17 # optionCharacterSet alts --character-set|-c # type: String min 0 max 1 --character-set | -c) @@ -1911,7 +1986,7 @@ dbImportStreamCommandParse() { optionCharacterSet="$1" ;; - # Option 5/18 + # Option 5/17 # optionHelp alts --help|-h # type: Boolean min 0 max 1 --help | -h) @@ -1927,7 +2002,7 @@ dbImportStreamCommandParse() { ;; - # Option 6/18 + # Option 6/17 # optionConfig alts --config # type: Boolean min 0 max 1 --config) @@ -1941,7 +2016,7 @@ dbImportStreamCommandParse() { ((++options_parse_optionParsedCountOptionConfig)) ;; - # Option 7/18 + # Option 7/17 # optionBashFrameworkConfig alts --bash-framework-config # type: String min 0 max 1 --bash-framework-config) @@ -1962,7 +2037,7 @@ dbImportStreamCommandParse() { ;; - # Option 8/18 + # Option 8/17 # optionInfoVerbose alts --verbose|-v # type: Boolean min 0 max 1 --verbose | -v) @@ -1980,7 +2055,7 @@ dbImportStreamCommandParse() { ;; - # Option 9/18 + # Option 9/17 # optionDebugVerbose alts -vv # type: Boolean min 0 max 1 -vv) @@ -1998,7 +2073,7 @@ dbImportStreamCommandParse() { ;; - # Option 10/18 + # Option 10/17 # optionTraceVerbose alts -vvv # type: Boolean min 0 max 1 -vvv) @@ -2016,25 +2091,7 @@ dbImportStreamCommandParse() { ;; - # Option 11/18 - # optionEnvFiles alts --env-file - # type: StringArray min 0 max -1 - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - ;; - - # Option 12/18 + # Option 11/17 # optionLogLevel alts --log-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -2062,7 +2119,7 @@ dbImportStreamCommandParse() { ;; - # Option 13/18 + # Option 12/17 # optionLogFile alts --log-file # type: String min 0 max 1 --log-file) @@ -2085,7 +2142,7 @@ dbImportStreamCommandParse() { ;; - # Option 14/18 + # Option 13/17 # optionDisplayLevel alts --display-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -2113,7 +2170,7 @@ dbImportStreamCommandParse() { ;; - # Option 15/18 + # Option 14/17 # optionNoColor alts --no-color # type: Boolean min 0 max 1 --no-color) @@ -2131,7 +2188,7 @@ dbImportStreamCommandParse() { ;; - # Option 16/18 + # Option 15/17 # optionTheme alts --theme # type: String min 0 max 1 # authorizedValues: default|default-force|noColor @@ -2159,7 +2216,7 @@ dbImportStreamCommandParse() { ;; - # Option 17/18 + # Option 16/17 # optionVersion alts --version # type: Boolean min 0 max 1 --version) @@ -2175,7 +2232,7 @@ dbImportStreamCommandParse() { ;; - # Option 18/18 + # Option 17/17 # optionQuiet alts --quiet|-q # type: Boolean min 0 max 1 --quiet | -q) @@ -2274,7 +2331,6 @@ dbImportStreamCommandParse() { - if ((options_parse_argParsedCountArgDumpFile < 1 )); then Log::displayError "Command ${SCRIPT_NAME} - Argument 'argDumpFile' should be provided at least 1 time(s)" return 1 @@ -2309,7 +2365,7 @@ dbImportStreamCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--profile|-p ]" "[--tables ]" "[--target-dsn|-t ]" "[--character-set|-c ]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" + optionsAltList=("[--profile|-p ]" "[--tables ]" "[--target-dsn|-t ]" "[--character-set|-c ]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "dbImportStream" "${optionsAltList[@]}" @@ -2339,11 +2395,9 @@ dbImportStreamCommandHelp() { - Array::wrap2 ' ' 76 6 " Default value: " "default" - echo echo -e " ${__HELP_OPTION_COLOR}--tables ${__HELP_NORMAL} {single}" - Array::wrap2 ' ' 76 4 " " "Import only table specified in the list." "If aws mode, ignore profile option." "" + Array::wrap2 ' ' 76 4 " " "Import only table specified in the list." "If aws mode, ignore profile option." echo @@ -2400,12 +2454,6 @@ dbImportStreamCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" - echo - - - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo diff --git a/bin/dbQueryAllDatabases b/bin/dbQueryAllDatabases index fd019ddf..e3657570 100755 --- a/bin/dbQueryAllDatabases +++ b/bin/dbQueryAllDatabases @@ -338,18 +338,79 @@ Bash::handlePipelineFailure() { } +read -r -d '\0' bashToolsDefaultConfigTemplate <<-EOM || true +# +# shellcheck disable=SC2034 + +# Default settings +# you can override these settings by creating ${HOME}/.bash-tools/.env file + +### +### DISPLAY Level +### minimum level of the messages that will be displayed on screen +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} + +### +### DISPLAY duration +### 0: no duration is displayed on the messages +### 1: duration between previous message and current is displayed +### with the message +### +DISPLAY_DURATION=${DISPLAY_DURATION:0} + +### +### Log to file +### +### all log messages will be redirected to log file specified +### this same path will be used inside and outside of the container +### +BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} + +### +### LOG Level +### minimum level of the messages that will be logged into LOG_FILE +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} + +# absolute directory containing db import sql dumps +DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} + +# garbage collect all files for which modification is greater than eg: 30 days (+30) +# each time an existing file is used by dbImport/dbImportTable +# the file modification time is set to now +DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} + +# absolute directory containing dbScripts used by dbScriptAllDatabases +SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} + +# ----------------------------------------------------- +# AWS Parameters +# ----------------------------------------------------- +S3_BASE_URL=${S3_BASE_URL:-} + +# ----------------------------------------------------- +# Postman Parameters +# ----------------------------------------------------- +POSTMAN_API_KEY= +EOM + # @description loads ~/.bash-tools/.env if available # if not creates it from a default template # else check if new options need to be added BashTools::Conf::requireLoad() { -Linux::requireTarCommand -Compiler::Embed::extractFileFromBase64 \ - "${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" \ - "IyEvdXNyL2Jpbi9lbnYgYmFzaAojIHNoZWxsY2hlY2sgZGlzYWJsZT1TQzIwMzQKCiMgRGVmYXVsdCBzZXR0aW5ncwojIHlvdSBjYW4gb3ZlcnJpZGUgdGhlc2Ugc2V0dGluZ3MgYnkgY3JlYXRpbmcgJHtIT01FfS8uYmFzaC10b29scy8uZW52IGZpbGUKCiMjIwojIyMgRElTUExBWSBMZXZlbAojIyMgbWluaW11bSBsZXZlbCBvZiB0aGUgbWVzc2FnZXMgdGhhdCB3aWxsIGJlIGRpc3BsYXllZCBvbiBzY3JlZW4KIyMjCiMjIyAwOiBOTyBMT0cKIyMjIDE6IEVSUk9SCiMjIyAyOiBXQVJOSU5HCiMjIyAzOiBJTkZPCiMjIyA0OiBERUJVRwojIyMKQkFTSF9GUkFNRVdPUktfRElTUExBWV9MRVZFTD0ke0JBU0hfRlJBTUVXT1JLX0RJU1BMQVlfTEVWRUw6LTN9CgojIyMKIyMjIERJU1BMQVkgZHVyYXRpb24KIyMjIDA6IG5vIGR1cmF0aW9uIGlzIGRpc3BsYXllZCBvbiB0aGUgbWVzc2FnZXMKIyMjIDE6IGR1cmF0aW9uIGJldHdlZW4gcHJldmlvdXMgbWVzc2FnZSBhbmQgY3VycmVudCBpcyBkaXNwbGF5ZWQKIyMjIHdpdGggdGhlIG1lc3NhZ2UKIyMjCkRJU1BMQVlfRFVSQVRJT049JHtESVNQTEFZX0RVUkFUSU9OOjB9CgojIyMKIyMjIExvZyB0byBmaWxlCiMjIwojIyMgYWxsIGxvZyBtZXNzYWdlcyB3aWxsIGJlIHJlZGlyZWN0ZWQgdG8gbG9nIGZpbGUgc3BlY2lmaWVkCiMjIyB0aGlzIHNhbWUgcGF0aCB3aWxsIGJlIHVzZWQgaW5zaWRlIGFuZCBvdXRzaWRlIG9mIHRoZSBjb250YWluZXIKIyMjCkJBU0hfRlJBTUVXT1JLX0xPR19GSUxFPSR7QkFTSF9GUkFNRVdPUktfTE9HX0ZJTEU6LSR7RlJBTUVXT1JLX1JPT1RfRElSfS9sb2dzL2Jhc2gubG9nfQoKIyMjCiMjIyBMT0cgTGV2ZWwKIyMjIG1pbmltdW0gbGV2ZWwgb2YgdGhlIG1lc3NhZ2VzIHRoYXQgd2lsbCBiZSBsb2dnZWQgaW50byBMT0dfRklMRQojIyMKIyMjIDA6IE5PIExPRwojIyMgMTogRVJST1IKIyMjIDI6IFdBUk5JTkcKIyMjIDM6IElORk8KIyMjIDQ6IERFQlVHCiMjIwpCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw9JHtCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw6LTB9CgojIGFic29sdXRlIGRpcmVjdG9yeSBjb250YWluaW5nIGRiIGltcG9ydCBzcWwgZHVtcHMKREJfSU1QT1JUX0RVTVBfRElSPSR7REJfSU1QT1JUX0RVTVBfRElSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2RiSW1wb3J0RHVtcHN9CgojIGdhcmJhZ2UgY29sbGVjdCBhbGwgZmlsZXMgZm9yIHdoaWNoIG1vZGlmaWNhdGlvbiBpcyBncmVhdGVyIHRoYW4gZWc6IDMwIGRheXMgKCszMCkKIyBlYWNoIHRpbWUgYW4gZXhpc3RpbmcgZmlsZSBpcyB1c2VkIGJ5IGRiSW1wb3J0L2RiSW1wb3J0VGFibGUKIyB0aGUgZmlsZSBtb2RpZmljYXRpb24gdGltZSBpcyBzZXQgdG8gbm93CkRCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUz0ke0RCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUzotKzMwfQoKIyBhYnNvbHV0ZSBkaXJlY3RvcnkgY29udGFpbmluZyBkYlNjcmlwdHMgdXNlZCBieSBkYlNjcmlwdEFsbERhdGFiYXNlcwpTQ1JJUFRTX0ZPTERFUj0ke1NDUklQVFNfRk9MREVSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2NvbmYvZGJTY3JpcHRzfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEFXUyBQYXJhbWV0ZXJzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KUzNfQkFTRV9VUkw9JHtTM19CQVNFX1VSTDotfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvc3RtYW4gUGFyYW1ldGVycwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClBPU1RNQU5fQVBJX0tFWT0K" \ - "755" - -declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" - BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" @@ -377,7 +438,7 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp ( echo "#!/usr/bin/env bash" # shellcheck disable=SC2154 - echo "${embed_file_bashToolsDefaultConfigTemplate}" + echo "${bashToolsDefaultConfigTemplate}" ) >"${envFile}" Log::displayInfo "Configuration file '${envFile}' created" else @@ -390,7 +451,7 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp ) >>"${envFile}" fi fi - # shellcheck source=/conf/.env + # shellcheck source=/conf/defaultEnv/.env source "${envFile}" || { Log::displayError "impossible to load '${envFile}'" exit 1 @@ -1388,6 +1449,7 @@ beforeParseCallback() { copyrightCallback() { # + # shellcheck disable=SC2155,SC2154,SC2250 echo "Copyright (c) 2020-now François Chastanet" } @@ -1431,16 +1493,6 @@ optionVersionCallback() { exit 0 } -# shellcheck disable=SC2317 # if function is overridden -optionEnvFileCallback() { - local envFile="$2" - Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" - if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" - exit 1 - fi -} - # shellcheck disable=SC2317 # if function is overridden optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' @@ -1615,9 +1667,6 @@ commandOptionParseFinished() { defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi # shellcheck disable=SC2154 if [[ -f "${optionBashFrameworkConfig}" ]]; then files+=("${optionBashFrameworkConfig}") @@ -1633,13 +1682,17 @@ commandOptionParseFinished() { -beforeParseCallback() { +defaultBeforeParseCallback() { BashTools::Conf::requireLoad Env::requireLoad UI::requireTheme Log::requireLoad } +beforeParseCallback() { + defaultBeforeParseCallback +} + declare -a PARALLEL_OPTIONS @@ -1661,6 +1714,24 @@ optionJobsCallback() { +fromDsnOptionLongDescription() { + local dsnList="" + dsnList="$(Conf::getMergedList "dsn" "env" " - ")" + + echo -e " ${__HELP_TITLE}Data Source Name (DSN)${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}Default dsn directory:${__HELP_NORMAL}" + echo -e " ${BASH_TOOLS_ROOT_DIR}/conf/dsn" + echo + echo -e " ${__HELP_TITLE}User dsn directory:${__HELP_NORMAL}" + echo -e " ${HOME}/.bash-tools/dsn" + echo -e ' Allows to override dsn defined in "Default dsn directory"' + echo + echo -e " ${__HELP_TITLE}List of available dsn:${__HELP_NORMAL}" + echo -e "${dsnList}" +} + + + optionVersionCallback() { # shellcheck disable=SC2154 echo "${SCRIPT_NAME} version 3.0" @@ -1681,10 +1752,7 @@ declare copyrightBeginYear="2020" declare defaultFromDsn="default.remote" beforeParseCallback() { - BashTools::Conf::requireLoad - Env::requireLoad - UI::requireTheme - Log::requireLoad + defaultBeforeParseCallback Linux::requireExecutedAsUser Linux::requireRealpathCommand } @@ -1716,33 +1784,34 @@ optionSeparatorCallback() { longDescriptionFunction() { local example1=$'dbQueryAllDatabases databaseSize -j 12 --separator "|" --bar 2>/dev/null | column -s "|" -t -n -c 40' - local dsnList queriesList - dsnList="$(Conf::getMergedList "dsn" "env" " - ")" - queriesList="$(Conf::getMergedList "dbQueries" "sql" " - " || true)" + local queriesList + queriesList="$(Conf::getMergedList "dbQueries" "sql" " - " || true)" - echo -e "${__HELP_TITLE} LIST OF AVAILABLE DSN:${__HELP_NORMAL}" - echo -e "${dsnList}" + fromDsnOptionLongDescription echo - echo -e "${__HELP_TITLE} DEFAULT QUERIES DIRECTORY:${__HELP_NORMAL}" - echo -e " ${QUERIES_DIR-configuration error}" + echo -e " ${__HELP_TITLE}QUERIES${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}Default queries directory:${__HELP_NORMAL}" + echo -e " ${QUERIES_DIR-configuration error}" echo - echo -e "${__HELP_TITLE} USER QUERIES DIRECTORY:${__HELP_NORMAL}" - echo -e " ${HOME_QUERIES_DIR-configuration error}" - echo -e " Allows to override queries defined in 'Default queries directory'" + echo -e " ${__HELP_TITLE}User queries directory:${__HELP_NORMAL}" + echo -e " ${HOME_QUERIES_DIR-configuration error}" + echo -e " Allows to override queries defined in 'Default queries directory'" echo - echo -e "${__HELP_TITLE} LIST OF AVAILABLE QUERIES:${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}List of available queries:${__HELP_NORMAL}" echo -e "${queriesList}" echo - echo -e "${__HELP_TITLE} EXAMPLES:${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}EXAMPLES:${__HELP_NORMAL}" echo -e " ${__HELP_EXAMPLE}${example1}${__HELP_NORMAL}" } argQueryHelpFunction() { - Array::wrap2 " " 80 6 \ - " Query to execute" "\n" \ - "- , try to execute the mysql query provided by the file" "\n" \ - '- , search for query file in queries directory (see below)' "\n" \ - '- else the argument is interpreted as query string' + echo " Query to execute" + echo " - , try to execute the mysql query" + echo " provided by the file" + echo ' - , search for query file in' + echo ' queries directory (see below)' + echo ' - else the argument is interpreted as' + echo ' query string' } dbQueryAllDatabasesEveryArgumentCallback() { @@ -1788,7 +1857,6 @@ declare optionBashFrameworkConfig="" declare optionInfoVerbose="0" declare optionDebugVerbose="0" declare optionTraceVerbose="0" -declare -a optionEnvFiles=() declare optionLogLevel="" declare optionLogFile="" declare optionDisplayLevel="" @@ -1830,7 +1898,6 @@ dbQueryAllDatabasesCommandParse() { optionTraceVerbose="0" local -i options_parse_optionParsedCountOptionTraceVerbose ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionLogLevel="" local -i options_parse_optionParsedCountOptionLogLevel ((options_parse_optionParsedCountOptionLogLevel = 0)) || true @@ -1867,7 +1934,7 @@ dbQueryAllDatabasesCommandParse() { local options_parse_arg="$1" local argOptDefaultBehavior=0 case "${options_parse_arg}" in - # Option 1/18 + # Option 1/17 # optionJobs alts --jobs|-j # type: String min 0 max 1 --jobs | -j) @@ -1888,7 +1955,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 2/18 + # Option 2/17 # optionProgressBar alts --bar|-b # type: Boolean min 0 max 1 --bar | -b) @@ -1904,7 +1971,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 3/18 + # Option 3/17 # optionFromDsn alts --from-dsn|-f # type: String min 0 max 1 --from-dsn | -f) @@ -1923,7 +1990,7 @@ dbQueryAllDatabasesCommandParse() { optionFromDsn="$1" ;; - # Option 4/18 + # Option 4/17 # optionHelp alts --help|-h # type: Boolean min 0 max 1 --help | -h) @@ -1939,7 +2006,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 5/18 + # Option 5/17 # optionConfig alts --config # type: Boolean min 0 max 1 --config) @@ -1953,7 +2020,7 @@ dbQueryAllDatabasesCommandParse() { ((++options_parse_optionParsedCountOptionConfig)) ;; - # Option 6/18 + # Option 6/17 # optionBashFrameworkConfig alts --bash-framework-config # type: String min 0 max 1 --bash-framework-config) @@ -1974,7 +2041,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 7/18 + # Option 7/17 # optionInfoVerbose alts --verbose|-v # type: Boolean min 0 max 1 --verbose | -v) @@ -1992,7 +2059,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 8/18 + # Option 8/17 # optionDebugVerbose alts -vv # type: Boolean min 0 max 1 -vv) @@ -2010,7 +2077,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 9/18 + # Option 9/17 # optionTraceVerbose alts -vvv # type: Boolean min 0 max 1 -vvv) @@ -2028,25 +2095,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 10/18 - # optionEnvFiles alts --env-file - # type: StringArray min 0 max -1 - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - ;; - - # Option 11/18 + # Option 10/17 # optionLogLevel alts --log-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -2074,7 +2123,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 12/18 + # Option 11/17 # optionLogFile alts --log-file # type: String min 0 max 1 --log-file) @@ -2097,7 +2146,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 13/18 + # Option 12/17 # optionDisplayLevel alts --display-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -2125,7 +2174,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 14/18 + # Option 13/17 # optionNoColor alts --no-color # type: Boolean min 0 max 1 --no-color) @@ -2143,7 +2192,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 15/18 + # Option 14/17 # optionTheme alts --theme # type: String min 0 max 1 # authorizedValues: default|default-force|noColor @@ -2171,7 +2220,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 16/18 + # Option 15/17 # optionVersion alts --version # type: Boolean min 0 max 1 --version) @@ -2187,7 +2236,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 17/18 + # Option 16/17 # optionQuiet alts --quiet|-q # type: Boolean min 0 max 1 --quiet | -q) @@ -2205,7 +2254,7 @@ dbQueryAllDatabasesCommandParse() { ;; - # Option 18/18 + # Option 17/17 # optionSeparator alts --separator|-s # type: String min 0 max 1 --separator | -s) @@ -2295,7 +2344,6 @@ dbQueryAllDatabasesCommandParse() { - if ((options_parse_argParsedCountArgQuery < 1 )); then Log::displayError "Command ${SCRIPT_NAME} - Argument 'argQuery' should be provided at least 1 time(s)" return 1 @@ -2310,7 +2358,7 @@ dbQueryAllDatabasesCommandParse() { # @description display command options and arguments help for dbQueryAllDatabasesCommand dbQueryAllDatabasesCommandHelp() { echo -e "${__HELP_TITLE_COLOR}SYNOPSIS:${__RESET_COLOR}" - Array::wrap2 ' ' 76 4 " " "Execute a query on multiple databases to generate a tsv format report.\\n The query can be parallelized on multiple databases." + Array::wrap2 ' ' 76 4 " " "Execute a query on multiple databases to generate a tsv format report." "The query can be parallelized on multiple databases." echo echo @@ -2323,7 +2371,7 @@ dbQueryAllDatabasesCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--jobs|-j ]" "[--bar|-b]" "[--from-dsn|-f ]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--separator|-s ]" + optionsAltList=("[--jobs|-j ]" "[--bar|-b]" "[--from-dsn|-f ]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--separator|-s ]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "dbQueryAllDatabases" "${optionsAltList[@]}" @@ -2353,7 +2401,7 @@ dbQueryAllDatabasesCommandHelp() { echo echo -e " ${__HELP_OPTION_COLOR}--bar${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-b${__HELP_NORMAL} {single}" - Array::wrap2 ' ' 76 4 " " "Show progress as a progress bar. In the bar is shown: % of jobs completed," "estimated seconds left, and number of jobs started." "" + Array::wrap2 ' ' 76 4 " " "Show progress as a progress bar. In the bar is shown: % of jobs" "completed, estimated seconds left, and number of jobs started." echo @@ -2402,12 +2450,6 @@ dbQueryAllDatabasesCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" - echo - - - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo diff --git a/bin/dbScriptAllDatabases b/bin/dbScriptAllDatabases index 4b34f5a7..23416349 100755 --- a/bin/dbScriptAllDatabases +++ b/bin/dbScriptAllDatabases @@ -338,18 +338,79 @@ Bash::handlePipelineFailure() { } +read -r -d '\0' bashToolsDefaultConfigTemplate <<-EOM || true +# +# shellcheck disable=SC2034 + +# Default settings +# you can override these settings by creating ${HOME}/.bash-tools/.env file + +### +### DISPLAY Level +### minimum level of the messages that will be displayed on screen +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} + +### +### DISPLAY duration +### 0: no duration is displayed on the messages +### 1: duration between previous message and current is displayed +### with the message +### +DISPLAY_DURATION=${DISPLAY_DURATION:0} + +### +### Log to file +### +### all log messages will be redirected to log file specified +### this same path will be used inside and outside of the container +### +BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} + +### +### LOG Level +### minimum level of the messages that will be logged into LOG_FILE +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} + +# absolute directory containing db import sql dumps +DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} + +# garbage collect all files for which modification is greater than eg: 30 days (+30) +# each time an existing file is used by dbImport/dbImportTable +# the file modification time is set to now +DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} + +# absolute directory containing dbScripts used by dbScriptAllDatabases +SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} + +# ----------------------------------------------------- +# AWS Parameters +# ----------------------------------------------------- +S3_BASE_URL=${S3_BASE_URL:-} + +# ----------------------------------------------------- +# Postman Parameters +# ----------------------------------------------------- +POSTMAN_API_KEY= +EOM + # @description loads ~/.bash-tools/.env if available # if not creates it from a default template # else check if new options need to be added BashTools::Conf::requireLoad() { -Linux::requireTarCommand -Compiler::Embed::extractFileFromBase64 \ - "${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" \ - "IyEvdXNyL2Jpbi9lbnYgYmFzaAojIHNoZWxsY2hlY2sgZGlzYWJsZT1TQzIwMzQKCiMgRGVmYXVsdCBzZXR0aW5ncwojIHlvdSBjYW4gb3ZlcnJpZGUgdGhlc2Ugc2V0dGluZ3MgYnkgY3JlYXRpbmcgJHtIT01FfS8uYmFzaC10b29scy8uZW52IGZpbGUKCiMjIwojIyMgRElTUExBWSBMZXZlbAojIyMgbWluaW11bSBsZXZlbCBvZiB0aGUgbWVzc2FnZXMgdGhhdCB3aWxsIGJlIGRpc3BsYXllZCBvbiBzY3JlZW4KIyMjCiMjIyAwOiBOTyBMT0cKIyMjIDE6IEVSUk9SCiMjIyAyOiBXQVJOSU5HCiMjIyAzOiBJTkZPCiMjIyA0OiBERUJVRwojIyMKQkFTSF9GUkFNRVdPUktfRElTUExBWV9MRVZFTD0ke0JBU0hfRlJBTUVXT1JLX0RJU1BMQVlfTEVWRUw6LTN9CgojIyMKIyMjIERJU1BMQVkgZHVyYXRpb24KIyMjIDA6IG5vIGR1cmF0aW9uIGlzIGRpc3BsYXllZCBvbiB0aGUgbWVzc2FnZXMKIyMjIDE6IGR1cmF0aW9uIGJldHdlZW4gcHJldmlvdXMgbWVzc2FnZSBhbmQgY3VycmVudCBpcyBkaXNwbGF5ZWQKIyMjIHdpdGggdGhlIG1lc3NhZ2UKIyMjCkRJU1BMQVlfRFVSQVRJT049JHtESVNQTEFZX0RVUkFUSU9OOjB9CgojIyMKIyMjIExvZyB0byBmaWxlCiMjIwojIyMgYWxsIGxvZyBtZXNzYWdlcyB3aWxsIGJlIHJlZGlyZWN0ZWQgdG8gbG9nIGZpbGUgc3BlY2lmaWVkCiMjIyB0aGlzIHNhbWUgcGF0aCB3aWxsIGJlIHVzZWQgaW5zaWRlIGFuZCBvdXRzaWRlIG9mIHRoZSBjb250YWluZXIKIyMjCkJBU0hfRlJBTUVXT1JLX0xPR19GSUxFPSR7QkFTSF9GUkFNRVdPUktfTE9HX0ZJTEU6LSR7RlJBTUVXT1JLX1JPT1RfRElSfS9sb2dzL2Jhc2gubG9nfQoKIyMjCiMjIyBMT0cgTGV2ZWwKIyMjIG1pbmltdW0gbGV2ZWwgb2YgdGhlIG1lc3NhZ2VzIHRoYXQgd2lsbCBiZSBsb2dnZWQgaW50byBMT0dfRklMRQojIyMKIyMjIDA6IE5PIExPRwojIyMgMTogRVJST1IKIyMjIDI6IFdBUk5JTkcKIyMjIDM6IElORk8KIyMjIDQ6IERFQlVHCiMjIwpCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw9JHtCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw6LTB9CgojIGFic29sdXRlIGRpcmVjdG9yeSBjb250YWluaW5nIGRiIGltcG9ydCBzcWwgZHVtcHMKREJfSU1QT1JUX0RVTVBfRElSPSR7REJfSU1QT1JUX0RVTVBfRElSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2RiSW1wb3J0RHVtcHN9CgojIGdhcmJhZ2UgY29sbGVjdCBhbGwgZmlsZXMgZm9yIHdoaWNoIG1vZGlmaWNhdGlvbiBpcyBncmVhdGVyIHRoYW4gZWc6IDMwIGRheXMgKCszMCkKIyBlYWNoIHRpbWUgYW4gZXhpc3RpbmcgZmlsZSBpcyB1c2VkIGJ5IGRiSW1wb3J0L2RiSW1wb3J0VGFibGUKIyB0aGUgZmlsZSBtb2RpZmljYXRpb24gdGltZSBpcyBzZXQgdG8gbm93CkRCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUz0ke0RCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUzotKzMwfQoKIyBhYnNvbHV0ZSBkaXJlY3RvcnkgY29udGFpbmluZyBkYlNjcmlwdHMgdXNlZCBieSBkYlNjcmlwdEFsbERhdGFiYXNlcwpTQ1JJUFRTX0ZPTERFUj0ke1NDUklQVFNfRk9MREVSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2NvbmYvZGJTY3JpcHRzfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEFXUyBQYXJhbWV0ZXJzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KUzNfQkFTRV9VUkw9JHtTM19CQVNFX1VSTDotfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvc3RtYW4gUGFyYW1ldGVycwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClBPU1RNQU5fQVBJX0tFWT0K" \ - "755" - -declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" - BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" @@ -377,7 +438,7 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp ( echo "#!/usr/bin/env bash" # shellcheck disable=SC2154 - echo "${embed_file_bashToolsDefaultConfigTemplate}" + echo "${bashToolsDefaultConfigTemplate}" ) >"${envFile}" Log::displayInfo "Configuration file '${envFile}' created" else @@ -390,7 +451,7 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp ) >>"${envFile}" fi fi - # shellcheck source=/conf/.env + # shellcheck source=/conf/defaultEnv/.env source "${envFile}" || { Log::displayError "impossible to load '${envFile}'" exit 1 @@ -398,36 +459,6 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp } -# @description convert base64 encoded back to target file -# if target file is executable prepend dir of target -# file to PATH to make binary available everywhere -# it is advised to include in the path of the target file -# the md5sum of the binFile -# -# @arg $1 targetFile:String the file to write -# @arg $2 binFileBase64:String the base64 encoded file -# @arg $3 fileMode:String the chmod to set on the file -# @set PATH String prepend target embedded file binary directory to PATH variable if binary executable -Compiler::Embed::extractFileFromBase64() { - local targetFile="$1" - local binFileBase64="$2" - local fileMode="${3:-+x}" - local targetDir="${targetFile%/*}" - - if [[ ! -f "${targetFile}" ]]; then - if [[ ! -d "${targetDir}" ]]; then - mkdir -p "${targetDir}" - fi - base64 -d >"${targetFile}" <<<"${binFileBase64}" - chmod "${fileMode}" "${targetFile}" - fi - - if [[ -x "${targetFile}" ]]; then - Env::pathPrepend "${targetDir}" - fi -} - - # @description get absolute conf file from specified conf folder deduced using these rules # * from absolute file (ignores and ) # * relative to where script is executed (ignores and ) @@ -759,19 +790,6 @@ Db::checkRequirements() { } -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" - fi - done -} - - # @description ensure env files are loaded # @arg $@ list of default files to load at the end # @exitcode 1 if one of env files fails to load @@ -854,14 +872,6 @@ Linux::requireRealpathCommand() { } -# @description ensure command tar is available -# @exitcode 1 if tar command not available -# @stderr diagnostics information is displayed -Linux::requireTarCommand() { - Assert::commandExists tar -} - - declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" @@ -1341,6 +1351,7 @@ beforeParseCallback() { copyrightCallback() { # + # shellcheck disable=SC2155,SC2154,SC2250 echo "Copyright (c) 2020-now François Chastanet" } @@ -1384,16 +1395,6 @@ optionVersionCallback() { exit 0 } -# shellcheck disable=SC2317 # if function is overridden -optionEnvFileCallback() { - local envFile="$2" - Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" - if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" - exit 1 - fi -} - # shellcheck disable=SC2317 # if function is overridden optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' @@ -1568,9 +1569,6 @@ commandOptionParseFinished() { defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi # shellcheck disable=SC2154 if [[ -f "${optionBashFrameworkConfig}" ]]; then files+=("${optionBashFrameworkConfig}") @@ -1586,13 +1584,17 @@ commandOptionParseFinished() { -beforeParseCallback() { +defaultBeforeParseCallback() { BashTools::Conf::requireLoad Env::requireLoad UI::requireTheme Log::requireLoad } +beforeParseCallback() { + defaultBeforeParseCallback +} + declare SCRIPTS_DIR @@ -1603,14 +1605,10 @@ declare defaultFromDsn="default.remote" declare outputDirectory="${HOME}/.bash-tools/output" beforeParseCallback() { + defaultBeforeParseCallback Assert::commandExists mysql "sudo apt-get install -y mysql-client" Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" Assert::commandExists parallel "sudo apt-get install -y parallel" - - BashTools::Conf::requireLoad - Env::requireLoad - UI::requireTheme - Log::requireLoad Linux::requireExecutedAsUser Linux::requireRealpathCommand } @@ -1628,37 +1626,36 @@ optionHelpCallback() { } longDescriptionFunction() { - local dsnList scriptsList - dsnList="$(Conf::getMergedList "dsn" "env")" - scriptsList="$(Conf::getMergedList "dbScripts" "sh")" + local scriptsList + scriptsList="$(Conf::getMergedList "dbScripts" "sh" " - " || true)" - echo -e "${__HELP_TITLE}NOTE:${__HELP_NORMAL}" - echo -e "the use of output, log-format, verbose options highly depends on the script used" - echo - echo -e "${__HELP_TITLE}LIST OF AVAILABLE DSN:${__HELP_NORMAL}" - echo -e "${dsnList}" + fromDsnOptionLongDescription echo - echo -e "${__HELP_TITLE}DEFAULT SCRIPTS DIRECTORY:${__HELP_NORMAL}" - echo -e "${SCRIPTS_DIR-configuration error}" + echo -e " ${__HELP_TITLE}SCRIPTS${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}Default scripts directory:${__HELP_NORMAL}" + echo -e " ${SCRIPTS_DIR-configuration error}" echo - echo -e "${__HELP_TITLE}USER SCRIPTS DIRECTORY:${__HELP_NORMAL}" - echo -e "${HOME_SCRIPTS_DIR-configuration error}" - echo -e "Allows to override queries defined in 'Default scripts directory'" + echo -e " ${__HELP_TITLE}User scripts directory:${__HELP_NORMAL}" + echo -e " ${HOME_SCRIPTS_DIR-configuration error}" + echo -e " Allows to override queries defined in 'Default scripts directory'" echo - echo -e "${__HELP_TITLE}LIST OF AVAILABLE SCRIPTS:${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}List of available scripts:${__HELP_NORMAL}" echo -e "${scriptsList}" echo - echo -e "${__HELP_TITLE}EXAMPLES:${__HELP_NORMAL} script conf/dbScripts/extractData.sh" - echo -e " executes query databaseSize (see conf/dbQueries/databaseSize.sql) on each db and log the result in log file in default output dir, call it using" + echo -e " ${__HELP_TITLE}NOTE:${__HELP_NORMAL}" + echo -e " the use of output, log-format, verbose options highly depends on the script used" + echo + echo -e " ${__HELP_TITLE}EXAMPLES:${__HELP_NORMAL} script conf/dbScripts/extractData.sh" + echo -e " 1. executes query databaseSize (see conf/dbQueries/databaseSize.sql) on each db and log the result in log file in default output dir, call it using" echo -e " ${__HELP_EXAMPLE}$0 -j 10 extractData databaseSize${__HELP_NORMAL}" echo - echo -e " executes query databaseSize on each db and display the result on stdout (2>/dev/null hides information messages)" + echo -e " 2. executes query databaseSize on each db and display the result on stdout (2>/dev/null hides information messages)" echo -e " ${__HELP_EXAMPLE}$0 -j 10 --log-format none extractData databaseSize${__HELP_NORMAL}" echo - echo -e " use --verbose to get some debug information" + echo -e " 3. use --verbose to get some debug information" echo -e " ${__HELP_EXAMPLE}$0 -j 10 --log-format none --verbose extractData databaseSize${__HELP_NORMAL}" echo - echo -e "${__HELP_TITLE}USE CASES:${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}USE CASES:${__HELP_NORMAL}" echo -e " you can use this script in order to check that each db model conforms with your ORM schema" echo -e " simply create a new script in conf/dbQueries that will call your orm schema checker" echo @@ -1716,6 +1713,24 @@ optionJobsCallback() { +fromDsnOptionLongDescription() { + local dsnList="" + dsnList="$(Conf::getMergedList "dsn" "env" " - ")" + + echo -e " ${__HELP_TITLE}Data Source Name (DSN)${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}Default dsn directory:${__HELP_NORMAL}" + echo -e " ${BASH_TOOLS_ROOT_DIR}/conf/dsn" + echo + echo -e " ${__HELP_TITLE}User dsn directory:${__HELP_NORMAL}" + echo -e " ${HOME}/.bash-tools/dsn" + echo -e ' Allows to override dsn defined in "Default dsn directory"' + echo + echo -e " ${__HELP_TITLE}List of available dsn:${__HELP_NORMAL}" + echo -e "${dsnList}" +} + + + optionVersionCallback() { # shellcheck disable=SC2154 echo "${SCRIPT_NAME} version 3.0" @@ -1738,7 +1753,6 @@ declare optionBashFrameworkConfig="" declare optionInfoVerbose="0" declare optionDebugVerbose="0" declare optionTraceVerbose="0" -declare -a optionEnvFiles=() declare optionLogLevel="" declare optionLogFile="" declare optionDisplayLevel="" @@ -1783,7 +1797,6 @@ dbScriptAllDatabasesCommandParse() { optionTraceVerbose="0" local -i options_parse_optionParsedCountOptionTraceVerbose ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionLogLevel="" local -i options_parse_optionParsedCountOptionLogLevel ((options_parse_optionParsedCountOptionLogLevel = 0)) || true @@ -1827,7 +1840,7 @@ dbScriptAllDatabasesCommandParse() { local options_parse_arg="$1" local argOptDefaultBehavior=0 case "${options_parse_arg}" in - # Option 1/20 + # Option 1/19 # optionJobs alts --jobs|-j # type: String min 0 max 1 --jobs | -j) @@ -1848,7 +1861,7 @@ dbScriptAllDatabasesCommandParse() { ;; - # Option 2/20 + # Option 2/19 # optionProgressBar alts --bar|-b # type: Boolean min 0 max 1 --bar | -b) @@ -1864,7 +1877,7 @@ dbScriptAllDatabasesCommandParse() { ;; - # Option 3/20 + # Option 3/19 # optionFromDsn alts --from-dsn|-f # type: String min 0 max 1 --from-dsn | -f) @@ -1883,7 +1896,7 @@ dbScriptAllDatabasesCommandParse() { optionFromDsn="$1" ;; - # Option 4/20 + # Option 4/19 # optionHelp alts --help|-h # type: Boolean min 0 max 1 --help | -h) @@ -1899,7 +1912,7 @@ dbScriptAllDatabasesCommandParse() { ;; - # Option 5/20 + # Option 5/19 # optionConfig alts --config # type: Boolean min 0 max 1 --config) @@ -1913,7 +1926,7 @@ dbScriptAllDatabasesCommandParse() { ((++options_parse_optionParsedCountOptionConfig)) ;; - # Option 6/20 + # Option 6/19 # optionBashFrameworkConfig alts --bash-framework-config # type: String min 0 max 1 --bash-framework-config) @@ -1934,7 +1947,7 @@ dbScriptAllDatabasesCommandParse() { ;; - # Option 7/20 + # Option 7/19 # optionInfoVerbose alts --verbose|-v # type: Boolean min 0 max 1 --verbose | -v) @@ -1952,7 +1965,7 @@ dbScriptAllDatabasesCommandParse() { ;; - # Option 8/20 + # Option 8/19 # optionDebugVerbose alts -vv # type: Boolean min 0 max 1 -vv) @@ -1970,7 +1983,7 @@ dbScriptAllDatabasesCommandParse() { ;; - # Option 9/20 + # Option 9/19 # optionTraceVerbose alts -vvv # type: Boolean min 0 max 1 -vvv) @@ -1988,25 +2001,7 @@ dbScriptAllDatabasesCommandParse() { ;; - # Option 10/20 - # optionEnvFiles alts --env-file - # type: StringArray min 0 max -1 - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - ;; - - # Option 11/20 + # Option 10/19 # optionLogLevel alts --log-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -2034,7 +2029,7 @@ dbScriptAllDatabasesCommandParse() { ;; - # Option 12/20 + # Option 11/19 # optionLogFile alts --log-file # type: String min 0 max 1 --log-file) @@ -2057,7 +2052,7 @@ dbScriptAllDatabasesCommandParse() { ;; - # Option 13/20 + # Option 12/19 # optionDisplayLevel alts --display-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -2085,7 +2080,7 @@ dbScriptAllDatabasesCommandParse() { ;; - # Option 14/20 + # Option 13/19 # optionNoColor alts --no-color # type: Boolean min 0 max 1 --no-color) @@ -2103,7 +2098,7 @@ dbScriptAllDatabasesCommandParse() { ;; - # Option 15/20 + # Option 14/19 # optionTheme alts --theme # type: String min 0 max 1 # authorizedValues: default|default-force|noColor @@ -2131,7 +2126,7 @@ dbScriptAllDatabasesCommandParse() { ;; - # Option 16/20 + # Option 15/19 # optionVersion alts --version # type: Boolean min 0 max 1 --version) @@ -2147,7 +2142,7 @@ dbScriptAllDatabasesCommandParse() { ;; - # Option 17/20 + # Option 16/19 # optionQuiet alts --quiet|-q # type: Boolean min 0 max 1 --quiet | -q) @@ -2165,7 +2160,7 @@ dbScriptAllDatabasesCommandParse() { ;; - # Option 18/20 + # Option 17/19 # optionDatabases alts --database # type: StringArray min 0 max 1 --database) @@ -2183,7 +2178,7 @@ dbScriptAllDatabasesCommandParse() { optionDatabases+=("$1") ;; - # Option 19/20 + # Option 18/19 # optionOutputDir alts --output|-o # type: String min 0 max 1 --output | -o) @@ -2204,7 +2199,7 @@ dbScriptAllDatabasesCommandParse() { ;; - # Option 20/20 + # Option 19/19 # optionLogFormat alts --log-format|-l # type: String min 0 max 1 # authorizedValues: none|log @@ -2310,7 +2305,6 @@ dbScriptAllDatabasesCommandParse() { - if ((options_parse_argParsedCountArgScriptToExecute < 1 )); then Log::displayError "Command ${SCRIPT_NAME} - Argument 'scriptToExecute' should be provided at least 1 time(s)" return 1 @@ -2338,7 +2332,7 @@ dbScriptAllDatabasesCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--jobs|-j ]" "[--bar|-b]" "[--from-dsn|-f ]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--database ]" "[--output|-o ]" "[--log-format|-l ]" + optionsAltList=("[--jobs|-j ]" "[--bar|-b]" "[--from-dsn|-f ]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--database ]" "[--output|-o ]" "[--log-format|-l ]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "dbScriptAllDatabases" "${optionsAltList[@]}" @@ -2372,7 +2366,7 @@ dbScriptAllDatabasesCommandHelp() { echo echo -e " ${__HELP_OPTION_COLOR}--bar${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-b${__HELP_NORMAL} {single}" - Array::wrap2 ' ' 76 4 " " "Show progress as a progress bar. In the bar is shown: % of jobs completed," "estimated seconds left, and number of jobs started." "" + Array::wrap2 ' ' 76 4 " " "Show progress as a progress bar. In the bar is shown: % of jobs" "completed, estimated seconds left, and number of jobs started." echo @@ -2421,12 +2415,6 @@ dbScriptAllDatabasesCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" - echo - - - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo @@ -2476,7 +2464,7 @@ dbScriptAllDatabasesCommandHelp() { echo echo -e "${__HELP_TITLE_COLOR}SCRIPTS OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--database ${__HELP_NORMAL} {single}" - Array::wrap2 ' ' 76 4 " " "If provided will check only this db, otherwise script will be executed on all dbs of mysql server." + Array::wrap2 ' ' 76 4 " " "If provided will check only this db," "otherwise script will be executed on all dbs of mysql server." echo diff --git a/bin/doc b/bin/doc index 7f13186c..7129dfb5 100755 --- a/bin/doc +++ b/bin/doc @@ -307,6 +307,127 @@ Assert::tty() { } +read -r -d '\0' bashToolsDefaultConfigTemplate <<-EOM || true +# +# shellcheck disable=SC2034 + +# Default settings +# you can override these settings by creating ${HOME}/.bash-tools/.env file + +### +### DISPLAY Level +### minimum level of the messages that will be displayed on screen +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} + +### +### DISPLAY duration +### 0: no duration is displayed on the messages +### 1: duration between previous message and current is displayed +### with the message +### +DISPLAY_DURATION=${DISPLAY_DURATION:0} + +### +### Log to file +### +### all log messages will be redirected to log file specified +### this same path will be used inside and outside of the container +### +BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} + +### +### LOG Level +### minimum level of the messages that will be logged into LOG_FILE +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} + +# absolute directory containing db import sql dumps +DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} + +# garbage collect all files for which modification is greater than eg: 30 days (+30) +# each time an existing file is used by dbImport/dbImportTable +# the file modification time is set to now +DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} + +# absolute directory containing dbScripts used by dbScriptAllDatabases +SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} + +# ----------------------------------------------------- +# AWS Parameters +# ----------------------------------------------------- +S3_BASE_URL=${S3_BASE_URL:-} + +# ----------------------------------------------------- +# Postman Parameters +# ----------------------------------------------------- +POSTMAN_API_KEY= +EOM + +# @description loads ~/.bash-tools/.env if available +# if not creates it from a default template +# else check if new options need to be added +BashTools::Conf::requireLoad() { + BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" + if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then + FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" + else + # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR + FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" + fi + # shellcheck disable=SC2034 + FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" + # shellcheck disable=SC2034 + FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + + if [[ -f "${HOME}/.bash-tools/.env" ]]; then + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") + fi + + local envFile="${HOME}/.bash-tools/.env" + if [[ ! -f "${envFile}" ]]; then + mkdir -p "${HOME}/.bash-tools" + ( + echo "#!/usr/bin/env bash" + # shellcheck disable=SC2154 + echo "${bashToolsDefaultConfigTemplate}" + ) >"${envFile}" + Log::displayInfo "Configuration file '${envFile}' created" + else + if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then + ( + echo '# -----------------------------------------------------' + echo '# Postman Parameters' + echo '# -----------------------------------------------------' + echo 'POSTMAN_API_KEY=' + ) >>"${envFile}" + fi + fi + # shellcheck source=/conf/defaultEnv/.env + source "${envFile}" || { + Log::displayError "impossible to load '${envFile}'" + exit 1 + } +} + + # @description get file content if file not expired # @arg $1 file:String the file to get content from # @arg $2 maxDuration:int number of seconds after which the file is considered expired @@ -1106,6 +1227,7 @@ beforeParseCallback() { copyrightCallback() { # + # shellcheck disable=SC2155,SC2154,SC2250 echo "Copyright (c) 2022-now François Chastanet" } @@ -1149,16 +1271,6 @@ optionVersionCallback() { exit 0 } -# shellcheck disable=SC2317 # if function is overridden -optionEnvFileCallback() { - local envFile="$2" - Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" - if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" - exit 1 - fi -} - # shellcheck disable=SC2317 # if function is overridden optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' @@ -1333,9 +1445,6 @@ commandOptionParseFinished() { defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi # shellcheck disable=SC2154 if [[ -f "${optionBashFrameworkConfig}" ]]; then files+=("${optionBashFrameworkConfig}") @@ -1351,12 +1460,15 @@ commandOptionParseFinished() { -# shellcheck disable=SC2034 -declare optionContinuousIntegrationMode=0 +defaultBeforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad +} -# shellcheck disable=SC2317 # if function is overridden -updateOptionContinuousIntegrationMode() { - BASH_FRAMEWORK_ARGV_FILTERED+=("$1") +beforeParseCallback() { + defaultBeforeParseCallback } @@ -1382,7 +1494,6 @@ declare optionBashFrameworkConfig="" declare optionInfoVerbose="0" declare optionDebugVerbose="0" declare optionTraceVerbose="0" -declare -a optionEnvFiles=() declare optionLogLevel="" declare optionLogFile="" declare optionDisplayLevel="" @@ -1416,7 +1527,6 @@ docCommandParse() { optionTraceVerbose="0" local -i options_parse_optionParsedCountOptionTraceVerbose ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionLogLevel="" local -i options_parse_optionParsedCountOptionLogLevel ((options_parse_optionParsedCountOptionLogLevel = 0)) || true @@ -1446,7 +1556,7 @@ docCommandParse() { local options_parse_arg="$1" local argOptDefaultBehavior=0 case "${options_parse_arg}" in - # Option 1/15 + # Option 1/14 # optionContinuousIntegrationMode alts --ci # type: Boolean min 0 max 1 --ci) @@ -1462,7 +1572,7 @@ docCommandParse() { ;; - # Option 2/15 + # Option 2/14 # optionHelp alts --help|-h # type: Boolean min 0 max 1 --help | -h) @@ -1478,7 +1588,7 @@ docCommandParse() { ;; - # Option 3/15 + # Option 3/14 # optionConfig alts --config # type: Boolean min 0 max 1 --config) @@ -1492,7 +1602,7 @@ docCommandParse() { ((++options_parse_optionParsedCountOptionConfig)) ;; - # Option 4/15 + # Option 4/14 # optionBashFrameworkConfig alts --bash-framework-config # type: String min 0 max 1 --bash-framework-config) @@ -1513,7 +1623,7 @@ docCommandParse() { ;; - # Option 5/15 + # Option 5/14 # optionInfoVerbose alts --verbose|-v # type: Boolean min 0 max 1 --verbose | -v) @@ -1531,7 +1641,7 @@ docCommandParse() { ;; - # Option 6/15 + # Option 6/14 # optionDebugVerbose alts -vv # type: Boolean min 0 max 1 -vv) @@ -1549,7 +1659,7 @@ docCommandParse() { ;; - # Option 7/15 + # Option 7/14 # optionTraceVerbose alts -vvv # type: Boolean min 0 max 1 -vvv) @@ -1567,25 +1677,7 @@ docCommandParse() { ;; - # Option 8/15 - # optionEnvFiles alts --env-file - # type: StringArray min 0 max -1 - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - ;; - - # Option 9/15 + # Option 8/14 # optionLogLevel alts --log-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1613,7 +1705,7 @@ docCommandParse() { ;; - # Option 10/15 + # Option 9/14 # optionLogFile alts --log-file # type: String min 0 max 1 --log-file) @@ -1636,7 +1728,7 @@ docCommandParse() { ;; - # Option 11/15 + # Option 10/14 # optionDisplayLevel alts --display-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1664,7 +1756,7 @@ docCommandParse() { ;; - # Option 12/15 + # Option 11/14 # optionNoColor alts --no-color # type: Boolean min 0 max 1 --no-color) @@ -1682,7 +1774,7 @@ docCommandParse() { ;; - # Option 13/15 + # Option 12/14 # optionTheme alts --theme # type: String min 0 max 1 # authorizedValues: default|default-force|noColor @@ -1710,7 +1802,7 @@ docCommandParse() { ;; - # Option 14/15 + # Option 13/14 # optionVersion alts --version # type: Boolean min 0 max 1 --version) @@ -1726,7 +1818,7 @@ docCommandParse() { ;; - # Option 15/15 + # Option 14/14 # optionQuiet alts --quiet|-q # type: Boolean min 0 max 1 --quiet | -q) @@ -1785,7 +1877,7 @@ docCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--ci]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" + optionsAltList=("[--ci]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "doc" "${optionsAltList[@]}" @@ -1839,12 +1931,6 @@ docCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" - echo - - - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo diff --git a/bin/gitIsAncestorOf b/bin/gitIsAncestorOf index b5abf0e9..c337d386 100755 --- a/bin/gitIsAncestorOf +++ b/bin/gitIsAncestorOf @@ -338,18 +338,79 @@ Bash::handlePipelineFailure() { } +read -r -d '\0' bashToolsDefaultConfigTemplate <<-EOM || true +# +# shellcheck disable=SC2034 + +# Default settings +# you can override these settings by creating ${HOME}/.bash-tools/.env file + +### +### DISPLAY Level +### minimum level of the messages that will be displayed on screen +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} + +### +### DISPLAY duration +### 0: no duration is displayed on the messages +### 1: duration between previous message and current is displayed +### with the message +### +DISPLAY_DURATION=${DISPLAY_DURATION:0} + +### +### Log to file +### +### all log messages will be redirected to log file specified +### this same path will be used inside and outside of the container +### +BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} + +### +### LOG Level +### minimum level of the messages that will be logged into LOG_FILE +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} + +# absolute directory containing db import sql dumps +DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} + +# garbage collect all files for which modification is greater than eg: 30 days (+30) +# each time an existing file is used by dbImport/dbImportTable +# the file modification time is set to now +DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} + +# absolute directory containing dbScripts used by dbScriptAllDatabases +SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} + +# ----------------------------------------------------- +# AWS Parameters +# ----------------------------------------------------- +S3_BASE_URL=${S3_BASE_URL:-} + +# ----------------------------------------------------- +# Postman Parameters +# ----------------------------------------------------- +POSTMAN_API_KEY= +EOM + # @description loads ~/.bash-tools/.env if available # if not creates it from a default template # else check if new options need to be added BashTools::Conf::requireLoad() { -Linux::requireTarCommand -Compiler::Embed::extractFileFromBase64 \ - "${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" \ - "IyEvdXNyL2Jpbi9lbnYgYmFzaAojIHNoZWxsY2hlY2sgZGlzYWJsZT1TQzIwMzQKCiMgRGVmYXVsdCBzZXR0aW5ncwojIHlvdSBjYW4gb3ZlcnJpZGUgdGhlc2Ugc2V0dGluZ3MgYnkgY3JlYXRpbmcgJHtIT01FfS8uYmFzaC10b29scy8uZW52IGZpbGUKCiMjIwojIyMgRElTUExBWSBMZXZlbAojIyMgbWluaW11bSBsZXZlbCBvZiB0aGUgbWVzc2FnZXMgdGhhdCB3aWxsIGJlIGRpc3BsYXllZCBvbiBzY3JlZW4KIyMjCiMjIyAwOiBOTyBMT0cKIyMjIDE6IEVSUk9SCiMjIyAyOiBXQVJOSU5HCiMjIyAzOiBJTkZPCiMjIyA0OiBERUJVRwojIyMKQkFTSF9GUkFNRVdPUktfRElTUExBWV9MRVZFTD0ke0JBU0hfRlJBTUVXT1JLX0RJU1BMQVlfTEVWRUw6LTN9CgojIyMKIyMjIERJU1BMQVkgZHVyYXRpb24KIyMjIDA6IG5vIGR1cmF0aW9uIGlzIGRpc3BsYXllZCBvbiB0aGUgbWVzc2FnZXMKIyMjIDE6IGR1cmF0aW9uIGJldHdlZW4gcHJldmlvdXMgbWVzc2FnZSBhbmQgY3VycmVudCBpcyBkaXNwbGF5ZWQKIyMjIHdpdGggdGhlIG1lc3NhZ2UKIyMjCkRJU1BMQVlfRFVSQVRJT049JHtESVNQTEFZX0RVUkFUSU9OOjB9CgojIyMKIyMjIExvZyB0byBmaWxlCiMjIwojIyMgYWxsIGxvZyBtZXNzYWdlcyB3aWxsIGJlIHJlZGlyZWN0ZWQgdG8gbG9nIGZpbGUgc3BlY2lmaWVkCiMjIyB0aGlzIHNhbWUgcGF0aCB3aWxsIGJlIHVzZWQgaW5zaWRlIGFuZCBvdXRzaWRlIG9mIHRoZSBjb250YWluZXIKIyMjCkJBU0hfRlJBTUVXT1JLX0xPR19GSUxFPSR7QkFTSF9GUkFNRVdPUktfTE9HX0ZJTEU6LSR7RlJBTUVXT1JLX1JPT1RfRElSfS9sb2dzL2Jhc2gubG9nfQoKIyMjCiMjIyBMT0cgTGV2ZWwKIyMjIG1pbmltdW0gbGV2ZWwgb2YgdGhlIG1lc3NhZ2VzIHRoYXQgd2lsbCBiZSBsb2dnZWQgaW50byBMT0dfRklMRQojIyMKIyMjIDA6IE5PIExPRwojIyMgMTogRVJST1IKIyMjIDI6IFdBUk5JTkcKIyMjIDM6IElORk8KIyMjIDQ6IERFQlVHCiMjIwpCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw9JHtCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw6LTB9CgojIGFic29sdXRlIGRpcmVjdG9yeSBjb250YWluaW5nIGRiIGltcG9ydCBzcWwgZHVtcHMKREJfSU1QT1JUX0RVTVBfRElSPSR7REJfSU1QT1JUX0RVTVBfRElSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2RiSW1wb3J0RHVtcHN9CgojIGdhcmJhZ2UgY29sbGVjdCBhbGwgZmlsZXMgZm9yIHdoaWNoIG1vZGlmaWNhdGlvbiBpcyBncmVhdGVyIHRoYW4gZWc6IDMwIGRheXMgKCszMCkKIyBlYWNoIHRpbWUgYW4gZXhpc3RpbmcgZmlsZSBpcyB1c2VkIGJ5IGRiSW1wb3J0L2RiSW1wb3J0VGFibGUKIyB0aGUgZmlsZSBtb2RpZmljYXRpb24gdGltZSBpcyBzZXQgdG8gbm93CkRCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUz0ke0RCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUzotKzMwfQoKIyBhYnNvbHV0ZSBkaXJlY3RvcnkgY29udGFpbmluZyBkYlNjcmlwdHMgdXNlZCBieSBkYlNjcmlwdEFsbERhdGFiYXNlcwpTQ1JJUFRTX0ZPTERFUj0ke1NDUklQVFNfRk9MREVSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2NvbmYvZGJTY3JpcHRzfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEFXUyBQYXJhbWV0ZXJzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KUzNfQkFTRV9VUkw9JHtTM19CQVNFX1VSTDotfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvc3RtYW4gUGFyYW1ldGVycwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClBPU1RNQU5fQVBJX0tFWT0K" \ - "755" - -declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" - BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" @@ -377,7 +438,7 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp ( echo "#!/usr/bin/env bash" # shellcheck disable=SC2154 - echo "${embed_file_bashToolsDefaultConfigTemplate}" + echo "${bashToolsDefaultConfigTemplate}" ) >"${envFile}" Log::displayInfo "Configuration file '${envFile}' created" else @@ -390,7 +451,7 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp ) >>"${envFile}" fi fi - # shellcheck source=/conf/.env + # shellcheck source=/conf/defaultEnv/.env source "${envFile}" || { Log::displayError "impossible to load '${envFile}'" exit 1 @@ -419,36 +480,6 @@ BashTools::runVerboseIfNeeded() { } -# @description convert base64 encoded back to target file -# if target file is executable prepend dir of target -# file to PATH to make binary available everywhere -# it is advised to include in the path of the target file -# the md5sum of the binFile -# -# @arg $1 targetFile:String the file to write -# @arg $2 binFileBase64:String the base64 encoded file -# @arg $3 fileMode:String the chmod to set on the file -# @set PATH String prepend target embedded file binary directory to PATH variable if binary executable -Compiler::Embed::extractFileFromBase64() { - local targetFile="$1" - local binFileBase64="$2" - local fileMode="${3:-+x}" - local targetDir="${targetFile%/*}" - - if [[ ! -f "${targetFile}" ]]; then - if [[ ! -d "${targetDir}" ]]; then - mkdir -p "${targetDir}" - fi - base64 -d >"${targetFile}" <<<"${binFileBase64}" - chmod "${fileMode}" "${targetFile}" - fi - - if [[ -x "${targetFile}" ]]; then - Env::pathPrepend "${targetDir}" - fi -} - - # @description check if all requirements are satisfied # to execute dbImport commands Db::checkRequirements() { @@ -468,19 +499,6 @@ Db::checkRequirements() { } -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" - fi - done -} - - # @description ensure env files are loaded # @arg $@ list of default files to load at the end # @exitcode 1 if one of env files fails to load @@ -535,14 +553,6 @@ Linux::requireExecutedAsUser() { } -# @description ensure command tar is available -# @exitcode 1 if tar command not available -# @stderr diagnostics information is displayed -Linux::requireTarCommand() { - Assert::commandExists tar -} - - declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" @@ -622,19 +632,6 @@ Log::displayInfo() { } -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logWarning "$1" -} - - # @description Display message using error color (red) and exit immediately with error status 1 # @arg $1 message:String the message to display # @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs @@ -718,15 +715,6 @@ Log::logMessage() { } -# @description log message to file -# @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" - fi -} - - # @description activate or not Log::display* and Log::log* functions # based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL # environment variables loaded by Env::requireLoad @@ -1022,6 +1010,7 @@ beforeParseCallback() { copyrightCallback() { # + # shellcheck disable=SC2155,SC2154,SC2250 echo "Copyright (c) 2020-now François Chastanet" } @@ -1065,16 +1054,6 @@ optionVersionCallback() { exit 0 } -# shellcheck disable=SC2317 # if function is overridden -optionEnvFileCallback() { - local envFile="$2" - Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" - if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" - exit 1 - fi -} - # shellcheck disable=SC2317 # if function is overridden optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' @@ -1249,9 +1228,6 @@ commandOptionParseFinished() { defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi # shellcheck disable=SC2154 if [[ -f "${optionBashFrameworkConfig}" ]]; then files+=("${optionBashFrameworkConfig}") @@ -1267,14 +1243,24 @@ commandOptionParseFinished() { -beforeParseCallback() { - Linux::requireExecutedAsUser +defaultBeforeParseCallback() { BashTools::Conf::requireLoad Env::requireLoad UI::requireTheme Log::requireLoad } +beforeParseCallback() { + defaultBeforeParseCallback +} + + + +beforeParseCallback() { + defaultBeforeParseCallback + Linux::requireExecutedAsUser +} + declare optionRedirectCmdOutputs="" optionRedirectCmdOutputs() { export optionTraceVerbose @@ -1320,7 +1306,6 @@ declare optionBashFrameworkConfig="" declare optionInfoVerbose="0" declare optionDebugVerbose="0" declare optionTraceVerbose="0" -declare -a optionEnvFiles=() declare optionLogLevel="" declare optionLogFile="" declare optionDisplayLevel="" @@ -1353,7 +1338,6 @@ gitIsAncestorOfCommandParse() { optionTraceVerbose="0" local -i options_parse_optionParsedCountOptionTraceVerbose ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionLogLevel="" local -i options_parse_optionParsedCountOptionLogLevel ((options_parse_optionParsedCountOptionLogLevel = 0)) || true @@ -1391,7 +1375,7 @@ gitIsAncestorOfCommandParse() { local options_parse_arg="$1" local argOptDefaultBehavior=0 case "${options_parse_arg}" in - # Option 1/14 + # Option 1/13 # optionHelp alts --help|-h # type: Boolean min 0 max 1 --help | -h) @@ -1407,7 +1391,7 @@ gitIsAncestorOfCommandParse() { ;; - # Option 2/14 + # Option 2/13 # optionConfig alts --config # type: Boolean min 0 max 1 --config) @@ -1421,7 +1405,7 @@ gitIsAncestorOfCommandParse() { ((++options_parse_optionParsedCountOptionConfig)) ;; - # Option 3/14 + # Option 3/13 # optionBashFrameworkConfig alts --bash-framework-config # type: String min 0 max 1 --bash-framework-config) @@ -1442,7 +1426,7 @@ gitIsAncestorOfCommandParse() { ;; - # Option 4/14 + # Option 4/13 # optionInfoVerbose alts --verbose|-v # type: Boolean min 0 max 1 --verbose | -v) @@ -1460,7 +1444,7 @@ gitIsAncestorOfCommandParse() { ;; - # Option 5/14 + # Option 5/13 # optionDebugVerbose alts -vv # type: Boolean min 0 max 1 -vv) @@ -1478,7 +1462,7 @@ gitIsAncestorOfCommandParse() { ;; - # Option 6/14 + # Option 6/13 # optionTraceVerbose alts -vvv # type: Boolean min 0 max 1 -vvv) @@ -1496,25 +1480,7 @@ gitIsAncestorOfCommandParse() { ;; - # Option 7/14 - # optionEnvFiles alts --env-file - # type: StringArray min 0 max -1 - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - ;; - - # Option 8/14 + # Option 7/13 # optionLogLevel alts --log-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1542,7 +1508,7 @@ gitIsAncestorOfCommandParse() { ;; - # Option 9/14 + # Option 8/13 # optionLogFile alts --log-file # type: String min 0 max 1 --log-file) @@ -1565,7 +1531,7 @@ gitIsAncestorOfCommandParse() { ;; - # Option 10/14 + # Option 9/13 # optionDisplayLevel alts --display-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1593,7 +1559,7 @@ gitIsAncestorOfCommandParse() { ;; - # Option 11/14 + # Option 10/13 # optionNoColor alts --no-color # type: Boolean min 0 max 1 --no-color) @@ -1611,7 +1577,7 @@ gitIsAncestorOfCommandParse() { ;; - # Option 12/14 + # Option 11/13 # optionTheme alts --theme # type: String min 0 max 1 # authorizedValues: default|default-force|noColor @@ -1639,7 +1605,7 @@ gitIsAncestorOfCommandParse() { ;; - # Option 13/14 + # Option 12/13 # optionVersion alts --version # type: Boolean min 0 max 1 --version) @@ -1655,7 +1621,7 @@ gitIsAncestorOfCommandParse() { ;; - # Option 14/14 + # Option 13/13 # optionQuiet alts --quiet|-q # type: Boolean min 0 max 1 --quiet | -q) @@ -1750,7 +1716,6 @@ gitIsAncestorOfCommandParse() { - if ((options_parse_argParsedCountClaimedBranchArg < 1 )); then Log::displayError "Command ${SCRIPT_NAME} - Argument 'claimedBranch' should be provided at least 1 time(s)" return 1 @@ -1783,7 +1748,7 @@ gitIsAncestorOfCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" + optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "gitIsAncestorOf" "${optionsAltList[@]}" @@ -1844,12 +1809,6 @@ gitIsAncestorOfCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" - echo - - - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo diff --git a/bin/gitIsBranch b/bin/gitIsBranch index b9f09656..4ab4d29e 100755 --- a/bin/gitIsBranch +++ b/bin/gitIsBranch @@ -338,18 +338,79 @@ Bash::handlePipelineFailure() { } +read -r -d '\0' bashToolsDefaultConfigTemplate <<-EOM || true +# +# shellcheck disable=SC2034 + +# Default settings +# you can override these settings by creating ${HOME}/.bash-tools/.env file + +### +### DISPLAY Level +### minimum level of the messages that will be displayed on screen +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} + +### +### DISPLAY duration +### 0: no duration is displayed on the messages +### 1: duration between previous message and current is displayed +### with the message +### +DISPLAY_DURATION=${DISPLAY_DURATION:0} + +### +### Log to file +### +### all log messages will be redirected to log file specified +### this same path will be used inside and outside of the container +### +BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} + +### +### LOG Level +### minimum level of the messages that will be logged into LOG_FILE +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} + +# absolute directory containing db import sql dumps +DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} + +# garbage collect all files for which modification is greater than eg: 30 days (+30) +# each time an existing file is used by dbImport/dbImportTable +# the file modification time is set to now +DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} + +# absolute directory containing dbScripts used by dbScriptAllDatabases +SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} + +# ----------------------------------------------------- +# AWS Parameters +# ----------------------------------------------------- +S3_BASE_URL=${S3_BASE_URL:-} + +# ----------------------------------------------------- +# Postman Parameters +# ----------------------------------------------------- +POSTMAN_API_KEY= +EOM + # @description loads ~/.bash-tools/.env if available # if not creates it from a default template # else check if new options need to be added BashTools::Conf::requireLoad() { -Linux::requireTarCommand -Compiler::Embed::extractFileFromBase64 \ - "${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" \ - "IyEvdXNyL2Jpbi9lbnYgYmFzaAojIHNoZWxsY2hlY2sgZGlzYWJsZT1TQzIwMzQKCiMgRGVmYXVsdCBzZXR0aW5ncwojIHlvdSBjYW4gb3ZlcnJpZGUgdGhlc2Ugc2V0dGluZ3MgYnkgY3JlYXRpbmcgJHtIT01FfS8uYmFzaC10b29scy8uZW52IGZpbGUKCiMjIwojIyMgRElTUExBWSBMZXZlbAojIyMgbWluaW11bSBsZXZlbCBvZiB0aGUgbWVzc2FnZXMgdGhhdCB3aWxsIGJlIGRpc3BsYXllZCBvbiBzY3JlZW4KIyMjCiMjIyAwOiBOTyBMT0cKIyMjIDE6IEVSUk9SCiMjIyAyOiBXQVJOSU5HCiMjIyAzOiBJTkZPCiMjIyA0OiBERUJVRwojIyMKQkFTSF9GUkFNRVdPUktfRElTUExBWV9MRVZFTD0ke0JBU0hfRlJBTUVXT1JLX0RJU1BMQVlfTEVWRUw6LTN9CgojIyMKIyMjIERJU1BMQVkgZHVyYXRpb24KIyMjIDA6IG5vIGR1cmF0aW9uIGlzIGRpc3BsYXllZCBvbiB0aGUgbWVzc2FnZXMKIyMjIDE6IGR1cmF0aW9uIGJldHdlZW4gcHJldmlvdXMgbWVzc2FnZSBhbmQgY3VycmVudCBpcyBkaXNwbGF5ZWQKIyMjIHdpdGggdGhlIG1lc3NhZ2UKIyMjCkRJU1BMQVlfRFVSQVRJT049JHtESVNQTEFZX0RVUkFUSU9OOjB9CgojIyMKIyMjIExvZyB0byBmaWxlCiMjIwojIyMgYWxsIGxvZyBtZXNzYWdlcyB3aWxsIGJlIHJlZGlyZWN0ZWQgdG8gbG9nIGZpbGUgc3BlY2lmaWVkCiMjIyB0aGlzIHNhbWUgcGF0aCB3aWxsIGJlIHVzZWQgaW5zaWRlIGFuZCBvdXRzaWRlIG9mIHRoZSBjb250YWluZXIKIyMjCkJBU0hfRlJBTUVXT1JLX0xPR19GSUxFPSR7QkFTSF9GUkFNRVdPUktfTE9HX0ZJTEU6LSR7RlJBTUVXT1JLX1JPT1RfRElSfS9sb2dzL2Jhc2gubG9nfQoKIyMjCiMjIyBMT0cgTGV2ZWwKIyMjIG1pbmltdW0gbGV2ZWwgb2YgdGhlIG1lc3NhZ2VzIHRoYXQgd2lsbCBiZSBsb2dnZWQgaW50byBMT0dfRklMRQojIyMKIyMjIDA6IE5PIExPRwojIyMgMTogRVJST1IKIyMjIDI6IFdBUk5JTkcKIyMjIDM6IElORk8KIyMjIDQ6IERFQlVHCiMjIwpCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw9JHtCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw6LTB9CgojIGFic29sdXRlIGRpcmVjdG9yeSBjb250YWluaW5nIGRiIGltcG9ydCBzcWwgZHVtcHMKREJfSU1QT1JUX0RVTVBfRElSPSR7REJfSU1QT1JUX0RVTVBfRElSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2RiSW1wb3J0RHVtcHN9CgojIGdhcmJhZ2UgY29sbGVjdCBhbGwgZmlsZXMgZm9yIHdoaWNoIG1vZGlmaWNhdGlvbiBpcyBncmVhdGVyIHRoYW4gZWc6IDMwIGRheXMgKCszMCkKIyBlYWNoIHRpbWUgYW4gZXhpc3RpbmcgZmlsZSBpcyB1c2VkIGJ5IGRiSW1wb3J0L2RiSW1wb3J0VGFibGUKIyB0aGUgZmlsZSBtb2RpZmljYXRpb24gdGltZSBpcyBzZXQgdG8gbm93CkRCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUz0ke0RCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUzotKzMwfQoKIyBhYnNvbHV0ZSBkaXJlY3RvcnkgY29udGFpbmluZyBkYlNjcmlwdHMgdXNlZCBieSBkYlNjcmlwdEFsbERhdGFiYXNlcwpTQ1JJUFRTX0ZPTERFUj0ke1NDUklQVFNfRk9MREVSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2NvbmYvZGJTY3JpcHRzfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEFXUyBQYXJhbWV0ZXJzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KUzNfQkFTRV9VUkw9JHtTM19CQVNFX1VSTDotfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvc3RtYW4gUGFyYW1ldGVycwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClBPU1RNQU5fQVBJX0tFWT0K" \ - "755" - -declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" - BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" @@ -377,7 +438,7 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp ( echo "#!/usr/bin/env bash" # shellcheck disable=SC2154 - echo "${embed_file_bashToolsDefaultConfigTemplate}" + echo "${bashToolsDefaultConfigTemplate}" ) >"${envFile}" Log::displayInfo "Configuration file '${envFile}' created" else @@ -390,7 +451,7 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp ) >>"${envFile}" fi fi - # shellcheck source=/conf/.env + # shellcheck source=/conf/defaultEnv/.env source "${envFile}" || { Log::displayError "impossible to load '${envFile}'" exit 1 @@ -419,36 +480,6 @@ BashTools::runVerboseIfNeeded() { } -# @description convert base64 encoded back to target file -# if target file is executable prepend dir of target -# file to PATH to make binary available everywhere -# it is advised to include in the path of the target file -# the md5sum of the binFile -# -# @arg $1 targetFile:String the file to write -# @arg $2 binFileBase64:String the base64 encoded file -# @arg $3 fileMode:String the chmod to set on the file -# @set PATH String prepend target embedded file binary directory to PATH variable if binary executable -Compiler::Embed::extractFileFromBase64() { - local targetFile="$1" - local binFileBase64="$2" - local fileMode="${3:-+x}" - local targetDir="${targetFile%/*}" - - if [[ ! -f "${targetFile}" ]]; then - if [[ ! -d "${targetDir}" ]]; then - mkdir -p "${targetDir}" - fi - base64 -d >"${targetFile}" <<<"${binFileBase64}" - chmod "${fileMode}" "${targetFile}" - fi - - if [[ -x "${targetFile}" ]]; then - Env::pathPrepend "${targetDir}" - fi -} - - # @description check if all requirements are satisfied # to execute dbImport commands Db::checkRequirements() { @@ -468,19 +499,6 @@ Db::checkRequirements() { } -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" - fi - done -} - - # @description ensure env files are loaded # @arg $@ list of default files to load at the end # @exitcode 1 if one of env files fails to load @@ -535,14 +553,6 @@ Linux::requireExecutedAsUser() { } -# @description ensure command tar is available -# @exitcode 1 if tar command not available -# @stderr diagnostics information is displayed -Linux::requireTarCommand() { - Assert::commandExists tar -} - - declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" @@ -622,19 +632,6 @@ Log::displayInfo() { } -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logWarning "$1" -} - - # @description Display message using error color (red) and exit immediately with error status 1 # @arg $1 message:String the message to display # @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs @@ -718,15 +715,6 @@ Log::logMessage() { } -# @description log message to file -# @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" - fi -} - - # @description activate or not Log::display* and Log::log* functions # based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL # environment variables loaded by Env::requireLoad @@ -1022,6 +1010,7 @@ beforeParseCallback() { copyrightCallback() { # + # shellcheck disable=SC2155,SC2154,SC2250 echo "Copyright (c) 2020-now François Chastanet" } @@ -1065,16 +1054,6 @@ optionVersionCallback() { exit 0 } -# shellcheck disable=SC2317 # if function is overridden -optionEnvFileCallback() { - local envFile="$2" - Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" - if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" - exit 1 - fi -} - # shellcheck disable=SC2317 # if function is overridden optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' @@ -1249,9 +1228,6 @@ commandOptionParseFinished() { defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi # shellcheck disable=SC2154 if [[ -f "${optionBashFrameworkConfig}" ]]; then files+=("${optionBashFrameworkConfig}") @@ -1267,14 +1243,24 @@ commandOptionParseFinished() { -beforeParseCallback() { - Linux::requireExecutedAsUser +defaultBeforeParseCallback() { BashTools::Conf::requireLoad Env::requireLoad UI::requireTheme Log::requireLoad } +beforeParseCallback() { + defaultBeforeParseCallback +} + + + +beforeParseCallback() { + defaultBeforeParseCallback + Linux::requireExecutedAsUser +} + declare optionRedirectCmdOutputs="" optionRedirectCmdOutputs() { export optionTraceVerbose @@ -1320,7 +1306,6 @@ declare optionBashFrameworkConfig="" declare optionInfoVerbose="0" declare optionDebugVerbose="0" declare optionTraceVerbose="0" -declare -a optionEnvFiles=() declare optionLogLevel="" declare optionLogFile="" declare optionDisplayLevel="" @@ -1352,7 +1337,6 @@ gitIsBranchCommandParse() { optionTraceVerbose="0" local -i options_parse_optionParsedCountOptionTraceVerbose ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionLogLevel="" local -i options_parse_optionParsedCountOptionLogLevel ((options_parse_optionParsedCountOptionLogLevel = 0)) || true @@ -1386,7 +1370,7 @@ gitIsBranchCommandParse() { local options_parse_arg="$1" local argOptDefaultBehavior=0 case "${options_parse_arg}" in - # Option 1/14 + # Option 1/13 # optionHelp alts --help|-h # type: Boolean min 0 max 1 --help | -h) @@ -1402,7 +1386,7 @@ gitIsBranchCommandParse() { ;; - # Option 2/14 + # Option 2/13 # optionConfig alts --config # type: Boolean min 0 max 1 --config) @@ -1416,7 +1400,7 @@ gitIsBranchCommandParse() { ((++options_parse_optionParsedCountOptionConfig)) ;; - # Option 3/14 + # Option 3/13 # optionBashFrameworkConfig alts --bash-framework-config # type: String min 0 max 1 --bash-framework-config) @@ -1437,7 +1421,7 @@ gitIsBranchCommandParse() { ;; - # Option 4/14 + # Option 4/13 # optionInfoVerbose alts --verbose|-v # type: Boolean min 0 max 1 --verbose | -v) @@ -1455,7 +1439,7 @@ gitIsBranchCommandParse() { ;; - # Option 5/14 + # Option 5/13 # optionDebugVerbose alts -vv # type: Boolean min 0 max 1 -vv) @@ -1473,7 +1457,7 @@ gitIsBranchCommandParse() { ;; - # Option 6/14 + # Option 6/13 # optionTraceVerbose alts -vvv # type: Boolean min 0 max 1 -vvv) @@ -1491,25 +1475,7 @@ gitIsBranchCommandParse() { ;; - # Option 7/14 - # optionEnvFiles alts --env-file - # type: StringArray min 0 max -1 - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - ;; - - # Option 8/14 + # Option 7/13 # optionLogLevel alts --log-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1537,7 +1503,7 @@ gitIsBranchCommandParse() { ;; - # Option 9/14 + # Option 8/13 # optionLogFile alts --log-file # type: String min 0 max 1 --log-file) @@ -1560,7 +1526,7 @@ gitIsBranchCommandParse() { ;; - # Option 10/14 + # Option 9/13 # optionDisplayLevel alts --display-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1588,7 +1554,7 @@ gitIsBranchCommandParse() { ;; - # Option 11/14 + # Option 10/13 # optionNoColor alts --no-color # type: Boolean min 0 max 1 --no-color) @@ -1606,7 +1572,7 @@ gitIsBranchCommandParse() { ;; - # Option 12/14 + # Option 11/13 # optionTheme alts --theme # type: String min 0 max 1 # authorizedValues: default|default-force|noColor @@ -1634,7 +1600,7 @@ gitIsBranchCommandParse() { ;; - # Option 13/14 + # Option 12/13 # optionVersion alts --version # type: Boolean min 0 max 1 --version) @@ -1650,7 +1616,7 @@ gitIsBranchCommandParse() { ;; - # Option 14/14 + # Option 13/13 # optionQuiet alts --quiet|-q # type: Boolean min 0 max 1 --quiet | -q) @@ -1729,7 +1695,6 @@ gitIsBranchCommandParse() { - if ((options_parse_argParsedCountBranchNameArg < 1 )); then Log::displayError "Command ${SCRIPT_NAME} - Argument 'branchName' should be provided at least 1 time(s)" return 1 @@ -1757,7 +1722,7 @@ gitIsBranchCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" + optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "gitIsBranch" "${optionsAltList[@]}" @@ -1814,12 +1779,6 @@ gitIsBranchCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" - echo - - - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo diff --git a/bin/gitRenameBranch b/bin/gitRenameBranch index cac3a882..ef6c4ee7 100755 --- a/bin/gitRenameBranch +++ b/bin/gitRenameBranch @@ -338,18 +338,79 @@ Bash::handlePipelineFailure() { } +read -r -d '\0' bashToolsDefaultConfigTemplate <<-EOM || true +# +# shellcheck disable=SC2034 + +# Default settings +# you can override these settings by creating ${HOME}/.bash-tools/.env file + +### +### DISPLAY Level +### minimum level of the messages that will be displayed on screen +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} + +### +### DISPLAY duration +### 0: no duration is displayed on the messages +### 1: duration between previous message and current is displayed +### with the message +### +DISPLAY_DURATION=${DISPLAY_DURATION:0} + +### +### Log to file +### +### all log messages will be redirected to log file specified +### this same path will be used inside and outside of the container +### +BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} + +### +### LOG Level +### minimum level of the messages that will be logged into LOG_FILE +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} + +# absolute directory containing db import sql dumps +DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} + +# garbage collect all files for which modification is greater than eg: 30 days (+30) +# each time an existing file is used by dbImport/dbImportTable +# the file modification time is set to now +DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} + +# absolute directory containing dbScripts used by dbScriptAllDatabases +SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} + +# ----------------------------------------------------- +# AWS Parameters +# ----------------------------------------------------- +S3_BASE_URL=${S3_BASE_URL:-} + +# ----------------------------------------------------- +# Postman Parameters +# ----------------------------------------------------- +POSTMAN_API_KEY= +EOM + # @description loads ~/.bash-tools/.env if available # if not creates it from a default template # else check if new options need to be added BashTools::Conf::requireLoad() { -Linux::requireTarCommand -Compiler::Embed::extractFileFromBase64 \ - "${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" \ - "IyEvdXNyL2Jpbi9lbnYgYmFzaAojIHNoZWxsY2hlY2sgZGlzYWJsZT1TQzIwMzQKCiMgRGVmYXVsdCBzZXR0aW5ncwojIHlvdSBjYW4gb3ZlcnJpZGUgdGhlc2Ugc2V0dGluZ3MgYnkgY3JlYXRpbmcgJHtIT01FfS8uYmFzaC10b29scy8uZW52IGZpbGUKCiMjIwojIyMgRElTUExBWSBMZXZlbAojIyMgbWluaW11bSBsZXZlbCBvZiB0aGUgbWVzc2FnZXMgdGhhdCB3aWxsIGJlIGRpc3BsYXllZCBvbiBzY3JlZW4KIyMjCiMjIyAwOiBOTyBMT0cKIyMjIDE6IEVSUk9SCiMjIyAyOiBXQVJOSU5HCiMjIyAzOiBJTkZPCiMjIyA0OiBERUJVRwojIyMKQkFTSF9GUkFNRVdPUktfRElTUExBWV9MRVZFTD0ke0JBU0hfRlJBTUVXT1JLX0RJU1BMQVlfTEVWRUw6LTN9CgojIyMKIyMjIERJU1BMQVkgZHVyYXRpb24KIyMjIDA6IG5vIGR1cmF0aW9uIGlzIGRpc3BsYXllZCBvbiB0aGUgbWVzc2FnZXMKIyMjIDE6IGR1cmF0aW9uIGJldHdlZW4gcHJldmlvdXMgbWVzc2FnZSBhbmQgY3VycmVudCBpcyBkaXNwbGF5ZWQKIyMjIHdpdGggdGhlIG1lc3NhZ2UKIyMjCkRJU1BMQVlfRFVSQVRJT049JHtESVNQTEFZX0RVUkFUSU9OOjB9CgojIyMKIyMjIExvZyB0byBmaWxlCiMjIwojIyMgYWxsIGxvZyBtZXNzYWdlcyB3aWxsIGJlIHJlZGlyZWN0ZWQgdG8gbG9nIGZpbGUgc3BlY2lmaWVkCiMjIyB0aGlzIHNhbWUgcGF0aCB3aWxsIGJlIHVzZWQgaW5zaWRlIGFuZCBvdXRzaWRlIG9mIHRoZSBjb250YWluZXIKIyMjCkJBU0hfRlJBTUVXT1JLX0xPR19GSUxFPSR7QkFTSF9GUkFNRVdPUktfTE9HX0ZJTEU6LSR7RlJBTUVXT1JLX1JPT1RfRElSfS9sb2dzL2Jhc2gubG9nfQoKIyMjCiMjIyBMT0cgTGV2ZWwKIyMjIG1pbmltdW0gbGV2ZWwgb2YgdGhlIG1lc3NhZ2VzIHRoYXQgd2lsbCBiZSBsb2dnZWQgaW50byBMT0dfRklMRQojIyMKIyMjIDA6IE5PIExPRwojIyMgMTogRVJST1IKIyMjIDI6IFdBUk5JTkcKIyMjIDM6IElORk8KIyMjIDQ6IERFQlVHCiMjIwpCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw9JHtCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw6LTB9CgojIGFic29sdXRlIGRpcmVjdG9yeSBjb250YWluaW5nIGRiIGltcG9ydCBzcWwgZHVtcHMKREJfSU1QT1JUX0RVTVBfRElSPSR7REJfSU1QT1JUX0RVTVBfRElSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2RiSW1wb3J0RHVtcHN9CgojIGdhcmJhZ2UgY29sbGVjdCBhbGwgZmlsZXMgZm9yIHdoaWNoIG1vZGlmaWNhdGlvbiBpcyBncmVhdGVyIHRoYW4gZWc6IDMwIGRheXMgKCszMCkKIyBlYWNoIHRpbWUgYW4gZXhpc3RpbmcgZmlsZSBpcyB1c2VkIGJ5IGRiSW1wb3J0L2RiSW1wb3J0VGFibGUKIyB0aGUgZmlsZSBtb2RpZmljYXRpb24gdGltZSBpcyBzZXQgdG8gbm93CkRCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUz0ke0RCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUzotKzMwfQoKIyBhYnNvbHV0ZSBkaXJlY3RvcnkgY29udGFpbmluZyBkYlNjcmlwdHMgdXNlZCBieSBkYlNjcmlwdEFsbERhdGFiYXNlcwpTQ1JJUFRTX0ZPTERFUj0ke1NDUklQVFNfRk9MREVSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2NvbmYvZGJTY3JpcHRzfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEFXUyBQYXJhbWV0ZXJzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KUzNfQkFTRV9VUkw9JHtTM19CQVNFX1VSTDotfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvc3RtYW4gUGFyYW1ldGVycwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClBPU1RNQU5fQVBJX0tFWT0K" \ - "755" - -declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" - BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" @@ -377,7 +438,7 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp ( echo "#!/usr/bin/env bash" # shellcheck disable=SC2154 - echo "${embed_file_bashToolsDefaultConfigTemplate}" + echo "${bashToolsDefaultConfigTemplate}" ) >"${envFile}" Log::displayInfo "Configuration file '${envFile}' created" else @@ -390,7 +451,7 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp ) >>"${envFile}" fi fi - # shellcheck source=/conf/.env + # shellcheck source=/conf/defaultEnv/.env source "${envFile}" || { Log::displayError "impossible to load '${envFile}'" exit 1 @@ -419,36 +480,6 @@ BashTools::runVerboseIfNeeded() { } -# @description convert base64 encoded back to target file -# if target file is executable prepend dir of target -# file to PATH to make binary available everywhere -# it is advised to include in the path of the target file -# the md5sum of the binFile -# -# @arg $1 targetFile:String the file to write -# @arg $2 binFileBase64:String the base64 encoded file -# @arg $3 fileMode:String the chmod to set on the file -# @set PATH String prepend target embedded file binary directory to PATH variable if binary executable -Compiler::Embed::extractFileFromBase64() { - local targetFile="$1" - local binFileBase64="$2" - local fileMode="${3:-+x}" - local targetDir="${targetFile%/*}" - - if [[ ! -f "${targetFile}" ]]; then - if [[ ! -d "${targetDir}" ]]; then - mkdir -p "${targetDir}" - fi - base64 -d >"${targetFile}" <<<"${binFileBase64}" - chmod "${fileMode}" "${targetFile}" - fi - - if [[ -x "${targetFile}" ]]; then - Env::pathPrepend "${targetDir}" - fi -} - - # @description check if all requirements are satisfied # to execute dbImport commands Db::checkRequirements() { @@ -468,19 +499,6 @@ Db::checkRequirements() { } -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" - fi - done -} - - # @description ensure env files are loaded # @arg $@ list of default files to load at the end # @exitcode 1 if one of env files fails to load @@ -535,14 +553,6 @@ Linux::requireExecutedAsUser() { } -# @description ensure command tar is available -# @exitcode 1 if tar command not available -# @stderr diagnostics information is displayed -Linux::requireTarCommand() { - Assert::commandExists tar -} - - declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" @@ -622,19 +632,6 @@ Log::displayInfo() { } -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logWarning "$1" -} - - # @description Display message using error color (red) and exit immediately with error status 1 # @arg $1 message:String the message to display # @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs @@ -718,15 +715,6 @@ Log::logMessage() { } -# @description log message to file -# @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" - fi -} - - # @description activate or not Log::display* and Log::log* functions # based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL # environment variables loaded by Env::requireLoad @@ -1045,6 +1033,7 @@ beforeParseCallback() { copyrightCallback() { # + # shellcheck disable=SC2155,SC2154,SC2250 echo "Copyright (c) 2020-now François Chastanet" } @@ -1088,16 +1077,6 @@ optionVersionCallback() { exit 0 } -# shellcheck disable=SC2317 # if function is overridden -optionEnvFileCallback() { - local envFile="$2" - Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" - if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" - exit 1 - fi -} - # shellcheck disable=SC2317 # if function is overridden optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' @@ -1272,9 +1251,6 @@ commandOptionParseFinished() { defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi # shellcheck disable=SC2154 if [[ -f "${optionBashFrameworkConfig}" ]]; then files+=("${optionBashFrameworkConfig}") @@ -1290,14 +1266,24 @@ commandOptionParseFinished() { -beforeParseCallback() { - Linux::requireExecutedAsUser +defaultBeforeParseCallback() { BashTools::Conf::requireLoad Env::requireLoad UI::requireTheme Log::requireLoad } +beforeParseCallback() { + defaultBeforeParseCallback +} + + + +beforeParseCallback() { + defaultBeforeParseCallback + Linux::requireExecutedAsUser +} + declare optionRedirectCmdOutputs="" optionRedirectCmdOutputs() { export optionTraceVerbose @@ -1366,7 +1352,6 @@ declare optionBashFrameworkConfig="" declare optionInfoVerbose="0" declare optionDebugVerbose="0" declare optionTraceVerbose="0" -declare -a optionEnvFiles=() declare optionLogLevel="" declare optionLogFile="" declare optionDisplayLevel="" @@ -1402,7 +1387,6 @@ gitRenameBranchCommandParse() { optionTraceVerbose="0" local -i options_parse_optionParsedCountOptionTraceVerbose ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionLogLevel="" local -i options_parse_optionParsedCountOptionLogLevel ((options_parse_optionParsedCountOptionLogLevel = 0)) || true @@ -1449,7 +1433,7 @@ gitRenameBranchCommandParse() { local options_parse_arg="$1" local argOptDefaultBehavior=0 case "${options_parse_arg}" in - # Option 1/17 + # Option 1/16 # optionHelp alts --help|-h # type: Boolean min 0 max 1 --help | -h) @@ -1465,7 +1449,7 @@ gitRenameBranchCommandParse() { ;; - # Option 2/17 + # Option 2/16 # optionConfig alts --config # type: Boolean min 0 max 1 --config) @@ -1479,7 +1463,7 @@ gitRenameBranchCommandParse() { ((++options_parse_optionParsedCountOptionConfig)) ;; - # Option 3/17 + # Option 3/16 # optionBashFrameworkConfig alts --bash-framework-config # type: String min 0 max 1 --bash-framework-config) @@ -1500,7 +1484,7 @@ gitRenameBranchCommandParse() { ;; - # Option 4/17 + # Option 4/16 # optionInfoVerbose alts --verbose|-v # type: Boolean min 0 max 1 --verbose | -v) @@ -1518,7 +1502,7 @@ gitRenameBranchCommandParse() { ;; - # Option 5/17 + # Option 5/16 # optionDebugVerbose alts -vv # type: Boolean min 0 max 1 -vv) @@ -1536,7 +1520,7 @@ gitRenameBranchCommandParse() { ;; - # Option 6/17 + # Option 6/16 # optionTraceVerbose alts -vvv # type: Boolean min 0 max 1 -vvv) @@ -1554,25 +1538,7 @@ gitRenameBranchCommandParse() { ;; - # Option 7/17 - # optionEnvFiles alts --env-file - # type: StringArray min 0 max -1 - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - ;; - - # Option 8/17 + # Option 7/16 # optionLogLevel alts --log-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1600,7 +1566,7 @@ gitRenameBranchCommandParse() { ;; - # Option 9/17 + # Option 8/16 # optionLogFile alts --log-file # type: String min 0 max 1 --log-file) @@ -1623,7 +1589,7 @@ gitRenameBranchCommandParse() { ;; - # Option 10/17 + # Option 9/16 # optionDisplayLevel alts --display-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1651,7 +1617,7 @@ gitRenameBranchCommandParse() { ;; - # Option 11/17 + # Option 10/16 # optionNoColor alts --no-color # type: Boolean min 0 max 1 --no-color) @@ -1669,7 +1635,7 @@ gitRenameBranchCommandParse() { ;; - # Option 12/17 + # Option 11/16 # optionTheme alts --theme # type: String min 0 max 1 # authorizedValues: default|default-force|noColor @@ -1697,7 +1663,7 @@ gitRenameBranchCommandParse() { ;; - # Option 13/17 + # Option 12/16 # optionVersion alts --version # type: Boolean min 0 max 1 --version) @@ -1713,7 +1679,7 @@ gitRenameBranchCommandParse() { ;; - # Option 14/17 + # Option 13/16 # optionQuiet alts --quiet|-q # type: Boolean min 0 max 1 --quiet | -q) @@ -1731,7 +1697,7 @@ gitRenameBranchCommandParse() { ;; - # Option 15/17 + # Option 14/16 # optionAssumeYes alts --assume-yes|--yes|-y # type: Boolean min 0 max 1 --assume-yes | --yes | -y) @@ -1745,7 +1711,7 @@ gitRenameBranchCommandParse() { ((++options_parse_optionParsedCountOptionAssumeYes)) ;; - # Option 16/17 + # Option 15/16 # optionPush alts --push|-p # type: Boolean min 0 max 1 --push | -p) @@ -1759,7 +1725,7 @@ gitRenameBranchCommandParse() { ((++options_parse_optionParsedCountOptionPush)) ;; - # Option 17/17 + # Option 16/16 # optionDelete alts --delete|-d # type: Boolean min 0 max 1 --delete | -d) @@ -1853,7 +1819,6 @@ gitRenameBranchCommandParse() { - if ((options_parse_argParsedCountNewBranchNameArg < 1 )); then Log::displayError "Command ${SCRIPT_NAME} - Argument 'newBranchName' should be provided at least 1 time(s)" return 1 @@ -1883,7 +1848,7 @@ gitRenameBranchCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--assume-yes|--yes|-y]" "[--push|-p]" "[--delete|-d]" + optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--assume-yes|--yes|-y]" "[--push|-p]" "[--delete|-d]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "gitRenameBranch" "${optionsAltList[@]}" @@ -1944,12 +1909,6 @@ gitRenameBranchCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" - echo - - - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo diff --git a/bin/installRequirements b/bin/installRequirements index 2f74fdae..6452910f 100755 --- a/bin/installRequirements +++ b/bin/installRequirements @@ -307,6 +307,127 @@ Assert::tty() { } +read -r -d '\0' bashToolsDefaultConfigTemplate <<-EOM || true +# +# shellcheck disable=SC2034 + +# Default settings +# you can override these settings by creating ${HOME}/.bash-tools/.env file + +### +### DISPLAY Level +### minimum level of the messages that will be displayed on screen +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} + +### +### DISPLAY duration +### 0: no duration is displayed on the messages +### 1: duration between previous message and current is displayed +### with the message +### +DISPLAY_DURATION=${DISPLAY_DURATION:0} + +### +### Log to file +### +### all log messages will be redirected to log file specified +### this same path will be used inside and outside of the container +### +BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} + +### +### LOG Level +### minimum level of the messages that will be logged into LOG_FILE +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} + +# absolute directory containing db import sql dumps +DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} + +# garbage collect all files for which modification is greater than eg: 30 days (+30) +# each time an existing file is used by dbImport/dbImportTable +# the file modification time is set to now +DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} + +# absolute directory containing dbScripts used by dbScriptAllDatabases +SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} + +# ----------------------------------------------------- +# AWS Parameters +# ----------------------------------------------------- +S3_BASE_URL=${S3_BASE_URL:-} + +# ----------------------------------------------------- +# Postman Parameters +# ----------------------------------------------------- +POSTMAN_API_KEY= +EOM + +# @description loads ~/.bash-tools/.env if available +# if not creates it from a default template +# else check if new options need to be added +BashTools::Conf::requireLoad() { + BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" + if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then + FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" + else + # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR + FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" + fi + # shellcheck disable=SC2034 + FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" + # shellcheck disable=SC2034 + FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + + if [[ -f "${HOME}/.bash-tools/.env" ]]; then + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") + fi + + local envFile="${HOME}/.bash-tools/.env" + if [[ ! -f "${envFile}" ]]; then + mkdir -p "${HOME}/.bash-tools" + ( + echo "#!/usr/bin/env bash" + # shellcheck disable=SC2154 + echo "${bashToolsDefaultConfigTemplate}" + ) >"${envFile}" + Log::displayInfo "Configuration file '${envFile}' created" + else + if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then + ( + echo '# -----------------------------------------------------' + echo '# Postman Parameters' + echo '# -----------------------------------------------------' + echo 'POSTMAN_API_KEY=' + ) >>"${envFile}" + fi + fi + # shellcheck source=/conf/defaultEnv/.env + source "${envFile}" || { + Log::displayError "impossible to load '${envFile}'" + exit 1 + } +} + + # @description ensure env files are loaded # @arg $@ list of default files to load at the end # @exitcode 1 if one of env files fails to load @@ -849,6 +970,7 @@ beforeParseCallback() { copyrightCallback() { # + # shellcheck disable=SC2155,SC2154,SC2250 echo "Copyright (c) 2022-now François Chastanet" } @@ -892,16 +1014,6 @@ optionVersionCallback() { exit 0 } -# shellcheck disable=SC2317 # if function is overridden -optionEnvFileCallback() { - local envFile="$2" - Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" - if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" - exit 1 - fi -} - # shellcheck disable=SC2317 # if function is overridden optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' @@ -1076,9 +1188,6 @@ commandOptionParseFinished() { defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi # shellcheck disable=SC2154 if [[ -f "${optionBashFrameworkConfig}" ]]; then files+=("${optionBashFrameworkConfig}") @@ -1094,6 +1203,19 @@ commandOptionParseFinished() { +defaultBeforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + +beforeParseCallback() { + defaultBeforeParseCallback +} + + + # shellcheck disable=SC2034 declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" @@ -1114,7 +1236,6 @@ declare optionBashFrameworkConfig="" declare optionInfoVerbose="0" declare optionDebugVerbose="0" declare optionTraceVerbose="0" -declare -a optionEnvFiles=() declare optionLogLevel="" declare optionLogFile="" declare optionDisplayLevel="" @@ -1145,7 +1266,6 @@ installRequirementsCommandParse() { optionTraceVerbose="0" local -i options_parse_optionParsedCountOptionTraceVerbose ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionLogLevel="" local -i options_parse_optionParsedCountOptionLogLevel ((options_parse_optionParsedCountOptionLogLevel = 0)) || true @@ -1175,7 +1295,7 @@ installRequirementsCommandParse() { local options_parse_arg="$1" local argOptDefaultBehavior=0 case "${options_parse_arg}" in - # Option 1/14 + # Option 1/13 # optionHelp alts --help|-h # type: Boolean min 0 max 1 --help | -h) @@ -1191,7 +1311,7 @@ installRequirementsCommandParse() { ;; - # Option 2/14 + # Option 2/13 # optionConfig alts --config # type: Boolean min 0 max 1 --config) @@ -1205,7 +1325,7 @@ installRequirementsCommandParse() { ((++options_parse_optionParsedCountOptionConfig)) ;; - # Option 3/14 + # Option 3/13 # optionBashFrameworkConfig alts --bash-framework-config # type: String min 0 max 1 --bash-framework-config) @@ -1226,7 +1346,7 @@ installRequirementsCommandParse() { ;; - # Option 4/14 + # Option 4/13 # optionInfoVerbose alts --verbose|-v # type: Boolean min 0 max 1 --verbose | -v) @@ -1244,7 +1364,7 @@ installRequirementsCommandParse() { ;; - # Option 5/14 + # Option 5/13 # optionDebugVerbose alts -vv # type: Boolean min 0 max 1 -vv) @@ -1262,7 +1382,7 @@ installRequirementsCommandParse() { ;; - # Option 6/14 + # Option 6/13 # optionTraceVerbose alts -vvv # type: Boolean min 0 max 1 -vvv) @@ -1280,25 +1400,7 @@ installRequirementsCommandParse() { ;; - # Option 7/14 - # optionEnvFiles alts --env-file - # type: StringArray min 0 max -1 - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - ;; - - # Option 8/14 + # Option 7/13 # optionLogLevel alts --log-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1326,7 +1428,7 @@ installRequirementsCommandParse() { ;; - # Option 9/14 + # Option 8/13 # optionLogFile alts --log-file # type: String min 0 max 1 --log-file) @@ -1349,7 +1451,7 @@ installRequirementsCommandParse() { ;; - # Option 10/14 + # Option 9/13 # optionDisplayLevel alts --display-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1377,7 +1479,7 @@ installRequirementsCommandParse() { ;; - # Option 11/14 + # Option 10/13 # optionNoColor alts --no-color # type: Boolean min 0 max 1 --no-color) @@ -1395,7 +1497,7 @@ installRequirementsCommandParse() { ;; - # Option 12/14 + # Option 11/13 # optionTheme alts --theme # type: String min 0 max 1 # authorizedValues: default|default-force|noColor @@ -1423,7 +1525,7 @@ installRequirementsCommandParse() { ;; - # Option 13/14 + # Option 12/13 # optionVersion alts --version # type: Boolean min 0 max 1 --version) @@ -1439,7 +1541,7 @@ installRequirementsCommandParse() { ;; - # Option 14/14 + # Option 13/13 # optionQuiet alts --quiet|-q # type: Boolean min 0 max 1 --quiet | -q) @@ -1498,7 +1600,7 @@ installRequirementsCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" + optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "installRequirements" "${optionsAltList[@]}" @@ -1545,12 +1647,6 @@ installRequirementsCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" - echo - - - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo diff --git a/bin/mysql2puml b/bin/mysql2puml index 788b9681..79e5830d 100755 --- a/bin/mysql2puml +++ b/bin/mysql2puml @@ -324,6 +324,127 @@ Assert::tty() { } +read -r -d '\0' bashToolsDefaultConfigTemplate <<-EOM || true +# +# shellcheck disable=SC2034 + +# Default settings +# you can override these settings by creating ${HOME}/.bash-tools/.env file + +### +### DISPLAY Level +### minimum level of the messages that will be displayed on screen +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} + +### +### DISPLAY duration +### 0: no duration is displayed on the messages +### 1: duration between previous message and current is displayed +### with the message +### +DISPLAY_DURATION=${DISPLAY_DURATION:0} + +### +### Log to file +### +### all log messages will be redirected to log file specified +### this same path will be used inside and outside of the container +### +BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} + +### +### LOG Level +### minimum level of the messages that will be logged into LOG_FILE +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} + +# absolute directory containing db import sql dumps +DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} + +# garbage collect all files for which modification is greater than eg: 30 days (+30) +# each time an existing file is used by dbImport/dbImportTable +# the file modification time is set to now +DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} + +# absolute directory containing dbScripts used by dbScriptAllDatabases +SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} + +# ----------------------------------------------------- +# AWS Parameters +# ----------------------------------------------------- +S3_BASE_URL=${S3_BASE_URL:-} + +# ----------------------------------------------------- +# Postman Parameters +# ----------------------------------------------------- +POSTMAN_API_KEY= +EOM + +# @description loads ~/.bash-tools/.env if available +# if not creates it from a default template +# else check if new options need to be added +BashTools::Conf::requireLoad() { + BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" + if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then + FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" + else + # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR + FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" + fi + # shellcheck disable=SC2034 + FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" + # shellcheck disable=SC2034 + FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + + if [[ -f "${HOME}/.bash-tools/.env" ]]; then + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") + fi + + local envFile="${HOME}/.bash-tools/.env" + if [[ ! -f "${envFile}" ]]; then + mkdir -p "${HOME}/.bash-tools" + ( + echo "#!/usr/bin/env bash" + # shellcheck disable=SC2154 + echo "${bashToolsDefaultConfigTemplate}" + ) >"${envFile}" + Log::displayInfo "Configuration file '${envFile}' created" + else + if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then + ( + echo '# -----------------------------------------------------' + echo '# Postman Parameters' + echo '# -----------------------------------------------------' + echo 'POSTMAN_API_KEY=' + ) >>"${envFile}" + fi + fi + # shellcheck source=/conf/defaultEnv/.env + source "${envFile}" || { + Log::displayError "impossible to load '${envFile}'" + exit 1 + } +} + + # @description convert base64 encoded back to target file # if target file is executable prepend dir of target # file to PATH to make binary available everywhere @@ -672,19 +793,6 @@ Log::displayInfo() { } -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logWarning "$1" -} - - # @description Display message using error color (red) and exit immediately with error status 1 # @arg $1 message:String the message to display # @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs @@ -768,15 +876,6 @@ Log::logMessage() { } -# @description log message to file -# @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" - fi -} - - # @description activate or not Log::display* and Log::log* functions # based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL # environment variables loaded by Env::requireLoad @@ -980,6 +1079,7 @@ beforeParseCallback() { copyrightCallback() { # + # shellcheck disable=SC2155,SC2154,SC2250 echo "Copyright (c) 2021-now François Chastanet" } @@ -1023,16 +1123,6 @@ optionVersionCallback() { exit 0 } -# shellcheck disable=SC2317 # if function is overridden -optionEnvFileCallback() { - local envFile="$2" - Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" - if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" - exit 1 - fi -} - # shellcheck disable=SC2317 # if function is overridden optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' @@ -1207,9 +1297,6 @@ commandOptionParseFinished() { defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi # shellcheck disable=SC2154 if [[ -f "${optionBashFrameworkConfig}" ]]; then files+=("${optionBashFrameworkConfig}") @@ -1225,6 +1312,19 @@ commandOptionParseFinished() { +defaultBeforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + +beforeParseCallback() { + defaultBeforeParseCallback +} + + + # shellcheck disable=SC2034 declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" @@ -1289,7 +1389,6 @@ declare optionBashFrameworkConfig="" declare optionInfoVerbose="0" declare optionDebugVerbose="0" declare optionTraceVerbose="0" -declare -a optionEnvFiles=() declare optionLogLevel="" declare optionLogFile="" declare optionDisplayLevel="" @@ -1322,7 +1421,6 @@ mysql2pumlCommandParse() { optionTraceVerbose="0" local -i options_parse_optionParsedCountOptionTraceVerbose ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionLogLevel="" local -i options_parse_optionParsedCountOptionLogLevel ((options_parse_optionParsedCountOptionLogLevel = 0)) || true @@ -1359,7 +1457,7 @@ mysql2pumlCommandParse() { local options_parse_arg="$1" local argOptDefaultBehavior=0 case "${options_parse_arg}" in - # Option 1/15 + # Option 1/14 # optionHelp alts --help|-h # type: Boolean min 0 max 1 --help | -h) @@ -1375,7 +1473,7 @@ mysql2pumlCommandParse() { ;; - # Option 2/15 + # Option 2/14 # optionConfig alts --config # type: Boolean min 0 max 1 --config) @@ -1389,7 +1487,7 @@ mysql2pumlCommandParse() { ((++options_parse_optionParsedCountOptionConfig)) ;; - # Option 3/15 + # Option 3/14 # optionBashFrameworkConfig alts --bash-framework-config # type: String min 0 max 1 --bash-framework-config) @@ -1410,7 +1508,7 @@ mysql2pumlCommandParse() { ;; - # Option 4/15 + # Option 4/14 # optionInfoVerbose alts --verbose|-v # type: Boolean min 0 max 1 --verbose | -v) @@ -1428,7 +1526,7 @@ mysql2pumlCommandParse() { ;; - # Option 5/15 + # Option 5/14 # optionDebugVerbose alts -vv # type: Boolean min 0 max 1 -vv) @@ -1446,7 +1544,7 @@ mysql2pumlCommandParse() { ;; - # Option 6/15 + # Option 6/14 # optionTraceVerbose alts -vvv # type: Boolean min 0 max 1 -vvv) @@ -1464,25 +1562,7 @@ mysql2pumlCommandParse() { ;; - # Option 7/15 - # optionEnvFiles alts --env-file - # type: StringArray min 0 max -1 - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - ;; - - # Option 8/15 + # Option 7/14 # optionLogLevel alts --log-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1510,7 +1590,7 @@ mysql2pumlCommandParse() { ;; - # Option 9/15 + # Option 8/14 # optionLogFile alts --log-file # type: String min 0 max 1 --log-file) @@ -1533,7 +1613,7 @@ mysql2pumlCommandParse() { ;; - # Option 10/15 + # Option 9/14 # optionDisplayLevel alts --display-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1561,7 +1641,7 @@ mysql2pumlCommandParse() { ;; - # Option 11/15 + # Option 10/14 # optionNoColor alts --no-color # type: Boolean min 0 max 1 --no-color) @@ -1579,7 +1659,7 @@ mysql2pumlCommandParse() { ;; - # Option 12/15 + # Option 11/14 # optionTheme alts --theme # type: String min 0 max 1 # authorizedValues: default|default-force|noColor @@ -1607,7 +1687,7 @@ mysql2pumlCommandParse() { ;; - # Option 13/15 + # Option 12/14 # optionVersion alts --version # type: Boolean min 0 max 1 --version) @@ -1623,7 +1703,7 @@ mysql2pumlCommandParse() { ;; - # Option 14/15 + # Option 13/14 # optionQuiet alts --quiet|-q # type: Boolean min 0 max 1 --quiet | -q) @@ -1641,7 +1721,7 @@ mysql2pumlCommandParse() { ;; - # Option 15/15 + # Option 14/14 # optionSkin alts --skin # type: String min 0 max 1 --skin) @@ -1732,7 +1812,7 @@ mysql2pumlCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--skin ]" + optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--skin ]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "mysql2puml" "${optionsAltList[@]}" @@ -1789,12 +1869,6 @@ mysql2pumlCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" - echo - - - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo diff --git a/bin/postmanCli b/bin/postmanCli index f51d1323..2eebe51d 100755 --- a/bin/postmanCli +++ b/bin/postmanCli @@ -355,6 +355,127 @@ Bash::handlePipelineFailure() { } +read -r -d '\0' bashToolsDefaultConfigTemplate <<-EOM || true +# +# shellcheck disable=SC2034 + +# Default settings +# you can override these settings by creating ${HOME}/.bash-tools/.env file + +### +### DISPLAY Level +### minimum level of the messages that will be displayed on screen +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} + +### +### DISPLAY duration +### 0: no duration is displayed on the messages +### 1: duration between previous message and current is displayed +### with the message +### +DISPLAY_DURATION=${DISPLAY_DURATION:0} + +### +### Log to file +### +### all log messages will be redirected to log file specified +### this same path will be used inside and outside of the container +### +BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} + +### +### LOG Level +### minimum level of the messages that will be logged into LOG_FILE +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} + +# absolute directory containing db import sql dumps +DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} + +# garbage collect all files for which modification is greater than eg: 30 days (+30) +# each time an existing file is used by dbImport/dbImportTable +# the file modification time is set to now +DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} + +# absolute directory containing dbScripts used by dbScriptAllDatabases +SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} + +# ----------------------------------------------------- +# AWS Parameters +# ----------------------------------------------------- +S3_BASE_URL=${S3_BASE_URL:-} + +# ----------------------------------------------------- +# Postman Parameters +# ----------------------------------------------------- +POSTMAN_API_KEY= +EOM + +# @description loads ~/.bash-tools/.env if available +# if not creates it from a default template +# else check if new options need to be added +BashTools::Conf::requireLoad() { + BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" + if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then + FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" + else + # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR + FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" + fi + # shellcheck disable=SC2034 + FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" + # shellcheck disable=SC2034 + FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + + if [[ -f "${HOME}/.bash-tools/.env" ]]; then + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") + fi + + local envFile="${HOME}/.bash-tools/.env" + if [[ ! -f "${envFile}" ]]; then + mkdir -p "${HOME}/.bash-tools" + ( + echo "#!/usr/bin/env bash" + # shellcheck disable=SC2154 + echo "${bashToolsDefaultConfigTemplate}" + ) >"${envFile}" + Log::displayInfo "Configuration file '${envFile}' created" + else + if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then + ( + echo '# -----------------------------------------------------' + echo '# Postman Parameters' + echo '# -----------------------------------------------------' + echo 'POSTMAN_API_KEY=' + ) >>"${envFile}" + fi + fi + # shellcheck source=/conf/defaultEnv/.env + source "${envFile}" || { + Log::displayError "impossible to load '${envFile}'" + exit 1 + } +} + + # @description check if all requirements are satisfied # to execute dbImport commands Db::checkRequirements() { @@ -1488,6 +1609,7 @@ beforeParseCallback() { copyrightCallback() { # + # shellcheck disable=SC2155,SC2154,SC2250 echo "Copyright (c) 2020-now François Chastanet" } @@ -1531,16 +1653,6 @@ optionVersionCallback() { exit 0 } -# shellcheck disable=SC2317 # if function is overridden -optionEnvFileCallback() { - local envFile="$2" - Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" - if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" - exit 1 - fi -} - # shellcheck disable=SC2317 # if function is overridden optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' @@ -1715,9 +1827,6 @@ commandOptionParseFinished() { defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi # shellcheck disable=SC2154 if [[ -f "${optionBashFrameworkConfig}" ]]; then files+=("${optionBashFrameworkConfig}") @@ -1733,6 +1842,19 @@ commandOptionParseFinished() { +defaultBeforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + +beforeParseCallback() { + defaultBeforeParseCallback +} + + + longDescriptionFunction() { echo -e " ${__HELP_TITLE}EXIT CODES:${__HELP_NORMAL}" echo -e " ${__HELP_OPTION_COLOR}1${__HELP_NORMAL}: if commit does not exists" @@ -1812,7 +1934,6 @@ declare optionBashFrameworkConfig="" declare optionInfoVerbose="0" declare optionDebugVerbose="0" declare optionTraceVerbose="0" -declare -a optionEnvFiles=() declare optionLogLevel="" declare optionLogFile="" declare optionDisplayLevel="" @@ -1846,7 +1967,6 @@ postmanCliCommandParse() { optionTraceVerbose="0" local -i options_parse_optionParsedCountOptionTraceVerbose ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionLogLevel="" local -i options_parse_optionParsedCountOptionLogLevel ((options_parse_optionParsedCountOptionLogLevel = 0)) || true @@ -1885,7 +2005,7 @@ postmanCliCommandParse() { local options_parse_arg="$1" local argOptDefaultBehavior=0 case "${options_parse_arg}" in - # Option 1/15 + # Option 1/14 # optionHelp alts --help|-h # type: Boolean min 0 max 1 --help | -h) @@ -1901,7 +2021,7 @@ postmanCliCommandParse() { ;; - # Option 2/15 + # Option 2/14 # optionConfig alts --config # type: Boolean min 0 max 1 --config) @@ -1915,7 +2035,7 @@ postmanCliCommandParse() { ((++options_parse_optionParsedCountOptionConfig)) ;; - # Option 3/15 + # Option 3/14 # optionBashFrameworkConfig alts --bash-framework-config # type: String min 0 max 1 --bash-framework-config) @@ -1936,7 +2056,7 @@ postmanCliCommandParse() { ;; - # Option 4/15 + # Option 4/14 # optionInfoVerbose alts --verbose|-v # type: Boolean min 0 max 1 --verbose | -v) @@ -1954,7 +2074,7 @@ postmanCliCommandParse() { ;; - # Option 5/15 + # Option 5/14 # optionDebugVerbose alts -vv # type: Boolean min 0 max 1 -vv) @@ -1972,7 +2092,7 @@ postmanCliCommandParse() { ;; - # Option 6/15 + # Option 6/14 # optionTraceVerbose alts -vvv # type: Boolean min 0 max 1 -vvv) @@ -1990,25 +2110,7 @@ postmanCliCommandParse() { ;; - # Option 7/15 - # optionEnvFiles alts --env-file - # type: StringArray min 0 max -1 - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - ;; - - # Option 8/15 + # Option 7/14 # optionLogLevel alts --log-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -2036,7 +2138,7 @@ postmanCliCommandParse() { ;; - # Option 9/15 + # Option 8/14 # optionLogFile alts --log-file # type: String min 0 max 1 --log-file) @@ -2059,7 +2161,7 @@ postmanCliCommandParse() { ;; - # Option 10/15 + # Option 9/14 # optionDisplayLevel alts --display-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -2087,7 +2189,7 @@ postmanCliCommandParse() { ;; - # Option 11/15 + # Option 10/14 # optionNoColor alts --no-color # type: Boolean min 0 max 1 --no-color) @@ -2105,7 +2207,7 @@ postmanCliCommandParse() { ;; - # Option 12/15 + # Option 11/14 # optionTheme alts --theme # type: String min 0 max 1 # authorizedValues: default|default-force|noColor @@ -2133,7 +2235,7 @@ postmanCliCommandParse() { ;; - # Option 13/15 + # Option 12/14 # optionVersion alts --version # type: Boolean min 0 max 1 --version) @@ -2149,7 +2251,7 @@ postmanCliCommandParse() { ;; - # Option 14/15 + # Option 13/14 # optionQuiet alts --quiet|-q # type: Boolean min 0 max 1 --quiet | -q) @@ -2167,7 +2269,7 @@ postmanCliCommandParse() { ;; - # Option 15/15 + # Option 14/14 # optionPostmanModelConfig alts --postman-model|-m # type: String min 0 max 1 --postman-model | -m) @@ -2264,7 +2366,7 @@ postmanCliCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--postman-model|-m ]" + optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--postman-model|-m ]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "postmanCli" "${optionsAltList[@]}" @@ -2325,12 +2427,6 @@ postmanCliCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" - echo - - - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo diff --git a/bin/upgradeGithubRelease b/bin/upgradeGithubRelease index be737dbe..5c62a189 100755 --- a/bin/upgradeGithubRelease +++ b/bin/upgradeGithubRelease @@ -380,6 +380,127 @@ Bash::handlePipelineFailure() { } +read -r -d '\0' bashToolsDefaultConfigTemplate <<-EOM || true +# +# shellcheck disable=SC2034 + +# Default settings +# you can override these settings by creating ${HOME}/.bash-tools/.env file + +### +### DISPLAY Level +### minimum level of the messages that will be displayed on screen +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} + +### +### DISPLAY duration +### 0: no duration is displayed on the messages +### 1: duration between previous message and current is displayed +### with the message +### +DISPLAY_DURATION=${DISPLAY_DURATION:0} + +### +### Log to file +### +### all log messages will be redirected to log file specified +### this same path will be used inside and outside of the container +### +BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} + +### +### LOG Level +### minimum level of the messages that will be logged into LOG_FILE +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} + +# absolute directory containing db import sql dumps +DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} + +# garbage collect all files for which modification is greater than eg: 30 days (+30) +# each time an existing file is used by dbImport/dbImportTable +# the file modification time is set to now +DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} + +# absolute directory containing dbScripts used by dbScriptAllDatabases +SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} + +# ----------------------------------------------------- +# AWS Parameters +# ----------------------------------------------------- +S3_BASE_URL=${S3_BASE_URL:-} + +# ----------------------------------------------------- +# Postman Parameters +# ----------------------------------------------------- +POSTMAN_API_KEY= +EOM + +# @description loads ~/.bash-tools/.env if available +# if not creates it from a default template +# else check if new options need to be added +BashTools::Conf::requireLoad() { + BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" + if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then + FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" + else + # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR + FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" + fi + # shellcheck disable=SC2034 + FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" + # shellcheck disable=SC2034 + FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + + if [[ -f "${HOME}/.bash-tools/.env" ]]; then + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") + fi + + local envFile="${HOME}/.bash-tools/.env" + if [[ ! -f "${envFile}" ]]; then + mkdir -p "${HOME}/.bash-tools" + ( + echo "#!/usr/bin/env bash" + # shellcheck disable=SC2154 + echo "${bashToolsDefaultConfigTemplate}" + ) >"${envFile}" + Log::displayInfo "Configuration file '${envFile}' created" + else + if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then + ( + echo '# -----------------------------------------------------' + echo '# Postman Parameters' + echo '# -----------------------------------------------------' + echo 'POSTMAN_API_KEY=' + ) >>"${envFile}" + fi + fi + # shellcheck source=/conf/defaultEnv/.env + source "${envFile}" || { + Log::displayError "impossible to load '${envFile}'" + exit 1 + } +} + + # @description check if all requirements are satisfied # to execute dbImport commands Db::checkRequirements() { @@ -662,19 +783,6 @@ Log::displaySuccess() { } -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logWarning "$1" -} - - # @description Display message using error color (red) and exit immediately with error status 1 # @arg $1 message:String the message to display # @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs @@ -767,15 +875,6 @@ Log::logSuccess() { } -# @description log message to file -# @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" - fi -} - - # @description activate or not Log::display* and Log::log* functions # based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL # environment variables loaded by Env::requireLoad @@ -1228,6 +1327,7 @@ beforeParseCallback() { copyrightCallback() { # + # shellcheck disable=SC2155,SC2154,SC2250 echo "Copyright (c) 2020-now François Chastanet" } @@ -1271,16 +1371,6 @@ optionVersionCallback() { exit 0 } -# shellcheck disable=SC2317 # if function is overridden -optionEnvFileCallback() { - local envFile="$2" - Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" - if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" - exit 1 - fi -} - # shellcheck disable=SC2317 # if function is overridden optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' @@ -1455,9 +1545,6 @@ commandOptionParseFinished() { defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi # shellcheck disable=SC2154 if [[ -f "${optionBashFrameworkConfig}" ]]; then files+=("${optionBashFrameworkConfig}") @@ -1473,6 +1560,19 @@ commandOptionParseFinished() { +defaultBeforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + +beforeParseCallback() { + defaultBeforeParseCallback +} + + + specificRequirements() { Linux::requireJqCommand } @@ -1582,7 +1682,6 @@ declare optionBashFrameworkConfig="" declare optionInfoVerbose="0" declare optionDebugVerbose="0" declare optionTraceVerbose="0" -declare -a optionEnvFiles=() declare optionLogLevel="" declare optionLogFile="" declare optionDisplayLevel="" @@ -1618,7 +1717,6 @@ upgradeGithubReleaseCommandParse() { optionTraceVerbose="0" local -i options_parse_optionParsedCountOptionTraceVerbose ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionLogLevel="" local -i options_parse_optionParsedCountOptionLogLevel ((options_parse_optionParsedCountOptionLogLevel = 0)) || true @@ -1665,7 +1763,7 @@ upgradeGithubReleaseCommandParse() { local options_parse_arg="$1" local argOptDefaultBehavior=0 case "${options_parse_arg}" in - # Option 1/17 + # Option 1/16 # optionHelp alts --help|-h # type: Boolean min 0 max 1 --help | -h) @@ -1681,7 +1779,7 @@ upgradeGithubReleaseCommandParse() { ;; - # Option 2/17 + # Option 2/16 # optionConfig alts --config # type: Boolean min 0 max 1 --config) @@ -1695,7 +1793,7 @@ upgradeGithubReleaseCommandParse() { ((++options_parse_optionParsedCountOptionConfig)) ;; - # Option 3/17 + # Option 3/16 # optionBashFrameworkConfig alts --bash-framework-config # type: String min 0 max 1 --bash-framework-config) @@ -1716,7 +1814,7 @@ upgradeGithubReleaseCommandParse() { ;; - # Option 4/17 + # Option 4/16 # optionInfoVerbose alts --verbose|-v # type: Boolean min 0 max 1 --verbose | -v) @@ -1734,7 +1832,7 @@ upgradeGithubReleaseCommandParse() { ;; - # Option 5/17 + # Option 5/16 # optionDebugVerbose alts -vv # type: Boolean min 0 max 1 -vv) @@ -1752,7 +1850,7 @@ upgradeGithubReleaseCommandParse() { ;; - # Option 6/17 + # Option 6/16 # optionTraceVerbose alts -vvv # type: Boolean min 0 max 1 -vvv) @@ -1770,25 +1868,7 @@ upgradeGithubReleaseCommandParse() { ;; - # Option 7/17 - # optionEnvFiles alts --env-file - # type: StringArray min 0 max -1 - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - ;; - - # Option 8/17 + # Option 7/16 # optionLogLevel alts --log-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1816,7 +1896,7 @@ upgradeGithubReleaseCommandParse() { ;; - # Option 9/17 + # Option 8/16 # optionLogFile alts --log-file # type: String min 0 max 1 --log-file) @@ -1839,7 +1919,7 @@ upgradeGithubReleaseCommandParse() { ;; - # Option 10/17 + # Option 9/16 # optionDisplayLevel alts --display-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1867,7 +1947,7 @@ upgradeGithubReleaseCommandParse() { ;; - # Option 11/17 + # Option 10/16 # optionNoColor alts --no-color # type: Boolean min 0 max 1 --no-color) @@ -1885,7 +1965,7 @@ upgradeGithubReleaseCommandParse() { ;; - # Option 12/17 + # Option 11/16 # optionTheme alts --theme # type: String min 0 max 1 # authorizedValues: default|default-force|noColor @@ -1913,7 +1993,7 @@ upgradeGithubReleaseCommandParse() { ;; - # Option 13/17 + # Option 12/16 # optionVersion alts --version # type: Boolean min 0 max 1 --version) @@ -1929,7 +2009,7 @@ upgradeGithubReleaseCommandParse() { ;; - # Option 14/17 + # Option 13/16 # optionQuiet alts --quiet|-q # type: Boolean min 0 max 1 --quiet | -q) @@ -1947,7 +2027,7 @@ upgradeGithubReleaseCommandParse() { ;; - # Option 15/17 + # Option 14/16 # optionVersionArg alts --version-arg # type: String min 0 max 1 --version-arg) @@ -1966,7 +2046,7 @@ upgradeGithubReleaseCommandParse() { optionVersionArg="$1" ;; - # Option 16/17 + # Option 15/16 # optionCurrentVersion alts --current-version|-c # type: String min 0 max 1 --current-version | -c) @@ -1985,7 +2065,7 @@ upgradeGithubReleaseCommandParse() { optionCurrentVersion="$1" ;; - # Option 17/17 + # Option 16/16 # optionExactVersion alts --exact-version|-e # type: String min 0 max 1 --exact-version | -e) @@ -2086,7 +2166,6 @@ upgradeGithubReleaseCommandParse() { - if ((options_parse_argParsedCountTargetFileArg < 1 )); then Log::displayError "Command ${SCRIPT_NAME} - Argument 'targetFile' should be provided at least 1 time(s)" return 1 @@ -2117,7 +2196,7 @@ upgradeGithubReleaseCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--version-arg ]" "[--current-version|-c ]" "[--exact-version|-e ]" + optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--version-arg ]" "[--current-version|-c ]" "[--exact-version|-e ]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "upgradeGithubRelease" "${optionsAltList[@]}" @@ -2178,12 +2257,6 @@ upgradeGithubReleaseCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" - echo - - - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo diff --git a/bin/waitForIt b/bin/waitForIt index a286f4dd..3a5dbfc9 100755 --- a/bin/waitForIt +++ b/bin/waitForIt @@ -355,6 +355,127 @@ Bash::handlePipelineFailure() { } +read -r -d '\0' bashToolsDefaultConfigTemplate <<-EOM || true +# +# shellcheck disable=SC2034 + +# Default settings +# you can override these settings by creating ${HOME}/.bash-tools/.env file + +### +### DISPLAY Level +### minimum level of the messages that will be displayed on screen +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} + +### +### DISPLAY duration +### 0: no duration is displayed on the messages +### 1: duration between previous message and current is displayed +### with the message +### +DISPLAY_DURATION=${DISPLAY_DURATION:0} + +### +### Log to file +### +### all log messages will be redirected to log file specified +### this same path will be used inside and outside of the container +### +BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} + +### +### LOG Level +### minimum level of the messages that will be logged into LOG_FILE +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} + +# absolute directory containing db import sql dumps +DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} + +# garbage collect all files for which modification is greater than eg: 30 days (+30) +# each time an existing file is used by dbImport/dbImportTable +# the file modification time is set to now +DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} + +# absolute directory containing dbScripts used by dbScriptAllDatabases +SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} + +# ----------------------------------------------------- +# AWS Parameters +# ----------------------------------------------------- +S3_BASE_URL=${S3_BASE_URL:-} + +# ----------------------------------------------------- +# Postman Parameters +# ----------------------------------------------------- +POSTMAN_API_KEY= +EOM + +# @description loads ~/.bash-tools/.env if available +# if not creates it from a default template +# else check if new options need to be added +BashTools::Conf::requireLoad() { + BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" + if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then + FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" + else + # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR + FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" + fi + # shellcheck disable=SC2034 + FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" + # shellcheck disable=SC2034 + FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + + if [[ -f "${HOME}/.bash-tools/.env" ]]; then + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") + fi + + local envFile="${HOME}/.bash-tools/.env" + if [[ ! -f "${envFile}" ]]; then + mkdir -p "${HOME}/.bash-tools" + ( + echo "#!/usr/bin/env bash" + # shellcheck disable=SC2154 + echo "${bashToolsDefaultConfigTemplate}" + ) >"${envFile}" + Log::displayInfo "Configuration file '${envFile}' created" + else + if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then + ( + echo '# -----------------------------------------------------' + echo '# Postman Parameters' + echo '# -----------------------------------------------------' + echo 'POSTMAN_API_KEY=' + ) >>"${envFile}" + fi + fi + # shellcheck source=/conf/defaultEnv/.env + source "${envFile}" || { + Log::displayError "impossible to load '${envFile}'" + exit 1 + } +} + + # @description run command specified # @arg $@ array:String[] the command to run # @env optionInfoVerbose int - if 1 displays the command specified before running it @@ -518,19 +639,6 @@ Log::displayInfo() { } -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logWarning "$1" -} - - # @description Display message using error color (red) and exit immediately with error status 1 # @arg $1 message:String the message to display # @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs @@ -614,15 +722,6 @@ Log::logMessage() { } -# @description log message to file -# @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" - fi -} - - # @description activate or not Log::display* and Log::log* functions # based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL # environment variables loaded by Env::requireLoad @@ -918,6 +1017,7 @@ beforeParseCallback() { copyrightCallback() { # + # shellcheck disable=SC2155,SC2154,SC2250 echo "Copyright (c) 2020-now François Chastanet" } @@ -961,16 +1061,6 @@ optionVersionCallback() { exit 0 } -# shellcheck disable=SC2317 # if function is overridden -optionEnvFileCallback() { - local envFile="$2" - Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" - if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" - exit 1 - fi -} - # shellcheck disable=SC2317 # if function is overridden optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' @@ -1145,9 +1235,6 @@ commandOptionParseFinished() { defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi # shellcheck disable=SC2154 if [[ -f "${optionBashFrameworkConfig}" ]]; then files+=("${optionBashFrameworkConfig}") @@ -1163,6 +1250,19 @@ commandOptionParseFinished() { +defaultBeforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + +beforeParseCallback() { + defaultBeforeParseCallback +} + + + declare -a availableAlgos=( "timeoutV1WithNc" "timeoutV2WithNc" @@ -1255,7 +1355,6 @@ declare optionBashFrameworkConfig="" declare optionInfoVerbose="0" declare optionDebugVerbose="0" declare optionTraceVerbose="0" -declare -a optionEnvFiles=() declare optionLogLevel="" declare optionLogFile="" declare optionDisplayLevel="" @@ -1297,7 +1396,6 @@ waitForItCommandParse() { optionTraceVerbose="0" local -i options_parse_optionParsedCountOptionTraceVerbose ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionLogLevel="" local -i options_parse_optionParsedCountOptionLogLevel ((options_parse_optionParsedCountOptionLogLevel = 0)) || true @@ -1343,7 +1441,7 @@ waitForItCommandParse() { local options_parse_arg="$1" local argOptDefaultBehavior=0 case "${options_parse_arg}" in - # Option 1/18 + # Option 1/17 # optionTimeout alts --timeout|-t # type: String min 0 max 1 --timeout | -t) @@ -1364,7 +1462,7 @@ waitForItCommandParse() { ;; - # Option 2/18 + # Option 2/17 # optionExecIfTimedOut alts --exec-command-on-timeout|--lax|-l # type: Boolean min 0 max 1 --exec-command-on-timeout | --lax | -l) @@ -1378,7 +1476,7 @@ waitForItCommandParse() { ((++options_parse_optionParsedCountOptionExecIfTimedOut)) ;; - # Option 3/18 + # Option 3/17 # optionHelp alts --help|-h # type: Boolean min 0 max 1 --help | -h) @@ -1394,7 +1492,7 @@ waitForItCommandParse() { ;; - # Option 4/18 + # Option 4/17 # optionConfig alts --config # type: Boolean min 0 max 1 --config) @@ -1408,7 +1506,7 @@ waitForItCommandParse() { ((++options_parse_optionParsedCountOptionConfig)) ;; - # Option 5/18 + # Option 5/17 # optionBashFrameworkConfig alts --bash-framework-config # type: String min 0 max 1 --bash-framework-config) @@ -1429,7 +1527,7 @@ waitForItCommandParse() { ;; - # Option 6/18 + # Option 6/17 # optionInfoVerbose alts --verbose|-v # type: Boolean min 0 max 1 --verbose | -v) @@ -1447,7 +1545,7 @@ waitForItCommandParse() { ;; - # Option 7/18 + # Option 7/17 # optionDebugVerbose alts -vv # type: Boolean min 0 max 1 -vv) @@ -1465,7 +1563,7 @@ waitForItCommandParse() { ;; - # Option 8/18 + # Option 8/17 # optionTraceVerbose alts -vvv # type: Boolean min 0 max 1 -vvv) @@ -1483,25 +1581,7 @@ waitForItCommandParse() { ;; - # Option 9/18 - # optionEnvFiles alts --env-file - # type: StringArray min 0 max -1 - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - ;; - - # Option 10/18 + # Option 9/17 # optionLogLevel alts --log-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1529,7 +1609,7 @@ waitForItCommandParse() { ;; - # Option 11/18 + # Option 10/17 # optionLogFile alts --log-file # type: String min 0 max 1 --log-file) @@ -1552,7 +1632,7 @@ waitForItCommandParse() { ;; - # Option 12/18 + # Option 11/17 # optionDisplayLevel alts --display-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1580,7 +1660,7 @@ waitForItCommandParse() { ;; - # Option 13/18 + # Option 12/17 # optionNoColor alts --no-color # type: Boolean min 0 max 1 --no-color) @@ -1598,7 +1678,7 @@ waitForItCommandParse() { ;; - # Option 14/18 + # Option 13/17 # optionTheme alts --theme # type: String min 0 max 1 # authorizedValues: default|default-force|noColor @@ -1626,7 +1706,7 @@ waitForItCommandParse() { ;; - # Option 15/18 + # Option 14/17 # optionVersion alts --version # type: Boolean min 0 max 1 --version) @@ -1642,7 +1722,7 @@ waitForItCommandParse() { ;; - # Option 16/18 + # Option 15/17 # optionQuiet alts --quiet|-q # type: Boolean min 0 max 1 --quiet | -q) @@ -1660,7 +1740,7 @@ waitForItCommandParse() { ;; - # Option 17/18 + # Option 16/17 # optionAlgo alts --algorithm|--algo # type: String min 0 max 1 --algorithm | --algo) @@ -1681,7 +1761,7 @@ waitForItCommandParse() { ;; - # Option 18/18 + # Option 17/17 # optionLegacy alts --user-nc # type: Boolean min 0 max 1 --user-nc) @@ -1785,7 +1865,6 @@ waitForItCommandParse() { - if ((options_parse_argParsedCountHostOrIpArg < 1 )); then Log::displayError "Command ${SCRIPT_NAME} - Argument 'hostOrIp' should be provided at least 1 time(s)" return 1 @@ -1818,7 +1897,7 @@ waitForItCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--timeout|-t ]" "[--exec-command-on-timeout|--lax|-l]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--algorithm|--algo ]" "[--user-nc]" + optionsAltList=("[--timeout|-t ]" "[--exec-command-on-timeout|--lax|-l]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--algorithm|--algo ]" "[--user-nc]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "waitForIt" "${optionsAltList[@]}" @@ -1898,12 +1977,6 @@ waitForItCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" - echo - - - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo diff --git a/bin/waitForMysql b/bin/waitForMysql index db71355d..afd01c54 100755 --- a/bin/waitForMysql +++ b/bin/waitForMysql @@ -338,6 +338,127 @@ Bash::handlePipelineFailure() { } +read -r -d '\0' bashToolsDefaultConfigTemplate <<-EOM || true +# +# shellcheck disable=SC2034 + +# Default settings +# you can override these settings by creating ${HOME}/.bash-tools/.env file + +### +### DISPLAY Level +### minimum level of the messages that will be displayed on screen +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} + +### +### DISPLAY duration +### 0: no duration is displayed on the messages +### 1: duration between previous message and current is displayed +### with the message +### +DISPLAY_DURATION=${DISPLAY_DURATION:0} + +### +### Log to file +### +### all log messages will be redirected to log file specified +### this same path will be used inside and outside of the container +### +BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} + +### +### LOG Level +### minimum level of the messages that will be logged into LOG_FILE +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} + +# absolute directory containing db import sql dumps +DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} + +# garbage collect all files for which modification is greater than eg: 30 days (+30) +# each time an existing file is used by dbImport/dbImportTable +# the file modification time is set to now +DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} + +# absolute directory containing dbScripts used by dbScriptAllDatabases +SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} + +# ----------------------------------------------------- +# AWS Parameters +# ----------------------------------------------------- +S3_BASE_URL=${S3_BASE_URL:-} + +# ----------------------------------------------------- +# Postman Parameters +# ----------------------------------------------------- +POSTMAN_API_KEY= +EOM + +# @description loads ~/.bash-tools/.env if available +# if not creates it from a default template +# else check if new options need to be added +BashTools::Conf::requireLoad() { + BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" + if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then + FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" + else + # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR + FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" + fi + # shellcheck disable=SC2034 + FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" + # shellcheck disable=SC2034 + FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + + if [[ -f "${HOME}/.bash-tools/.env" ]]; then + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") + fi + + local envFile="${HOME}/.bash-tools/.env" + if [[ ! -f "${envFile}" ]]; then + mkdir -p "${HOME}/.bash-tools" + ( + echo "#!/usr/bin/env bash" + # shellcheck disable=SC2154 + echo "${bashToolsDefaultConfigTemplate}" + ) >"${envFile}" + Log::displayInfo "Configuration file '${envFile}' created" + else + if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then + ( + echo '# -----------------------------------------------------' + echo '# Postman Parameters' + echo '# -----------------------------------------------------' + echo 'POSTMAN_API_KEY=' + ) >>"${envFile}" + fi + fi + # shellcheck source=/conf/defaultEnv/.env + source "${envFile}" || { + Log::displayError "impossible to load '${envFile}'" + exit 1 + } +} + + # @description run command specified # @arg $@ array:String[] the command to run # @env optionInfoVerbose int - if 1 displays the command specified before running it @@ -501,19 +622,6 @@ Log::displayInfo() { } -# @description Display message using warning color (yellow) -# @arg $1 message:String the message to display -# @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs -# @env LOG_CONTEXT String allows to contextualize the log -Log::displayWarning() { - if ((BASH_FRAMEWORK_DISPLAY_LEVEL >= __LEVEL_WARNING)); then - Log::computeDuration - echo -e "${__WARNING_COLOR}WARN - ${LOG_CONTEXT:-}${LOG_LAST_DURATION_STR:-}${1}${__RESET_COLOR}" >&2 - fi - Log::logWarning "$1" -} - - # @description Display message using error color (red) and exit immediately with error status 1 # @arg $1 message:String the message to display # @env DISPLAY_DURATION int (default 0) if 1 display elapsed time information between 2 info logs @@ -597,15 +705,6 @@ Log::logMessage() { } -# @description log message to file -# @arg $1 message:String the message to display -Log::logWarning() { - if ((BASH_FRAMEWORK_LOG_LEVEL >= __LEVEL_WARNING)); then - Log::logMessage "${2:-WARNING}" "$1" - fi -} - - # @description activate or not Log::display* and Log::log* functions # based on BASH_FRAMEWORK_DISPLAY_LEVEL and BASH_FRAMEWORK_LOG_LEVEL # environment variables loaded by Env::requireLoad @@ -901,6 +1000,7 @@ beforeParseCallback() { copyrightCallback() { # + # shellcheck disable=SC2155,SC2154,SC2250 echo "Copyright (c) 2020-now François Chastanet" } @@ -944,16 +1044,6 @@ optionVersionCallback() { exit 0 } -# shellcheck disable=SC2317 # if function is overridden -optionEnvFileCallback() { - local envFile="$2" - Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" - if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" - exit 1 - fi -} - # shellcheck disable=SC2317 # if function is overridden optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' @@ -1128,9 +1218,6 @@ commandOptionParseFinished() { defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi # shellcheck disable=SC2154 if [[ -f "${optionBashFrameworkConfig}" ]]; then files+=("${optionBashFrameworkConfig}") @@ -1146,6 +1233,19 @@ commandOptionParseFinished() { +defaultBeforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + +beforeParseCallback() { + defaultBeforeParseCallback +} + + + longDescriptionFunction() { echo -e " ${__HELP_TITLE}EXIT STATUS CODES:${__HELP_NORMAL}" echo -e " ${__HELP_OPTION_COLOR}0${__HELP_NORMAL}: mysql is available" @@ -1202,7 +1302,6 @@ declare optionBashFrameworkConfig="" declare optionInfoVerbose="0" declare optionDebugVerbose="0" declare optionTraceVerbose="0" -declare -a optionEnvFiles=() declare optionLogLevel="" declare optionLogFile="" declare optionDisplayLevel="" @@ -1244,7 +1343,6 @@ waitForMysqlCommandParse() { optionTraceVerbose="0" local -i options_parse_optionParsedCountOptionTraceVerbose ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionLogLevel="" local -i options_parse_optionParsedCountOptionLogLevel ((options_parse_optionParsedCountOptionLogLevel = 0)) || true @@ -1292,7 +1390,7 @@ waitForMysqlCommandParse() { local options_parse_arg="$1" local argOptDefaultBehavior=0 case "${options_parse_arg}" in - # Option 1/16 + # Option 1/15 # optionTimeout alts --timeout|-t # type: String min 0 max 1 --timeout | -t) @@ -1313,7 +1411,7 @@ waitForMysqlCommandParse() { ;; - # Option 2/16 + # Option 2/15 # optionExecIfTimedOut alts --exec-command-on-timeout|--lax|-l # type: Boolean min 0 max 1 --exec-command-on-timeout | --lax | -l) @@ -1327,7 +1425,7 @@ waitForMysqlCommandParse() { ((++options_parse_optionParsedCountOptionExecIfTimedOut)) ;; - # Option 3/16 + # Option 3/15 # optionHelp alts --help|-h # type: Boolean min 0 max 1 --help | -h) @@ -1343,7 +1441,7 @@ waitForMysqlCommandParse() { ;; - # Option 4/16 + # Option 4/15 # optionConfig alts --config # type: Boolean min 0 max 1 --config) @@ -1357,7 +1455,7 @@ waitForMysqlCommandParse() { ((++options_parse_optionParsedCountOptionConfig)) ;; - # Option 5/16 + # Option 5/15 # optionBashFrameworkConfig alts --bash-framework-config # type: String min 0 max 1 --bash-framework-config) @@ -1378,7 +1476,7 @@ waitForMysqlCommandParse() { ;; - # Option 6/16 + # Option 6/15 # optionInfoVerbose alts --verbose|-v # type: Boolean min 0 max 1 --verbose | -v) @@ -1396,7 +1494,7 @@ waitForMysqlCommandParse() { ;; - # Option 7/16 + # Option 7/15 # optionDebugVerbose alts -vv # type: Boolean min 0 max 1 -vv) @@ -1414,7 +1512,7 @@ waitForMysqlCommandParse() { ;; - # Option 8/16 + # Option 8/15 # optionTraceVerbose alts -vvv # type: Boolean min 0 max 1 -vvv) @@ -1432,25 +1530,7 @@ waitForMysqlCommandParse() { ;; - # Option 9/16 - # optionEnvFiles alts --env-file - # type: StringArray min 0 max -1 - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - ;; - - # Option 10/16 + # Option 9/15 # optionLogLevel alts --log-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1478,7 +1558,7 @@ waitForMysqlCommandParse() { ;; - # Option 11/16 + # Option 10/15 # optionLogFile alts --log-file # type: String min 0 max 1 --log-file) @@ -1501,7 +1581,7 @@ waitForMysqlCommandParse() { ;; - # Option 12/16 + # Option 11/15 # optionDisplayLevel alts --display-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1529,7 +1609,7 @@ waitForMysqlCommandParse() { ;; - # Option 13/16 + # Option 12/15 # optionNoColor alts --no-color # type: Boolean min 0 max 1 --no-color) @@ -1547,7 +1627,7 @@ waitForMysqlCommandParse() { ;; - # Option 14/16 + # Option 13/15 # optionTheme alts --theme # type: String min 0 max 1 # authorizedValues: default|default-force|noColor @@ -1575,7 +1655,7 @@ waitForMysqlCommandParse() { ;; - # Option 15/16 + # Option 14/15 # optionVersion alts --version # type: Boolean min 0 max 1 --version) @@ -1591,7 +1671,7 @@ waitForMysqlCommandParse() { ;; - # Option 16/16 + # Option 15/15 # optionQuiet alts --quiet|-q # type: Boolean min 0 max 1 --quiet | -q) @@ -1729,7 +1809,6 @@ waitForMysqlCommandParse() { - if ((options_parse_argParsedCountMysqlHostArg < 1 )); then Log::displayError "Command ${SCRIPT_NAME} - Argument 'mysqlHost' should be provided at least 1 time(s)" return 1 @@ -1770,7 +1849,7 @@ waitForMysqlCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--timeout|-t ]" "[--exec-command-on-timeout|--lax|-l]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" + optionsAltList=("[--timeout|-t ]" "[--exec-command-on-timeout|--lax|-l]" "[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "waitForMysql" "${optionsAltList[@]}" @@ -1858,12 +1937,6 @@ waitForMysqlCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" - echo - - - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo diff --git a/conf/dbScripts/extractData b/conf/dbScripts/extractData index 439c37c4..e8c8111b 100755 --- a/conf/dbScripts/extractData +++ b/conf/dbScripts/extractData @@ -349,18 +349,79 @@ Bash::handlePipelineFailure() { } +read -r -d '\0' bashToolsDefaultConfigTemplate <<-EOM || true +# +# shellcheck disable=SC2034 + +# Default settings +# you can override these settings by creating ${HOME}/.bash-tools/.env file + +### +### DISPLAY Level +### minimum level of the messages that will be displayed on screen +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} + +### +### DISPLAY duration +### 0: no duration is displayed on the messages +### 1: duration between previous message and current is displayed +### with the message +### +DISPLAY_DURATION=${DISPLAY_DURATION:0} + +### +### Log to file +### +### all log messages will be redirected to log file specified +### this same path will be used inside and outside of the container +### +BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} + +### +### LOG Level +### minimum level of the messages that will be logged into LOG_FILE +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} + +# absolute directory containing db import sql dumps +DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} + +# garbage collect all files for which modification is greater than eg: 30 days (+30) +# each time an existing file is used by dbImport/dbImportTable +# the file modification time is set to now +DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} + +# absolute directory containing dbScripts used by dbScriptAllDatabases +SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} + +# ----------------------------------------------------- +# AWS Parameters +# ----------------------------------------------------- +S3_BASE_URL=${S3_BASE_URL:-} + +# ----------------------------------------------------- +# Postman Parameters +# ----------------------------------------------------- +POSTMAN_API_KEY= +EOM + # @description loads ~/.bash-tools/.env if available # if not creates it from a default template # else check if new options need to be added BashTools::Conf::requireLoad() { -Linux::requireTarCommand -Compiler::Embed::extractFileFromBase64 \ - "${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" \ - "IyEvdXNyL2Jpbi9lbnYgYmFzaAojIHNoZWxsY2hlY2sgZGlzYWJsZT1TQzIwMzQKCiMgRGVmYXVsdCBzZXR0aW5ncwojIHlvdSBjYW4gb3ZlcnJpZGUgdGhlc2Ugc2V0dGluZ3MgYnkgY3JlYXRpbmcgJHtIT01FfS8uYmFzaC10b29scy8uZW52IGZpbGUKCiMjIwojIyMgRElTUExBWSBMZXZlbAojIyMgbWluaW11bSBsZXZlbCBvZiB0aGUgbWVzc2FnZXMgdGhhdCB3aWxsIGJlIGRpc3BsYXllZCBvbiBzY3JlZW4KIyMjCiMjIyAwOiBOTyBMT0cKIyMjIDE6IEVSUk9SCiMjIyAyOiBXQVJOSU5HCiMjIyAzOiBJTkZPCiMjIyA0OiBERUJVRwojIyMKQkFTSF9GUkFNRVdPUktfRElTUExBWV9MRVZFTD0ke0JBU0hfRlJBTUVXT1JLX0RJU1BMQVlfTEVWRUw6LTN9CgojIyMKIyMjIERJU1BMQVkgZHVyYXRpb24KIyMjIDA6IG5vIGR1cmF0aW9uIGlzIGRpc3BsYXllZCBvbiB0aGUgbWVzc2FnZXMKIyMjIDE6IGR1cmF0aW9uIGJldHdlZW4gcHJldmlvdXMgbWVzc2FnZSBhbmQgY3VycmVudCBpcyBkaXNwbGF5ZWQKIyMjIHdpdGggdGhlIG1lc3NhZ2UKIyMjCkRJU1BMQVlfRFVSQVRJT049JHtESVNQTEFZX0RVUkFUSU9OOjB9CgojIyMKIyMjIExvZyB0byBmaWxlCiMjIwojIyMgYWxsIGxvZyBtZXNzYWdlcyB3aWxsIGJlIHJlZGlyZWN0ZWQgdG8gbG9nIGZpbGUgc3BlY2lmaWVkCiMjIyB0aGlzIHNhbWUgcGF0aCB3aWxsIGJlIHVzZWQgaW5zaWRlIGFuZCBvdXRzaWRlIG9mIHRoZSBjb250YWluZXIKIyMjCkJBU0hfRlJBTUVXT1JLX0xPR19GSUxFPSR7QkFTSF9GUkFNRVdPUktfTE9HX0ZJTEU6LSR7RlJBTUVXT1JLX1JPT1RfRElSfS9sb2dzL2Jhc2gubG9nfQoKIyMjCiMjIyBMT0cgTGV2ZWwKIyMjIG1pbmltdW0gbGV2ZWwgb2YgdGhlIG1lc3NhZ2VzIHRoYXQgd2lsbCBiZSBsb2dnZWQgaW50byBMT0dfRklMRQojIyMKIyMjIDA6IE5PIExPRwojIyMgMTogRVJST1IKIyMjIDI6IFdBUk5JTkcKIyMjIDM6IElORk8KIyMjIDQ6IERFQlVHCiMjIwpCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw9JHtCQVNIX0ZSQU1FV09SS19MT0dfTEVWRUw6LTB9CgojIGFic29sdXRlIGRpcmVjdG9yeSBjb250YWluaW5nIGRiIGltcG9ydCBzcWwgZHVtcHMKREJfSU1QT1JUX0RVTVBfRElSPSR7REJfSU1QT1JUX0RVTVBfRElSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2RiSW1wb3J0RHVtcHN9CgojIGdhcmJhZ2UgY29sbGVjdCBhbGwgZmlsZXMgZm9yIHdoaWNoIG1vZGlmaWNhdGlvbiBpcyBncmVhdGVyIHRoYW4gZWc6IDMwIGRheXMgKCszMCkKIyBlYWNoIHRpbWUgYW4gZXhpc3RpbmcgZmlsZSBpcyB1c2VkIGJ5IGRiSW1wb3J0L2RiSW1wb3J0VGFibGUKIyB0aGUgZmlsZSBtb2RpZmljYXRpb24gdGltZSBpcyBzZXQgdG8gbm93CkRCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUz0ke0RCX0lNUE9SVF9HQVJCQUdFX0NPTExFQ1RfREFZUzotKzMwfQoKIyBhYnNvbHV0ZSBkaXJlY3RvcnkgY29udGFpbmluZyBkYlNjcmlwdHMgdXNlZCBieSBkYlNjcmlwdEFsbERhdGFiYXNlcwpTQ1JJUFRTX0ZPTERFUj0ke1NDUklQVFNfRk9MREVSOi0ke0hPTUV9Ly5iYXNoLXRvb2xzL2NvbmYvZGJTY3JpcHRzfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIEFXUyBQYXJhbWV0ZXJzCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KUzNfQkFTRV9VUkw9JHtTM19CQVNFX1VSTDotfQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIFBvc3RtYW4gUGFyYW1ldGVycwojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClBPU1RNQU5fQVBJX0tFWT0K" \ - "755" - -declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp}/db87222729e0f1ed9c597a486a61a08c/bashToolsDefaultConfigTemplate" - BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" @@ -388,7 +449,7 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp ( echo "#!/usr/bin/env bash" # shellcheck disable=SC2154 - echo "${embed_file_bashToolsDefaultConfigTemplate}" + echo "${bashToolsDefaultConfigTemplate}" ) >"${envFile}" Log::displayInfo "Configuration file '${envFile}' created" else @@ -401,7 +462,7 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp ) >>"${envFile}" fi fi - # shellcheck source=/conf/.env + # shellcheck source=/conf/defaultEnv/.env source "${envFile}" || { Log::displayError "impossible to load '${envFile}'" exit 1 @@ -409,36 +470,6 @@ declare -gx embed_file_bashToolsDefaultConfigTemplate="${PERSISTENT_TMPDIR:-/tmp } -# @description convert base64 encoded back to target file -# if target file is executable prepend dir of target -# file to PATH to make binary available everywhere -# it is advised to include in the path of the target file -# the md5sum of the binFile -# -# @arg $1 targetFile:String the file to write -# @arg $2 binFileBase64:String the base64 encoded file -# @arg $3 fileMode:String the chmod to set on the file -# @set PATH String prepend target embedded file binary directory to PATH variable if binary executable -Compiler::Embed::extractFileFromBase64() { - local targetFile="$1" - local binFileBase64="$2" - local fileMode="${3:-+x}" - local targetDir="${targetFile%/*}" - - if [[ ! -f "${targetFile}" ]]; then - if [[ ! -d "${targetDir}" ]]; then - mkdir -p "${targetDir}" - fi - base64 -d >"${targetFile}" <<<"${binFileBase64}" - chmod "${fileMode}" "${targetFile}" - fi - - if [[ -x "${targetFile}" ]]; then - Env::pathPrepend "${targetDir}" - fi -} - - # @description get absolute conf file from specified conf folder deduced using these rules # * from absolute file (ignores and ) # * relative to where script is executed (ignores and ) @@ -696,19 +727,6 @@ Db::checkRequirements() { } -# @description prepend directories to the PATH environment variable -# @arg $@ args:String[] list of directories to prepend -# @set PATH update PATH with the directories prepended -Env::pathPrepend() { - local arg - for arg in "$@"; do - if [[ -d "${arg}" && ":${PATH}:" != *":${arg}:"* ]]; then - PATH="$(realpath "${arg}"):${PATH}" - fi - done -} - - # @description ensure env files are loaded # @arg $@ list of default files to load at the end # @exitcode 1 if one of env files fails to load @@ -791,14 +809,6 @@ Linux::requireRealpathCommand() { } -# @description ensure command tar is available -# @exitcode 1 if tar command not available -# @stderr diagnostics information is displayed -Linux::requireTarCommand() { - Assert::commandExists tar -} - - declare -g FIRST_LOG_DATE LOG_LAST_LOG_DATE LOG_LAST_LOG_DATE_INIT LOG_LAST_DURATION_STR FIRST_LOG_DATE="${EPOCHREALTIME/[^0-9]/}" LOG_LAST_LOG_DATE="${FIRST_LOG_DATE}" @@ -1278,6 +1288,7 @@ beforeParseCallback() { copyrightCallback() { # + # shellcheck disable=SC2155,SC2154,SC2250 echo "Copyright (c) 2020-now François Chastanet" } @@ -1321,16 +1332,6 @@ optionVersionCallback() { exit 0 } -# shellcheck disable=SC2317 # if function is overridden -optionEnvFileCallback() { - local envFile="$2" - Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" - if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" - exit 1 - fi -} - # shellcheck disable=SC2317 # if function is overridden optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' @@ -1505,9 +1506,6 @@ commandOptionParseFinished() { defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi # shellcheck disable=SC2154 if [[ -f "${optionBashFrameworkConfig}" ]]; then files+=("${optionBashFrameworkConfig}") @@ -1523,13 +1521,17 @@ commandOptionParseFinished() { -beforeParseCallback() { +defaultBeforeParseCallback() { BashTools::Conf::requireLoad Env::requireLoad UI::requireTheme Log::requireLoad } +beforeParseCallback() { + defaultBeforeParseCallback +} + optionVersionCallback() { @@ -1585,9 +1587,7 @@ unknownArg() { } beforeParseCallback() { - Env::requireLoad - UI::requireTheme - Log::requireLoad + defaultBeforeParseCallback Linux::requireExecutedAsUser Linux::requireRealpathCommand init @@ -1605,7 +1605,6 @@ declare optionBashFrameworkConfig="" declare optionInfoVerbose="0" declare optionDebugVerbose="0" declare optionTraceVerbose="0" -declare -a optionEnvFiles=() declare optionLogLevel="" declare optionLogFile="" declare optionDisplayLevel="" @@ -1636,7 +1635,6 @@ extractDataCommandParse() { optionTraceVerbose="0" local -i options_parse_optionParsedCountOptionTraceVerbose ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionLogLevel="" local -i options_parse_optionParsedCountOptionLogLevel ((options_parse_optionParsedCountOptionLogLevel = 0)) || true @@ -1666,7 +1664,7 @@ extractDataCommandParse() { local options_parse_arg="$1" local argOptDefaultBehavior=0 case "${options_parse_arg}" in - # Option 1/14 + # Option 1/13 # optionHelp alts --help|-h # type: Boolean min 0 max 1 --help | -h) @@ -1682,7 +1680,7 @@ extractDataCommandParse() { ;; - # Option 2/14 + # Option 2/13 # optionConfig alts --config # type: Boolean min 0 max 1 --config) @@ -1696,7 +1694,7 @@ extractDataCommandParse() { ((++options_parse_optionParsedCountOptionConfig)) ;; - # Option 3/14 + # Option 3/13 # optionBashFrameworkConfig alts --bash-framework-config # type: String min 0 max 1 --bash-framework-config) @@ -1717,7 +1715,7 @@ extractDataCommandParse() { ;; - # Option 4/14 + # Option 4/13 # optionInfoVerbose alts --verbose|-v # type: Boolean min 0 max 1 --verbose | -v) @@ -1735,7 +1733,7 @@ extractDataCommandParse() { ;; - # Option 5/14 + # Option 5/13 # optionDebugVerbose alts -vv # type: Boolean min 0 max 1 -vv) @@ -1753,7 +1751,7 @@ extractDataCommandParse() { ;; - # Option 6/14 + # Option 6/13 # optionTraceVerbose alts -vvv # type: Boolean min 0 max 1 -vvv) @@ -1771,25 +1769,7 @@ extractDataCommandParse() { ;; - # Option 7/14 - # optionEnvFiles alts --env-file - # type: StringArray min 0 max -1 - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - ;; - - # Option 8/14 + # Option 7/13 # optionLogLevel alts --log-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1817,7 +1797,7 @@ extractDataCommandParse() { ;; - # Option 9/14 + # Option 8/13 # optionLogFile alts --log-file # type: String min 0 max 1 --log-file) @@ -1840,7 +1820,7 @@ extractDataCommandParse() { ;; - # Option 10/14 + # Option 9/13 # optionDisplayLevel alts --display-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1868,7 +1848,7 @@ extractDataCommandParse() { ;; - # Option 11/14 + # Option 10/13 # optionNoColor alts --no-color # type: Boolean min 0 max 1 --no-color) @@ -1886,7 +1866,7 @@ extractDataCommandParse() { ;; - # Option 12/14 + # Option 11/13 # optionTheme alts --theme # type: String min 0 max 1 # authorizedValues: default|default-force|noColor @@ -1914,7 +1894,7 @@ extractDataCommandParse() { ;; - # Option 13/14 + # Option 12/13 # optionVersion alts --version # type: Boolean min 0 max 1 --version) @@ -1930,7 +1910,7 @@ extractDataCommandParse() { ;; - # Option 14/14 + # Option 13/13 # optionQuiet alts --quiet|-q # type: Boolean min 0 max 1 --quiet | -q) @@ -1989,7 +1969,7 @@ extractDataCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" + optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "extractData" "${optionsAltList[@]}" @@ -2036,12 +2016,6 @@ extractDataCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" - echo - - - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo diff --git a/conf/.env b/conf/defaultEnv/.env similarity index 100% rename from conf/.env rename to conf/defaultEnv/.env diff --git a/conf/mysql2pumlSkins/default.png b/conf/mysql2pumlSkins/default.png deleted file mode 100644 index aaf7784b43255f532035d355498ed8d50af4ddee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 954 zcmV;r14aCaP)^?c_3(XbZ~PzFE4OxVQzGFZEP-MZ*6wY${zp#11@P)Safh~ zVQzGFZEOGm0001ZoTXK5Pa-i8{=UCrTnR!%Kqcm~0iuU;Q9wBnzg`MEK*Mf#uiHwv zUw_l?f^Z6_cO<0U&df7Ur}J`|C?=H-1Lp{|7)N1$oL8&P!GzEij|4oiQGkV{3h$Ro1 zT*4d;kEuiyL^mLbk!rzzY9s*@K7IHolW|1XGBOdVA3_Jg230cDp-8!wAg7kGS&UM{ zb!ivR({+-qDLn1%#c~0o@IeDy*P-ttxK86^xsjZrS|A8$kVe1bLOySh;DA2c9SgAq zQn%OeB>K&Io*y6_c63a5 zO^N^?(+jWSWKJ!O`(d~HhDf$KHs~WJb~@kGi-SER(wE$(kewM4!~5Is!<)S{nD%*e zlEgSxQimU6^nP5js)AIJ3tOC$PiHDi>_Wd&f;Rm}k(!k#j|Fb$->VO>;JGxf2gJ=t z*VX2w)o0TD^ZB`P5#8L#RSUV{&?sanQm^QLP2Tss*+l-7yU*dLS^iN6y8t(Aw;p=c z2UEpA+Z??twOhre39uK&ZGm`nUgzYMH_dLu#&fUr(0zDZ*I)@2WQJLuaI(#|_Q`3T zsW8Zn#*uFtW5G7tR;y^bIHE@?D)uTBNHH-Wt z{YYp&U}SPBabCxPwY*YF@(~S?TPm*s|AkO>$Q=zHt~jY*(%sEOaxT}y>HqQ9pnY?b zVXq4>)csADKP{hC&uZmbVZanz#S*F)a9Z=u&%LuM44+zH#6_p@t>1+t(sGQZCQ}z= zUv%#1N#Icv{g49o5Tm%e?yL^rM!awt2{TunyXW4k!bR=mD_1Z;Nlf6m=Un52=|GVM zJqFELkG!`}zBpYbrkYI&8->$~2v;siHgJ9e*J(~38P>250000HNkl diff --git a/install b/install index 94cd53e8..884310ae 100755 --- a/install +++ b/install @@ -355,6 +355,127 @@ Backup::dir() { } +read -r -d '\0' bashToolsDefaultConfigTemplate <<-EOM || true +# +# shellcheck disable=SC2034 + +# Default settings +# you can override these settings by creating ${HOME}/.bash-tools/.env file + +### +### DISPLAY Level +### minimum level of the messages that will be displayed on screen +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_DISPLAY_LEVEL=${BASH_FRAMEWORK_DISPLAY_LEVEL:-3} + +### +### DISPLAY duration +### 0: no duration is displayed on the messages +### 1: duration between previous message and current is displayed +### with the message +### +DISPLAY_DURATION=${DISPLAY_DURATION:0} + +### +### Log to file +### +### all log messages will be redirected to log file specified +### this same path will be used inside and outside of the container +### +BASH_FRAMEWORK_LOG_FILE=${BASH_FRAMEWORK_LOG_FILE:-${FRAMEWORK_ROOT_DIR}/logs/bash.log} + +### +### LOG Level +### minimum level of the messages that will be logged into LOG_FILE +### +### 0: NO LOG +### 1: ERROR +### 2: WARNING +### 3: INFO +### 4: DEBUG +### +BASH_FRAMEWORK_LOG_LEVEL=${BASH_FRAMEWORK_LOG_LEVEL:-0} + +# absolute directory containing db import sql dumps +DB_IMPORT_DUMP_DIR=${DB_IMPORT_DUMP_DIR:-${HOME}/.bash-tools/dbImportDumps} + +# garbage collect all files for which modification is greater than eg: 30 days (+30) +# each time an existing file is used by dbImport/dbImportTable +# the file modification time is set to now +DB_IMPORT_GARBAGE_COLLECT_DAYS=${DB_IMPORT_GARBAGE_COLLECT_DAYS:-+30} + +# absolute directory containing dbScripts used by dbScriptAllDatabases +SCRIPTS_FOLDER=${SCRIPTS_FOLDER:-${HOME}/.bash-tools/conf/dbScripts} + +# ----------------------------------------------------- +# AWS Parameters +# ----------------------------------------------------- +S3_BASE_URL=${S3_BASE_URL:-} + +# ----------------------------------------------------- +# Postman Parameters +# ----------------------------------------------------- +POSTMAN_API_KEY= +EOM + +# @description loads ~/.bash-tools/.env if available +# if not creates it from a default template +# else check if new options need to be added +BashTools::Conf::requireLoad() { + BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" + if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then + FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" + else + # if the directory does not exist yet, give a value to FRAMEWORK_ROOT_DIR + FRAMEWORK_ROOT_DIR="${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" + fi + # shellcheck disable=SC2034 + FRAMEWORK_SRC_DIR="${FRAMEWORK_ROOT_DIR}/src" + # shellcheck disable=SC2034 + FRAMEWORK_BIN_DIR="${FRAMEWORK_ROOT_DIR}/bin" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_DIR="${FRAMEWORK_ROOT_DIR}/vendor" + # shellcheck disable=SC2034 + FRAMEWORK_VENDOR_BIN_DIR="${FRAMEWORK_ROOT_DIR}/vendor/bin" + + if [[ -f "${HOME}/.bash-tools/.env" ]]; then + # shellcheck disable=SC2034 + BASH_FRAMEWORK_ENV_FILES=("${HOME}/.bash-tools/.env") + fi + + local envFile="${HOME}/.bash-tools/.env" + if [[ ! -f "${envFile}" ]]; then + mkdir -p "${HOME}/.bash-tools" + ( + echo "#!/usr/bin/env bash" + # shellcheck disable=SC2154 + echo "${bashToolsDefaultConfigTemplate}" + ) >"${envFile}" + Log::displayInfo "Configuration file '${envFile}' created" + else + if ! grep -q '^POSTMAN_API_KEY=' "${envFile}"; then + ( + echo '# -----------------------------------------------------' + echo '# Postman Parameters' + echo '# -----------------------------------------------------' + echo 'POSTMAN_API_KEY=' + ) >>"${envFile}" + fi + fi + # shellcheck source=/conf/defaultEnv/.env + source "${envFile}" || { + Log::displayError "impossible to load '${envFile}'" + exit 1 + } +} + + # @description ensure env files are loaded # @arg $@ list of default files to load at the end # @exitcode 1 if one of env files fails to load @@ -828,6 +949,7 @@ beforeParseCallback() { copyrightCallback() { # + # shellcheck disable=SC2155,SC2154,SC2250 echo "Copyright (c) 2022-now François Chastanet" } @@ -871,16 +993,6 @@ optionVersionCallback() { exit 0 } -# shellcheck disable=SC2317 # if function is overridden -optionEnvFileCallback() { - local envFile="$2" - Log::displayWarning "Command ${SCRIPT_NAME} - Option --env-file is deprecated and will be removed in the future" - if [[ ! -f "${envFile}" || ! -r "${envFile}" ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option --env-file - File '${envFile}' doesn't exist" - exit 1 - fi -} - # shellcheck disable=SC2317 # if function is overridden optionInfoVerboseCallback() { BASH_FRAMEWORK_ARGS_VERBOSE_OPTION='--verbose' @@ -1055,9 +1167,6 @@ commandOptionParseFinished() { defaultEnvFile="${PERSISTENT_TMPDIR}/.framework-config" echo "${defaultFrameworkConfig}" >"${defaultEnvFile}" local -a files=("${defaultEnvFile}") - if [[ -f "${envFile}" ]]; then - files+=("${envFile}") - fi # shellcheck disable=SC2154 if [[ -f "${optionBashFrameworkConfig}" ]]; then files+=("${optionBashFrameworkConfig}") @@ -1073,6 +1182,19 @@ commandOptionParseFinished() { +defaultBeforeParseCallback() { + BashTools::Conf::requireLoad + Env::requireLoad + UI::requireTheme + Log::requireLoad +} + +beforeParseCallback() { + defaultBeforeParseCallback +} + + + # shellcheck disable=SC2034 declare optionBashFrameworkConfig="${BASH_TOOLS_ROOT_DIR}/.framework-config" @@ -1081,6 +1203,12 @@ optionHelpCallback() { exit 0 } +beforeParseCallback() { + defaultBeforeParseCallback + Linux::requireExecutedAsUser + Linux::requireTarCommand +} + # ------------------------------------------ # Command installCommand @@ -1093,7 +1221,6 @@ declare optionBashFrameworkConfig="" declare optionInfoVerbose="0" declare optionDebugVerbose="0" declare optionTraceVerbose="0" -declare -a optionEnvFiles=() declare optionLogLevel="" declare optionLogFile="" declare optionDisplayLevel="" @@ -1125,7 +1252,6 @@ installCommandParse() { optionTraceVerbose="0" local -i options_parse_optionParsedCountOptionTraceVerbose ((options_parse_optionParsedCountOptionTraceVerbose = 0)) || true - optionLogLevel="" local -i options_parse_optionParsedCountOptionLogLevel ((options_parse_optionParsedCountOptionLogLevel = 0)) || true @@ -1158,7 +1284,7 @@ installCommandParse() { local options_parse_arg="$1" local argOptDefaultBehavior=0 case "${options_parse_arg}" in - # Option 1/15 + # Option 1/14 # optionHelp alts --help|-h # type: Boolean min 0 max 1 --help | -h) @@ -1174,7 +1300,7 @@ installCommandParse() { ;; - # Option 2/15 + # Option 2/14 # optionConfig alts --config # type: Boolean min 0 max 1 --config) @@ -1188,7 +1314,7 @@ installCommandParse() { ((++options_parse_optionParsedCountOptionConfig)) ;; - # Option 3/15 + # Option 3/14 # optionBashFrameworkConfig alts --bash-framework-config # type: String min 0 max 1 --bash-framework-config) @@ -1209,7 +1335,7 @@ installCommandParse() { ;; - # Option 4/15 + # Option 4/14 # optionInfoVerbose alts --verbose|-v # type: Boolean min 0 max 1 --verbose | -v) @@ -1227,7 +1353,7 @@ installCommandParse() { ;; - # Option 5/15 + # Option 5/14 # optionDebugVerbose alts -vv # type: Boolean min 0 max 1 -vv) @@ -1245,7 +1371,7 @@ installCommandParse() { ;; - # Option 6/15 + # Option 6/14 # optionTraceVerbose alts -vvv # type: Boolean min 0 max 1 -vvv) @@ -1263,25 +1389,7 @@ installCommandParse() { ;; - # Option 7/15 - # optionEnvFiles alts --env-file - # type: StringArray min 0 max -1 - --env-file) - shift - if (($# == 0)); then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - a value needs to be specified" - return 1 - fi - - ((++options_parse_optionParsedCountOptionEnvFiles)) - optionEnvFiles+=("$1") - optionEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - updateArgListEnvFileCallback "${options_parse_arg}" "${optionEnvFiles[@]}" - - ;; - - # Option 8/15 + # Option 7/14 # optionLogLevel alts --log-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1309,7 +1417,7 @@ installCommandParse() { ;; - # Option 9/15 + # Option 8/14 # optionLogFile alts --log-file # type: String min 0 max 1 --log-file) @@ -1332,7 +1440,7 @@ installCommandParse() { ;; - # Option 10/15 + # Option 9/14 # optionDisplayLevel alts --display-level # type: String min 0 max 1 # authorizedValues: OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE @@ -1360,7 +1468,7 @@ installCommandParse() { ;; - # Option 11/15 + # Option 10/14 # optionNoColor alts --no-color # type: Boolean min 0 max 1 --no-color) @@ -1378,7 +1486,7 @@ installCommandParse() { ;; - # Option 12/15 + # Option 11/14 # optionTheme alts --theme # type: String min 0 max 1 # authorizedValues: default|default-force|noColor @@ -1406,7 +1514,7 @@ installCommandParse() { ;; - # Option 13/15 + # Option 12/14 # optionVersion alts --version # type: Boolean min 0 max 1 --version) @@ -1422,7 +1530,7 @@ installCommandParse() { ;; - # Option 14/15 + # Option 13/14 # optionQuiet alts --quiet|-q # type: Boolean min 0 max 1 --quiet | -q) @@ -1440,7 +1548,7 @@ installCommandParse() { ;; - # Option 15/15 + # Option 14/14 # optionSkipBackup alts --skip-backup # type: Boolean min 0 max 1 --skip-backup) @@ -1495,7 +1603,7 @@ installCommandHelp() { # ------------------------------------------ # usage/options section # ------------------------------------------ - optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--env-file ]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--skip-backup]" + optionsAltList=("[--help|-h]" "[--config]" "[--bash-framework-config ]" "[--verbose|-v]" "[-vv]" "[-vvv]" "[--log-level ]" "[--log-file ]" "[--display-level ]" "[--no-color]" "[--theme ]" "[--version]" "[--quiet|-q]" "[--skip-backup]" ) Array::wrap2 " " 80 2 "${__HELP_TITLE_COLOR}USAGE:${__RESET_COLOR}" \ "install" "${optionsAltList[@]}" @@ -1542,12 +1650,6 @@ installCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--env-file ${__HELP_NORMAL} {list} (optional)" - Array::wrap2 ' ' 76 4 " " "Load the specified env file (deprecated, please use --bash-framework-config option instead)" - echo - - - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo @@ -1652,9 +1754,6 @@ installCommandParse "$@" MAIN_FUNCTION_NAME="main" main() { -Linux::requireExecutedAsUser -Linux::requireTarCommand - if ! command -v parallel &>/dev/null; then Log::displayInfo "We will install GNU parallel software, please enter you sudo password" sudo apt-get update || true @@ -1680,6 +1779,7 @@ if [[ -d "${HOME}/.bash-tools" ]]; then Log::displayInfo "Updating configuration" cp -R --no-clobber "${BASH_TOOLS_ROOT_DIR}/conf/." "${HOME}/.bash-tools" + cp "${BASH_TOOLS_ROOT_DIR}/conf/defaultEnv/.env" "${HOME}/.bash-tools" if [[ "${FRAMEWORK_ROOT_DIR}/conf/.env" -nt "${HOME}/.bash-tools/.env" ]]; then Log::displayWarning "${FRAMEWORK_ROOT_DIR}/conf/.env is newer than ${HOME}/.bash-tools/.env, compare the files to check if some updates need to be applied" else @@ -1688,7 +1788,8 @@ if [[ -d "${HOME}/.bash-tools" ]]; then else Log::displayInfo "Installing configuration in ~/.bash-tools" mkdir -p "${HOME}/.bash-tools" - cp -R "${BASE_DIR}/conf/." "${HOME}/.bash-tools" + cp -R "${BASH_TOOLS_ROOT_DIR}/conf/." "${HOME}/.bash-tools" + cp "${BASH_TOOLS_ROOT_DIR}/conf/defaultEnv/.env" "${HOME}/.bash-tools" fi } diff --git a/src/BashTools/Conf/requireLoad.bats b/src/BashTools/Conf/requireLoad.bats index e0e77315..63035cd9 100755 --- a/src/BashTools/Conf/requireLoad.bats +++ b/src/BashTools/Conf/requireLoad.bats @@ -11,7 +11,7 @@ setup() { export TMPDIR="${BATS_TEST_TMPDIR}" export HOME="${BATS_TEST_TMPDIR}/home" export BASH_FRAMEWORK_THEME="noColor" - export bashToolsDefaultConfigTemplate="$(cat "${rootDir}/conf/.env")" + export bashToolsDefaultConfigTemplate="$(cat "${rootDir}/conf/defaultEnv/.env")" } function BashTools::Conf::requireLoad::envFileDoesNotExist { #@test @@ -25,7 +25,7 @@ function BashTools::Conf::requireLoad::envFileDoesNotExist { #@test function BashTools::Conf::requireLoad::envFileWithApiKeyExists { #@test mkdir -p "${HOME}/.bash-tools" - cp "${rootDir}/conf/.env" "${HOME}/.bash-tools/.env" + cp "${rootDir}/conf/defaultEnv/.env" "${HOME}/.bash-tools/.env" sed -i -E -e 's/^POSTMAN_API_KEY=/POSTMAN_API_KEY=fake2/' "${HOME}/.bash-tools/.env" local status=0 BashTools::Conf::requireLoad >"${BATS_TEST_TMPDIR}/result" 2>&1 || status=$? @@ -37,7 +37,7 @@ function BashTools::Conf::requireLoad::envFileWithApiKeyExists { #@test function BashTools::Conf::requireLoad::envFileExistsMissingApiKey { #@test mkdir -p "${HOME}/.bash-tools" - cp "${rootDir}/conf/.env" "${HOME}/.bash-tools/.env" + cp "${rootDir}/conf/defaultEnv/.env" "${HOME}/.bash-tools/.env" sed -i -E -e 's/^POSTMAN_API_KEY=//' "${HOME}/.bash-tools/.env" local status=0 BashTools::Conf::requireLoad >"${BATS_TEST_TMPDIR}/result" 2>&1 || status=$? @@ -50,7 +50,7 @@ function BashTools::Conf::requireLoad::envFileExistsMissingApiKey { #@test function BashTools::Conf::requireLoad::envFileImpossibleToLoad { #@test mkdir -p "${HOME}/.bash-tools" - cp "${rootDir}/conf/.env" "${HOME}/.bash-tools/.env" + cp "${rootDir}/conf/defaultEnv/.env" "${HOME}/.bash-tools/.env" echo "return 12" >>"${HOME}/.bash-tools/.env" run BashTools::Conf::requireLoad 2>&1 assert_failure 1 diff --git a/src/BashTools/Conf/requireLoad.sh b/src/BashTools/Conf/requireLoad.sh index 0a34b868..82734318 100755 --- a/src/BashTools/Conf/requireLoad.sh +++ b/src/BashTools/Conf/requireLoad.sh @@ -1,10 +1,13 @@ #!/bin/bash +read -r -d '\0' bashToolsDefaultConfigTemplate <<-EOM || true +#{{ include ".env" .Data . -}} +EOM + # @description loads ~/.bash-tools/.env if available # if not creates it from a default template # else check if new options need to be added BashTools::Conf::requireLoad() { - # @embed "${BASH_TOOLS_ROOT_DIR}/conf/.env" as bashToolsDefaultConfigTemplate BASH_TOOLS_ROOT_DIR="$(cd "${CURRENT_DIR}/${RELATIVE_FRAMEWORK_DIR_TO_CURRENT_DIR}" && pwd -P)" if [[ -d "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework/" ]]; then FRAMEWORK_ROOT_DIR="$(cd "${BASH_TOOLS_ROOT_DIR}/vendor/bash-tools-framework" && pwd -P)" @@ -32,7 +35,7 @@ BashTools::Conf::requireLoad() { ( echo "#!/usr/bin/env bash" # shellcheck disable=SC2154 - echo "${embed_file_bashToolsDefaultConfigTemplate}" + echo "${bashToolsDefaultConfigTemplate}" ) >"${envFile}" Log::displayInfo "Configuration file '${envFile}' created" else @@ -45,7 +48,7 @@ BashTools::Conf::requireLoad() { ) >>"${envFile}" fi fi - # shellcheck source=/conf/.env + # shellcheck source=/conf/defaultEnv/.env source "${envFile}" || { Log::displayError "impossible to load '${envFile}'" exit 1 diff --git a/src/_binaries/Converters/mysql2puml/mysql2puml-binary.yaml b/src/_binaries/Converters/mysql2puml/mysql2puml-binary.yaml index 3236b44e..a5ad19be 100644 --- a/src/_binaries/Converters/mysql2puml/mysql2puml-binary.yaml +++ b/src/_binaries/Converters/mysql2puml/mysql2puml-binary.yaml @@ -1,4 +1,5 @@ extends: + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" @@ -7,7 +8,7 @@ vars: compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/mysql2puml" - relativeRootDirBasedOnTargetDir: .. + binData: commands: default: diff --git a/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt b/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt index 8561fa04..9b759192 100644 --- a/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt +++ b/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt @@ -4,7 +4,7 @@ USAGE: mysql2puml [OPTIONS] [ARGUMENTS] USAGE: mysql2puml [--help|-h] [--config] [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] - [--env-file ] [--log-level ] [--log-file ] + [--log-level ] [--log-file ] [--display-level ] [--no-color] [--theme ] [--version] [--quiet|-q] [--skin ] @@ -25,9 +25,6 @@ Debug level verbose mode (alias of --display-level DEBUG) -vvv {single} Trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-con - fig option instead) --log-level  {single} Set log level Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE diff --git a/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.png b/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.png index 4cfcb8eb81a2b107fbc849ed717a640c088fa248..2f3081b62271fb6a52d8d923af5c96c67bad5b50 100644 GIT binary patch literal 56573 zcma&O1yq$=*EYOqr3L8}L|R%(K%{%qNJ}?JONTT_r-XEaq=a;*(k(5G(hc82@p;a9 zzxVsc?>J)|hwQ!XwdTF%oY%bOH3@>silHGBB10e$GzoEG1qcK#1OkC&zXuEcg*SYUPR4a-`eJ_g`tr>M9k32(Dt>xp#izxTXGY7dmB3*CMFw;*H-opmKIO-tt}k~ zd&wXW1Zh(xHT%E+4uJvdI494&vQ%6W$GlF}88Tw@(@^^5s2cLJmrtuyWp7nwu5>&C zmD!lJX8_y^TJE7Ohu^~P7#irJWns#>#G5AgjnU{p9JAS*FtKAYy|WZiN&BBo zqOqr#zG=LVa==c6%|rBPSv?1K*0|w~J?a;Kist8`HvZAI=5sYQJN8eAC6IoG9X(xC z#AE6mIHtJ2T7$8{x7@MQF(F{p)PLw}k|v_Tnv;sfquH9xUQGVLVAd#C_sUx`QLC1x zu;so-T_oXXM0fIgY-Sy}qP?SclN0M75=pW(*dzqn4m$3uPX=1yb(_iH;y^)xAovJ63PWe^3O)$TSq zLM=CSb=m39Np+dkYI#qm_c)^j%xN#h$H?~$_eGXq1S#97HdfZW$1aOvbhVxfHtM-( zRd~{jl4zpKk>=nBez9GaZ-EJ$@nHcd_Z1y2=x?j%eAL)I1ScR0e1qF={zj5m6)H^1?VDeSYgHdrr_o=oM4JUi5b>wEFO@Ab<*VYcBwd5N?Db~UJR(E5)( z7s53+3baGbuu$gZ_5`UwKRcGkZ!Da6p2gZytu-^4~CFn~g^#~ZYtKzQg zx5)V7gmh1&x?~zwXd*Uw4=1*&mD}eGTNl_<$PjcHPw+AgzkK@IgkT+^!%VzJOedXG z7XGzWZ~s`gT7<|2riRkNk7jP-24S^EG-++P&;*V>#AXH4%?g`i)L5<8m#^NPt$#7p zE;HmrOk0Q8q@wLUU_L2B9vAYCi-3F%ar!(3N=Yv zZDG|Zq$dVy-1j97GT%;cupUSgar0IO9Sf%~cZ4RV_AnUsJl>2UIYu7iHLj@{l=H`Xj z4J9)X!s6oM6tOYWBKIX@XG}~bXc;p`r^zK5PsYEiTjf(#OXbsfOe5xJOAbv+_-IE; zk2YR44YH5)u(5sZ=jN73wHsW!+TZ6{JL5li&Sn?)HqFk6pLQRF?%O|+G_BA6{0*Ad z480BL5HGd=e}5V1>F<{o7Y_^%zrlxa`lWu4rk0C)9OQls2Nv;zK^-IJu}u^3)P`*? zPNzPLp;eqT763ni;x|JF&Q6!Zr+m2~yWQ@+)|73JmCrCPih*7Na190ez>C^+;gMQb}UpYshb>Kv2|@gU@! z%u3+vJ`f>}E-KWc56XoUzCILiusNOW+iB0a-FIh^=vBVm*nd#ZODULt{G>bt<|C{> zNS7}#!2|DE<*Alv5?mk8gt@tMdYnFbl|LjmBDPD9Z7e#?@&L!q1^!cXwul6uh6p5^f&v%yfm5l}cJ6KvtM|opud$0w50&!7=!8 zC!x#dC)68XF2+>V$;Ph4`0_n9Ef{z)Axi{idU{L2IP{ZM@Q@YiLX~3mMmO#YGx(3q z-swq{Md?=4pVA}K(ke|NEz&mZe)MmRh@H%Pzk?T1$QNL&GF=vRPrsENfq7J$J0liGr`KHb zRBLoTYgnPOXk+ZESmU$D!2su!ifX3Q&+q!3p)7TU+4^1YuP#pFtpfrAFo?OBo#u!~ zXy1ucwqc>-c)fhRztXc^_`nMm$=~dKf`6UE)_6A}+UWPU%R``K#zyvK!QX1yCRn^JuaCNe%9p?|WPbd3W&Y;+ z^YANHd>!7vr=o={4x6Krv2HJ*gM0iXQy=2viz^=? zD_-oc_QlpmKat7UiDGB#RTM|mpeAG;+S0 zHWp<~L{N~G=u_d40VBkyOX^Q>vSZHD&sE~-xzg_K1M?rdsu!D$&Gf~YVi;i*5zsM8 zQGW9MA^N5YSZ~P0`paJDr`K)%=p;M_-4TI-fjrMz_}=cXd=&I;ZEamI`TW^wC__ZG zSe=SV)&&n6JCig*eRNZFtlDx$a(1uEcfIu$8ohr(st?O(DxWJ|c9r?Wa5o;4cFZ=5 z(%^UyB5D;mc=5B<#qO^=<>e!kpb!w#?tv)yU!XM~pEXqn2= zDLk=~M_Uut-@lwCahOMeKhe<*U%oV6?n}LWbM{#F$V(apO;%C-f9#;q{emu=QKR~s zODey+`Y^s(06Ix1YV02aiTj^yt%)b>dBJSIfN&Av(_zBk7jWwlnpNb;9Jr903Bv;) zdZ@~4_d~i&uOp7(Rja+Yghcu)5XY$Jvx)2{V1zgbb%q%-P&_$Ig8ALgX;KpFql}O{ zp>4{>Te4~V4bq9M`#ayheLI==)S(?KRFz{72V}8a6$+aAXtk@3x$P*e3;G+SK%X5V;jwPzxW*(*J^U^ivqmz@r zSk2Xi;4!(ped~U{kvkfdltlR8fqbQjJQAuhaN0lHJ3rnbdic;D_~_x`VNnsoj}mZA zSVosRLi&VAxNTQz*HRTT6!i7=&Bu%0o*$YL8gzydjLNVm?K~9y?X^COuW$VnJ%B*W z-+$V{sJdc(wzgvbv6I*HAMliuHRK>yJ@^xqz6ll2ym!Xx~IPyPWB0nE?ba5JdafLaL9CX zVDrxxGBKfXU*DXr+VX}&S`l^e4<{37^td_?BjiZpbFH3+A`z~@^cAUB6&Uu#fpBX& zoE7BOTO}{c4CfPu@Lw;Mw3x1>0GB6{X@gS6!@f4ts=)%aj$fjYh;B?z^2BZE2 z!4Wjl=gxC=4k)+`5lvr42kwvi=;{l4-M!?ZbS$9&^quLtDy?`pC(9B4I6Wj=1O)|! z=?%dh3)KZo=24%P`>#8V1c`q?yB5xijg_@Gj76vU{h-;@ zq$KqmX#z!|?CjoHdQP`fo9ryzpW#35J$ovwoA&}9ppZa|TNLv4XiT-f+VVq#LC~>y z=VjmZSI@d?bFnMGtJuV^9U*w}TaS=pe1QKj;v8_fb#*5ya%v(=q-N$*e zOS^D;qGU`XVuBv@qz^Vky;=@2O!~H_rlukxxLS4g`NLRPSi=D0jE*Xq$w^BqO#?U1 zpZp+C`fIAf7-YI?o5h#eSs%kFj9z>ypGQ`cpYp96vX$jR{CQSWoTS@A+rl#42%;%@95z>$mnqY}@>zd> zjx_?@Z)hhkFVD*h5+&Q?$boZ~sArx0d|`dvB+2SzcP?sYb^ySSzMbi0T|{`I5V*VK zsLG-Q*}n(e#Tlf9yhs&3MnW@T@jz2jvzj+CrQ=Y!WUdEtZ?a7YQsh;{V>$!x-k}o@(Z0cB+2o& z8ki~l^75uUoJ0|K&4eZwURd;nkfG`WNW{&Zvn>*B4#;PcUYA_0XL`C_FkWR6O{kO_ z@pAj}7HiDA9LSsclZ2a{_WZ7RtVlm({?4h;hXpVwvsOlbn_sP`~Jx7x3{ee zT1v`gQ)+8?*54;^6H6ECzVy!EqJB~Y^&^&p+81XQ4QR_?7H)4}{Lgqs*X3xrmE-Nv z(F?;~e1c6f2?@>sw7&5RJDs31x1vJfW<${V`X>sTG{%=Ba|OoD$@CHe+=>U<41 zlll{D1JQ+N#(AnIKXYqKfJ>>;QnYqMxtaY7#=mK>uza4WzN&y1I>P_QKHrz5v%0vH zs#gg#UaOOoRT_Uy4MT(m@gzTg8^xG+YMw* zSNVpa1;BoLjD~qFUOJKP{srncIw*#o@=`4>>`^_c+P#XB2oS0IoD!K@?$hguFU+`8G%$*E6lm@UJZAb zXozE!H$8p7X{*Ahtm;ybF=ndk{oK)!`@*V2lf|njsln3A@zPaJ*3m*FjPGdZjXDJn z!-R(4TF&uw46b_ijwWN-+}9k0Zop0YDrE1*KXtFSoN*m1SXKN+M23VqMlp?T!4=t- zj0HJ{y@Q|=P?WTyqC(db?W*W15#<+=|{cEA*@ zwv;N~25k1=&SpM@bdbT$Rn(&v%JAox1^hfb7pHH|Z>)R~EikCluUlI52+NMrn6|?x z;%W$c|M5i%e7b|JTJtM?PQ<`qbArtQZ@5I`>EcN>v-t*BPNMHQJ_Vy)Gcz9;S1@y( zjLvjqO}WFkZh*N;z|DuK!Qa(6_xzDxz%0DB4!qi}v-d6tqo8nc$Vu6VirO<#;c_`# ze^0_YXhQKAur|*?(sbI@EGNi`AzLxT-tI7=u78)VoyH=xlXLj9U`I}Og+5r3hbnBc zAe~V7fs`hQYLHL&?vmynTGcH9VY~LkOaq_CaeGpLI+Er2bGMJ1_@)RWdouE+UP++f zZ?an3(`F?N^M1AS_TUHKK|kBRb-kS(*(j4vr z20PhjNY?Sb5EYd&YHaZ;ky&nKg6Q@kFd5Nrmtgt}m^nw|bv({?W}6VrG`P5Q^Blvh z@kIAm9wv-^2TNa`9h~gUh>^ZsIru7|>SSXY>R2v?zj^ek zB8FYBI?g&TW%%|!n4g8A#s+~K@$ZX>DMXdB3Bdo07cZbtsD;ILt=-xRh>W=^1xoow z14*TmT!4nCHeBhB%+gcLkxn;sJzN)*3L_J^p6BifDNm0=gGWSEK*~<}c)Z#dPwzx6 zlf>?RZohMC>(CL5ljQ{vg}l5xV5OAehDBa^gBqz$tI6X^G>mW{m2XBoH4yN5!?NAq zP!g1^(Ji-q5Kv6`h@M&CV_{`f5*0s7=NJXlPGjOSrtnO)rRj*$k-*TLz`kYX7`fxl zG!6;L&f3}pD4{2`T^T!$wc=f{S=rdSLJ1U0wfWux6x>6t;vXAI1{=aVMTM$-FG3V` zmhi6|;hVX+J(uX5t}>5*szpdl4A=+#TKBH^B-hv1D^wwtpgb|v=oX|`Etv&-@I%1^ zMOIW?#!RrceAP=o9YWW z0WfD$wVd9L3(<1?5`=YTV+HZrE)orLq5iQo7~UNzsQH zOUp2owbWJF0B#&7N!$UZ+I$bQ5@WyI+S8NpIZ_A%U~+#~`V-LAK6Cq5AQ3wR1_$e* zaD$`=<*feblk)YL=;ROcvN@W+ytAWKX)>f-sM14K>}-n0@#Kl9vTQfYQM$&)>5ec!cvEC)ldCnB0hW77Eh6s(0K0Z)b?w8`KGr z{JkVGG9NKW+3PPm3ZyHH2BZU4pTmzkv7+KY$XlS^x3o4$SG%3vi^9MiUxOn_lvA;m zBj9YiMJWNt`Z4gK^i{j_tF2jepOx)5)zMkCO=!Jxv4f@YO*NV#_-qLVJG4$3Q@%jA8w5F z#n4jDqobiwaVtt5ZH`3`_!Ha!Y6m!o;thjU@E6$p{CrTJ$S7q?jTgOAR82iqV^;!} ze~qeKZs`pCJEor92)MTLkpRoI0G1QSLvdyEu>uiw+Wcn#$f8h5#y}fe8f;g4E57oZ z^DwME`qUT=>Ibh+^6gP?pihwoO;mnWvg;uo+ob=!UGPE;&U+d0@e{de9&}UhC@Cm> zl7(1C0XK$-fIuSPksK<RB%65Au{+-6I_pIR>>)3z1@^Q{{#zbJ~Eu#AFNkBEd6Pp4cU zKDeqV9di7ln}(#p6eOF^ZfE-|PhY=&9gGmwe|CBb_$!|$mJ=l@`&*wJJ5lhSemR~t zAFa@=wXL)|ys0!Du{9bIeB}-L0cYpuQJ#HBk2A?WQn{9_a@ukkRY@$Yz-9Tkm=V4k z4?e!1zj?a19P7v-dLhQDjm9d@!#pDNDq9kVuvX^7vwhm8O&N0UoJ`g*iTX&ybpEVX z+a43OpbJd3Sr9>xbY)3cc%p^zc2Gu;9c5nQovq1DI~@13FI~>G{f9m$2mD0|%RI;$ zyYYdtuYuq1b4Qw$mzU?syW>&@R93RHergNA(5bccD<%+9+@7mH!P^_A`kKrw^r3Bj z(Q$_if#-aRW1&c0=9eQ7uj=DJ(rNfxp?i4)!HLKAW;RC9*Gl0q`V=V3ju1 z*2Y1;^hI3N$JxseinznUvNyTxdLgcAfR6h^j zK)mY3At`!^-ojB4Up`=!#9%uCiCHSqbA0j5DAUFW-D}gY3Z8h4HxJ4@83sX z1Lng*5J5Q~PEPBa{Rsdyr+kplF4#7EaBV;CP0{X1P>vTQ zxRvMI#>?}#W(H=@D%#lEgh5rBT|j6svI~8>JhF?60K(!dP1q!@YYp^{(ff^mrSQ#K z^i>I20NZMUmTAA}DEdIEHg{oVfiS(u>*%8Cl)`%-H#MO+cWDlW2B zv?#+i>5spmQd($CCLk^}vx7>;wgzrvB+3YL467E3^{IjZNo7rku4WuMM~kIdZcKdu zDhH^VY5U8&OCUA@T@D3o8-{~grO5-0Zg0sb#sg5b>aO)!=80~6i+WR8V{j29nL%sV^UCrV7fi~Psw(O#dw?%{sg1835%4im4Y=n=gLjDgM|c{A{7VIj*6l#oFk$wjJp-l{f_qy?0-# z2a$`6P?epksQUajWg{1&qHQM(|6Rf3J`euKx0!?ucN1df9_&x^(0@u|QJNcz zV<0k%BLa6swpS}27!vXrL})nBe90gvkmsp1z-6==f^siQC1r<3OpL$s9>YbesuHB* z($9b|jDTVM`+1$=pbs>PhAU*z-3$ zdm+Rj84y=9v^0(u%huT7Z^rj&`n>8=v~VEnpKG=;|QtnmylnPDU4!HVou5AMmQ zJ3>!PO$gy>@TbTH5ITrBtqewd?Z`c&ED+mpm7uZbcTulDFKilZA`8~eHZV~K61r~{ zYn9BO{8&p1v;r1aS2>)2E!={>0UDjJ(E^|+6HL0>-rf#+@k4CTYxWL|QmA#g{4Nu9 zl`7uRU>W|F`BfM0++3bJ`xqs&|EAGD_XFDC@Cq#?LX+Y)@ytq5-Lq>}mwul=b2?0d z(q029sJtn}H}w+zjf*Ff@0k`?@(1Mp6->72tPf6wyGEk{X<3D~xz+7fChi#)7I&Th(6K4cVo`GHm91_Jpt`$e%u19I z$h$^0@6r`0KaeKFy=^i;2{c5ZTPe-YqXwmvlR|MiYkqSwv7YD$`va@7xzO`p(e_o{ zwMNsW%!VwtmDunwC~0vS)y0jHvcDV5&FaXSl|JZI;iVd%SSqQu%G-@ev6vwB|I;w} z8(z~tawz?95tKz|$&?XQZOunh{){0Yq1p#Jiup~)L@wMfRNrsJzTGPR++9o+X7mQu zi>l%OTKSdLzn5NZmKddH-ghP0r%e3?`uQM1qzHKOn+lUM3ENSCqpGjHpEg|aN)xh> z{~(PPduzh&sfyd0t;XijNNQZ13g{NPoYOKsdi03T^*CnxK13yR<=R%kH4IukOmv)=&ZLm40Pef)w4(xXo)kuHh#wx-)Tm4ZHG6-wFLvmeFFH4JojPkwn@G5bAV zks9>$&fMO(oDWS~%~Z>jQ$r?cPU54dL9ky_3q5jhu_-rjh39+3JGDd_2cX5RFvi&@ z-PYrHNxuU<%*I&(k$y|p{pl7Ti7z_QEo4qr0?>N6fvy%LJi->?9kV9r$_l2&=o5PX zUWwr`Dn`a)(9nAP@fM4WMaw;-lKsf0HI^NL2b_2kZPsLOMD-=U_n zHL*{$Bl=?~3m2nDwz}p&cWVj1z^XF+SuT|IURyM(0#O(&tOHU{d2$|gCU2&}^L$w9 zqlgOhx)Fkb?70_nsnZc6(=%v<>>sD%nRI07g^-++0DGjP?Q?n}kDJ!|AnKiKoOdk4E6J$NxM1&_q)8mtu6Lk{p(N*9`wwC1D z`pBEIt64{sHxqrP=YRZ6;+Lw9`&g+?6Bd=^w_G29t%m179S@qh(jGPrNbzDHJLX+~ zHh>;6hxvGDoU(^A4(O&Y-20G)vLDpy(*F7n0$eAgJBW@*>Zrz^0ez9lm*`3v=%ZmE zP3Eu#KZ~&#%o?O+M{ZNxrOQ(!N=&N`LoB10mqUrh?z!RP>mfh-cFx1~$$L6AsuKza z3%fE?`Bj2b=;h0mde9wA?^*}U(i*DlLniIyf(H*C97blUmTKFpx(`S$!V2LK&pp=K z(+}#<5qdr3-i`vfvkwKnS(IR9x`Evj2ibIa{8UX2NlSyueiO`ktRsU^qZsyR^>Q917s{&>AhOq*pX~p^7#tcppj-6310q)B%O{o-qU~dM*#Rh!Ot`il311&eeI0J zSykq+>-8d2a9eUTx`s_MNrJKU71E$RE+=&OFh*mWRcUZR_yg#`KeH^DC1KGM2C+c- zs1@v7eZ>ju6YW_8Y$~?Me^9kBvr-S&bi|t{JzS{a(bIWo_t**BT{2SJQ{;y~D4|gw zeKR598ZljCody~$ z&nbO7a6a*qjEvZ21d71jakk|CW)Cg%IFvG(pZ9Q8$rE9kum>Iq^AThcP&G)5*_5oV z75NU?PuGS%gU*cH>Mt~nfT$sy|J)>z)o^_%OaHlpiliS3Y8CWbs+!(|5TCq`MFIkb z60f6;q7!?s$KIL$d={NPc$f$$s$4l+mAn&XNRSJ%y~f-Z{=ZzypKKy7tcIeQ&U5M=$FG8UvO?cFk4^~@6GjNei!nTmk z45ZpUr4V}%@>j8hUqIWr zwtp@lhAA`pXg`hWiu6O)k0ZfSYpZ$|{s*1ubw83bV?>j{C9YKRZ+nvja97uuldSOH~bdfu^(dFhsSNoaJq?! zd6-%IkXTAN#^N9y>G0!eIEBIV5Q`_w%n{WhQ$*lR zg;k?>H$%_Vq$!{Ewm3*CFj3!E!>fOCYqssAJ`=bUzoAn-(f?isLDbkYo^+u4BjI(_ zthSiSlg|W-%xaG-7YQaPlvNf9^()D0_r}@9&nAJZ!x0u17Ql>uOa;8-RFh}ZuBI8+ ze7lVTP7klCwnFwF;26uKa}9VgTZt=<`-18WOd>=tDJ!k#Q*qNHfvl_h%PRk7iczJ* z3sC$*BJNWBI-wbdzQN4aTs`M)eKqP`S9VJ$5(-iAD#?A+NYjMD-hp$K_w!z@f#`zuGSO()z8iH>o$9-SDE#Ej!o&D;`s;> zau5Lwd@?Axf6Mo@gc$YufmVuYh6=&r5fYs|(q<>F(x(GcROMoSa9W^zh_z-L@0LJ9 zRTbghca8c!R-*OcOX7+(P|(F7IUr}kZb{9gT835$+=O*f30$7)oZUfdddBNcTih6)NTLcT2(mOl)8!Fk)G zYIJ+6&dk6dJ*0Oe%Qk&`od$TVEYz8mzWzNjxqp&aD0nFT(Q}tWnkxD!xwr443uspR zvl~Wz<0u$a(%K4)0AaUjr{=&i%>xMaurgB8{)9NIG=Naqa=ZGIx$RDWE!yiJ{1~`Z zY2CZ`F-T8e|7!|w?jeh$VB9Mp@B%M(Yczi(O9Fcp-h6I~0GZ*lS}%)|Fy3EP3GoSl z5Ypc1gb%E^pQ~A_i@L7}Xp|<~L?tFl`1T3lwF61*!xB(%{zp<9cOhQw@J&YW$-nEu zj^24i!hyl&v*Nbe+KwnoY)q|fth*}=?h{4}6>2Qu)Zqd#pyM9TsBYZyVN@E;1h5>4(idNqXWZL`~h zlv~1PglNIu0W4;vg-4*6>+I;jVQB`9VV>>p4IdcuKGGefrvitaEBgGOrvMLe{gG(A zGC!V~nHhV~-J^(vM)(jHSCr3`Dg>7iG@$5eA6iBDNC&?XJ_;*B^>n1?;`@I*&uX?N zQU+u2H4sim$?Stp)LEhAUn-6x{?um40cT|CFw(-a^?FE*3}~@M%*@OHkXw0-oB{NH zKn$e=PD@~HYg-;4AFse#$8URayu%)OE)DIm_}`xEgKu`ditxkT0G_H234jVeSp9%x zXRVG(E_Z|5CNQA%fiU{Ex3xcSDg{~<+(3AiHY>=lH8u+Un#w1I&8S%erIo-n;_TS? z{ia$UXl*0^H;W~jYo&;P7ZXJda|KE4<;5j?@Px8}R@ixW#3dha3D70&wLSrA-oyR< zb0ATs`amu!EF5t!iLk(cS8f5wNTcO+yZ=;j2G8nWdavS2Rm;4vFO#}s*g*M@j5@{# zs<3o-55kVEg0vD*cLUI{Q~-9N5DC8g)rKKSnJ5UHkX|Vd3ObW6Yw6iV_-5+r>Oi|I zj@=i}Bp1?``)=P@bwRW5=j^OrPxbv$%DPFMzZ7Cl`sNP=TU%TzUy;WH-N>+rRD_nW zHXyXRmSL5eXNM;ptbP4CT_trQCBvU9mzEQ>@{T#AIEjs?EFFS%m<`vCvZLjk-m z6c%~#5D?9vgxXil`dvb;DZYIyzy#$qj%*5S@K>i147!wo$o`KE+LGyKopAI-d^6wKQm8w0c0b+w!S1rRl|6Cbh zderkGDeEV_GZ!EX+s|)3$G(dRKKKQ=Nc*rPYVCoo$ddL;PaueZen_U7|1<5N!`T~; zO$2lbKY#!d0UrJvMOj(d+F(kwMN`y>aUT$wH|qKFBlV^@=zDSdZe{$g-F@_HyZ=+U zv&Waanto0wtONr(%mU1@#cYP(UD@a_djLSS zh?p3Ch;$l%iV7<|y_D%5Q11OlWx9X`{P^88*=k=9nk4b}FF{SbrzrcF}K%D>TP_gI7TR^5{x$NHqO?fnA zWFC)8XBU^cOZQZH)K5bTbv8yZhfkE3Up!?s=GoB{CCb||Elro~^u-1u#xm;s^DT)sNHfWd)!>xx( zBSOm!dXRDGzIh#@A57|p_rN;+S`dsX22ysMp4!T88pH&)nx~>{k}lB&i<*63Um;~~ zRNDP1nElPzYx+T-mCXHch}Ez+;>Cyt65t0&{{njq(0eD)#VzA%#0@Yu_*4Tso?G#K zgymf@FoZ30@V>H=#P4hGc71tvi-SBk^F=s+Kkt8|35MT#VXk2j(O3;EOw}=k&E>YoqET z>OPENH*QS?KQ&rL!68Bya&TDZDA&Z;F-L-+-|ZYUK2s71p019wf(?4QX@x#nB`SDu ziNl;ERsO|W`ZsqLWd+RgtPxta_;2kdz>ByH*ClZ8LpJ8B7QY{Kt${3$&m5ZD^fTo>3nqu{AzTmZ;j8Raq#47~j$j zl1Vsrpai}>%+sWF4-h)R*re_R79=7K^j?DbCo4)tA;`p_B?T~-x1qmOQjvx1X4TMX zoz0W69J|6d;2>Fv?<>H$z?_Y3%B0N@YV2v#&4qvqj4b4>e`=Tow#~hh@6muH{O716 zJj*C-A8I`Nt0{1*!P`?6$gHWf)yAb&z>3;QByDg3Tif^FHot%u$YFkkdMkeXstQJ* zfM$kA1LFAd;*+2+BCq4NE(!!<3Kg%!lEVwR>(o1+G=h(OCq}7>1R1Ygt3C6B6$3BLm~{9r4@ zAX8`O)6erBeYjrHr(oz1kAUC|z~;Fo&$MC~$UY*NqhnRFynX<&LgedtooCez(fGSphQ&rI0SMG z`bKddPc(TJKtm^$5n|4u)A$99<^lW-ULgJfpu3wccIyEH0r~`n<}L;X27pruutFu~ z!t6mEFLM$QfbN)aocx?8ir!W0t3+7I%AzuEsAaHX`s0J-)HX0_dUbW>d3B@; zZtEw&jV9OtFx=eS42OjN31I=VQUDZ@w;&uALN^R_a}R0DJ#Rd+vw;m zf9LbON|D-Liqm(~nQF!*?qc1azaMk^>cFN3fV8Tj@aAIv=KTB|Oo7HTLVgPapWMD| zyR|_J`fj+XsVSz0!(qTjfG2S$gV)i&(H%)CDFtv9kRg_CMxmSqm}2D+a+uLLdD9;6 zt|4(>sPJPb*P6G3-lDsk8*qpoTnNMnOlyaOd9?sPzenA38=7_Yv@|p(puPuAPL!CK zXdrv*7Q_4Nnc*esRgzb-%sSq^dk5yPDvFy?T~8|D zh*no-H^_T#CO3ggvXI&I^zBq3GCRO)U*!a@+AHB+gyOR-k=>kj z0XP9nbuw*U3dCsQRyx3s0BlW|30!x=&%C;9)>Y;01jF4xl!nWo)(j4g*bJQ`uFtqm zNNy_A@A3}_7(uH}>xzzV=*^-AU>ie;FMe{h6fvp22>(YM6orh(RJGK)(g<0Pgy5b{~)fPN|STK4i59l;1*& z?}K1e2pJG3>06Jc@vJd1Gk*dk1JIiSBPJgTpNoGh(c&F+Dub+-gJ%AxPt3xH^_ZYK z*#bS|G=6s=aUTFNhZ`7*z%}zaCRu=$ZdmcsBrl}RH8w5P8GtyjTx7@QIX$HATSc}+ z^b;_f&Zq&=vjhI+d3}%u8V{T$rgpfHZM-V&ViqYzvr`g=X!l zZZJ!%4krC0BET#CW^=TuEFd5-BFFziZFjy&K(9RzSV*hUEwLD8Vej^RXXXjW4wAVS zfz#F5;+eeBEmaSAuM7ml+P4xN@QV?^`vjhBpy6F8m?kbHBoz4r1_FmVHoy{m&enUz z>w0Vff-{vw)K6Hg?YY9^m*GB5U|b`NXbN~?R!+{a)Dd_G7BDsBz)vl|eQW`=75EV; zD(V>JC(2+AxR6hKgS#)2Nx3mmZ0VhT%O!w0GQ+bpon&)M2&MhNE;U~+*z9gvXvV0!=* z{E7gYdf-!FMzI>wuKVqyARsQ=bgJQj?cBk1@TAIuBN+542j~X;8i&{5OQ^SBlH)&n zM;vI0&}swn0|3RHot-}v?IAYz*Q2vQT(EAs>csWpjY>@=B_s@PnumFgi%;i#8gB~Z z(srApwAB#dC9o+#7lYmfg9e1OO^nQ)-F_WfjZ?z}I__5EMN@$fD1Qw-bS4Ln(*jf{xM^<-D88qrGwPUy(KM5k$Y zZEdYcfF5K20~oLd38KASxEeB>l8}IZ&fHTCi?IMU0sQ0VSm6ZFl}-{ub^!%2lE~|1 z1?EtJ7c)0q9TU0dz0k`LD>ME|dTwBQ@I$5=^4u3Z?POzwnumulk+c~U7%uFGLByWM zUvYr`_IPiP+xZu{2KdDxKt>PL(m_mve92&E4L8>p;4y>!{RZZc8cJrRa3dhe0MoC! z8s7Auz}zI56IeQbCZ`J|$_9BgDu|dsvkJ7(8-POr(Zvc31KHKOUpRt93w#m}2M54F zow8LV43C?u69A%aHKU9W7B#@&K%tmxYJOtlXfhIhLf zI2N!q(>yvdauTO?s+%iNVS~$O@Zv?Yz%}SFyPq%YNW0x;ojA0*mY84Nbv(cUFF>YT znQ>-^X^UgjaC388VImy8J1?~@2$^8YQ$C1Z;rpQY8*S%BKOm3jaj$dS2?iTn0IdSB zLBd-d(|fV;>|1I14fN@3XXgFNh}7ZecS_20fq%}L+C8D;Iah8dwOX?8=y!kvyxmE znD+g^X|1wLXKf4skuz}T*kb?$7k2vnYV0?#_+mi3P*w+LdZiY?qNC>UDDktDbgVzAe~YY(k-PR-C)s@!Xg(bpnyrc1*9aT1yoWHq@={}Ug|#Q ze821c=WtzT@4eQuo;l|o;~ry7`P}xzjEoFehVuX;x8C`?5O3j!aB^~vPbK%wOnv|S zcI~GiC+8*aU+8WKp6(pf165Wn-=yb5Ip^PSel4Db%I;bzES-t;r}sEagO3y#nV5ur z*;vb)Mgr`-e-ViJPhhwhcxNX_V;KKC4DuA~3#Or>xB?%?gipq@_x(F;|M1AmADk?j zJ#+lnOR!jU+gG4m1M)Qbpg$652aGx7+M%^U`2M0SlI{-#rP&}x)vfayD_-c1h&a)a%K3AFcIF^c; zd9FgAb%_i8LC_fOvw?#JSR6pO?1BI$4vgsSZC|D=>pQ?JAA?LSC6x&|G6%jN4k;+` zzDaw}+QDgtcAM-I9LJwS5ocY^SU;S=yadAA8^gZ1TU)PxD4rc&Z>GVJdw%cGLo7zp?ipm~BI{SC^63V14r4Gu)g^0GI-)u>rwEl`65UG>tX9ODs8 zUqU!f9RRbQ{`?7s9lhrw@qHnW!U56C7KI#=I{6~d1AJk8b1(YJLacZDPv&%hrz8Nc zijygq!iL)R>o7?mAS65jr$6R6MNwOv2EicKC11J=NAvyf`rgFPqfJ0>Q~&#YK5&;sKhD|0vJ3odvu%pl|+ z*hc)!BsD}QTq}$??VzCM!a~;8d90%W&^f`$UX6fnN`Y_62ci9kIvcArE{M-*rKMc5 zYAW%u9Eyt9&R%9VguCF-=Un7VGZ5xKpQ)mEp&L!K6?*FGm0E!a=@u>*5)kFrV6=eERF#%dJnr1USGzdgSQr9CG{i=ks%zitv%H z6LoW|?@N|m0xD7V3*9B{j3c%i8qsA2)wy#Auzn4=7>IKrRI49fpD6u08iK9aAccXT zp{u%`mXach?$c>7NGvQY78e(x1vaugbjKCyoWGa2ivJ1S%|T#SyL|aG*-H!{*}Y(g zPzshCGXBd@jVnL+PclCev8|0%zxeVyoJIIARc}Dr29J^!@3~yX#zy!);5IUf!)AsW zxuF(%^BJEu5P|eXc*d8QwaezuT?8X9Ol-XRS`6w7;2Q;ktoHpTw8Pdns0`3!0uBPKcl0X9o*!xxIMPq|FH(#7@J53Av3|Vjd<#$k$?a^aU{8aV@ z=C8W`7>GZi3NVOlH?YAIWJhm{x450{_Mq%C=aP0b*UluhtZHp1AN1aUF*JZRemC)z z)j;;T@%i&5)`sjL!TC%K^fXN*#;xZQ$nYS1L>w;S!!au$X1pLID{J|4r92U8zN#%O zW57mEX8niw=M~U0Ab2MtI5~V;0_oEpOD`$WbG7j|3v>dRkUAhJ+9~b1Re(MgumB{Z z_I22c-!HYYc0$XqA<`7pos6VG(1Paa&Vnj^=qz)MXN}`N&qg&b-zUM?uXk&I; zhifdJ03H+%af2OzqWwO>$IyPNy0K4LwuU~hA`)GMp6lr-h>cm8X{Xk)iFBDp79GsjG_xb0!rvOqi z{U#ELALg)kaDa}}=nUK9ZNpuxBq8AsG56Q;L>ttZk8TrUsG$o|6&01ju!EcthX5|D z_9R#W;*uY6Q2l_Y4xX6J$NJT+27roo%qtvG@Y!8tKRsC!Y%^va7VT^c57au)#xb-MXct(+}`rxqjidtu5R4 zcy<3gA>GvQGoE&XT%P*lBOol%wg*vP(+UchX=rE|8DDzO$tl{P@R~ZLOu$P8AHL;PkwPDZ(LgUgX8iEs;C?-#4BNoqe_a6@XPK zuq8PiwWpOPjJ*&|7iP`@qNL(=BIQRAV7Op0-u3r~+R0rWo|vF*V~jp^hU5rbPk11n zdH0DrW-Gi9C;;;M6$Jzkj|~=_p>(00 zc^FIF1eH3-5~0P(;?nZ&@9#f_(!w9;o3mk2oWq0vx*EFD0geZ~542bMl~%p8wh*dKv=wH$2DJeeKZmi+wUf3y9%s=mb#U*u8t1zYZWDfI3Y0GX|Rd9 z1ikMO#4Ab+Wlys@>m;0~olY=g0j@Kz{v&ud7fc^t`w3;GCojeZ$g9$N;8-Puqz22sX8`*_kxFf7Z zIAEz2BllJw-Tnq?%1$my9vBJjrrGkr;wM;mxP!1mfvYaanz!2CorPHsILZ}{HX@U! zxTa^DJ`r6HPV~SUC9)dhlCT;CgFsjh^!|iDa1i~3=0J^kg3d)YnDtcX|MaUH1RW4= z;wlV4uX3bq2CYeVcdF0p@nXZ0kn^vU^_N3FemoCQ_@{!K&0#+Sp^60P;~%t(96)fK z?Cgt2D}@i+1PxoIQz2Q{(PFgg{dt*q7e(&%DZz)n%__TM>pm$e&SvMX(|^{RJ_?<5 zy#wwa10POCsp+TQ%wR(Y*cVYMu&RJ%(Fh$G?1}m4=xBH$+>Ge3llqYi?03ewkrQn& zblsEu1+#p7a}nFK`kPxCt*wkorztgKtE{GsM@Mf+m~Jig(SYrvWV?a7ve`JiwV&{u zWCR2#c;RrKCiO5TV~|L+E*vmms;HFVx1+z|KJNhtr)Z2xQFoVwu4?poTIzSpYP0{p zSEM(yoT^b2KZ^qj66-OX#>tx+-|6InJb}9c{em@EN5NigaKezJW1in9354{G9^1_0 zMT&+(R`5)J2`5b)U>B@AQ!z-{f<>R#>LbBFsvkd9tU7rR5_IP-(*;*@u$6rN9YnD( z`^RSuRrZP5_=iX*B;***@}wDt<8m$@vHvw{G|z^}?LMdFYYwJoI9F&;_Qx zB#rH&A`S%AZ(Ugh#$?>L?>6%WH*VoMq$wX8u`O^fsI7kfcBP|Z-Cw`u0fT9GK$nmGD)=8kE3-5M1r4BA{Wj8X0}u<=pvFK?dAu)Re2Y6c zB)x=N7kfaTuaExVX0K^#dh()!L2N?8#U_u@wuQD|Hs_4D1WdnVz4>`keS4%WabW=f zgT@%k&rm6b!@E2ZX8P?pi4lz6_l!kh?6>QX&Q4D6?a|bRWeSS3=C*#dU((=pu?R15 zlQhBwdk2ax!5vVV7XH&M`jP*aoRUYDC+Zy@@vzxagz}oYrM3U#=76QAa@)=y&P$r#7>n%F;L-93=BB(0W#^ch z<}fyirY?~L!irIxZ>6JPl3yB-Zc4gKLBgOG8}EI5>=7hpFbM%9bs32~F>!e~?SXpg z`J_h^>biO~YA}mt@?bPRKV{02^a2;(wiH)|O zO#4XoO@~NkmFWDdqO*fIu`!q~Kylu8^S@pSNbTnpQ1aYbudG-NuHFOI-EFA!GhVW7 zxsrK)OW==$y??#W?fGzX}*Jgx-8@f)eqpv+ImfmxN}^0bZjxd)-h_s zgnplRxKrwQNYK~)){OyB4 z()1r6gdM;$L815p;F0MUFpIkmTRNwh;`Q3_;l4!uLt@u3Ic_yz#$|7Aw0^|@sF z+-8%(T_n_E=KNk$jd5oCur+~oEf=s_w~VmeXFH)EB?nYbz+-?L24l0A?b zL&7l{ahovAJ03OKP*%S6^t>aP_!s~hxcE4HNq5O=;>3-9X@rO8ot@Z%sqH$QdWt@J zZ#wJdep!+qdu?3)YH#GqMv(yc@VUkU=G%L74R0yP?BQa<`MB8ZYrH7M`lwJW5Wx_$igTTx*ohGLwX?Qnl0zl` z*QGK5jc#09Lo=UAnqcGtITW0M_4V~1+IFHJ^W6Ds)-3A~0frQQxO3f zvH{b#$U7M+ZX5tr6WY*W+z=XWIArNQ*cC+z_0L&ZTv2<|NyKC~=eF{t@m8O! zN{*vSmLK-QIF))zkl^XJ!vxzwUkokt;BRP{jww7H+7mr;)9FvxYJEqV%Lt@?O7g%M zMnW$U&L$X$+QBjdzt47$QFd?iB} z@mRjHX#KIal!TS}Yi-F&rxcEMmSL1~mxO6b6Oy5f=l^xK^nT%;+qi;GFv-YP*uf`? zM`|Zr&4&T=4^xKaB{Sf#xnEl1Vq3?P)^7Ik$hOu%b7-{zo_#26bpho4Xa$yQ`QM)? z($}@yyFOuFi0i#n_Hcjcp$gD@KO8x>n+~JT8JXmm^ZC~7hr1K3%Y1Ez9I))2_pl!{ zT{haeB0Gl0r(|28C<5aLXv~m7mfPRovhqH+1{>ugK@t?50PlBb-svUhp!Guo`F0&x zM-Q32avK|Kc>nx@5Bs|kFLEdQMhmj`e;hdD58QJ=eV`&A@3^;^3r~*ZtsbGIr+0G- z-Msh>hj=`Y9$a>-&$oHLm3l5{7Lb(8;C>)XfH94ocd{52Z#nVy?JCMEq$ixsCGzkP zTzYwCFK%bcq>b3O0GOWaJu+8iT1`qX{slC#V`qRl_H05W59b?pqh%FjA1H>+wGoGz zw)ZON+?%RQ?@`j%%FnU#h%255q1|mSE%CB3;7Z4P_|aFY|I{gShxo+(KFz{qCoM~MD0lfc21Gk@CgE7chQ8OIEhm$d$Kh4* zbN&0x50{79R?pErv!A`VS-mk70X2W2tQ5-r(mJ66uGzmU8GS@L&WC;-XV7b!7!}?< z`(Wh*#<0Dc>*?>uX?0<^R$lwmsocX1( zQ0UR ziJ(M>mfTy~y8H zl>D$kQ!who??p$(!=igUD21vM*dJxyFf1tzHEf#=hw{zV*7p4Q$7pvkqjlRO_+s!X z1@;KrKv|_0;hECy6--vshMzY=GM%a2I1X{R32`Vxu%d8M(uQwSoZ)5S8&Q|?74bzE zvEk5^(Pm!5RS8pmq?s_hLx2^<7t0pQm7Wr7a>gt7#ZSWD#qZ{mu%RDdANA7Tc_`zx z|Ik+R69CG}mqHOP6H&H3b;Sl$P2tAcE6a7e(q*^Q=*b5%pBa9cPg03XW+T96oy;er zH9^JomejyMrlzmT*}j_I)1yi7a?ecw*L4+=H`H$iv;sU@AHF~n5-A;W?c5@5V>O?N zs)YFQ;Wj<}Ql8wH&p7IRYhk#<9D6ecjX*RTaj2cYqO}2%lA(!Fdby;T%W)J%QaIv& zUM`}Tx`F*vHHbY90shcV1`~A}j*ieQ`6{=MegAB6Iq{zIe;#bY1l6AL<0Io(Ly4gw zR8tjKFI(IVx|sff0?gZ zwlULkzR2cfWR@`PtM3|zD~0pdnu(iz4#^(V#>e7FdYoHWY1vvSoO;0CjQ8J{WPvpD zD+!uu#uT;`Vvmwr88=c62Sf+cfNc58Exltba z2Juik@JVXX{>Dq!ouh zsr8(3xhW|yHtbp(H4ggbsJqNrqF=c8YCg%iXLVkQa|?fOkMjnKC+tg4!V_nHALEDT zhbk@i4O2O`@wH7%Q&XNj>(?vr6EGj#`!U3Ct9X5w&0+CnM*wMuQ1*r1#LPwPJXL^k zF5jh7u?2;9Dk%r`~!6P z4Rs_Bmm?ltwDr54gZQ?+_a#$^thdFV_;<(6M}PhFS@QbEKSCpzHk7|e<361)!=|6k z!MPZ?)0_T4`=OpqleSFU&o>>Y3M<0p{ z7~i4Y_O9k_V#Fn@(BE*7q+%qZ5qL?aOfJ{1NA2DhPf|4QS7pZiW2?({(0_=#P~n}K zIKJBxmx}P8`=6zglD03HQ?_5D-L^1zeO zu*{vOaYX)A=0o9YWp_WmUOd0H*xpeY}f82gRerfra6N3I|CN1cU4i9Rd&>~l^5Ga;uR-h2_{ z=d#A1i4i;-3;eopo}WkK=wh`26(tMwqYMmH)bghbF&hk0k4$1 zcXxk6X6aqb(>TiQZItH}{^bWo>1SFR8!-Jfg!6%k*oQr#tpzsez^6l2b*gS*7X3g9 z_-taDpb|&B&O5>%PJ1$h)X|0ox&2;w|Jw+scRV&PHHh1Gtkp2Gi#pMpCI->W-NbO` z&bvx$naw-rU4utztz>8fle?)_g~_jJ-TYRMr-zKDH1%d2ASMoV=!*cGTSJtVw9j%@ zjqT%%(hiYwPm876XltZ<5caZ+xeMbrR)bwf0ycXUU4i4n!2%}f7Oc7DN4w$6IZuyv zStAg#5{{Z0^R3Sx-_7~Evh3`bS>rGu%|H_%J?CDSnyTl4>lxJK^<@`uX#_s)Z4=SM zAI(W??I%#_Z+D-xbEP0 zfU8EEXBBZeN}8idLNb)wt>EhnCvOFH!?e zf~Jerb5vAhXapi>zU;2}e-%XRufCs^d49|A$kTbE4);Zv@3vi?Dznnr;$>F;2&mtd zbNeO##I{aI;V`ROg7NUi#s&-on%&2>#cH9r>WIcY^*DjtfPUdqbu?96-;r*?2yRj$ zoOG3GZ1_>h@&kLjw=a5GPDkTQW3kxk?ev@3erDGsE@%N2G00!WY&#HL?2)J`#0SN%EKzRyK-`bVWn!+5E5T=wd zIt28TJ|VtM&pbPc<;YJ}n?2$Lv_f5{C7LM{+(qfR-aOT6=7V3+H)WhsQ8OTp!gbOZ zdSs!fs1@;3op`7-rk^rQj{oJ&EYp55zoo)jjc;UL7i#iiFUB?^Zh}AW!jfXjQ zUl~z&`~~ajnahzngUHco@YQJS*Ie8a($zRU643TJgSO|mp>*?SAL^c^#T!FFK+T*4PKLL}T;Nvw2jU$)j z1g}3&>6pLyCHKaZpM)nF>`v;q+|Wf;?^OW{@udXTh4U}HZ3ZghiO6_jx*b!9VUe48 z#l(*G+^$A?KCjhEzK^F;8T{dZa2W2Z7{3|3&MqS^J|Pi7^MF@T@g$CRr1dmSm^Ou) z3i}Z()I!V=@quh_A_2Negy-2yAx|`@zaHVc$9JlKa_Zk4pCigHVkN>yRq^26vGb(N z0&OEdg2tJtnQ+p91Qz6|L8?EN5PrBy&c)l- zA7r`)G22pf4N>$lc<%+nL{GNy&#w;j0Oh@a2ScZOyHvu%pyNWx*@t_*#)-Xb{?&xD zvLWJ{4~x0-ZPjj5y?u*!d(l%u&z37vH)u zX~2#sZ99`at#Bbhd($XaW7QbAK!~VeCku|I_{rXdexRGy9~8Og#mF6C@Vq041%E%@XM5-GX`2sp~CG83JL`_ zi}&NzkULtr{>Dah;Y;5D;`S^u#WX{W7O7x=NO>!{mc#iZ_O2?P)2Fc;Q$i9O%G74a zswMu=r5vwm`Op>Sr3^XGfpDzVcv5`F8Wr`{EMmU-7teR7#qX%>vBkNHi3+m+AjQ|g zbF+bhji9p~zZu4RXcQeyoD+A_2KB?W(C=?x4&3?iGfWRjtIX69nYtshMVK2cnX}Yr zr2%$r@}2l?@aiRpCo-zNtUAE>kcd7b$@s0)lm2?_j`NP3)B^qo)Mi*#U9*3_e%*$6 zPL$qJq+GXAT_w)lM+ipmNliPu0|jzFW?q%Xp!HZVmED~YzW)N7n5RFcJC6ls;a#1b z#m`-OfSm`#k~RQ1E}8Y>So9dKUKZ~PwYv^Nrl{x9bzs&xQEiU4#{kstPMi(ub5W(i z(UXdG|Acd$uWmd~fmI5$zVPmMM7*Gi6!5@ES8)*6v|3a~?AKs6kF5cP=9fpgHy7Ys z`DJ}Kz-mIEufx^WeUWn)5*q9l9mcBgdxK3Dra9HOxjX2`%D`U#yjkgtEiO;|B^MOCgC zf-^t^v^Kaz=OjV@2Z{jj8PWzBjoi(;2M_cCYdk>d5l6by%ij{sXmNe-0PHpRl$L2i z0)|9iG}!%hqKYe&auir3DvVOV*8=dQtvYR46(6Q)o12>?KaUP(e7IjkajZ`QqGDfs zEo80iBc9^v_NDz9prDMlc1$kD0Q)NDf$~djvP#wQ4VerdqcWL%&}lkcQdBK}^De$7 zx!B&l_*>*)FxV|e(?5HhNocfrFZ})R5PU6goduS1!^{?Fs=zasF*ucyfRr@cRFJCF zH97GaZ#!_>fiy9JGUS|Ofb$db#T2!nhClR+u7`NY*9W75733 z*ymviO~PQM$!q2SJhgF_`aaZSOVS8*J8+9UxlnEYn7u3bQ{(Hw`Y8YwC=fIa($=E% zr3Hdx0N(+~dEojnGZ);S2v&;9ipX`2zOG{_T9s&!7~Fzm%p~EglSIj_Tus8N$n-Hj zHZ}u5<>fo<@#!w)gEzm;60$b~;TP~EGr3@J)PVQ{bioVcTUNFsPz6~Hc3^SiN zy*qbE@XGAVL)1!SMa2ql6+WBt`vRueQ1o(9PR@GW70Po&d*7;uh!0t7Ux zUxCtXzzBoIJuev5K|?0_crls|5sKYfidJYcTN!Cz1Y8Q@>XB zOYwVBTmx{E7u-d+LmNEQ`|(9!H5^lpx%RFAo$yT7;{#w8p?pdlxtQnuk1aXc#&k8~ z`Bm2<&w?=;aI3!FH{;QCzF4G|*@1_LC%|(i(1ulF0JA4T?ysgpR_!cpHSjO=QYVG@ z9c)TQAZ_+2b6Ap&i6)~=Mf*;zz@;PJV=A<_2;&$va%F!jlB3PT|KeQ=iBP(kh> zF@Jd;z71En52(ndIi!gPUNiz>7u3@O;}hWK55@~0w_PhNA9c!yNa}){ z@^B8&2AE67L9v5gPjd1Fd(B)%78cU-(W=u>JIXb@U_+i_AIxu7g29XvB?JC;Z)ip|LN|g?wz6LufNvk<`kWwlZxVHJX;1pLWDm1WF>Y$y^H_T`dcMprhPU?9+jwU&gjtJOa}iB%^KEK8;5`#X*q)MR)PP z?Uxynl;V=aNqYraXxL-yKu|ZvZgO3zQg!a!8>L^n%5b$@ta9Be)33xnK0dA?Fq43I z54Yukw2d3hWpl^k1s@(G#r?03+f+B&ZhVgK!Eq-$iy=SKe4~14WEL_|9gB(E@eY1quxgHi zYYB@kXYSr_2Zq$T>+fG)$_un)(6<=EH3@*uv~NQFm*h^p{Zlow0m+m~VJzSXp%|^|@dXegXQG^+RF)SIF*nkf3>%`;7SruUHL`9vO6|7yM3hv{G z*j-37!+ysZEtW{Ez6$if4(v^m2&seMs=P7UJ2XI&F$J%BFIZ8KuBB;8r$o!*F)Rt2 z-oLc$1yJ3VOc-2=Agz_TTn|bjZL;ydo4#SUC`m7_Pv;JU29mOC5+5b55mWXqj4&D4*yexib)RdX ziK{XhC+U(@zlo+%#1)+j-DflcVu@*>z{(WEIG^E?rK?<_${pWi1NU

8cJUYQw?g z`0jBCtBi*aX$MB8AyTM|Ch5RJg7u~_xK=<+ZKpZ4&3GvJ1IAepcyQ*lMmw+6y*A=v zmWw!(ze_+JO40S%`0ty;B=;AU7!G!AfLOiGA`bhfGU)}j?25{-C#M{kZ};f6mD}IP zRTgf)HWabhCt%n*0okO(AM`7)znUu`hKGUBj#Vg{zJRlSci5wGhp z5Z6C!uf7Lf6A07#vlK&c`WS;AEgyFXToD{&jX1ploevD<&lm6alykQ~(^*_mp$=7F z(!*{`@hUN}54Y!r_=4|Me_@g#7$7XPdkG5*3xLEdK*Lo_3`rUYQ*#0 zVP7n24Pd4Vx3plF3v0W&xNwO)JeGvutGP-j*H4Ic#?(1k{Q)h2L|nD%Samc}hNyRTdvy=sl4SZ@&xer=PF8_NXZatjJbZigodZOiiV zx`3!rU8u%@J#v}oser3VS+X(CRjo&K<|;jeHJJ*! zr>!F#WkHa?By}$4s5Ti` zXW=+?s!LLRYJ3E*lKFS@d+{*2kC{1~oe^npJ{gHm4zpH(2|_4Ys@~5Ctxl&1jzLgU zksA1_mKHFYD^r#u9Jw5AreyOSsQS%rqv#j7Uw%F$z%s)r*X(+}Yq94`4es)+b|Jj? z*Pj%>Vf%d|Vh}VcxX~eoG-;d?6VqPJ}UpWOMoH#&+a#oC^kd&4O9}Nwe66xp^)!lC|xV3 z$<7~H&zJy50pRACDm_~I`t?eLis6bjxl>O0ss(Xhvxf zq8!5^zaBZBhTIgh6iw!HiCket-LV(RL+zg60O6|M!|94C0)l6CeaoweUy8!NK~~%5Fs`8@D)W);+;)B%3Rg-#e!RsscTRy)@bV{v zdnFYo>N+uGo>^%0hCF0)Qx zLYol^+D!C~RG=q(wbbH2c`5?dVnwRsNnd1B<5C1o2+R3R3Pg9=v{?DX*h%qOCxuji zQ>xO{P-_Wz@~k%SxPx5b1kSxY=%mtYA5nJ2C`z74nt-7sTRAK&{r=rP zBEB1ppa2Gm2LvuWMIr?u%crZr&9Bs|P?fp@KY3lAPty?v2Oa?+ou~z}v*Q|^OnXL< zIXHTKz@@2r-u}$9<-aY}xgIEx1x!#F2pwU4&?EqbyXW-QQ#XWZz+;7y{@>sDb#rqL zKsI31f}(xzOK&>B6{m{{tf>F{OLdWOZ|ZrV+SLp8^z}J-nc@FCM02<27d0+HMY|=j z3*#qd10~`~**~BRuLKAOfTtf2pVoH()sWD614R$=ZMWSDB9qRjff`6XKnd6r?imKK zLe>7v@`zgCx>1R7PT;#j!yy!UBfuOiO7D@3e|*;H+mH85QO;U|nmPsexJksB+2og_ z$UNO)dIA4GjBug2+}YiY38V0MLV+^a=S+T%NJIQBvKz&Dd3XRsV?EHzCHy>~$9@^f z+Dp7kG?j%2glRVf7pE&k39q^1xjvcPPfN)4!g_3@Qv}xr7u^H!R-K#!-3YDVBynsPKy^U2W&8)%=jiL@`lUU!mv&z5~n^m(XXmX z$)?+|MH&IVsWac)i#v=R5uW-U^(JxS#645Co-%%%pl}Y$Pic9Gj=n z-PthfVGi00XiIe4juHl072p=%zI}soc%>w|ZbvEf5Gb87^@gu^`UJ7@h{4qHfsLDS zav2T!CP=u27|DCRjqs|B$h#kUenPYlkF-)8o#Jb0gR)aoQ@ee`k!Y0Ftk%ff*P2VB z|GeW2-xx>Lm3%hS@f;$0Nq<>Y;usZy66#UKo6#|;Bc}=D zhKO$ME)Rh6)9um37+E=unh=6&lC2m`D*<2Q`YxRP;H{4e7aOtSLWY#}^hn1*Nd(GW zri}z(Rhz&agajmjohE#iZDM`dIf4kZJhN4ZTkW+yG8`tZ)Oh2hOQt4RluiC{AK$51 zPUKFsIDbI1b8+C~RPZegA@H>$8!dxbr|JdIFMTs0tKi2pAER=(mC+2`br{#XgO(U- z^tC0636KoIan z{iN6k>3mAbk(_^M#Vy8VXv!DrpcztMri10FA&PT6!Su~3dQ>u$b{Pp?A|5N$1f>IJhW0k92&vB zV>sdgFv0f%oG=qk z;f|R~oq^Qt{{=K1r2hb#?0mGVW_wNRh4!DWB23|pVBx#7{R7UuRpD3#Qv_W9^EP_W zHEgYA<~5I1Xb)Rn$!&8fIBFmSYZKSiYNFk`Rw`2IE|?v9LP;6CuYDX< zQx}J(O5q0nD1zgl1IPoQd_IJEp1FhpD4c-m1V&Arqz539k0r-`X`67-P@qGPx{9mR z2yyqFA6clf7`x~ibe=kddGv7t>$Tx=3{+IcT>55)Y}+bVO+E_r0Pv&FCj0W`OMv5U z!~F#S$YSAY3G$T`4Lum4s;0hbjr)QotX5;d7fHQtBvvGlxY$7UAqD^S7}0q6>G#XU z`bcd*S))~LKC_x^*-@@16q@;mjJrLFy-Dm^*Kqy0<6}#z(bSTUujucqFBEC7#g``Z zKLgiq9EB)s{<2ty>Fp!8yAK|KABj1Xw%jeW*f1er5w*iWH>aw=KTOPp*+a|)u#J$- ztWXAsoei&RFxh7O{@rrbpPwHnONSZ|g^R_`a=$B1dQk+~F2s;1$U`6{sRUu<+S#7O6vHqtWu z1Lrn`#JGB^?drNyx_u+`;iLmUsxWw&J_Lhni(R4)rd&kJj)3@R6TW%6C{Y-_RR*z% zTr}GeMO=@i(s?%j>nE#6N-lwb$%%5>@SywE7i3vIqy*6zW9{H|zrgqYi^~K#%hg(_ zup<+#V8J4Oezhg1xOQPdhr`qu9!vwNY7jr%Zg_KrMe2OK6kf{4p@KVdMIPpZ;2of3tGlwDqfbIFUJBV5bb82M9?Yc=e%lzfvKyUt zE&I@+cIKLpHgfaenV_6whlVPsdb_f59sEj&Lyn8ugj>s}o?rv;(F}LXg6dJI#`-EJ zBo7x4w(??}<}Y6yCy&~jF;7luuie5V&D`W&3TJP4s%isPW3kuOk=o>OCXhm|$Zx;z zi)}Ae;-i|oVL_Pz@90|oEQN~`$d8HTO6bHKa58?uWDuINXzz!>7gugkdu6|xZx2kFOe{l{ z)^#-*W);JZ@=DK9$k)($ZR1jjuorhHonAIWy_T}Ja<}$tj6o`W8vCFcO*E5szzVf1F$#nkww^zaDU0BMIwX0h^`=Hn z6IH9uo(b(JKa1>qxJsyV93%do8DJ2L3e}}IN%Ddchf&u$m{d(@6tXcxCPqi8oGG1) z&dNYE~b`oNG4X9y0{UN7c8pYkO#21$a@PIGL%B2rCzcgpA7pGYpDk>5Da zD{)onoht!c%f1f3lGyKUvNPT4$X^ZJnZCM`tER3VMKyUXlNWP8dklC=OA-^5wKY*{ zDR=G@G~y-i?7GQnjTH>vFP`F{kgj6WlI2^AarQ56KE!nY@Wr30(-S-*h%M&P_O>X<)NQ}`9WLwUR7`)Ag4 z;0|V={G}vjFy_E?ezg++VmQXosebKFd&@3p)`$JaLxxW+0|%jsWL8FUBu0DDtgDOd ziBjX7^XQ5Bn1(x!X8S3qEL%EUOmTvT_IppE@r0ulaJdn>AvfxpM9-qSB>-&~-Z1 z^`I;&%^su;mKDf^tO}h7%nO?!_-+Sw7BlueWviO?Ma-Vkd z#R=RX#lgh|McTDsIVe5hFja17BL9%fP4AaocZjhb*Qo05xj+G3!z49TyaXHMUx4k-#e9ACG)77-jEqQDWBts36P4@eqZ?9OoEnKOx&ZW zoT`9ullrc^A#a)@-FW^o#z0*65d`r4o0RX#(E5v)Mkv|OjeH!g{|IpqXmIeRWuuau zOLB#)#I76gL@!I?!=0VNd{j{p5_nD$o#!22nxV3ZjZ{YP2bLvgMwp76bzkb@t-+LE zg`5!m#2c59>v!L!B^J>?Ni>^GyO;{kF3GrL<*4ZNPm#{kTq=^3$8M?sg+KHBr^r*| zHx|y!u?#(m0VcJAM-Mj8$~?u<2Q#6Ujb`shM=ZP~!U}^;UV1Jl5HLqcID!Cn$|uu} zL&%Ms8txH-oe@WTA*ln2KszF^hzG*Yyy4fo?{*T4G$=W^_4eMX>2gJ-b-Yr^S!H1} zPN!qdNE&OSk7=a;P55X^%npPf6o*ppl&Yc%W>p(lV`8&S_r0Uy)#N3vX@WYyE@xER z01#aUNJN2P_fSQiOO}XhfS%_A=pTkd<=zM&zS3y%;4`5I1jX6vJNTSQRKYr9YGZt? zXM1d0ktpHf+mSlD4xt7-M!%ei`Ak5HDJi~0A1v$~#!-YyU8yF2*`J8=M12)oEigW3o16Osth{{93l z_z5Gwf5KX1Df%bGe$8%B$d4ZT51ZBC>2vGp)CQ9*(p6sMs*V@R zKi)>Q1rTNpj<^V2*aEZ4!}jIS$s|?o7w8SA>2Pi_I<<$80=+)>b{6&T1;>al=B5*; zRQ}!{J{@=e9JM+c0iGC>ls%O(rui(_$WUFXb1^+KZ>)RSY;=*iLAZUA0Rko-L4+!M z0Kcttg#+S%{uQtg&+NEvaKQ0RWLWAdWZ1=X>XhQ8XEPp+Ez&8SkA5EgdkfeInjm;$ zN;Qw|O#E}PC21*FoVD=Qj1ZanNtd5Oyztw4pC1hRS@6_+{`m3Zv?~wjz7=gjR|D(< zXh`r*&cApzc3U}BzxB$xP*``$Z1>O*Sx~C)MqR7Lz~mq*wUmqN$p1&OwcCOA2zPe_ zI;7b{VN@(3a)%?@!Xb-`$KAoCBqSxE9R)8@l9uC_GvV(oLJE8cwT?0Xj3^(_-G7wh z*j_w)+W-_rxey?yK$i|~8dqzsHh|~Tr6c$sFr}FSmNNLmMkau0J0T$<{S;yPC4DRH z*cc#+7|t_b*TAigwZP4Q#0Y}4F-K3AF-^Ve>J*<-et(Ymlxz01WA>FW@Ri6u5f* zmAjhx5%X=Q zX(f?r{P;TF*qpu)c#|L$Vu`dh55x?ylbrAEV5<$-S0|x{}z{0ec0T zriw*WL{9R*MEcS0{EIiR1Zr$vX8rH))FY+*#uCgi`1%J-}`xv=Qy7J>L@;+>w1sZJYVNYMQqw{42f!^rVTiG5e4;|d~tAEqHr9i zvl0>b0mR_iTMihHMnc)MM3Pr3YNlq%lUFKO`%Rl>6F#)eTBFgZ>Y1HfpKSe{`lpsTB6_KK~R>8oC^P}3lBt_Kg$Jk51z z%kA`b99OgAmrP?@_SYG9IfNErrf{sIr?XeaqTm0V+2M$!NgPISGTZ};%K(lv&D@3%8O$?XKob~)1&C(CKZPfQ@c!~k^1McEfSlE00z0OxfiES=cN2yjpU7Y}6 z0>&1tre@d{J)L2k_hnB|ytwh-qh(NF^fg@Km&{p>^T!#5H@9XN(*GD3a!K7PnZ~{R zoW?n7k&T1!>c77H&*K{e>pvk80z!iNAlJ{atYGb-Q;(jqlo-UcHtdX!i3yj|Y@vLx zKg^KkYx^2~Y6_nnngk?H({!XY+;+J?vOM7^ypw-x=k|*)MNVa^y+5`KpDD82NKs&U zYJev6Fi0?#Yj+a1^%rj!__aB7D}r$dC6o!*0l-x-{WUi?2kEq@plC0BZ8ST^clsW< zobDeYeZ-90zJPx5*YADWmBt-xPjxDicBy1PXJeEvM5I`tBA}x0dE5SO7_5fIhpCU- z4!`TnSR+yG(svZ|vNt%lKT(%ipyLW~DLJzvgof3aq_!4wyJ%o=`cPb1>mjsk4bE#; z8Cbs)V7z*T!$PoAq1!b+ipyMjzct%R)ZyfD$=5)1<%IzHm1G5h(e@%x*w~_Oaxt6C zZ2wxZ{<)c@6YEIc#LguD{P|P;+sotVE%bv)2rx#Ve2TS2PyFmPHN2ZbV;2eZqnWAN zWp#SxfNub7ueJnvYo>~~GXPQN-~Fn%+SDkV8apE)o0VYwJjrWYr9|%2biyxuJ!l=Q zb?bzc>k2fJmfIUZ7FS^4rW6GQ^MSm%9^#}QA55}^D4) zF&=v=B|4CS$@59Ns1$CXVO~B{u|`F;)Lax-DtF83%JpFix@?AC&=XQS1VyC9{%VQ^LMI? zBa1}Ta_qQ&obR??w(hOl3WNtO!d~Wk&1!KxM^Z8VbY4 z5RR5k=jl8VGr5Ncg_)x7oq1U+oXbYtS7a~}$e>Vo9NX_0V0}SpW5k-@;g#8k`&Yt2 zuu*Jz5AZLRCqP@%R0a<}T$`FrP%fSkRZ_B=PwLz<0#Z}*jqVHI;?JPOnoxgZgnx;M z7;Y;66Ir>7Ycz~w?w?7~cl7E^V9PIpz>G+Q2>2cCFSDQk6FM<7W~(EE^&2nD+3{ZZ z6e7e_$DrE$aY+7m0H=7@+2c~QcJ6h4-)qtTSG36v+)}6R;Gcj5#tdb8(PL@yPPw+! zA&z0xSFOtVWzua+MYJHr);~H(FL=WYpvg-4R*gYh150-d*)i+pO|X3x_$OQw z4`mrq7ZE9fziJ%-)ZVhxLjPb&D9uxHnt-Dr_cqFRM;k0{5x&`+UAgKPtTml8`j%Z$TC)F-?CuVd_PA;b9qKr*;3{iL}|qN$5M6n3hs?9kvV4lZh&by53y!b{@;o5xoR}{Go&TL z#t&W&7U!Cp8=W0>{oKrq=kepG^mEWSZrwp}8KWn8Pth0vz@mc1-mzO>;vPMGi1IE= z>NS(gG-jukP!JdEY81}hF@IpvS@1Efo(ke~_YPf35#5$+$Aw6kmYU*3^DuwzeqyZ) z>j;IJ#coe){hYlU%hxap$}?UTj{l!6zbJ!$TCw9!(Kb!VtO=KY-!i ztt=x;?*~jdw>yxF(!SBU3oic&Mwyn}Hx)Hz(Ew^PN-uO#ynIc!ULe1&KL2zX(!_wX z?S+DOxAl;AR7Y7HC(_wV_Stw3kMMtDcJ!SRrHnnN#dtR#P* zY0SxwcZJkYVTx)w_zGh`DtQ217izI>3eU2`+{$<(oF6tx3NBrC({*P0wWts;xX(e! z`B{^s_BWDCso|*5h&WW%y1>=@B6q9K@xKwM+L%Z*SjxaC4R z>S~_PfL=cvVq-@-IiY2mmoSC!`yTwcNs zbit7&Z0jM+<~{Fr@(8hS$2JlNz4RlSYcq7^pF0Rd9mi0RTLjHlq$@2!oEn?j(O|#N zRt$ajFCf$Hk9{hO46JIeaX`6i*H5Y7$G#$!LS^QB|7ZqGAP|(^Iur21W8zuNVmMZp@ zJow(L#!~VW7Em%LT2LVxkB{rXB8KY#u9x4}p}ALHKSytY`6po{Yr-@sh#pCr{$fV1 zEy1MRUv-a;&8PCpT8&APj_-&O8JmQ`mx@VcyJHB2P|37P^Pb1|h?TeDFihAn=W#wg zmA`67l@MokZQS>pen;08-rdep(t?(ql)H!m_@aS=;|lAS0i!^jz@|-C(Bnm^?mGbD zR%un&c_PHZlrNp;l>`o#5uh@}xksVyfR=FQ3x=jopseT@KnEVy&Bxbw2|9B?1)DVMruflfr%jp&U9s4xa57 zkOO!Y!%K&bOOr!XZ0vdkXEUUgXfHl`(Z05TdFL&(1Gs+u`a;zVBNx{II!8dTVmr=NQ7d^2X>Z%M4QQb_l5AWz!x_JG)z6)N^eX4Ck=A z=x@zVO%Z0#(^Gc&6-8UmO(SmwU@Cn5{?OB(sD@~W-+uJ}GW&k!0+@+bX5ZzQ)~(wN zW@@w{iHU(KKHM6*ewk;bmY|Y<{bhC*$Zl_tk~`J1`ovHh}ridy%Jk-VG_w+lvWm%BS5N$6I$Uzo-61wzAcu#E<)y?ezt(n1?l7h^dNDo^QlI6+OA1-Cf_~HK$sr^uE0;EI0jm(V z6WTGr8G_|hM%WJ*nTbe%`S|$YpzvhsE?NxQYhd+eE>2BNS=t2>5x~S7UkwIwQCdvf z*ab{S)Ybh2gx|1GCkGrY0CK zjeLAP1KVh>y4s<$yira_DT!7#%ITOb79qZOH7>Yz8qPn4u4*xlM6jk4>k&nw*$jO! z@V^7Nb}2de0lv2z-h!MFD;q5P9DFArR}J^E-~0!12e!HAO_9 zJ)75Pe0p^`mE#UG(3DH;v}XLpi$wN_wv5}$ z8b*0>+_$DZ-$8qew_ilJ90Pr4iBCRCd@=_i>4wQSRONubZH7(raK8o+#fq-mdS%aW z;zv1%AN9*_xkV7;;dElQ5WX;KRRY9JAWY$R;sn&cw#dmPDhNeR@9st%&MA#*Q1&3@An2@dwy%)l>@%)g_YJsD{O}2shZYVp zSZ7i>DiB{p!52<&bYuMpROa_1g)M&vNmxMFn6DEL(^*bQuF$83A zWQSIdRXLULa-h0*)Gpi`4Vf;(XFTO*`UD%Sx#G zOtMFABbQl+nx@O^`gyKn^$xxC+JiIN45e^98m{*-Yz&Rn*C#)3eV5zFM24r%^q&yD zyah8nTD)N_8-A=ZhJ;e^!t`nvajHK{-@zMBsKwgOKJM%+h|r?~?pDT7mlX(>#dXB5 zMc!N5#`5R+9unF*A8`K=i=f!{Q)DTR{q^hDgrQtFK0c1id6yxIw?HG9w*)P3kX{`H zS*OdhwSPW9l2CFBe+)G>%(RK{NuxoDGF}yWqqYq{U`~jIp}vUg(o4fiY%2?~tx^n{ z2=uzKq=4Cqulpq@D+?gwf!;C`J}S-H;HBFmt8;1SiSOVh7V&mSNFRppU?&x}Y)Quj z$)(?_&Je*HwUrown2blX&~tY2`>r<_41&g$6Ye`=hS7h1Bnd$ethT?uKjI6ET3nH+ za3a`!6Ho~}BEnfY!uaG6*2_m9I*F*9z$ZMHK!;#F3j2M!D}5!O)2vz^_^pockF92bOTC*kw) z;ZXv_J8(ziVB&1oAq0fzIE?0y-W{0%oa@QK!E%IdPjIv}7)}zjXi6JqbC*CLP4nf0 z64g*mmt9SWP>Ajf!b`#u(&>77&Y)n4z|#`owq-Nkq+1KCgL@D3i?oAun9^oqRYT_q z=^q3DxJU>)DiHWe4h;{#flwx4%OHiqwEVs{#A)QtMCbq-}cius?{oYgnAxP8N#V9!sgv$`TLMm^);_ip)iKQ7EL{xx+PlWrm zrQBdWy;ca!tb_6+aOrpu8Oq9IyD<5OO^1lpa|DXS_B>zye9G|SAoyL0AX2J4z+eBc zqod>g{X7&C5irHvjTtc6v4c?mL;yyF`cM=jjOQA{g{H?L^+6;1gD&f(^mN*#;2-0K z_eyD+9?0}ndjZ~ya13T1H!PLdL|NiV3khK;FjZo{Qz>Xc$)(M3(3~R7h^A5^RFJ}f zW`;#ujCi=^w8wx>gp!9R3nlQMS;31AZhkB~%z2|YpHyG&?d`=uL((I)r$UgJJP@E7 z@?TyE&@vu`BW746Vvdfpiv`6rVyJ4$}qT&VOX-bG6x8xHObB0Sp z)Tau6zF$@YUm(20ZP)C^jR2*tsb{3nV6Y_YRt+00EK2m_VD^U^N*)X6WibZruuivV z23c$wf(U^7_EXxCYn&h~c3XUE>JRAZ5RYhcOCARoMuAYfDLQkKa_XBP;}Zm_Ihk(M zG@IRCjEs&dU}L|3KY^8{awHfHYedj)I9R<$brL$-{2ZaC@}Ews3VSmQ%8{P zoVjm11GzDATdXr~+yHmh7!Z<#0&Qw^5ZFPm)pFG=8cK-nDEF-~y_lR_1zy9IScyTC zT^wI3jAKvUBBY|?k9J#7P!KX#)%|48_u{r=-a>AaD)$|z8(@+S(|JNc^f@jD7U4@P za0A*%XvsA)r;vvex;gL@QPI)HDOd|Ha5GLYksBjCT+hNn6p1g7VDtSzlHSnJ0FZrg zNfa+tE)ng8jRJ3z{-)LTXdcV<3lwr8>S5AKkzs2Uw-yx5UucV7~H}(uqftW z!3vPDnKP1Wco8Ryu4Wo~f#vr_NR2oh z{Tp!XxgfyD|F5fN!V<<4hfsS5E*OH-=LiwtG%ZRkItB)~p5*a6-$uBBCA?p4j9eY* z@a4k7N(JOa%i$g`XNYtIN)9k{V;yPB>YMC=@wS`6gfq{K$b>lhlU2QMyQ5wQGEF|dig*=@{51W|7D zBPL>gkqr3<;E~*G2w7ElG{jtY?$Xu+NT_|}hzJ<<81mzYwOBU_SO|jYMKc}*A=3a{ zfd3ry^Y6#U_aJP+8k8q9SPmgwTIEN>waS(0y2G7!q+Hbdbcs2kcaO8OAd!1C2|=>o z^8G#l*MPuLxWE%p1M~r_Sv-UYoSU5eF7M;$kOLr?{z?+3VTN#4*P0J!1wPcHBrlhQ zz=mj@4L=SUVXv(jk+O6U29ro$sD{qq!(CDSC)W^M{+cFT7H4bg{)e%0nwlzT8jK`c zVlAHKYn-wS3{+=Vox3B|C@3sk@s;r;8E!W(6JaC7ho)#0_y*NWEq#x?knehQ*g;2U zg~fBS`(4KrkFAz2@5O+?Zy#T^1u>L!;a8~m2`GIHKiyR$X2<=7^gORi9PT&p1dx=N z&8~iCVQXu;QU44Eq-}V8Nd9>^4_2|}Cpj4^Yw4(T_TD^adAXgZm*Zv5{p`hG#`!P! zh-(~ATn1~#o#sW<)K5;+ul7r=@(?K7sj!s&F8kbiK=s>iX}Yfc=QA}}Cytd`-gNGd zyJFKOZrsAtdu;q;12o#6t#>s2_fdp){9IIIkFcwZ+1TUMeqSB zxUlUz<4!E$vPJON&TX6%3H^lk!}rs94jwF~DL8b}?#Oyh!y1^G_PR_j*2U-vjLMv>|4S^%3 zBIJAKK|U?wz0LPFYAjPjumFGtyFwRQrv(`?<|>pi|AuNOVoYA4J`UJcK1ySNkN3o7 z;y!$U=2%+VJ})o%Bs6A0t+OAl`X6o^M-2CIzYZG!vC#JI+q-vYyu~nd5Wd&y~mt48yBRzb;cG}-dX@v~Q#^Vi>(R4G^Us!gfI!^pMuv%SSe za*D?gt}1>odjM>~;BafLE=9a1#o+r-pXk&ud*6uBbB3B5pnEBf+v+oU(L@$}NJ!_H zPt}<90o$*<)n(T-%0P05>11B+BH3VzXj78;pjb; z4ny#O5b|nhFFV#mp=cCzLIz!TG%v>iDo6DxCnpE@u8>CIFZ={we@cGs1f0xo#*VnF zvvXa*+l0NWN3E*f_I7|W)icDwOa|6xSj41%Gl2N=qGs!fduWy6g1-a6GL z6{?>$(_?yGv*UU7)rQ94(0QI+n(DZb45ZWiRM~%BudD$RIZhSjn9ye(9>eZ$Z zH2&pCwT!O9L!m|qanf7I$LNyK`q*%T5NXemgZB$d0DfhDex4PV8I{D@oS+x!7&_tN zg5-qJdo7Irpw_s030tbIPncE@y%l30MGR%gbUi*luP>TG<2`0+M?p^hKH%*yEKIiA zxTqmb0E5>UzSgX212YE)b+Xf(i1H0#^YQ{xGR`eXe8H+$sknNXTlu&Dh@xTbzv+8S zTuQ3KN1{wah(;l3p!#?9#rt$WksSWdN}dukvv1AUJ*DE+{Guk_GB-8&lBzG=`vKq9 zB+)AZP0#+d!In9od7R}zB zN6YWsI?^K1(D1E2y`|LTM@nVj&(5;j&fU2Wz&d1<`C{WgF6OjFQ4%s03W$Hd?)&z| zW7t4y4^yS*!Om*8o6pMBYaJidJ3V>v^XDnX&s(bw|KmvvvW=n#8GjPa0OgSn{pEQn zDV7Lo-m2F}GKuufkEl6{|MTOWIYx}9m*caQ#UhH@%Ktpa|F=UYXPH1orM{hks~l&+ z5WF^2{KSXV@SmXj|MfYnVvfiEc`7_W+A`6=%IcN#M5HSE9-X-Prs6VX51Zs}n~HFW zWd7-b)A`NKs~sG4ySuMwIBV`EGLYXvL+;KT;nSxJ1AjQZ7|YtYeS5j$m-y~N!yg03 zDlbz)aqaPmj|$ahXPVMdXX6EUI#Vx8>e}4iQt142=k_yq>_C;RqOardkAY$-GBWk~ z9AsAQYA`>;arZ9L!;N5Yes+C}u!vd4f!jZve>5&#mGmQcw$HG(fbLG#%u$nO)n9{? zzbzv5X%Qn;1(JwCg@woL(jiwJgnJG>Hu&}9!=qzN%!6(hZu%mu1(&zDNs+FxaW-cQ z3Y>h6H&4qQ%13py`h2*R z=&*=M8G;yFLy8IH6C+dZN5to6$2S&)kmyHlxkx_^E#ibYJo zkjEfx?(KWygt%>2kv2USe|K&${~f%KmDUCjO}6)4B;9VRrsOGTnPYovwuyz?S|@_$ zzVQl0uvyW+0i?ERPC;a3NtNh=P?pkN3)#(Q3U(t=G49S4C6FmEPhZ-UZ>Sow$E-##qD)HXn38(dXrw&X$1Cg z(X)Gj3)MKU0#aYM{1wLM-2C&1m1-$8xXTl>^&@zgw)Z59->7H`U&&g2$A1Ny`Zl*_ zmj+b7T@<_KL%*iNe48ja>)ji7BiW;06;rU9)lWaz*L!AroWC;rpteEsTZt3VJBnHO zMfe{k1>`>6TPza3%T0?@pw~<~%1X3~yiJ3VVc(sth04J<#@c*42O8);aE(>4Pu)8@ z_N_@??n7YT&uf=nsc$&+cyd1Ly7ZhO$4RH=jG0DGt|f=6#j*!8P}Z5*)o^S+R=ut4 za8Vd^0=wWyF>OT3IHdj(9f#s zxxOF%qe|b~NVOIe0o5AYz@ zq7a6B7zqQRwHE{B`0Q1GKjM+mlP6D(A3vU)=Yv`kD5G<@1L;^AFco|jrS0uIclP!5 zt+_DxmK)pzP)8)t!qi}*LMgo z??TU=B!mRg6^BNivWuNuo$NWg2W150Lb=>kTA#ijdpuodfJ(QhsOSu%Um3zpt4$Zs z7eIwhVe@9P#R7Dlr|0JAXlWNv>^flj6N~wK1?pY3+}zxRKGvZo5;t}t_r3C3Z_GU|<$Q5A z#iQ+qdS+|4I-gXNQCnNvmzfzfIIk-9qPmD!wDMzmI=3FOX|VyJtA)qEkF+qiEJj1w zJ8HxNkCxWDl)Ryl~4NnMOyshz2o=#`TLth z?YR=bks2#_QmUs0dAa81mM^#J>R5*IQs`Vf-`RZ6VEA!yt z!x@o`8whKrUa5utIc;VA1b2&+-RD=JZL2>u+n#taokgr_YH&5S6cgP^e{C*cH2 zU8b6Q_B=-{Wn*JgT2dldmleySu7(gFl~Hx~w;`34azb4v4jvpuRM31$t?J3^*OH&T zLD2>>^x$9`2#U67`NvvGsjI8EZaF!3<%he@lab5W+gm|sN_8`E?6N42 zq^5GdxK>oeuHej)@%XSX)!+9_jr;*_nbYIf(UjEtjwB_Sl!N6mcdOtjV|HhI`|Qj* zcZJD`iJ941!EuLS94vIb+l^2&L)9cBKK|}u;}kHu==wb8XRdng>bh_&_$Svlo3A$) zJh{|PO!4ZQg_C0&%K-A5wxwy-XgQ*)s+vryn+hI@#+Is@nv2?%wYBrllhk?ZEZ zTV%7?E~TOjXv9KmDfeBDH~|sKo%Thjt$zXe<@l)!h8W{j)#m^<{E`fH`KAU)u#%9F z;uf|?733sX&~=BhBr;v}n9wjTw%oDWzR26#JIQ+AzBaAF!NGQ;=a=hH1WX`9PARUe zd`WXs(j-BmHWF-95_Y%K-RSQaQ4oi&L%%ytX@KvlS_ar}j$KFF(%ZoOd&F~=DyhMCIw72j6kr%djy%u=}(k?J$ zf>Ma=kFn5xVuZxHZ{qs#F@u&x7wy&%+A}6}2QAFZXzv&u1L;Hy%J{>4Cj5tr5b5U` znV9rwa7^yl?XyleOY?Hg(%R(*HJ2LR;y-%?8rdkxJG;0X_x5gZA0(s4<&dzhxsJu1 ztSl&a=vaQeD0f|k$=-gLn(tIQ-6?6=Qu3UN(4$4xpDyh{R+o|T<%LVxdL{Re&j3Be z-D@2Rgf;zbhvnCPrO1`;t(q%aOxE?OQAlV2B&57flLJ&9?MA59zMY&5Kw)je5Y3=k2*ySOgHDu>>u%lZw>LFq&d5TUw9>}sQAvR|xB=qWe2@~W z-Ro9e_3&&P^k-lrz>JMF)BC~soF0Ih@7LaDMiGotJ*a-uWeA=A-tXo%MY9qf9-d<( zdxB~e5_&<|oEY0ZhZaCf%lAYRBpv8$Ex3G2O-=3TG0_#;=c%QSS^+gRT0=oQcdoAU zt}xU}p5@Xlv88EkZ50XKvXA4s);oQ*!&Q4nQ@n9bCD_`~ArP#~CI(FMKIjTM4WVPw zf0YGrp3%`!SL@HTv!zNxZz{C1`hX?S;S#Lbq#bNiQ&&fT5`-8QoTN)Q*x8$}Wh9cq zhBly=S%2uiHZ*3D(|P}X0cCE#WDFkcVOhG6bpp*1gc$%#*Vos-eEITyBN~MXdetM5 zn5BHx)n#O9*~iylNW~#z)__$`A!|51&w;%JYi!cg$D40jAIZtdfl~F|@URw}&chw+ zzY10~Gz0?NbKnx1a`Fc;zUgZ1P8qXq;~JisO$+UbR^plqJAZ;o^fcPx-3jkEb$>@6 z3J8ov^BYos*Sl1v_u4hdxsXN=SH*p>1c8AKvi7GpT`ego$;k-~M?jEMU$G@Q*d&xy8xd0|1veS-F>Le-JY_+wut=(gJRaF?4P)FfEH}_uiG2Xww zlF051>kVc<&t4J?2Q94D`1v42RZO~uWcVY?B}{IkXcYe-D}?}A0k6}3QO2V5>8n@2 zV1im{S^wPPCc(&CN4ojvRnuek8!0?3)q*6$c9BbmLec8jWbP6NPAM>OH_X$6DX=ik5G4u7X;Jbz@M5 zp_G)AUF9KtcX#Q7!otEcS?3B(u-~|VpPxIqy0Wm8KX-)}#yB<*pv@pMG-XgobM{Gk z^77?wW*BSM;RJWm)CR!}pVZYwo%yzeyEayNiY8%!=~l2-gL?p*&!e4 zj$j7a>EtBp#}v#p24u#sg#{>#qU`k?&B;UqxXZ%){G{aMkFOgBo<6NADLHcx@@DbJ zpgTG;HU_~+z%3JFEiEiiVT$mYFjWO4ER0`X37|$N(7> zlXTc7Sg1CvGCSOAN781Xpi6*w02iAa8qy4AYI^vv2kqwcv^1`t2dEo<$Oobph|!h3 z*X>C=gf1VNcl>k02@j$z_WDjIH7#v^IO^wOt7Jv=l#_~SXEndFu7?4`jYPOn%APX$ z0&Psew2mDEumy-vI-8r{b_1J2s7Bj8Z-`qk&O_{2ZZmH=5XOIl=#!!DdwsgPT9KZf zzC5Qjelu(EB?Gmcj(yLc7qf4^9TK`en?KQr-5EPPP{3?(OWEbwrs{zwPqcWt&~g@Z z_=Ob4LAycIzH2@)QCy zkI?GTx?>GrpJAN@9Rls_-oS0EjiQrsL&xXWzQ1P1gF!10J;=(+FQ{lIhaRpXT0luY zsH+|}UCb^nj=nJt;GAoU;*pd!++IW?mY8U2Y|O|*O-04PUXop0pXWio zhG8~=mM1%ZZ$AmGxlu4mWdJ6P)d7ovHBR4jZS5c9hHn+h&CM0weeupGhmW5=y`z6F zazj*D_~Dy3S#Ri#Am_JtuO3%MMh4s2QDgxLufE*7e_!`&%^e{@L9o69?bVo|!$y7vA za)XMONZq&WYu7p-KYrXCap12tQi^=>`t@xn2Ukd8}w zch;Mk4+?m+cBUq{Yf(E9FbG(0B#*&j+_0w>L94udS(x z3#oVo>waXBeAA!z^5E7&)51kVgY?Z-&_-ZmpU(kmhurT7QOSqTQTCe=G_3ru4@q8Q z?dLbYl2$o^bi~ocrS!nkg_nJOn|XQPZdHwqjoma4&}6=gMJdYKL+H2$vSBtaFaOQ5 z-#Px<@2pMzIO|_bZzPxKKmBA^;V)oCh}=QZjnli(@HAUuW~RY`7;2 zX4c~2c=L4meV0@EBb;Kjo!?ZX&yP<`5FW>@qLE7%E~hk&dtUr3m8bAEM>y)Q)6;tp zYXUwAzy83H&*3273n1YHTiA3>tgP5CPwxRFa%7*7kPwR4D93|ZJpV4@KA_X$3<8}C z8yYewTUvnjC^q|(%{~9Qh2nui_oT?Ue6qla2TAsL7x|M;8?p}-46&M`_b+%CHKmvJ zewC2=!u#*vzu#%Iq-1ADM{LvHGNsaOPkvYZWNu24?=|8x@#dQB?MPDYY!y=zj%5c^ z{oFM_4H>v&9nLLsEx=zHSvE$}t=H=Yk`(C;Aa`AoE%J{?{=`i2>SuEvB2_lh)s0R} zWS+OSu~~0kdiClkUjm<6X>F|^j2_aXOt=$?$O0BNHa#(k(recW_z7>b^W}=SVa*X8 z8cKcdon)rJmf!Z2t?DU6BS~hFV&^eX7f1l{Q^cSMOnrvAv$J!ykfQOV!`F$YTLk}x zg-19`1OnO`VXkV4@r!nU@xXDNl11^M37h*tq&SsRyPmSahmzhXL$sLI{8~PaM}AH~N}z4n zPng%;jnWFCB+)# zE(|y1PJ|?gCC1LS8BsilhO$OSmU`CbPsp24`KkE6^q&G31m1NU5v;=;BGCelU^G&% zZg(T<5ddA2FQ#Px7jpORT_BQ@CeJS=N|f*Uf4CW|2>2M_ZBYU8_42y@!S8qSG@rSW zJZX67ldl)LTHuNM@}Ax4f}?eh-Kp%1TPd$eOXPmK6eR?C2V zU5s9)cF!;xp`Si20_xe?*ePtDi&Q0OU|=AP#SL+X^*eNuu3CuKrXmfx+;aHfp+j_B zitUdcou)}xg>YUbwC?0L$(9SHr61y6?>xH!xgd}$&L1Ib?=<^na?;hqLw{C9Z?FC+ zRp#irD;j0L9}X1&{|6ihjWPa$qs<$74$kJlhM_;_n(>j0SRptUmv7xt z(KFI^vRKJ)9Mezy^*9JbahMQl%xpeRbH=5o>>MTIl^nZ|^7K>N`9xlKck|f_{J-yu>o>`j6IKt?XP zo5WTbx=6+v8lf{wIwT}_f9+q<+{4> z`}aJr=lVyN`kbG09OM0dAIJH5FD>~91ML|HE}keeDjaOFpV39A;asm)WwZG)sBOJu z=VTad&-cDf;WtG?Q(@snTt_d8KSvmL>l~5*&gA*06hgk-ape28wj$@(>oD+^*lzWz z5M|NcFe@eJ@m1)(zi!ZTPDx0Io}=IAQQ~=gUctL>74Zj+Q-^zbBclm_Xy9-X45=gx zKQ{g$=G#EI#k%%%?&3MYI!BV|@mII_F1S7MS+VEP(!Hn0ceft(liK0L6XGaDiO!5| z>`K{AWn@NbsuST)6?d!c>*1c*oVmugJ+Rh`VxDSka`>Igx;C30)QNBn3X4mZbdB;~i`ucayvQtYe?gS9e!R}e zokyxG?8a=wyJyk5T7r6?^`5pqaIMJHc+gmDNOi8Q?otXzScN}+h|S2#YtrWNguGEw z$At;IvH|rkadZcM!>sDVYRk_#2>*%?FLYi@!IBZoQt~gNomb4>T{E3mAv+yS6-JqSrR>Vhb}AX z5U?&_&&F9f_Ea6PNq)M;9e?Yw?I&q=JYnL-Md^KY^H$WSvUs#IMLfLO;VhB{=nv)k z2S7HUr5hmaKF#BfR;D>(NxtI?$hn^zjIs>{4H`F3t_85iNP5010=!v_5 zed+j7%nO0fH&<9`+PdFe(i3@LF}rE8y%i|F+5MW7N~XJtvgJ}KheBJ*&n~*_mR55N zsXx2KuB=gEJ8~j0=#&C5IIP4;{7LNjkiXzjeXC(EkF6-{MQZhC^LfEs{Oto% zHX~!JT9ZLPlIQ6}D>HcGrGlS&bjsM-EHx;M7{f)J?2BIM+-aOB43d)SST<%xE!P& zw@3HssU@$bdbJMwg_!xr+|>fTobm57EgWUomAMko(!CPc#7WUD1t#^-#O+D4E(Ouy z+;~H&cvJSB`1~}D<{R1_f79271JSt%7Y3mtc6*A0{T2@FFUlX3Y0LZuC%&Z^C;0o) zj2h+SD$|kQXN=ESLe^r*j66@C|GjeYR)k%gcO=1(l0*Erbw^>zjwwY}k}>9ZAA_43 zIP)msbsU=W``Z}`9rDTR-BcfqG0=(kaA@YVAC*m9@v3N?Mw?P)d($_Q^kd{9uCScQ zrl(jzbyxpD#;Ux@x10oNel4PKhpds2UPUJ<|U z{#q54nDLyaD|)f>BTYu=1Af+R4vhEMK5>N&nvEXJ~h9rq0zKKw4N?O-IXq| zl`+4qBZPht8z-ncZ?Z5>s3wZ@o@t$%Dy?Ebhjv7v-%cWfT7F-So(;0$KM#p-&}~jg z@l_oy6{vmsl&|g*r5#*})j34Y?bt?}^R&{|Kk8g2+R3iiBEc-f3feqTP+XQqex`l- zGLa-+6h^)&(BS7U#j}3?>SO7qK%ICRdRN?7F@`;~e2MH5vac-1oJKcAZ=33B>syG8 zV6kkZB_=YHKFYuLioE$h|22a_LEGaD1uIna0+%g+wutMFe(TKs{sRL8OP06TBD{@R zs1L-lHUn@rw8J{HSrCB&K~L9EwZop8H`K((#}hI$w~c8I?sb1|QWqU5zwgvn)*d6v zFw^X*fxe7`HTlw*5${gx`P^G}3Oq_MIfna_W3Y=73;q84_meoYw3$e+X=xT44SbSZ zXqqW$4piHm!l=IC{`qG1GC8u{gm+}d2@bZp`3NJWY1SoNz{uaJJlK<^8p0iR-(uz)@^@wl&(Ga94QuNDMpN(E&9hlbgk)6L?*FyW&v|(c zDH7k4eJ>P9h%VEF#!9qsJ| zypDV0b?)=k>+966Fcmdw8N6A662RP4A?ua_<&aq4 zE98p-fr0qli-20fehREK-R#ytK3N2Ov^$u^WVK?QzXCd zL`Fu|*7ECKP8t;-VX%Fe?@u|}RMs+l?xC>&%|@nu?)BfM{4V{Nj+HwysvG-p^g?%< z#Z(h^G+zOn#oczEB*Tg5VxkM zr>Tb^$V`!|w+UH!J~LldSE;0wP7ub}mU0h$TbZKQb=bho#-=b}@-4RRd%`1wJ|6-u z+s3PvBm+&?OsAge$j{El7ZYk~&+p$}H9^+=Yf&}TJ1X*{T1UR!-FaG#`Ea(oE8B~G z7cX4+8pXBy<^8or9|F}Xdn-rB%1#_Y!sy`O;Fy>iFv{L6)yjx*a`oz`zjh>pgzMxdey552}Sdy*P08}S|080?TPiYl2PM|1}*et zDAqXtS{bR()77O}=CWDwdiO3>KI4XHQ?f+Zr;1fW(>dXj^Qd4}Cb%o=j`C% z6V-I&e?-L>yP@SQ#OK1=$@p5^+PE!dF7Hbe&&S-!QL6~v7s8HsMQ1VH5(H^XJx{ZK zzAMFMdGKMru&yhjW%$64JA;r8MVTU`ZQXP$RYr<;Lxg;^y4+kx?D#0*k*_Xg!057m zW*w%RSOBcDvgPT_KQH|L{d-~vFPW&GgoIZ<0X}}KgdlE+b#b@?x)+g#IFJjo|+nACj?Jc#21YX~4ri^(&%_|KH(pJ}D*p!q} z6jEdt zV{a*uB8g8S*?l2BhEE|&h0j>UQtmq5`|H=JAO=olwA~w}eqqQ|*Qpz3NjA?Cs*~RO zC16DDD~rruV;zzl>FDX%P)@2YoIg7Bgc8P~L7d)%`D3efbvnb5DUJ_ah zC%;!ulR>w7y=YqARwqAoZf;JBcmFjn8L9VS!&7OBVC@e?E{Dv^@u3@e<$urjko*V- z2gi6IUk3mUkHZewwEl1@GaQ^Kx6QF=*=8}IB!KxDiEgR@Ng#m;}LNf$$%#t%d}R} z-xHU>J4P(A)Z@!$vw&CQXqzL-&(7fNuL`nRq;QKK2BQX%|B3QCy?Yn$NL4bg(`@R? zs5Tc!go|e6xz|>lJt=ainy=lgikR8>@s_CNn&~KuY4H2_;yXBJ5#m}SUoHonV3{J` zhe0>)v*+3fj%!;W^C~FmP`yBB@zd*ZPrB1Ol`nXRYzX$Mq#YEOO1~F*X5$Yhh?M-e z*DnB`;5r-P%5iNbjG&~082u+Fi5^ze=O9StI^_q}pDAp7!53A)bDeMg6VmVdx_Edf z@*d0$mcAs9aTXhbKPKeePV~IoU!aFI6lZR;!F4_@E>=9me|?adJS>cz%ci&3vw~k2 zMw-pIj2IU9;}ET)hqQE+{vRJ)&5R)6^z&UL6{c$UA?+9M8Hep`c799J1Gx z6`B$m5OG->-F~}~(2|)+S1jbgi$WCW{q9xhJl6!!vxv3Nn zN1pJCwDW3J5;b)_r6kqz0@jFA(RsByl(7G{K}fC@XSwbDipO{Z(|Mtb2)BNaeXDw$FyN(D<&g#FAkwo zZ4h6LEDrQ;<+ACt-e_yZzxyA9!HJFwpAA;Im-3job+M-+-;9{+>tlUPMZ%j(jtkd~ z8NW8C8QG_NWm)#{7-rz9w64fSpCXg0{E@|iFk?J3zmdJh#LyBKkE)ki>&<~n9J^%^ zI=wloU+KF1W5w$XOVBI$CE;*KT5SHeu#bX@zetx;BMW<##n5n>d=R z+`mSnT*s<4ZN6suCF8QZ0kr_GEB4tQPwZVBuK`z@kbje2_%Rl}_R`r5ULI~@&NLDp zZPZm?_VeOi7tU=dB>#M3zB*?QyTQYK@NlJk@?&P1pkU*3sh7_lS3g8vvpL;f7+A%P z0YQ4^yq1$OG4ZPzSxfjvBZ6kq0wLKgJ|TYW8s}D({$KvR#*Hz+K}$Fu2mL-=BDi?` zZ}JFeme%(|&%1?4QKg6Klo#WY_p5zNYHhMz4sB~|1DIqu^d6kD6mn0|iyo4guCA^D z6RdAew+I2`z+!BIsHtb@<&xyFEs<`X#Sl1sBS-NNh9ZZ&03z1@@#+h2nV$}|UXp6d zjACVA@X=ly+-Z+s2e4!Y45X~AY>IVYwA_l8wIRnqBVRDu7OX)!XlI4@69Z!M-{p>t zI7)vOQl1&Da`?HkjDPdy%_~>@A|o9S_g+@48T8Xo5XX$U9RG@qjfJ#8jTJP|)DZvB zbFj!zvBYF#e0=;rXbYW2H8&kyCU6?@^z5pPs1q zEV_u3Fxe~&|H;W+zfXV0kv?d?_L1s+ZoZs%u)BJ^Gi2KJ*1*6(ihKQa`mx?1fq|#@ z=G}JyB)0)^$Rr5Y?A0?ixdzcHvi!WdTD7YV$gxmQh$&0Wb^{|?9YV_r4Kku6@J|Hq z^mZBBoSvN>qID+zYCRlxC>C{Di%YJ5a$uQ^bQoCD04+Pa4MY+5i&zSS_0-qxZW0l> ztc?pKhZy}PLbQyGzK$lHiOuC!i=oUP?HR3pCO?j7Dj1KnHY=Wm$t6A~S{AQSf*@kz_|12zk=_rcViIa*-WI*@o zrRC3R71n7)o!*ywBJcqo3LoQqU?Y`TO)`-Z5!svYWSsjf^cJ&7|GQvMM|?$NLj%jB zK0>)4g;7RJayX>i_FMYyzo*xeseE#5Tf_s|@)^OW2jI=^t#PywGS&8t;ls*MkzpUm zBj2peb-g5LIrbZ*a=>!m$Tu8{4QlZAhalY{J0oO5hoqRdIl!ofQ)Nh1Jpg{wE=!z? zbF%&M31wTT{H2(DDmf)3B@>e$o>1EElM*oDTze0Pm9iATu?u=ho-2|*^|#d=q?xy6 zX)GN#&#w+V8MHPIR<5u%mOt(&H*KMt**_Pb}sX&ch>yB4z}%1%wc>A zpRWph_dhbO=+)zii3yQv^SN(vZf@SZuV%bk^R!rr%V<;izL-%8lWNos^@q&GA)&PKS<^Yq9PpO8Be-+rQ{_*+jj87H)g$akyyeKj8=A*Qzd(Oz zVI!zLY=|_%|JOpuGTP|RYw$8&xh?lCDIyH)FxRQ0U1@zey;Dr`j0vzG*^NPDB?Sk<2q(jkO_qqWxociPVJG^B zyD@SO{>9x4@&4g%B8Ad5cphQH>i!q~rjpg+xEA|XN2|C{nJfwEw*x_#Nq$n1^3d~! z+Y>Iyp(5n-9lbMiE4(-!NvAZc!ySva$bUY3Gw_h&^dCPw`l_^DJUsd$7*8m z_}fC66AZeNTLwaT9N3&z{hz5+tWDu_pBUwceDZ-x9=9B$*IPUzGmTS)O6N7DZtvIk z>4gD*#V!P)pc`|u<$yv5Fa+3%iv8F(Z}TM}Mz1 zF26FGETezZ1kt7btz370ot5D-OIhLSDBo|rzV>Ca@9X;y--1X^ArFFZqCLGZ z-~ZoSWU>E|(~+$2IoC>4`d**4_RXQVPlO%J-s3oEy;;;*ORaL=)^TXetnV4N12Z+7 zH94bqjcD`ND=c@Dq_I^(PC)`vseev-XN&Ne3k2*v>bfKl_e|iiLGp`2boqB-qL$w& z4Jx(sx~%I8tjZau2Y;&e(7UDlsKJSwKHX(L&92Xw=Yq<3)5U1<|BYEh7{qRZbuT3T zbD%?vP{|>bj^6p|kHrc^G=f7qA5KmR(RF600c1zt?+Qv&DM`zQr`=i>#L?8fs%9CT z9zv%u`@B-GH9~eMyPZV_dyql)#1xQ1qyIg4-(+lfWp>X;g_27r+KUcZ4$HgUsUG@4 z29M&AqOs73YLY@&c7XhaXZB|n4mJ=jd<6naq%{w!3viB}q!NgS8+cK!zhfNA?h)4qscRS;? zyh7-#2eS$IRi&{9=jU<2;O4Sf@MGk*KY1BIv#w-{gZVIT$t7$t3 zta?p)`Hzo3D00Oeyie1h%R$SG^~zx2tXU_?0sEQ#2KHmPdP%P2QuKw@rSDEC8isX+ zZh@wUTP=V1FnDg_AH#g2@H+U^$3t8D8*;E)yAzV|D;H9P8!j!RFMRm$;mMJ?D>eZ^ zmC*ppfgf2EGc$8bv9;j#HQ5>^bsQxDJViB2=Sfp#i$MmCx01U_g+1+bPlOcz^_Yv} z*1xgAwNPW9dzvWQ`tuiZ4$}OYgXpCnRvX;6+aMcKVa6H&S!yWp;Xtoss!9$6k! zs7`=?3ma=(DM_Zu?|D`#Qi{0So|lsUmh`Q-Vh7)19Z$AoE}Ca_U`_Y$abqHF*;!WC zhQIT$7s&mNmC5I5E$}$t6;(!sYHrMK<>}=jy+79*i3+lCYM76w0;T3Y>$C=c9}87X zC!tHq{ylj~!$ZY#ZnqRMRcRqbVvedarz&4x#c~`fHYPBkb2rcJJOj4vtdEC{8fI)(}R$M|8+1PSC442^n$|V6~{t*{V4hz=pQUhXU<)+QDmlp@sRU~I$a=Nnc=H~+nc%<3pM=VtRbEN{* ze82NadL7YAcZeWOf$y$r(d4oydUV+r4rYi9k>Avs?USD-mQqSH6v`j1_r&em0eZo1 zI$8-xO@f?s66g6p9%F!U!_Mk=Fc9jSpAY&soG*0{%5(1)NQ?~HvNh7!v@nghy(sTF zS{ZeseW#a;&(NIA%L z==f-SqWOcN@>1%l$*-N)aLPIh1P~M}f3L{R?ZwH0uDjuR9lt6I9)EXPJ*O@K9h1z~ z0HmU+k1fR;#gDgz0xW5U+`<;S(+X_YCjfB-Vsvtt441|@AH4V3qW>L-XDt`g2e)z^ z4sQKCqybFvaMy4d<;?gXqN^KoM`#6c2?*Rv`TfEWZP}aL@d9oM_}qTuNzA14LisXs za)~B-gf#eWVTQe#VWFXWJ&GDpTsA(A6#H}Hj8zZx)_%Y3a_!b%5gryJVNXVtI#W0p zBgo{w9$Jg42e+a$j#7P87Yi?Se!+RI}f1D)*}z#GC)gR*Lw( z%?{^LdTe`+LeVVT@Gc*H4QH`^;C}uQyX~Di@kt!9sidGbp}IEY3zzwW=RcBVNJvh| z&lp5=mCx#4_@_=GIzg1fMre_8!j2(%PA)4iAwy6 z|AJ#6a!AefHgt`q*`2PS5NF1gwIAbTpZVg$O%mK}EGI8cRNYmNd+_q99N$#CQBEq> zw`?`84G&VOBiLykmm}`wjguW-p($7gNGF@UoHT7on1W0-NlGg|^PgA;K>#oaSageZ&3H=TyzsTTkpR7>t8{B&`v%ybF0HV zxBuI8Q^gF|=G>4`)o-x|@D_%Z|Fu}KVUWJ(xCl>D>#sQD5j*0A@wVHy8{v}RS>{4{VhLY~fa z3k#Iip;on0wLALp|81IFrou^@gM|*kN|_(wy*>Ot=Sq*&)?VxVaD}u5{%)@ zK|@6)9{VyoJq;yj+wt00;1JImsLoGM8@*XPe)!;lAFJQrdIzU_ew=3?ZE8bEbhKo? z`bK%vt5Z0^E5Q|UaH7P>@%%UyVB8e-Uj84nzT2(`5`e)^C~D&0)vxuExzy4+aJldJ z=QN{B+EIbMSj2wXx;@G&{W~8Y3M>O$QBEyQ&5y>*MhgXX5;t*i8@;eAPnQBFsI7Z~ zj?MlC*raF!&yM0XMv_c$KZX?GWgmxxhl|-me4B^zM_TK<(i=a{_v|h7i6!h9 z+$2tINNHy&+K+c58whG)sn7X(QlR*6;C(WT=lUnBHZnK=7e9Abu`XSbE*??$X5{2N z2$bTLv4QN*uK#U(?t4O^A(UuOmMelOQ1KDNJ?E?2?*S(ZTff~--zz^~fnHu4@mJgw z&X<_5`%b~QpH!R6Z=Yfb*7 zkab6Lp9_hIygM*rx|Xe6@Wz@N|Ie`j@uj)bx*;vEGHI(=T9Yxccok^%-SFc`m-pmE@5NH|Mmvdh(jMFp`laP`^ChSU=r`<3PDp%W`$9sEFr0`c@IDG@?MPNsy;uaL6 zp&&k_b=7&G{8452^Vobk;DllG@R6CTu3-oU^FU3RDnq5yO(FY~Y`{Zq9ZDGEDQ`^6Su6 z4ciC5hBg@QTlezirKOrO+*LsxhCZqEitCj^_^|97XB$f{dWZ}W;)IcTHOHI}QkWO1 z3)PFZ)BW@sReRE#ee@#J3yULrdXt+^y>W`uTGDy-aP+R6Sia34blRnE zRVkLS;9tr7Wp8~Fz>O$xS1=$O;Rg@8)8#$2pPeXMoBCK;CdBt+o*cX-OZLQNOkQ>q z$}pfzfsJ`_whBn{-W}-5$;E?_vAi)WwmM!buf9mDbx44(m!H%O|B}vh)~Vl*MDsZO zM+HMp88>I}b`CF*X{8$DvFzHQj9Q2G|2zz$Lji+&(S3C%-cj2M^`n&WBF`~e*9Cj$ zNB^sZ61A7x_RQFB@7qrHSloKOU{7(JtLFrppo>OmwCHmM2tmO9vy1d1Ib& zuNHqbd}PHFcp_fqw+wrfxKX7bpMN^kS5#FP`E}9~QSWuPIstnsI3PUy*6rI*QzWCH z2Wh%Bq<}P2>|X+zQjMawp3!cwxH+&tX;)&UUO~{a-S+S_au_)hrsK{qNK)Ex^4AtQ zsSSy@&T~s%lx}#W&|79P3vwr)c9Y*K@RUy;=tCu@s{;j;Tutf!o*Zw#0MnkM^ zN7dTR)_DWJrZ^)?;}Muw6_i3SE?*`a(**ULh|l?HnAl@HYAUqWh^Hazq_@6sm9Hbu zSKnP0;fywth+xYC^f)CKc&!=|)&AyeU_#(qVD2L|E;TG(>;+X5SY1R8OU#^?yZ`s~ z$`9w2oT9VEGK}-q4J*(VngYlSK+fIY--j~OXDDlM+cL3c2})mAU@Bv9f26%;kMbJ^ zYYC@I-D{?(*`L8T|E8#JH{DlrWMyPT=hUB}$fy(gOtPFp5v>PZH6wzVbK-1ee?dY6S*nw_l#{2DIsiDCnwkRIH$bx{ z)`Rxp6BRs6HlZ@SX;*M670E85#fbD7#dnOS+KIbfM^@sNrO_GJJm{Y$}1V zYU^rL-T(C(W;@@y!E86q{Fg6ZLaE{{CIJl<)g3%vdp6Ed20Ap_(5E4Fq_@a8%Ikh( zSa#%9!21-B!>NvA1O*{+(rpeYj)V>g>f8gdUlRYIUJAbHJ z(dnFj{P+P_1Wp@F)O4i$o-r0CraIJKoyEWFD8u)SWoQIxvJMPU%4y zDOh%_*C3UWX=Fk%Gm!FlAtNW6&n4O#sP=zp3>6$v!~Z!EJR)V~Cq15^Oo5<~X1fxA zSfltiy6;}B24C|>0>@?;dUqIoLehELh_Z!;zm+|? zxqApGR{xRTz&r=+g0J?Z5vYNISIh;&%K$j=rHg&U3wuPQFV2Gzed=9sc0y`InB6iraqW z4?QB;jtBJ_8Y$b`+jU}y5T!lgP#extZDim>bU;frxoGfv`|p?km@M5BIB>dx)YNxm z;ifgecC=I7*vA|Ljork;?;2bk(k6kL*|R=ES4C*k2_S2IP^C{l`rVu8-EQaaC$FXZ zI9Bw;3B85G&Lwzu^S;B>$_Kg2wad^;DH@=Ic4>nNfutR#yRp&AC>6~eX6zw$aIlyo zBqYRld}vXOj7SGp!OfRoqSX#dez`VYx3;zhW%xV-X}jb74wt6pr!~$zLr92h!INh2 zf_x+Uet|nMV6uNe0JI=oEyj7e{TYkzfv|A4Ql8r~0ugdDQNGsmW*GSr7X8J>szydj zP#f&WOm)+0^s!p%Ux$CL&4q~FfEg0Eo-YXJw4!k+bFZPMr4^TuX#4tAmn*i#+}u1q zAp!7ht2Kh39NN=Kl#>sRD-fZdf%)LlDb=d4jGG2CYK#I1DaIK-DcnKqIWT}U_k8< z=Ux-Wtkq!A!P8jmR)KTl#?Hz}grk;%y8720Bv+&+7J)IY)k;>VKNsV?<~=^#eE04h z;KzId#EQj_Paeg_Lo-uTxdh`#Aq;B3uEs#kdZM7v0MD|*vyVhYMJueA0epY8c4dXr zfo|fBf?sN=_f4|=xyG+d&V5v5jetAGKzedUUqg^i3dwQAfK`*H$brvd*WLjsK)4HL8B zIPmVL=|BnIK;w}ZWtht5bclxY;d(WH%4-DXwri>==R7@%`G8Ek@2N|?ZhTwD9{r8BuGq5 zY>T`vyRWlC>dU=x^mEnFB@A7KAx3*6%*az}W9;~77uq4h>(3#u@+HjpSBhyBGM}l| z$wMxM>m1OIfIEbALm`c*fcoQkeIPkT7tKn|0!-J(ekE-Cx*U>1i;N>>vo*r?!*?7M z1=zh;0NfK^lsGu;Plu3whhF1LLr4npX?%EcZ4vCI;OIuQYh`8uw1!_J@5Au`{?p__ zjALqtnf1NHCS9|69i0m;%bpKj{FW;g;D(n91z-J=>Ak7ax;msUxqI_)1Vv5C+)mmv`6~Tqw}^FXOWxKW=t}cuy_19S=ly2rr$i6Il@btlhF96lPkWbJxO>a_Dl_@GIK?_ce{&$jM!_GY+oNjljn#r3QQOt9){QG>FDGU zAZW;H$doCLsz22%0ab^_2Uga=WkfC+<&P_e;z>t7(gK?OLRdUo;1u};86GfK|e?Seow2x>-pz5d0FF>K7>4j&?o`D%n)O4Oo~~aHz<=P$#ywDg4-1d zSOV=Ho66J>fuPFHnqGuFdAPp`2GZW#{G6-S#g9?L(sB!a1yPOmB{QeneaD~6B{DKH z>?Xs{xN23w7=u23)Giqsz|75s=qnk?;ctxM5*QMKi;vF*mn~o$S^RWV26LZ({u0zs zhprB12x?8N3Ky00-jBDrxlE3e&oRN(VWWRWm{x`*x8S{mYtuU z4-s<*mfTqJ{)fUt(M)pj;M}b767WKY?S+TcVPDQ&L>CM;P@BpQ2@Y;3c4LGLSp{pn zMj-+7Jh_*1m59ifPm?(edT>B^JgPa~J#H%bXhh_iM%-FRwXtj^A|m4K%sYg{t64Gx zGB;F68m(P*{H6jDL&?5tbJZThN)N%ojyGeDy-bN~;bMeJH|Ug4WMu~w*3v+mf-E*c ze;iz--5da7XE9FO1i(C(gRYo2m+{>AK3!Pk4>ouR0o?c9I}XP^rXi$i;cBYq&!59> z02qI1$(n1w^LXtumeIw~OU;aLG4OYwj0XBl&@wU;sdkt`A@s8G9sa;FqHTH(zC$(y zon43_q+~Yeg*i!mSeK6OVRS@Fwi{?$SV@F-r4^_Ugzgt)$ds z?TQE2S&SOI@Qi3f2VbLJv)`Iq?~>vJ)1+{t>oSKo*T4;nVw|bXnFv$pN10qLkyu^K zsaXL}tp0Xm>nqnP_trO|)NLe+H*{Sp;4hCR@Wc%EJfL9-F6+E(@*{>tX#cFGYf{|* z{)kAeKo?^Osh0`z7K2*(c!ySPCM>BHGAg^vkz?^TW&}o``%^J-aoHzNfJfC^-*nAM z)=&bj048O3jB`Qb0j*M{E%W}&F^C&RZ;?s``OQoD>8Ie<9T<9*<^0vvhA)N}iAXeZ zK#l-?D{8saeOG?6$ekP7u7o>1b83#lck3sL+?yRYnkgX&KJvK@O%&s5sFl_T(eZOd ze(RHsQh-p5FY; zcu52kH&woWdB@Ms5AIjw6VQ}C7hMEiLUzu0q&#SO{IjC0`BcQ)Gc0m4gqXFpwUSal z0MgIaYd_GCntz-t`T!S3ju~C{J@X%%N}NChX3YnnNU9VY-PikidpK9S*k*MUDt(X` zeGM8FyQHcP7Ba3;O3D?lKRjO#qYDZN9dGBjFfW%1)=I&EuK`gtHHjQ_EY5k0QukYf z+6lo83a68A$jcxIK6>zgY4vKYVi%Er{Y^qbNl)`h&r99#_oDI5hZE-%FYQr3jScqm z_h;7scIUuvG_=X)Gu#1_D?VBIaPCwIm~v=nC?xuCHyrQ%k^zi#k;svsXu|W-3s5&P zAiI{nBsH$XIp>Uqo4Owq5&{X`XskM__#3_~pcZ|4{rp$>!5X%ZRfz1Sun(mC)_b}?ziCo~VPWJW(^iPS$OUzz{k3pCQ=9^^_ z$DR!5&bQG-nnJrZ_Z=C=dI`^+Xr`vKpA@@l93jBf9RFGkI~l1DG>1=lJjl~MM-=J4%i{ip%r{gmRn8NFHkeHP8BV+#2k~M^Q zj$aj=Xc`a3i3hYH*Zo~u;1a~sv4xRVy`vJx%SK!@G zK_3Og*=CIp!BS2Ri-IPz#}RBNP~c){eVS|vXhShULG{+IjsWxri;cxuEMS<8h{a3o;X8}bfhFR;v5ZW`Z5tG%fl#Yy?YL!#4+=Ql_Y~Zi@ z1X4JF8px#ca94zsj&2FmdZUXXlP948evu`BJnjZ%e7^NB*BC3;K7ox5@FJ)o)n`Gw zuU`cA5E@0%Z|w8_Sc+#)%+78bzS4$hLrBoQn)+4no+G*^S{j$PKXLH<_OCi;AKSUA%KmJ8b6T$Bz(9 z* z57ejT=g6w``h%;u3{UlkikF)MDF-lJX-L`a@;iwjQ-ZdV?}RXi1R{9i*Lj*KLukRz zONN?D@KPHX-2t*moe~bS7I4(rKM8SRAwP)JeWTVddF9Dq|!x~1rmsG(n zT>uZEqoYG=F9~PX5(aaUDZwOrP1j|lZa<#{dNL#w9*A|Q;EZ`%7l)8WSwK1jDK9Sz zL74lj&Mh^S>d^Q?(*@<^v`A<(BqKZTavAeOPW1uJ-Md?G^V8^}?dVBJ=x+S^tIiOv z{%~2&t&JTYg)Af12!O4!7<7_VE+cnCQW71n%jvAlgd_@q>g3ud7lPXWtREX2JN4t` z4OllJu&@`STXdwklv6YX)>79EL_qciHrB6zU_1aiFB-pJ zkE@YOfEcZ>l5sn^K+=9qio34(-0(~2T|N5s^LW|RC4t_J8#QtwstRs=UL9G-yu4Nh zNrn@g;`nIwxE~Y2*jXdvHa7n~p%LZ-Lk9uAhvm{Mw;%@ZCqk!VDVKk??b&c0+AkYpNbKAxW}HXS1*B=n&xplKR_+nGh>(?g~;o>iHaRn3WQCa{Pe~1p2tuS3gvi702-eayv{L98Jq)v=aPr9Eh;8o$cH=;(+_iE+&__r=D(uHep1k zod23^Q6h)!19H=NXOj5IEh^=0?fr})yH^)tY0TvJkwz!rX2kCCMo3L=79!{+s3nhx zoVJ!r#wdmWArCn1jsQaaS+jKKn^1lm#7lqEO~4yigxsljZ``=Spi(#&b9|UGsf2j% zC(ozE$DX<@~>dNa|}li?!X-2uJF5ML~0^Le?URWDJfE6EYo!tiM}wl zV~{A%K)bY*%gss%D7fmj#K6M1xFQJ9?%-V&0IVxmokWH&=4ld(luGhc-kINKol&)T z3Cm>*MCApa^--EsZlw$Mh_ED)Mx6C|?% z==_Eo{(W$5G5jPK{|K3pq@JbW;MnVMIbz+kRzvW8`uVZ}jKSq_{ZS9UtC&#mJQ0YO z3Q9_;M#n$Lj(vQ5gyoP+R15WGfW0+}g zbdwBY@z7o~eU$)U$rqp9#~39DtUt{79Qyzyl?sJA+LOXFP$#&BFK5?Z<*>_lwC?L8 zgrGrEPIkGYtQ$!27_bps^P~#E@t-n>i$*ScpNZgt3!Cxa7oQ8ClO?0VBO(khBEm9( zD}hY0WR8-fRA#QDtv!XscgSDdT=Il$>sx>xA_!dO%?L&U zlI}}@7PQ{0YMQr>fgoc70%~36R+6bv!^_Qm21QR4Z7wP;qX!pwj z5C@l}Nz0NCa`b9-;42(cTOj_pt7VTrJh_CRudi=!&xy)|#k+TB2;CINIJ4N#^S^T^oIPD!E%0opUj<;Ss-%s>m@)C9c5 z)ea+kA}6=i;f)Vyf5IPNO^JaJ!w7upqpI8zkI!)rKL7E7L0#JhVFp9DGJpUW9DL*U z?cvgiUERBLRGL~^0|bbZMlqs~R)klt3Rc(9F*52^)X6uo@DMxvI)Kl81aMkMvRTfR z$fnHDE14Nii-9f{c%yTalxAU5r?r0sA?K%a@Oq=S6F@KDS}ii@cu=?z+ak7`g}5^C z9r5)}>lT(b?Gek&slFWh!cV8X4@|AGaziX+Z7s7;gEEYK)qTqv72V<2fF9$~)-^5^ zeiZGn>W5wJCs&CcJ%SER?QgLHOLq`@rOGoK)2)<`!?)O);-C=(5|^H8yFMrv0HT+s z=a`#VM&2cc!q-H;$i9DHFCOxOReZ+Xi7bBG@ipNL>SdI(MX@`HRj10z2sU+PFM>U%_IV*}}R{;xWj@Ilq2T=S#5P zVl|(V6%}P?WhuO$v~m$o^lEzzCVqqD`DfmAo51{Ys&;Ob&F3+y16H+zuXO@6DR_*;j##GYH3E<$Kch>t`xxrMVima?^WCr1o2 zY%n{`ee2V(eHGRH{l6{)W*$1-j8X_;8HY@Rg@%@{kQKfm9p-(~1(pgmWVptNfr%Ln z=g4z~;Bz5|gp*zy*aoPPFZ!|{Q{b_iJVhF9p5j%nK;82TDvFjzOFR3(EHRHD`Aj*f?5|6%I+-{C${W+s4G?*ICF$%%Z~@xhUz z;{|ZZ_wV1ER<6IXN62~fnVjR*9-zCH*Podgp`%`D`x3rhz)ZlY?M;+}VZJqKjaT#T z9dlY5?s|==V+%~sF{E7T!E28wwNmMd*txeHCm%a0Hg1({u>pb}8+%7bH9~;}3qFmI zxy2O_L%PD@Z3O7PP1`-$e&RD{b-l2 zn3G+|8Wywj19E&f)_enyN__`ONy%r=?y$aj0v7^J0fla(?G9T=LB~pW2U_~q6C=u3 z34X6+WPhWb<&iJpV}`|+pmlLElCg(**It#_($|JAJ=qO6tVmjcB`3IYwXwkZ;tDEd2kW&`H)`9%Ab}-cZh8@Ry(18%>|T3`5-K$=#{M z%;h@POCOrF)jUNn-(P25@$Ee}#D#`D|NLTD<-xFAo4Y;@V%TjT)47ydq{N|uQoa@+ zaU5=CTEG5Ok|s~kdLj@=&YPH+m>nr9f?$js?f4z-sEJ348s#_rA0Dz~WI3){1{ogW zq~EzS8XP|+CZ>Gj^?Q%AMwo&jN3`G&9aL5-2-Bd-`+dull}u^J64GM*oD;W}SwClzk2Z-;#Eyd!^?)GK}sTFo|ftRqDMv zTY-EZX){VbONgmpUg02uY}fR_KI1}@Ey^&0^;vQKXBB8^KK`P}nAaz6y1Z2`rMaz5 z;k~W48EkG+=|N(F37tVxR>gE%g3YVrDcdE#i%KR)#DyE89&Ki3W?;|`XM^BS1s<-Tvxh9`qH^Rdg84SIqP4!(vVxW0G)TqVesak()nII@ilg zd)_Z~SNQ#iH`}mp73>l!Ivo$>?|ZoSR-ArzFZnhU#U4UitX%S{%36B+zpa_UZ$)=` z-KkU$#5BJ-HEenNgFmt>2(dS-{sIjy>AaLqaEFddSAjDam|7{-J!I>>TQWb=Q?3pV0>qG3z zhu`sU3a#KT7|+^CxgPDjJ*94k9gRhb48i&0UzW0BORZkNYWar`!%Fr8f~Iv(Z5X)^ z7ua)@AFe_ju?UIQy?f1Kyn*&#Yz|0PE_qF*(7p$!E4jQYPL%(TVvDR0FYoZz+Y)j5 z8`aJ>u3363cdNf2+b2K|N?v(}Ys7Dt(}d&|$*m7}F)DpP{T;M!4R>Q}dE%4)m8H9r z{AaI;ybRZ(4!acuu*(xIu^_Y1y_sX}^Lw5?k(XY%V#V{_9r6#4q@mD;iRKtm@X_ab zp%+@7RBbv+5mb$lcb^&a?fH(C3=fMN7Vhsb-WJL#;!ZVwiGBhq$=d~ z7#~PgU^;!EB3+C8-8EG!C6bA$nM-%-s=pg2hq;0Ur^tF|Ke&o=UI$X`D6!Gt&^ZJm)^uA%{x0I|sT_J^F9$dp6myKLLl?9eMZiAXb}YUJBxK zqsLR)ur(~S*^FNHX&4)E>!xXW_Rd8l4N0)l!4@+YWgKG1|^*j&a*IHJ00 z|Ek|#ieq{CX%JxpXKGp+18P0SM(r$ugDnYaUfpJuG_h9>F*S6w(t$93ZwM!2HADXy1Ixb07w!n8A`KIV!2E@NPeENXyuJ)c`eho z^6w(~N+RES%dKyv{x|5MYnRBesv#YJRAGszl40)Z&f@H_aruH5qZBlPn1zzNYL(4f zQ1Cbyde#2!vZYq3`mmD6jh&6_?fdxo4MgJh3XGfed}wz3LV3F*EW_FzN6%Q!Ua<#5 zlccuz&%4@18P$C|+4h5LwT?x2Tgp*EMqa&lfhquVL?EXIY^0pO{9LLSx)zRiZbR@d zIN?O-t=-<8TE;I?cUqkJh1QM}sgoO&wg?j-@vMA4nmXj7qs=Bs>PrMZ4b z?yPF;{H&qIe0H~mj!qwg^3$K5ac$LowL9OH=U2xoUyhOPE}CA!q>^gOn#OnON5K^3 z-l5tkx{~xsU%uA&FJ9;Z-X?I_sx`aCuuvq?*`>siTIkCE0qjuw@KEBRS%ishB6qNR z6KP?sHrw94Ovr|9he?ddkdEUWk&k*eDwTs0E3~ysFJETW z);3kVH(IkZe#j(-d+(FI0>KIzZqpb0im${{t=wrt8ToO&2{h6!-TAIo03^(^zJ|O6uGOY zkUv?*ETMdUdcwPk#(FssyqcRQq$&y`+#UWjvXhn76v`PBLf=f7iw>+B{mUexa|iXZ zvI6R`pF{qf?7fbmvJ(9bJmM~7mY#J78*`YLb%X9ow#bL_e2tZaoKRUexFaH$zjf1X zT^cgvj2It^$?pgnLKP+f6Q34yCH~Yxg`ojTa*n5*s!*wC{iA~6x@XsCv`s(q+TKiM z>R3!_0-xEU16s!|msNq^b*B@Q~%Tpnk29zUH+ ztOFi>*TU!I`jV7*g^>B($nvCkTdA<2(KpZ!a}=#$f;Le#U_;fG@@>FPY?FJ6AT zZjbWup_+yHZpd9swxyNKmARF!*;Cu@e=A4WJ=eIWD$(PYVWV^X4{~O}ba#b)z?ogE z2?eD5Yxy%9@-K7q9S!Ap%yMnSvc2s%-15UQXKtn^i;L;Gfxg=>4GVSQGe=?Fr6&V7J`~eUJ$qbR=H}ga z$)M?$LqUN%_VG>jr<)74WSv`iCp{HPNPOqc1)Ia+SeY~j{TQgAqDrZJpzHSK#P^rR zUFID(Z!hLljP|W~xD+mON19r^-PwHaYD;Qmkg^1iW$ic3AN@kcMgX?O9(;T=+Wfp> zY8VcoUW5LFSc7of4qBnIvG=>S38?tkV$CwkN%Lx6weq}rwzsD!%Avt#g|UA zBk%dkr?g^G`p#;Uxw^8k%>PuOz#J;UpGFgoxs->a#P@>5sdvG6^)lsW>xb^&%>33F zg~G*1W`6@~R$q{5#k-T*)+?y&({ zId;t+euidM;I8}G+}b~RQ02tO;Zo9`!n%^4cLlRG!);xU3-TSfa=cQM?!OBp1utcs z_st@*XxTnppmZ<=R|#<~#2*K$g;`uIx(pCLp52gF2LDW(+X`wAEilQ!G zcPsqzeB%cKyQkDcduqBg@4&mOA46-4N64KQHTzKd<-X(>ku|xyLN5;9%*16N6~Q# z>DeKbt8mBELVhx6HuJ{9RQT^VPrPTEs8%`V^27Sg+h9J`?-2rnhlLJ|g$Ta;Ou4wX zSM_|0{v4CI@9M4BxfzPTqzhdESZy0<2YdDy1EDE~LSXgdQbgGcC>f%s{S_s@$}>g9 z75q&>1y;?iGD^HNg948CXD@!0?|yydk@J&2{N#G8dHIl3g>UiUUAgB7S2U}!xGU`u z7nKw>SN27hHYMgWq9>+QSMfyl@X@%e=G!JO|6fTkC7b1}tgMD^-%Gp7gYh2@i63uK zGDkC9*maUyM9C;uJ8m*QTq~51i)Z$3-_^_sS&E<})zAdJ;VhLCe@Rh` zRhfHT+T14J_T8qN1&OmiQ+~?w+S5%!x#hRR*4#YcdTU>TQD|SY0U0!>U$P|V)S}(_ z%xa(b!t__P3K~HVa#QNr7;MW&L|6I78vSfX%RJFp<13o8MFxv{7l6S>7prqB?b8%C zoV97Ym3iUe+tz@M*I!@x#j!s>Ffs9y!}C4=(DkI&BTF6x3p_F5q8PheK?B}KurM}p z2PYx-i2B=<`cCFrjgEZH%=2M$dalit1}pa+SduxFb~Z8_x{nfcm&jjMvuM$m*Q3ak z*xIUxsFhgN9+{{1_5?mTj22GAt? zDd_#&WhtNjrEvE2+&72y07TH!(kAKW3HEJ8N%KnLQ|8N3PhO&2-DS>gkbip0TrF3* z^1DjG&8sq9jF_Y(YC#?+j)E4+bMbY`Wp&88)@G`Wnd<~8)5(I4H@v`^&N@?34UTQ3ear@D8l z#rbCrYs7Lhbe;XV+goGp{<1!U{*G~uj9yfUWH&_XJ1q><1HJJ2_3J!`0qLzynsu3= zEsJT?y;Z$)R6eyF6>~P|p*=|~TKJJ-Y;4^38l^do*D_x+&c0sBf7#l4c=R&?GE#7| zfi$(ICL@!wIMF@8A}Hm-|DRyn`>_1l-WC}J1-GwCr?OD@4|;Q~U^Cw)mDN$|H1e9;w_UQrBkKyjqT3Ip3rgnmFY$8emay$ho9ZJ=^=W8m44?Yu z0toQ5^uc56y>Cq?zr-%rsbo-dGT%|7pnq9L$hk^;_F$cBLd%L2rOj<^)0R#Uk|U1@ zZ$wqr>SFxks9N^~WZ_X78n(MyGA8<0SP=4Y9y1k&+iQ|G%S^F$%so>nn7wkJX;1AM zrNZyE1MQK;pelvjzdzR-b316?>&cnGwQx?Ex9LYb@oGig%GzwFBV4Kcy)46iK3tfH zN1vr>INT`ch9`biX0u2$X~TIxUtenakN{{i-;rthrRL^j@#5}~T4Mi}y$8TXvu~~k zwxNI1a86GYHhjXqy~g1M&Iw+}M&H!EPNxqUm)=oB*KXG!PWQv0-?1dAR>~=5(6Uig zBKFwZDWTCQo>;3!vxtlzzh4NorDaA7^~Bb`oYxcaPniXPy|h1I|0)*2avu3*mt zZA~mNi}-Gc;de?CQMU8$;$GV!)qSpF*aHf_vfwS`3hrV!f0-T?YZ;x3f9t*_d@b0? z^VI=S!pR4xpVHXU>U-~t+io`csPl+G-W~26PTYSF^2`6~QNe*&etB~XSdu+_qC-jr zN8A5Vf@?f3v@9r20$oJ{)da=qCh-)EZn3Dqby-RuWpS|&cGm|X!FFvk9@~|p3L14>pQlwdol6;!H7PZ@!(Sqlecrp;c2G4wq7W;h zc<v0)N55F&D5?WyLz|Bz*jReN%PoUmBnMF;bdTzIa;g z>_joAR?~aMb})@{U`q# z&hkVzIB~W`!uyHtN@xrd8qnTzuASg8W`Kph;*w6^wrn*?1t~ zj?KOsuImFFbu%wS0Q(gdmIZDk0CkByuERo{fKLu;T77!4i=CaFm9-n261g}qW<%=v zPnoUc7ksb3>b+8*V(1CyG}x2anp|zZX1g6uOO9>#fjxmNBnZRkDhq6tzv!5IT4Jon zI!mvmxVM_W`8YvU!9J{s5r!m&`yrijnbFGgvnH%SPoNTg#G!xVgF)bubvC7Jez#TS z*)y$_cASO@xUd_i@0jTL}gQ!aj}Hu2E;2h5o#Q!wmAgT9*aETWA{DPLrFl=Am18d}F=P z72u_v1{#I{o7rc0>W$GIwg3uCOeDX#PS?4QMgZd>@AfXydU{^#rAo7%PqOiZw~KG* z_tlB^<>`%?>lG5bId^Epk1HqgxqtzOPvg)v_#dd z9#*fEKkAp%T2{}nHpVp}jMBZ^Ey0z>fLn6YQFDY68MpEpXzgDscA{+!tu5eTRvMnI z{PL45Tl-}`C+*2MDbG)rA=KjLv!*^ZS@YTH0|4O1&d)E?t9)P_a+hm+Vy1Elne*w` zDO0(KtNsMaUuQz#1O_w}e4_h7^b>`j;Zh^i4$pNgEG$l(xK>mo;Z8PuapsEZQ+L_Z zw}yAW9vq5S(WzNAv661<#~$;aKSAhE$_vuo(PEK5_#(DTr#AP~>lbnc3W*VHi|3+mhhJ4-jHFMAg)QVv`oE`lhW8I(yU zcpKBOkdWA}Q+}MLyxOTJX58YWw5-6#(t>Qn!rTltEi`P@I2sH0K6``L@Fj$)93mk0 z@xGVKDsAzO^ZoF`uk+N}cbTQ+4xau8_{&rl-m=wn;B2 zbgcpkOPxl~nx0_BP2v2VFrDH0blvfjNo=Gw;0GkYPO>$KzPZ@y=(x*FL9cV}m> z&{*?-Jn$&hA@iDRn0tlX`m`S{=2t^Rd1&(VZzHE z%S8-ISXxM(Jjn;4a=uN;63^lcOu}`kaxm}xhDUH0OU_T%>1TQ`i04n0Ts+M@GxC{v zy*`VS~hW$>-5>TIMasXFf+o76WYs=yhdS=$yef zppl|4s2OVxZ%metSi@_|xu_j%qfVXnd~Jh)>rSrkIZX{Y`K+dKPo8`{xh9W`2?!?Y zRjUwae*;$^{yL6cK`!Ua{O9vmrLN~3x)a8us|nHAAB)R17d=f(kLUkuf--fJt%~Kk z!o19=HLN?MXv)kkCg}Pn6fwL2DFy_bgM7!B)06sRsPn>drM@(Unt$c~@{&5kn^T{E zD8q`{uaYLQIE%35kzU9ie~0}XIs58be3VPS8=yvcskZSK9Vl~Pc%0X{t6jc2?fJbn zTPrHfFLr6q?Jmnx)RC>}zqo$an^(nj-W#VP;72MP8=pyBNPqXjkFZvx@^cbc8_E)zV^n9gw~_tV$kXF(IQkY2yyDEdR7GtpkX@H^?q~qk)Si%VLvh~4 zT1`!ji1qWD2eh^KHd#~1XD@xV{5*Pk-46EXA+;Cp?nke2^K-Rc?*4P42^ZBECdx&C z$=d>?UMSB-HBJG+r(`%eADO0E~GCxrm zrLzt{voTP=zcyYuUi6J4hY{&In-B9YQ}6Pfrn$^{ zn-aeV96c^ta`>)sm~iY%*eYiWw^7X`V6Wt8&1R{*Pa0?M8eCWOpgSV@&;rC3u<$iA zOSL$XLKZtF94l7P*9^bZ@#Y-e#4ohWFNPxxx62S1O$CK6u2x!(+FOLH>@<>z=wvow z+Y*0qP?ezYp_zT2hv`5$kn!>w=|nP{oqagzIER|;@ArY|@7`Uz9zpbn!lK>wdcI-n zGsc0cD+BLZskzt1RsxhIiYh#|XMtVMI>R0VnTRv||9VCTPJBSe7bmld683z%f!**{n$MHEw#=13$L|D4nwuhrP5CO%J52ww{*bWiYCc{(y ziPOWr&F4%^Mq^E*T|e;d-Y&1g_0%>pRpL5YOOAatvA|E zuP?4|dr2Yl^OTXLsLeUMxqMHBS~O>qCTknLMhw>8vGH-(%VZfH^>F z+JY+$KrrNo1^_yLvJ#w}Z-J~px>p-Zbu90UqLa+h{MuN}IW-Z=VJduAYFKNxza}-b z_{!*D7vddsMXVRWgmrhnJBgt3`rB z+RJu4%RQHa!zVPT{c;aW?yc&slT$N@pS+(asZMqVf6vMjD zWo9H&mV9R=L19u}(GqLrB9r)Yv1U8__ z8Jt8~@|q(p-EZ|8P3kA{A-wJ`yuFW+I_9aqdyv+7(eE}lyNt3DLcEq(0i9g?gv14> z))>`?o83=W7sMZSEbNb}2U<|z>?`U_`6J=O8|@>f`{ExWMVyM4n7<{?-1Pj*PE!{u zzwL$Vy)4;#i7;JN5^ zN1xX9Xu!$BmS5A0W5aRZ{`# zAmaOJ7GmggE`IhNUUfL>#TpKVXwjA&yZEtrc~+w!w2W#LAmYcnAU0T`7shX%7)zr? zOuM{gPK4EpJ!=v-Hru}SFjAUP-XEHKt(_Kl`p3#f_Fn*b(4!h|infZdUXc>hRXgyR zTz*=eBY3{_98Za_7m5{-T<2}O)hkGb=K98awvr|IgCL+mg1ua`bcf1MO4;ZPDSvu? z&#Zq#c@&NNH9&7!Sf!Hh2jg0@!tM|2)px;+hH#ZaGIN`Q*j0w=H*n3(F(>JLBca-+ zTKGUVOHyEkBGQl*#JDv=S?V?@5Zhyu|2xF4Bi$ae=itb5i{{mc+7F~T+CVsZC%QHR zXFoE3_LgVMrWov+L;EyGh2_Kx+JpEx=0CpWM(E7+d^Kn`B{z0>gntRk=v~}X+jb+$ zG|5a~Eavs|d*`%&&%Mk7D515+(J9YPVAgMzl$4-?@`yXzb;|nZ$Cwrc{oPIDAmml4|G>A86G=IWS5i?y+(*2^LLBGC2*;uJ?* zf(Gi7PTsmPti;;!wvbYfV?^SfiK!+(|2^J{`U_lJ?uJ|>f`%CgVGy{(+l23$s6Wpd z16Dgk97j+bfI=1m!V3_og^~UxW#fy`v%35Pt^~jlp-1H#GGW$~e_6mm-q2h(PPb%$ zeP+T1F+21YDu-RUkEiY4sqYOXg2CC>4UcUSeSCaQT}cyat+PY?As?V%YCUPc-!X9Qy-7hV@u0l-uEH~ zr%P3w!f;*P-mvSjW++ogL~(_f9wr26*m?5iKUo#O%i?}MH|LgHM9;*3jqNMYfYpN# zWT+Bae&f8&a=BKwg_)i{YqP+S^UV*IJvgR4bCa+Bqf%J6)so4i@3SV71}O$f6wms; zA2hFdb6&%dVh44|=p%Q{>Or}g;4!W}fJ%vq+y8E=c>v^YHMZh8;j-jBGVK~?k_nU7N*zl9imt-|DJ|X zAXv{r>EqQd+w=ZiH@C&;l=KEs(I^F2CMZRa*&qube>IRv;2YRakYO>(!&BAn(YVBo z0ae7+(y3>iN8|jLDqFS^+C8XeDmpz!$QCrL4zKVjN-@9m+#alu=x4e4A(sRol&9CyuraqseJaGv-7N5$~+tktF z2>~6%y0LVe+rXmjt_ahXXQ$0-qZVDwq1Nu%(-`!-b}|)Ze-1{vWg4ANOGc#aL4ME9HLgHbe>UNex z78?haQC?XIEu)9W50<1*cXVdbl*#itl!qv;@QaY;J+!Y^(iRJ5dqUQ`nswEN`}cO5 z_J`W#c}GD$GiHHyTl)OFJ-46smt6WiVc~04;}{ML-V*j3kap6qT6eL9`YUT#ZWkra zyR_0j^j@%GgVNZ!1}sSsb&UMFy@wAao0_Y|MfYG^?l#T6*&h_w3}>9B1w7cKPU8?H|C6g#dEvuVlX5f>L15D*|{ zrR>cc|7}OcJT|MWB;M$bHANKSOA;cCA|7*x4`sW}@FQBAt2$#*uC=jNzvq*KfBP8! zHj##xG3kSo##zgZ?QJgk@_)$uuvE*uE=4_L`|nMg%D1~WlSX<lgfr{w?P)g=Tf@Yj7-E#`esBGlsOM4xRM)Owuc19j?|}~bWtQ_HmclT+aK~;P!N+rc=aGMjDA#;NE@yVD8+} z-1s=F@@C!qGwUU@6!$L$wD*ZM=+fX|NPAx0J4V$PbuW|E(&oE2sxC+@W^D43vU~S^ zJAFdskCi2+xZeKrUaLsA?OGIzuiC4{o%ApYY|{Moq496OzTHOew^}ZccMDd)d!QX! z2)S0?--Ez+(ViF$Al-jYm|~4$#P%yqnzr^OzirrWb7kg-ZetDxgmQmYFVnL6+1B0# zU`>{`-#XGZV%NNbPcYN{v(w8RdG7Vq&!f~bbDFaD)o$;yUlv#5kfQrxXpIZ+>~3%C z$TRs}s;fwWOD`=(y!09S@+;KwW(Nbdh7g8cK}^_fV#3(B=-cirNw_@z=W9;P-|Le! znkPSWH&vIUwE{xBb3`^G+-k+I=h#FoRSL~kGOjU#q)U@#6++ zgWq;)X`eY(I0wU+I11Io+PI0y0f^a0}ZO;2Ml7 zH*u)xT5{w_KNz;Kl1szAUEm}~Q3ygknAo-Z^ke&>5C9~{Q;nnsTrML+*DLsD-6=u) z**f>Va;}6{7E$Wv2`{X9ZQQ+1bgk?Kdr66PqMV~NOGc^{tKgM-{2%Y)5U%DZlF;y! z;aLN+M1h|wk&u}NFA{+0A44r#36GWA?JWT1F1Yyl72@qY`@UD}Ube)imJHRLyChd| zkkLWL&^&T}2ivOcD{nB9&vr}CAD(<#ye}KX49Q$Jb9W%O7@TMtZHIwRl7e8uVLKW{Z0XHHDvnGC^U&+Igh4GavJ zmzh_zd{C*-Dxh9zxwAL;kmktBl`DZB4-Kgm40%EPe9++Jzkiu1IeFXscAKX%)nt+z z%0h^4X3Y~>q*sqSA+Pi1(49fGJNzh^WefVZ-Pl!bEwN65bJXJ>AIvtr^4EJaKaY#s zuk&1yIWnEK>*J&2OLtOPo~etk-qVLyg;Mcp+)YwZFA-_hEzQ~xM1MS}&4aS=wAeD| z&t!HVG|jblyky?vm}70<_=Ed3{435`>7)b>e@)BULg{CFg@s+vgag_9G5ZlnDxo5E ziO&D$L6^NKg(RuP2f7?M@?)flof(5Ish*5}(Q@1P_8K>D!Zb|^$$d3W5%MBg6%H|Z z$>*lH)F^J1TNFukXF!5-lRA+2cx9bGu`BGh=kYyiTe&=tW#H2(9mm<1Enc5>C_C7% z9y@Aew3~IyQmA;du1>vBN*3E1C5$Nn9Yw{n={k{p=>DQAuya#WfxjbZ~)2+IqTq1Fe~E{O{xG&94sE@Y^UCD;ldhG%4p7Uy9ET3dC9unN|vt{iksPUYtxFdSaCeEk2YlItFk38P{&&g^2 z75QAht+iIuUlW5pJtuc$LVzOp;X`G^J{Ep3&l;}s{&Fx>q_@O+jfIeJ;fRkf%P`4f zd_U2D9sh6>F@+Yb;=Iuo{VCi=C%ySbmt5V%uHxPi^6LLS&%Ewxb#inM0Fh4@MR!^e zVnK;6EWgfXC&stQT(Z1Ft-Or^!?{t9xR?8!#TfA-T<&#geq0UVJQuGf=_T=t8N#6VGOlRVGYi0k4IPM`gquV^t z{%d_d>@M8;d;=ZzzhWq3dpZ%Hb7AWbF#Hp61pW#czG?hAmY?(_!Ai(Yc6N5ytDkVf z{qQT9uWC(CKNxF)cB}NFxfWIc%Z-9JFcD~BKnw5G4Nx*uTXJ~+6Q)+w7)*Ib0f)!9 zW5+}6)*Z9GmJk|+DA0)0qqi94aIE!#>r#1g6@Ma7BFGah^uHOgbQ-n?^B-uj{T*tj z{BB)}T}7&gvs@E0{HW*kUiJA|dd8BgE=g;IxQ}4RVTeKIiUY2 zV9F!>)Bl~;Mzw~(-`R2dk7tD=XPDOaMBS?{X!$Z*aWnkZ@32R$xL0vav=8#bl3z=T zJ{XJp3;&zdz21oO*kmFK|DLN~kwdak^=qN!=D&=u(SYaK8>2beC|-Tb;~$B&VWs(Z z2+I7$?$5)nk^4y90ac%wuiOaVXX>4kvlQ7r$dfl>TK{KX{ju{X1L-#kOLqKFCW17! zgalgpI7`LNi@z6(fp3oy8*!q41b}s>cIBvPD#tj2uXi6le7n8!Vj}YY!l&;L7)Zd$ zEj+fT8vphQu22cuu-uhkUNW0o{MiahCaQ#TC`i=%-89O0{5QBri5RYjyL;adZO})=o5uw z4;2W7tTrmsj}L-}AKGt@b>%RG?llT1u~Xayb~#*n5~jub@7%w)hQ2mSVrwePg^x4~{-G@u|_ zIuthTqPfb2iwm>%Rb5HT@;9imIs1Wp4&*^H$|fBivpj}9{Ou3NyV__ztUfdqBI>bh$G=T#9rZkUyL*1K7%=4kQB%HvdDF z#G)vK2BA*I4K)|g_1!YmcHtY3P;1(0ihnzS^J>4(4LESHtID;grkA6gsY+0~Wn_9G z?}&=%jBrm$%?pwNI}gu$E5C0p@j$H>LW%=$SaIZ`m}(K&Oj2xI+;tzHf*%dKn(sOF zl%SmCu;v~irln3A-8!syu(9Q%VjpCBR@2kts?$UGnkBy0QY|)>(P-dz^QMfv{MP{z zNx=m5Rvz6!QMrq9t?rlgC3@LPD70-y{6dz|aVsBL30NZVQMri?vr>=qX=6R52`QLrTB{ zPYUd5=B=;thXj?hq#aO^&^+tJa<$4&4FfEeLn!K?J&P;E0&*`gVZPdlS(X|#Hz@wR zxTy4S0pw5!mIU3uUpYKNA`QnE%p&qdnHlYeyPd;bqiyuXmY?sf)LOq-wRbacHpbqm*#ih1gsiqO8)NHK7S*kn^v6RYmB@TCFE$@x~>XeUaf=6(4p($;~p?>JE6 zdgwG@1uq`x9uA9$ID(ac63i$><5nfv{=l=6IybD+%7Kf4;s@jDF4(0?%9ft%vx6v? z$0(xZ#(tU#Esz z-Zm~HWfKsA3l}aRFjsycT-vydEC62)acgt5@gUq?;5f9^a22U@Zy28;x@GnVM{%$B zjIft24F?0o2OHCWhlDscHzD5ZT{O9p)aixTl-D3X8&Go~mxs~FoU!m4LrZ~z=6@h$ z4EOBXbw!X^*&CobjG`|zT%XhkTn>J8j1C_Q1HYGN>B{X3xIG17KqofE6Yb{>Fn)AR z-QbhmSV%ifkImQ<04#JyalbSVPXH$H&e8IqG1gcG$~}Byqa@|pm?gSa0E<^4oK=>X ze}gyPa@b?e88IrFb(3f_jysT|Vc*?4XV0RbNDINbc!daCU>Pu#N4t?i<0Y*97@3cQdo0u zqY#HZ{&0rCR(O8)*^KLZa7uh=T;_G~i2~_3{yz%vQ}8J8d`Zb=hIFL3<0BJb8r0@_ zfOP2GNJU|+TW^go2TaZTOCajlE;J$hJ5hJYg z{Z(j#DM0BX=+2#wXel*!4s0`e2~E~c&I$2YC$55XtQ{|$C6<#{v`dgo9bi)EU|;j` z3D_agZjmuoztZ1~FUFR{?uSkCQH0zcKVeSP0&1n zd9I) zG%y1_V#4MrO@rMdBa2Y$bh_JvgMa2I%SfUwrb&M0Q9E%_LybINJtho1 zAr1}>JZf)Sj@^xR{t4@!omc65-QQ@1t#s`m=I8|G$l1%dC>l}1l8+t4wui1de5@By z`x@Pn`AH7MIqct+P(}o>pcix}To?+E)Twxff0^hlo^1u#40*ICsmOM4{`yew!BPVX zYCrx>5f+-i7CW;C>z<1FLx+4Gu_#QQeGGmLfa2%)4n(1P21vzRL>T1s_ng}S+5A0Z6BX9>FG(?Z7GXU0t%dfWLjsysxebzm! z(%uRl-2cuV%cvu+gd+l)*@?b!(B$BP*n&br>We%V5sG59LhI$aQY!(b>3lD`daB z5EOl{Cy5Vdb$GZ!>hSonWAO7%Js(&wr)~_G)2eLyzM6k+i)GW#u(KQ?_Zk~!fOr~n zR~&|!nhuA)Dguhjz>*+jzy!KMJ9!d;IovEE1doA^pn5whh7ZXqDXsg*+O?>h3qa`t zHsEIRXJEst1qs(n%v2IQF`zh;2)I0d)N8ptxE{O3aM{kcx57SR&p=2Nd4U^5i3lJ3 zhGvSEG#ZU#=+s9r5qjZCr-kX_Pf)X0QJiVIySoeK#wt)4>i_`_h8Cw5H@kx5d=;Q! z<%i;85LG;KM0@V_TYhP3(ha^xuU@?Zi>y}TC#YLQh5+{)e)lTz1i}At|H%(3r4r-f`5n*@ z@eZQyv|}?jNzw9S8!#0g1*$}A;eb$zzmh`bWqZ8eV;M=DlZS_A_iiHuMKG~6*^|_y zJ&t_4lIn$#!a)w?I+5s&U!T%Sf?R3|ax)$7X|8J5XhH3xZ`xPvN?%HZ9+8f0>|8@?|tZ+H(e5 zJo9|D5fY*4=;b5}9JnY!CuuDLMs!{)GzT{&0p1k&-ku`_yWnmGJ7}nI6(IUFHAQctPPwF}xA(047 zlG4kfX(5Mx^=`g}=lXe>=yk&dx~Z{u^a|6gTNg*ai^8kVgLF$={|HK!d3HTY<(VYX z6!r<(68csIUlXS_>WH>q%jniHz#ElTy}xqGL$e*xGs5NSVd%apX;lC~ONM;B&#=l>m-C$=~h=!TtO)2NW)LTF>d8IS*^bh@=Tq$xTY8W z!K-7_#*LNt4kh8oiBJqQm3<7WF zqQ{(tqpAi7Df5O6s(GtdD9*L!-;bf~glcA*t(YhwrXdMT zx9Jc+c#zDyGIuYWhaiOO-S`+tdolUpL^Ot)gQ(O8Tv{QGv>W~r8CW_}2GAP`4Z)7p zm4OEKE`5E=qbt0KpXhzn$;>1pC-)N*@m!P3bJy~tGcbWOW(covQe+DEV@DDpt$fK~ zuT*?k6kkZ_g9l>9Wos97sJ+GzITcsJiHM0WCuMKfcb>~wP3oqHV?}Af`y`Cd3xR%d zE3&~oIOvLTkesm=f4~o})IxonqD{b-@9HKdNszt5J<^HVY1X$T#LrHTTA7G+M|#m=kualb^87C?@18#qUr3+H<(!p3hb z^_xh{7(>pC5hWiRIMTRGv^~|dOxNaA>cO8UGj8ATDZoXX^%Y?=yv4Quj4Os0EUTc+OpK7*kte{%|LQLq! zgNrz$dj9hdT?dQvIzTf%;o+6W9)q9V_J z_}P3>IHU11I+pqj9f@5rvA((5VS-kLs0!et z+%geF(r^Z+(0-vEauHwK-R#OnKR1&T0wS9^e+nTx&XNx75~-I_R1COzb98FT2G?++ zg+V@MWn#h#BsILoNpPo#+mApf=JMPaCAPt*#un{Ix~&IC$H!w*c6iRc%uQkvFav{C z{khH|lrw&{Ezt5Pfso#tzZxQ5A0^L>zQW{#8TE3pKw_KvvUA=mhOZ{o_R@Wm~ z(HDflM$*StChO-8MhYr2?_gD9W0^Hk-`HU;OI{CyC;-+4vFS*2MiTm*a!2HS*K*O& z(?3Kmfvg3w-HXfP-(cPs={BN;7PB$@Hwh<2FDEGD$w4BEM)ZhVn}|CiKu*4pg>XPF z4MiN4m9Z)CQyZ<|_W+f9=R#<56Y;G6L!8UM4ek+{X**-+Y+royG${kFA9CqrXhHqEGNYY#@3@_ zD>&n=8opuw4*vDu_rhjiDhgw=9&wF8KFZHQD2(<8A+7{8laPwtxDD8B^07RQ;4;f)_UXC zt)&N@TgURVD5apxNkrL`tQ~P;~;yu>MRPc zxTOR*DgwGDcNu+jWQw3M9Wr&od5moR(Lw@~d#u$Dn?-i!FKt*Ts659$d2(OeU6_l@ z5+w*UAHFnC|MhR{B{+|nYWu9*$O;jcScFyR-Qu@yToqCeX;Q0PT+k4E$GZ*b08Dw) zUw;vIkgvJfKf0NLK@ELsjGS%n-WNVMo<4+^(nwUlm4SP!%y3$Ot@=#A{3hru98s#N z&(JGAbLI?+H!d!@nE@#Xod2Ghe2;k-J-ycJQyq;RYMdAlb&O?5W&4S!19#8Tn3DhmGox6MB%%OcVVSn!;Yf5c|Ab)HKv zXDkJ`5|s_4jI&%hi7orfpS$szVzP0`$2`{x#iWZv3f&z6tnv##Cu%)e_8*-eXz!%^ z$56ufpG!%OqmLCJwtp8b-+bYoGWywbwX0|x|!?{R+jJW#;+xBqlfi_7TE`arlw&`Xg; z-?(uDL0=?MuT(kW5z`v^&%lLEy+{6bI8L_he;-z`T_U^rwMhT(hoJ9d#Yk(YuAmZ|ls;54|M!$(NA{qA`+&c@kzPFe zZb&TI#MCrEAXKA{==8ctz4Z6@-$1o-{fXm}A-oZj&4O7qwX`i6wV{4V#RvW#UmnMJ z!*#mMM2!)#=`)hYUbVg5K08yqjI%ZE-0jjW^{SDGDaJRIvY#J&y*g)iyZJ1)e_&zA z-A6o|wXPWdJr+kbv$GYU6fNQKc6>Nm$w3-<&D-xd0E-d(l0~( z-HZ0_>v(S1@vX-x3-_=y@^V|{8xNU(ZJ>ns47GI7uFjsq9aEh)_r;%+lNS33hM{-O z-)qsN7?HVZd$hgA$CQqMhaQhpe1@98|9_QTdpOkj9zKC9Fr zC1J)Gxm%2)T*K#=?4b-vOJnDTHjawy+QNB@f#Iq1ZI{O za(xw6Tf~&6__^eMb3da=EoZ{V9<^@sb1(CM{{9BlTlV>bjawqrqob}D29niF3-=b& zZst2!kn}-zY1Ow#x@ns(ULWC3==s)qiH?jI#hRo|85S6XCHcKxs&Pj!Q|fv^w}N_U z!22CuFB1(}_LrP=bE?1ov1t0TZ?(fn`rU20PL6InbA=RLt*UrR{?fIa+h1K{$N5Jt zKYZSjxT52EWqkZz<*n{=7N1qx){D`4_uI7c>auM&G3Rr(&w{we%3kz%1?-J^C%NV& zk6BN*T&>Fue&ILF&M9^Yyt@Ar?wD8Us%+|^xr_;4B*?mL1%>icS~@3eZ{ouY-KG`! zTdk`;?jxs1d^25Lu0gZ@dslL)hC|3zXQ~6&th1~$Y#?SXbHZ7;j#{o(bTTHrBYqp= zn<~@ywJL?lTgi*A5&vp6OLQkERe?DCnq{jG&S$JV@UCd@U~hA?E33ZP_}Upis=brw z0|e|{IBe3>&|s#EBV`kul0ZeZu41DTi&jczdH6@>3(%M$O1ySVwe;tuZ??4btItg= z%KlTWk`0qthn`*a46$LnKNzfTMw(%PtmrFUr|bmkF@G8%$`OZbq6PIzTrlB}E49Q- zD4fQo+np$sS8@g=0Dwv#x8zoPv>DmZ@Jt$Pjj<1f&*vk{BpL*yq3b4wT-lvX#eFcL zSfgZwO2x#5!lEDTtfGs8L$M)?Zi_B4Zex6>IM~9d0|!P3Bu@ZQ*V@P)K&QSiw0VlR z*8YzlAXZat<$Gcfg??nVghVLzyA!y`P`?+~aWkcbXO}8iOl;G^))z1qE|*RH!r&TO zb)!I0R@NkM4?uG2p7-Z`b{7DTlkH%OH!g1f4dBZMp%A$;^tbToIq}`)GJ{EW#k)@f zwUg_N>SAUyyUuo6K1#+LjFJ)kob9Kbk`-`D*&R!`T^t~^Oj5LrQt6lDt=mVfizj;C6khn%s6?!-+b4h;LP z^j1t4rFLIlP!tp$wDqs(p6GE;YuMe^*S!70g2CW}17%0>!R%~6GJuC=MHXtny30Qr zvl1HLHWi<;p*%PBfbitk>;#>A##VfH8wJ6&^(rJ1>0wA_A2u{*b%vQr>KGVoX=sbs z2K`%i4cWj>Z*66C_Z?4>nkKafRZ5&&!Nt!m3F9|>rxTAcl{<~1tEzfLn5U7Ap2?HD z1aNeYRq9kuO%Z+FGh{w@Qwq5$BE>QA2*$U0#va7f$?#l;x29B1!Nq?6R}W%QzhmP8 zC7@#4ku00+q?J1(x^%)%ao)z$R!g46s%<>LykV_1&Y>;S)ygsOt*pFI&o=7ATC5Di zwDI=^+GC841VugxxFjvDDpl5yP?&5G5N?y{RU@O`k`?q&kz^#^dKmy*f=HF3WoT%q ztsNVv!ZI-1aea2Vz3jE)^z*c%9+lCwK@9S7REk?MJeMN{GYUK|f5>|^??-Z;glUhT#Qp2BadI^xmfzH}ZSL3gnT-mzbp#+RiwEY>S ziI0eb3Oyqszwhho!&nJ@#nYd*Zkjn(g|tC~tZKts3N-9|O2_NhfDDX%$;wjBvO+xO z`3n{-AdwQ1l5*MGg<0aGFRe3L?|=8ece>V1CH?5EnUie)^Kw4S>kt zLH7rDo%RtUm1o$uG6=}Jp&KtgxK|HC>r1f(%UyO1J%94HW-`e^9ow7t(RC<7=jtC6=P1?m?_8_wN-4JkW7kH-#m3QijGobOYUFg=#MD$}*|LQI2$6o+q5QRG0`n?kSJxEU zt5p=)f@U6ulNU67b4`JsYYReV<0Q;uaVI0oVopP~khfz^7lR zKXPK;QATN65CkOX{G|p(Tog%AQI_Mg*@c0s8#IX>5qwV_GgsMw$&s&TM3H?HoC$U% z2%=@P&iO(mXN!eEOgH|tcx>HYM_s+T6wqp;Q~8GdCVB#+!^CSPmpJnMYrLEC3%Bh% z0DGd{E#tgBp2S$Bs>r4FH6LJ2&>s8QxV?p$C=_Pw{+$4t%37d<;LvM6L7ZglT8jE6>*4%5zs-W%kf_bqGnT zS96m(ZWP`T;|OWOhzyktVbexP?9kPTKAGD4tGQ42kK}?p`0RlLfX7h>To4A1ot~(8 z8SO|@SjG0vtPRM`8z6gF^8(JLkhzj8Dpx?SDr)T?YZLVpxk z*A#hld4WzZw|Md5en!o_5*#Gp2T$4yNOE$!;EKkLr&W4j%Nerj){5u}LHfMu<&<>I z)Z@s}S;PG=2oEg=MpKX5FX_-XG-B_Av$Hcse4~2+9|dPW#bmAU+$DZ5WFDZnvrn5UF`>7;eH$x5XO1nmD-w2#KaA9d@^}A<_e@{L zGGhrbGjCdc*n;Iym0wyqjJ^RDF=iwNit-O5#AnQozW~j)^fs^!x2(LENcg)Xe1A8rsZmmgIov)|}%_^M4^U%y#y`T-0H98zC83=iCS29>qx!v*f1kKwQ#xfzI`*`1R z`Y7XhXjp_8AeQCO(#r^Y%WhQQ{3nsZh5!bGeMR#_ULdM}9QlzY8F{^<5zVGy8E6DR z*p@xBnHQA1Nu<}C9N5-?{&*ms2$Vt9NJbdafiHleVIE5D&h|T>7MjE7j9v(1;&hi- zFCd3-^~1g)Am+I5JS?=nREjPI(T;KhlO+i$DKqTG#P%#KkqmyCX{~Z2@y$tvSa<$~ zQ9^WCp@t7nw*Mw3yB|J$K&cJR>X7`rc`$@}Aas(Q6uEDXZz==D#utqsE{Lgv<>AD* zEA0n=WE6JU7$>=V>v$2j7Zv1p%#s}qI-TdRgCOUSj>2I=OFCU#ysWGYmVD~1%$JXY z%VRRmJ7j2$GK*i@XC-^H?sVtk1wv2N??xq>=4PPVd;a@k9rK>(T>>9jN+NK<2i3nQ z-D;-Hr|wmrBYJRyCOok|(HWV9k6{(JkeKh!{nsr{u1o2Ou`=KWU|sMV^OaKPd$WU{DIsVOOr#Bal&*TfC{yT66Q$+P`iOnX;n6aQ`FIt!Cr IBiHc%0rHCj6#xJL diff --git a/src/_binaries/Database/dbImport/dbImport-binary.yaml b/src/_binaries/Database/dbImport/dbImport-binary.yaml index b2c198bb..0cc15986 100644 --- a/src/_binaries/Database/dbImport/dbImport-binary.yaml +++ b/src/_binaries/Database/dbImport/dbImport-binary.yaml @@ -3,7 +3,9 @@ extends: - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsMysqlCollationName.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsMysqlTarget.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsProfile.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsTables.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsMysqlSource.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" @@ -12,9 +14,7 @@ vars: compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/dbImport" - relativeRootDirBasedOnTargetDir: .. - srcDirs: - - ${BASH_TOOLS_ROOT_DIR}/src + binData: commands: default: @@ -31,6 +31,7 @@ binData: 20: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbImport/dbImport-options.sh mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbImport/dbImport-main.sh help: Import source db into target db using eventual table filter. + longDescription: longDescriptionFunction args: - help: The name of the source/remote database. type: String diff --git a/src/_binaries/Database/dbImport/dbImport-options.sh b/src/_binaries/Database/dbImport/dbImport-options.sh index 7c3980a2..9ef914d0 100755 --- a/src/_binaries/Database/dbImport/dbImport-options.sh +++ b/src/_binaries/Database/dbImport/dbImport-options.sh @@ -12,10 +12,7 @@ declare PROFILES_DIR declare HOME_PROFILES_DIR beforeParseCallback() { - BashTools::Conf::requireLoad - Env::requireLoad - UI::requireTheme - Log::requireLoad + defaultBeforeParseCallback Linux::requireRealpathCommand Linux::requireExecutedAsUser } @@ -33,32 +30,16 @@ optionHelpCallback() { } longDescriptionFunction() { - local profilesList="" - local dsnList="" - dsnList="$(Conf::getMergedList "dsn" "env")" - profilesList="$(Conf::getMergedList "dbImportProfiles" "sh" || true)" - - echo -e "${__HELP_TITLE}Default profiles directory:${__HELP_NORMAL}" - echo -e "${PROFILES_DIR-configuration error}" - - echo -e "${__HELP_TITLE}User profiles directory:${__HELP_NORMAL}" - echo -e "${HOME_PROFILES_DIR-configuration error}" - echo -e "Allows to override profiles defined in 'Default profiles directory'" - - echo -e "${__HELP_TITLE}List of available profiles:${__HELP_NORMAL}" - echo -e "${profilesList}" - - echo -e "${__HELP_TITLE}List of available dsn:${__HELP_NORMAL}" - echo -e "${dsnList}" - - echo -e "${__HELP_TITLE}Aws s3 location:${__HELP_NORMAL}" - echo -e "${S3_BASE_URL}" + mysqlSourceLongDescription + echo + profileOptionLongDescription echo - echo -e "${__HELP_TITLE}Example 1: from one database to another one${__HELP_NORMAL}" - echo -e "${__HELP_EXAMPLE}TODO${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}Examples${__HELP_NORMAL}" + echo -e " 1. from one database to another one" + echo -e " ${__HELP_EXAMPLE}dbImport --from-dsn localhost --target-dsn remote fromDb toDb${__HELP_NORMAL}" echo - echo -e "${__HELP_TITLE}Example 2: import from S3${__HELP_NORMAL}" - echo -e "${__HELP_EXAMPLE}TODO${__HELP_NORMAL}" + echo -e " 2. import from S3" + echo -e " ${__HELP_EXAMPLE}dbImport --from-aws awsFile.tar.gz --target-dsn localhost fromDb toDb${__HELP_NORMAL}" Db::checkRequirements } diff --git a/src/_binaries/Database/dbImport/testsData/dbImport.help.txt b/src/_binaries/Database/dbImport/testsData/dbImport.help.txt index 7c2bf8ff..0e8602cc 100644 --- a/src/_binaries/Database/dbImport/testsData/dbImport.help.txt +++ b/src/_binaries/Database/dbImport/testsData/dbImport.help.txt @@ -7,9 +7,9 @@ [--profile|-p ] [--tables ] [--skip-schema|-s] [--from-dsn|-f ] [--from-aws|-a ] [--help|-h] [--config] [--bash-framework-config ] [--verbose|-v] - [-vv] [-vvv] [--env-file ] [--log-level ] - [--log-file ] [--display-level ] [--no-color] - [--theme ] [--version] [--quiet|-q] + [-vv] [-vvv] [--log-level ] [--log-file ] + [--display-level ] [--no-color] [--theme ] [--version] + [--quiet|-q] ARGUMENTS: fromDbName {single} (mandatory) @@ -30,13 +30,12 @@ PROFILE OPTIONS: --profile, -p  {single} - The name of the profile to use in order to include or exclude tables. - Default value: default + The name of the profile to use in order to + include or exclude tables. --tables  {single} Import only table specified in the list. If aws mode, ignore profile option. - SOURCE OPTIONS: --skip-schema, -s {single} Avoids to import the schema. @@ -62,9 +61,6 @@ Debug level verbose mode (alias of --display-level DEBUG) -vvv {single} Trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-con - fig option instead) --log-level  {single} Set log level Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE @@ -85,6 +81,44 @@ --quiet, -q {single} Quiet mode, doesn't display any output. + +DESCRIPTION: + Data Source Name (DSN) + Default dsn directory: + /bash/conf/dsn + + User dsn directory: + home/.bash-tools/dsn + Allows to override dsn defined in "Default dsn directory" + + List of available dsn: + - default.local + - default.remote + - localhost-root + + Aws s3 location: + s3://s3server/exports + + Profiles + Default profiles directory: + /bash/conf/dbImportProfiles + + User profiles directory: + home/.bash-tools/dbImportProfiles + Allows to override profiles defined in "Default profiles directory" + + List of available profiles: + - all + - default + - none + + Examples + 1. from one database to another one + dbImport --from-dsn localhost --target-dsn remote fromDb toDb + + 2. import from S3 + dbImport --from-aws awsFile.tar.gz --target-dsn localhost fromDb toDb + VERSION: 3.0 AUTHOR: [François Chastanet](https://github.com/fchastanet) diff --git a/src/_binaries/Database/dbImportProfile/dbImportProfile-binary.yaml b/src/_binaries/Database/dbImportProfile/dbImportProfile-binary.yaml index 7249502c..11a1ec54 100644 --- a/src/_binaries/Database/dbImportProfile/dbImportProfile-binary.yaml +++ b/src/_binaries/Database/dbImportProfile/dbImportProfile-binary.yaml @@ -1,6 +1,9 @@ extends: + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsRatio.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsProfile.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsFromDsn.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" @@ -9,9 +12,7 @@ vars: compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/dbImportProfile" - relativeRootDirBasedOnTargetDir: .. - srcDirs: - - ${BASH_TOOLS_ROOT_DIR}/src + binData: commands: default: @@ -25,31 +26,10 @@ binData: callbacks: - dbImportProfileCommandCallback@100 definitionFiles: - 20: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbImportProfile/dbImportProfile-options.sh - 99: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.sh + 99: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbImportProfile/dbImportProfile-options.sh mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbImportProfile/dbImportProfile-main.sh help: Generate optimized profiles to be used by dbImport. longDescription: longDescriptionFunction - options: - - variableName: optionProfile - group: OptionsGroup - type: String - help: optionProfileHelpFunction - helpValueName: profile - alts: - - --profile - - -p - - - variableName: optionRatio - group: OptionsGroup - type: String - help: optionRatioHelpFunction - helpValueName: ratio - defaultValue: 70 - alts: - - --ratio - - -r - args: - help: The name of the source/remote database. type: String diff --git a/src/_binaries/Database/dbImportProfile/dbImportProfile-main.sh b/src/_binaries/Database/dbImportProfile/dbImportProfile-main.sh index 0aecadda..6485b4f9 100755 --- a/src/_binaries/Database/dbImportProfile/dbImportProfile-main.sh +++ b/src/_binaries/Database/dbImportProfile/dbImportProfile-main.sh @@ -12,12 +12,6 @@ WHERE ORDER BY maxSize DESC EOM2 -# check dependencies -Linux::requireExecutedAsUser -Linux::requireRealpathCommand -Assert::commandExists mysql "sudo apt-get install -y mysql-client" -Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" - # create db instance declare -Agx dbFromInstance diff --git a/src/_binaries/Database/dbImportProfile/dbImportProfile-options.sh b/src/_binaries/Database/dbImportProfile/dbImportProfile-options.sh index 7cf29b78..c8a7cf90 100755 --- a/src/_binaries/Database/dbImportProfile/dbImportProfile-options.sh +++ b/src/_binaries/Database/dbImportProfile/dbImportProfile-options.sh @@ -3,36 +3,37 @@ declare defaultFromDsn="default.remote" # shellcheck disable=SC2034 declare PROFILES_DIR declare HOME_PROFILES_DIR +# shellcheck disable=SC2034 +defaultOptionProfile="" + +beforeParseCallback() { + defaultBeforeParseCallback + Linux::requireExecutedAsUser + Linux::requireRealpathCommand + Assert::commandExists mysql "sudo apt-get install -y mysql-client" + Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" +} initConf() { # shellcheck disable=SC2034 PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbImportProfiles" + # shellcheck disable=SC2034 HOME_PROFILES_DIR="${HOME}/.bash-tools/dbImportProfiles" } +initProfileCommandCallback() { + : +} + optionHelpCallback() { dbImportProfileCommandHelp exit 0 } longDescriptionFunction() { - local profilesList="" - local dsnList="" - dsnList="$(Conf::getMergedList "dsn" "env")" - profilesList="$(Conf::getMergedList "dbImportProfiles" "sh" || true)" - - echo -e "${__HELP_TITLE}Default profiles directory:${__HELP_NORMAL}" - echo -e "${PROFILES_DIR-configuration error}" + fromDsnOptionLongDescription echo - echo -e "${__HELP_TITLE}User profiles directory:${__HELP_NORMAL}" - echo -e "${HOME_PROFILES_DIR-configuration error}" - echo -e 'Allows to override profiles defined in "Default profiles directory"' - echo - echo -e "${__HELP_TITLE}List of available profiles:${__HELP_NORMAL}" - echo -e "${profilesList}" - echo - echo -e "${__HELP_TITLE}List of available dsn:${__HELP_NORMAL}" - echo -e "${dsnList}" + profileOptionLongDescription } optionProfileHelpFunction() { @@ -49,16 +50,6 @@ optionFromDsnHelpFunction() { echo } -optionRatioHelpFunction() { - Array::wrap2 " " 80 4 \ - " define the ratio to use (0 to 100% - default 70).\n" \ - "- 0 means profile will filter out all the tables\n" \ - "- 100 means profile will keep all the tables.\n" \ - "Eg: 70 means that tables with size(table+index)\n" \ - "that are greater than 70% of the max table size will be excluded." - echo -} - dbImportProfileCommandCallback() { if [[ -z "${fromDbName}" ]]; then Log::fatal "you must provide fromDbName" diff --git a/src/_binaries/Database/dbImportProfile/dbImportProfile.bats b/src/_binaries/Database/dbImportProfile/dbImportProfile.bats index 57ee26f5..c397bc03 100755 --- a/src/_binaries/Database/dbImportProfile/dbImportProfile.bats +++ b/src/_binaries/Database/dbImportProfile/dbImportProfile.bats @@ -17,7 +17,8 @@ setup() { "${HOME}/.bash-tools/dbImportDumps" \ "${HOME}/.bash-tools/dbImportProfiles" cp "${BATS_TEST_DIRNAME}/testsData/dsn/"* "${HOME}/.bash-tools/dsn/" - cp "${rootDir}/conf/.env" "${HOME}/.bash-tools/.env" + cp "${rootDir}/conf/defaultEnv/.env" "${HOME}/.bash-tools/.env" + cp "${BATS_TEST_DIRNAME}/testsData/dbImportProfiles/"* "${HOME}/.bash-tools/dbImportProfiles/" touch "${HOME}/bin/mysql" "${HOME}/bin/mysqldump" "${HOME}/bin/mysqlshow" "${HOME}/bin/builtinCommandWrapper" chmod +x "${HOME}/bin/"* diff --git a/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt b/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt index 93a3a111..0975e6f8 100644 --- a/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt +++ b/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt @@ -2,16 +2,30 @@ Generate optimized profiles to be used by dbImport. USAGE: dbImportProfile [OPTIONS] [ARGUMENTS] -USAGE: dbImportProfile [--from-dsn|-f ] [--help|-h] [--config] +USAGE: dbImportProfile [--ratio|-r ] + [--profile|-p ] [--from-dsn|-f ] [--help|-h] [--config] [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] - [--env-file ] [--log-level ] [--log-file ] + [--log-level ] [--log-file ] [--display-level ] [--no-color] [--theme ] [--version] - [--quiet|-q] [--profile|-p ] [--ratio|-r ] + [--quiet|-q] ARGUMENTS: fromDbName {single} (mandatory) The name of the source/remote database. +PROFILE OPTIONS: + --ratio, -r  {single} + define the ratio to use (0 to 100% - default 70). + - 0 means profile will filter out all the tables. + - 100 means profile will keep all the tables. + Eg: 70 means that tables with size(table+index) + that are greater than 70% of the max table size + will be excluded. + Default value: 70 + --profile, -p  {single} + The name of the profile to use in order to + include or exclude tables. + SOURCE OPTIONS: --from-dsn, -f  {single} Target mysql server. @@ -29,9 +43,6 @@ Debug level verbose mode (alias of --display-level DEBUG) -vvv {single} Trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-con - fig option instead) --log-level  {single} Set log level Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE @@ -52,39 +63,33 @@ --quiet, -q {single} Quiet mode, doesn't display any output. -OPTIONS: - --profile, -p  {single} - The name of the profile to write in profiles directory. - - If not provided, the file name pattern will be 'auto__.sh' - --ratio, -r  {single} - define the ratio to use (0 to 100% - default 70). - - - 0 means profile will filter out all the tables - - - 100 means profile will keep all the tables. - - Eg: 70 means that tables with size(table+index) - - that are greater than 70% of the max table size will be excluded. - Default value: 70 - DESCRIPTION: -Default profiles directory: -/bash/conf/dbImportProfiles - -User profiles directory: -home/.bash-tools/dbImportProfiles -Allows to override profiles defined in "Default profiles directory" - -List of available profiles: - - -List of available dsn: - - default.local - - default.remote - - localhost-root + Data Source Name (DSN) + Default dsn directory: + /bash/conf/dsn + + User dsn directory: + home/.bash-tools/dsn + Allows to override dsn defined in "Default dsn directory" + + List of available dsn: + - default.local + - default.remote + - localhost-root + + Profiles + Default profiles directory: + /bash/conf/dbImportProfiles + + User profiles directory: + home/.bash-tools/dbImportProfiles + Allows to override profiles defined in "Default profiles directory" + + List of available profiles: + - all + - default + - none VERSION: 3.0 diff --git a/src/_binaries/Database/dbImportStream/dbImportStream-binary.yaml b/src/_binaries/Database/dbImportStream/dbImportStream-binary.yaml index a1012ed5..a1fb8e42 100644 --- a/src/_binaries/Database/dbImportStream/dbImportStream-binary.yaml +++ b/src/_binaries/Database/dbImportStream/dbImportStream-binary.yaml @@ -1,7 +1,9 @@ extends: - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsProfile.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsTables.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsMysqlTarget.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" @@ -10,9 +12,7 @@ vars: compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/dbImportStream" - relativeRootDirBasedOnTargetDir: .. - srcDirs: - - ${BASH_TOOLS_ROOT_DIR}/src + binData: commands: default: @@ -27,7 +27,6 @@ binData: - dbImportStreamCommandCallback@100 definitionFiles: 20: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbImportStream/dbImportStream-options.sh - 99: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.sh mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbImportStream/dbImportStream-main.sh help: Stream tar.gz file or gz file through mysql. longDescription: longDescriptionFunction diff --git a/src/_binaries/Database/dbImportStream/dbImportStream-options.sh b/src/_binaries/Database/dbImportStream/dbImportStream-options.sh index 7a7d923a..beb477a9 100755 --- a/src/_binaries/Database/dbImportStream/dbImportStream-options.sh +++ b/src/_binaries/Database/dbImportStream/dbImportStream-options.sh @@ -6,10 +6,7 @@ declare HOME_PROFILES_DIR declare defaultFromDsn="default.remote" beforeParseCallback() { - BashTools::Conf::requireLoad - Env::requireLoad - UI::requireTheme - Log::requireLoad + defaultBeforeParseCallback Linux::requireExecutedAsUser Linux::requireRealpathCommand } @@ -17,6 +14,7 @@ beforeParseCallback() { initConf() { # shellcheck disable=SC2034 PROFILES_DIR="${BASH_TOOLS_ROOT_DIR}/conf/dbImportProfiles" + # shellcheck disable=SC2034 HOME_PROFILES_DIR="${HOME}/.bash-tools/dbImportProfiles" Db::checkRequirements } @@ -27,23 +25,9 @@ optionHelpCallback() { } longDescriptionFunction() { - local profilesList="" - local dsnList="" - dsnList="$(Conf::getMergedList "dsn" "env")" - profilesList="$(Conf::getMergedList "dbImportProfiles" "sh" || true)" - - echo -e "${__HELP_TITLE}Default profiles directory:${__HELP_NORMAL}" - echo -e "${PROFILES_DIR-configuration error}" - echo - echo -e "${__HELP_TITLE}User profiles directory:${__HELP_NORMAL}" - echo -e "${HOME_PROFILES_DIR-configuration error}" - echo -e "Allows to override profiles defined in 'Default profiles directory'" - echo - echo -e "${__HELP_TITLE}List of available profiles:${__HELP_NORMAL}" - echo -e "${profilesList}" + fromDsnOptionLongDescription echo - echo -e "${__HELP_TITLE}List of available dsn:${__HELP_NORMAL}" - echo -e "${dsnList}" + profileOptionLongDescription } dbImportStreamCommandCallback() { diff --git a/src/_binaries/Database/dbImportStream/dbImportStream.bats b/src/_binaries/Database/dbImportStream/dbImportStream.bats index 27bcba65..5af80c7e 100755 --- a/src/_binaries/Database/dbImportStream/dbImportStream.bats +++ b/src/_binaries/Database/dbImportStream/dbImportStream.bats @@ -19,7 +19,8 @@ setup() { cp "${BATS_TEST_DIRNAME}/testsData/dsn/"* "${HOME}/.bash-tools/dsn/" touch "${HOME}/bin/mysql" "${HOME}/bin/mysqldump" "${HOME}/bin/mysqlshow" "${HOME}/bin/builtinCommandWrapper" chmod +x "${HOME}/bin/"* - cp "${rootDir}/conf/.env" "${HOME}/.bash-tools/.env" + cp "${rootDir}/conf/defaultEnv/.env" "${HOME}/.bash-tools/.env" + cp "${BATS_TEST_DIRNAME}/testsData/dbImportProfiles/"* "${HOME}/.bash-tools/dbImportProfiles/" export BASH_FRAMEWORK_COMMAND="builtinCommandWrapper" diff --git a/src/_binaries/Database/dbImportStream/testsData/dbImportProfiles/all.sh b/src/_binaries/Database/dbImportStream/testsData/dbImportProfiles/all.sh new file mode 100755 index 00000000..09b79b19 --- /dev/null +++ b/src/_binaries/Database/dbImportStream/testsData/dbImportProfiles/all.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +# cat represents the whole list of tables +cat diff --git a/src/_binaries/Database/dbImportStream/testsData/dbImportProfiles/default.sh b/src/_binaries/Database/dbImportStream/testsData/dbImportProfiles/default.sh new file mode 100755 index 00000000..8cf9b92b --- /dev/null +++ b/src/_binaries/Database/dbImportStream/testsData/dbImportProfiles/default.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +# cat represents the whole list of tables +cat | + grep -v '.*_log' | + grep -v '.*logs' | + grep -v '.*tracking' | + grep -v '.*stats' | + grep -v '.*history.*' | + # always finish by a cat to be sure the command does not return exit code != 0 + cat || { + # avoid failure on premature process close (check Bash::handlePipelineFailure) + declare exitCode=$?; if (( exitCode == 141 )); then exit 0; fi + exit "${exitCode}" + } diff --git a/src/_binaries/Database/dbImportStream/testsData/dbImportProfiles/none.sh b/src/_binaries/Database/dbImportStream/testsData/dbImportProfiles/none.sh new file mode 100755 index 00000000..5604fdca --- /dev/null +++ b/src/_binaries/Database/dbImportStream/testsData/dbImportProfiles/none.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +# useful if you want to dump only the db structure + +# cat represents the whole list of tables +echo 'propel_migration' diff --git a/src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt b/src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt index c954c747..6f0e9cf6 100644 --- a/src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt +++ b/src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt @@ -6,7 +6,7 @@ [--tables ] [--target-dsn|-t ] [--character-set|-c ] [--help|-h] [--config] [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] - [--env-file ] [--log-level ] [--log-file ] + [--log-level ] [--log-file ] [--display-level ] [--no-color] [--theme ] [--version] [--quiet|-q] @@ -18,13 +18,12 @@ PROFILE OPTIONS: --profile, -p  {single} - The name of the profile to use in order to include or exclude tables. - Default value: default + The name of the profile to use in order to + include or exclude tables. --tables  {single} Import only table specified in the list. If aws mode, ignore profile option. - TARGET OPTIONS: --target-dsn, -t  {single} Dsn to use for target database. @@ -45,9 +44,6 @@ Debug level verbose mode (alias of --display-level DEBUG) -vvv {single} Trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-con - fig option instead) --log-level  {single} Set log level Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE @@ -70,20 +66,31 @@ DESCRIPTION: -Default profiles directory: -/bash/conf/dbImportProfiles - -User profiles directory: -home/.bash-tools/dbImportProfiles -Allows to override profiles defined in 'Default profiles directory' - -List of available profiles: - - -List of available dsn: - - default.local - - default.remote - - localhost-root + Data Source Name (DSN) + Default dsn directory: + /bash/conf/dsn + + User dsn directory: + home/.bash-tools/dsn + Allows to override dsn defined in "Default dsn directory" + + List of available dsn: + - default.local + - default.remote + - localhost-root + + Profiles + Default profiles directory: + /bash/conf/dbImportProfiles + + User profiles directory: + home/.bash-tools/dbImportProfiles + Allows to override profiles defined in "Default profiles directory" + + List of available profiles: + - all + - default + - none VERSION: 3.0 diff --git a/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-binary.yaml b/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-binary.yaml index cd5110ec..3ae2f25a 100644 --- a/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-binary.yaml +++ b/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-binary.yaml @@ -2,6 +2,7 @@ extends: - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsJobs.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsFromDsn.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" @@ -10,9 +11,7 @@ vars: compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/dbQueryAllDatabases" - relativeRootDirBasedOnTargetDir: .. - srcDirs: - - ${BASH_TOOLS_ROOT_DIR}/src + binData: commands: default: @@ -28,12 +27,11 @@ binData: everyArgumentCallbacks: - dbQueryAllDatabasesEveryArgumentCallback definitionFiles: - 11: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.sh 99: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-options.sh mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-main.sh help: "Execute a query on multiple databases to generate a tsv format - report.\\n The query can be parallelized on multiple databases." + report.\nThe query can be parallelized on multiple databases." longDescription: longDescriptionFunction optionGroups: groupQueryOptions: diff --git a/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-options.sh b/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-options.sh index 82e0f446..f334f02f 100755 --- a/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-options.sh +++ b/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases-options.sh @@ -11,10 +11,7 @@ declare copyrightBeginYear="2020" declare defaultFromDsn="default.remote" beforeParseCallback() { - BashTools::Conf::requireLoad - Env::requireLoad - UI::requireTheme - Log::requireLoad + defaultBeforeParseCallback Linux::requireExecutedAsUser Linux::requireRealpathCommand } @@ -46,33 +43,34 @@ optionSeparatorCallback() { longDescriptionFunction() { local example1=$'dbQueryAllDatabases databaseSize -j 12 --separator "|" --bar 2>/dev/null | column -s "|" -t -n -c 40' - local dsnList queriesList - dsnList="$(Conf::getMergedList "dsn" "env" " - ")" - queriesList="$(Conf::getMergedList "dbQueries" "sql" " - " || true)" + local queriesList + queriesList="$(Conf::getMergedList "dbQueries" "sql" " - " || true)" - echo -e "${__HELP_TITLE} LIST OF AVAILABLE DSN:${__HELP_NORMAL}" - echo -e "${dsnList}" + fromDsnOptionLongDescription echo - echo -e "${__HELP_TITLE} DEFAULT QUERIES DIRECTORY:${__HELP_NORMAL}" - echo -e " ${QUERIES_DIR-configuration error}" + echo -e " ${__HELP_TITLE}QUERIES${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}Default queries directory:${__HELP_NORMAL}" + echo -e " ${QUERIES_DIR-configuration error}" echo - echo -e "${__HELP_TITLE} USER QUERIES DIRECTORY:${__HELP_NORMAL}" - echo -e " ${HOME_QUERIES_DIR-configuration error}" - echo -e " Allows to override queries defined in 'Default queries directory'" + echo -e " ${__HELP_TITLE}User queries directory:${__HELP_NORMAL}" + echo -e " ${HOME_QUERIES_DIR-configuration error}" + echo -e " Allows to override queries defined in 'Default queries directory'" echo - echo -e "${__HELP_TITLE} LIST OF AVAILABLE QUERIES:${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}List of available queries:${__HELP_NORMAL}" echo -e "${queriesList}" echo - echo -e "${__HELP_TITLE} EXAMPLES:${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}EXAMPLES:${__HELP_NORMAL}" echo -e " ${__HELP_EXAMPLE}${example1}${__HELP_NORMAL}" } argQueryHelpFunction() { - Array::wrap2 " " 80 6 \ - " Query to execute" "\n" \ - "- , try to execute the mysql query provided by the file" "\n" \ - '- , search for query file in queries directory (see below)' "\n" \ - '- else the argument is interpreted as query string' + echo " Query to execute" + echo " - , try to execute the mysql query" + echo " provided by the file" + echo ' - , search for query file in' + echo ' queries directory (see below)' + echo ' - else the argument is interpreted as' + echo ' query string' } dbQueryAllDatabasesEveryArgumentCallback() { diff --git a/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases.bats b/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases.bats index baf31aa8..3cab8ea0 100755 --- a/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases.bats +++ b/src/_binaries/Database/dbQueryAllDatabases/dbQueryAllDatabases.bats @@ -17,12 +17,13 @@ setup() { cp "${BATS_TEST_DIRNAME}/testsData/pv" "${HOME}/bin" cp "${BATS_TEST_DIRNAME}/testsData/gawk" "${HOME}/bin" cp "${BATS_TEST_DIRNAME}/testsData/gawk" "${HOME}/bin/awk" - cp "${rootDir}/conf/.env" "${HOME}/.bash-tools/.env" + cp "${rootDir}/conf/defaultEnv/.env" "${HOME}/.bash-tools/.env" touch \ "${HOME}/bin/mysql" \ "${HOME}/bin/mysqldump" \ "${HOME}/bin/mysqlshow" \ - "${HOME}/bin/builtinCommandWrapper" + "${HOME}/bin/builtinCommandWrapper" \ + "${HOME}/.bash-tools/dbQueries/fakeQuery.sql" chmod +x "${HOME}/bin/"* export BASH_FRAMEWORK_COMMAND="builtinCommandWrapper" @@ -32,10 +33,12 @@ setup() { } teardown() { + rm -Rf "${HOME}" || true unstub_all } function Database::dbQueryAllDatabases::display_help { #@test + testCommand "${binDir}/dbQueryAllDatabases" dbQueryAllDatabases.help.txt } diff --git a/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt b/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt index c3451ef0..70c974d5 100644 --- a/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt +++ b/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt @@ -1,34 +1,32 @@ SYNOPSIS: Execute a query on multiple databases to generate a tsv format report. - The query can be parallelized on multiple databases. USAGE: dbQueryAllDatabases [OPTIONS] [ARGUMENTS] USAGE: dbQueryAllDatabases [--jobs|-j ] [--bar|-b] [--from-dsn|-f ] [--help|-h] [--config] [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] - [--env-file ] [--log-level ] [--log-file ] + [--log-level ] [--log-file ] [--display-level ] [--no-color] [--theme ] [--version] [--quiet|-q] [--separator|-s ] ARGUMENTS: argQuery {single} (mandatory) Query to execute + - , try to execute the mysql query + provided by the file + - , search for query file in + queries directory (see below) + - else the argument is interpreted as + query string - - , try to execute the mysql query provided by the file - - - , search for query file in queries directory (see below) - - - else the argument is interpreted as query string JOB OPTIONS: --jobs, -j  {single} The number of databases to query in parallel. Default value: 1 --bar, -b {single} - - Show progress as a progress bar. In the bar is shown: % of jobs completed, - estimated seconds left, and number of jobs started. - + Show progress as a progress bar. In the bar is shown: % of jobs + completed, estimated seconds left, and number of jobs started. SOURCE OPTIONS: --from-dsn, -f  {single} @@ -47,9 +45,6 @@ Debug level verbose mode (alias of --display-level DEBUG) -vvv {single} Trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-con - fig option instead) --log-level  {single} Set log level Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE @@ -77,21 +72,30 @@ DESCRIPTION: - LIST OF AVAILABLE DSN: - - dsn_local - - dsn_valid + Data Source Name (DSN) + Default dsn directory: + /bash/conf/dsn + + User dsn directory: + home/.bash-tools/dsn + Allows to override dsn defined in "Default dsn directory" - DEFAULT QUERIES DIRECTORY: - /bash/conf/dbQueries + List of available dsn: + - dsn_local + - dsn_valid - USER QUERIES DIRECTORY: - home/.bash-tools/dbQueries - Allows to override queries defined in 'Default queries directory' + QUERIES + Default queries directory: + /bash/conf/dbQueries - LIST OF AVAILABLE QUERIES: + User queries directory: + home/.bash-tools/dbQueries + Allows to override queries defined in 'Default queries directory' + List of available queries: + - fakeQuery - EXAMPLES: + EXAMPLES: dbQueryAllDatabases databaseSize -j 12 --separator "|" --bar 2>/dev/null | column -s "|" -t -n -c 40 VERSION: 3.0 diff --git a/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-binary.yaml b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-binary.yaml index cf0e8bb7..9a9d8b5a 100644 --- a/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-binary.yaml +++ b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-binary.yaml @@ -2,6 +2,8 @@ extends: - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsJobs.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsFromDsn.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" @@ -10,9 +12,7 @@ vars: compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/dbScriptAllDatabases" - relativeRootDirBasedOnTargetDir: .. - srcDirs: - - ${BASH_TOOLS_ROOT_DIR}/src + binData: commands: default: @@ -26,7 +26,6 @@ binData: callbacks: - dbScriptAllDatabasesCommandCallback@100 definitionFiles: - 11: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.sh 20: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-options.sh mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-main.sh help: | @@ -57,8 +56,8 @@ binData: group: groupScriptsOptions type: StringArray help: - If provided will check only this db, otherwise script will be - executed on all dbs of mysql server. + "If provided will check only this db,\notherwise script will be + executed on all dbs of mysql server." helpValueName: dbName alts: - --database diff --git a/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-options.sh b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-options.sh index abd36d41..e0c11658 100755 --- a/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-options.sh +++ b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-options.sh @@ -8,14 +8,10 @@ declare defaultFromDsn="default.remote" declare outputDirectory="${HOME}/.bash-tools/output" beforeParseCallback() { + defaultBeforeParseCallback Assert::commandExists mysql "sudo apt-get install -y mysql-client" Assert::commandExists mysqlshow "sudo apt-get install -y mysql-client" Assert::commandExists parallel "sudo apt-get install -y parallel" - - BashTools::Conf::requireLoad - Env::requireLoad - UI::requireTheme - Log::requireLoad Linux::requireExecutedAsUser Linux::requireRealpathCommand } @@ -33,37 +29,36 @@ optionHelpCallback() { } longDescriptionFunction() { - local dsnList scriptsList - dsnList="$(Conf::getMergedList "dsn" "env")" - scriptsList="$(Conf::getMergedList "dbScripts" "sh")" + local scriptsList + scriptsList="$(Conf::getMergedList "dbScripts" "sh" " - " || true)" - echo -e "${__HELP_TITLE}NOTE:${__HELP_NORMAL}" - echo -e "the use of output, log-format, verbose options highly depends on the script used" - echo - echo -e "${__HELP_TITLE}LIST OF AVAILABLE DSN:${__HELP_NORMAL}" - echo -e "${dsnList}" + fromDsnOptionLongDescription echo - echo -e "${__HELP_TITLE}DEFAULT SCRIPTS DIRECTORY:${__HELP_NORMAL}" - echo -e "${SCRIPTS_DIR-configuration error}" + echo -e " ${__HELP_TITLE}SCRIPTS${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}Default scripts directory:${__HELP_NORMAL}" + echo -e " ${SCRIPTS_DIR-configuration error}" echo - echo -e "${__HELP_TITLE}USER SCRIPTS DIRECTORY:${__HELP_NORMAL}" - echo -e "${HOME_SCRIPTS_DIR-configuration error}" - echo -e "Allows to override queries defined in 'Default scripts directory'" + echo -e " ${__HELP_TITLE}User scripts directory:${__HELP_NORMAL}" + echo -e " ${HOME_SCRIPTS_DIR-configuration error}" + echo -e " Allows to override queries defined in 'Default scripts directory'" echo - echo -e "${__HELP_TITLE}LIST OF AVAILABLE SCRIPTS:${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}List of available scripts:${__HELP_NORMAL}" echo -e "${scriptsList}" echo - echo -e "${__HELP_TITLE}EXAMPLES:${__HELP_NORMAL} script conf/dbScripts/extractData.sh" - echo -e " executes query databaseSize (see conf/dbQueries/databaseSize.sql) on each db and log the result in log file in default output dir, call it using" + echo -e " ${__HELP_TITLE}NOTE:${__HELP_NORMAL}" + echo -e " the use of output, log-format, verbose options highly depends on the script used" + echo + echo -e " ${__HELP_TITLE}EXAMPLES:${__HELP_NORMAL} script conf/dbScripts/extractData.sh" + echo -e " 1. executes query databaseSize (see conf/dbQueries/databaseSize.sql) on each db and log the result in log file in default output dir, call it using" echo -e " ${__HELP_EXAMPLE}$0 -j 10 extractData databaseSize${__HELP_NORMAL}" echo - echo -e " executes query databaseSize on each db and display the result on stdout (2>/dev/null hides information messages)" + echo -e " 2. executes query databaseSize on each db and display the result on stdout (2>/dev/null hides information messages)" echo -e " ${__HELP_EXAMPLE}$0 -j 10 --log-format none extractData databaseSize${__HELP_NORMAL}" echo - echo -e " use --verbose to get some debug information" + echo -e " 3. use --verbose to get some debug information" echo -e " ${__HELP_EXAMPLE}$0 -j 10 --log-format none --verbose extractData databaseSize${__HELP_NORMAL}" echo - echo -e "${__HELP_TITLE}USE CASES:${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}USE CASES:${__HELP_NORMAL}" echo -e " you can use this script in order to check that each db model conforms with your ORM schema" echo -e " simply create a new script in conf/dbQueries that will call your orm schema checker" echo diff --git a/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases.bats b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases.bats index a032b493..20dfe1e9 100755 --- a/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases.bats +++ b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases.bats @@ -15,11 +15,12 @@ setup() { "${HOME}/.bash-tools/dbQueries" \ "${HOME}/.bash-tools/dbScripts" cp "${BATS_TEST_DIRNAME}/testsData/pv" "${HOME}/bin" - cp "${rootDir}/conf/.env" "${HOME}/.bash-tools/.env" + cp "${rootDir}/conf/defaultEnv/.env" "${HOME}/.bash-tools/.env" touch \ "${HOME}/bin/mysqldump" \ "${HOME}/bin/mysqlshow" \ - "${HOME}/bin/builtinCommandWrapper" + "${HOME}/bin/builtinCommandWrapper" \ + "${HOME}/.bash-tools/dbScripts/fakeScript.sh" chmod +x "${HOME}/bin/"* export BASH_FRAMEWORK_COMMAND="builtinCommandWrapper" diff --git a/src/_binaries/Database/dbScriptAllDatabases/dbScriptOneDatabase.sh b/src/_binaries/Database/dbScriptAllDatabases/dbScriptOneDatabase.sh index 9e0b7b86..2ca4d01b 100755 --- a/src/_binaries/Database/dbScriptAllDatabases/dbScriptOneDatabase.sh +++ b/src/_binaries/Database/dbScriptAllDatabases/dbScriptOneDatabase.sh @@ -44,9 +44,7 @@ unknownArg() { } beforeParseCallback() { - Env::requireLoad - UI::requireTheme - Log::requireLoad + defaultBeforeParseCallback Linux::requireExecutedAsUser Linux::requireRealpathCommand init diff --git a/src/_binaries/Database/dbScriptAllDatabases/extractData-binary.yaml b/src/_binaries/Database/dbScriptAllDatabases/extractData-binary.yaml index d9a4bea0..5ebd536f 100644 --- a/src/_binaries/Database/dbScriptAllDatabases/extractData-binary.yaml +++ b/src/_binaries/Database/dbScriptAllDatabases/extractData-binary.yaml @@ -1,5 +1,6 @@ extends: - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" @@ -23,6 +24,5 @@ binData: unknownArgumentCallbacks: - unknownArg definitionFiles: - 11: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.sh 99: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbScriptAllDatabases/dbScriptOneDatabase.sh mainFile: ${BASH_TOOLS_ROOT_DIR}/src/_binaries/Database/dbScriptAllDatabases/extractData.sh diff --git a/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt b/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt index 682d92fc..4c699c78 100644 --- a/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt +++ b/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt @@ -6,7 +6,7 @@ USAGE: dbScriptAllDatabases [--jobs|-j ] [--bar|-b] [--from-dsn|-f ] [--help|-h] [--config] [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] - [--env-file ] [--log-level ] [--log-file ] + [--log-level ] [--log-file ] [--display-level ] [--no-color] [--theme ] [--version] [--quiet|-q] [--database ] [--output|-o ] [--log-format|-l ] @@ -22,10 +22,8 @@ The number of databases to query in parallel. Default value: 1 --bar, -b {single} - - Show progress as a progress bar. In the bar is shown: % of jobs completed, - estimated seconds left, and number of jobs started. - + Show progress as a progress bar. In the bar is shown: % of jobs + completed, estimated seconds left, and number of jobs started. SOURCE OPTIONS: --from-dsn, -f  {single} @@ -44,9 +42,6 @@ Debug level verbose mode (alias of --display-level DEBUG) -vvv {single} Trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-con - fig option instead) --log-level  {single} Set log level Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE @@ -69,8 +64,8 @@ SCRIPTS OPTIONS: --database  {single} - If provided will check only this db, otherwise script will be executed o - n all dbs of mysql server. + If provided will check only this db, + otherwise script will be executed on all dbs of mysql server. --output, -o  {single} Output directory, see log-format option. --log-format, -l  {single} @@ -80,34 +75,43 @@ DESCRIPTION: -NOTE: -the use of output, log-format, verbose options highly depends on the script used + Data Source Name (DSN) + Default dsn directory: + /bash/conf/dsn + + User dsn directory: + home/.bash-tools/dsn + Allows to override dsn defined in "Default dsn directory" -LIST OF AVAILABLE DSN: - - dsn_local - - dsn_valid + List of available dsn: + - dsn_local + - dsn_valid -DEFAULT SCRIPTS DIRECTORY: -/bash/conf/dbScripts + SCRIPTS + Default scripts directory: + /bash/conf/dbScripts -USER SCRIPTS DIRECTORY: -home/.bash-tools/dbScripts -Allows to override queries defined in 'Default scripts directory' + User scripts directory: + home/.bash-tools/dbScripts + Allows to override queries defined in 'Default scripts directory' -LIST OF AVAILABLE SCRIPTS: + List of available scripts: + - fakeScript + NOTE: + the use of output, log-format, verbose options highly depends on the script used -EXAMPLES: script conf/dbScripts/extractData.sh - executes query databaseSize (see conf/dbQueries/databaseSize.sql) on each db and log the result in log file in default output dir, call it using + EXAMPLES: script conf/dbScripts/extractData.sh + 1. executes query databaseSize (see conf/dbQueries/databaseSize.sql) on each db and log the result in log file in default output dir, call it using /bash/bin/dbScriptAllDatabases -j 10 extractData databaseSize - executes query databaseSize on each db and display the result on stdout (2>/dev/null hides information messages) + 2. executes query databaseSize on each db and display the result on stdout (2>/dev/null hides information messages) /bash/bin/dbScriptAllDatabases -j 10 --log-format none extractData databaseSize - use --verbose to get some debug information + 3. use --verbose to get some debug information /bash/bin/dbScriptAllDatabases -j 10 --log-format none --verbose extractData databaseSize -USE CASES: + USE CASES: you can use this script in order to check that each db model conforms with your ORM schema simply create a new script in conf/dbQueries that will call your orm schema checker diff --git a/src/_binaries/Docker/cli/cli-binary.yaml b/src/_binaries/Docker/cli/cli-binary.yaml index 979ea398..65b0131f 100644 --- a/src/_binaries/Docker/cli/cli-binary.yaml +++ b/src/_binaries/Docker/cli/cli-binary.yaml @@ -1,5 +1,6 @@ extends: - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" @@ -8,9 +9,7 @@ vars: compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/cli" - relativeRootDirBasedOnTargetDir: .. - srcDirs: - - ${BASH_TOOLS_ROOT_DIR}/src + binData: commands: default: diff --git a/src/_binaries/Docker/cli/cli-options.sh b/src/_binaries/Docker/cli/cli-options.sh index cc8f297f..0a112e03 100755 --- a/src/_binaries/Docker/cli/cli-options.sh +++ b/src/_binaries/Docker/cli/cli-options.sh @@ -13,10 +13,7 @@ declare PROFILES_DIR declare HOME_PROFILES_DIR beforeParseCallback() { - BashTools::Conf::requireLoad - Env::requireLoad - UI::requireTheme - Log::requireLoad + defaultBeforeParseCallback Linux::requireRealpathCommand Assert::commandExists docker "check https://docs.docker.com/engine/install/ubuntu/" } diff --git a/src/_binaries/Docker/cli/cli.bats b/src/_binaries/Docker/cli/cli.bats index f76dd647..dd5bbd21 100755 --- a/src/_binaries/Docker/cli/cli.bats +++ b/src/_binaries/Docker/cli/cli.bats @@ -12,6 +12,7 @@ setup() { touch "${HOME}/bin/docker" chmod +x "${HOME}/bin/"* cp -R "${rootDir}/conf" "${HOME}/.bash-tools" + cp "${rootDir}/conf/defaultEnv/.env" "${HOME}/.bash-tools" export PATH="${PATH}:${HOME}/bin" } diff --git a/src/_binaries/Docker/cli/testsData/cli.help.txt b/src/_binaries/Docker/cli/testsData/cli.help.txt index 7839be46..aced4feb 100644 --- a/src/_binaries/Docker/cli/testsData/cli.help.txt +++ b/src/_binaries/Docker/cli/testsData/cli.help.txt @@ -4,7 +4,7 @@ USAGE: cli [OPTIONS] [ARGUMENTS] USAGE: cli [--help|-h] [--config] [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] - [--env-file ] [--log-level ] [--log-file ] + [--log-level ] [--log-file ] [--display-level ] [--no-color] [--theme ] [--version] [--quiet|-q] @@ -45,9 +45,6 @@ Debug level verbose mode (alias of --display-level DEBUG) -vvv {single} Trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-con - fig option instead) --log-level  {single} Set log level Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE diff --git a/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-binary.yaml b/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-binary.yaml index e64ec961..736f4a23 100644 --- a/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-binary.yaml +++ b/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf-binary.yaml @@ -1,6 +1,7 @@ extends: - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/Git/gitToolsDefaultOptions.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" @@ -9,9 +10,7 @@ vars: compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/gitIsAncestorOf" - relativeRootDirBasedOnTargetDir: .. - srcDirs: - - ${BASH_TOOLS_ROOT_DIR}/src + binData: commands: default: diff --git a/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf.bats b/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf.bats index 06a86e19..33c1bea2 100755 --- a/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf.bats +++ b/src/_binaries/Git/gitIsAncestorOf/gitIsAncestorOf.bats @@ -10,7 +10,7 @@ setup() { export HOME="${BATS_TEST_TMPDIR}/home" export BASH_FRAMEWORK_ENV_FILEPATH="${BATS_TEST_DIRNAME}/testsData/.env" mkdir -p "${HOME}/.bash-tools" - cp "${rootDir}/conf/.env" "${HOME}/.bash-tools/.env" + cp "${rootDir}/conf/defaultEnv/.env" "${HOME}/.bash-tools/.env" } teardown() { diff --git a/src/_binaries/Git/gitIsAncestorOf/testsData/gitIsAncestorOf.help.txt b/src/_binaries/Git/gitIsAncestorOf/testsData/gitIsAncestorOf.help.txt index 054c5c23..b7a62a3e 100644 --- a/src/_binaries/Git/gitIsAncestorOf/testsData/gitIsAncestorOf.help.txt +++ b/src/_binaries/Git/gitIsAncestorOf/testsData/gitIsAncestorOf.help.txt @@ -4,7 +4,7 @@ USAGE: gitIsAncestorOf [OPTIONS] [ARGUMENTS] USAGE: gitIsAncestorOf [--help|-h] [--config] [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] - [--env-file ] [--log-level ] [--log-file ] + [--log-level ] [--log-file ] [--display-level ] [--no-color] [--theme ] [--version] [--quiet|-q] @@ -27,9 +27,6 @@ Debug level verbose mode (alias of --display-level DEBUG) -vvv {single} Trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-con - fig option instead) --log-level  {single} Set log level Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE diff --git a/src/_binaries/Git/gitIsBranch/gitIsBranch-binary.yaml b/src/_binaries/Git/gitIsBranch/gitIsBranch-binary.yaml index 743d2325..a77ef75a 100644 --- a/src/_binaries/Git/gitIsBranch/gitIsBranch-binary.yaml +++ b/src/_binaries/Git/gitIsBranch/gitIsBranch-binary.yaml @@ -1,6 +1,7 @@ extends: - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/Git/gitToolsDefaultOptions.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" @@ -9,9 +10,7 @@ vars: compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/gitIsBranch" - relativeRootDirBasedOnTargetDir: .. - srcDirs: - - ${BASH_TOOLS_ROOT_DIR}/src + binData: commands: default: diff --git a/src/_binaries/Git/gitIsBranch/gitIsBranch.bats b/src/_binaries/Git/gitIsBranch/gitIsBranch.bats index 83564465..c7998a68 100755 --- a/src/_binaries/Git/gitIsBranch/gitIsBranch.bats +++ b/src/_binaries/Git/gitIsBranch/gitIsBranch.bats @@ -10,7 +10,7 @@ setup() { export HOME="${BATS_TEST_TMPDIR}/home" export BASH_FRAMEWORK_ENV_FILEPATH="${BATS_TEST_DIRNAME}/testsData/.env" mkdir -p "${HOME}/.bash-tools" - cp "${rootDir}/conf/.env" "${HOME}/.bash-tools/.env" + cp "${rootDir}/conf/defaultEnv/.env" "${HOME}/.bash-tools/.env" } teardown() { diff --git a/src/_binaries/Git/gitIsBranch/testsData/gitIsBranch.help.txt b/src/_binaries/Git/gitIsBranch/testsData/gitIsBranch.help.txt index a844954f..1d03b7b8 100644 --- a/src/_binaries/Git/gitIsBranch/testsData/gitIsBranch.help.txt +++ b/src/_binaries/Git/gitIsBranch/testsData/gitIsBranch.help.txt @@ -4,7 +4,7 @@ USAGE: gitIsBranch [OPTIONS] [ARGUMENTS] USAGE: gitIsBranch [--help|-h] [--config] [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] - [--env-file ] [--log-level ] [--log-file ] + [--log-level ] [--log-file ] [--display-level ] [--no-color] [--theme ] [--version] [--quiet|-q] @@ -25,9 +25,6 @@ Debug level verbose mode (alias of --display-level DEBUG) -vvv {single} Trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-con - fig option instead) --log-level  {single} Set log level Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE diff --git a/src/_binaries/Git/gitRenameBranch/gitRenameBranch-binary.yaml b/src/_binaries/Git/gitRenameBranch/gitRenameBranch-binary.yaml index d31c5e3e..8d7ef91e 100644 --- a/src/_binaries/Git/gitRenameBranch/gitRenameBranch-binary.yaml +++ b/src/_binaries/Git/gitRenameBranch/gitRenameBranch-binary.yaml @@ -1,6 +1,7 @@ extends: - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/Git/gitToolsDefaultOptions.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" @@ -9,9 +10,7 @@ vars: compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/gitRenameBranch" - relativeRootDirBasedOnTargetDir: .. - srcDirs: - - ${BASH_TOOLS_ROOT_DIR}/src + binData: commands: default: diff --git a/src/_binaries/Git/gitRenameBranch/gitRenameBranch.bats b/src/_binaries/Git/gitRenameBranch/gitRenameBranch.bats index 54f2feab..99d616d9 100755 --- a/src/_binaries/Git/gitRenameBranch/gitRenameBranch.bats +++ b/src/_binaries/Git/gitRenameBranch/gitRenameBranch.bats @@ -10,7 +10,7 @@ setup() { export HOME="${BATS_TEST_TMPDIR}/home" mkdir -p "${HOME}/.bash-tools" - cp "${rootDir}/conf/.env" "${HOME}/.bash-tools/.env" + cp "${rootDir}/conf/defaultEnv/.env" "${HOME}/.bash-tools/.env" mkdir "${BATS_TEST_TMPDIR}/gitRepo" mkdir "${BATS_TEST_TMPDIR}/gitRepoFake" diff --git a/src/_binaries/Git/gitRenameBranch/testsData/gitRenameBranch.help.txt b/src/_binaries/Git/gitRenameBranch/testsData/gitRenameBranch.help.txt index c14be34d..23d53f7a 100644 --- a/src/_binaries/Git/gitRenameBranch/testsData/gitRenameBranch.help.txt +++ b/src/_binaries/Git/gitRenameBranch/testsData/gitRenameBranch.help.txt @@ -4,7 +4,7 @@ USAGE: gitRenameBranch [OPTIONS] [ARGUMENTS] USAGE: gitRenameBranch [--help|-h] [--config] [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] - [--env-file ] [--log-level ] [--log-file ] + [--log-level ] [--log-file ] [--display-level ] [--no-color] [--theme ] [--version] [--quiet|-q] [--assume-yes|--yes|-y] [--push|-p] [--delete|-d] @@ -27,9 +27,6 @@ Debug level verbose mode (alias of --display-level DEBUG) -vvv {single} Trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-con - fig option instead) --log-level  {single} Set log level Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE diff --git a/src/_binaries/Git/gitToolsDefaultOptions.sh b/src/_binaries/Git/gitToolsDefaultOptions.sh index 210e4067..994b3563 100755 --- a/src/_binaries/Git/gitToolsDefaultOptions.sh +++ b/src/_binaries/Git/gitToolsDefaultOptions.sh @@ -1,11 +1,8 @@ #!/usr/bin/env bash beforeParseCallback() { + defaultBeforeParseCallback Linux::requireExecutedAsUser - BashTools::Conf::requireLoad - Env::requireLoad - UI::requireTheme - Log::requireLoad } declare optionRedirectCmdOutputs="" diff --git a/src/_binaries/Github/upgradeGithubRelease/testsData/upgradeGithubRelease.help.txt b/src/_binaries/Github/upgradeGithubRelease/testsData/upgradeGithubRelease.help.txt index 0917fa79..38a220e6 100644 --- a/src/_binaries/Github/upgradeGithubRelease/testsData/upgradeGithubRelease.help.txt +++ b/src/_binaries/Github/upgradeGithubRelease/testsData/upgradeGithubRelease.help.txt @@ -4,7 +4,7 @@ USAGE: upgradeGithubRelease [OPTIONS] [ARGUMENTS] USAGE: upgradeGithubRelease [--help|-h] [--config] [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] - [--env-file ] [--log-level ] [--log-file ] + [--log-level ] [--log-file ] [--display-level ] [--no-color] [--theme ] [--version] [--quiet|-q] [--version-arg ] [--current-version|-c ] [--exact-version|-e ] @@ -31,9 +31,6 @@ Debug level verbose mode (alias of --display-level DEBUG) -vvv {single} Trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-con - fig option instead) --log-level  {single} Set log level Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE diff --git a/src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease-binary.yaml b/src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease-binary.yaml index b0edc529..0d7bd316 100644 --- a/src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease-binary.yaml +++ b/src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease-binary.yaml @@ -1,5 +1,6 @@ extends: - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" @@ -8,9 +9,7 @@ vars: compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/upgradeGithubRelease" - relativeRootDirBasedOnTargetDir: .. - srcDirs: - - ${BASH_TOOLS_ROOT_DIR}/src + binData: commands: default: diff --git a/src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease.bats b/src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease.bats index d739e202..09a65140 100755 --- a/src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease.bats +++ b/src/_binaries/Github/upgradeGithubRelease/upgradeGithubRelease.bats @@ -10,7 +10,7 @@ setup() { export HOME="${BATS_TEST_TMPDIR}/home" export BASH_FRAMEWORK_ENV_FILEPATH="${BATS_TEST_DIRNAME}/testsData/.env" mkdir -p "${HOME}/.bash-tools" - cp "${rootDir}/conf/.env" "${HOME}/.bash-tools/.env" + cp "${rootDir}/conf/defaultEnv/.env" "${HOME}/.bash-tools/.env" } diff --git a/src/_binaries/Postman/postmanCli/postmanCli-binary.yaml b/src/_binaries/Postman/postmanCli/postmanCli-binary.yaml index ed68379a..fe482bd3 100644 --- a/src/_binaries/Postman/postmanCli/postmanCli-binary.yaml +++ b/src/_binaries/Postman/postmanCli/postmanCli-binary.yaml @@ -1,5 +1,6 @@ extends: - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" @@ -8,9 +9,7 @@ vars: compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/postmanCli" - relativeRootDirBasedOnTargetDir: .. - srcDirs: - - ${BASH_TOOLS_ROOT_DIR}/src + binData: commands: default: diff --git a/src/_binaries/Postman/postmanCli/postmanCli.bats b/src/_binaries/Postman/postmanCli/postmanCli.bats index 71ddecfc..6302111c 100755 --- a/src/_binaries/Postman/postmanCli/postmanCli.bats +++ b/src/_binaries/Postman/postmanCli/postmanCli.bats @@ -9,6 +9,7 @@ setup() { export HOME="${BATS_TEST_TMPDIR}/home" mkdir -p "${HOME}" cp -R "${rootDir}/conf" "${HOME}/.bash-tools" + cp "${rootDir}/conf/defaultEnv/.env" "${HOME}/.bash-tools" } teardown() { @@ -27,11 +28,12 @@ function PostmanCli::config { #@test assert_line --index 2 "BASH_FRAMEWORK_ARGV = ([0]=\"--config\" [1]=\"-m\" [2]=\"${rootDir}/conf/postmanCli/openApis.json\")" assert_line --index 3 "BASH_FRAMEWORK_ARGV_FILTERED = ()" assert_line --index 4 'BASH_FRAMEWORK_DISPLAY_LEVEL = "3"' - assert_line --index 5 "BASH_FRAMEWORK_LOG_FILE = \"${TMPDIR}/logFile\"" - assert_line --index 6 'BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION = "5"' - assert_line --index 7 'BASH_FRAMEWORK_LOG_LEVEL = "0"' - assert_line --index 8 'BASH_FRAMEWORK_THEME = "default"' - assert_line --index 9 "--------------------------------------------------------------------------------" - assert_line --index 10 'POSTMAN_API_KEY = ...(truncated)' - assert_lines_count 11 + assert_line --index 5 "BASH_FRAMEWORK_ENV_FILES = ([0]=\"${HOME}/.bash-tools/.env\")" + assert_line --index 6 "BASH_FRAMEWORK_LOG_FILE = \"${TMPDIR}/logFile\"" + assert_line --index 7 'BASH_FRAMEWORK_LOG_FILE_MAX_ROTATION = "5"' + assert_line --index 8 'BASH_FRAMEWORK_LOG_LEVEL = "0"' + assert_line --partial --index 9 'BASH_FRAMEWORK_THEME = ' + assert_line --index 10 "--------------------------------------------------------------------------------" + assert_line --index 11 'POSTMAN_API_KEY = ...(truncated)' + assert_lines_count 12 } diff --git a/src/_binaries/Postman/postmanCli/testsData/postmanCli.help.txt b/src/_binaries/Postman/postmanCli/testsData/postmanCli.help.txt index 78ef2487..21ba2db6 100644 --- a/src/_binaries/Postman/postmanCli/testsData/postmanCli.help.txt +++ b/src/_binaries/Postman/postmanCli/testsData/postmanCli.help.txt @@ -4,7 +4,7 @@ USAGE: postmanCli [OPTIONS] [ARGUMENTS] USAGE: postmanCli [--help|-h] [--config] [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] - [--env-file ] [--log-level ] [--log-file ] + [--log-level ] [--log-file ] [--display-level ] [--no-color] [--theme ] [--version] [--quiet|-q] [--postman-model|-m ] @@ -31,9 +31,6 @@ Debug level verbose mode (alias of --display-level DEBUG) -vvv {single} Trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-con - fig option instead) --log-level  {single} Set log level Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE diff --git a/src/_binaries/Utils/waitForIt/testsData/waitForIt.help.txt b/src/_binaries/Utils/waitForIt/testsData/waitForIt.help.txt index c1cda629..7a5d30b7 100644 --- a/src/_binaries/Utils/waitForIt/testsData/waitForIt.help.txt +++ b/src/_binaries/Utils/waitForIt/testsData/waitForIt.help.txt @@ -5,7 +5,7 @@ USAGE: waitForIt [--timeout|-t ] [--exec-command-on-timeout|--lax|-l] [--help|-h] [--config] [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] - [--env-file ] [--log-level ] [--log-file ] + [--log-level ] [--log-file ] [--display-level ] [--no-color] [--theme ] [--version] [--quiet|-q] [--algorithm|--algo ] [--user-nc] @@ -38,9 +38,6 @@ Debug level verbose mode (alias of --display-level DEBUG) -vvv {single} Trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-con - fig option instead) --log-level  {single} Set log level Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE diff --git a/src/_binaries/Utils/waitForIt/waitForIt-binary.yaml b/src/_binaries/Utils/waitForIt/waitForIt-binary.yaml index 4ea5411f..82975ce6 100644 --- a/src/_binaries/Utils/waitForIt/waitForIt-binary.yaml +++ b/src/_binaries/Utils/waitForIt/waitForIt-binary.yaml @@ -1,6 +1,8 @@ extends: - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsTimeout.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" @@ -9,9 +11,7 @@ vars: compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/waitForIt" - relativeRootDirBasedOnTargetDir: .. - srcDirs: - - ${BASH_TOOLS_ROOT_DIR}/src + binData: commands: default: diff --git a/src/_binaries/Utils/waitForIt/waitForIt.bats b/src/_binaries/Utils/waitForIt/waitForIt.bats index f32340ea..5ed69028 100755 --- a/src/_binaries/Utils/waitForIt/waitForIt.bats +++ b/src/_binaries/Utils/waitForIt/waitForIt.bats @@ -9,7 +9,7 @@ setup() { export HOME="${BATS_TEST_TMPDIR}/home" mkdir -p "${HOME}/.bash-tools" mkdir -p "${HOME}/bin" - cp "${rootDir}/conf/.env" "${HOME}/.bash-tools/.env" + cp "${rootDir}/conf/defaultEnv/.env" "${HOME}/.bash-tools/.env" export PATH="${HOME}/bin:${PATH}" } diff --git a/src/_binaries/Utils/waitForMysql/testsData/waitForMysql.help.txt b/src/_binaries/Utils/waitForMysql/testsData/waitForMysql.help.txt index a9e6e3b9..88377118 100644 --- a/src/_binaries/Utils/waitForMysql/testsData/waitForMysql.help.txt +++ b/src/_binaries/Utils/waitForMysql/testsData/waitForMysql.help.txt @@ -5,7 +5,7 @@ USAGE: waitForMysql [--timeout|-t ] [--exec-command-on-timeout|--lax|-l] [--help|-h] [--config] [--bash-framework-config ] [--verbose|-v] [-vv] [-vvv] - [--env-file ] [--log-level ] [--log-file ] + [--log-level ] [--log-file ] [--display-level ] [--no-color] [--theme ] [--version] [--quiet|-q] @@ -42,9 +42,6 @@ Debug level verbose mode (alias of --display-level DEBUG) -vvv {single} Trace level verbose mode (alias of --display-level TRACE) - --env-file  {list} (optional) - Load the specified env file (deprecated, please use --bash-framework-con - fig option instead) --log-level  {single} Set log level Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE diff --git a/src/_binaries/Utils/waitForMysql/waitForMysql-binary.yaml b/src/_binaries/Utils/waitForMysql/waitForMysql-binary.yaml index eabfb55b..2dc22cd7 100644 --- a/src/_binaries/Utils/waitForMysql/waitForMysql-binary.yaml +++ b/src/_binaries/Utils/waitForMysql/waitForMysql-binary.yaml @@ -1,6 +1,7 @@ extends: - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsTimeout.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" @@ -9,9 +10,7 @@ vars: compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/waitForMysql" - relativeRootDirBasedOnTargetDir: .. - srcDirs: - - ${BASH_TOOLS_ROOT_DIR}/src + binData: commands: default: diff --git a/src/_binaries/Utils/waitForMysql/waitForMysql.bats b/src/_binaries/Utils/waitForMysql/waitForMysql.bats index e0c54717..6cdd9fb6 100755 --- a/src/_binaries/Utils/waitForMysql/waitForMysql.bats +++ b/src/_binaries/Utils/waitForMysql/waitForMysql.bats @@ -10,7 +10,7 @@ setup() { mkdir -p "${HOME}/.bash-tools" mkdir -p "${HOME}/bin" export PATH="${HOME}/bin:${PATH}" - cp "${rootDir}/conf/.env" "${HOME}/.bash-tools/.env" + cp "${rootDir}/conf/defaultEnv/.env" "${HOME}/.bash-tools/.env" } teardown() { diff --git a/src/_binaries/build/doc/doc-binary.yaml b/src/_binaries/build/doc/doc-binary.yaml index f6fb90df..255dc363 100644 --- a/src/_binaries/build/doc/doc-binary.yaml +++ b/src/_binaries/build/doc/doc-binary.yaml @@ -1,5 +1,6 @@ extends: - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/optionsCi.yaml" + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" @@ -8,7 +9,6 @@ vars: compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/doc" - relativeRootDirBasedOnTargetDir: .. binData: commands: default: diff --git a/src/_binaries/build/install/install-binary.yaml b/src/_binaries/build/install/install-binary.yaml index f77d051c..2973f4fa 100644 --- a/src/_binaries/build/install/install-binary.yaml +++ b/src/_binaries/build/install/install-binary.yaml @@ -1,4 +1,5 @@ extends: + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" diff --git a/src/_binaries/build/install/install-main.sh b/src/_binaries/build/install/install-main.sh index fe7a4391..08efb1e3 100755 --- a/src/_binaries/build/install/install-main.sh +++ b/src/_binaries/build/install/install-main.sh @@ -1,8 +1,5 @@ #!/usr/bin/env bash -Linux::requireExecutedAsUser -Linux::requireTarCommand - if ! command -v parallel &>/dev/null; then Log::displayInfo "We will install GNU parallel software, please enter you sudo password" sudo apt-get update || true @@ -28,6 +25,7 @@ if [[ -d "${HOME}/.bash-tools" ]]; then Log::displayInfo "Updating configuration" cp -R --no-clobber "${BASH_TOOLS_ROOT_DIR}/conf/." "${HOME}/.bash-tools" + cp "${BASH_TOOLS_ROOT_DIR}/conf/defaultEnv/.env" "${HOME}/.bash-tools" if [[ "${FRAMEWORK_ROOT_DIR}/conf/.env" -nt "${HOME}/.bash-tools/.env" ]]; then Log::displayWarning "${FRAMEWORK_ROOT_DIR}/conf/.env is newer than ${HOME}/.bash-tools/.env, compare the files to check if some updates need to be applied" else @@ -36,5 +34,6 @@ if [[ -d "${HOME}/.bash-tools" ]]; then else Log::displayInfo "Installing configuration in ~/.bash-tools" mkdir -p "${HOME}/.bash-tools" - cp -R "${BASE_DIR}/conf/." "${HOME}/.bash-tools" + cp -R "${BASH_TOOLS_ROOT_DIR}/conf/." "${HOME}/.bash-tools" + cp "${BASH_TOOLS_ROOT_DIR}/conf/defaultEnv/.env" "${HOME}/.bash-tools" fi diff --git a/src/_binaries/build/install/install-options.sh b/src/_binaries/build/install/install-options.sh index b1cd70fd..bdd6da78 100755 --- a/src/_binaries/build/install/install-options.sh +++ b/src/_binaries/build/install/install-options.sh @@ -7,3 +7,9 @@ optionHelpCallback() { installCommandHelp exit 0 } + +beforeParseCallback() { + defaultBeforeParseCallback + Linux::requireExecutedAsUser + Linux::requireTarCommand +} diff --git a/src/_binaries/build/installRequirements/installRequirements-binary.yaml b/src/_binaries/build/installRequirements/installRequirements-binary.yaml index 33e71db9..1d310961 100644 --- a/src/_binaries/build/installRequirements/installRequirements-binary.yaml +++ b/src/_binaries/build/installRequirements/installRequirements-binary.yaml @@ -1,4 +1,5 @@ extends: + - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" @@ -7,7 +8,6 @@ vars: compilerConfig: targetFile: "${BASH_TOOLS_ROOT_DIR}/bin/installRequirements" - relativeRootDirBasedOnTargetDir: .. binData: commands: default: diff --git a/src/_binaries/commandDefinitions/optionsDefault.sh b/src/_binaries/commandDefinitions/optionsDefault.sh index 7a6b250f..554c49f7 100755 --- a/src/_binaries/commandDefinitions/optionsDefault.sh +++ b/src/_binaries/commandDefinitions/optionsDefault.sh @@ -1,8 +1,12 @@ #!/usr/bin/env bash -beforeParseCallback() { +defaultBeforeParseCallback() { BashTools::Conf::requireLoad Env::requireLoad UI::requireTheme Log::requireLoad } + +beforeParseCallback() { + defaultBeforeParseCallback +} diff --git a/src/_binaries/commandDefinitions/optionsDefault.yaml b/src/_binaries/commandDefinitions/optionsDefault.yaml new file mode 100644 index 00000000..6e232b19 --- /dev/null +++ b/src/_binaries/commandDefinitions/optionsDefault.yaml @@ -0,0 +1,15 @@ +--- +compilerConfig: + relativeRootDirBasedOnTargetDir: .. + srcDirs: + - ${BASH_TOOLS_ROOT_DIR}/src + templateDirs: + - ${BASH_TOOLS_ROOT_DIR}/conf/defaultEnv + +binData: + commands: + default: + definitionFiles: + 11: "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.sh" + beforeParseCallbacks: + - beforeParseCallback diff --git a/src/_binaries/commandDefinitions/optionsFromDsn.sh b/src/_binaries/commandDefinitions/optionsFromDsn.sh new file mode 100755 index 00000000..ec487d25 --- /dev/null +++ b/src/_binaries/commandDefinitions/optionsFromDsn.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +fromDsnOptionLongDescription() { + local dsnList="" + dsnList="$(Conf::getMergedList "dsn" "env" " - ")" + + echo -e " ${__HELP_TITLE}Data Source Name (DSN)${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}Default dsn directory:${__HELP_NORMAL}" + echo -e " ${BASH_TOOLS_ROOT_DIR}/conf/dsn" + echo + echo -e " ${__HELP_TITLE}User dsn directory:${__HELP_NORMAL}" + echo -e " ${HOME}/.bash-tools/dsn" + echo -e ' Allows to override dsn defined in "Default dsn directory"' + echo + echo -e " ${__HELP_TITLE}List of available dsn:${__HELP_NORMAL}" + echo -e "${dsnList}" +} diff --git a/src/_binaries/commandDefinitions/optionsFromDsn.yaml b/src/_binaries/commandDefinitions/optionsFromDsn.yaml index 415204b8..a46d4462 100644 --- a/src/_binaries/commandDefinitions/optionsFromDsn.yaml +++ b/src/_binaries/commandDefinitions/optionsFromDsn.yaml @@ -2,6 +2,8 @@ binData: commands: default: + definitionFiles: + 29: "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsFromDsn.sh" optionGroups: groupSourceDbOptions: title: "SOURCE OPTIONS:" diff --git a/src/_binaries/commandDefinitions/optionsJobs.yaml b/src/_binaries/commandDefinitions/optionsJobs.yaml index d5352c4d..4d09ceab 100644 --- a/src/_binaries/commandDefinitions/optionsJobs.yaml +++ b/src/_binaries/commandDefinitions/optionsJobs.yaml @@ -23,9 +23,9 @@ binData: - variableName: optionProgressBar group: groupJobOptions type: Boolean - help: | - Show progress as a progress bar. In the bar is shown: % of jobs completed, - estimated seconds left, and number of jobs started. + help: + "Show progress as a progress bar. In the bar is shown: % of + jobs\ncompleted, estimated seconds left, and number of jobs started." callbacks: - optionProgressBarCallback alts: diff --git a/src/_binaries/commandDefinitions/optionsMysqlSource.sh b/src/_binaries/commandDefinitions/optionsMysqlSource.sh index b4d0d502..653bf543 100755 --- a/src/_binaries/commandDefinitions/optionsMysqlSource.sh +++ b/src/_binaries/commandDefinitions/optionsMysqlSource.sh @@ -3,3 +3,10 @@ dsnHelpFunction() { echo " target mysql server" } + +mysqlSourceLongDescription() { + fromDsnOptionLongDescription + echo + echo -e " ${__HELP_TITLE}Aws s3 location:${__HELP_NORMAL}" + echo -e " ${S3_BASE_URL}" +} diff --git a/src/_binaries/commandDefinitions/optionsMysqlSource.yaml b/src/_binaries/commandDefinitions/optionsMysqlSource.yaml index 255d4ab2..0110e894 100644 --- a/src/_binaries/commandDefinitions/optionsMysqlSource.yaml +++ b/src/_binaries/commandDefinitions/optionsMysqlSource.yaml @@ -3,6 +3,7 @@ binData: commands: default: definitionFiles: + 29: "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsFromDsn.sh" 40: "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsMysqlSource.sh" optionGroups: groupSourceDbOptions: diff --git a/src/_binaries/commandDefinitions/optionsMysqlTarget.yaml b/src/_binaries/commandDefinitions/optionsMysqlTarget.yaml index c8313238..743f636f 100644 --- a/src/_binaries/commandDefinitions/optionsMysqlTarget.yaml +++ b/src/_binaries/commandDefinitions/optionsMysqlTarget.yaml @@ -4,6 +4,7 @@ binData: default: definitionFiles: 23: "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsMysqlTarget.sh" + 29: "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsFromDsn.sh" optionGroups: groupTargetOptions: title: "TARGET OPTIONS:" diff --git a/src/_binaries/commandDefinitions/optionsProfile.sh b/src/_binaries/commandDefinitions/optionsProfile.sh index 54bbac73..e0b1a1f9 100755 --- a/src/_binaries/commandDefinitions/optionsProfile.sh +++ b/src/_binaries/commandDefinitions/optionsProfile.sh @@ -1,12 +1,42 @@ #!/usr/bin/env bash -profileOptionHelpFunction() { - Array::wrap2 " " 80 4 \ - " The name of the profile to use in order to include or exclude tables." +profileOptionLongDescription() { + local profilesList="" + profilesList="$(Conf::getMergedList "dbImportProfiles" "sh" " - " || true)" + + echo -e " ${__HELP_TITLE}Profiles${__HELP_NORMAL}" + echo -e " ${__HELP_TITLE}Default profiles directory:${__HELP_NORMAL}" + echo -e " ${PROFILES_DIR-configuration error}" + echo + echo -e " ${__HELP_TITLE}User profiles directory:${__HELP_NORMAL}" + echo -e " ${HOME_PROFILES_DIR-configuration error}" + echo -e ' Allows to override profiles defined in "Default profiles directory"' echo + echo -e " ${__HELP_TITLE}List of available profiles:${__HELP_NORMAL}" + echo -e "${profilesList}" +} + +profileOptionHelpFunction() { + echo " The name of the profile to use in order to" + echo " include or exclude tables." } +initOptionProfileIfNotSet() { + if [[ -z "${optionProfile}" ]]; then + optionProfile="${defaultOptionProfile}" + fi + local -a profilesArray + readarray -t profilesArray < <(Conf::getMergedList "dbImportProfiles" "sh" "" || true) + if ! Array::contains "${optionProfile}" "${profilesArray[@]}"; then + Log::displayError "${SCRIPT_NAME} - invalid profile '${optionProfile}' provided" + return 1 + fi +} + +declare defaultOptionProfile="default" initProfileCommandCallback() { + initOptionProfileIfNotSet + # shellcheck disable=SC2154 if [[ "${optionProfile}" != "default" && -n "${optionTables}" ]]; then Log::fatal "Command ${SCRIPT_NAME} - you cannot use table and profile options at the same time" @@ -34,18 +64,3 @@ initProfileCommandCallback() { chmod +x "${profileCommandFile}" Log::displayInfo "${profileMsgInfo}" } - -profileOptionCallback() { - local -a profilesArray - readarray -t profilesArray < <(Conf::getMergedList "dbImportProfiles" "sh" "" || true) - if ! Array::contains "$2" "${profilesArray[@]}"; then - Log::displayError "${SCRIPT_NAME} - invalid profile '$2' provided" - return 1 - fi -} - -optionTablesCallback() { - if [[ ! ${optionTables} =~ ^[A-Za-z0-9_]+(,[A-Za-z0-9_]+)*$ ]]; then - Log::fatal "Command ${SCRIPT_NAME} - Table list is not valid : ${optionTables}" - fi -} diff --git a/src/_binaries/commandDefinitions/optionsProfile.yaml b/src/_binaries/commandDefinitions/optionsProfile.yaml index df305689..f5d47a54 100644 --- a/src/_binaries/commandDefinitions/optionsProfile.yaml +++ b/src/_binaries/commandDefinitions/optionsProfile.yaml @@ -14,22 +14,7 @@ binData: group: groupProfileOptions type: String help: profileOptionHelpFunction - callbacks: - - profileOptionCallback - defaultValue: default helpValueName: profile alts: - --profile - -p - - - variableName: optionTables - group: groupProfileOptions - type: String - help: | - Import only table specified in the list. - If aws mode, ignore profile option. - callbacks: - - optionTablesCallback - helpValueName: tablesSeparatedByComma - alts: - - --tables diff --git a/src/_binaries/commandDefinitions/optionsRatio.sh b/src/_binaries/commandDefinitions/optionsRatio.sh new file mode 100755 index 00000000..4670ac8c --- /dev/null +++ b/src/_binaries/commandDefinitions/optionsRatio.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +optionRatioHelpFunction() { + echo " define the ratio to use (0 to 100% - default 70)." + echo " - 0 means profile will filter out all the tables." + echo " - 100 means profile will keep all the tables." + echo " Eg: 70 means that tables with size(table+index)" + echo " that are greater than 70% of the max table size" + echo " will be excluded." +} diff --git a/src/_binaries/commandDefinitions/optionsRatio.yaml b/src/_binaries/commandDefinitions/optionsRatio.yaml new file mode 100644 index 00000000..c03b7ad4 --- /dev/null +++ b/src/_binaries/commandDefinitions/optionsRatio.yaml @@ -0,0 +1,19 @@ +--- +binData: + commands: + default: + definitionFiles: + 28: "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsRatio.sh" + optionGroups: + groupProfileOptions: + title: "PROFILE OPTIONS:" + options: + - variableName: optionRatio + group: groupProfileOptions + type: String + help: optionRatioHelpFunction + helpValueName: ratio + defaultValue: 70 + alts: + - --ratio + - -r diff --git a/src/_binaries/commandDefinitions/optionsTables.sh b/src/_binaries/commandDefinitions/optionsTables.sh new file mode 100755 index 00000000..d4328ce5 --- /dev/null +++ b/src/_binaries/commandDefinitions/optionsTables.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +optionTablesCallback() { + # shellcheck disable=SC2154 + if [[ ! ${optionTables} =~ ^[A-Za-z0-9_]+(,[A-Za-z0-9_]+)*$ ]]; then + Log::fatal "Command ${SCRIPT_NAME} - Table list is not valid : ${optionTables}" + fi +} diff --git a/src/_binaries/commandDefinitions/optionsTables.yaml b/src/_binaries/commandDefinitions/optionsTables.yaml new file mode 100644 index 00000000..5bac08ba --- /dev/null +++ b/src/_binaries/commandDefinitions/optionsTables.yaml @@ -0,0 +1,21 @@ +--- +binData: + commands: + default: + definitionFiles: + 28: "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsTables.sh" + optionGroups: + groupProfileOptions: + title: "PROFILE OPTIONS:" + options: + - variableName: optionTables + group: groupProfileOptions + type: String + help: + "Import only table specified in the list.\nIf aws mode, ignore + profile option." + callbacks: + - optionTablesCallback + helpValueName: tablesSeparatedByComma + alts: + - --tables diff --git a/src/batsHeaders.sh b/src/batsHeaders.sh index d9786497..2a8fb64c 100755 --- a/src/batsHeaders.sh +++ b/src/batsHeaders.sh @@ -16,7 +16,7 @@ export BASH_FRAMEWORK_DISPLAY_LEVEL="${__LEVEL_INFO}" export BASH_FRAMEWORK_LOG_LEVEL=${__LEVEL_OFF} export SKIP_REQUIREMENTS_CHECKS=1 -Env::requireLoad "${rootDir}/conf/.env" +Env::requireLoad "${rootDir}/conf/defaultEnv/.env" Log::requireLoad # @description test command help From 5ef39aec762a5e61da9c18ae5752d5edeeb51be4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Sun, 15 Sep 2024 17:14:37 +0200 Subject: [PATCH 21/24] update binaries template using authorizedValues --- .pre-commit-config-github.yaml | 7 +- .pre-commit-config.yaml | 7 +- bin/cli | 59 +++++++------ bin/dbImport | 66 +++++++------- bin/dbImportProfile | 54 ++++++------ bin/dbImportStream | 62 +++++++------- bin/dbQueryAllDatabases | 53 ++++++------ bin/dbScriptAllDatabases | 70 +++++++-------- bin/doc | 45 +++++----- bin/gitIsAncestorOf | 58 +++++++------ bin/gitIsBranch | 51 ++++++----- bin/gitRenameBranch | 61 ++++++------- bin/installRequirements | 44 ++++++---- bin/mysql2puml | 52 +++++------ bin/postmanCli | 66 ++++++++------ bin/upgradeGithubRelease | 59 ++++++------- bin/waitForIt | 69 ++++++++------- bin/waitForMysql | 81 +++++++++--------- conf/dbScripts/extractData | 44 ++++++---- install | 45 +++++----- .../mysql2puml/testsData/mysql2puml.help.txt | 28 ++++-- .../mysql2puml/testsData/mysql2puml.svg | 2 +- .../testsData/mysql2pumlSkins/default.png | Bin 950 -> 949 bytes .../testsData/mysql2pumlSkins/default.svg | 2 +- .../dbImport/testsData/dbImport.help.txt | 31 +++++-- .../testsData/dbImportProfile.help.txt | 28 ++++-- .../testsData/dbImportStream.help.txt | 31 +++++-- .../testsData/dbQueryAllDatabases.help.txt | 28 ++++-- .../dbScriptAllDatabases-binary.yaml | 7 +- .../testsData/dbScriptAllDatabases.help.txt | 35 ++++++-- .../Docker/cli/testsData/cli.help.txt | 34 ++++++-- .../testsData/gitIsAncestorOf.help.txt | 31 +++++-- .../testsData/gitIsBranch.help.txt | 28 ++++-- .../testsData/gitRenameBranch.help.txt | 31 +++++-- .../testsData/upgradeGithubRelease.help.txt | 31 +++++-- .../postmanCli/testsData/postmanCli.help.txt | 34 ++++++-- .../waitForIt/testsData/waitForIt.help.txt | 34 ++++++-- .../Utils/waitForIt/waitForIt-binary.yaml | 1 - .../testsData/waitForMysql.help.txt | 40 ++++++--- 39 files changed, 901 insertions(+), 608 deletions(-) diff --git a/.pre-commit-config-github.yaml b/.pre-commit-config-github.yaml index f095b61a..9539ab7b 100644 --- a/.pre-commit-config-github.yaml +++ b/.pre-commit-config-github.yaml @@ -94,13 +94,13 @@ repos: )$ - repo: https://github.com/rhysd/actionlint - rev: v1.6.27 + rev: v1.7.1 hooks: - id: actionlint stages: [pre-commit, pre-push, manual] - repo: https://github.com/codespell-project/codespell - rev: v2.2.6 + rev: v2.3.0 hooks: - id: codespell args: @@ -161,7 +161,7 @@ repos: exclude: /testsData/ - repo: https://github.com/fchastanet/bash-tools-framework - rev: 5.1.2 + rev: 4.0.4 hooks: - id: fixShebangExecutionBit - id: awkLint @@ -192,6 +192,7 @@ repos: WARNING, ] - id: plantuml + args: [--same-dir, -f, png, -f, svg, --limit-size, "1200"] exclude: | (?x)( ^conf/mysql2pumlSkins/default.puml$ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7c89af86..29ce7dd3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -89,13 +89,13 @@ repos: )$ - repo: https://github.com/rhysd/actionlint - rev: v1.6.27 + rev: v1.7.1 hooks: - id: actionlint stages: [pre-commit, pre-push, manual] - repo: https://github.com/codespell-project/codespell - rev: v2.2.6 + rev: v2.3.0 hooks: - id: codespell args: @@ -156,7 +156,7 @@ repos: exclude: /testsData/ - repo: https://github.com/fchastanet/bash-tools-framework - rev: 5.1.2 + rev: 4.0.4 hooks: - id: fixShebangExecutionBit - id: awkLint @@ -187,6 +187,7 @@ repos: WARNING, ] - id: plantuml + args: [--same-dir, -f, png, -f, svg, --limit-size, "1200"] exclude: | (?x)( ^conf/mysql2pumlSkins/default.puml$ diff --git a/bin/cli b/bin/cli index 75d631b3..b1aa1dfc 100755 --- a/bin/cli +++ b/bin/cli @@ -1824,7 +1824,7 @@ cliCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1875,7 +1875,7 @@ cliCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1921,7 +1921,7 @@ cliCommandParse() { return 1 fi if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default, default-force, noColor)" return 1 fi @@ -1991,7 +1991,6 @@ cliCommandParse() { # Argument 1/3 - containerArg # Argument containerArg min 0 max 1 - # Argument containerArg authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && options_parse_parsedArgIndex < maxParsedArgIndex1 )); then if ((options_parse_argParsedCountContainerArg >= 1 )); then @@ -2005,7 +2004,6 @@ cliCommandParse() { # Argument 2/3 - userArg # Argument userArg min 0 max 1 - # Argument userArg authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex1 && options_parse_parsedArgIndex < maxParsedArgIndex2 )); then if ((options_parse_argParsedCountUserArg >= 1 )); then @@ -2019,7 +2017,6 @@ cliCommandParse() { # Argument 3/3 - commandArg # Argument commandArg min 0 max 1 - # Argument commandArg authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex2 && options_parse_parsedArgIndex < maxParsedArgIndex3 )); then if ((options_parse_argParsedCountCommandArg >= 1 )); then @@ -2081,18 +2078,18 @@ cliCommandHelp() { echo echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}container${__HELP_NORMAL} {single}] - " + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}container${__HELP_NORMAL} {single}]" containerArgHelpFunction - Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}user${__HELP_NORMAL} {single}] - " + + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}user${__HELP_NORMAL} {single}]" userArgHelpFunction - Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}command${__HELP_NORMAL} {single}] - " + + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}command${__HELP_NORMAL} {single}]" commandArgHelpFunction + # ------------------------------------------ # options section # ------------------------------------------ @@ -2103,69 +2100,77 @@ cliCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Displays configuration" echo - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." echo - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" echo - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" echo - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" echo - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log file" echo - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set display level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." echo - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." echo - Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}default${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}default-force${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}noColor${__RESET_COLOR}" Array::wrap2 ' ' 76 6 " Default value: " "default" echo @@ -2174,12 +2179,10 @@ cliCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." echo - # ------------------------------------------ # longDescription section # ------------------------------------------ diff --git a/bin/dbImport b/bin/dbImport index 90a9de58..7805b7f4 100755 --- a/bin/dbImport +++ b/bin/dbImport @@ -2338,7 +2338,7 @@ dbImportCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -2389,7 +2389,7 @@ dbImportCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -2435,7 +2435,7 @@ dbImportCommandParse() { return 1 fi if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default, default-force, noColor)" return 1 fi @@ -2506,7 +2506,6 @@ dbImportCommandParse() { # Argument 1/2 - fromDbName # Argument fromDbName min 1 max 1 - # Argument fromDbName authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && options_parse_parsedArgIndex < maxParsedArgIndex1 )); then if ((options_parse_argParsedCountFromDbName >= 1 )); then @@ -2520,7 +2519,6 @@ dbImportCommandParse() { # Argument 2/2 - targetDbName # Argument targetDbName min 0 max 1 - # Argument targetDbName authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex1 && options_parse_parsedArgIndex < maxParsedArgIndex2 )); then if ((options_parse_argParsedCountTargetDbName >= 1 )); then @@ -2612,14 +2610,14 @@ dbImportCommandHelp() { echo echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}fromDbName${__HELP_NORMAL} {single} (mandatory) - " + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}fromDbName${__HELP_NORMAL} {single} (mandatory)" Array::wrap2 ' ' 76 4 " " "The name of the source/remote database." - echo - Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}${__HELP_NORMAL} {single}] - " + echo + + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}${__HELP_NORMAL} {single}]" Array::wrap2 ' ' 76 4 " " "The name of the target database" "Default value: (without extension)" "" - echo + echo + # ------------------------------------------ # options section # ------------------------------------------ @@ -2630,12 +2628,10 @@ dbImportCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--target-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-t ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Dsn to use for target database." echo - Array::wrap2 ' ' 76 6 " Default value: " "default.local" echo @@ -2643,7 +2639,6 @@ dbImportCommandHelp() { Array::wrap2 ' ' 76 4 " " "Change the character set used during database creation." echo - echo echo -e "${__HELP_TITLE_COLOR}PROFILE OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--profile${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-p ${__HELP_NORMAL} {single}" @@ -2651,12 +2646,10 @@ dbImportCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--tables ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Import only table specified in the list." "If aws mode, ignore profile option." echo - echo echo -e "${__HELP_TITLE_COLOR}SOURCE OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--skip-schema${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-s${__HELP_NORMAL} {single}" @@ -2664,18 +2657,15 @@ dbImportCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--from-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-f ${__HELP_NORMAL} {single}" dsnHelpFunction - echo -e " ${__HELP_OPTION_COLOR}--from-aws${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-a ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "db dump will be downloaded from s3 instead of using remote db." "The value is the name of the file without s3 location" "(Only .gz or tar.gz file are supported)." "This option is incompatible with -f|--from-dsn option." "" echo - echo echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" @@ -2683,69 +2673,77 @@ dbImportCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Displays configuration" echo - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." echo - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" echo - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" echo - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" echo - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log file" echo - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set display level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." echo - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." echo - Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}default${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}default-force${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}noColor${__RESET_COLOR}" Array::wrap2 ' ' 76 6 " Default value: " "default" echo @@ -2754,12 +2752,10 @@ dbImportCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." echo - # ------------------------------------------ # longDescription section # ------------------------------------------ diff --git a/bin/dbImportProfile b/bin/dbImportProfile index 1dfc9a80..7aa5a393 100755 --- a/bin/dbImportProfile +++ b/bin/dbImportProfile @@ -2053,7 +2053,7 @@ dbImportProfileCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -2104,7 +2104,7 @@ dbImportProfileCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -2150,7 +2150,7 @@ dbImportProfileCommandParse() { return 1 fi if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default, default-force, noColor)" return 1 fi @@ -2219,7 +2219,6 @@ dbImportProfileCommandParse() { # Argument 1/1 - fromDbName # Argument fromDbName min 1 max 1 - # Argument fromDbName authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && options_parse_parsedArgIndex < maxParsedArgIndex1 )); then if ((options_parse_argParsedCountFromDbName >= 1 )); then @@ -2306,10 +2305,10 @@ dbImportProfileCommandHelp() { echo echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}fromDbName${__HELP_NORMAL} {single} (mandatory) - " + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}fromDbName${__HELP_NORMAL} {single} (mandatory)" Array::wrap2 ' ' 76 4 " " "The name of the source/remote database." - echo + echo + # ------------------------------------------ # options section # ------------------------------------------ @@ -2319,7 +2318,6 @@ dbImportProfileCommandHelp() { optionRatioHelpFunction - Array::wrap2 ' ' 76 6 " Default value: " "70" echo @@ -2327,14 +2325,12 @@ dbImportProfileCommandHelp() { profileOptionHelpFunction - echo echo -e "${__HELP_TITLE_COLOR}SOURCE OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--from-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-f ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Target mysql server." echo - echo echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" @@ -2342,69 +2338,77 @@ dbImportProfileCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Displays configuration" echo - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." echo - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" echo - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" echo - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" echo - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log file" echo - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set display level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." echo - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." echo - Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}default${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}default-force${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}noColor${__RESET_COLOR}" Array::wrap2 ' ' 76 6 " Default value: " "default" echo @@ -2413,12 +2417,10 @@ dbImportProfileCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." echo - # ------------------------------------------ # longDescription section # ------------------------------------------ diff --git a/bin/dbImportStream b/bin/dbImportStream index 7c30dc70..e3a49d87 100755 --- a/bin/dbImportStream +++ b/bin/dbImportStream @@ -2102,7 +2102,7 @@ dbImportStreamCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -2153,7 +2153,7 @@ dbImportStreamCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -2199,7 +2199,7 @@ dbImportStreamCommandParse() { return 1 fi if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default, default-force, noColor)" return 1 fi @@ -2270,7 +2270,6 @@ dbImportStreamCommandParse() { # Argument 1/2 - argDumpFile # Argument argDumpFile min 1 max 1 - # Argument argDumpFile authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && options_parse_parsedArgIndex < maxParsedArgIndex1 )); then if ((options_parse_argParsedCountArgDumpFile >= 1 )); then @@ -2284,7 +2283,6 @@ dbImportStreamCommandParse() { # Argument 2/2 - argTargetDbName # Argument argTargetDbName min 1 max 1 - # Argument argTargetDbName authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex1 && options_parse_parsedArgIndex < maxParsedArgIndex2 )); then if ((options_parse_argParsedCountArgTargetDbName >= 1 )); then @@ -2377,14 +2375,14 @@ dbImportStreamCommandHelp() { echo echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}argDumpFile${__HELP_NORMAL} {single} (mandatory) - " + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}argDumpFile${__HELP_NORMAL} {single} (mandatory)" Array::wrap2 ' ' 76 4 " " "The file that will be streamed through mysql." - echo - Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}argTargetDbName${__HELP_NORMAL} {single} (mandatory) - " + echo + + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}argTargetDbName${__HELP_NORMAL} {single} (mandatory)" Array::wrap2 ' ' 76 4 " " "The name of the mysql target database." - echo + echo + # ------------------------------------------ # options section # ------------------------------------------ @@ -2395,19 +2393,16 @@ dbImportStreamCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--tables ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Import only table specified in the list." "If aws mode, ignore profile option." echo - echo echo -e "${__HELP_TITLE_COLOR}TARGET OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--target-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-t ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Dsn to use for target database." echo - Array::wrap2 ' ' 76 6 " Default value: " "default.local" echo @@ -2415,7 +2410,6 @@ dbImportStreamCommandHelp() { Array::wrap2 ' ' 76 4 " " "Change the character set used during database creation." echo - echo echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" @@ -2423,69 +2417,77 @@ dbImportStreamCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Displays configuration" echo - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." echo - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" echo - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" echo - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" echo - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log file" echo - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set display level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." echo - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." echo - Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}default${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}default-force${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}noColor${__RESET_COLOR}" Array::wrap2 ' ' 76 6 " Default value: " "default" echo @@ -2494,12 +2496,10 @@ dbImportStreamCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." echo - # ------------------------------------------ # longDescription section # ------------------------------------------ diff --git a/bin/dbQueryAllDatabases b/bin/dbQueryAllDatabases index e3657570..e2b1af68 100755 --- a/bin/dbQueryAllDatabases +++ b/bin/dbQueryAllDatabases @@ -2106,7 +2106,7 @@ dbQueryAllDatabasesCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -2157,7 +2157,7 @@ dbQueryAllDatabasesCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -2203,7 +2203,7 @@ dbQueryAllDatabasesCommandParse() { return 1 fi if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default, default-force, noColor)" return 1 fi @@ -2293,7 +2293,6 @@ dbQueryAllDatabasesCommandParse() { # Argument 1/1 - argQuery # Argument argQuery min 1 max 1 - # Argument argQuery authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && options_parse_parsedArgIndex < maxParsedArgIndex1 )); then if ((options_parse_argParsedCountArgQuery >= 1 )); then @@ -2383,10 +2382,10 @@ dbQueryAllDatabasesCommandHelp() { echo echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}argQuery${__HELP_NORMAL} {single} (mandatory) - " + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}argQuery${__HELP_NORMAL} {single} (mandatory)" argQueryHelpFunction + # ------------------------------------------ # options section # ------------------------------------------ @@ -2396,7 +2395,6 @@ dbQueryAllDatabasesCommandHelp() { Array::wrap2 ' ' 76 4 " " "The number of databases to query in parallel." echo - Array::wrap2 ' ' 76 6 " Default value: " "1" echo @@ -2404,14 +2402,12 @@ dbQueryAllDatabasesCommandHelp() { Array::wrap2 ' ' 76 4 " " "Show progress as a progress bar. In the bar is shown: % of jobs" "completed, estimated seconds left, and number of jobs started." echo - echo echo -e "${__HELP_TITLE_COLOR}SOURCE OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--from-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-f ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Target mysql server." echo - echo echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" @@ -2419,69 +2415,77 @@ dbQueryAllDatabasesCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Displays configuration" echo - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." echo - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" echo - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" echo - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" echo - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log file" echo - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set display level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." echo - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." echo - Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}default${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}default-force${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}noColor${__RESET_COLOR}" Array::wrap2 ' ' 76 6 " Default value: " "default" echo @@ -2490,19 +2494,16 @@ dbQueryAllDatabasesCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." echo - echo echo -e "${__HELP_TITLE_COLOR}QUERY OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--separator${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-s ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Character to use to separate mysql column." echo - Array::wrap2 ' ' 76 6 " Default value: " "|" echo # ------------------------------------------ diff --git a/bin/dbScriptAllDatabases b/bin/dbScriptAllDatabases index 23416349..b13433d3 100755 --- a/bin/dbScriptAllDatabases +++ b/bin/dbScriptAllDatabases @@ -2012,7 +2012,7 @@ dbScriptAllDatabasesCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -2063,7 +2063,7 @@ dbScriptAllDatabasesCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -2109,7 +2109,7 @@ dbScriptAllDatabasesCommandParse() { return 1 fi if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default, default-force, noColor)" return 1 fi @@ -2210,7 +2210,7 @@ dbScriptAllDatabasesCommandParse() { return 1 fi if [[ ! "$1" =~ none|log ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([none log])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(none, log)" return 1 fi @@ -2245,7 +2245,6 @@ dbScriptAllDatabasesCommandParse() { # Argument 1/2 - argScriptToExecute # Argument argScriptToExecute min 1 max 1 - # Argument argScriptToExecute authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && options_parse_parsedArgIndex < maxParsedArgIndex1 )); then if ((options_parse_argParsedCountArgScriptToExecute >= 1 )); then @@ -2260,7 +2259,6 @@ dbScriptAllDatabasesCommandParse() { # Argument 2/2 - scriptArguments # Argument scriptArguments min 0 max -1 - # Argument scriptArguments authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex1 )); then ((++options_parse_argParsedCountScriptArguments)) # shellcheck disable=SC2034 @@ -2344,14 +2342,14 @@ dbScriptAllDatabasesCommandHelp() { echo echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}scriptToExecute${__HELP_NORMAL} {single} (mandatory) - " + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}scriptToExecute${__HELP_NORMAL} {single} (mandatory)" Array::wrap2 ' ' 76 4 " " "The script that will be executed on each databases." - echo - Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}scriptArguments${__HELP_NORMAL} {list} (optional)] - " + echo + + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}scriptArguments${__HELP_NORMAL} {list} (optional)]" Array::wrap2 ' ' 76 4 " " "Optional parameters to pass to the script." - echo + echo + # ------------------------------------------ # options section # ------------------------------------------ @@ -2361,7 +2359,6 @@ dbScriptAllDatabasesCommandHelp() { Array::wrap2 ' ' 76 4 " " "The number of databases to query in parallel." echo - Array::wrap2 ' ' 76 6 " Default value: " "1" echo @@ -2369,14 +2366,12 @@ dbScriptAllDatabasesCommandHelp() { Array::wrap2 ' ' 76 4 " " "Show progress as a progress bar. In the bar is shown: % of jobs" "completed, estimated seconds left, and number of jobs started." echo - echo echo -e "${__HELP_TITLE_COLOR}SOURCE OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--from-dsn${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-f ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Target mysql server." echo - echo echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" @@ -2384,69 +2379,77 @@ dbScriptAllDatabasesCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Displays configuration" echo - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." echo - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" echo - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" echo - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" echo - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log file" echo - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set display level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." echo - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." echo - Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}default${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}default-force${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}noColor${__RESET_COLOR}" Array::wrap2 ' ' 76 6 " Default value: " "default" echo @@ -2455,12 +2458,10 @@ dbScriptAllDatabasesCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." echo - echo echo -e "${__HELP_TITLE_COLOR}SCRIPTS OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--database ${__HELP_NORMAL} {single}" @@ -2468,19 +2469,18 @@ dbScriptAllDatabasesCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--output${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-o ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Output directory, see log-format option." echo - echo -e " ${__HELP_OPTION_COLOR}--log-format${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-l ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "If output dir provided, will log each db result to log file." echo - Array::wrap2 ' ' 76 6 " Possible values: " "none, " "log" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}none${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}log${__RESET_COLOR}" Array::wrap2 ' ' 76 6 " Default value: " "none" echo # ------------------------------------------ diff --git a/bin/doc b/bin/doc index 7129dfb5..838ff28d 100755 --- a/bin/doc +++ b/bin/doc @@ -1688,7 +1688,7 @@ docCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1739,7 +1739,7 @@ docCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1785,7 +1785,7 @@ docCommandParse() { return 1 fi if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default, default-force, noColor)" return 1 fi @@ -1892,7 +1892,6 @@ docCommandHelp() { Array::wrap2 ' ' 76 4 " " "Activate continuous integration mode (tmp folder not shared with host)" "" echo - echo echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" @@ -1900,69 +1899,77 @@ docCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Displays configuration" echo - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." echo - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" echo - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" echo - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" echo - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log file" echo - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set display level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." echo - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." echo - Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}default${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}default-force${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}noColor${__RESET_COLOR}" Array::wrap2 ' ' 76 6 " Default value: " "default" echo @@ -1971,12 +1978,10 @@ docCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." echo - # ------------------------------------------ # version section # ------------------------------------------ diff --git a/bin/gitIsAncestorOf b/bin/gitIsAncestorOf index c337d386..c935a28e 100755 --- a/bin/gitIsAncestorOf +++ b/bin/gitIsAncestorOf @@ -1491,7 +1491,7 @@ gitIsAncestorOfCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1542,7 +1542,7 @@ gitIsAncestorOfCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1588,7 +1588,7 @@ gitIsAncestorOfCommandParse() { return 1 fi if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default, default-force, noColor)" return 1 fi @@ -1659,7 +1659,6 @@ gitIsAncestorOfCommandParse() { # Argument 1/2 - claimedBranchArg # Argument claimedBranchArg min 1 max 1 - # Argument claimedBranchArg authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && options_parse_parsedArgIndex < maxParsedArgIndex1 )); then if ((options_parse_argParsedCountClaimedBranchArg >= 1 )); then @@ -1673,7 +1672,6 @@ gitIsAncestorOfCommandParse() { # Argument 2/2 - refArg # Argument refArg min 1 max 1 - # Argument refArg authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex1 && options_parse_parsedArgIndex < maxParsedArgIndex2 )); then if ((options_parse_argParsedCountRefArg >= 1 )); then @@ -1760,14 +1758,14 @@ gitIsAncestorOfCommandHelp() { echo echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}claimedBranch${__HELP_NORMAL} {single} (mandatory) - " + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}claimedBranch${__HELP_NORMAL} {single} (mandatory)" Array::wrap2 ' ' 76 4 " " "The branch in which the ref will be searched." - echo - Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}ref${__HELP_NORMAL} {single} (mandatory) - " + echo + + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}ref${__HELP_NORMAL} {single} (mandatory)" Array::wrap2 ' ' 76 4 " " "The ref to check." - echo + echo + # ------------------------------------------ # options section # ------------------------------------------ @@ -1778,69 +1776,77 @@ gitIsAncestorOfCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Displays configuration" echo - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." echo - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" echo - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" echo - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" echo - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log file" echo - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set display level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." echo - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." echo - Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}default${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}default-force${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}noColor${__RESET_COLOR}" Array::wrap2 ' ' 76 6 " Default value: " "default" echo @@ -1849,12 +1855,10 @@ gitIsAncestorOfCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." echo - # ------------------------------------------ # longDescription section # ------------------------------------------ diff --git a/bin/gitIsBranch b/bin/gitIsBranch index 4ab4d29e..6a4bfa31 100755 --- a/bin/gitIsBranch +++ b/bin/gitIsBranch @@ -1486,7 +1486,7 @@ gitIsBranchCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1537,7 +1537,7 @@ gitIsBranchCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1583,7 +1583,7 @@ gitIsBranchCommandParse() { return 1 fi if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default, default-force, noColor)" return 1 fi @@ -1652,7 +1652,6 @@ gitIsBranchCommandParse() { # Argument 1/1 - branchNameArg # Argument branchNameArg min 1 max 1 - # Argument branchNameArg authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && options_parse_parsedArgIndex < maxParsedArgIndex1 )); then if ((options_parse_argParsedCountBranchNameArg >= 1 )); then @@ -1734,10 +1733,10 @@ gitIsBranchCommandHelp() { echo echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}branchName${__HELP_NORMAL} {single} (mandatory) - " + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}branchName${__HELP_NORMAL} {single} (mandatory)" Array::wrap2 ' ' 76 4 " " "The branch name to check." - echo + echo + # ------------------------------------------ # options section # ------------------------------------------ @@ -1748,69 +1747,77 @@ gitIsBranchCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Displays configuration" echo - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." echo - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" echo - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" echo - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" echo - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log file" echo - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set display level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." echo - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." echo - Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}default${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}default-force${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}noColor${__RESET_COLOR}" Array::wrap2 ' ' 76 6 " Default value: " "default" echo @@ -1819,12 +1826,10 @@ gitIsBranchCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." echo - # ------------------------------------------ # longDescription section # ------------------------------------------ diff --git a/bin/gitRenameBranch b/bin/gitRenameBranch index ef6c4ee7..5ac67014 100755 --- a/bin/gitRenameBranch +++ b/bin/gitRenameBranch @@ -1549,7 +1549,7 @@ gitRenameBranchCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1600,7 +1600,7 @@ gitRenameBranchCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1646,7 +1646,7 @@ gitRenameBranchCommandParse() { return 1 fi if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default, default-force, noColor)" return 1 fi @@ -1759,7 +1759,6 @@ gitRenameBranchCommandParse() { # Argument 1/2 - newBranchNameArg # Argument newBranchNameArg min 1 max 1 - # Argument newBranchNameArg authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && options_parse_parsedArgIndex < maxParsedArgIndex1 )); then if ((options_parse_argParsedCountNewBranchNameArg >= 1 )); then @@ -1773,7 +1772,6 @@ gitRenameBranchCommandParse() { # Argument 2/2 - oldBranchNameArg # Argument oldBranchNameArg min 0 max 1 - # Argument oldBranchNameArg authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex1 && options_parse_parsedArgIndex < maxParsedArgIndex2 )); then if ((options_parse_argParsedCountOldBranchNameArg >= 1 )); then @@ -1860,14 +1858,14 @@ gitRenameBranchCommandHelp() { echo echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}newBranchName${__HELP_NORMAL} {single} (mandatory) - " + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}newBranchName${__HELP_NORMAL} {single} (mandatory)" Array::wrap2 ' ' 76 4 " " "The new branch name." - echo - Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}oldBranchName${__HELP_NORMAL} {single}] - " + echo + + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}oldBranchName${__HELP_NORMAL} {single}]" Array::wrap2 ' ' 76 4 " " "The name of the old branch if not current one." - echo + echo + # ------------------------------------------ # options section # ------------------------------------------ @@ -1878,69 +1876,77 @@ gitRenameBranchCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Displays configuration" echo - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." echo - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" echo - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" echo - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" echo - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log file" echo - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set display level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." echo - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." echo - Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}default${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}default-force${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}noColor${__RESET_COLOR}" Array::wrap2 ' ' 76 6 " Default value: " "default" echo @@ -1949,12 +1955,10 @@ gitRenameBranchCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." echo - echo echo -e "${__HELP_TITLE_COLOR}OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--assume-yes${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}--yes${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-y${__HELP_NORMAL} {single}" @@ -1962,18 +1966,15 @@ gitRenameBranchCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--push${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-p${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Push the new branch." echo - echo -e " ${__HELP_OPTION_COLOR}--delete${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-d${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Delete the old remote branch." echo - # ------------------------------------------ # longDescription section # ------------------------------------------ diff --git a/bin/installRequirements b/bin/installRequirements index 6452910f..56732489 100755 --- a/bin/installRequirements +++ b/bin/installRequirements @@ -1411,7 +1411,7 @@ installRequirementsCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1462,7 +1462,7 @@ installRequirementsCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1508,7 +1508,7 @@ installRequirementsCommandParse() { return 1 fi if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default, default-force, noColor)" return 1 fi @@ -1616,69 +1616,77 @@ installRequirementsCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Displays configuration" echo - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." echo - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" echo - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" echo - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" echo - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log file" echo - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set display level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." echo - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." echo - Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}default${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}default-force${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}noColor${__RESET_COLOR}" Array::wrap2 ' ' 76 6 " Default value: " "default" echo @@ -1687,12 +1695,10 @@ installRequirementsCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." echo - # ------------------------------------------ # longDescription section # ------------------------------------------ diff --git a/bin/mysql2puml b/bin/mysql2puml index 79e5830d..fcec3383 100755 --- a/bin/mysql2puml +++ b/bin/mysql2puml @@ -1573,7 +1573,7 @@ mysql2pumlCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1624,7 +1624,7 @@ mysql2pumlCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1670,7 +1670,7 @@ mysql2pumlCommandParse() { return 1 fi if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default, default-force, noColor)" return 1 fi @@ -1760,7 +1760,6 @@ mysql2pumlCommandParse() { # Argument 1/1 - inputSqlFile # Argument inputSqlFile min 0 max 1 - # Argument inputSqlFile authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && options_parse_parsedArgIndex < maxParsedArgIndex1 )); then if ((options_parse_argParsedCountInputSqlFile >= 1 )); then @@ -1824,10 +1823,10 @@ mysql2pumlCommandHelp() { echo echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}sqlFile${__HELP_NORMAL} {single}] - " + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}sqlFile${__HELP_NORMAL} {single}]" Array::wrap2 ' ' 76 4 " " "Sql filepath to parse (read from stdin if not provided)." - echo + echo + # ------------------------------------------ # options section # ------------------------------------------ @@ -1838,69 +1837,77 @@ mysql2pumlCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Displays configuration" echo - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." echo - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" echo - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" echo - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" echo - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log file" echo - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set display level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." echo - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." echo - Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}default${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}default-force${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}noColor${__RESET_COLOR}" Array::wrap2 ' ' 76 6 " Default value: " "default" echo @@ -1909,19 +1916,16 @@ mysql2pumlCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." echo - echo echo -e "${__HELP_TITLE_COLOR}OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--skin ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Header configuration of the plantuml file." echo - Array::wrap2 ' ' 76 6 " Default value: " "default" echo # ------------------------------------------ diff --git a/bin/postmanCli b/bin/postmanCli index 2eebe51d..44ecf2e5 100755 --- a/bin/postmanCli +++ b/bin/postmanCli @@ -2121,7 +2121,7 @@ postmanCliCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -2172,7 +2172,7 @@ postmanCliCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -2218,7 +2218,7 @@ postmanCliCommandParse() { return 1 fi if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default, default-force, noColor)" return 1 fi @@ -2305,9 +2305,13 @@ postmanCliCommandParse() { # Argument 1/2 - argCommand # Argument argCommand min 0 max 1 - # Argument argCommand authorizedValues: + # Argument argCommand authorizedValues: pull|push elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && options_parse_parsedArgIndex < maxParsedArgIndex1 )); then + if [[ ! "${options_parse_arg}" =~ pull|push ]]; then + Log::displayError "Command ${SCRIPT_NAME} - Argument command - value '${options_parse_arg}' is not part of authorized values(pull, push)" + return 1 + fi if ((options_parse_argParsedCountArgCommand >= 1 )); then Log::displayError "Command ${SCRIPT_NAME} - Argument command - Maximum number of argument occurrences reached(1)" return 1 @@ -2319,7 +2323,6 @@ postmanCliCommandParse() { # Argument 2/2 - commandArgs # Argument commandArgs min 0 max -1 - # Argument commandArgs authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex1 )); then ((++options_parse_argParsedCountCommandArgs)) # shellcheck disable=SC2034 @@ -2378,14 +2381,20 @@ postmanCliCommandHelp() { echo echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}command${__HELP_NORMAL} {single}] - " + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}command${__HELP_NORMAL} {single}]" argCommandHelpFunction - Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}ref${__HELP_NORMAL} {list} (optional)] - " + + echo " Possible values:" + Array::wrap2 ' ' 76 8 " - ${__OPTION_COLOR}pull:${__RESET_COLOR} Pull" + echo + Array::wrap2 ' ' 76 8 " - ${__OPTION_COLOR}push:${__RESET_COLOR} Push" + echo + + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}ref${__HELP_NORMAL} {list} (optional)]" commandArgsHelpFunction + # ------------------------------------------ # options section # ------------------------------------------ @@ -2396,69 +2405,77 @@ postmanCliCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Displays configuration" echo - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." echo - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" echo - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" echo - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" echo - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log file" echo - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set display level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." echo - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." echo - Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}default${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}default-force${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}noColor${__RESET_COLOR}" Array::wrap2 ' ' 76 6 " Default value: " "default" echo @@ -2467,19 +2484,16 @@ postmanCliCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." echo - echo echo -e "${__HELP_TITLE_COLOR}PUSH/PULL OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--postman-model${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-m ${__HELP_NORMAL} {single}" optionPostmanModelConfigHelpFunction - # ------------------------------------------ # longDescription section # ------------------------------------------ diff --git a/bin/upgradeGithubRelease b/bin/upgradeGithubRelease index 5c62a189..28f6a289 100755 --- a/bin/upgradeGithubRelease +++ b/bin/upgradeGithubRelease @@ -1879,7 +1879,7 @@ upgradeGithubReleaseCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1930,7 +1930,7 @@ upgradeGithubReleaseCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1976,7 +1976,7 @@ upgradeGithubReleaseCommandParse() { return 1 fi if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default, default-force, noColor)" return 1 fi @@ -2104,7 +2104,6 @@ upgradeGithubReleaseCommandParse() { # Argument 1/2 - targetFileArg # Argument targetFileArg min 1 max 1 - # Argument targetFileArg authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && options_parse_parsedArgIndex < maxParsedArgIndex1 )); then if ((options_parse_argParsedCountTargetFileArg >= 1 )); then @@ -2119,7 +2118,6 @@ upgradeGithubReleaseCommandParse() { # Argument 2/2 - githubUrlPatternArg # Argument githubUrlPatternArg min 1 max 1 - # Argument githubUrlPatternArg authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex1 && options_parse_parsedArgIndex < maxParsedArgIndex2 )); then if ((options_parse_argParsedCountGithubUrlPatternArg >= 1 )); then @@ -2208,14 +2206,14 @@ upgradeGithubReleaseCommandHelp() { echo echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}targetFile${__HELP_NORMAL} {single} (mandatory) - " + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}targetFile${__HELP_NORMAL} {single} (mandatory)" Array::wrap2 ' ' 76 4 " " "The binary downloaded will be written to this file path. Ensure the path is writable." - echo - Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}githubUrlPattern${__HELP_NORMAL} {single} (mandatory) - " + echo + + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}githubUrlPattern${__HELP_NORMAL} {single} (mandatory)" githubUrlPatternHelpFunction + # ------------------------------------------ # options section # ------------------------------------------ @@ -2226,69 +2224,77 @@ upgradeGithubReleaseCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Displays configuration" echo - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." echo - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" echo - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" echo - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" echo - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log file" echo - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set display level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." echo - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." echo - Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}default${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}default-force${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}noColor${__RESET_COLOR}" Array::wrap2 ' ' 76 6 " Default value: " "default" echo @@ -2297,19 +2303,16 @@ upgradeGithubReleaseCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." echo - echo echo -e "${__HELP_TITLE_COLOR}VERSION MANAGEMENT:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--version-arg ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "The argument that will be provided to the currently installed binary" "to check the version of the software. See options constraints below." "" echo - Array::wrap2 ' ' 76 6 " Default value: " "--version" echo @@ -2318,12 +2321,10 @@ upgradeGithubReleaseCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--exact-version${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-e ${__HELP_NORMAL} {single}" exactVersionHelpFunction - # ------------------------------------------ # longDescription section # ------------------------------------------ diff --git a/bin/waitForIt b/bin/waitForIt index 3a5dbfc9..e832ee7b 100755 --- a/bin/waitForIt +++ b/bin/waitForIt @@ -1592,7 +1592,7 @@ waitForItCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1643,7 +1643,7 @@ waitForItCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1689,7 +1689,7 @@ waitForItCommandParse() { return 1 fi if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default, default-force, noColor)" return 1 fi @@ -1794,7 +1794,6 @@ waitForItCommandParse() { # Argument 1/3 - hostOrIpArg # Argument hostOrIpArg min 1 max 1 - # Argument hostOrIpArg authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && options_parse_parsedArgIndex < maxParsedArgIndex1 )); then if ((options_parse_argParsedCountHostOrIpArg >= 1 )); then @@ -1808,7 +1807,6 @@ waitForItCommandParse() { # Argument 2/3 - portArg # Argument portArg min 1 max 1 - # Argument portArg authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex1 && options_parse_parsedArgIndex < maxParsedArgIndex2 )); then if ((options_parse_argParsedCountPortArg >= 1 )); then @@ -1823,7 +1821,6 @@ waitForItCommandParse() { # Argument 3/3 - commandArgs # Argument commandArgs min 0 max -1 - # Argument commandArgs authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex2 )); then ((++options_parse_argParsedCountCommandArgs)) # shellcheck disable=SC2034 @@ -1909,18 +1906,18 @@ waitForItCommandHelp() { echo echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}hostOrIp${__HELP_NORMAL} {single} (mandatory) - " + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}hostOrIp${__HELP_NORMAL} {single} (mandatory)" Array::wrap2 ' ' 76 4 " " "Host or IP under test." - echo - Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}port${__HELP_NORMAL} {single} (mandatory) - " + echo + + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}port${__HELP_NORMAL} {single} (mandatory)" Array::wrap2 ' ' 76 4 " " "TCP port under test." - echo - Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}commandArgs${__HELP_NORMAL} {list} (optional)] - " + echo + + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}commandArgs${__HELP_NORMAL} {list} (optional)]" Array::wrap2 ' ' 76 4 " " "Execute command with args after the test finishes or exit with status code if no command provided." - echo + echo + # ------------------------------------------ # options section # ------------------------------------------ @@ -1930,7 +1927,6 @@ waitForItCommandHelp() { Array::wrap2 ' ' 76 4 " " "Timeout in seconds, zero for no timeout." echo - Array::wrap2 ' ' 76 6 " Default value: " "15" echo @@ -1938,7 +1934,6 @@ waitForItCommandHelp() { Array::wrap2 ' ' 76 4 " " "Execute sub-command even if timeout is reached." echo - echo echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" @@ -1946,69 +1941,77 @@ waitForItCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Displays configuration" echo - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." echo - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" echo - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" echo - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" echo - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log file" echo - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set display level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." echo - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." echo - Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}default${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}default-force${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}noColor${__RESET_COLOR}" Array::wrap2 ' ' 76 6 " Default value: " "default" echo @@ -2017,12 +2020,10 @@ waitForItCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." echo - echo echo -e "${__HELP_TITLE_COLOR}OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--algorithm${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}--algo ${__HELP_NORMAL} {single}" @@ -2030,12 +2031,10 @@ waitForItCommandHelp() { - echo -e " ${__HELP_OPTION_COLOR}--user-nc${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Legacy mode using nc command or while loop (uses timeout command by default)." echo - # ------------------------------------------ # longDescription section # ------------------------------------------ diff --git a/bin/waitForMysql b/bin/waitForMysql index afd01c54..d271b2a3 100755 --- a/bin/waitForMysql +++ b/bin/waitForMysql @@ -1541,7 +1541,7 @@ waitForMysqlCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1592,7 +1592,7 @@ waitForMysqlCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1638,7 +1638,7 @@ waitForMysqlCommandParse() { return 1 fi if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default, default-force, noColor)" return 1 fi @@ -1712,7 +1712,6 @@ waitForMysqlCommandParse() { # Argument 1/5 - mysqlHostArg # Argument mysqlHostArg min 1 max 1 - # Argument mysqlHostArg authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex0 && options_parse_parsedArgIndex < maxParsedArgIndex1 )); then if ((options_parse_argParsedCountMysqlHostArg >= 1 )); then @@ -1726,7 +1725,6 @@ waitForMysqlCommandParse() { # Argument 2/5 - mysqlPortArg # Argument mysqlPortArg min 1 max 1 - # Argument mysqlPortArg authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex1 && options_parse_parsedArgIndex < maxParsedArgIndex2 )); then if ((options_parse_argParsedCountMysqlPortArg >= 1 )); then @@ -1741,7 +1739,6 @@ waitForMysqlCommandParse() { # Argument 3/5 - mysqlUserArg # Argument mysqlUserArg min 1 max 1 - # Argument mysqlUserArg authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex2 && options_parse_parsedArgIndex < maxParsedArgIndex3 )); then if ((options_parse_argParsedCountMysqlUserArg >= 1 )); then @@ -1755,7 +1752,6 @@ waitForMysqlCommandParse() { # Argument 4/5 - mysqlPasswordArg # Argument mysqlPasswordArg min 1 max 1 - # Argument mysqlPasswordArg authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex3 && options_parse_parsedArgIndex < maxParsedArgIndex4 )); then if ((options_parse_argParsedCountMysqlPasswordArg >= 1 )); then @@ -1769,7 +1765,6 @@ waitForMysqlCommandParse() { # Argument 5/5 - commandArgs # Argument commandArgs min 0 max -1 - # Argument commandArgs authorizedValues: elif (( options_parse_parsedArgIndex >= minParsedArgIndex4 )); then ((++options_parse_argParsedCountCommandArgs)) # shellcheck disable=SC2034 @@ -1861,26 +1856,26 @@ waitForMysqlCommandHelp() { echo echo -e "${__HELP_TITLE_COLOR}ARGUMENTS:${__RESET_COLOR}" - Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}mysqlHost${__HELP_NORMAL} {single} (mandatory) - " + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}mysqlHost${__HELP_NORMAL} {single} (mandatory)" Array::wrap2 ' ' 76 4 " " "Mysql host name." - echo - Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}mysqlPort${__HELP_NORMAL} {single} (mandatory) - " + echo + + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}mysqlPort${__HELP_NORMAL} {single} (mandatory)" Array::wrap2 ' ' 76 4 " " "Mysql port." - echo - Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}mysqlUser${__HELP_NORMAL} {single} (mandatory) - " + echo + + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}mysqlUser${__HELP_NORMAL} {single} (mandatory)" Array::wrap2 ' ' 76 4 " " "Mysql user name." - echo - Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}mysqlPassword${__HELP_NORMAL} {single} (mandatory) - " + echo + + Array::wrap2 " " 80 2 " ${__HELP_OPTION_COLOR}mysqlPassword${__HELP_NORMAL} {single} (mandatory)" Array::wrap2 ' ' 76 4 " " "Mysql user password." - echo - Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}commandArgs${__HELP_NORMAL} {list} (optional)] - " + echo + + Array::wrap2 " " 80 2 " [${__HELP_OPTION_COLOR}commandArgs${__HELP_NORMAL} {list} (optional)]" Array::wrap2 ' ' 76 4 " " "Execute command with args after the test finishes or exit with status code if no command provided." - echo + echo + # ------------------------------------------ # options section # ------------------------------------------ @@ -1890,7 +1885,6 @@ waitForMysqlCommandHelp() { Array::wrap2 ' ' 76 4 " " "Timeout in seconds, zero for no timeout." echo - Array::wrap2 ' ' 76 6 " Default value: " "15" echo @@ -1898,7 +1892,6 @@ waitForMysqlCommandHelp() { Array::wrap2 ' ' 76 4 " " "Execute sub-command even if timeout is reached." echo - echo echo -e "${__HELP_TITLE_COLOR}GLOBAL OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--help${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-h${__HELP_NORMAL} {single}" @@ -1906,69 +1899,77 @@ waitForMysqlCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Displays configuration" echo - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." echo - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" echo - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" echo - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" echo - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log file" echo - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set display level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." echo - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." echo - Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}default${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}default-force${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}noColor${__RESET_COLOR}" Array::wrap2 ' ' 76 6 " Default value: " "default" echo @@ -1977,12 +1978,10 @@ waitForMysqlCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." echo - # ------------------------------------------ # longDescription section # ------------------------------------------ diff --git a/conf/dbScripts/extractData b/conf/dbScripts/extractData index e8c8111b..6dff9821 100755 --- a/conf/dbScripts/extractData +++ b/conf/dbScripts/extractData @@ -1780,7 +1780,7 @@ extractDataCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1831,7 +1831,7 @@ extractDataCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1877,7 +1877,7 @@ extractDataCommandParse() { return 1 fi if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default, default-force, noColor)" return 1 fi @@ -1985,69 +1985,77 @@ extractDataCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Displays configuration" echo - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." echo - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" echo - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" echo - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" echo - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log file" echo - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set display level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." echo - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." echo - Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}default${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}default-force${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}noColor${__RESET_COLOR}" Array::wrap2 ' ' 76 6 " Default value: " "default" echo @@ -2056,12 +2064,10 @@ extractDataCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." echo - # ------------------------------------------ # version section # ------------------------------------------ diff --git a/install b/install index 884310ae..653a0668 100755 --- a/install +++ b/install @@ -1400,7 +1400,7 @@ installCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1451,7 +1451,7 @@ installCommandParse() { return 1 fi if [[ ! "$1" =~ OFF|ERR|ERROR|WARN|WARNING|INFO|DEBUG|TRACE ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([OFF ERR ERROR WARN WARNING INFO DEBUG TRACE])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE)" return 1 fi @@ -1497,7 +1497,7 @@ installCommandParse() { return 1 fi if [[ ! "$1" =~ default|default-force|noColor ]]; then - Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values([default default-force noColor])" + Log::displayError "Command ${SCRIPT_NAME} - Option ${options_parse_arg} - value '$1' is not part of authorized values(default, default-force, noColor)" return 1 fi @@ -1619,69 +1619,77 @@ installCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--config${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Displays configuration" echo - echo -e " ${__HELP_OPTION_COLOR}--bash-framework-config ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Use alternate bash framework configuration." echo - echo -e " ${__HELP_OPTION_COLOR}--verbose${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-v${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Info level verbose mode (alias of --display-level INFO)" echo - echo -e " ${__HELP_OPTION_COLOR}-vv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Debug level verbose mode (alias of --display-level DEBUG)" echo - echo -e " ${__HELP_OPTION_COLOR}-vvv${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Trace level verbose mode (alias of --display-level TRACE)" echo - echo -e " ${__HELP_OPTION_COLOR}--log-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--log-file ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set log file" echo - echo -e " ${__HELP_OPTION_COLOR}--display-level ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Set display level" echo - Array::wrap2 ' ' 76 6 " Possible values: " "OFF, " "ERR, " "ERROR, " "WARN, " "WARNING, " "INFO, " "DEBUG, " "TRACE" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}OFF${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}ERROR${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARN${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}WARNING${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}INFO${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}DEBUG${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}TRACE${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--no-color${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Produce monochrome output. alias of --theme noColor." echo - echo -e " ${__HELP_OPTION_COLOR}--theme ${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Choose color theme - default-force means colors will be produced even if command is piped." echo - Array::wrap2 ' ' 76 6 " Possible values: " "default, " "default-force, " "noColor" - echo + echo " Possible values: " + echo -e " - ${__OPTION_COLOR}default${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}default-force${__RESET_COLOR}" + echo -e " - ${__OPTION_COLOR}noColor${__RESET_COLOR}" Array::wrap2 ' ' 76 6 " Default value: " "default" echo @@ -1690,19 +1698,16 @@ installCommandHelp() { echo - echo -e " ${__HELP_OPTION_COLOR}--quiet${__HELP_NORMAL}, ${__HELP_OPTION_COLOR}-q${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "Quiet mode, doesn't display any output." echo - echo echo -e "${__HELP_TITLE_COLOR}OPTIONS:${__RESET_COLOR}" echo -e " ${__HELP_OPTION_COLOR}--skip-backup${__HELP_NORMAL} {single}" Array::wrap2 ' ' 76 4 " " "skip backup of ~/bash-tools." echo - # ------------------------------------------ # longDescription section # ------------------------------------------ diff --git a/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt b/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt index 9b759192..88c0be89 100644 --- a/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt +++ b/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.help.txt @@ -9,8 +9,7 @@ [--quiet|-q] [--skin ] ARGUMENTS: - [sqlFile {single}] - Sql filepath to parse (read from stdin if not provided). + [sqlFile {single}] Sql filepath to parse (read from stdin if not provided). GLOBAL OPTIONS: --help, -h {single} @@ -27,18 +26,37 @@ Trace level verbose mode (alias of --display-level TRACE) --log-level  {single} Set log level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --log-file  {single} Set log file --display-level  {single} Set display level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --no-color {single} Produce monochrome output. alias of --theme noColor. --theme  {single} Choose color theme - default-force means colors will be produced even if command is piped. - Possible values: default, default-force, noColor + Possible values: + - default + - default-force + - noColor Default value: default --version {single} Print version information and quit. diff --git a/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.svg b/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.svg index 35dbbd03..6b7ce65c 100644 --- a/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.svg +++ b/src/_binaries/Converters/mysql2puml/testsData/mysql2puml.svg @@ -1 +1 @@ -customer id : int11identifier : varchar128created_at : datetime6updated_at : datetime6learner id : int10 unsignedcustomer_id : int11external_id : varchar255created_at : datetime6updated_at : datetime6learner_attribute id : int11attribute_id : int10 unsignedlearner_id : int10 unsignedcreated_at : datetime6interest : doublelevel : doubleupdated_at : datetime6attribute id : int10 unsignedmapped_attribute_id : int10 unsignedinternal_id : int11created_at : datetime6translations : jsonupdated_at : datetime6product id : int10 unsignedcustomer_id : int11external_id : int10 unsignedcreated_at : datetime6updated_at : datetime6product_attribute id : int11attribute_id : int10 unsignedtraining_course_id : int10 unsignedcreated_at : datetime6relevance : doubleupdated_at : datetime6id0..*1id0..*1id0..*1id0..*1id0..*1id0..*1id0..*1 +customer id : int11identifier : varchar128created_at : datetime6updated_at : datetime6learner id : int10 unsignedcustomer_id : int11external_id : varchar255created_at : datetime6updated_at : datetime6learner_attribute id : int11attribute_id : int10 unsignedlearner_id : int10 unsignedcreated_at : datetime6interest : doublelevel : doubleupdated_at : datetime6attribute id : int10 unsignedmapped_attribute_id : int10 unsignedinternal_id : int11created_at : datetime6translations : jsonupdated_at : datetime6product id : int10 unsignedcustomer_id : int11external_id : int10 unsignedcreated_at : datetime6updated_at : datetime6product_attribute id : int11attribute_id : int10 unsignedtraining_course_id : int10 unsignedcreated_at : datetime6relevance : doubleupdated_at : datetime6id0..*1id0..*1id0..*1id0..*1id0..*1id0..*1id0..*1 diff --git a/src/_binaries/Converters/mysql2puml/testsData/mysql2pumlSkins/default.png b/src/_binaries/Converters/mysql2puml/testsData/mysql2pumlSkins/default.png index c27f2fa4bb2b4c28ce42ae1cc08e0677090d2b08..0205cf6f941d096b7457d65d8d29a314eb34badd 100644 GIT binary patch delta 805 zcmV+=1KRwy2ek)~S^+7MTOxmX>8iDg0JWoKYJrxO(yx;t2UsLd=GaB-*YE6v7S_Sm z6$#n#-97jCd@mP?Vp8caa1KF>Nfd@C6sS2uzy-n>gJ=nS3adyTN(7y7F#}SIwL%F? zuI9i%qT+&q&yzS{D?&;ZT*OSW5d4_Kr&`U)O$lA`NWdc-2UyGt7Gi(N0mzZDmIB;K zz>)-9I07r9)rCd_ijU=kPUn(-C1|x=Cx^mcW09w&;Y2*6z=vrhA(lL3as>-CJZ2J6 z5Z!p^*ek`1IkUOePUs%g98ez6%`$8&t_qha%-#hP+zEW-&@l*QH&&NY`n$ zrtq}47t05X!Us)oU59_Z58*0}kL5;kifWM{ph+5ot_y{NL4q88cDfc~3#4JM;Ysvc zjRHZm8dZBjF(Ew+65IjU+z&VoH10+GJA@7ME{r!eg^&xKWD3IP)9vV(@R}3>KA{(0 z$H|;p8h4{!?+uY`b7auROzd>Nskfg+`jp!ovNOYCba(S@biIF<2h%=}j*}QCO6u?f zl(MpdRFMl?os!SyD$DHRpj(Cx{l}4-mno42ZYSWY53u0*G_i-o%}8I$%}dMAJof$a17n)&HBlfAVHC`Cabbho7eTzdG0jxM92X(CbE+D*oQ*=w+$j zDmF`iJu_}A#G`-n1}Cq)Wp-mWS$OUH-u=T%5td;|W|-v(C);dmpPbgY3WMxu68WYx z7Hzj}w@aptBYLEwVy{}I6clDoRH*q^ZLh|pV$2Wv)Ips&_Yp!=^TU-QA2N=i+~5I(;1f1$1C;GV1r>ncBZ; z^YzL}?c}s_Rva<~LyVzz2KBmEKlkcqFnVl*5tp3e*Fg`GNXrSDnoM1geaX3{CxHh| z^g{~ReT?Gvs=GdbYw^rwB+OuSZlC(E3ZGApzi{JG5)*jpJ6AYmI#6Umk3oahBk!H# jbEn6|OtTdkVWV)`5#h=e$%f7^uIWydHI_6YlZ^u@@~n#u delta 806 zcmV+>1KIqw2et>0S^+ANTOxmfwy2sSK<#LmTA*d6?CWI60TzjqId&2I^*cL(!aCTx zA|X4zyXPLC@8v2{Oe!4)&M{~)iNX+t0yRenxIh?V5G{dEVHN3PiJ%iMWz6Qawz;Y7I|74PQ)_`e3(WOV#z}$SFk|CVB*@Wcr)wd0KpOTMoJ2Nas4|iWjw+DZDFzxf`G>LJdqz*qo zDJv^T6}hn0Dfw)!vdk_Hx@G9le;lcKnG#vxZUVmg01KW^6MIP9jC50OURr)8&)=V* zTNlx-tz@;3TMmsvmLrX-{@?8VlQ)~m?{fD({4~w~)xkc%E!&-kUN^#2@%KJQFH8Ma zu~`D_g>hRU9-V(TICa$4sq46>t1F+b{42X*G$M+i~PBR@$$5*iQ~nOsVo z*Lh$qua%N~OcUge%4^7fBGepmN0WyuPU=^5cQcZltIdCO`Z)dubYN~W>i6J<+P`V@ zXO;8XdA(9E4w-`MSVHX*&g$O9g?C`V-)u{-Sq+7iWe>;VFs&n|E>S3a9Kb7%oPk#5)=5=cW!XXbfCzB9)kw0N8UT9 kpPU{OGtCxegpI;!M}#X^BpW)v0Hq2}hNZ8Hi<684Da|x`3;+NC diff --git a/src/_binaries/Converters/mysql2puml/testsData/mysql2pumlSkins/default.svg b/src/_binaries/Converters/mysql2puml/testsData/mysql2pumlSkins/default.svg index d10d03a9..db2c1b79 100644 --- a/src/_binaries/Converters/mysql2puml/testsData/mysql2pumlSkins/default.svg +++ b/src/_binaries/Converters/mysql2puml/testsData/mysql2pumlSkins/default.svg @@ -1 +1 @@ - + diff --git a/src/_binaries/Database/dbImport/testsData/dbImport.help.txt b/src/_binaries/Database/dbImport/testsData/dbImport.help.txt index 0e8602cc..709ae77b 100644 --- a/src/_binaries/Database/dbImport/testsData/dbImport.help.txt +++ b/src/_binaries/Database/dbImport/testsData/dbImport.help.txt @@ -12,10 +12,8 @@ [--quiet|-q] ARGUMENTS: - fromDbName {single} (mandatory) - The name of the source/remote database. - [ {single}] - The name of the target database + fromDbName {single} (mandatory) The name of the source/remote database. + [ {single}] The name of the target database Default value: (without extension) @@ -63,18 +61,37 @@ Trace level verbose mode (alias of --display-level TRACE) --log-level  {single} Set log level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --log-file  {single} Set log file --display-level  {single} Set display level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --no-color {single} Produce monochrome output. alias of --theme noColor. --theme  {single} Choose color theme - default-force means colors will be produced even if command is piped. - Possible values: default, default-force, noColor + Possible values: + - default + - default-force + - noColor Default value: default --version {single} Print version information and quit. diff --git a/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt b/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt index 0975e6f8..036e1094 100644 --- a/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt +++ b/src/_binaries/Database/dbImportProfile/testsData/dbImportProfile.help.txt @@ -10,8 +10,7 @@ [--quiet|-q] ARGUMENTS: - fromDbName {single} (mandatory) - The name of the source/remote database. + fromDbName {single} (mandatory) The name of the source/remote database. PROFILE OPTIONS: --ratio, -r  {single} @@ -45,18 +44,37 @@ Trace level verbose mode (alias of --display-level TRACE) --log-level  {single} Set log level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --log-file  {single} Set log file --display-level  {single} Set display level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --no-color {single} Produce monochrome output. alias of --theme noColor. --theme  {single} Choose color theme - default-force means colors will be produced even if command is piped. - Possible values: default, default-force, noColor + Possible values: + - default + - default-force + - noColor Default value: default --version {single} Print version information and quit. diff --git a/src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt b/src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt index 6f0e9cf6..7d6d1838 100644 --- a/src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt +++ b/src/_binaries/Database/dbImportStream/testsData/dbImportStream.help.txt @@ -11,10 +11,8 @@ [--quiet|-q] ARGUMENTS: - argDumpFile {single} (mandatory) - The file that will be streamed through mysql. - argTargetDbName {single} (mandatory) - The name of the mysql target database. + argDumpFile {single} (mandatory) The file that will be streamed through mysql. + argTargetDbName {single} (mandatory) The name of the mysql target database. PROFILE OPTIONS: --profile, -p  {single} @@ -46,18 +44,37 @@ Trace level verbose mode (alias of --display-level TRACE) --log-level  {single} Set log level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --log-file  {single} Set log file --display-level  {single} Set display level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --no-color {single} Produce monochrome output. alias of --theme noColor. --theme  {single} Choose color theme - default-force means colors will be produced even if command is piped. - Possible values: default, default-force, noColor + Possible values: + - default + - default-force + - noColor Default value: default --version {single} Print version information and quit. diff --git a/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt b/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt index 70c974d5..a5988178 100644 --- a/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt +++ b/src/_binaries/Database/dbQueryAllDatabases/testsData/dbQueryAllDatabases.help.txt @@ -11,8 +11,7 @@ [--quiet|-q] [--separator|-s ] ARGUMENTS: - argQuery {single} (mandatory) - Query to execute + argQuery {single} (mandatory) Query to execute - , try to execute the mysql query provided by the file - , search for query file in @@ -47,18 +46,37 @@ Trace level verbose mode (alias of --display-level TRACE) --log-level  {single} Set log level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --log-file  {single} Set log file --display-level  {single} Set display level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --no-color {single} Produce monochrome output. alias of --theme noColor. --theme  {single} Choose color theme - default-force means colors will be produced even if command is piped. - Possible values: default, default-force, noColor + Possible values: + - default + - default-force + - noColor Default value: default --version {single} Print version information and quit. diff --git a/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-binary.yaml b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-binary.yaml index 9a9d8b5a..99437851 100644 --- a/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-binary.yaml +++ b/src/_binaries/Database/dbScriptAllDatabases/dbScriptAllDatabases-binary.yaml @@ -3,7 +3,6 @@ extends: - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsFromDsn.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" - - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" @@ -78,9 +77,9 @@ binData: type: String help: If output dir provided, will log each db result to log file. helpValueName: logFormat - authorizedValuesList: - - none - - log + authorizedValues: + - value: none + - value: log defaultValue: none callbacks: - outputDirectoryCallback diff --git a/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt b/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt index 4c699c78..d48cd093 100644 --- a/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt +++ b/src/_binaries/Database/dbScriptAllDatabases/testsData/dbScriptAllDatabases.help.txt @@ -12,10 +12,8 @@ [--log-format|-l ] ARGUMENTS: - scriptToExecute {single} (mandatory) - The script that will be executed on each databases. - [scriptArguments {list} (optional)] - Optional parameters to pass to the script. + scriptToExecute {single} (mandatory) The script that will be executed on each databases. + [scriptArguments {list} (optional)] Optional parameters to pass to the script. JOB OPTIONS: --jobs, -j  {single} @@ -44,18 +42,37 @@ Trace level verbose mode (alias of --display-level TRACE) --log-level  {single} Set log level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --log-file  {single} Set log file --display-level  {single} Set display level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --no-color {single} Produce monochrome output. alias of --theme noColor. --theme  {single} Choose color theme - default-force means colors will be produced even if command is piped. - Possible values: default, default-force, noColor + Possible values: + - default + - default-force + - noColor Default value: default --version {single} Print version information and quit. @@ -70,7 +87,9 @@ Output directory, see log-format option. --log-format, -l  {single} If output dir provided, will log each db result to log file. - Possible values: none, log + Possible values: + - none + - log Default value: none diff --git a/src/_binaries/Docker/cli/testsData/cli.help.txt b/src/_binaries/Docker/cli/testsData/cli.help.txt index aced4feb..edb7108c 100644 --- a/src/_binaries/Docker/cli/testsData/cli.help.txt +++ b/src/_binaries/Docker/cli/testsData/cli.help.txt @@ -9,23 +9,20 @@ [--quiet|-q] ARGUMENTS: - [container {single}] - Container should be the name of a profile from profile list, + [container {single}] Container should be the name of a profile from profile list, check containers list below. If no value provided, it will load the container specified in default configuration. Default: project-apache2 - [user {single}] - user to connect on this container + [user {single}] user to connect on this container If no value provided, it will load the user specified in default configuration. Default: www-data - [command {single}] - The command to execute + [command {single}] The command to execute If no value provided, it will load the command specified in default configuration. @@ -47,18 +44,37 @@ Trace level verbose mode (alias of --display-level TRACE) --log-level  {single} Set log level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --log-file  {single} Set log file --display-level  {single} Set display level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --no-color {single} Produce monochrome output. alias of --theme noColor. --theme  {single} Choose color theme - default-force means colors will be produced even if command is piped. - Possible values: default, default-force, noColor + Possible values: + - default + - default-force + - noColor Default value: default --version {single} Print version information and quit. diff --git a/src/_binaries/Git/gitIsAncestorOf/testsData/gitIsAncestorOf.help.txt b/src/_binaries/Git/gitIsAncestorOf/testsData/gitIsAncestorOf.help.txt index b7a62a3e..d76b4354 100644 --- a/src/_binaries/Git/gitIsAncestorOf/testsData/gitIsAncestorOf.help.txt +++ b/src/_binaries/Git/gitIsAncestorOf/testsData/gitIsAncestorOf.help.txt @@ -9,10 +9,8 @@ [--quiet|-q] ARGUMENTS: - claimedBranch {single} (mandatory) - The branch in which the ref will be searched. - ref {single} (mandatory) - The ref to check. + claimedBranch {single} (mandatory) The branch in which the ref will be searched. + ref {single} (mandatory) The ref to check. GLOBAL OPTIONS: --help, -h {single} @@ -29,18 +27,37 @@ Trace level verbose mode (alias of --display-level TRACE) --log-level  {single} Set log level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --log-file  {single} Set log file --display-level  {single} Set display level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --no-color {single} Produce monochrome output. alias of --theme noColor. --theme  {single} Choose color theme - default-force means colors will be produced even if command is piped. - Possible values: default, default-force, noColor + Possible values: + - default + - default-force + - noColor Default value: default --version {single} Print version information and quit. diff --git a/src/_binaries/Git/gitIsBranch/testsData/gitIsBranch.help.txt b/src/_binaries/Git/gitIsBranch/testsData/gitIsBranch.help.txt index 1d03b7b8..df54d8f5 100644 --- a/src/_binaries/Git/gitIsBranch/testsData/gitIsBranch.help.txt +++ b/src/_binaries/Git/gitIsBranch/testsData/gitIsBranch.help.txt @@ -9,8 +9,7 @@ [--quiet|-q] ARGUMENTS: - branchName {single} (mandatory) - The branch name to check. + branchName {single} (mandatory) The branch name to check. GLOBAL OPTIONS: --help, -h {single} @@ -27,18 +26,37 @@ Trace level verbose mode (alias of --display-level TRACE) --log-level  {single} Set log level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --log-file  {single} Set log file --display-level  {single} Set display level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --no-color {single} Produce monochrome output. alias of --theme noColor. --theme  {single} Choose color theme - default-force means colors will be produced even if command is piped. - Possible values: default, default-force, noColor + Possible values: + - default + - default-force + - noColor Default value: default --version {single} Print version information and quit. diff --git a/src/_binaries/Git/gitRenameBranch/testsData/gitRenameBranch.help.txt b/src/_binaries/Git/gitRenameBranch/testsData/gitRenameBranch.help.txt index 23d53f7a..96659d93 100644 --- a/src/_binaries/Git/gitRenameBranch/testsData/gitRenameBranch.help.txt +++ b/src/_binaries/Git/gitRenameBranch/testsData/gitRenameBranch.help.txt @@ -9,10 +9,8 @@ [--quiet|-q] [--assume-yes|--yes|-y] [--push|-p] [--delete|-d] ARGUMENTS: - newBranchName {single} (mandatory) - The new branch name. - [oldBranchName {single}] - The name of the old branch if not current one. + newBranchName {single} (mandatory) The new branch name. + [oldBranchName {single}] The name of the old branch if not current one. GLOBAL OPTIONS: --help, -h {single} @@ -29,18 +27,37 @@ Trace level verbose mode (alias of --display-level TRACE) --log-level  {single} Set log level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --log-file  {single} Set log file --display-level  {single} Set display level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --no-color {single} Produce monochrome output. alias of --theme noColor. --theme  {single} Choose color theme - default-force means colors will be produced even if command is piped. - Possible values: default, default-force, noColor + Possible values: + - default + - default-force + - noColor Default value: default --version {single} Print version information and quit. diff --git a/src/_binaries/Github/upgradeGithubRelease/testsData/upgradeGithubRelease.help.txt b/src/_binaries/Github/upgradeGithubRelease/testsData/upgradeGithubRelease.help.txt index 38a220e6..d53dbad4 100644 --- a/src/_binaries/Github/upgradeGithubRelease/testsData/upgradeGithubRelease.help.txt +++ b/src/_binaries/Github/upgradeGithubRelease/testsData/upgradeGithubRelease.help.txt @@ -10,11 +10,9 @@ [--current-version|-c ] [--exact-version|-e ] ARGUMENTS: - targetFile {single} (mandatory) - The binary downloaded will be written to this file path. Ensure the path + targetFile {single} (mandatory) The binary downloaded will be written to this file path. Ensure the path is writable. - githubUrlPattern {single} (mandatory) - The url pattern to use to download the binary, see examples below. + githubUrlPattern {single} (mandatory) The url pattern to use to download the binary, see examples below. @version@ is template variable that will be replaced by the latest version tag found on github. @@ -33,18 +31,37 @@ Trace level verbose mode (alias of --display-level TRACE) --log-level  {single} Set log level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --log-file  {single} Set log file --display-level  {single} Set display level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --no-color {single} Produce monochrome output. alias of --theme noColor. --theme  {single} Choose color theme - default-force means colors will be produced even if command is piped. - Possible values: default, default-force, noColor + Possible values: + - default + - default-force + - noColor Default value: default --version {single} Print version information and quit. diff --git a/src/_binaries/Postman/postmanCli/testsData/postmanCli.help.txt b/src/_binaries/Postman/postmanCli/testsData/postmanCli.help.txt index 21ba2db6..2ea3f858 100644 --- a/src/_binaries/Postman/postmanCli/testsData/postmanCli.help.txt +++ b/src/_binaries/Postman/postmanCli/testsData/postmanCli.help.txt @@ -9,13 +9,14 @@ [--quiet|-q] [--postman-model|-m ] ARGUMENTS: - [command {single}] - pull + [command {single}] pull Pull collections from Postman back to repositories. push Push repositories collections to Postman. - [ref {list} (optional)] - List of postman collection's references to pull or push + Possible values: + - pull: Pull + - push: Push + [ref {list} (optional)] List of postman collection's references to pull or push or no argument to pull or push all the collections GLOBAL OPTIONS: @@ -33,18 +34,37 @@ Trace level verbose mode (alias of --display-level TRACE) --log-level  {single} Set log level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --log-file  {single} Set log file --display-level  {single} Set display level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --no-color {single} Produce monochrome output. alias of --theme noColor. --theme  {single} Choose color theme - default-force means colors will be produced even if command is piped. - Possible values: default, default-force, noColor + Possible values: + - default + - default-force + - noColor Default value: default --version {single} Print version information and quit. diff --git a/src/_binaries/Utils/waitForIt/testsData/waitForIt.help.txt b/src/_binaries/Utils/waitForIt/testsData/waitForIt.help.txt index 7a5d30b7..e576098b 100644 --- a/src/_binaries/Utils/waitForIt/testsData/waitForIt.help.txt +++ b/src/_binaries/Utils/waitForIt/testsData/waitForIt.help.txt @@ -10,12 +10,9 @@ [--quiet|-q] [--algorithm|--algo ] [--user-nc] ARGUMENTS: - hostOrIp {single} (mandatory) - Host or IP under test. - port {single} (mandatory) - TCP port under test. - [commandArgs {list} (optional)] - Execute command with args after the test finishes or exit with status co + hostOrIp {single} (mandatory) Host or IP under test. + port {single} (mandatory) TCP port under test. + [commandArgs {list} (optional)] Execute command with args after the test finishes or exit with status co de if no command provided. TIMEOUT OPTIONS: @@ -40,18 +37,37 @@ Trace level verbose mode (alias of --display-level TRACE) --log-level  {single} Set log level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --log-file  {single} Set log file --display-level  {single} Set display level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --no-color {single} Produce monochrome output. alias of --theme noColor. --theme  {single} Choose color theme - default-force means colors will be produced even if command is piped. - Possible values: default, default-force, noColor + Possible values: + - default + - default-force + - noColor Default value: default --version {single} Print version information and quit. diff --git a/src/_binaries/Utils/waitForIt/waitForIt-binary.yaml b/src/_binaries/Utils/waitForIt/waitForIt-binary.yaml index 82975ce6..82717c03 100644 --- a/src/_binaries/Utils/waitForIt/waitForIt-binary.yaml +++ b/src/_binaries/Utils/waitForIt/waitForIt-binary.yaml @@ -2,7 +2,6 @@ extends: - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsTimeout.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsVersion.yaml" - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" - - "${BASH_TOOLS_ROOT_DIR}/src/_binaries/commandDefinitions/optionsDefault.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/defaultCommand.yaml" - "${FRAMEWORK_ROOT_DIR}/src/_binaries/commandDefinitions/frameworkConfig.yaml" diff --git a/src/_binaries/Utils/waitForMysql/testsData/waitForMysql.help.txt b/src/_binaries/Utils/waitForMysql/testsData/waitForMysql.help.txt index 88377118..c74ce7c5 100644 --- a/src/_binaries/Utils/waitForMysql/testsData/waitForMysql.help.txt +++ b/src/_binaries/Utils/waitForMysql/testsData/waitForMysql.help.txt @@ -10,16 +10,11 @@ [--quiet|-q] ARGUMENTS: - mysqlHost {single} (mandatory) - Mysql host name. - mysqlPort {single} (mandatory) - Mysql port. - mysqlUser {single} (mandatory) - Mysql user name. - mysqlPassword {single} (mandatory) - Mysql user password. - [commandArgs {list} (optional)] - Execute command with args after the test finishes or exit with status co + mysqlHost {single} (mandatory) Mysql host name. + mysqlPort {single} (mandatory) Mysql port. + mysqlUser {single} (mandatory) Mysql user name. + mysqlPassword {single} (mandatory) Mysql user password. + [commandArgs {list} (optional)] Execute command with args after the test finishes or exit with status co de if no command provided. TIMEOUT OPTIONS: @@ -44,18 +39,37 @@ Trace level verbose mode (alias of --display-level TRACE) --log-level  {single} Set log level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --log-file  {single} Set log file --display-level  {single} Set display level - Possible values: OFF, ERR, ERROR, WARN, WARNING, INFO, DEBUG, TRACE + Possible values: + - OFF + - ERR + - ERROR + - WARN + - WARNING + - INFO + - DEBUG + - TRACE --no-color {single} Produce monochrome output. alias of --theme noColor. --theme  {single} Choose color theme - default-force means colors will be produced even if command is piped. - Possible values: default, default-force, noColor + Possible values: + - default + - default-force + - noColor Default value: default --version {single} Print version information and quit. From 82c8c151e6243337ad8aeb4e3f3c032406a79a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Sun, 15 Sep 2024 19:58:18 +0200 Subject: [PATCH 22/24] pre-commit btf 5.1.3, bash-compiler 0.3.6 --- .pre-commit-config-github.yaml | 4 ++-- .pre-commit-config.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config-github.yaml b/.pre-commit-config-github.yaml index 9539ab7b..35757e87 100644 --- a/.pre-commit-config-github.yaml +++ b/.pre-commit-config-github.yaml @@ -161,7 +161,7 @@ repos: exclude: /testsData/ - repo: https://github.com/fchastanet/bash-tools-framework - rev: 4.0.4 + rev: v5.1.3 hooks: - id: fixShebangExecutionBit - id: awkLint @@ -210,6 +210,6 @@ repos: stages: [manual] # GITHUB - repo: https://github.com/fchastanet/bash-compiler - rev: 0.3.4 + rev: v0.3.6 hooks: - id: buildBashBinaries diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 29ce7dd3..308356b7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -156,7 +156,7 @@ repos: exclude: /testsData/ - repo: https://github.com/fchastanet/bash-tools-framework - rev: 4.0.4 + rev: v5.1.3 hooks: - id: fixShebangExecutionBit - id: awkLint @@ -205,6 +205,6 @@ repos: stages: [] # GITHUB - repo: https://github.com/fchastanet/bash-compiler - rev: 0.3.4 + rev: v0.3.6 hooks: - id: buildBashBinaries From 35e547ee7f8fefcbc2f3f87e9d32addde638caf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Mon, 16 Sep 2024 19:30:41 +0200 Subject: [PATCH 23/24] github pipeline use megalinter v8 --- .github/workflows/lint-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint-test.yml b/.github/workflows/lint-test.yml index 9ea25ae9..8244345b 100644 --- a/.github/workflows/lint-test.yml +++ b/.github/workflows/lint-test.yml @@ -122,7 +122,7 @@ jobs: if: ${{ always() }} # You can override MegaLinter flavor used to have faster performances # More info at https://megalinter.io/flavors/ - uses: oxsecurity/megalinter/flavors/terraform@v7 + uses: oxsecurity/megalinter/flavors/terraform@v8 # All available variables are described in documentation # https://megalinter.io/configuration/ env: From a76c8a67be3a5e6e3a27f0e5f8151cdf8409890d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chastanet?= Date: Sat, 21 Sep 2024 23:54:36 +0200 Subject: [PATCH 24/24] use bash-compiler v1.0.0 --- .bash-compiler | 4 ++-- .pre-commit-config-github.yaml | 4 ++-- .pre-commit-config.yaml | 4 ++-- .v8rrc.yaml | 4 ++++ bin/dbImport | 4 ++-- bin/dbImportStream | 4 ++-- bin/dbQueryAllDatabases | 4 ++-- bin/doc | 4 ++-- bin/mysql2puml | 4 ++-- 9 files changed, 20 insertions(+), 16 deletions(-) diff --git a/.bash-compiler b/.bash-compiler index 1124bd26..20a6e524 100644 --- a/.bash-compiler +++ b/.bash-compiler @@ -1,2 +1,2 @@ -FRAMEWORK_ROOT_DIR=${FRAMEWORK_ROOT_DIR:-vendor/bash-tools-framework} -BASH_TOOLS_ROOT_DIR=${BASH_TOOLS_ROOT_DIR:-.} +FRAMEWORK_ROOT_DIR=${ROOT_DIR}/vendor/bash-tools-framework +BASH_TOOLS_ROOT_DIR=${ROOT_DIR} diff --git a/.pre-commit-config-github.yaml b/.pre-commit-config-github.yaml index 35757e87..22c1ff7d 100644 --- a/.pre-commit-config-github.yaml +++ b/.pre-commit-config-github.yaml @@ -161,7 +161,7 @@ repos: exclude: /testsData/ - repo: https://github.com/fchastanet/bash-tools-framework - rev: v5.1.3 + rev: 6.0.0 hooks: - id: fixShebangExecutionBit - id: awkLint @@ -210,6 +210,6 @@ repos: stages: [manual] # GITHUB - repo: https://github.com/fchastanet/bash-compiler - rev: v0.3.6 + rev: v1.0.0 hooks: - id: buildBashBinaries diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 308356b7..93ae6e5b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -156,7 +156,7 @@ repos: exclude: /testsData/ - repo: https://github.com/fchastanet/bash-tools-framework - rev: v5.1.3 + rev: 6.0.0 hooks: - id: fixShebangExecutionBit - id: awkLint @@ -205,6 +205,6 @@ repos: stages: [] # GITHUB - repo: https://github.com/fchastanet/bash-compiler - rev: v0.3.6 + rev: v1.0.0 hooks: - id: buildBashBinaries diff --git a/.v8rrc.yaml b/.v8rrc.yaml index d3728c3d..891d72eb 100644 --- a/.v8rrc.yaml +++ b/.v8rrc.yaml @@ -24,3 +24,7 @@ customCatalog: description: pre-commit fileMatch: [".pre-commit-config*.yml", ".pre-commit-config*.yaml"] location: https://json.schemastore.org/pre-commit-config.json + - name: eslint-json + description: eslint-json + fileMatch: [".eslintrc-json.json"] + location: https://json.schemastore.org/eslintrc.json diff --git a/bin/dbImport b/bin/dbImport index 7805b7f4..8492dee2 100755 --- a/bin/dbImport +++ b/bin/dbImport @@ -2803,11 +2803,11 @@ main() { Linux::requireTarCommand Compiler::Embed::extractFileFromBase64 \ - "${PERSISTENT_TMPDIR:-/tmp}/95e2e89e049fcfcd74a8b75390f067bf/dumpSizeQuery" \ + "${PERSISTENT_TMPDIR:-/tmp}/2e26e9149612ae897e866701fc461bca4f7dc43993209e69077db41f10c2a0a6/dumpSizeQuery" \ "U0VMRUNUIFJPVU5EKFNVTShkYXRhX2xlbmd0aCArIGluZGV4X2xlbmd0aCkgLyAxMDI0IC8gMTAyNCwgMCkgQVMgc2l6ZQpGUk9NIGluZm9ybWF0aW9uX3NjaGVtYS5UQUJMRVMgV0hFUkUgdGFibGVfc2NoZW1hPScke2Zyb21EYk5hbWV9JwpBTkQgdGFibGVfbmFtZSBJTigke2xpc3RUYWJsZXNEdW1wU2l6ZX0sICdkdW1teScpCkdST1VQIEJZIHRhYmxlX3NjaGVtYQo=" \ "644" -declare -gx embed_file_dumpSizeQuery="${PERSISTENT_TMPDIR:-/tmp}/95e2e89e049fcfcd74a8b75390f067bf/dumpSizeQuery" +declare -gx embed_file_dumpSizeQuery="${PERSISTENT_TMPDIR:-/tmp}/2e26e9149612ae897e866701fc461bca4f7dc43993209e69077db41f10c2a0a6/dumpSizeQuery" # dump header/footer diff --git a/bin/dbImportStream b/bin/dbImportStream index e3a49d87..3783491a 100755 --- a/bin/dbImportStream +++ b/bin/dbImportStream @@ -2547,11 +2547,11 @@ main() { Linux::requireTarCommand Compiler::Embed::extractFileFromBase64 \ - "${PERSISTENT_TMPDIR:-/tmp}/6565a8a43f2126f7367983c8747b544f/dbImportStreamScript" \ + "${PERSISTENT_TMPDIR:-/tmp}/1a25e8fe87d1af27f299ca9f5af14fe2fad803ddce20d659e5f6784c6056c9cc/dbImportStreamScript" \ "QkVHSU57CiAgd3JpdGU9MQp9CnsKICBidWZmZXIgPSBzdWJzdHIoJDAsIDEsIDE1MCkKICBsaW5lID0gJDAKICBpZihtYXRjaChidWZmZXIsIC9eTE9DSyBUQUJMRVMgYChbXmBdKylgIFdSSVRFOyQvLCBhcnIpICE9IDApIHsKICAgICMgY2hlY2sgaWYgaW5zZXJ0cyBhcmUgcGFydCBvZiB0aGUgcHJvZmlsZQogICAgdGFibGVOYW1lPWFyclsxXQogICAgaWYgKCEgKHRhYmxlTmFtZSBpbiBtYXApKSB7CiAgICAgIHByb2ZpbGVDbWQgPSAiZWNobyAnIiB0YWJsZU5hbWUgIicgfCAiIFBST0ZJTEVfQ09NTUFORCAiIHwgZ3JlcCAtcSAiIHRhYmxlTmFtZQogICAgICBtYXBbdGFibGVOYW1lXSA9IChzeXN0ZW0ocHJvZmlsZUNtZCkgPT0gMCkKICAgIH0KICAgIGlmIChtYXBbdGFibGVOYW1lXSkgewogICAgICBwcmludCAiXDAzM1s0NG0iICJiZWdpbiBpbnNlcnQgIiB0YWJsZU5hbWUgIlwwMzNbMG0iICA+ICIvZGV2L3N0ZGVyciIKICAgICAgbGluZSA9IGxpbmUgIlxuVFJVTkNBVEUgVEFCTEUgYCIgdGFibGVOYW1lICJgOyIKICAgICAgd3JpdGU9MQogICAgfSBlbHNlIHsKICAgICAgcHJpbnQgImlnbm9yZSB0YWJsZSAiIHRhYmxlTmFtZSAgPiAiL2Rldi9zdGRlcnIiCiAgICAgIHdyaXRlPTAKICAgIH0KICB9IGVsc2UgaWYobWF0Y2goYnVmZmVyLCAvXmNvbW1pdDskLywgYXJyKSAhPSAwKSB7CiAgICB3cml0ZT0xCiAgfSBlbHNlIGlmKG1hdGNoKGJ1ZmZlciwgL1NFVCBOQU1FUyAoW14gXSspLywgYXJyKSAhPSAwKSB7CiAgICBpZiAoQ0hBUkFDVEVSX1NFVCAhPSAiIikgewogICAgICBzdWIoL1NFVCBOQU1FUyAoW14gXSspLywgIlNFVCBOQU1FUyAiIENIQVJBQ1RFUl9TRVQsIGxpbmUpCiAgICB9CiAgICB3cml0ZT0xCiAgfSBlbHNlIGlmKG1hdGNoKGJ1ZmZlciwgL1NFVCBjaGFyYWN0ZXJfc2V0X2NsaWVudCA9IChbXiBdKykvLCBhcnIpICE9IDAgJiYgc3Vic3RyKGFyclsxXSwgMCwgMSkgIT0gIkAiKSB7CiAgICBpZiAoQ0hBUkFDVEVSX1NFVCAhPSAiIikgewogICAgICBzdWIoL1NFVCBjaGFyYWN0ZXJfc2V0X2NsaWVudCA9IChbXiBdKykvLCAiU0VUIGNoYXJhY3Rlcl9zZXRfY2xpZW50ID0gIiBDSEFSQUNURVJfU0VULCBsaW5lKQogICAgfQogICAgd3JpdGU9MQogIH0KCgogIGlmICh3cml0ZSA9PSAxKSB7CiAgICBwcmludCBsaW5lCiAgfQp9Cg==" \ "644" -declare -gx embed_file_dbImportStreamScript="${PERSISTENT_TMPDIR:-/tmp}/6565a8a43f2126f7367983c8747b544f/dbImportStreamScript" +declare -gx embed_file_dbImportStreamScript="${PERSISTENT_TMPDIR:-/tmp}/1a25e8fe87d1af27f299ca9f5af14fe2fad803ddce20d659e5f6784c6056c9cc/dbImportStreamScript" # create db instances diff --git a/bin/dbQueryAllDatabases b/bin/dbQueryAllDatabases index e2b1af68..8c83cfbb 100755 --- a/bin/dbQueryAllDatabases +++ b/bin/dbQueryAllDatabases @@ -2553,11 +2553,11 @@ main() { Linux::requireTarCommand Compiler::Embed::extractFileFromBase64 \ - "${PERSISTENT_TMPDIR:-/tmp}/f256a4bbcf47bbd258e277f86718408c/dbQueryAllDatabasesScript" \ + "${PERSISTENT_TMPDIR:-/tmp}/101ee8873657e2b35014acccb93127462c5f709e4e72f74dfa2c94a05be94876/dbQueryAllDatabasesScript" \ "QkVHSU57CiAgaGVhZGVyUHJpbnRlZD0wCn0KL14kLyB7bmV4dH0KewogIGJ1ZmZlciA9IHN1YnN0cigkMCwgMCwgMzUpCiAgbGluZSA9ICQwCiAgaWYoYnVmZmVyID09ICJAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQCIpIHsKICAgIGlmIChoZWFkZXJQcmludGVkID09IDApIHsKICAgICAgbGluZT1zdWJzdHIobGluZSwgMzYpCiAgICAgIGhlYWRlclByaW50ZWQ9MQogICAgfSBlbHNlIHsKICAgICAgbmV4dAogICAgfQogIH0KICBwcmludCBsaW5lCgp9Cg==" \ "644" -declare -gx embed_file_dbQueryAllDatabasesScript="${PERSISTENT_TMPDIR:-/tmp}/f256a4bbcf47bbd258e277f86718408c/dbQueryAllDatabasesScript" +declare -gx embed_file_dbQueryAllDatabasesScript="${PERSISTENT_TMPDIR:-/tmp}/101ee8873657e2b35014acccb93127462c5f709e4e72f74dfa2c94a05be94876/dbQueryAllDatabasesScript" # later on, parallel calls this script(inception) diff --git a/bin/doc b/bin/doc index 838ff28d..7da3dec8 100755 --- a/bin/doc +++ b/bin/doc @@ -1008,11 +1008,11 @@ ShellDoc::fixMarkdownToc() { Linux::requireTarCommand Compiler::Embed::extractFileFromBase64 \ - "${PERSISTENT_TMPDIR:-/tmp}/647e7865a1d35cf1435bb0aa74a5cdbe/fixMarkdownTocScript" \ + "${PERSISTENT_TMPDIR:-/tmp}/0b6e60db87bd75970a68b765368a17462e0b2c2a402df35f1598f3a6f3f42f40/fixMarkdownTocScript" \ "ewogIGxpbmU9JDAKICBpZiAobWF0Y2gobGluZSwgL14oXHMqLSBcWyhbMC05XStcLikrIFteXV0rXF1cKCMpKFteKV0rKVwpLywgYXJyKSkgewogICAgcHJpbnQgYXJyWzFdICJfIiByZXdyaXRlKGFyclszXSkgIikiCiAgfSBlbHNlIHsKICAgIHByaW50IGxpbmUKICB9Cn0KZnVuY3Rpb24gcmV3cml0ZShzdHIpCnsKICAgIGdzdWIoLy0oLSkrLywgIi0iLCBzdHIpCiAgICByZXR1cm4gc3RyCn0K" \ "644" -declare -gx embed_file_fixMarkdownTocScript="${PERSISTENT_TMPDIR:-/tmp}/647e7865a1d35cf1435bb0aa74a5cdbe/fixMarkdownTocScript" +declare -gx embed_file_fixMarkdownTocScript="${PERSISTENT_TMPDIR:-/tmp}/0b6e60db87bd75970a68b765368a17462e0b2c2a402df35f1598f3a6f3f42f40/fixMarkdownTocScript" # shellcheck disable=SC2154 awk -i inplace -f "${embed_file_fixMarkdownTocScript}" "${file}" diff --git a/bin/mysql2puml b/bin/mysql2puml index fcec3383..4fe80457 100755 --- a/bin/mysql2puml +++ b/bin/mysql2puml @@ -1974,11 +1974,11 @@ main() { Linux::requireTarCommand Compiler::Embed::extractFileFromBase64 \ - "${PERSISTENT_TMPDIR:-/tmp}/abbc557ccf07f1917f739b75af2fba89/mysql2pumlScript" \ + "${PERSISTENT_TMPDIR:-/tmp}/8ab6652e563ee45005c17d4a7f70a813cabae8c73bae3a83f86f63e46464f90c/mysql2pumlScript" \ "IyEvYmluL2F3ayAtZgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIwojIG15c3FsMnB1bWwgLSBTUUwgRERMIHRvIG1hcmtkb3duIGNvbnZlcnRlcgojCiMgSW52b2NhdGlvbi9FeGVjdXRpb246CiMgICBhd2sgLWYgbXlzcWwycHVtbC5hd2sgc2tpbkZpbGUgPCBpbnB1dEZpbGUgPiBvdXRwdXRGaWxlCiMgICBERUJVRz0xIGF3ayAtZiBteXNxbDJwdW1sLmF3ayBza2luRmlsZSA8IGlucHV0RmlsZSA+IG91dHB1dEZpbGUKIwojIFN1cHBvcnRzCiMgICBQbGFudFVNTCBmb3JtYXQKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CgpmdW5jdGlvbiBkZWJ1ZyhmaXJzdCwgc2Vjb25kLCB0aGlyZCwgZm91cnRoLCBmaWZ0aCwgc2l4dGgsIHNldmVudGgsIGVpZ2h0aCwgbmludGgsIHRlbnRoLCBlbGV2ZW50aCwgdHdlbGZ0aCkgewogICAgaWYgKERFQlVHID09IDEpIHByaW50KCJERUJVRyAtICIsIGZpcnN0LCBzZWNvbmQsIHRoaXJkLCBmb3VydGgsIGZpZnRoLCBzaXh0aCwgc2V2ZW50aCwgZWlnaHRoLCBuaW50aCwgdGVudGgsIGVsZXZlbnRoLCB0d2VsZnRoKSA+ICIvZGV2L3N0ZGVyciIKfQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CgpmdW5jdGlvbiB1bWxfc3RhcnQoKQp7CiAgICBpZiAoIXNraW5GaWxlKSB7CiAgICAgICAgcHJpbnQoInNraW5GaWxlIHBhcmFtZXRlciBtaXNzaW5nIikgPiAiL2Rldi9zdGRlcnIiCiAgICAgICAgZXhpdCAxCiAgICB9CiAgICB3aGlsZSAoKGdldGxpbmUgdG1wIDwgc2tpbkZpbGUpID4gMCkgewogICAgICAgIGlmIChtYXRjaCh0bXAsIC9AZW5kdW1sLykpIHsKICAgICAgICAgICAgYnJlYWsKICAgICAgICB9CiAgICAgICAgcHJpbnQodG1wKQogICAgfQogICAgcHJpbnQgIicgZW50aXRpZXMiCn0KCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKZnVuY3Rpb24gdW1sX2VuZCgpCnsKICAgIHByaW50ICJAZW5kdW1sIgp9CgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KCmZ1bmN0aW9uIGx0cmltKHMpIHsgc3ViKC9eWyBcdFxyXG5dKy8sICIiLCBzKTsgcmV0dXJuIHMgfQpmdW5jdGlvbiBydHJpbShzKSB7IHN1YigvWyBcdFxyXG5dKyQvLCAiIiwgcyk7IHJldHVybiBzIH0KZnVuY3Rpb24gdHJpbShzKSB7IHJldHVybiBydHJpbShsdHJpbShzKSk7IH0KCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKZnVuY3Rpb24gY29sdW1uX3dlaWdodChjb2x1bW4pCnsKICAgIHJldHVybiAoY29sdW1uc0RldGFpbHNbY29sdW1uICIscGsiXSA9PSAiMSIgPyA4IDogMCkgXAogICAgICAgICsgKGNvbHVtbnNEZXRhaWxzW2NvbHVtbiAiLGZrIl0gPT0gIjEiID8gNCA6IDApIFwKICAgICAgICArIChjb2x1bW5zRGV0YWlsc1tjb2x1bW4gIix1bmlxdWUiXSA9PSAiMSIgPyAyIDogMCkgXAogICAgICAgICsgKGNvbHVtbnNEZXRhaWxzW2NvbHVtbiAiLG51bGwiXSA9PSAiMCIgPyAxIDogMCkKfQoKZnVuY3Rpb24gY29sdW1uX3NvcnQoaTEsIGNvbHVtbjEsIGkyLCBjb2x1bW4yKQp7CiAgICB3ZWlnaHQxID0gY29sdW1uX3dlaWdodChjb2x1bW4xKQogICAgd2VpZ2h0MiA9IGNvbHVtbl93ZWlnaHQoY29sdW1uMikKICAgIGlmICh3ZWlnaHQxID09IHdlaWdodDIpIHsKICAgICAgICBsID0gdG9sb3dlcihjb2x1bW4xKQogICAgICAgIHIgPSB0b2xvd2VyKGNvbHVtbjIpCgogICAgICAgIGlmIChsIDwgcikgewogICAgICAgICAgICByZXR1cm4gLTEKICAgICAgICB9IGVsc2UgaWYgKGwgPT0gcikgewogICAgICAgICAgICByZXR1cm4gMAogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIHJldHVybiAxCiAgICAgICAgfQogICAgfSBlbHNlIHsKICAgICAgICByZXR1cm4gd2VpZ2h0MiAtIHdlaWdodDEKICAgIH0KfQoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CgpmdW5jdGlvbiB1bWxfdGFibGUoY3JlYXRlVGFibGUpCiMgRERMIHRvIHBsYW50dW1sCiMgQ1JFQVRFIFRBQkxFIGBjb3JlX2N1c3RvbWVyYCAoYGlkYCBpbnQoMTEpIE5PVCBOVUxMIEFVVE9fSU5DUkVNRU5ULCBgaW5zdGFuY2VfbmFtZWAgdmFyY2hhcigxMjgpIE5PVCBOVUxMLCBQUklNQVJZIEtFWSAoYGlkYCksIFVOSVFVRSBLRVkgYGluc3RhbmNlX25hbWVgIChgaW5zdGFuY2VfbmFtZWApICkgRU5HSU5FPUlubm9EQiBBVVRPX0lOQ1JFTUVOVD01IERFRkFVTFQgQ0hBUlNFVD11dGY4OwojIENSRUFURSBUQUJMRSBgY29yZV9sZWFybmVyX3NraWxsYCAoYGlkYCBpbnQoMTEpIE5PVCBOVUxMIEFVVE9fSU5DUkVNRU5ULCBgY3VzdG9tZXJfaWRgIGludCgxMCkgdW5zaWduZWQgTk9UIE5VTEwsIFBSSU1BUlkgS0VZIChgaWRgKSwgS0VZIGBjdXN0b21lcl9pZF82ODRmOTA0Zl9ma19jb3JlX2xlYXJuZXJfaWRgIChgY3VzdG9tZXJfaWRgKSwgQ09OU1RSQUlOVCBgY3VzdG9tZXJfaWRfNjg0ZjkwNGZfZmtfY29yZV9jdXN0b21lcl9pZGAgRk9SRUlHTiBLRVkgKGBjdXN0b21lcl9pZGApIFJFRkVSRU5DRVMgYGNvcmVfY3VzdG9tZXJgIChgaWRgKSkgRU5HSU5FPUlubm9EQiBBVVRPX0lOQ1JFTUVOVD00MTUgREVGQVVMVCBDSEFSU0VUPXV0Zjg7CiN0YWJsZSggdXNlciApIHsKIyAgcHJpbWFyeV9rZXkoIGlkICk6IFVVSUQKIyAgY29sdW1uKCBpc0FjdGl2ZSApOiBCT09MRUFOCiN9CiNMZWFybmVyU2tpbGwgIjAuLioiIC0tPiAiMSIgTGVhcm5lciA6ICJsZWFybmVyX2lkIgp7CiAgICBkZWJ1ZygidW1sX3RhYmxlIiwgY3JlYXRlVGFibGUpCiAgICBzcGxpdChjcmVhdGVUYWJsZSxsaW5lcywiXG4iKQogICAgY29sdW1uSWR4PTAKICAgIGZvciAobGluZSBpbiBsaW5lcykgewogICAgICAgIGRlYnVnKGxpbmVzW2xpbmVdKQogICAgICAgIGlmIChtYXRjaChsaW5lc1tsaW5lXSwgL0NSRUFURSBUQUJMRSBgKFteYF0rKWAgXCgvLCBhcnIpKSB7CiAgICAgICAgICAgIGRlYnVnKCJUQUJMRSAiLCBsaW5lc1tsaW5lXSkKICAgICAgICAgICAgdGFibGVOYW1lID0gYXJyWzFdCiAgICAgICAgfSBlbHNlIGlmIChtYXRjaChsaW5lc1tsaW5lXSwgL1BSSU1BUlkgS0VZIFwoKFteKV0rKVwpLywgYXJyKSkgewogICAgICAgICAgICAjIFBSSU1BUlkgS0VZIChgaWRgKSwKICAgICAgICAgICAgZGVidWcoIlBLICIsIGxpbmVzW2xpbmVdKQogICAgICAgICAgICBzcGxpdChhcnJbMV0scHJpbWFyeUtleXMsIiwiKQogICAgICAgICAgICBmb3IgKGkgaW4gcHJpbWFyeUtleXMpIHsKICAgICAgICAgICAgICAgIGlmKG1hdGNoKHByaW1hcnlLZXlzW2ldLCAvWyBdP2AoW15gXSspK2BbIF0/LywgYXJyKSkgewogICAgICAgICAgICAgICAgICAgIGNvbHVtbnNEZXRhaWxzW2FyclsxXSAiLHBrIl0gPSAiMSIKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0gZWxzZSBpZiAobWF0Y2gobGluZXNbbGluZV0sIC9DT05TVFJBSU5UIGBbXmBdK2AgRk9SRUlHTiBLRVkgXChgKFteYF0rKWBcKSBSRUZFUkVOQ0VTIGAoW15gXSspYCBcKGAoW15gXSspYFwpLywgYXJyKSkgewogICAgICAgICAgICAjIENPTlNUUkFJTlQgYGNvcmVfbGVhcm5lcl9jdXN0b21lcl9pZF82ZDM1NmRhOF9ma19jb3JlX2N1c3RvbWVyX2lkYCBGT1JFSUdOIEtFWSAoYGN1c3RvbWVyX2lkYCkgUkVGRVJFTkNFUyBgY29yZV9jdXN0b21lcmAgKGBpZGApCiAgICAgICAgICAgIGRlYnVnKCJDb25zdHJhaW50ICIsIGxpbmVzW2xpbmVdKQogICAgICAgICAgICBjb2x1bW5OYW1lPWFyclsxXQogICAgICAgICAgICBjb2x1bW5zRGV0YWlsc1tjb2x1bW5OYW1lICIsZmsiXT0iMSIKICAgICAgICAgICAgY29sdW1uc0RldGFpbHNbY29sdW1uTmFtZSAiLGZrVGFibGUiXT1hcnJbMl0KICAgICAgICAgICAgY29sdW1uc0RldGFpbHNbY29sdW1uTmFtZSAiLGZrQ29sdW1uIl09YXJyWzNdCiAgICAgICAgfSBlbHNlIGlmIChtYXRjaChsaW5lc1tsaW5lXSwgL1VOSVFVRSBLRVkgYFteYF0rYCBcKChbXildKylcKS8sIGFycikpIHsKICAgICAgICAgICAgIyBVTklRVUUgS0VZIGB1bmlxdWVfbGVhcm5lcl9ieV9jdXN0b21lcmAgKGBleHRlcm5hbF9pZGAsYGN1c3RvbWVyX2lkYCksCiAgICAgICAgICAgIGRlYnVnKCJVbmlxdWUgS2V5ICIsIGxpbmVzW2xpbmVdKQogICAgICAgICAgICBzcGxpdChhcnJbMV0sdW5pcXVlS2V5cywiLCIpCiAgICAgICAgICAgIGZvciAoaSBpbiB1bmlxdWVLZXlzKSB7CiAgICAgICAgICAgICAgICBpZihtYXRjaCh1bmlxdWVLZXlzW2ldLCAvWyBdP2AoW15gXSspK2BbIF0/LywgYXJyKSkgewogICAgICAgICAgICAgICAgICAgIGNvbHVtbnNEZXRhaWxzW2FyclsxXSAiLHVuaXF1ZSJdID0gIjEiCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICB9IGVsc2UgaWYgKG1hdGNoKGxpbmVzW2xpbmVdLCAvYChbXmBdKylgICgoW0EtWmEtel0rKSgoXChbXildK1wpfClbIF0/KHVuc2lnbmVkfCkpKVsgXT8oTk9UIE5VTEx8TlVMTHwpWyBdPyhBVVRPX0lOQ1JFTUVOVHwpLywgYXJyKSkgewogICAgICAgICAgICAjIGBpZGAgaW50KDExKSBOT1QgTlVMTCBBVVRPX0lOQ1JFTUVOVAogICAgICAgICAgICAjIGB0ZXN0YCBpbnQoMTEpIHVuc2lnbmVkIE5VTEwKICAgICAgICAgICAgIyBgaW5zdGFuY2VfbmFtZWAgdmFyY2hhcigxMjgpIE5PVCBOVUxMCiAgICAgICAgICAgIGRlYnVnKCJDb2x1bW4gIiwgbGluZXNbbGluZV0pCiAgICAgICAgICAgIGNvbHVtbk5hbWUgPSBhcnJbMV0KICAgICAgICAgICAgY29sdW1uc1tjb2x1bW5JZHgrK10gPSBjb2x1bW5OYW1lCiAgICAgICAgICAgIGNvbHVtblR5cGU9YXJyWzJdCiAgICAgICAgICAgIGdzdWIoL1wofFwpLywgIiIsIGNvbHVtblR5cGUpCiAgICAgICAgICAgIGNvbHVtbnNEZXRhaWxzW2NvbHVtbk5hbWUgIix0eXBlIl0gPSBjb2x1bW5UeXBlICAjIGVnOiBpbnQoMTEpIHVuc2lnbmVkID0+IGludDExIHVuc2lnbmVkCiAgICAgICAgICAgIGNvbHVtbnNEZXRhaWxzW2NvbHVtbk5hbWUgIixudWxsIl0gPSBhcnJbN109PSJOT1QgTlVMTCIgPyAiMCIgOiAiMSIgIyBlZzogTk9UIE5VTEwKICAgICAgICAgICAgY29sdW1uc0RldGFpbHNbY29sdW1uTmFtZSAiLGF1dG9JbmNyZW1lbnQiXSA9IGFycls4XSAjIGVnOiBBVVRPX0lOQ1JFTUVOVAogICAgICAgIH0KICAgIH0KCiAgICBpZiAoREVCVUcgPT0gMSkgewogICAgICAgIGRlYnVnKCJUYWJsZSAiLCB0YWJsZU5hbWUpCiAgICAgICAgZm9yIChpIGluIGNvbHVtbnMpIGRlYnVnKCJjb2x1bW4gIiwgaSwgIiAiLCBjb2x1bW5zW2ldKQogICAgICAgIGZvciAoaSBpbiBjb2x1bW5zRGV0YWlscykgZGVidWcoIm1hdGNoICIsIGksICIgIiwgY29sdW1uc0RldGFpbHNbaV0pCiAgICB9CiAgICBhc29ydChjb2x1bW5zLCBjb2x1bW5zLCAiY29sdW1uX3NvcnQiKQogICAgaWYgKERFQlVHID09IDEpIHsKICAgICAgICBkZWJ1ZygiKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqIikKICAgICAgICBkZWJ1ZygiQ29sdW1ucyBvcmRlciBhZnRlciBzb3J0IikKICAgICAgICBmb3IgKGkgaW4gY29sdW1ucykgZGVidWcoImNvbHVtbiAiLCBpLCAiICIsIGNvbHVtbnNbaV0sIGNvbHVtbl93ZWlnaHQoY29sdW1uc1tpXSkpCiAgICB9CiAgICBwcmludGYoIlRhYmxlKCVzKSB7XG4iLCB0YWJsZU5hbWUgKQogICAgZm9yKGkgaW4gY29sdW1ucykgewogICAgICAgIGNvbHVtbk5hbWU9Y29sdW1uc1tpXQogICAgICAgICMgY29sdW1uKCRuYW1lLCAkdHlwZSwgJG51bGw9IiIsICRwaz0iIiwgJGZrPSIiLCAkdW5pcXVlPSIiKQogICAgICAgIHByaW50ZiggXAogICAgICAgICAgICAiICBjb2x1bW4oXCIlc1wiLCBcIiVzXCIsIFwiJXNcIiwgXCIlc1wiLCBcIiVzXCIsIFwiJXNcIilcbiIsIFwKICAgICAgICAgICAgY29sdW1uTmFtZSwgXAogICAgICAgICAgICB0cmltKGNvbHVtbnNEZXRhaWxzW2NvbHVtbk5hbWUgIix0eXBlIl0pLCBcCiAgICAgICAgICAgIChjb2x1bW5zRGV0YWlsc1tjb2x1bW5OYW1lICIsbnVsbCJdID09ICIxIikgPyAiTlVMTCIgOiAiTk9UIE5VTEwiLCBcCiAgICAgICAgICAgIChjb2x1bW5zRGV0YWlsc1tjb2x1bW5OYW1lICIscGsiXSA9PSAiMSIpID8gIlBLIiA6ICIiLCBcCiAgICAgICAgICAgIChjb2x1bW5zRGV0YWlsc1tjb2x1bW5OYW1lICIsZmtUYWJsZSJdID09ICIxIikgPyAiRksiIDogIiIsIFwKICAgICAgICAgICAgKGNvbHVtbnNEZXRhaWxzW2NvbHVtbk5hbWUgIix1bmlxdWUiXSA9PSAiMSIpID8gIlVOSVFVRSIgOiAiIiBcCiAgICAgICAgKQogICAgfQogICAgcHJpbnQoIn0iKQoKICAgIGZvcihpIGluIGNvbHVtbnMpIHsKICAgICAgICBjb2x1bW5OYW1lPWNvbHVtbnNbaV0KICAgICAgICBpZiAoY29sdW1uc0RldGFpbHNbY29sdW1uTmFtZSAiLGZrIl0gPT0gIjEiKSB7CiAgICAgICAgICAgICNMZWFybmVyU2tpbGwgIjAuLioiIC0tPiAiMSIgTGVhcm5lciA6ICJsZWFybmVyX2lkIgogICAgICAgICAgICBwcmludGYoIiVzIFwiMC4uKlwiIC0tPiBcIjFcIiAlcyA6IFwiJXNcIlxuIiwgdGFibGVOYW1lLCBjb2x1bW5zRGV0YWlsc1tjb2x1bW5OYW1lICIsZmtUYWJsZSJdLCBjb2x1bW5zRGV0YWlsc1tjb2x1bW5OYW1lICIsZmtDb2x1bW4iXSApCiAgICAgICAgfQogICAgfQogICAgcHJpbnQoIiIpCgogICAgZGVsZXRlIGNvbHVtbnNEZXRhaWxzCiAgICBkZWxldGUgY29sdW1ucwp9CgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KZnVuY3Rpb24gdW1sX3BhcnNlX2xpbmUoY3VycmVudExpbmUpCnsKICAgIGlmIChsZW5ndGgoY3VycmVudExpbmUpIDwgMiB8fCBtYXRjaChjdXJyZW50TGluZSwgIl4tLSIpID4gMCkgewogICAgICAgIHJldHVybgogICAgfQoKICAgIGlmIChtYXRjaChjdXJyZW50TGluZSwiOyIpPjApIHsKICAgICAgICBzcWxMaW5lID0gc3FsTGluZSAiXG4iIGN1cnJlbnRMaW5lCiAgICAgICAgZGVidWcoc3FsTGluZSkKICAgICAgICBpZiAobWF0Y2goc3FsTGluZSwiQ1JFQVRFIFRBQkxFIikgPiAwKSB7CiAgICAgICAgICAgIHVtbF90YWJsZShzcWxMaW5lKQogICAgICAgIH0KICAgICAgICBzcWxMaW5lPSIiCiAgICB9CiAgICBlbHNlIHsKICAgICAgICBzcWxMaW5lID0gc3FsTGluZSAiXG4iIGN1cnJlbnRMaW5lCiAgICB9Cn0KCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKQkVHSU4gewogICAgREVCVUc9RU5WSVJPTlsiREVCVUciXQogICAgc2tpbkZpbGU9QVJHVlsxXQogICAgZGVidWcoIlNraW4gZmlsZSIsIHNraW5GaWxlKQogICAgdW1sX3N0YXJ0KCkKfQoKewogICAgbGluZT0kMAogICAgdW1sX3BhcnNlX2xpbmUobGluZSkKfQoKRU5EIHsKICAgIHVtbF9lbmQoKQogICAgZXhpdCAwCiAgICAjIGZha2UgY2FsbCBvY2N1cnJpbmcgYWZ0ZXIgZXhpdCB0byByZW1vdmUgd2FybmluZyBhYm91dCB1bnVzZWQgZnVuY3Rpb24KICAgIGNvbHVtbl9zb3J0KCkKfQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0K" \ "755" -declare -gx embed_file_mysql2pumlScript="${PERSISTENT_TMPDIR:-/tmp}/abbc557ccf07f1917f739b75af2fba89/mysql2pumlScript" +declare -gx embed_file_mysql2pumlScript="${PERSISTENT_TMPDIR:-/tmp}/8ab6652e563ee45005c17d4a7f70a813cabae8c73bae3a83f86f63e46464f90c/mysql2pumlScript" Linux::requireRealpathCommand