Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CICD-761] Generate a dynamic excludes file list for rsync #40

Closed
wants to merge 15 commits into from

Conversation

mike-day
Copy link
Member

@mike-day mike-day commented Dec 4, 2024

JIRA Ticket

CICD-761

Bug description

Customer reports that mu-plugins are being deleted when deploying with the GitHub Action. The plugins that are mentioned as being deleted are already part of our global excludes list in wpengine/site-deploy.

Reported in: wpengine/github-action-wpe-site-deploy#91

Background

We worked through several options for this ticket before landing on this implementation.

In #37, we tried forcing files to adhere to a specific structure expected by the remote target (making a relative remote), but felt the approach ended up being a bit risky with re-writing directory structure on each sync.

In #39, we tried removing relative path prepending in the exclude.txt file, but the approach was deemed a risk for affecting users that deploy to the root directory of a site (the paths in exclude.txt were treated like globs, meaning custom files could be unintentionally excluded).

The approach outlined below came up while solutioning in a review session, and we believe it is another good long-term option that reduces such risks.

What Are We Doing Here

This approach creates a excludes list for rsync on the fly, with the path for files in the excludes list inferred from the REMOTE_PATH environment variable.

No exclude.txt is created — instead, the list is output to stdout before being used in a process substitution. This provides a temp file rsync can consume before it is destroyed at the end of the process.

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.
Moves the test-plugin (used by e2e test) to a wp-content dir for reuse as a fixture in unit testing.
Resolves a warning about unused variables in the common file.
@mike-day mike-day requested a review from a team as a code owner December 4, 2024 16:32
Copy link

changeset-bot bot commented Dec 4, 2024

🦋 Changeset detected

Latest commit: cec397d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@wpengine/site-deploy Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@@ -19,15 +19,15 @@ 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
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The plugin used for e2e tests was moved to reside under a wp-content parent dir. The reasoning was to make the file structure more closely resemble a WordPress install, making the plugin reusable for unit testing.

entrypoint.sh Outdated
Comment on lines 111 to 112
# Generate the excludes list before using the output with rsync
local exclude_from; exclude_from="$(generate_exclude_from)"
Copy link
Member Author

@mike-day mike-day Dec 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found it preferable to generate the output in a variable here.

The text works more reliably when quoted, and while calling the function directly in the process substitution works (eg, <(echo "generate_exclude_from")), it produces warnings about a needless echo.

entrypoint.sh Outdated
#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}"
Copy link
Member Author

@mike-day mike-day Dec 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a block inside the process substitution and disabling debug mode prevents unwanted output. Sending errors to /dev/null also prevents ++ set +x from being added to the output.

The result is debugging output that is identical to how it was previously (with the exception of --exclude-from=/dev/fd/63, which is referring to the temp file path created by the process substitution).

exclude-from.sh Outdated
Comment on lines 15 to 24
case "$REMOTE_PATH" in
"wp-content")
base_path=""
mu_dir_path="mu-plugins/"
;;
"wp-content/mu-plugins")
base_path=""
mu_dir_path=""
;;
esac
Copy link
Member Author

@mike-day mike-day Dec 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check me on this, but I believe these represent the only cases in which we would need to modify the paths.

If the user is deploying to other expected directories (eg, wp-content/plugins or wp-content/themes), the full relative paths are included.

While the above has no negative effect, we could consider further modifying the excludes list at that point to completely remove the WP and WPE-specific file paths. I did consider implementing that, but it felt a bit like overkill 🙃

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if wp-content/ and wp-content/mu-plugins (/ appended) should also be matched 🤔

It seems like the deploy would likely work with either as the REMOTE_PATH, so those might need to be added for comparison here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To-do: test with and without appended slash for REMOTE_PATH.

Copy link
Member Author

@mike-day mike-day Dec 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To-do: test with rsync destructive mode to confirm excludes work from the other direction (remote to local). Might mostly refer to a need for a new test case.

exclude-from.sh Outdated
Comment on lines 33 to 36
{
IFS= read -r base_path
IFS= read -r mu_dir_path
} <<< "$(determine_exclude_paths)"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a shorthand approach for assigning local variables that respect "" as a valid value.

In other words, calling read -r base_path mu_dir_path "$(determine_exclude_paths)" can overwrite the first value with the second if only the first value will be evaluated as "".

Comment on lines 103 to 126
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
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this is very simplistic, it confirms that basic includes/excludes are working as expected with this new implementation and rsync.

Further testing of entrypoint can be much more thorough when we have stubbing figured out.

@mike-day mike-day force-pushed the CICD-761-try-dynamic-exclude-from branch from 39787b5 to 3351cbd Compare December 4, 2024 18:12
@mike-day
Copy link
Member Author

mike-day commented Jan 8, 2025

Closed in favor of #42

@mike-day mike-day closed this Jan 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant