From 927ce115607b3dfb36edea862ff5f143c32640ee Mon Sep 17 00:00:00 2001 From: "John L. Villalovos" Date: Sun, 8 Mar 2026 08:29:46 -0700 Subject: [PATCH 1/2] fix: convert from long `RUN` statements to a `setup.sh` script Convert the multiple `RUN` statements into a `setup.sh` bash script. Add a CI job to check the shell scripts. --- .github/workflows/build_pull_request.yml | 25 +++++ Dockerfile | 119 ++--------------------- setup.sh | 113 +++++++++++++++++++++ 3 files changed, 144 insertions(+), 113 deletions(-) create mode 100644 setup.sh diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 4636b92..98d71e8 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -18,6 +18,31 @@ jobs: with: dockerfile: Dockerfile + lint_shell_scripts: + name: Lint shell scripts + runs-on: ubuntu-latest + steps: + - + name: Checkout github repository + uses: actions/checkout@v6 + - + name: Install shell lint tools + run: | + sudo apt-get update + sudo apt-get install -y shellcheck shfmt + - + name: Run shellcheck + run: | + set -euo pipefail + mapfile -t scripts < <(git ls-files '*.sh') + shellcheck --severity style "${scripts[@]}" + - + name: Run shfmt + run: | + set -euo pipefail + mapfile -t scripts < <(git ls-files '*.sh') + shfmt -d -i 4 -ci "${scripts[@]}" + check_build: runs-on: ubuntu-latest env: diff --git a/Dockerfile b/Dockerfile index 0f8680f..cd7af0e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,123 +23,16 @@ COPY --from=comp /usr/bin/composer /usr/bin/composer # Update and install required debian packages ENV DEBIAN_FRONTEND=noninteractive -SHELL ["/bin/bash", "-o", "pipefail", "-c"] -# hadolint ignore=DL3008 # 'Pin versions in apt get install' -RUN < /etc/apache2/conf-available/remoteip.conf <//' -EORUN - -# Get and customize librebooking ARG APP_GH_REF ARG APP_GH_ADD_SHA=false +# hadolint ignore=DL3008 # 'Pin versions in apt get install' +COPY setup.sh /usr/local/bin/setup.sh RUN < /var/www/html/config/version-suffix.txt - else - echo "ERROR determining the LB_SHORT_SHA value from TARBALL_FILENAME ${TARBALL_FILENAME}" >&2 - exit 1 - fi -fi -if [ -f /var/www/html/composer.json ]; then - sed \ - -i /var/www/html/composer.json \ - -e "s:\(.*\)nickdnk/graph-sdk\(.*\)7.0\(.*\):\1joelbutcher/facebook-graph-sdk\26.1\3:" - composer install -fi -sed \ - -i /var/www/html/database_schema/create-user.sql \ - -e "s:^DROP USER ':DROP USER IF EXISTS ':g" \ - -e "s:booked_user:schedule_user:g" \ - -e "s:localhost:%:g" -if ! [ -d /var/www/html/tpl_c ]; then - mkdir /var/www/html/tpl_c -fi -mkdir /var/www/html/Web/uploads/reservation -EORUN - -RUN </etc/apache2/conf-available/remoteip.conf <//' + +set -xeuo pipefail +LB_TARBALL_URL="https://api.github.com/repos/LibreBooking/librebooking/tarball/${APP_GH_REF}" +curl \ + --fail \ + --silent \ + --location "${LB_TARBALL_URL}" | + tar --extract --gzip --directory=/var/www/html --strip-components=1 +if [ "${APP_GH_ADD_SHA}" = "true" ]; then + LB_SHORT_SHA="" + # TARBALL_FILENAME will be like the result of a `git describe` For + # example: 'LibreBooking-librebooking-v4.1.0-126-g6cc8a4c.tar.gz' where + # 'g6cc8a4c' is the short SHA prefixed with 'g'. So the short SHA is + # '6cc8a4c' + TARBALL_FILENAME=$( + curl \ + --head \ + --fail \ + --silent \ + --show-error \ + --location "${LB_TARBALL_URL}" | + sed -nE 's/.*filename="?([^";]+)"?.*/\1/p' + ) + LB_SHORT_SHA=$(echo "${TARBALL_FILENAME}" | sed -E 's/.*-g([0-9a-f]+)\.tar\.gz/\1/') + if [ -n "${LB_SHORT_SHA}" ]; then + printf '%s\n' "${LB_SHORT_SHA}" >/var/www/html/config/version-suffix.txt + else + echo "ERROR determining the LB_SHORT_SHA value from TARBALL_FILENAME ${TARBALL_FILENAME}" >&2 + exit 1 + fi +fi +if [ -f /var/www/html/composer.json ]; then + sed \ + -i /var/www/html/composer.json \ + -e "s:\(.*\)nickdnk/graph-sdk\(.*\)7.0\(.*\):\1joelbutcher/facebook-graph-sdk\26.1\3:" + composer install +fi +sed \ + -i /var/www/html/database_schema/create-user.sql \ + -e "s:^DROP USER ':DROP USER IF EXISTS ':g" \ + -e "s:booked_user:schedule_user:g" \ + -e "s:localhost:%:g" +if ! [ -d /var/www/html/tpl_c ]; then + mkdir /var/www/html/tpl_c +fi +mkdir /var/www/html/Web/uploads/reservation + +chown www-data:root \ + /var/www/html/config \ + /var/www/html/tpl_c \ + /var/www/html/Web/uploads/images \ + /var/www/html/Web/uploads/reservation \ + /usr/local/etc/php/conf.d/librebooking.ini +chmod g+rwx \ + /var/www/html/config \ + /var/www/html/tpl_c \ + /var/www/html/Web/uploads/images \ + /var/www/html/Web/uploads/reservation \ + /usr/local/etc/php/conf.d/librebooking.ini +chown --recursive www-data:root \ + /var/www/html/plugins +chmod --recursive g+rwx \ + /var/www/html/plugins From 9b404d816b1b8adcf81b24eaa18669b812c34aa9 Mon Sep 17 00:00:00 2001 From: "John L. Villalovos" Date: Sun, 8 Mar 2026 22:32:05 -0700 Subject: [PATCH 2/2] chore: resolve shellcheck issues with bin/*.sh Also run `shfmt -i 4 -ci -w bin/*sh` --- .github/workflows/build_pull_request.yml | 2 +- Dockerfile | 5 +- bin/cron.sh | 25 ++--- bin/entrypoint.sh | 51 +++++----- setup.sh | 119 ++++++++++++----------- 5 files changed, 105 insertions(+), 97 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 98d71e8..93dbb61 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -41,7 +41,7 @@ jobs: run: | set -euo pipefail mapfile -t scripts < <(git ls-files '*.sh') - shfmt -d -i 4 -ci "${scripts[@]}" + shfmt -d -i 2 -ci "${scripts[@]}" check_build: runs-on: ubuntu-latest diff --git a/Dockerfile b/Dockerfile index cd7af0e..2737b0b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,10 +29,11 @@ ARG APP_GH_ADD_SHA=false COPY setup.sh /usr/local/bin/setup.sh RUN <&2 "error: both $var and $fileVar are set (but are exclusive)" - exit 1 + echo >&2 "error: both $var and $fileVar are set (but are exclusive)" + exit 1 fi if [ -n "${varValue}" ]; then - export "$var"="${varValue}" + export "$var"="${varValue}" elif [ -n "${fileVarValue}" ]; then - export "$var"="$(cat "${fileVarValue}")" + export "$var"="$(cat "${fileVarValue}")" elif [ -n "${def}" ]; then - export "$var"="$def" + export "$var"="$def" fi unset "$fileVar" } @@ -36,10 +39,10 @@ LB_LOGGING_SQL=${LB_LOGGING_SQL:-${DFT_LOGGING_SQL}} APP_PATH=${APP_PATH:-${DFT_APP_PATH}} # Set the php timezone file -if [ -f /usr/share/zoneinfo/${LB_DEFAULT_TIMEZONE} ]; then +if [ -f /usr/share/zoneinfo/"${LB_DEFAULT_TIMEZONE}" ]; then INI_FILE="/usr/local/etc/php/conf.d/librebooking.ini" - echo "[Date]" >> ${INI_FILE} - echo "date.timezone=\"${LB_DEFAULT_TIMEZONE}\"" >> ${INI_FILE} + echo "[Date]" >>${INI_FILE} + echo "date.timezone=\"${LB_DEFAULT_TIMEZONE}\"" >>${INI_FILE} fi # Link the configuration file diff --git a/bin/entrypoint.sh b/bin/entrypoint.sh index 94efdfd..e63f2e9 100644 --- a/bin/entrypoint.sh +++ b/bin/entrypoint.sh @@ -1,4 +1,5 @@ #!/bin/bash +# vim: set expandtab ts=2 sw=2 ai : set -ex @@ -12,25 +13,27 @@ file_env() { local var="$1" local fileVar="${var}_FILE" local def="${2:-}" - local varValue=$(env | grep -E "^${var}=" | sed -E -e "s/^${var}=//") - local fileVarValue=$(env | grep -E "^${fileVar}=" | sed -E -e "s/^${fileVar}=//") + local varValue + varValue=$(env | grep -E "^${var}=" | sed -E -e "s/^${var}=//") + local fileVarValue + fileVarValue=$(env | grep -E "^${fileVar}=" | sed -E -e "s/^${fileVar}=//") if [ -n "${varValue}" ] && [ -n "${fileVarValue}" ]; then - echo >&2 "error: both $var and $fileVar are set (but are exclusive)" - exit 1 + echo >&2 "error: both $var and $fileVar are set (but are exclusive)" + exit 1 fi if [ -n "${varValue}" ]; then - export "$var"="${varValue}" + export "$var"="${varValue}" elif [ -n "${fileVarValue}" ]; then - export "$var"="$(cat "${fileVarValue}")" + export "$var"="$(cat "${fileVarValue}")" elif [ -n "${def}" ]; then - export "$var"="$def" + export "$var"="$def" fi unset "$fileVar" } # Exit if incompatible mount (images prior to V2) if [ "$(mount | grep /var/www/html)" = "/var/www/html" ]; then - echo "The volume must be mapped to container directory /config" >2 + echo "The volume must be mapped to container directory /config" >&2 exit 1 fi @@ -46,8 +49,8 @@ APP_PATH=${APP_PATH:-${DFT_APP_PATH}} # If volume was used with images older than v2, then archive useless files pushd /config if [ -d Web ]; then - mkdir archive - mv $(ls --ignore=archive) archive + mkdir -p archive + find . -mindepth 1 -maxdepth 1 ! -name archive -exec mv -t archive -- {} + if [ -f archive/config/config.php ]; then cp archive/config/config.php config.php fi @@ -79,21 +82,21 @@ sed \ -e "s:\(\['logging'\]\['sql'\].*\) '.*':\1 '${LB_LOGGING_SQL}':" # Create the plugins configuration file inside the volume -for source in $(find /var/www/html/plugins -type f -name "*dist*"); do - target=$(echo "${source}" | sed -e "s/.dist//") - if ! [ -f "/config/$(basename ${target})" ]; then - cp --no-clobber "${source}" "/config/$(basename ${target})" +while IFS= read -r -d '' source; do + target=${source//.dist/} + if ! [ -f "/config/$(basename "${target}")" ]; then + cp --no-clobber "${source}" "/config/$(basename "${target}")" fi - if ! [ -f ${target} ]; then - ln -s "/config/$(basename ${target})" "${target}" + if ! [ -f "${target}" ]; then + ln -s "/config/$(basename "${target}")" "${target}" fi -done +done < <(find /var/www/html/plugins -type f -name "*dist*" -print0) # Set the php timezone file -if [ -f /usr/share/zoneinfo/${LB_DEFAULT_TIMEZONE} ]; then +if [ -f /usr/share/zoneinfo/"${LB_DEFAULT_TIMEZONE}" ]; then INI_FILE="/usr/local/etc/php/conf.d/librebooking.ini" - echo "[Date]" >> ${INI_FILE} - echo "date.timezone=\"${LB_DEFAULT_TIMEZONE}\"" >> ${INI_FILE} + echo "[Date]" >>${INI_FILE} + echo "date.timezone=\"${LB_DEFAULT_TIMEZONE}\"" >>${INI_FILE} fi # Missing log directory @@ -102,7 +105,7 @@ if ! [ -d "${LB_LOGGING_FOLDER}" ]; then fi # A URL path prefix was set -if ! [ -z "${APP_PATH}" ]; then +if [ -n "${APP_PATH}" ]; then ## Set server document root 1 directory up sed \ -i /etc/apache2/sites-enabled/000-default.conf \ @@ -115,16 +118,16 @@ if ! [ -z "${APP_PATH}" ]; then ## Adapt the .htaccess file sed \ - -i /var/www/${APP_PATH}/.htaccess \ + -i /var/www/"${APP_PATH}"/.htaccess \ -e "s:\(RewriteCond .*\)/Web/:\1\.\*/Web/:" \ -e "s:\(RewriteRule .*\) /Web/:\1 /${APP_PATH}/Web/:" fi # Send log files to /dev/stdout as background jobs touch "${LB_LOGGING_FOLDER}/app.log" -tail --follow "${LB_LOGGING_FOLDER}/app.log" >> /dev/stdout & +tail --follow "${LB_LOGGING_FOLDER}/app.log" >>/dev/stdout & touch "${LB_LOGGING_FOLDER}/sql.log" -tail --follow "${LB_LOGGING_FOLDER}/sql.log" >> /dev/stdout & +tail --follow "${LB_LOGGING_FOLDER}/sql.log" >>/dev/stdout & # Switch to the apache server exec "$@" diff --git a/setup.sh b/setup.sh index 28eba0d..2fab56e 100644 --- a/setup.sh +++ b/setup.sh @@ -1,23 +1,24 @@ #!/bin/bash +# vim: set expandtab ts=2 sw=2 ai : set -e set -u set -o pipefail trap 'echo "Exit status $? at line $LINENO from: $BASH_COMMAND"' ERR -export PS4='+ ${BASH_SOURCE:-}:${FUNCNAME[0]:-}:L${LINENO:-}: ' +PS4='+ ${BASH_SOURCE:-}:${FUNCNAME[0]:-}:L${LINENO:-}: ' set -x apt-get update apt-get upgrade --yes apt-get install --yes --no-install-recommends \ - cron \ - git \ - libjpeg-dev \ - libldap-dev \ - libpng-dev \ - libfreetype6-dev \ - unzip + cron \ + git \ + libjpeg-dev \ + libldap-dev \ + libpng-dev \ + libfreetype6-dev \ + unzip apt-get clean rm -rf /var/lib/apt/lists/* @@ -42,72 +43,72 @@ chown --recursive www-data:root /var/log/librebooking chmod --recursive g+rwx /var/log/librebooking touch /usr/local/etc/php/conf.d/librebooking.ini sed \ - -i /etc/apache2/ports.conf \ - -e 's/Listen 80/Listen 8080/' \ - -e 's/Listen 443/Listen 8443/' + -i /etc/apache2/ports.conf \ + -e 's/Listen 80/Listen 8080/' \ + -e 's/Listen 443/Listen 8443/' sed \ - -i /etc/apache2/sites-available/000-default.conf \ - -e 's///' + -i /etc/apache2/sites-available/000-default.conf \ + -e 's///' set -xeuo pipefail LB_TARBALL_URL="https://api.github.com/repos/LibreBooking/librebooking/tarball/${APP_GH_REF}" curl \ - --fail \ - --silent \ - --location "${LB_TARBALL_URL}" | - tar --extract --gzip --directory=/var/www/html --strip-components=1 + --fail \ + --silent \ + --location "${LB_TARBALL_URL}" | + tar --extract --gzip --directory=/var/www/html --strip-components=1 if [ "${APP_GH_ADD_SHA}" = "true" ]; then - LB_SHORT_SHA="" - # TARBALL_FILENAME will be like the result of a `git describe` For - # example: 'LibreBooking-librebooking-v4.1.0-126-g6cc8a4c.tar.gz' where - # 'g6cc8a4c' is the short SHA prefixed with 'g'. So the short SHA is - # '6cc8a4c' - TARBALL_FILENAME=$( - curl \ - --head \ - --fail \ - --silent \ - --show-error \ - --location "${LB_TARBALL_URL}" | - sed -nE 's/.*filename="?([^";]+)"?.*/\1/p' - ) - LB_SHORT_SHA=$(echo "${TARBALL_FILENAME}" | sed -E 's/.*-g([0-9a-f]+)\.tar\.gz/\1/') - if [ -n "${LB_SHORT_SHA}" ]; then - printf '%s\n' "${LB_SHORT_SHA}" >/var/www/html/config/version-suffix.txt - else - echo "ERROR determining the LB_SHORT_SHA value from TARBALL_FILENAME ${TARBALL_FILENAME}" >&2 - exit 1 - fi + LB_SHORT_SHA="" + # TARBALL_FILENAME will be like the result of a `git describe` For + # example: 'LibreBooking-librebooking-v4.1.0-126-g6cc8a4c.tar.gz' where + # 'g6cc8a4c' is the short SHA prefixed with 'g'. So the short SHA is + # '6cc8a4c' + TARBALL_FILENAME=$( + curl \ + --head \ + --fail \ + --silent \ + --show-error \ + --location "${LB_TARBALL_URL}" | + sed -nE 's/.*filename="?([^";]+)"?.*/\1/p' + ) + LB_SHORT_SHA=$(echo "${TARBALL_FILENAME}" | sed -E 's/.*-g([0-9a-f]+)\.tar\.gz/\1/') + if [ -n "${LB_SHORT_SHA}" ]; then + printf '%s\n' "${LB_SHORT_SHA}" >/var/www/html/config/version-suffix.txt + else + echo "ERROR determining the LB_SHORT_SHA value from TARBALL_FILENAME ${TARBALL_FILENAME}" >&2 + exit 1 + fi fi if [ -f /var/www/html/composer.json ]; then - sed \ - -i /var/www/html/composer.json \ - -e "s:\(.*\)nickdnk/graph-sdk\(.*\)7.0\(.*\):\1joelbutcher/facebook-graph-sdk\26.1\3:" - composer install + sed \ + -i /var/www/html/composer.json \ + -e "s:\(.*\)nickdnk/graph-sdk\(.*\)7.0\(.*\):\1joelbutcher/facebook-graph-sdk\26.1\3:" + composer install fi sed \ - -i /var/www/html/database_schema/create-user.sql \ - -e "s:^DROP USER ':DROP USER IF EXISTS ':g" \ - -e "s:booked_user:schedule_user:g" \ - -e "s:localhost:%:g" + -i /var/www/html/database_schema/create-user.sql \ + -e "s:^DROP USER ':DROP USER IF EXISTS ':g" \ + -e "s:booked_user:schedule_user:g" \ + -e "s:localhost:%:g" if ! [ -d /var/www/html/tpl_c ]; then - mkdir /var/www/html/tpl_c + mkdir /var/www/html/tpl_c fi mkdir /var/www/html/Web/uploads/reservation chown www-data:root \ - /var/www/html/config \ - /var/www/html/tpl_c \ - /var/www/html/Web/uploads/images \ - /var/www/html/Web/uploads/reservation \ - /usr/local/etc/php/conf.d/librebooking.ini + /var/www/html/config \ + /var/www/html/tpl_c \ + /var/www/html/Web/uploads/images \ + /var/www/html/Web/uploads/reservation \ + /usr/local/etc/php/conf.d/librebooking.ini chmod g+rwx \ - /var/www/html/config \ - /var/www/html/tpl_c \ - /var/www/html/Web/uploads/images \ - /var/www/html/Web/uploads/reservation \ - /usr/local/etc/php/conf.d/librebooking.ini + /var/www/html/config \ + /var/www/html/tpl_c \ + /var/www/html/Web/uploads/images \ + /var/www/html/Web/uploads/reservation \ + /usr/local/etc/php/conf.d/librebooking.ini chown --recursive www-data:root \ - /var/www/html/plugins + /var/www/html/plugins chmod --recursive g+rwx \ - /var/www/html/plugins + /var/www/html/plugins