From 5ad0641203f57baea456a55d60c50df82d0d23ef Mon Sep 17 00:00:00 2001 From: Michael Day Date: Mon, 2 Dec 2024 22:06:53 -0600 Subject: [PATCH 01/15] [CICD-761] Generate excludes list dynamically Outputs the list to stdout, which is called with process substitution in the initial rsync command. Also disables debug mode for the block calling the generate function. This prevents unwanted debug output (specific to the excludes list generation) from being shown. --- Dockerfile | 2 +- Makefile | 1 + entrypoint.sh | 10 ++- exclude-from.sh | 103 ++++++++++++++++++++++ exclude.txt => tests/test_exclude_from.sh | 56 ++++++++++-- 5 files changed, 161 insertions(+), 11 deletions(-) create mode 100644 exclude-from.sh rename exclude.txt => tests/test_exclude_from.sh (53%) mode change 100644 => 100755 diff --git a/Dockerfile b/Dockerfile index d2939ed..aebb08c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,6 +8,6 @@ RUN apk update \ && rm -rf /var/cache/apk/* # Add entrypoint and excludes ADD functions.sh /functions.sh +ADD exclude-from.sh /exclude-from.sh ADD entrypoint.sh /entrypoint.sh -ADD exclude.txt /exclude.txt ENTRYPOINT ["/entrypoint.sh"] diff --git a/Makefile b/Makefile index 6206910..deb8c19 100644 --- a/Makefile +++ b/Makefile @@ -24,3 +24,4 @@ version: build test: ./tests/test_functions.sh + ./tests/test_exclude_from.sh diff --git a/entrypoint.sh b/entrypoint.sh index 90edf79..a177dc1 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 the functions.sh and exclude-from.sh files relative to the current script's location source "${SCRIPT_DIR}/functions.sh" +source "${SCRIPT_DIR}/exclude-from.sh" validate() { # mandatory params @@ -107,14 +108,17 @@ check_cache() { } sync_files() { + # Generate the excludes list before using the output with rsync + local exclude_from; exclude_from="$(generate_exclude_from)" + #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 "$exclude_from"; } ) --chmod=D775,F664 "${SRC_PATH}" "${WPE_DESTINATION}" set +x - + if [[ -n ${SCRIPT} || -n ${CACHE_CLEAR} ]]; then if [[ -n ${SCRIPT} ]]; then diff --git a/exclude-from.sh b/exclude-from.sh new file mode 100644 index 0000000..43cf55d --- /dev/null +++ b/exclude-from.sh @@ -0,0 +1,103 @@ +#!/bin/bash + +set -e + +# Determine the paths to exclude from the deployment +# +# Paths are inferred from the REMOTE_PATH environment variable. +# +# The function returns two paths: the relative base path for prefixing excluded files, +# and the relative mu-plugins path. +determine_exclude_paths() { + local base_path="wp-content/" + local mu_dir_path="wp-content/mu-plugins/" + + case "$REMOTE_PATH" in + "wp-content") + base_path="" + mu_dir_path="mu-plugins/" + ;; + "wp-content/mu-plugins") + base_path="" + mu_dir_path="" + ;; + esac + + printf "%s\n%s\n" "$base_path" "$mu_dir_path" +} + +# Generate a list of files to exclude from the deployment +# +# The list is output to stdout, which can be used as an argument to --exclude-from. +generate_exclude_from() { + { + IFS= read -r base_path + IFS= read -r mu_dir_path + } <<< "$(determine_exclude_paths)" + + cat << EOF +# 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 ignored in the WPE sample .gitignore per best practice. +wp-config.php +${base_path}uploads/ +${base_path}blogs.dir/ +${base_path}upgrade/* +${base_path}backup-db/* +${base_path}advanced-cache.php +${base_path}wp-cache-config.php +${base_path}cache/* +${base_path}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 +${base_path}object-cache.php +${mu_dir_path}mu-plugin.php +${mu_dir_path}slt-force-strong-passwords.php +${mu_dir_path}wpengine-security-auditor.php +${mu_dir_path}stop-long-comments.php +${mu_dir_path}force-strong-passwords* +${mu_dir_path}wpengine-common* +${mu_dir_path}wpe-wp-sign-on-plugin* +${mu_dir_path}wpe-elasticpress-autosuggest-logger* +${mu_dir_path}wpe-cache-plugin* +${mu_dir_path}wp-cache-memcached* +${base_path}drop-ins/ +${base_path}drop-ins/wp-cache-memcached* +${base_path}mysql.sql + +# Local specific +${mu_dir_path}local-by-flywheel-live-link-helper.php +EOF +} diff --git a/exclude.txt b/tests/test_exclude_from.sh old mode 100644 new mode 100755 similarity index 53% rename from exclude.txt rename to tests/test_exclude_from.sh index 9d376d4..2ce8439 --- a/exclude.txt +++ b/tests/test_exclude_from.sh @@ -1,7 +1,17 @@ -# Version Control -# NOTE: +#!/bin/bash + +# Get the directory of the current script +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +source "${SCRIPT_DIR}/common.sh" +source "${SCRIPT_DIR}/../exclude-from.sh" + +test_generate_exclude_from() { + export REMOTE_PATH="" + expected_output="# Version Control +# NOTE: # WP Engine does not support server side versioning so hosting any version control -# on the server would not be advantageous. +# on the server would not be advantageous. *~ .git @@ -16,10 +26,10 @@ Thumbs.db # WordPress specific files # NOTE: -# These files are excluded from the deploy so as to prevent unwanted errors from occurring, +# 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-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-config.php wp-content/uploads/ wp-content/blogs.dir/ @@ -60,4 +70,36 @@ wp-content/drop-ins/wp-cache-memcached* wp-content/mysql.sql # Local specific -wp-content/mu-plugins/local-by-flywheel-live-link-helper.php +wp-content/mu-plugins/local-by-flywheel-live-link-helper.php" + + # Show the diff between the generated list and the expected output + if diff <(generate_exclude_from) <(echo "$expected_output"); then + echo -e "${GREEN}Test passed for generating excludes list with REMOTE_PATH='${REMOTE_PATH}'.${NC}" + else + echo -e "${RED}Test failed for generating excludes list with REMOTE_PATH='${REMOTE_PATH}'.${NC}" + fi +} + +test_determine_exclude_paths() { + export REMOTE_PATH="wp-content" + expected_base_path="" + expected_mu_dir_path="mu-plugins/" + + # Run the function and capture the output + output=$(determine_exclude_paths) + { + IFS= read -r base_path + IFS= read -r mu_dir_path + } <<< "$output" + + # Check the assigned paths + if [[ "$base_path" == "$expected_base_path" && "$mu_dir_path" == "$expected_mu_dir_path" ]]; then + echo -e "${GREEN}Test passed for updating excludes list file paths with REMOTE_PATH='${REMOTE_PATH}'.${NC}" + else + echo -e "${RED}Test failed for updating excludes list file paths with REMOTE_PATH='${REMOTE_PATH}'.${NC}" + fi +} + +# Run the tests +test_generate_exclude_from +test_determine_exclude_paths From 9f7ae31b19d8c5f909fee9b1646aa015a5e17e4d Mon Sep 17 00:00:00 2001 From: Michael Day Date: Tue, 3 Dec 2024 22:47:29 -0600 Subject: [PATCH 02/15] [CICD-761] Add tests for verifying rsync file inclusion and exclusion Moves the test-plugin (used by e2e test) to a wp-content dir for reuse as a fixture in unit testing. --- .github/workflows/e2e-deploy.yml | 4 +- tests/common.sh | 1 + .../data/wp-content/mu-plugins/mu-plugin.php | 15 ++++++++ .../plugins/test-plugin/test-plugin.php | 2 +- tests/test_exclude_from.sh | 37 +++++++++++++++++-- tests/test_flag_formats.sh | 7 ++-- 6 files changed, 57 insertions(+), 9 deletions(-) create mode 100644 tests/data/wp-content/mu-plugins/mu-plugin.php rename tests/data/{ => wp-content}/plugins/test-plugin/test-plugin.php (80%) diff --git a/.github/workflows/e2e-deploy.yml b/.github/workflows/e2e-deploy.yml index 4176800..45e807d 100644 --- a/.github/workflows/e2e-deploy.yml +++ b/.github/workflows/e2e-deploy.yml @@ -19,7 +19,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Bump test plugin version number - run: sed -i 's/0.0.1/0.0.2/' tests/data/plugins/test-plugin/test-plugin.php + run: sed -i 's/0.0.1/0.0.2/' tests/data/wp-content/plugins/test-plugin/test-plugin.php - name: Deploy to WP Engine uses: ./.github/actions/deploy with: @@ -27,7 +27,7 @@ jobs: WPE_SSHG_KEY_PRIVATE: ${{ secrets.WPE_SSHG_KEY_PRIVATE }} WPE_ENV: sitedeploye2e # Deploy Options - SRC_PATH: "tests/data/plugins/test-plugin" + SRC_PATH: "tests/data/wp-content/plugins/test-plugin" REMOTE_PATH: "wp-content/plugins/" PHP_LINT: true FLAGS: '-r --backup --backup-dir=/tmp --itemize-changes' diff --git a/tests/common.sh b/tests/common.sh index 154e436..f6f0917 100644 --- a/tests/common.sh +++ b/tests/common.sh @@ -2,4 +2,5 @@ GREEN='\033[0;32m' RED='\033[0;31m' +BLUE='\033[0;34m' NC='\033[0m' # No Color diff --git a/tests/data/wp-content/mu-plugins/mu-plugin.php b/tests/data/wp-content/mu-plugins/mu-plugin.php new file mode 100644 index 0000000..fd82676 --- /dev/null +++ b/tests/data/wp-content/mu-plugins/mu-plugin.php @@ -0,0 +1,15 @@ + true + )); + } \ No newline at end of file diff --git a/tests/data/plugins/test-plugin/test-plugin.php b/tests/data/wp-content/plugins/test-plugin/test-plugin.php similarity index 80% rename from tests/data/plugins/test-plugin/test-plugin.php rename to tests/data/wp-content/plugins/test-plugin/test-plugin.php index aff8bca..3c47e08 100644 --- a/tests/data/plugins/test-plugin/test-plugin.php +++ b/tests/data/wp-content/plugins/test-plugin/test-plugin.php @@ -1,6 +1,6 @@ /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 From 3351cbde8639e4b6572b5e9d7c9370f8c37096b7 Mon Sep 17 00:00:00 2001 From: Michael Day Date: Tue, 3 Dec 2024 22:56:52 -0600 Subject: [PATCH 03/15] [CICD-761] Explicitly export color globals Resolves a warning about unused variables in the common file. --- tests/common.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/common.sh b/tests/common.sh index f6f0917..143fb80 100644 --- a/tests/common.sh +++ b/tests/common.sh @@ -1,6 +1,6 @@ #!/bin/bash -GREEN='\033[0;32m' -RED='\033[0;31m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color +export GREEN='\033[0;32m' +export RED='\033[0;31m' +export BLUE='\033[0;34m' +export NC='\033[0m' # No Color From 5abebbf322af6a3317f144adf294178b29f77537 Mon Sep 17 00:00:00 2001 From: Michael Day Date: Thu, 5 Dec 2024 11:10:16 -0600 Subject: [PATCH 04/15] [CICD-761] Bump version with patch update --- .changeset/stale-flowers-hide.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/stale-flowers-hide.md 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 From 2c6cf5c289bd56c01051870465240deac30d2760 Mon Sep 17 00:00:00 2001 From: Michael Day Date: Thu, 12 Dec 2024 00:17:00 -0600 Subject: [PATCH 05/15] [CICD-761] Update the test fixtures dir structure --- .../plugins/test-plugin/test-plugin.php | 2 +- .../wp-content/mu-plugins/mu-plugin.php | 3 +-- .../remote/wp-content/object-cache.php | 14 +++++++++++++ .../src/wp-content/mu-plugins/mu-plugin.php | 14 +++++++++++++ .../wp-content/plugins/cache/object-cache.php | 21 +++++++++++++++++++ .../plugins/test-plugin/test-plugin.php | 14 +++++++++++++ tests/test_flag_formats.sh | 2 +- 7 files changed, 66 insertions(+), 4 deletions(-) rename tests/data/{wp-content => }/plugins/test-plugin/test-plugin.php (80%) rename tests/{data => fixtures/remote}/wp-content/mu-plugins/mu-plugin.php (64%) create mode 100644 tests/fixtures/remote/wp-content/object-cache.php create mode 100644 tests/fixtures/src/wp-content/mu-plugins/mu-plugin.php create mode 100644 tests/fixtures/src/wp-content/plugins/cache/object-cache.php create mode 100644 tests/fixtures/src/wp-content/plugins/test-plugin/test-plugin.php diff --git a/tests/data/wp-content/plugins/test-plugin/test-plugin.php b/tests/data/plugins/test-plugin/test-plugin.php similarity index 80% rename from tests/data/wp-content/plugins/test-plugin/test-plugin.php rename to tests/data/plugins/test-plugin/test-plugin.php index 3c47e08..aff8bca 100644 --- a/tests/data/wp-content/plugins/test-plugin/test-plugin.php +++ b/tests/data/plugins/test-plugin/test-plugin.php @@ -1,6 +1,6 @@ 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..894c800 --- /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/test_flag_formats.sh b/tests/test_flag_formats.sh index d8ebca9..6127da2 100755 --- a/tests/test_flag_formats.sh +++ b/tests/test_flag_formats.sh @@ -18,7 +18,7 @@ set -x FLAGS="-avzr --filter=':- .gitignore' --exclude='.*'" FLAGS_ARRAY=("-avzr" "--filter=:- .gitignore" "--exclude='.*'") -FIXTURE_PATH="$SCRIPT_DIR/data/wp-content" +FIXTURE_PATH="$SCRIPT_DIR/data" test_flags_no_quotes() { rsync $FLAGS --dry-run "$FIXTURE_PATH" . > /dev/null From 510580064c3ba068ae437326aba236cfa6f3487c Mon Sep 17 00:00:00 2001 From: Michael Day Date: Thu, 12 Dec 2024 00:18:23 -0600 Subject: [PATCH 06/15] [CICD-761] Create tests/utils dir Also moves common.sh to new utils dir. --- tests/test_flag_formats.sh | 2 +- tests/test_functions.sh | 2 +- tests/utils/change_working_dir.sh | 17 +++++++++++++++++ tests/{ => utils}/common.sh | 0 tests/utils/log-debug.sh | 18 ++++++++++++++++++ 5 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 tests/utils/change_working_dir.sh rename tests/{ => utils}/common.sh (100%) create mode 100644 tests/utils/log-debug.sh diff --git a/tests/test_flag_formats.sh b/tests/test_flag_formats.sh index 6127da2..e5668dc 100755 --- a/tests/test_flag_formats.sh +++ b/tests/test_flag_formats.sh @@ -12,7 +12,7 @@ # Get the directory of the current script SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/common.sh" +source "${SCRIPT_DIR}/utils/common.sh" set -x diff --git a/tests/test_functions.sh b/tests/test_functions.sh index 3f1c10c..d0415b6 100755 --- a/tests/test_functions.sh +++ b/tests/test_functions.sh @@ -3,7 +3,7 @@ # Get the directory of the current script SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/common.sh" +source "${SCRIPT_DIR}/utils/common.sh" source "${SCRIPT_DIR}/../functions.sh" # First argument represents the value of the FLAGS variable. diff --git a/tests/utils/change_working_dir.sh b/tests/utils/change_working_dir.sh new file mode 100644 index 0000000..40e32ff --- /dev/null +++ b/tests/utils/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/common.sh b/tests/utils/common.sh similarity index 100% rename from tests/common.sh rename to tests/utils/common.sh diff --git a/tests/utils/log-debug.sh b/tests/utils/log-debug.sh new file mode 100644 index 0000000..addc19d --- /dev/null +++ b/tests/utils/log-debug.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -e + +log_debug() { + local subject; subject="$1" + local message; message="$2" + + cat < Date: Fri, 13 Dec 2024 23:32:01 -0600 Subject: [PATCH 07/15] [CICD-761] Create tests/helpers dir Also moves common.sh to new utils dir. --- tests/{utils => helpers}/common.sh | 0 tests/{utils/log-debug.sh => helpers/log_debug.sh} | 0 tests/test_flag_formats.sh | 2 +- tests/test_functions.sh | 4 ++-- 4 files changed, 3 insertions(+), 3 deletions(-) rename tests/{utils => helpers}/common.sh (100%) rename tests/{utils/log-debug.sh => helpers/log_debug.sh} (100%) diff --git a/tests/utils/common.sh b/tests/helpers/common.sh similarity index 100% rename from tests/utils/common.sh rename to tests/helpers/common.sh diff --git a/tests/utils/log-debug.sh b/tests/helpers/log_debug.sh similarity index 100% rename from tests/utils/log-debug.sh rename to tests/helpers/log_debug.sh diff --git a/tests/test_flag_formats.sh b/tests/test_flag_formats.sh index e5668dc..c5341f6 100755 --- a/tests/test_flag_formats.sh +++ b/tests/test_flag_formats.sh @@ -12,7 +12,7 @@ # Get the directory of the current script SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/utils/common.sh" +source "${SCRIPT_DIR}/helpers/common.sh" set -x diff --git a/tests/test_functions.sh b/tests/test_functions.sh index d0415b6..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}/utils/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. From 53c0e65625211e80001a98991488817a373892dc Mon Sep 17 00:00:00 2001 From: Michael Day Date: Fri, 13 Dec 2024 23:36:03 -0600 Subject: [PATCH 08/15] [CICD-761] e2e deploy back to original data path --- .github/workflows/e2e-deploy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/e2e-deploy.yml b/.github/workflows/e2e-deploy.yml index 45e807d..4176800 100644 --- a/.github/workflows/e2e-deploy.yml +++ b/.github/workflows/e2e-deploy.yml @@ -19,7 +19,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Bump test plugin version number - run: sed -i 's/0.0.1/0.0.2/' tests/data/wp-content/plugins/test-plugin/test-plugin.php + run: sed -i 's/0.0.1/0.0.2/' tests/data/plugins/test-plugin/test-plugin.php - name: Deploy to WP Engine uses: ./.github/actions/deploy with: @@ -27,7 +27,7 @@ jobs: WPE_SSHG_KEY_PRIVATE: ${{ secrets.WPE_SSHG_KEY_PRIVATE }} WPE_ENV: sitedeploye2e # Deploy Options - SRC_PATH: "tests/data/wp-content/plugins/test-plugin" + SRC_PATH: "tests/data/plugins/test-plugin" REMOTE_PATH: "wp-content/plugins/" PHP_LINT: true FLAGS: '-r --backup --backup-dir=/tmp --itemize-changes' From 37e4056eb47844e77bdc8dec41d0bdb1e6f86a09 Mon Sep 17 00:00:00 2001 From: Michael Day Date: Fri, 13 Dec 2024 23:36:36 -0600 Subject: [PATCH 09/15] [CICD-761] Update verbiage in fixture --- .../fixtures/src/wp-content/plugins/cache/object-cache.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/fixtures/src/wp-content/plugins/cache/object-cache.php b/tests/fixtures/src/wp-content/plugins/cache/object-cache.php index 894c800..59e83d4 100644 --- a/tests/fixtures/src/wp-content/plugins/cache/object-cache.php +++ b/tests/fixtures/src/wp-content/plugins/cache/object-cache.php @@ -1,6 +1,6 @@ Date: Fri, 13 Dec 2024 23:37:16 -0600 Subject: [PATCH 10/15] [CICD-761] Create print_mount_paths util --- utils/print_mount_paths.sh | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 utils/print_mount_paths.sh 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}' +} From a8ed10c21e271e1d7629f7db30b2dbfe88903591 Mon Sep 17 00:00:00 2001 From: Michael Day Date: Fri, 13 Dec 2024 23:38:11 -0600 Subject: [PATCH 11/15] [CICD-761] Move functions to utils --- functions.sh => utils/functions.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename functions.sh => utils/functions.sh (100%) diff --git a/functions.sh b/utils/functions.sh similarity index 100% rename from functions.sh rename to utils/functions.sh From a71151a91ef947f7c70c4a64e7d7252da1f1ca16 Mon Sep 17 00:00:00 2001 From: Michael Day Date: Fri, 13 Dec 2024 23:40:01 -0600 Subject: [PATCH 12/15] [CICD-761] WIP Create generate_path_excludes util Include a dockerfile for test image --- Makefile | 20 +- tests/docker/Dockerfile_test-path-excludes | 10 + tests/test_generate_path_excludes.sh | 129 ++++++++++++ utils/generate_path_excludes.sh | 227 +++++++++++++++++++++ 4 files changed, 385 insertions(+), 1 deletion(-) create mode 100644 tests/docker/Dockerfile_test-path-excludes create mode 100755 tests/test_generate_path_excludes.sh create mode 100644 utils/generate_path_excludes.sh diff --git a/Makefile b/Makefile index deb8c19..a27b1a9 100644 --- a/Makefile +++ b/Makefile @@ -24,4 +24,22 @@ version: build test: ./tests/test_functions.sh - ./tests/test_exclude_from.sh +# ./tests/test_exclude_from.sh + @make test-excludes + +# Test that rsync is properly parsing dynamic file/path exclusions +# Running with an image to emulate the way in which generate_path_excludes.sh will actually be used +test-excludes: + @echo "🚧 Building container for testing dynamic file/path exclusions with rsync... 🚧" + @docker build -f tests/docker/Dockerfile_test-path-excludes -t test-path-excludes . + @echo "🚧 Running tests... 🚧" + @docker run --rm -e REPO_PATH=$(PWD) -v $(PWD)/tests/fixtures/src:/site --workdir /site test-path-excludes + @docker run --rm -e REPO_PATH=$(PWD) -v $(PWD)/tests/fixtures/src:/site --workdir /site/wp-content test-path-excludes + @docker run --rm -e REPO_PATH=$(PWD) -v $(PWD)/tests/fixtures/src:/site --workdir /site/wp-content/plugins test-path-excludes + @docker run --rm -e REPO_PATH=$(PWD) -v $(PWD)/tests/fixtures/src/wp-content:/site --workdir /site test-path-excludes + @docker run --rm -e REPO_PATH=$(PWD) -v $(PWD)/tests/fixtures/src/wp-content:/site --workdir /site/plugins test-path-excludes + @docker run --rm -e REPO_PATH=$(PWD) -v $(PWD)/tests/fixtures/src/wp-content/plugins:/site --workdir /site test-path-excludes + #@docker run --rm -v $(PWD)/tests/fixtures/src:/site --workdir /site/wp-content/mu-plugins test-path-excludes #MU-PLUGINS + #@docker run --rm -v $(PWD)/tests/fixtures/src/wp-content:/site --workdir /site/mu-plugins test-path-excludes #MU-PLUGINS + #@docker run --rm -v $(PWD)/tests/fixtures/src/wp-content/mu-plugins:/site --workdir /site test-path-excludes #MU-PLUGINS + @echo "🚀 All tests passed for dynamic file/path exclusions with rsync 🚀" diff --git a/tests/docker/Dockerfile_test-path-excludes b/tests/docker/Dockerfile_test-path-excludes new file mode 100644 index 0000000..888ec3e --- /dev/null +++ b/tests/docker/Dockerfile_test-path-excludes @@ -0,0 +1,10 @@ +FROM instrumentisto/rsync-ssh:alpine3.20 + +RUN apk add --no-cache bash + +COPY ../../utils/* /tests/helpers/* / +COPY /tests/test_generate_path_excludes.sh /test_generate_path_excludes.sh + +RUN chmod +x /test_generate_path_excludes.sh + +ENTRYPOINT ["/test_generate_path_excludes.sh"] diff --git a/tests/test_generate_path_excludes.sh b/tests/test_generate_path_excludes.sh new file mode 100755 index 0000000..f9a705f --- /dev/null +++ b/tests/test_generate_path_excludes.sh @@ -0,0 +1,129 @@ +#!/bin/bash + +# Get the directory of the current script +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +source "${SCRIPT_DIR}/common.sh" +source "${SCRIPT_DIR}/log_debug.sh" +source "${SCRIPT_DIR}/print_mount_paths.sh" +source "${SCRIPT_DIR}/generate_path_excludes.sh" + +test_rsync_with_excludes() { + export SRC_PATH=$1 REMOTE_PATH=$2 + # shellcheck disable=SC2206 + local FLAGS=($3) + + # Generate the exclusion lists 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)" + + # Capture the output of the rsync command + output=$(rsync --dry-run -azvr --inplace --exclude='.*' "${FLAGS[@]}" \ + --exclude-from=<(echo "$source_exclude_from") \ + --rsync-path="rsync $remote_excludes" \ + "$SRC_PATH" "$REPO_PATH/tests/fixtures/remote/$REMOTE_PATH") + + if [[ "$SRC_PATH" != *mu-plugins?(/) && "$(pwd)" != *mu-plugins ]]; then + # Fail if the output is missing the expected file + if ! echo "$output" | grep -q "test-plugin.php"; then + echo -e "${RED}Test failed for pwd='$(pwd)', SRC_PATH='$SRC_PATH', REMOTE_PATH='$REMOTE_PATH': test-plugin.php excluded${NC}" + echo -e "${BLUE}$(log_debug "Rsync output:" "$output")\n${NC}" + echo -e "${BLUE}$(log_debug "Source exclude from:" "$source_exclude_from")\n${NC}" + echo -e "${BLUE}$(log_debug "Remote excludes:" "$remote_excludes")\n${NC}" + exit 1 + fi + + # Fail if the output is missing the nested plugin file that is excluded at a different path + if ! echo "$output" | grep -q "cache/object-cache"; then + echo -e "${RED}Test failed for pwd='$(pwd)', SRC_PATH='$SRC_PATH', REMOTE_PATH='$REMOTE_PATH': cache/object-cache.php excluded${NC}" + echo -e "${BLUE}$(log_debug "Rsync output:" "$output")\n${NC}" + echo -e "${BLUE}$(log_debug "Source exclude from:" "$source_exclude_from")\n${NC}" + echo -e "${BLUE}$(log_debug "Remote excludes:" "$remote_excludes")\n${NC}" + exit 1 + fi + fi + + # Fail if the output contains the excluded mu-plugin file + if echo "$output" | grep -q "mu-plugin.php"; then + echo -e "${RED}Test failed for pwd='$(pwd)', SRC_PATH='$SRC_PATH', REMOTE_PATH='$REMOTE_PATH': WPE mu-plugin.php included${NC}" + echo -e "${BLUE}$(log_debug "Rsync output:" "$output")\n${NC}" + echo -e "${BLUE}$(log_debug "Source exclude from:" "$source_exclude_from")\n${NC}" + echo -e "${BLUE}$(log_debug "Remote excludes:" "$remote_excludes")\n${NC}" + exit 1 + fi +} + +main() { + read -r mounted_src_path base_mount_path <<< "$(print_mount_paths "/site")" + echo -e "${BLUE}Info: mounted_src_path='$mounted_src_path', base_mount_path='$base_mount_path', pwd='$(pwd)'${NC}" + + local test_cases=( + # SRC_PATH REMOTE_PATH + "." "" + "." "wp-content" + "." "wp-content/" # Trailing slash in REMOTE_PATH + "." "wp-content/plugins" + "." "wp-content/plugins/" # Trailing slash in REMOTE_PATH + "." "wp-content/mu-plugins" + "." "wp-content/mu-plugins/" # Trailing slash in REMOTE_PATH + ) + + if [[ "$mounted_src_path" == *src && "$(pwd)" == *site ]]; then + test_cases+=( + "wp-content" "" + "wp-content" "wp-content" + "wp-content" "wp-content/" # Trailing slash in REMOTE_PATH + "wp-content/" "" + "wp-content/" "wp-content" + "wp-content/" "wp-content/" # Trailing slash in REMOTE_PATH + "wp-content/plugins" "" + "wp-content/plugins" "wp-content/plugins" + "wp-content/plugins" "wp-content/plugins/" # Trailing slash in REMOTE_PATH + "wp-content/plugins/" "wp-content/plugins" + "wp-content/plugins/" "wp-content/plugins/" # Trailing slash in REMOTE_PATH + "wp-content/mu-plugins" "" + "wp-content/mu-plugins" "wp-content" +# "wp-content/mu-plugins" "wp-content/mu-plugins" +# "wp-content/mu-plugins" "wp-content/mu-plugins/" # Trailing slash in REMOTE_PATH +# "wp-content/mu-plugins/" "wp-content/mu-plugins" +# "wp-content/mu-plugins/" "wp-content/mu-plugins/" # Trailing slash in REMOTE_PATH +# "wp-content/mu-plugins" "" +# "wp-content/mu-plugins" "wp-content" +# "wp-content/mu-plugins" "wp-content/mu-plugins" +# "wp-content/mu-plugins" "wp-content/mu-plugins/" # Trailing slash in REMOTE_PATH +# "wp-content/mu-plugins/" "wp-content" +# "wp-content/mu-plugins/" "wp-content/mu-plugins" +# "wp-content/mu-plugins/" "wp-content/mu-plugins/" # Trailing slash in REMOTE_PATH + ) + fi + + if [[ "$mounted_src_path" == *wp-content && "$(pwd)" == *site ]]; then + test_cases+=( + "plugins" "" + "plugins" "wp-content/plugins" + "plugins" "wp-content/plugins/" # Trailing slash in REMOTE_PATH + "plugins/" "wp-content/plugins" + "plugins/" "wp-content/plugins/" # Trailing slash in REMOTE_PATH + "mu-plugins" "" +# "mu-plugins" "wp-content/mu-plugins" +# "mu-plugins" "wp-content/mu-plugins/" # Trailing slash in REMOTE_PATH +# "mu-plugins/" "wp-content/mu-plugins" +# "mu-plugins/" "wp-content/mu-plugins/" # Trailing slash in REMOTE_PATH + ) + fi + + for ((i=0; i<${#test_cases[@]}; i+=2)); do + echo -e "${BLUE}Generating rsync excludes for SRC_PATH='${test_cases[i]}', REMOTE_PATH='${test_cases[i+1]}'${NC}" + test_rsync_with_excludes "${test_cases[i]}" "${test_cases[i+1]}" + done + + local flags=("--delete") + for ((i=0; i<${#test_cases[@]}; i+=2)); do + echo -e "${BLUE}Generating rsync excludes with flags='${flags[*]}' for SRC_PATH='${test_cases[i]}', REMOTE_PATH='${test_cases[i+1]}'${NC}" + test_rsync_with_excludes "${test_cases[i]}" "${test_cases[i+1]}" "${flags[@]}" + done + + echo -e "${GREEN}Tests passed for mounted_src_path='$mounted_src_path', base_mount_path='$base_mount_path', pwd='$(pwd)'${NC}" +} + +main diff --git a/utils/generate_path_excludes.sh b/utils/generate_path_excludes.sh new file mode 100644 index 0000000..56f040f --- /dev/null +++ b/utils/generate_path_excludes.sh @@ -0,0 +1,227 @@ +#!/bin/bash + +set -e +shopt -s extglob + +# 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 +# +# The function returns three values: the absolute base path for prefixing excluded files, +# the absolute mu-plugins path, and a debug string indicating which switch case was matched. +determine_source_exclude_paths() { + local base_path + local mu_dir_path + local debug + + read -r mounted_source_path base_mount_path <<< "$(print_mount_paths "/wp-content")" + + if [[ -n "${SRC_PATH}" && "${SRC_PATH}" != '.' ]] || + [[ "$(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/" + debug="SRC_PATH switch: matched 'wp-content'" + ;; + wp-content/ | *mu-plugins ) + base_path="/" + mu_dir_path="/mu-plugins/" + debug="SRC_PATH switch: 'wp-content/ | *mu-plugins'" + ;; + *mu-plugins/ ) + mu_dir_path="/" + debug="SRC_PATH switch: '*mu-plugins/'" + ;; + esac + 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/" + debug="Matched: *wp-content" + break + ;; + *mu-plugins ) + mu_dir_path="/" + debug="Matched: *wp-content/mu-plugins" + break + ;; + esac + done + fi + fi + + printf "%s\n%s\n%s\n" "$base_path" "$mu_dir_path" "$debug" +} + +determine_remote_exclude_paths() { + local base_path + local mu_dir_path + local debug="default" + + if [[ -n "${REMOTE_PATH}" ]]; then + case "$REMOTE_PATH" in + wp-content?(/) ) + base_path="" + mu_dir_path="mu-plugins/" + debug="REMOTE_PATH switch: 'wp-content?(/)'" + ;; + wp-content/mu-plugins?(/) ) + mu_dir_path="" + debug="REMOTE_PATH switch: 'wp-content/mu-plugins'" + ;; + esac + fi + + printf "%s\n%s\n%s\n" "$base_path" "$mu_dir_path" "$debug" +} + +# Generate a list of files to exclude from the deployment +# +# The list is output to stdout, which can be used as an argument to --exclude-from. +print_dynamic_excludes() { + local func=$1 + { + IFS= read -r base_path + IFS= read -r mu_dir_path + IFS= read -r debug + } <<< "$($func)" + + if [[ -n "$base_path" && -n "$mu_dir_path" ]]; then + cat << EOF +# WordPress specific files (dynamic paths) +${base_path}uploads/ +${base_path}blogs.dir/ +${base_path}upgrade/* +${base_path}backup-db/* +${base_path}advanced-cache.php +${base_path}wp-cache-config.php +${base_path}cache/* +${base_path}cache/supercache/* + +# WP Engine specific files (dynamic paths) +${base_path}object-cache.php +${base_path}drop-ins/ +${base_path}drop-ins/wp-cache-memcached* +${base_path}mysql.sql +${mu_dir_path}mu-plugin.php +${mu_dir_path}slt-force-strong-passwords.php +${mu_dir_path}wpengine-security-auditor.php +${mu_dir_path}stop-long-comments.php +${mu_dir_path}force-strong-passwords* +${mu_dir_path}wpengine-common* +${mu_dir_path}wpe-wp-sign-on-plugin* +${mu_dir_path}wpe-elasticpress-autosuggest-logger* +${mu_dir_path}wpe-cache-plugin* +${mu_dir_path}wp-cache-memcached* + +# Local specific (dynamic paths) +${mu_dir_path}local-by-flywheel-live-link-helper.php + +#################### Debug: ${debug} +EOF + elif [[ -n "$mu_dir_path" ]]; then + cat << EOF +# WP Engine specific files (dynamic paths) +${mu_dir_path}mu-plugin.php +${mu_dir_path}slt-force-strong-passwords.php +${mu_dir_path}wpengine-security-auditor.php +${mu_dir_path}stop-long-comments.php +${mu_dir_path}force-strong-passwords* +${mu_dir_path}wpengine-common* +${mu_dir_path}wpe-wp-sign-on-plugin* +${mu_dir_path}wpe-elasticpress-autosuggest-logger* +${mu_dir_path}wpe-cache-plugin* +${mu_dir_path}wp-cache-memcached* + +# Local specific (dynamic paths) +${mu_dir_path}local-by-flywheel-live-link-helper.php + +#################### Debug: ${debug} +EOF + else + cat << EOF +# 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 ignored 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/drop-ins/ +wp-content/drop-ins/wp-cache-memcached* +wp-content/mysql.sql +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* + +# Local specific +wp-content/local-by-flywheel-live-link-helper.php +EOF + fi +} + +generate_source_exclude_from() { + print_dynamic_excludes determine_source_exclude_paths +} + +generate_remote_excludes() { + print_dynamic_excludes determine_remote_exclude_paths | grep -Ev '^\s*(#|$)' | awk '{printf "--exclude='\''%s'\'' ", $0}' | sed 's/[[:space:]]*$//' +} From 3b3904af757855967f6ddc0c1342f8bc80f22355 Mon Sep 17 00:00:00 2001 From: Michael Day Date: Fri, 13 Dec 2024 23:41:15 -0600 Subject: [PATCH 13/15] [CICD-761] Generate the excludes dynamically in entrypoint --- Dockerfile | 7 +++---- entrypoint.sh | 31 +++++++++++++++++-------------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/Dockerfile b/Dockerfile index aebb08c..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 exclude-from.sh /exclude-from.sh +# Add entrypoint and utils +COPY utils /utils ADD entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] diff --git a/entrypoint.sh b/entrypoint.sh index a177dc1..0985cfe 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -5,9 +5,9 @@ set -e # Get the directory of the current script SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -# Source the functions.sh and exclude-from.sh files relative to the current script's location -source "${SCRIPT_DIR}/functions.sh" -source "${SCRIPT_DIR}/exclude-from.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 @@ -28,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 @@ -55,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 @@ -76,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}" @@ -93,7 +93,7 @@ check_lint() { fi done echo "PHP Lint Successful! No errors detected!" - else + else echo "Skipping PHP Linting." fi } @@ -109,14 +109,17 @@ check_cache() { sync_files() { # Generate the excludes list before using the output with rsync - local exclude_from; exclude_from="$(generate_exclude_from)" + 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=<( { { set +x; } 2>/dev/null; echo "$exclude_from"; } ) --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 From eb6bfabe0ea406cbb1ec1977770c11125d570d89 Mon Sep 17 00:00:00 2001 From: Michael Day Date: Wed, 18 Dec 2024 11:45:06 -0600 Subject: [PATCH 14/15] [CICD-761] Move change_working_dir helper --- tests/{utils => helpers}/change_working_dir.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{utils => helpers}/change_working_dir.sh (100%) diff --git a/tests/utils/change_working_dir.sh b/tests/helpers/change_working_dir.sh similarity index 100% rename from tests/utils/change_working_dir.sh rename to tests/helpers/change_working_dir.sh From cec397d1a0347be2dfbeef35034a018a14e2b69b Mon Sep 17 00:00:00 2001 From: Michael Day Date: Wed, 18 Dec 2024 12:23:51 -0600 Subject: [PATCH 15/15] [CICD-761] Move to a simpler approach for unit tests --- Makefile | 22 +- exclude-from.sh | 103 ------ tests/docker/Dockerfile_test-path-excludes | 10 - tests/fixtures/excludes/remote_excludes.txt | 1 + .../fixtures/excludes/source_exclude_from.txt | 48 +++ tests/test_exclude_from.sh | 136 -------- tests/test_generate_path_excludes.sh | 278 +++++++++------- utils/generate_path_excludes.sh | 301 +++++++++--------- 8 files changed, 366 insertions(+), 533 deletions(-) delete mode 100644 exclude-from.sh delete mode 100644 tests/docker/Dockerfile_test-path-excludes create mode 100644 tests/fixtures/excludes/remote_excludes.txt create mode 100644 tests/fixtures/excludes/source_exclude_from.txt delete mode 100755 tests/test_exclude_from.sh diff --git a/Makefile b/Makefile index a27b1a9..c9ca11a 100644 --- a/Makefile +++ b/Makefile @@ -22,24 +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_exclude_from.sh - @make test-excludes - -# Test that rsync is properly parsing dynamic file/path exclusions -# Running with an image to emulate the way in which generate_path_excludes.sh will actually be used -test-excludes: - @echo "🚧 Building container for testing dynamic file/path exclusions with rsync... 🚧" - @docker build -f tests/docker/Dockerfile_test-path-excludes -t test-path-excludes . - @echo "🚧 Running tests... 🚧" - @docker run --rm -e REPO_PATH=$(PWD) -v $(PWD)/tests/fixtures/src:/site --workdir /site test-path-excludes - @docker run --rm -e REPO_PATH=$(PWD) -v $(PWD)/tests/fixtures/src:/site --workdir /site/wp-content test-path-excludes - @docker run --rm -e REPO_PATH=$(PWD) -v $(PWD)/tests/fixtures/src:/site --workdir /site/wp-content/plugins test-path-excludes - @docker run --rm -e REPO_PATH=$(PWD) -v $(PWD)/tests/fixtures/src/wp-content:/site --workdir /site test-path-excludes - @docker run --rm -e REPO_PATH=$(PWD) -v $(PWD)/tests/fixtures/src/wp-content:/site --workdir /site/plugins test-path-excludes - @docker run --rm -e REPO_PATH=$(PWD) -v $(PWD)/tests/fixtures/src/wp-content/plugins:/site --workdir /site test-path-excludes - #@docker run --rm -v $(PWD)/tests/fixtures/src:/site --workdir /site/wp-content/mu-plugins test-path-excludes #MU-PLUGINS - #@docker run --rm -v $(PWD)/tests/fixtures/src/wp-content:/site --workdir /site/mu-plugins test-path-excludes #MU-PLUGINS - #@docker run --rm -v $(PWD)/tests/fixtures/src/wp-content/mu-plugins:/site --workdir /site test-path-excludes #MU-PLUGINS - @echo "🚀 All tests passed for dynamic file/path exclusions with rsync 🚀" + ./tests/test_generate_path_excludes.sh diff --git a/exclude-from.sh b/exclude-from.sh deleted file mode 100644 index 43cf55d..0000000 --- a/exclude-from.sh +++ /dev/null @@ -1,103 +0,0 @@ -#!/bin/bash - -set -e - -# Determine the paths to exclude from the deployment -# -# Paths are inferred from the REMOTE_PATH environment variable. -# -# The function returns two paths: the relative base path for prefixing excluded files, -# and the relative mu-plugins path. -determine_exclude_paths() { - local base_path="wp-content/" - local mu_dir_path="wp-content/mu-plugins/" - - case "$REMOTE_PATH" in - "wp-content") - base_path="" - mu_dir_path="mu-plugins/" - ;; - "wp-content/mu-plugins") - base_path="" - mu_dir_path="" - ;; - esac - - printf "%s\n%s\n" "$base_path" "$mu_dir_path" -} - -# Generate a list of files to exclude from the deployment -# -# The list is output to stdout, which can be used as an argument to --exclude-from. -generate_exclude_from() { - { - IFS= read -r base_path - IFS= read -r mu_dir_path - } <<< "$(determine_exclude_paths)" - - cat << EOF -# 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 ignored in the WPE sample .gitignore per best practice. -wp-config.php -${base_path}uploads/ -${base_path}blogs.dir/ -${base_path}upgrade/* -${base_path}backup-db/* -${base_path}advanced-cache.php -${base_path}wp-cache-config.php -${base_path}cache/* -${base_path}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 -${base_path}object-cache.php -${mu_dir_path}mu-plugin.php -${mu_dir_path}slt-force-strong-passwords.php -${mu_dir_path}wpengine-security-auditor.php -${mu_dir_path}stop-long-comments.php -${mu_dir_path}force-strong-passwords* -${mu_dir_path}wpengine-common* -${mu_dir_path}wpe-wp-sign-on-plugin* -${mu_dir_path}wpe-elasticpress-autosuggest-logger* -${mu_dir_path}wpe-cache-plugin* -${mu_dir_path}wp-cache-memcached* -${base_path}drop-ins/ -${base_path}drop-ins/wp-cache-memcached* -${base_path}mysql.sql - -# Local specific -${mu_dir_path}local-by-flywheel-live-link-helper.php -EOF -} diff --git a/tests/docker/Dockerfile_test-path-excludes b/tests/docker/Dockerfile_test-path-excludes deleted file mode 100644 index 888ec3e..0000000 --- a/tests/docker/Dockerfile_test-path-excludes +++ /dev/null @@ -1,10 +0,0 @@ -FROM instrumentisto/rsync-ssh:alpine3.20 - -RUN apk add --no-cache bash - -COPY ../../utils/* /tests/helpers/* / -COPY /tests/test_generate_path_excludes.sh /test_generate_path_excludes.sh - -RUN chmod +x /test_generate_path_excludes.sh - -ENTRYPOINT ["/test_generate_path_excludes.sh"] 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/test_exclude_from.sh b/tests/test_exclude_from.sh deleted file mode 100755 index fec02d5..0000000 --- a/tests/test_exclude_from.sh +++ /dev/null @@ -1,136 +0,0 @@ -#!/bin/bash - -# Get the directory of the current script -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" - -source "${SCRIPT_DIR}/common.sh" -source "${SCRIPT_DIR}/../exclude-from.sh" - -test_generate_exclude_from() { - export REMOTE_PATH="" - expected_output="# 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 ignored 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" - - # Show the diff between the generated list and the expected output - if diff <(generate_exclude_from) <(echo "$expected_output"); then - echo -e "${GREEN}Test passed for generating excludes list with REMOTE_PATH='${REMOTE_PATH}'.${NC}" - else - echo -e "${RED}Test failed for generating excludes list with REMOTE_PATH='${REMOTE_PATH}'.${NC}" - fi -} - -test_determine_exclude_paths() { - export REMOTE_PATH="wp-content" - expected_base_path="" - expected_mu_dir_path="mu-plugins/" - - # Run the function and capture the output - output=$(determine_exclude_paths) - { - IFS= read -r base_path - IFS= read -r mu_dir_path - } <<< "$output" - - # Check the assigned paths - if [[ "$base_path" == "$expected_base_path" && "$mu_dir_path" == "$expected_mu_dir_path" ]]; then - echo -e "${GREEN}Test passed for updating excludes list file paths with REMOTE_PATH='${REMOTE_PATH}'.${NC}" - else - echo -e "${RED}Test failed for updating excludes list file paths with REMOTE_PATH='${REMOTE_PATH}'.${NC}" - fi -} - -test_rsync_with_excludes() { - export REMOTE_PATH=$1 - echo -e "${BLUE}--- REMOTE_PATH: '$REMOTE_PATH'${NC}" - - local exclude_from; exclude_from="$(generate_exclude_from)" - local fixture_path="$SCRIPT_DIR/data" - - # Capture the output of the rsync command - output=$(rsync --dry-run --verbose --recursive --exclude-from=<(echo "$exclude_from") "$fixture_path" .) - - # Check if the output contains the expected file - if echo "$output" | grep -q "test-plugin.php"; then - echo -e "${GREEN}Test passed for rsync syncing files: test-plugin.php included.${NC}" - else - echo -e "${RED}Test failed for rsync syncing files: test-plugin.php excluded.${NC}" - fi - - # Check that the output does not contain the excluded file - if echo "$output" | grep -q "mu-plugin.php"; then - echo -e "${RED}Test failed for rsync parsing excludes: mu-plugin.php included.${NC}" - else - echo -e "${GREEN}Test passed for rsync parsing excludes: mu-plugin.php excluded.${NC}" - fi -} - -main() { - test_generate_exclude_from - test_determine_exclude_paths - test_rsync_with_excludes "" - test_rsync_with_excludes "wp-content" - test_rsync_with_excludes "wp-content/mu-plugins" -} - -main diff --git a/tests/test_generate_path_excludes.sh b/tests/test_generate_path_excludes.sh index f9a705f..2f00f2a 100755 --- a/tests/test_generate_path_excludes.sh +++ b/tests/test_generate_path_excludes.sh @@ -1,129 +1,193 @@ #!/bin/bash +set -e + # Get the directory of the current script SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/common.sh" -source "${SCRIPT_DIR}/log_debug.sh" -source "${SCRIPT_DIR}/print_mount_paths.sh" -source "${SCRIPT_DIR}/generate_path_excludes.sh" - -test_rsync_with_excludes() { - export SRC_PATH=$1 REMOTE_PATH=$2 - # shellcheck disable=SC2206 - local FLAGS=($3) - - # Generate the exclusion lists 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)" - - # Capture the output of the rsync command - output=$(rsync --dry-run -azvr --inplace --exclude='.*' "${FLAGS[@]}" \ - --exclude-from=<(echo "$source_exclude_from") \ - --rsync-path="rsync $remote_excludes" \ - "$SRC_PATH" "$REPO_PATH/tests/fixtures/remote/$REMOTE_PATH") - - if [[ "$SRC_PATH" != *mu-plugins?(/) && "$(pwd)" != *mu-plugins ]]; then - # Fail if the output is missing the expected file - if ! echo "$output" | grep -q "test-plugin.php"; then - echo -e "${RED}Test failed for pwd='$(pwd)', SRC_PATH='$SRC_PATH', REMOTE_PATH='$REMOTE_PATH': test-plugin.php excluded${NC}" - echo -e "${BLUE}$(log_debug "Rsync output:" "$output")\n${NC}" - echo -e "${BLUE}$(log_debug "Source exclude from:" "$source_exclude_from")\n${NC}" - echo -e "${BLUE}$(log_debug "Remote excludes:" "$remote_excludes")\n${NC}" +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)" - # Fail if the output is missing the nested plugin file that is excluded at a different path - if ! echo "$output" | grep -q "cache/object-cache"; then - echo -e "${RED}Test failed for pwd='$(pwd)', SRC_PATH='$SRC_PATH', REMOTE_PATH='$REMOTE_PATH': cache/object-cache.php excluded${NC}" - echo -e "${BLUE}$(log_debug "Rsync output:" "$output")\n${NC}" - echo -e "${BLUE}$(log_debug "Source exclude from:" "$source_exclude_from")\n${NC}" - echo -e "${BLUE}$(log_debug "Remote excludes:" "$remote_excludes")\n${NC}" + 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 - 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}" +} - # Fail if the output contains the excluded mu-plugin file - if echo "$output" | grep -q "mu-plugin.php"; then - echo -e "${RED}Test failed for pwd='$(pwd)', SRC_PATH='$SRC_PATH', REMOTE_PATH='$REMOTE_PATH': WPE mu-plugin.php included${NC}" - echo -e "${BLUE}$(log_debug "Rsync output:" "$output")\n${NC}" - echo -e "${BLUE}$(log_debug "Source exclude from:" "$source_exclude_from")\n${NC}" - echo -e "${BLUE}$(log_debug "Remote excludes:" "$remote_excludes")\n${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}" } -main() { - read -r mounted_src_path base_mount_path <<< "$(print_mount_paths "/site")" - echo -e "${BLUE}Info: mounted_src_path='$mounted_src_path', base_mount_path='$base_mount_path', pwd='$(pwd)'${NC}" - - local test_cases=( - # SRC_PATH REMOTE_PATH - "." "" - "." "wp-content" - "." "wp-content/" # Trailing slash in REMOTE_PATH - "." "wp-content/plugins" - "." "wp-content/plugins/" # Trailing slash in REMOTE_PATH - "." "wp-content/mu-plugins" - "." "wp-content/mu-plugins/" # Trailing slash in REMOTE_PATH - ) - - if [[ "$mounted_src_path" == *src && "$(pwd)" == *site ]]; then - test_cases+=( - "wp-content" "" - "wp-content" "wp-content" - "wp-content" "wp-content/" # Trailing slash in REMOTE_PATH - "wp-content/" "" - "wp-content/" "wp-content" - "wp-content/" "wp-content/" # Trailing slash in REMOTE_PATH - "wp-content/plugins" "" - "wp-content/plugins" "wp-content/plugins" - "wp-content/plugins" "wp-content/plugins/" # Trailing slash in REMOTE_PATH - "wp-content/plugins/" "wp-content/plugins" - "wp-content/plugins/" "wp-content/plugins/" # Trailing slash in REMOTE_PATH - "wp-content/mu-plugins" "" - "wp-content/mu-plugins" "wp-content" -# "wp-content/mu-plugins" "wp-content/mu-plugins" -# "wp-content/mu-plugins" "wp-content/mu-plugins/" # Trailing slash in REMOTE_PATH -# "wp-content/mu-plugins/" "wp-content/mu-plugins" -# "wp-content/mu-plugins/" "wp-content/mu-plugins/" # Trailing slash in REMOTE_PATH -# "wp-content/mu-plugins" "" -# "wp-content/mu-plugins" "wp-content" -# "wp-content/mu-plugins" "wp-content/mu-plugins" -# "wp-content/mu-plugins" "wp-content/mu-plugins/" # Trailing slash in REMOTE_PATH -# "wp-content/mu-plugins/" "wp-content" -# "wp-content/mu-plugins/" "wp-content/mu-plugins" -# "wp-content/mu-plugins/" "wp-content/mu-plugins/" # Trailing slash in REMOTE_PATH - ) - fi +test_generate_remote_excludes() { + export REMOTE_PATH="" + local output + local expected_output - if [[ "$mounted_src_path" == *wp-content && "$(pwd)" == *site ]]; then - test_cases+=( - "plugins" "" - "plugins" "wp-content/plugins" - "plugins" "wp-content/plugins/" # Trailing slash in REMOTE_PATH - "plugins/" "wp-content/plugins" - "plugins/" "wp-content/plugins/" # Trailing slash in REMOTE_PATH - "mu-plugins" "" -# "mu-plugins" "wp-content/mu-plugins" -# "mu-plugins" "wp-content/mu-plugins/" # Trailing slash in REMOTE_PATH -# "mu-plugins/" "wp-content/mu-plugins" -# "mu-plugins/" "wp-content/mu-plugins/" # Trailing slash in REMOTE_PATH - ) - fi + output=$(generate_remote_excludes) + expected_output=$(cat "tests/fixtures/excludes/remote_excludes.txt") - for ((i=0; i<${#test_cases[@]}; i+=2)); do - echo -e "${BLUE}Generating rsync excludes for SRC_PATH='${test_cases[i]}', REMOTE_PATH='${test_cases[i+1]}'${NC}" - test_rsync_with_excludes "${test_cases[i]}" "${test_cases[i+1]}" - done + 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 - local flags=("--delete") - for ((i=0; i<${#test_cases[@]}; i+=2)); do - echo -e "${BLUE}Generating rsync excludes with flags='${flags[*]}' for SRC_PATH='${test_cases[i]}', REMOTE_PATH='${test_cases[i+1]}'${NC}" - test_rsync_with_excludes "${test_cases[i]}" "${test_cases[i+1]}" "${flags[@]}" - done + echo -e "${GREEN}Test passed for source exclude from: generated output matches expected output.${NC}" +} - echo -e "${GREEN}Tests passed for mounted_src_path='$mounted_src_path', base_mount_path='$base_mount_path', pwd='$(pwd)'${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/utils/generate_path_excludes.sh b/utils/generate_path_excludes.sh index 56f040f..104a6aa 100644 --- a/utils/generate_path_excludes.sh +++ b/utils/generate_path_excludes.sh @@ -3,6 +3,34 @@ 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)" @@ -10,152 +38,140 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "${SCRIPT_DIR}/print_mount_paths.sh" # Determine the source paths to exclude from the deployment -# -# The function returns three values: the absolute base path for prefixing excluded files, -# the absolute mu-plugins path, and a debug string indicating which switch case was matched. determine_source_exclude_paths() { local base_path local mu_dir_path - local debug - - read -r mounted_source_path base_mount_path <<< "$(print_mount_paths "/wp-content")" - - if [[ -n "${SRC_PATH}" && "${SRC_PATH}" != '.' ]] || - [[ "$(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/" - debug="SRC_PATH switch: matched 'wp-content'" - ;; - wp-content/ | *mu-plugins ) + + 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/" - debug="SRC_PATH switch: 'wp-content/ | *mu-plugins'" + break ;; - *mu-plugins/ ) + *mu-plugins ) mu_dir_path="/" - debug="SRC_PATH switch: '*mu-plugins/'" + break ;; esac - 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/" - debug="Matched: *wp-content" - break - ;; - *mu-plugins ) - mu_dir_path="/" - debug="Matched: *wp-content/mu-plugins" - break - ;; - esac - done - fi + done fi - printf "%s\n%s\n%s\n" "$base_path" "$mu_dir_path" "$debug" + printf "%s\n%s\n" "$base_path" "$mu_dir_path" } determine_remote_exclude_paths() { local base_path local mu_dir_path - local debug="default" - if [[ -n "${REMOTE_PATH}" ]]; then + 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/" - debug="REMOTE_PATH switch: 'wp-content?(/)'" + base_path="/" + mu_dir_path="/mu-plugins/" ;; wp-content/mu-plugins?(/) ) - mu_dir_path="" - debug="REMOTE_PATH switch: 'wp-content/mu-plugins'" + mu_dir_path="/" ;; esac fi - printf "%s\n%s\n%s\n" "$base_path" "$mu_dir_path" "$debug" + printf "%s\n%s\n" "$base_path" "$mu_dir_path" } -# Generate a list of files to exclude from the deployment -# -# The list is output to stdout, which can be used as an argument to --exclude-from. +# 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 - IFS= read -r debug } <<< "$($func)" - if [[ -n "$base_path" && -n "$mu_dir_path" ]]; then - cat << EOF -# WordPress specific files (dynamic paths) -${base_path}uploads/ -${base_path}blogs.dir/ -${base_path}upgrade/* -${base_path}backup-db/* -${base_path}advanced-cache.php -${base_path}wp-cache-config.php -${base_path}cache/* -${base_path}cache/supercache/* - -# WP Engine specific files (dynamic paths) -${base_path}object-cache.php -${base_path}drop-ins/ -${base_path}drop-ins/wp-cache-memcached* -${base_path}mysql.sql -${mu_dir_path}mu-plugin.php -${mu_dir_path}slt-force-strong-passwords.php -${mu_dir_path}wpengine-security-auditor.php -${mu_dir_path}stop-long-comments.php -${mu_dir_path}force-strong-passwords* -${mu_dir_path}wpengine-common* -${mu_dir_path}wpe-wp-sign-on-plugin* -${mu_dir_path}wpe-elasticpress-autosuggest-logger* -${mu_dir_path}wpe-cache-plugin* -${mu_dir_path}wp-cache-memcached* - -# Local specific (dynamic paths) -${mu_dir_path}local-by-flywheel-live-link-helper.php - -#################### Debug: ${debug} -EOF - elif [[ -n "$mu_dir_path" ]]; then - cat << EOF -# WP Engine specific files (dynamic paths) -${mu_dir_path}mu-plugin.php -${mu_dir_path}slt-force-strong-passwords.php -${mu_dir_path}wpengine-security-auditor.php -${mu_dir_path}stop-long-comments.php -${mu_dir_path}force-strong-passwords* -${mu_dir_path}wpengine-common* -${mu_dir_path}wpe-wp-sign-on-plugin* -${mu_dir_path}wpe-elasticpress-autosuggest-logger* -${mu_dir_path}wpe-cache-plugin* -${mu_dir_path}wp-cache-memcached* - -# Local specific (dynamic paths) -${mu_dir_path}local-by-flywheel-live-link-helper.php - -#################### Debug: ${debug} -EOF - else - cat << EOF -# Version Control -# NOTE: -# WP Engine does not support server side versioning so hosting any version control -# on the server would not be advantageous. + 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 @@ -168,60 +184,31 @@ EOF 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 ignored 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. + +# WP Engine specific files and directories .smushit-status .gitattributes .wpe-devkit/ .wpengine-conf/ _wpeprivate -wp-content/object-cache.php -wp-content/drop-ins/ -wp-content/drop-ins/wp-cache-memcached* -wp-content/mysql.sql -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* - -# Local specific -wp-content/local-by-flywheel-live-link-helper.php +$(echo -e "$dynamic_excludes") EOF - fi -} - -generate_source_exclude_from() { - print_dynamic_excludes determine_source_exclude_paths } generate_remote_excludes() { - print_dynamic_excludes determine_remote_exclude_paths | grep -Ev '^\s*(#|$)' | awk '{printf "--exclude='\''%s'\'' ", $0}' | sed 's/[[:space:]]*$//' + 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 }