Skip to content

Commit

Permalink
[CICD-761] Use only REMOTE_PATH for generating excludes
Browse files Browse the repository at this point in the history
The goal of the excludes list is to prevent users from accidentally
breaking their site by overwriting or deleting important files on
the server.

While there are many files that could be deemed important, we explicily
want to exclude a handful of files from the site root, some from wp-content,
and some from wp-content/mu-plugins.

In order to determine if a deploy is at risk of overwriting or deleting an
important file on the server, we just need to look at the REMOTE_PATH.

If REMOTE_PATH is unset, we know the destination is the site root.
If REMOTE_PATH ends in wp-content, we know the destination is wp-content.
If REMOTE_PATH ends in wp-content/mu-plugins, we know the destination is
 wp-content/mu-plugins.

All three of these cases carry the risk of overwritting or deleting
important files. In order to prevent this, we need to make the exclude
rules relative to the REMOTE_PATH.

SRC_PATH no longer matters because rsync assumes it to be identical to
REMOTE_PATH for the sake of determining transfer/deletion deltas.
  • Loading branch information
apmatthews committed Jan 7, 2025
1 parent b5bfbff commit 632088c
Show file tree
Hide file tree
Showing 10 changed files with 45 additions and 195 deletions.
8 changes: 4 additions & 4 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -109,16 +109,16 @@ 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)"
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=<( { { set +x; } 2>/dev/null; echo "$source_exclude_from"; } ) --rsync-path="rsync $remote_excludes" \
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

Expand Down
File renamed without changes.
1 change: 0 additions & 1 deletion tests/fixtures/excludes/remote_excludes.txt

This file was deleted.

Empty file.
Empty file.
Empty file.
17 changes: 0 additions & 17 deletions tests/helpers/change_working_dir.sh

This file was deleted.

Empty file removed tests/test_exclude_from.sh
Empty file.
108 changes: 20 additions & 88 deletions tests/test_generate_path_excludes.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,128 +6,60 @@ set -e
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
echo -e "${RED}Test failed for SRC_PATH='${SRC_PATH}', pwd='$(pwd)': expected '$expected_base_path, $expected_mu_dir_path', got '$base_path, $mu_dir_path'.${NC}"
exit 1
fi
}

# 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/"
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/"
change_working_dir "tests/fixtures/src/wp-content" run_test_determine_source_exclude_paths "" "/" "/mu-plugins/"

# Workdir -> mu-plugins directory
change_working_dir "tests/fixtures/src/wp-content/mu-plugins" run_test_determine_source_exclude_paths "." "" "/"

# Workdir -> plugins directory
change_working_dir "tests/fixtures/src/wp-content/plugins" run_test_determine_source_exclude_paths "." "" ""

# Workdir -> fixtures directory (checks that glob_current_dir is working (wpengine-common should be detected))
change_working_dir "tests/fixtures" run_test_determine_source_exclude_paths "." "" "/"

echo -e "${GREEN}All tests passed for determining the source excludes path.${NC}"
}

test_determine_remote_exclude_paths() {
run_test_determine_remote_exclude_paths() {
test_determine_exclude_paths() {
run_test_determine_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)"
} <<< "$(determine_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}', pwd='$(pwd)': expected '$expected_base_path, $expected_mu_dir_path', got '$base_path, $mu_dir_path'.${NC}"
echo -e "${RED}Test failed for REMOTE_PATH='${REMOTE_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

output=$(change_working_dir "tests/fixtures/src" generate_source_exclude_from)
expected_output=$(cat "tests/fixtures/excludes/source_exclude_from.txt")

if [[ "$output" != "$expected_output" ]]; then
echo -e "${RED}Test failed for pwd='$(pwd)': 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
run_test_determine_exclude_paths "." "/wp-content/" "/wp-content/mu-plugins/"
run_test_determine_exclude_paths "" "/wp-content/" "/wp-content/mu-plugins/"
run_test_determine_exclude_paths "wp-content" "/" "/mu-plugins/"
run_test_determine_exclude_paths "wp-content/" "/" "/mu-plugins/"
run_test_determine_exclude_paths "wp-content/mu-plugins" "" "/"
run_test_determine_exclude_paths "wp-content/mu-plugins/" "" "/"
run_test_determine_exclude_paths "wp-content/plugins" "" ""
run_test_determine_exclude_paths "wp-content/plugins/" "" ""

echo -e "${GREEN}Test passed for source exclude from: generated output matches expected output.${NC}"
echo -e "${GREEN}All tests passed for determining the excludes path.${NC}"
}

test_generate_remote_excludes() {
test_generate_exclude_from() {
export REMOTE_PATH=""
local output
local expected_output

output=$(change_working_dir "tests/fixtures/src" generate_remote_excludes)
expected_output=$(cat "tests/fixtures/excludes/remote_excludes.txt")
output=$(generate_exclude_from)
expected_output=$(cat "tests/fixtures/excludes/exclude_from.txt")

if [[ "$output" != "$expected_output" ]]; then
echo -e "${RED}Test failed for pwd='$(pwd)': generated output does not match expected output.${NC}"
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}"
echo -e "${GREEN}Test passed for generating exclude-from rules: 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
test_determine_exclude_paths
test_generate_exclude_from
}

main
106 changes: 21 additions & 85 deletions utils/generate_path_excludes.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ shopt -s extglob

# Dynamic Excludes
#
# This script generates two lists of files to exclude; one for the source, and one for the remote.
# This script generates a list of files to exclude from the rsync deployment.
#
# Notes about excluded files:
#
Expand All @@ -31,84 +31,37 @@ shopt -s extglob
# (such as some of the WP Engine managed plugins) might be useful in rare
# circumstances to have as a reference for debugging purposes.

determine_source_exclude_paths() {
local base_path
local mu_dir_path
determine_exclude_paths() {
local base_path; base_path="/wp-content/"
local mu_dir_path; mu_dir_path="/wp-content/mu-plugins/"

parse_src_path() {
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
remote_path_is_set() {
[[ -n "${REMOTE_PATH}" && "${REMOTE_PATH}" != '.' ]]
}

parse_current_path() {
case "$(pwd)" in
*wp-content )
base_path="/"
mu_dir_path="/mu-plugins/"
;;
*mu-plugins )
mu_dir_path="/"
;;
esac
}

glob_current_dir() {
if [[ -d wp-content ]]; then
base_path="/wp-content/"
mu_dir_path="/wp-content/mu-plugins/"
elif [[ -d mu-plugins ]]; then
base_path="/"
mu_dir_path="/mu-plugins/"
elif [[ -d wpengine-common ]]; then
mu_dir_path="/"
fi
}

# If SRC_PATH is set, use it to determine base and mu-plugins paths.
# Otherwise, use the current directory or check its contents to set the paths.
if [[ -n "${SRC_PATH}" && "${SRC_PATH}" != '.' ]]; then
parse_src_path
elif [[ "$(pwd)" == *wp-content* ]]; then
parse_current_path
else
glob_current_dir
fi

printf "%s\n%s\n" "$base_path" "$mu_dir_path"
}

determine_remote_exclude_paths() {
local base_path
local mu_dir_path

parse_remote_path() {
case "$REMOTE_PATH" in
make_exclude_paths_relative_to_remote() {
case "${REMOTE_PATH}" in
wp-content?(/) )
# REMOTE_PATH is the wp-content directory
base_path="/"
mu_dir_path="/mu-plugins/"
;;
wp-content/mu-plugins?(/) )
# REMOTE_PATH is the mu-plugins directory
base_path=""
mu_dir_path="/"
;;
* )
# REMOTE_PATH is set, but it's not the site root, wp-content, or mu-plugins.
# No dynamic excludes needed.
base_path=""
mu_dir_path=""
;;
esac
}

if [[ -z "${REMOTE_PATH}" ]]; then
base_path="/wp-content/"
mu_dir_path="/wp-content/mu-plugins/"
else
parse_remote_path
if remote_path_is_set; then
make_exclude_paths_relative_to_remote
fi

printf "%s\n%s\n" "$base_path" "$mu_dir_path"
Expand Down Expand Up @@ -175,8 +128,8 @@ print_dynamic_excludes() {
echo -e "$output"
}

generate_source_exclude_from() {
local dynamic_excludes; dynamic_excludes=$(print_dynamic_excludes determine_source_exclude_paths "\n")
generate_exclude_from() {
local dynamic_excludes; dynamic_excludes=$(print_dynamic_excludes determine_exclude_paths "\n")

cat << EOF
# Version Control
Expand All @@ -203,20 +156,3 @@ _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
}

0 comments on commit 632088c

Please sign in to comment.