diff --git a/contrib/docker/Dockerfile b/contrib/docker/Dockerfile index 471ac5820..708d8247b 100644 --- a/contrib/docker/Dockerfile +++ b/contrib/docker/Dockerfile @@ -154,7 +154,7 @@ RUN --mount=type=cache,id=pixelfed-php-${PHP_VERSION}-${PHP_DEBIAN_RELEASE}-${TA # PHP: composer and source code ####################################################### -FROM php-extensions AS composer-and-src +FROM base AS composer-and-src ARG PHP_VERSION ARG PHP_DEBIAN_RELEASE @@ -178,7 +178,7 @@ COPY --link --from=composer-image /usr/bin/composer /usr/bin/composer USER ${RUNTIME_UID}:${RUNTIME_GID} # Copy over only composer related files so docker layer cache isn't invalidated on PHP file changes -COPY --link --chown=${RUNTIME_UID}:${RUNTIME_GID} composer.json composer.lock /var/www/ +COPY --chown=${RUNTIME_UID}:${RUNTIME_GID} composer.json composer.lock /var/www/ # Install composer dependencies # NOTE: we skip the autoloader generation here since we don't have all files avaliable (yet) @@ -187,14 +187,7 @@ RUN --mount=type=cache,id=pixelfed-composer-${PHP_VERSION}-${PHP_DEBIAN_RELEASE} && composer install --prefer-dist --no-autoloader --ignore-platform-reqs # Copy all other files over -COPY --link --chown=${RUNTIME_UID}:${RUNTIME_GID} . /var/www/ - -# Generate optimized autoloader now that we have all files around -RUN set -ex \ - && composer dump-autoload --optimize - -#! Changing back to root -USER root:root +COPY --chown=${RUNTIME_UID}:${RUNTIME_GID} . /var/www/ ####################################################### # Runtime: base @@ -213,9 +206,19 @@ ENV RUNTIME_GID=${RUNTIME_GID} COPY --link --from=php-extensions /usr/local/lib/php/extensions /usr/local/lib/php/extensions COPY --link --from=php-extensions /usr/local/etc/php /usr/local/etc/php -COPY --link --from=composer-and-src --chown=${RUNTIME_UID}:${RUNTIME_GID} /var/www /var/www COPY --link --from=forego-image /usr/local/bin/forego /usr/local/bin/forego COPY --link --from=gomplate-image /usr/local/bin/gomplate /usr/local/bin/gomplate +COPY --link --from=composer-image /usr/bin/composer /usr/bin/composer +COPY --link --from=composer-and-src --chown=${RUNTIME_UID}:${RUNTIME_GID} /var/www /var/www + +#! Changing user to runtime user +USER ${RUNTIME_UID}:${RUNTIME_GID} + +# Generate optimized autoloader now that we have all files around +RUN set -ex \ + && composer dump-autoload --optimize + +USER root # for detail why storage is copied this way, pls refer to https://github.com/pixelfed/pixelfed/pull/2137#discussion_r434468862 RUN set -ex \ diff --git a/contrib/docker/shared/root/docker/entrypoint.d/04-defaults.envsh b/contrib/docker/shared/root/docker/entrypoint.d/04-defaults.envsh index 2244be9b8..00415b22f 100755 --- a/contrib/docker/shared/root/docker/entrypoint.d/04-defaults.envsh +++ b/contrib/docker/shared/root/docker/entrypoint.d/04-defaults.envsh @@ -7,20 +7,20 @@ # and future entrypoint.d scripts # -set_identity "${BASH_SOURCE[0]}" +entrypoint-set-name "${BASH_SOURCE[0]}" load-config-files : ${POST_MAX_SIZE_BUFFER:=1M} -log "POST_MAX_SIZE_BUFFER is set to [${POST_MAX_SIZE_BUFFER}]" +log-info "POST_MAX_SIZE_BUFFER is set to [${POST_MAX_SIZE_BUFFER}]" buffer=$(numfmt --invalid=fail --from=auto --to=none --to-unit=K "${POST_MAX_SIZE_BUFFER}") -log "POST_MAX_SIZE_BUFFER converted to KB is [${buffer}]" +log-info "POST_MAX_SIZE_BUFFER converted to KB is [${buffer}]" -log "POST_MAX_SIZE will be calculated by [({MAX_PHOTO_SIZE} * {MAX_ALBUM_LENGTH}) + {POST_MAX_SIZE_BUFFER}]" -log " MAX_PHOTO_SIZE=${MAX_PHOTO_SIZE}" -log " MAX_ALBUM_LENGTH=${MAX_ALBUM_LENGTH}" -log " POST_MAX_SIZE_BUFFER=${buffer}" +log-info "POST_MAX_SIZE will be calculated by [({MAX_PHOTO_SIZE} * {MAX_ALBUM_LENGTH}) + {POST_MAX_SIZE_BUFFER}]" +log-info " MAX_PHOTO_SIZE=${MAX_PHOTO_SIZE}" +log-info " MAX_ALBUM_LENGTH=${MAX_ALBUM_LENGTH}" +log-info " POST_MAX_SIZE_BUFFER=${buffer}" : ${POST_MAX_SIZE:=$(numfmt --invalid=fail --from=auto --from-unit=K --to=si $(((${MAX_PHOTO_SIZE} * ${MAX_ALBUM_LENGTH}) + ${buffer})))} -log "POST_MAX_SIZE was calculated to [${POST_MAX_SIZE}]" +log-info "POST_MAX_SIZE was calculated to [${POST_MAX_SIZE}]" export POST_MAX_SIZE diff --git a/contrib/docker/shared/root/docker/entrypoint.d/05-templating.sh b/contrib/docker/shared/root/docker/entrypoint.d/05-templating.sh index 468b10617..22975a905 100755 --- a/contrib/docker/shared/root/docker/entrypoint.d/05-templating.sh +++ b/contrib/docker/shared/root/docker/entrypoint.d/05-templating.sh @@ -1,7 +1,7 @@ #!/bin/bash source /docker/helpers.sh -set_identity "$0" +entrypoint-set-name "$0" declare template_dir="${ENVSUBST_TEMPLATE_DIR:-/docker/templates}" declare output_dir="${ENVSUBST_OUTPUT_DIR:-}" @@ -23,13 +23,13 @@ find "$template_dir" -follow -type f -print | while read -r template; do output_dir=$(dirname "$output_path") if [ ! -w "$output_dir" ]; then - log_error_and_exit "ERROR: $template_dir exists, but $output_dir is not writable" + log-error-and-exit "ERROR: $template_dir exists, but $output_dir is not writable" fi # create a subdirectory where the template file exists mkdir -p "$output_dir/$subdir" - log "Running [gomplate] on [$template] --> [$output_path]" + log-info "Running [gomplate] on [$template] --> [$output_path]" cat "$template" | gomplate >"$output_path" # Show the diff from the envsubst command diff --git a/contrib/docker/shared/root/docker/entrypoint.d/10-storage.sh b/contrib/docker/shared/root/docker/entrypoint.d/10-storage.sh index a35eeb5d3..14f66dc27 100755 --- a/contrib/docker/shared/root/docker/entrypoint.d/10-storage.sh +++ b/contrib/docker/shared/root/docker/entrypoint.d/10-storage.sh @@ -1,10 +1,10 @@ #!/bin/bash source /docker/helpers.sh -set_identity "$0" +entrypoint-set-name "$0" -as_runtime_user cp --recursive storage.skel/* storage/ -as_runtime_user php artisan storage:link +run-as-runtime-user cp --recursive storage.skel/* storage/ +run-as-runtime-user php artisan storage:link -log "Ensure permissions are correct" +log-info "Ensure permissions are correct" chown --recursive ${RUNTIME_UID}:${RUNTIME_GID} storage/ bootstrap/ diff --git a/contrib/docker/shared/root/docker/entrypoint.d/20-horizon.sh b/contrib/docker/shared/root/docker/entrypoint.d/20-horizon.sh index 7c1d8fdc6..2d3394746 100755 --- a/contrib/docker/shared/root/docker/entrypoint.d/20-horizon.sh +++ b/contrib/docker/shared/root/docker/entrypoint.d/20-horizon.sh @@ -1,6 +1,6 @@ #!/bin/bash source /docker/helpers.sh -set_identity "$0" +entrypoint-set-name "$0" -as_runtime_user php artisan horizon:publish +run-as-runtime-user php artisan horizon:publish diff --git a/contrib/docker/shared/root/docker/entrypoint.d/30-cache.sh b/contrib/docker/shared/root/docker/entrypoint.d/30-cache.sh index 11965fcea..5c0f20bb8 100755 --- a/contrib/docker/shared/root/docker/entrypoint.d/30-cache.sh +++ b/contrib/docker/shared/root/docker/entrypoint.d/30-cache.sh @@ -1,8 +1,8 @@ #!/bin/bash source /docker/helpers.sh -set_identity "$0" +entrypoint-set-name "$0" -as_runtime_user php artisan route:cache -as_runtime_user php artisan view:cache -as_runtime_user php artisan config:cache +run-as-runtime-user php artisan route:cache +run-as-runtime-user php artisan view:cache +run-as-runtime-user php artisan config:cache diff --git a/contrib/docker/shared/root/docker/entrypoint.sh b/contrib/docker/shared/root/docker/entrypoint.sh index dbc8aa1b5..4f2e2eb90 100755 --- a/contrib/docker/shared/root/docker/entrypoint.sh +++ b/contrib/docker/shared/root/docker/entrypoint.sh @@ -13,7 +13,9 @@ if [[ ${ENTRYPOINT_SKIP} == 0 ]]; then source /docker/helpers.sh - declare -a skip_scripts=() + entrypoint-set-name "entrypoint.sh" + + declare -a skip_scripts IFS=' ' read -a skip_scripts <<<"$ENTRYPOINT_SKIP_SCRIPTS" declare script_name @@ -22,49 +24,52 @@ if [[ ${ENTRYPOINT_SKIP} == 0 ]]; then mkdir -p "${ENTRYPOINT_ROOT}" if /usr/bin/find "${ENTRYPOINT_ROOT}" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; then - log "looking for shell scripts in /docker/entrypoint.d/" + log-info "looking for shell scripts in /docker/entrypoint.d/" - find "${ENTRYPOINT_ROOT}" -follow -type f -print | sort -V | while read -r f; do - script_name="$(get_script_name $f)" - if array_value_exists skip_scripts "${script_name}"; then - log_warning "Skipping script [${script_name}] since it's in the skip list (\$ENTRYPOINT_SKIP_SCRIPTS)" + find "${ENTRYPOINT_ROOT}" -follow -type f -print | sort -V | while read -r file; do + script_name="$(get-entrypoint-script-name $file)" + + if in-array "${script_name}" skip_scripts; then + log-warning "Skipping script [${script_name}] since it's in the skip list (\$ENTRYPOINT_SKIP_SCRIPTS)" continue fi - case "$f" in + case "${file}" in *.envsh) - if [ -x "$f" ]; then - log "Sourcing $f" - - source "$f" - - resetore_identity - else + if ! is-executable "${file}"; then # warn on shell scripts without exec bit - log_error_and_exit "File [$f] is not executable (please 'chmod +x' it)" + log-error-and-exit "File [${file}] is not executable (please 'chmod +x' it)" fi + + log-info "Sourcing [${file}]" + + source "${file}" + + # the sourced file will (should) than the log prefix, so this restores our own + # "global" log prefix once the file is done being sourced + entrypoint-restore-name ;; *.sh) - if [ -x "$f" ]; then - log "Launching $f" - "$f" - else + if ! is-executable "${file}"; then # warn on shell scripts without exec bit - log_error_and_exit "File [$f] is not executable (please 'chmod +x' it)" + log-error-and-exit "File [${file}] is not executable (please 'chmod +x' it)" fi + + log-info "Running [${file}]" + "${file}" ;; *) - log_warning "Ignoring $f" + log-warning "Ignoring unrecognized file [${file}]" ;; esac done - log "Configuration complete; ready for start up" + log-info "Configuration complete; ready for start up" else - log_warning "No files found in ${ENTRYPOINT_ROOT}, skipping configuration" + log-warning "No files found in ${ENTRYPOINT_ROOT}, skipping configuration" fi fi diff --git a/contrib/docker/shared/root/docker/helpers.sh b/contrib/docker/shared/root/docker/helpers.sh index f1ca33d09..5fb04d7a9 100644 --- a/contrib/docker/shared/root/docker/helpers.sh +++ b/contrib/docker/shared/root/docker/helpers.sh @@ -1,70 +1,79 @@ #!/bin/bash set -e -o errexit -o nounset -o pipefail +# Some splash of color for important messages declare -g error_message_color="\033[1;31m" declare -g warn_message_color="\033[1;34m" declare -g color_clear="\033[1;0m" + +# Current and previous log prefix declare -g log_prefix= -declare -g old_log_prefix= +declare -g log_prefix_previous= + +# dot-env files to source when reading config declare -ra dot_env_files=( /var/www/.env.docker /var/www/.env ) + +# environment keys seen when source dot files (so we can [export] them) declare -ga seen_dot_env_variables=() -function set_identity() { - old_log_prefix="${log_prefix}" - log_prefix="ENTRYPOINT - [$(get_script_name $1)] - " +function entrypoint-set-name() { + log_prefix_previous="${log_prefix}" + log_prefix="ENTRYPOINT - [$(get-entrypoint-script-name $1)] - " } -function resetore_identity() { - log_prefix="${old_log_prefix}" +function entrypoint-restore-name() { + log_prefix="${log_prefix_previous}" } -function as_runtime_user() { +function run-as-runtime-user() { local -i exit_code local target_user target_user=$(id -un ${RUNTIME_UID}) - log "👷 Running [${*}] as [${target_user}]" + log-info "👷 Running [${*}] as [${target_user}]" su --preserve-environment "${target_user}" --shell /bin/bash --command "${*}" exit_code=$? if [[ $exit_code != 0 ]]; then - log_error "❌ Error!" + log-error "❌ Error!" return $exit_code fi - log "✅ OK!" + log-info "✅ OK!" return $exit_code } -# @description Display the given error message with its line number on stderr and exit with error. +# @description Print the given error message to stderr # @arg $message string A error message. -function log_error() { - echo -e "${error_message_color}${log_prefix}ERROR - ${1}${color_clear}" >/dev/stderr +function log-error() { + echo -e "${error_message_color}${log_prefix}ERROR - ${*}${color_clear}" >/dev/stderr } -# @description Display the given error message with its line number on stderr and exit with error. -# @arg $message string A error message. +# @description Print the given error message to stderr and exit 1 +# @arg $@ string A error message. # @exitcode 1 -function log_error_and_exit() { - log_error "$1" +function log-error-and-exit() { + log-error "$@" exit 1 } -# @description Display the given warning message with its line number on stderr. -# @arg $message string A warning message. -function log_warning() { - echo -e "${warn_message_color}${log_prefix}WARNING - ${1}${color_clear}" >/dev/stderr +# @description Print the given warning message to stderr +# @arg $@ string A warning message. +function log-warning() { + echo -e "${warn_message_color}${log_prefix}WARNING - ${*}${color_clear}" >/dev/stderr } -function log() { +# @description Print the given message to stderr unless [ENTRYPOINT_QUIET_LOGS] is set +# @arg $@ string A warning message. +function log-info() { if [ -z "${ENTRYPOINT_QUIET_LOGS:-}" ]; then - echo "${log_prefix}$@" + echo "${log_prefix}$*" fi } @@ -74,11 +83,11 @@ function load-config-files() { for f in "${dot_env_files[@]}"; do if [ ! -e "$f" ]; then - log_warning "Could not source file [${f}]: does not exists" + log-warning "Could not source file [${f}]: does not exists" continue fi - log "Sourcing ${f}" + log-info "Sourcing ${f}" source "${f}" # find all keys in the dot-env file and store them in our temp associative array @@ -90,13 +99,17 @@ function load-config-files() { seen_dot_env_variables=(${!_tmp_dot_env_keys[@]}) } -function array_value_exists() { - local -nr validOptions=$1 - local -r providedValue="\<${2}\>" +function in-array() { + local -r needle="\<${1}\>" + local -nr haystack=$2 - [[ ${validOptions[*]} =~ $providedValue ]] + [[ ${haystack[*]} =~ $needle ]] } -function get_script_name() { +function is-executable() { + [[ -x "$1" ]] +} + +function get-entrypoint-script-name() { echo "${1#"$ENTRYPOINT_ROOT"}" }