diff --git a/Dockerfile b/Dockerfile index 116631f60..33d0eeee3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,6 +17,11 @@ ARG FOREGO_VERSION="0.17.2" # See: https://github.com/hairyhenderson/gomplate ARG GOMPLATE_VERSION="v3.11.6" +# See: https://github.com/dotenv-linter/dotenv-linter/releases +# +# WARN: v3.3.0 and above requires newer libc version than Ubuntu ships with +ARG DOTENV_LINTER_VERSION="v3.2.0" + ### # PHP base configuration ### @@ -99,8 +104,10 @@ ARG PHP_VERSION ARG RUNTIME_GID ARG RUNTIME_UID ARG TARGETPLATFORM +ARG DOTENV_LINTER_VERSION ENV DEBIAN_FRONTEND="noninteractive" +ENV DOTENV_LINTER_VERSION="${DOTENV_LINTER_VERSION}" # Ensure we run all scripts through 'bash' rather than 'sh' SHELL ["/bin/bash", "-c"] diff --git a/docker/shared/root/docker/entrypoint.d/00-check-config.sh b/docker/shared/root/docker/entrypoint.d/00-check-config.sh new file mode 100755 index 000000000..36244f6ca --- /dev/null +++ b/docker/shared/root/docker/entrypoint.d/00-check-config.sh @@ -0,0 +1,18 @@ +#!/bin/bash +source /docker/helpers.sh + +entrypoint-set-script-name "$0" + +# Validating dot-env files for any issues +for file in "${dot_env_files[@]}"; do + if file-exists "$file"; then + log-warning "Could not source file [${file}]: does not exists" + continue + fi + + log-info "Linting dotenv file ${file}" + dotenv-linter --skip=QuoteCharacter --skip=UnorderedKey "${file}" +done + +# Write the config cache +run-as-runtime-user php artisan config:cache diff --git a/docker/shared/root/docker/entrypoint.d/05-templating.sh b/docker/shared/root/docker/entrypoint.d/05-templating.sh index cafb9d133..1bb62aae6 100755 --- a/docker/shared/root/docker/entrypoint.d/05-templating.sh +++ b/docker/shared/root/docker/entrypoint.d/05-templating.sh @@ -49,7 +49,7 @@ find "${ENTRYPOINT_TEMPLATE_DIR}" -follow -type f -print | while read -r templat cat "${template_file}" | gomplate >"${output_file_path}" # Show the diff from the envsubst command - if [[ ${ENTRYPOINT_SHOW_TEMPLATE_DIFF:-1} = 1 ]]; then - git --no-pager diff --color=always "${template_file}" "${output_file_path}" || : + if is-true "${ENTRYPOINT_SHOW_TEMPLATE_DIFF}"; then + git --no-pager diff --color=always "${template_file}" "${output_file_path}" || : # ignore diff exit code fi done diff --git a/docker/shared/root/docker/entrypoint.d/11-first-time-setup.sh b/docker/shared/root/docker/entrypoint.d/11-first-time-setup.sh index 529c1d0cf..f9fd6a61b 100755 --- a/docker/shared/root/docker/entrypoint.d/11-first-time-setup.sh +++ b/docker/shared/root/docker/entrypoint.d/11-first-time-setup.sh @@ -10,13 +10,3 @@ only-once "storage:link" run-as-runtime-user php artisan storage:link only-once "key:generate" run-as-runtime-user php artisan key:generate only-once "initial:migrate" run-as-runtime-user php artisan migrate --force only-once "import:cities" run-as-runtime-user php artisan import:cities - -# if [ ! -e "./storage/docker-instance-actor-has-run" ]; then -# run-as-runtime-user php artisan instance:actor -# touch "./storage/docker-instance-actor-has-run" -# fi - -# if [ ! -e "./storage/docker-passport-keys-has-run" ]; then -# run-as-runtime-user php artisan instance:actor -# touch "./storage/docker-passport-keys-has-run" -# fi diff --git a/docker/shared/root/docker/entrypoint.d/12-migrations.sh b/docker/shared/root/docker/entrypoint.d/12-migrations.sh index 68008c596..20cbe3a31 100755 --- a/docker/shared/root/docker/entrypoint.d/12-migrations.sh +++ b/docker/shared/root/docker/entrypoint.d/12-migrations.sh @@ -6,13 +6,6 @@ entrypoint-set-script-name "$0" # Allow automatic applying of outstanding/new migrations on startup : ${DOCKER_APPLY_NEW_MIGRATIONS_AUTOMATICALLY:=0} -if [[ $DOCKER_APPLY_NEW_MIGRATIONS_AUTOMATICALLY -eq 0 ]]; then - log-info "Automatic applying of new database migrations is disabled" - log-info "Please set [DOCKER_APPLY_NEW_MIGRATIONS_AUTOMATICALLY=1] in your [.env] file to enable this." - - exit 0 -fi - # Wait for the database to be ready await-database-ready @@ -20,7 +13,7 @@ await-database-ready declare -i new_migrations=0 run-as-runtime-user php artisan migrate:status | grep No && new_migrations=1 -if [[ $new_migrations -eq 0 ]]; then +if is-true "${new_migrations}"; then log-info "No outstanding migrations detected" exit 0 @@ -28,4 +21,11 @@ fi log-warning "New migrations available, will automatically apply them now" +if is-false "${DOCKER_APPLY_NEW_MIGRATIONS_AUTOMATICALLY}"; then + log-info "Automatic applying of new database migrations is disabled" + log-info "Please set [DOCKER_APPLY_NEW_MIGRATIONS_AUTOMATICALLY=1] in your [.env] file to enable this." + + exit 0 +fi + run-as-runtime-user php artisan migrate --force diff --git a/docker/shared/root/docker/helpers.sh b/docker/shared/root/docker/helpers.sh index 24bd7f1f8..e71b4f295 100644 --- a/docker/shared/root/docker/helpers.sh +++ b/docker/shared/root/docker/helpers.sh @@ -224,17 +224,17 @@ function load-config-files() { # Associative array (aka map/dictionary) holding the unique keys found in dot-env files local -A _tmp_dot_env_keys - for f in "${dot_env_files[@]}"; do - if [ ! -e "$f" ]; then - log-warning "Could not source file [${f}]: does not exists" + for file in "${dot_env_files[@]}"; do + if ! file-exists "${file}"; then + log-warning "Could not source file [${file}]: does not exists" continue fi - log-info "Sourcing ${f}" - source "${f}" + log-info "Sourcing ${file}" + source "${file}" # find all keys in the dot-env file and store them in our temp associative array - for k in "$(grep -v '^#' "${f}" | sed -E 's/(.*)=.*/\1/' | xargs)"; do + for k in "$(grep -v '^#' "${file}" | cut -d"=" -f1 | xargs)"; do _tmp_dot_env_keys[$k]=1 done done @@ -270,6 +270,21 @@ function is-writable() { [[ -w "$1" ]] } +# @description Checks if $1 exists (directory or file) +# @arg $1 string The path to check +# @exitcode 0 If $1 exists +# @exitcode 1 If $1 does *NOT* exists +function path-exists() { + [[ -e "$1" ]] +} + +# @description Checks if $1 exists (file only) +# @arg $1 string The path to check +# @exitcode 0 If $1 exists +# @exitcode 1 If $1 does *NOT* exists +function file-exists() { + [[ -f "$1" ]] +} # @description Checks if $1 contains any files or not # @arg $1 string The path to check # @exitcode 0 If $1 contains files @@ -328,7 +343,7 @@ function acquire-lock() { ensure-directory-exists "$(dirname "${file}")" log-info "🔑 Trying to acquire lock: ${file}: " - while [[ -e "${file}" ]]; do + while file-exists "${file}"; do log-info "🔒 Waiting on lock ${file}" staggered-sleep @@ -384,15 +399,17 @@ declare -f -t on-trap function await-database-ready() { log-info "❓ Waiting for database to be ready" + load-config-files + case "${DB_CONNECTION:-}" in mysql) - while ! echo "SELECT 1" | mysql --user="$DB_USERNAME" --password="$DB_PASSWORD" --host="$DB_HOST" "$DB_DATABASE" --silent >/dev/null; do + while ! echo "SELECT 1" | mysql --user="${DB_USERNAME}" --password="${DB_PASSWORD}" --host="${DB_HOST}" "${DB_DATABASE}" --silent >/dev/null; do staggered-sleep done ;; pgsql) - while ! echo "SELECT 1" | psql --user="$DB_USERNAME" --password="$DB_PASSWORD" --host="$DB_HOST" "$DB_DATABASE" >/dev/null; do + while ! echo "SELECT 1" | PGPASSWORD="${DB_PASSWORD}" psql --user="${DB_USERNAME}" --host="${DB_HOST}" "${DB_DATABASE}" >/dev/null; do staggered-sleep done ;; @@ -453,3 +470,50 @@ function show-call-stack() { log-error " at: ${func} ${src}:${lineno}" done } + +# @description Helper function see if $1 could be considered truthy +# @arg $1 string The string to evaluate +# @see as-boolean +function is-true() { + as-boolean "${1:-}" && return 0 +} + +# @description Helper function see if $1 could be considered falsey +# @arg $1 string The string to evaluate +# @see as-boolean +function is-false() { + as-boolean "${1:-}" || return 0 +} + +# @description Helper function see if $1 could be truethy or falsey. +# since this is a bash context, returning 0 is true and 1 is false +# so it works with [if is-false $input; then .... fi] +# +# This is a bit confusing, *especially* in a PHP world where [1] would be truthy and +# [0] would be falsely as return values +# @arg $1 string The string to evaluate +function as-boolean() { + local input="${1:-}" + local var="${input,,}" # convert input to lower-case + + case "$var" in + 1 | true) + log-info "[as-boolean] variable [${var}] was detected as truthy/true, returning [0]" + + return 0 + ;; + + 0 | false) + log-info "[as-boolean] variable [${var}] was detected as falsey/false, returning [1]" + + return 1 + ;; + + *) + log-warning "[as-boolean] variable [${var}] could not be detected as true or false, returning [1] (false) as default" + + return 1 + ;; + + esac +} diff --git a/docker/shared/root/docker/install/base.sh b/docker/shared/root/docker/install/base.sh index d3da207e5..5856836f9 100755 --- a/docker/shared/root/docker/install/base.sh +++ b/docker/shared/root/docker/install/base.sh @@ -83,3 +83,6 @@ apt-get install -y \ locale-gen update-locale + +# Install dotenv linter (https://github.com/dotenv-linter/dotenv-linter) +curl -sSfL https://raw.githubusercontent.com/dotenv-linter/dotenv-linter/master/install.sh | sh -s -- -b /usr/local/bin ${DOTENV_LINTER_VERSION}