From 1f0d1ff3f37849216fb52563d6e62d8e360ae0a4 Mon Sep 17 00:00:00 2001 From: Kelly Mears <developers@tinypixel.dev> Date: Fri, 6 May 2022 11:13:35 -0700 Subject: [PATCH 1/4] add multi-sync script --- multi-sync.sh | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++ sync.json | 15 ++++ 2 files changed, 222 insertions(+) create mode 100644 multi-sync.sh create mode 100644 sync.json diff --git a/multi-sync.sh b/multi-sync.sh new file mode 100644 index 0000000..a40f592 --- /dev/null +++ b/multi-sync.sh @@ -0,0 +1,207 @@ +#!/bin/bash + +# Syncing Trellis & Bedrock-based WordPress environments with WP-CLI aliases +# Version 1.2.0 +# Copyright (c) Ben Word + +# Requires: `jq` and new-ish bash (> 4) +# MacOS users: +# > `brew install jq bash` +# > `source ~/.zshrc` + +JSON_MAPPING="./sync.json" + +function connection() { + echo $(jq -r ".connection | .$1" $JSON_MAPPING) +} + +function site() { + echo $(jq -r ".sites | .[0] | .$1" $JSON_MAPPING) +} + +function env() { + echo $(jq -r ".sites | .[$1] | .$2 | .$3" $JSON_MAPPING) +} + +function replace() { + from=$1 + to=$2 + use_alias=$3 + id=0 + for _x in $(jq -c ".sites | .[]" $JSON_MAPPING); do + if [ "$use_alias" = true ]; then + echo "[@${to}] $(env $id $from) => $(env $id $to)"; + wp search-replace "@${2}" "$(env $id $from)" "$(env $id $to)" --all-tables-with-prefix --network; + else + echo "[local] $(env $id $from) => $(env $id $to)"; + wp search-replace "$(env $id $from)" "$(env $id $to)" --all-tables-with-prefix --network; + fi + + ((id++)); + done +} + +uploads=$(jq -r ".uploads" $JSON_MAPPING) + +LOCAL=false +SKIP_DB=false +SKIP_ASSETS=false +POSITIONAL_ARGS=() + +while [[ $# -gt 0 ]]; do + case $1 in + --skip-db) + SKIP_DB=true + shift + ;; + --skip-assets) + SKIP_ASSETS=true + shift + ;; + --local) + LOCAL=true + shift + ;; + --*) + echo "Unknown option $1" + exit 1 + ;; + *) + POSITIONAL_ARGS+=("$1") + shift + ;; + esac +done + +set -- "${POSITIONAL_ARGS[@]}" + +if [ $# != 2 ] +then + echo "Usage: $0 [[--skip-db] [--skip-assets] [--local]] [ENV_FROM] [ENV_TO]" +exit; +fi + +FROM=$1 +TO=$2 + +bold=$(tput bold) +normal=$(tput sgr0) + +case "$1-$2" in + production-development) DIR="down ⬇️ " FROMSITE=$(site production); FROMDIR=$(connection production); TOSITE=$(site development); TODIR=$uploads; ;; + staging-development) DIR="down ⬇️ " FROMSITE=$(site staging); FROMDIR=$(connection staging); TOSITE=$(site development); TODIR=$uploads; ;; + development-production) DIR="up ⬆️ " FROMSITE=$(site development); FROMDIR=$uploads; TOSITE=$(site production); TODIR=$(connection production); ;; + development-staging) DIR="up ⬆️ " FROMSITE=$(site development); FROMDIR=$uploads; TOSITE=$(site staging); TODIR=$(connection staging); ;; + production-staging) DIR="horizontally ↔️ "; FROMSITE=$(site production); FROMDIR=$(connection production); TOSITE=$(site staging); TODIR=$(connection staging); ;; + staging-production) DIR="horizontally ↔️ "; FROMSITE=$(site staging); FROMDIR=$(connection staging); TOSITE=$(site production); TODIR=$(connection production); ;; + *) echo "usage: $0 [[--skip-db] [--skip-assets] [--local]] production development | staging development | development staging | development production | staging production | production staging" && exit 1 ;; +esac + +if [ "$SKIP_DB" = false ] +then + DB_MESSAGE=" - ${bold}reset the $TO database${normal} ($TOSITE)" +fi + +if [ "$SKIP_ASSETS" = false ] +then + ASSETS_MESSAGE=" - sync ${bold}$DIR${normal} from $FROM ($FROMSITE)?" +fi + +if [ "$SKIP_DB" = true ] && [ "$SKIP_ASSETS" = true ] +then + echo "Nothing to synchronize." + exit; +fi + +echo +echo "Would you really like to " +echo $DB_MESSAGE +echo $ASSETS_MESSAGE +read -r -p " [y/N] " response + +if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then + # Change to site directory + cd ../ && + echo + + # Make sure both environments are available before we continue + availfrom() { + local AVAILFROM + + if [[ "$LOCAL" = true && $FROM == "development" ]]; then + AVAILFROM=$(wp option get home 2>&1) + else + AVAILFROM=$(wp "@$FROM" option get home 2>&1) + fi + if [[ $AVAILFROM == *"Error"* ]]; then + echo "❌ Unable to connect to $FROM" + exit 1 + else + echo "✅ Able to connect to $FROM" + fi + }; + availfrom + + availto() { + local AVAILTO + if [[ "$LOCAL" = true && $TO == "development" ]]; then + AVAILTO=$(wp option get home 2>&1) + else + AVAILTO=$(wp "@$TO" option get home 2>&1) + fi + + if [[ $AVAILTO == *"Error"* ]]; then + echo "❌ Unable to connect to $TO $AVAILTO" + exit 1 + else + echo "✅ Able to connect to $TO" + fi + }; + availto + + if [ "$SKIP_DB" = false ] + then + echo "Syncing database..." + # Export/import database, run search & replace + if [[ "$LOCAL" = true && $TO == "development" ]]; then + wp db export --default-character-set=utf8mb4 && + wp db reset --yes && + wp "@$FROM" db export --default-character-set=utf8mb4 - | wp db import - && + replace $FROM $TO + elif [[ "$LOCAL" = true && $FROM == "development" ]]; then + wp "@$TO" db export --default-character-set=utf8mb4 && + wp "@$TO" db reset --yes && + wp db export --default-character-set=utf8mb4 - | wp "@$TO" db import - && + replace $FROM $TO true + else + wp "@$TO" db export --default-character-set=utf8mb4 && + wp "@$TO" db reset --yes && + wp "@$FROM" db export --default-character-set=utf8mb4 - | wp "@$TO" db import - && + replace $FROM $TO true + fi + fi + + if [ "$SKIP_ASSETS" = false ] + then + echo "Syncing assets..." + # Sync uploads directory + $(chmod -R 755 "$uploads") && + if [[ $DIR == "horizontally"* ]]; then + [[ $FROMDIR =~ ^(.*): ]] && FROMHOST=${BASH_REMATCH[1]} + [[ $FROMDIR =~ ^(.*):(.*)$ ]] && FROMDIR=${BASH_REMATCH[2]} + [[ $TODIR =~ ^(.*): ]] && TOHOST=${BASH_REMATCH[1]} + [[ $TODIR =~ ^(.*):(.*)$ ]] && TODIR=${BASH_REMATCH[2]} + + ssh -o ForwardAgent=yes $FROMHOST "rsync -aze 'ssh -o StrictHostKeyChecking=no' --progress $FROMDIR $TOHOST:$TODIR" + else + rsync -az --progress "$FROMDIR" "$TODIR" + fi + fi + + # Slack notification when sync direction is up or horizontal + # if [[ $DIR != "down"* ]]; then + # USER="$(git config user.name)" + # curl -X POST -H "Content-type: application/json" --data "{\"attachments\":[{\"fallback\": \"\",\"color\":\"#36a64f\",\"text\":\"🔄 Sync from ${FROMSITE} to ${TOSITE} by ${USER} complete \"}],\"channel\":\"#site\"}" https://hooks.slack.com/services/xx/xx/xx + # fi + echo -e "\n🔄 Sync from $FROM to $TO complete.\n\n ${bold}$TOSITE${normal}\n" +fi diff --git a/sync.json b/sync.json new file mode 100644 index 0000000..ae150cc --- /dev/null +++ b/sync.json @@ -0,0 +1,15 @@ +{ + "uploads": "web/app/uploads/", + "connection": { + "development": "web@example.com:/srv/www/example.com/shared/uploads/", + "staging": "web@staging.example.com:/srv/www/example.com/shared/uploads/", + "production": "web@example.com:/srv/www/example.com/shared/uploads/" + }, + "sites": [ + { + "production": "https://example.com", + "staging": "https://staging.example.com", + "development": "http://example.com" + } + ] +} From 18545f0d8aec96b0bbad2bc330f61a08a69bfc2f Mon Sep 17 00:00:00 2001 From: Kelly Mears <developers@tinypixel.dev> Date: Fri, 6 May 2022 14:17:14 -0700 Subject: [PATCH 2/4] improve/fix --- multi-sync.sh | 45 +++++++++++++++++++++++---------------------- sync.json | 11 ++++++++--- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/multi-sync.sh b/multi-sync.sh index a40f592..a3b51a5 100644 --- a/multi-sync.sh +++ b/multi-sync.sh @@ -9,40 +9,43 @@ # > `brew install jq bash` # > `source ~/.zshrc` -JSON_MAPPING="./sync.json" +basedir="$(dirname $(realpath ../composer.json))" +jsonpath="$basedir/scripts/sync.json" +uploads="$basedir/$(jq -r '.uploads' $jsonpath)" function connection() { - echo $(jq -r ".connection | .$1" $JSON_MAPPING) + echo $(jq -r ".connection | .$1" $jsonpath) } function site() { - echo $(jq -r ".sites | .[0] | .$1" $JSON_MAPPING) + echo $(jq -r ".sites | .[0] | .$1" $jsonpath) } function env() { - echo $(jq -r ".sites | .[$1] | .$2 | .$3" $JSON_MAPPING) + echo $(jq -r ".sites | .[$1] | .$2 | .$3" $jsonpath) +} + +function protocol() { + result="$(jq -r ".secure | .$1" $jsonpath)" + if [[ "$result" == "true" ]]; then + echo "https://" + else + echo "http://" + fi } function replace() { from=$1 to=$2 - use_alias=$3 id=0 - for _x in $(jq -c ".sites | .[]" $JSON_MAPPING); do - if [ "$use_alias" = true ]; then - echo "[@${to}] $(env $id $from) => $(env $id $to)"; - wp search-replace "@${2}" "$(env $id $from)" "$(env $id $to)" --all-tables-with-prefix --network; - else - echo "[local] $(env $id $from) => $(env $id $to)"; - wp search-replace "$(env $id $from)" "$(env $id $to)" --all-tables-with-prefix --network; - fi - + for _x in $(jq -c ".sites | .[]" $jsonpath); do + echo "[@${to}] $(env $id $from) => $(env $id $to)"; + wp "@${to}" search-replace "$(env $id $from)" "$(env $id $to)" --network; + wp "@${to}" search-replace "$(protocol $from)$(env $id $to)" "$(protocol $to)$(env $id $to)" --network; ((id++)); done } -uploads=$(jq -r ".uploads" $JSON_MAPPING) - LOCAL=false SKIP_DB=false SKIP_ASSETS=false @@ -166,18 +169,16 @@ if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then if [[ "$LOCAL" = true && $TO == "development" ]]; then wp db export --default-character-set=utf8mb4 && wp db reset --yes && - wp "@$FROM" db export --default-character-set=utf8mb4 - | wp db import - && + wp "@$FROM" db export --default-character-set=utf8mb4 - | wp db import - replace $FROM $TO elif [[ "$LOCAL" = true && $FROM == "development" ]]; then wp "@$TO" db export --default-character-set=utf8mb4 && - wp "@$TO" db reset --yes && wp db export --default-character-set=utf8mb4 - | wp "@$TO" db import - && - replace $FROM $TO true + replace $FROM $TO else wp "@$TO" db export --default-character-set=utf8mb4 && - wp "@$TO" db reset --yes && wp "@$FROM" db export --default-character-set=utf8mb4 - | wp "@$TO" db import - && - replace $FROM $TO true + replace $FROM $TO fi fi @@ -185,7 +186,7 @@ if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then then echo "Syncing assets..." # Sync uploads directory - $(chmod -R 755 "$uploads") && + $(chmod -R 755 $uploads) && if [[ $DIR == "horizontally"* ]]; then [[ $FROMDIR =~ ^(.*): ]] && FROMHOST=${BASH_REMATCH[1]} [[ $FROMDIR =~ ^(.*):(.*)$ ]] && FROMDIR=${BASH_REMATCH[2]} diff --git a/sync.json b/sync.json index ae150cc..eaade29 100644 --- a/sync.json +++ b/sync.json @@ -5,11 +5,16 @@ "staging": "web@staging.example.com:/srv/www/example.com/shared/uploads/", "production": "web@example.com:/srv/www/example.com/shared/uploads/" }, + "secure": { + "development": false, + "staging": true, + "production": true + }, "sites": [ { - "production": "https://example.com", - "staging": "https://staging.example.com", - "development": "http://example.com" + "production": "example.com", + "staging": "staging.example.com", + "development": "example.com" } ] } From b63b2cdb86abcd898184b7d2ddd05cc2f0c31f6d Mon Sep 17 00:00:00 2001 From: Kelly Mears <developers@tinypixel.dev> Date: Fri, 6 May 2022 14:17:58 -0700 Subject: [PATCH 3/4] support local --- multi-sync.sh | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/multi-sync.sh b/multi-sync.sh index a3b51a5..b9a1cc9 100644 --- a/multi-sync.sh +++ b/multi-sync.sh @@ -37,11 +37,26 @@ function protocol() { function replace() { from=$1 to=$2 + local=$3 id=0 for _x in $(jq -c ".sites | .[]" $jsonpath); do echo "[@${to}] $(env $id $from) => $(env $id $to)"; - wp "@${to}" search-replace "$(env $id $from)" "$(env $id $to)" --network; - wp "@${to}" search-replace "$(protocol $from)$(env $id $to)" "$(protocol $to)$(env $id $to)" --network; + echo "[@${to}] $(protocol $from)$(env $id $to) => $(protocol $to)$(env $id $to)"; + + if [[ "$local" == "true" ]]; then + wp search-replace "$(env $id $from)" "$(env $id $to)" --network; + if [[ "$(protocol $from)$(env $id $to)" != "$(protocol $from)$(env $id $to)" ]] + then + wp search-replace "$(protocol $from)$(env $id $to)" "$(protocol $to)$(env $id $to)" --network; + fi + else + wp "@${to}" search-replace "$(env $id $from)" "$(env $id $to)" --network; + if [[ "$(protocol $from)$(env $id $to)" != "$(protocol $from)$(env $id $to)" ]] + then + wp "@${to}" search-replace "$(protocol $from)$(env $id $to)" "$(protocol $to)$(env $id $to)" --network; + fi + fi; + ((id++)); done } @@ -170,13 +185,15 @@ if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then wp db export --default-character-set=utf8mb4 && wp db reset --yes && wp "@$FROM" db export --default-character-set=utf8mb4 - | wp db import - - replace $FROM $TO + replace $FROM $TO true elif [[ "$LOCAL" = true && $FROM == "development" ]]; then wp "@$TO" db export --default-character-set=utf8mb4 && + wp "@$TO" db reset --yes && wp db export --default-character-set=utf8mb4 - | wp "@$TO" db import - && replace $FROM $TO else wp "@$TO" db export --default-character-set=utf8mb4 && + wp "@$TO" db reset --yes && wp "@$FROM" db export --default-character-set=utf8mb4 - | wp "@$TO" db import - && replace $FROM $TO fi From 2ea1a79b6660a87e33e65a2729739b087ca6fbbf Mon Sep 17 00:00:00 2001 From: Kelly Mears <developers@tinypixel.dev> Date: Fri, 6 May 2022 14:19:59 -0700 Subject: [PATCH 4/4] protocol search-replace conditional --- multi-sync.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/multi-sync.sh b/multi-sync.sh index b9a1cc9..f8033df 100644 --- a/multi-sync.sh +++ b/multi-sync.sh @@ -45,13 +45,13 @@ function replace() { if [[ "$local" == "true" ]]; then wp search-replace "$(env $id $from)" "$(env $id $to)" --network; - if [[ "$(protocol $from)$(env $id $to)" != "$(protocol $from)$(env $id $to)" ]] + if [[ "$(protocol $from)$(env $id $to)" != "$(protocol $to)$(env $id $to)" ]] then wp search-replace "$(protocol $from)$(env $id $to)" "$(protocol $to)$(env $id $to)" --network; fi else wp "@${to}" search-replace "$(env $id $from)" "$(env $id $to)" --network; - if [[ "$(protocol $from)$(env $id $to)" != "$(protocol $from)$(env $id $to)" ]] + if [[ "$(protocol $from)$(env $id $to)" != "$(protocol $to)$(env $id $to)" ]] then wp "@${to}" search-replace "$(protocol $from)$(env $id $to)" "$(protocol $to)$(env $id $to)" --network; fi