diff --git a/.changeset/stale-flowers-hide.md b/.changeset/stale-flowers-hide.md new file mode 100644 index 0000000..563228a --- /dev/null +++ b/.changeset/stale-flowers-hide.md @@ -0,0 +1,5 @@ +--- +"@wpengine/site-deploy": patch +--- + +Fixes a bug when deploying to dirs other than site root by dynamically generating the file excludes list on the fly diff --git a/Dockerfile b/Dockerfile index d2939ed..36304af 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,13 +1,12 @@ FROM instrumentisto/rsync-ssh:alpine3.20 -# Intsall dependencies +# Install dependencies RUN apk update \ && apk upgrade \ && apk add --no-cache \ bash \ php \ && rm -rf /var/cache/apk/* -# Add entrypoint and excludes -ADD functions.sh /functions.sh +# Add entrypoint and utils +COPY utils /utils ADD entrypoint.sh /entrypoint.sh -ADD exclude.txt /exclude.txt ENTRYPOINT ["/entrypoint.sh"] diff --git a/Makefile b/Makefile index 6206910..c9ca11a 100644 --- a/Makefile +++ b/Makefile @@ -22,5 +22,6 @@ version: build docker image tag $(IMAGE) $(IMAGE_NAME):v$(MAJOR_VERSION).$(MINOR_VERSION) && \ docker image tag $(IMAGE) $(IMAGE_NAME):v$(MAJOR_VERSION).$(MINOR_VERSION).$(PATCH_VERSION) -test: +test-unit: ./tests/test_functions.sh + ./tests/test_generate_path_excludes.sh diff --git a/entrypoint.sh b/entrypoint.sh index 90edf79..0985cfe 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -5,8 +5,9 @@ set -e # Get the directory of the current script SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -# Source the functions.sh file relative to the current script's location -source "${SCRIPT_DIR}/functions.sh" +# Source the functions.sh and exclude_from.sh files relative to the current script's location +source "${SCRIPT_DIR}/utils/functions.sh" +source "${SCRIPT_DIR}/utils/generate_path_excludes.sh" validate() { # mandatory params @@ -27,7 +28,7 @@ setup_env() { WPE_ENV_NAME="${PRD_ENV}"; elif [[ -n ${STG_ENV} ]]; then WPE_ENV_NAME="${STG_ENV}"; - elif [[ -n ${DEV_ENV} ]]; then + elif [[ -n ${DEV_ENV} ]]; then WPE_ENV_NAME="${DEV_ENV}"; else echo "Failure: Missing environment variable..." && exit 1; fi @@ -54,18 +55,18 @@ setup_env() { setup_ssh_dir() { echo "setup ssh path" - if [ ! -d "${HOME}/.ssh" ]; then - mkdir "${HOME}/.ssh" - SSH_PATH="${HOME}/.ssh" + if [ ! -d "${HOME}/.ssh" ]; then + mkdir "${HOME}/.ssh" + SSH_PATH="${HOME}/.ssh" mkdir "${SSH_PATH}/ctl/" - # Set Key Perms + # Set Key Perms chmod -R 700 "$SSH_PATH" - else + else SSH_PATH="${HOME}/.ssh" echo "using established SSH KEY path..."; fi - #Copy secret keys to container + #Copy secret keys to container WPE_SSHG_KEY_PRIVATE_PATH="${SSH_PATH}/wpe_id_rsa" if [ "${CICD_VENDOR}" == "wpe_bb" ]; then @@ -75,7 +76,7 @@ setup_ssh_dir() { fi chmod 600 "${WPE_SSHG_KEY_PRIVATE_PATH}" - #establish knownhosts + #establish knownhosts KNOWN_HOSTS_PATH="${SSH_PATH}/known_hosts" ssh-keyscan -t rsa "${WPE_SSH_HOST}" >> "${KNOWN_HOSTS_PATH}" chmod 644 "${KNOWN_HOSTS_PATH}" @@ -92,7 +93,7 @@ check_lint() { fi done echo "PHP Lint Successful! No errors detected!" - else + else echo "Skipping PHP Linting." fi } @@ -107,14 +108,20 @@ check_cache() { } sync_files() { + # Generate the excludes list before using the output with rsync + local source_exclude_from; source_exclude_from="$(generate_source_exclude_from)" + local remote_excludes; remote_excludes="$(generate_remote_excludes)" + #create multiplex connection ssh -nNf -v -i "${WPE_SSHG_KEY_PRIVATE_PATH}" -o StrictHostKeyChecking=no -o ControlMaster=yes -o ControlPath="$SSH_PATH/ctl/%C" "$WPE_FULL_HOST" echo "!!! MULTIPLEX SSH CONNECTION ESTABLISHED !!!" set -x - rsync --rsh="ssh -v -p 22 -i ${WPE_SSHG_KEY_PRIVATE_PATH} -o StrictHostKeyChecking=no -o 'ControlPath=$SSH_PATH/ctl/%C'" "${FLAGS_ARRAY[@]}" --exclude-from='/exclude.txt' --chmod=D775,F664 "${SRC_PATH}" "${WPE_DESTINATION}" + rsync --rsh="ssh -v -p 22 -i ${WPE_SSHG_KEY_PRIVATE_PATH} -o StrictHostKeyChecking=no -o 'ControlPath=$SSH_PATH/ctl/%C'" "${FLAGS_ARRAY[@]}" \ + --exclude-from=<( { { set +x; } 2>/dev/null; echo "$source_exclude_from"; } ) --rsync-path="rsync $remote_excludes" \ + --chmod=D775,F664 "${SRC_PATH}" "${WPE_DESTINATION}" set +x - + if [[ -n ${SCRIPT} || -n ${CACHE_CLEAR} ]]; then if [[ -n ${SCRIPT} ]]; then diff --git a/exclude.txt b/exclude.txt deleted file mode 100644 index 9d376d4..0000000 --- a/exclude.txt +++ /dev/null @@ -1,63 +0,0 @@ -# Version Control -# NOTE: -# WP Engine does not support server side versioning so hosting any version control -# on the server would not be advantageous. - -*~ -.git -.github -.gitignore -.DS_Store -.svn -.cvs -*.bak -*.swp -Thumbs.db - -# WordPress specific files -# NOTE: -# These files are excluded from the deploy so as to prevent unwanted errors from occurring, -# such as accidentally deploying a local version of wp-config.php or accidentally deleting -# wp-content/uploads/ if a --delete flag is passed while deploying root. Most paths here -# are ingnored in the WPE sample .gitignore per best practice. -wp-config.php -wp-content/uploads/ -wp-content/blogs.dir/ -wp-content/upgrade/* -wp-content/backup-db/* -wp-content/advanced-cache.php -wp-content/wp-cache-config.php -wp-content/cache/* -wp-content/cache/supercache/* - -# WP Engine specific files -# NOTE: -# These files are specific to running a WordPress site at WP Engine and would -# likely result in a broken production site if modified in production (in -# fact, permissions would prevent modification for many of these files). While -# some of these files (such as those in /_wpeprivate) would be extremely large -# and completely useless in the context of local WordPress development, others -# (such as some of the WP Engine managed plugins) might be useful in rare -# circumstances to have as a reference for debugging purposes. -.smushit-status -.gitattributes -.wpe-devkit/ -.wpengine-conf/ -_wpeprivate -wp-content/object-cache.php -wp-content/mu-plugins/mu-plugin.php -wp-content/mu-plugins/slt-force-strong-passwords.php -wp-content/mu-plugins/wpengine-security-auditor.php -wp-content/mu-plugins/stop-long-comments.php -wp-content/mu-plugins/force-strong-passwords* -wp-content/mu-plugins/wpengine-common* -wp-content/mu-plugins/wpe-wp-sign-on-plugin* -wp-content/mu-plugins/wpe-elasticpress-autosuggest-logger* -wp-content/mu-plugins/wpe-cache-plugin* -wp-content/mu-plugins/wp-cache-memcached* -wp-content/drop-ins/ -wp-content/drop-ins/wp-cache-memcached* -wp-content/mysql.sql - -# Local specific -wp-content/mu-plugins/local-by-flywheel-live-link-helper.php diff --git a/tests/common.sh b/tests/common.sh deleted file mode 100644 index 154e436..0000000 --- a/tests/common.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -GREEN='\033[0;32m' -RED='\033[0;31m' -NC='\033[0m' # No Color diff --git a/tests/fixtures/excludes/remote_excludes.txt b/tests/fixtures/excludes/remote_excludes.txt new file mode 100644 index 0000000..24750a2 --- /dev/null +++ b/tests/fixtures/excludes/remote_excludes.txt @@ -0,0 +1 @@ +--exclude='wp-config.php' --exclude='.smushit-status' --exclude='.gitattributes' --exclude='.wpe-devkit/' --exclude='.wpengine-conf/' --exclude='_wpeprivate' --exclude='/wp-content/uploads/' --exclude='/wp-content/blogs.dir/' --exclude='/wp-content/upgrade/*' --exclude='/wp-content/backup-db/*' --exclude='/wp-content/advanced-cache.php' --exclude='/wp-content/wp-cache-config.php' --exclude='/wp-content/cache/*' --exclude='/wp-content/cache/supercache/*' --exclude='/wp-content/object-cache.php' --exclude='/wp-content/drop-ins/' --exclude='/wp-content/drop-ins/wp-cache-memcached*' --exclude='/wp-content/mysql.sql' --exclude='/wp-content/mu-plugins/mu-plugin.php' --exclude='/wp-content/mu-plugins/slt-force-strong-passwords.php' --exclude='/wp-content/mu-plugins/wpengine-security-auditor.php' --exclude='/wp-content/mu-plugins/stop-long-comments.php' --exclude='/wp-content/mu-plugins/force-strong-passwords*' --exclude='/wp-content/mu-plugins/wpengine-common*' --exclude='/wp-content/mu-plugins/wpe-wp-sign-on-plugin*' --exclude='/wp-content/mu-plugins/wpe-elasticpress-autosuggest-logger*' --exclude='/wp-content/mu-plugins/wpe-cache-plugin*' --exclude='/wp-content/mu-plugins/wp-cache-memcached*' --exclude='/wp-content/mu-plugins/local-by-flywheel-live-link-helper.php' \ No newline at end of file diff --git a/tests/fixtures/excludes/source_exclude_from.txt b/tests/fixtures/excludes/source_exclude_from.txt new file mode 100644 index 0000000..69a9d8a --- /dev/null +++ b/tests/fixtures/excludes/source_exclude_from.txt @@ -0,0 +1,48 @@ +# Version Control +*~ +.git +.github +.gitignore +.DS_Store +.svn +.cvs +*.bak +*.swp +Thumbs.db + +# WordPress specific files +wp-config.php + +# WP Engine specific files and directories +.smushit-status +.gitattributes +.wpe-devkit/ +.wpengine-conf/ +_wpeprivate + +### Dynamic file and directory exclusions +/wp-content/uploads/ +/wp-content/blogs.dir/ +/wp-content/upgrade/* +/wp-content/backup-db/* +/wp-content/advanced-cache.php +/wp-content/wp-cache-config.php +/wp-content/cache/* +/wp-content/cache/supercache/* +/wp-content/object-cache.php +/wp-content/drop-ins/ +/wp-content/drop-ins/wp-cache-memcached* +/wp-content/mysql.sql + +### Dynamic mu-plugin file and directory exclusions +/wp-content/mu-plugins/mu-plugin.php +/wp-content/mu-plugins/slt-force-strong-passwords.php +/wp-content/mu-plugins/wpengine-security-auditor.php +/wp-content/mu-plugins/stop-long-comments.php +/wp-content/mu-plugins/force-strong-passwords* +/wp-content/mu-plugins/wpengine-common* +/wp-content/mu-plugins/wpe-wp-sign-on-plugin* +/wp-content/mu-plugins/wpe-elasticpress-autosuggest-logger* +/wp-content/mu-plugins/wpe-cache-plugin* +/wp-content/mu-plugins/wp-cache-memcached* +/wp-content/mu-plugins/local-by-flywheel-live-link-helper.php \ No newline at end of file diff --git a/tests/fixtures/remote/wp-content/mu-plugins/mu-plugin.php b/tests/fixtures/remote/wp-content/mu-plugins/mu-plugin.php new file mode 100644 index 0000000..ad29275 --- /dev/null +++ b/tests/fixtures/remote/wp-content/mu-plugins/mu-plugin.php @@ -0,0 +1,14 @@ + true + )); + } \ No newline at end of file diff --git a/tests/fixtures/remote/wp-content/object-cache.php b/tests/fixtures/remote/wp-content/object-cache.php new file mode 100644 index 0000000..7c176d8 --- /dev/null +++ b/tests/fixtures/remote/wp-content/object-cache.php @@ -0,0 +1,14 @@ + true + )); + } \ No newline at end of file diff --git a/tests/fixtures/src/wp-content/mu-plugins/mu-plugin.php b/tests/fixtures/src/wp-content/mu-plugins/mu-plugin.php new file mode 100644 index 0000000..ad29275 --- /dev/null +++ b/tests/fixtures/src/wp-content/mu-plugins/mu-plugin.php @@ -0,0 +1,14 @@ + true + )); + } \ No newline at end of file diff --git a/tests/fixtures/src/wp-content/plugins/cache/object-cache.php b/tests/fixtures/src/wp-content/plugins/cache/object-cache.php new file mode 100644 index 0000000..59e83d4 --- /dev/null +++ b/tests/fixtures/src/wp-content/plugins/cache/object-cache.php @@ -0,0 +1,21 @@ + true + )); + } \ No newline at end of file diff --git a/tests/fixtures/src/wp-content/plugins/test-plugin/test-plugin.php b/tests/fixtures/src/wp-content/plugins/test-plugin/test-plugin.php new file mode 100644 index 0000000..e47c32e --- /dev/null +++ b/tests/fixtures/src/wp-content/plugins/test-plugin/test-plugin.php @@ -0,0 +1,14 @@ + true + )); + } \ No newline at end of file diff --git a/tests/helpers/change_working_dir.sh b/tests/helpers/change_working_dir.sh new file mode 100644 index 0000000..40e32ff --- /dev/null +++ b/tests/helpers/change_working_dir.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +set -e + +# Change the working directory to the target directory and run the given command +change_working_dir() { + local target_dir=$1 + local orig_dir; orig_dir=$(pwd) + shift + + ( + cd "$target_dir" || exit 1 + # Restore to the original directory after the command is run + trap 'cd "$orig_dir"' EXIT + "$@" + ) +} diff --git a/tests/helpers/common.sh b/tests/helpers/common.sh new file mode 100644 index 0000000..143fb80 --- /dev/null +++ b/tests/helpers/common.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +export GREEN='\033[0;32m' +export RED='\033[0;31m' +export BLUE='\033[0;34m' +export NC='\033[0m' # No Color diff --git a/tests/helpers/log_debug.sh b/tests/helpers/log_debug.sh new file mode 100644 index 0000000..addc19d --- /dev/null +++ b/tests/helpers/log_debug.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -e + +log_debug() { + local subject; subject="$1" + local message; message="$2" + + cat < /dev/null + rsync $FLAGS --dry-run "$FIXTURE_PATH" . > /dev/null if [ $? -eq 0 ]; then echo -e "${GREEN}test_flags_no_quotes: Success${NC}" else @@ -29,7 +30,7 @@ test_flags_no_quotes() { } test_flags_double_quotes() { - rsync "$FLAGS" --dry-run "$SCRIPT_DIR"/data . > /dev/null + rsync "$FLAGS" --dry-run "$FIXTURE_PATH" . > /dev/null if [ $? -eq 0 ]; then echo -e "${GREEN}test_flags_double_quotes: Success${NC}" else @@ -38,7 +39,7 @@ test_flags_double_quotes() { } test_flags_array() { - rsync "${FLAGS_ARRAY[@]}" --dry-run "$SCRIPT_DIR"/data . > /dev/null + rsync "${FLAGS_ARRAY[@]}" --dry-run "$FIXTURE_PATH" . > /dev/null if [ $? -eq 0 ]; then echo -e "${GREEN}test_flags_array: Success${NC}" else diff --git a/tests/test_functions.sh b/tests/test_functions.sh index 3f1c10c..41f6af2 100755 --- a/tests/test_functions.sh +++ b/tests/test_functions.sh @@ -3,8 +3,8 @@ # Get the directory of the current script SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/common.sh" -source "${SCRIPT_DIR}/../functions.sh" +source "${SCRIPT_DIR}/helpers/common.sh" +source "${SCRIPT_DIR}/../utils/functions.sh" # First argument represents the value of the FLAGS variable. # The rest of the arguments represent the expected values of the FLAGS_ARRAY. diff --git a/tests/test_generate_path_excludes.sh b/tests/test_generate_path_excludes.sh new file mode 100755 index 0000000..2f00f2a --- /dev/null +++ b/tests/test_generate_path_excludes.sh @@ -0,0 +1,193 @@ +#!/bin/bash + +set -e + +# Get the directory of the current script +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +source "${SCRIPT_DIR}/helpers/common.sh" +source "${SCRIPT_DIR}/helpers/change_working_dir.sh" +source "${SCRIPT_DIR}/../utils/generate_path_excludes.sh" + +test_determine_source_exclude_paths() { + run_test_determine_source_exclude_paths() { + export SRC_PATH=$1 + local expected_base_path=$2 + local expected_mu_dir_path=$3 + + { + IFS= read -r base_path || base_path="" + IFS= read -r mu_dir_path || mu_dir_path="" + } <<< "$(determine_source_exclude_paths)" + + if [[ "${base_path}" != "${expected_base_path}" || "${mu_dir_path}" != "${expected_mu_dir_path}" ]]; then + read -r mounted_source_path _ <<< "$(print_mount_paths "tests/fixtures/src")" + echo -e "${RED}Test failed for SRC_PATH='${SRC_PATH}': expected '$expected_base_path, $expected_mu_dir_path', got '$base_path, $mu_dir_path'.${NC}" + echo -e "${BLUE}INFO: mounted_source_path='${mounted_source_path}, pwd=$(pwd)'${NC}" + exit 1 + fi + } + + mount_from_root() { + echo -e "${BLUE}INFO: mounting from /site...${NC}" + + # Mock the print_mount_paths function + print_mount_paths() { + # shellcheck disable=SC2317 + printf "/path/to/site /site" + } + + # Workdir -> src directory + change_working_dir "tests/fixtures/src" run_test_determine_source_exclude_paths "wp-content" "/wp-content/" "/wp-content/mu-plugins/" + change_working_dir "tests/fixtures/src" run_test_determine_source_exclude_paths "wp-content/" "/" "/mu-plugins/" + change_working_dir "tests/fixtures/src" run_test_determine_source_exclude_paths "wp-content/mu-plugins" "/" "/mu-plugins/" + change_working_dir "tests/fixtures/src" run_test_determine_source_exclude_paths "wp-content/mu-plugins/" "" "/" + change_working_dir "tests/fixtures/src" run_test_determine_source_exclude_paths "wp-content/plugins" "" "" + change_working_dir "tests/fixtures/src" run_test_determine_source_exclude_paths "wp-content/plugins/" "" "" + change_working_dir "tests/fixtures/src" run_test_determine_source_exclude_paths "." "/wp-content/" "/wp-content/mu-plugins/" + + # Workdir -> wp-content directory + change_working_dir "tests/fixtures/src/wp-content" run_test_determine_source_exclude_paths "mu-plugins" "/" "/mu-plugins/" + change_working_dir "tests/fixtures/src/wp-content" run_test_determine_source_exclude_paths "mu-plugins/" "" "/" + change_working_dir "tests/fixtures/src/wp-content" run_test_determine_source_exclude_paths "plugins" "" "" + change_working_dir "tests/fixtures/src/wp-content" run_test_determine_source_exclude_paths "plugins/" "" "" + change_working_dir "tests/fixtures/src/wp-content" run_test_determine_source_exclude_paths "." "/" "/mu-plugins/" + } + + mount_from_wp_content() { + echo -e "${BLUE}INFO: mounting from /site/wp-content...${NC}" + + # Mock the print_mount_paths function + print_mount_paths() { + # shellcheck disable=SC2317 + printf "/path/to/site/wp-content /site" + } + + # Workdir -> src directory + change_working_dir "tests/fixtures/src" run_test_determine_source_exclude_paths "." "/" "/mu-plugins/" + + # Workdir -> wp-content directory + change_working_dir "tests/fixtures/src/wp-content" run_test_determine_source_exclude_paths "." "/" "/mu-plugins/" + } + + mount_from_mu_plugins() { + echo -e "${BLUE}INFO: mounting from /site/mu-plugins...${NC}" + + # Mock the print_mount_paths function + print_mount_paths() { + # shellcheck disable=SC2317 + printf "/path/to/site/wp-content/mu-plugins /site" + } + + # Workdir -> src directory + change_working_dir "tests/fixtures/src" run_test_determine_source_exclude_paths "." "" "/" + + # Workdir -> mu-plugins directory + change_working_dir "tests/fixtures/src/wp-content/mu-plugins" run_test_determine_source_exclude_paths "." "" "/" + } + + mount_from_plugins() { + echo -e "${BLUE}INFO: mounting from /site/plugins...${NC}" + + # Mock the print_mount_paths function + print_mount_paths() { + # shellcheck disable=SC2317 + printf "/path/to/site/wp-content/plugins /site" + } + + # Workdir -> src directory + change_working_dir "tests/fixtures/src" run_test_determine_source_exclude_paths "." "" "" + + # Workdir -> plugins directory + change_working_dir "tests/fixtures/src/wp-content/plugins" run_test_determine_source_exclude_paths "." "" "" + } + + mount_from_root + mount_from_wp_content + mount_from_mu_plugins + mount_from_plugins + + echo -e "${GREEN}All tests passed for determining the source excludes path.${NC}" +} + +test_determine_remote_exclude_paths() { + run_test_determine_remote_exclude_paths() { + export REMOTE_PATH=$1 + local expected_base_path=$2 + local expected_mu_dir_path=$3 + + { + IFS= read -r base_path || base_path="" + IFS= read -r mu_dir_path || mu_dir_path="" + } <<< "$(determine_remote_exclude_paths)" + + if [[ "${base_path}" != "${expected_base_path}" || "${mu_dir_path}" != "${expected_mu_dir_path}" ]]; then + echo -e "${RED}Test failed for REMOTE_PATH='${SRC_PATH}': expected '$expected_base_path, $expected_mu_dir_path', got '$base_path, $mu_dir_path'.${NC}" + exit 1 + fi + } + + run_test_determine_remote_exclude_paths "" "/wp-content/" "/wp-content/mu-plugins/" + run_test_determine_remote_exclude_paths "wp-content" "/" "/mu-plugins/" + run_test_determine_remote_exclude_paths "wp-content/" "/" "/mu-plugins/" + run_test_determine_remote_exclude_paths "wp-content/mu-plugins" "" "/" + run_test_determine_remote_exclude_paths "wp-content/mu-plugins/" "" "/" + run_test_determine_remote_exclude_paths "wp-content/plugins/" "" "" + + echo -e "${GREEN}All tests passed for determining the remote excludes path.${NC}" +} + +test_generate_source_exclude_from() { + export SRC_PATH="." + local output + local expected_output + + # Mock the print_mount_paths function + print_mount_paths() { + # shellcheck disable=SC2317 + printf "/path/to/site /site" + } + + output=$(generate_source_exclude_from) + expected_output=$(cat "tests/fixtures/excludes/source_exclude_from.txt") + + if [[ "$output" != "$expected_output" ]]; then + echo -e "${RED}Test failed: generated output does not match expected output.${NC}" + echo -e "${BLUE}Generated output:${NC}" + echo "$output" + echo -e "${BLUE}Expected output:${NC}" + echo "$expected_output" + exit 1 + fi + + echo -e "${GREEN}Test passed for source exclude from: generated output matches expected output.${NC}" +} + +test_generate_remote_excludes() { + export REMOTE_PATH="" + local output + local expected_output + + output=$(generate_remote_excludes) + expected_output=$(cat "tests/fixtures/excludes/remote_excludes.txt") + + if [[ "$output" != "$expected_output" ]]; then + echo -e "${RED}Test failed: generated output does not match expected output.${NC}" + echo -e "${BLUE}Generated output:${NC}" + echo "$output" + echo -e "${BLUE}Expected output:${NC}" + echo "$expected_output" + exit 1 + fi + + echo -e "${GREEN}Test passed for source exclude from: generated output matches expected output.${NC}" +} + +main() { + test_determine_source_exclude_paths + test_determine_remote_exclude_paths + test_generate_source_exclude_from + test_generate_remote_excludes +} + +main diff --git a/functions.sh b/utils/functions.sh similarity index 100% rename from functions.sh rename to utils/functions.sh diff --git a/utils/generate_path_excludes.sh b/utils/generate_path_excludes.sh new file mode 100644 index 0000000..104a6aa --- /dev/null +++ b/utils/generate_path_excludes.sh @@ -0,0 +1,214 @@ +#!/bin/bash + +set -e +shopt -s extglob + +# Dynamic Excludes +# +# This script generates two lists of files to exclude; one for the source, and one for the remote. +# +# Notes about excluded files: +# +# Version Control +# NOTE: +# WP Engine does not support server side versioning so hosting any version control +# on the server would not be advantageous. +# +# WordPress specific files +# NOTE: +# These files are excluded from the deploy so as to prevent unwanted errors from occurring, +# such as accidentally deploying a local version of wp-config.php or accidentally deleting +# wp-content/uploads/ if a --delete flag is passed while deploying root. Most paths here +# are ignored in the WPE sample .gitignore per best practice. +# +# WP Engine specific files +# NOTE: +# These files are specific to running a WordPress site at WP Engine and would +# likely result in a broken production site if modified in production (in +# fact, permissions would prevent modification for many of these files). While +# some of these files (such as those in /_wpeprivate) would be extremely large +# and completely useless in the context of local WordPress development, others +# (such as some of the WP Engine managed plugins) might be useful in rare +# circumstances to have as a reference for debugging purposes. + +# Get the directory of the current script +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Source the print_mount_paths.sh file relative to the current script's location +source "${SCRIPT_DIR}/print_mount_paths.sh" + +# Determine the source paths to exclude from the deployment +determine_source_exclude_paths() { + local base_path + local mu_dir_path + + read -r mounted_source_path _ <<< "$(print_mount_paths "wp-content")" + + # elif [[ "$(pwd)" == @(*wp-content|*mu-plugins) ]] || [[ "$(pwd)" == "$base_mount_path" && -n "${mounted_source_path}" ]]; then + + if [[ -n "${SRC_PATH}" && "${SRC_PATH}" != '.' ]]; then + case "${SRC_PATH}" in + wp-content ) + base_path="/wp-content/" + mu_dir_path="/wp-content/mu-plugins/" + ;; + wp-content/ | *mu-plugins ) + base_path="/" + mu_dir_path="/mu-plugins/" + ;; + *mu-plugins/ ) + mu_dir_path="/" + ;; + esac + elif [[ "$(pwd)" != *wp-content* && "${mounted_source_path}" != *wp-content* ]]; then + base_path="/wp-content/" + mu_dir_path="/wp-content/mu-plugins/" + else + # Iterate over the possible paths and break when a match is found + # !!! Ordering is important for the switch cases !!! + for value in "$(pwd)" "$mounted_source_path"; do + case "$value" in + *wp-content ) + base_path="/" + mu_dir_path="/mu-plugins/" + break + ;; + *mu-plugins ) + mu_dir_path="/" + break + ;; + esac + done + fi + + printf "%s\n%s\n" "$base_path" "$mu_dir_path" +} + +determine_remote_exclude_paths() { + local base_path + local mu_dir_path + + if [[ -z "${REMOTE_PATH}" ]]; then + base_path="/wp-content/" + mu_dir_path="/wp-content/mu-plugins/" + else + case "$REMOTE_PATH" in + wp-content?(/) ) + base_path="/" + mu_dir_path="/mu-plugins/" + ;; + wp-content/mu-plugins?(/) ) + mu_dir_path="/" + ;; + esac + fi + + printf "%s\n%s\n" "$base_path" "$mu_dir_path" +} + +# Generate the dynamic list of files to exclude from the deployment +print_dynamic_excludes() { + local func=$1 + local delimiter=$2 + local output="" + + { + IFS= read -r base_path + IFS= read -r mu_dir_path + } <<< "$($func)" + + local wp_content_exclude_paths=( + # WordPress specific files and directories + uploads/ + blogs.dir/ + upgrade/* + backup-db/* + advanced-cache.php + wp-cache-config.php + cache/* + cache/supercache/* + # WP Engine specific files and directories + object-cache.php + drop-ins/ + drop-ins/wp-cache-memcached* + mysql.sql + ) + + local mu_plugin_exclude_paths=( + # WP Engine specific files and directories + mu-plugin.php + slt-force-strong-passwords.php + wpengine-security-auditor.php + stop-long-comments.php + force-strong-passwords* + wpengine-common* + wpe-wp-sign-on-plugin* + wpe-elasticpress-autosuggest-logger* + wpe-cache-plugin* + wp-cache-memcached* + # Local specific files + local-by-flywheel-live-link-helper.php + ) + + if [[ -n "$base_path" ]]; then + output+="${delimiter}### Dynamic file and directory exclusions${delimiter}" + for path in "${wp_content_exclude_paths[@]}"; do + output+="${base_path}$path${delimiter}" + done + fi + + if [[ -n "$mu_dir_path" ]]; then + output+="${delimiter}### Dynamic mu-plugin file and directory exclusions${delimiter}" + for path in "${mu_plugin_exclude_paths[@]}"; do + output+="${mu_dir_path}$path${delimiter}" + done + fi + + echo -e "$output" +} + +generate_source_exclude_from() { + local dynamic_excludes; dynamic_excludes=$(print_dynamic_excludes determine_source_exclude_paths "\n") + + cat << EOF +# Version Control +*~ +.git +.github +.gitignore +.DS_Store +.svn +.cvs +*.bak +*.swp +Thumbs.db + +# WordPress specific files +wp-config.php + +# WP Engine specific files and directories +.smushit-status +.gitattributes +.wpe-devkit/ +.wpengine-conf/ +_wpeprivate +$(echo -e "$dynamic_excludes") +EOF +} + +generate_remote_excludes() { + local dynamic_excludes; dynamic_excludes=$(print_dynamic_excludes determine_remote_exclude_paths "\n") + + cat << EOF | grep -Ev '^\s*(#|$)|\s+#' | awk '{printf "--exclude='\''%s'\'' ", $0}' | sed 's/[[:space:]]*$//' +# WordPress specific files +wp-config.php + +# WP Engine specific files and directories +.smushit-status +.gitattributes +.wpe-devkit/ +.wpengine-conf/ +_wpeprivate +$(echo -e "$dynamic_excludes") +EOF +} diff --git a/utils/print_mount_paths.sh b/utils/print_mount_paths.sh new file mode 100644 index 0000000..426616c --- /dev/null +++ b/utils/print_mount_paths.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +set -e + +print_mount_paths() { + local mount_path=$1 + # Print the mounted source path and base mount path, listed in positions 4 and 5 of the resulting string + grep "$mount_path" /proc/self/mountinfo | awk '{print $4, $5}' +}