Skip to content

Instantly share code, notes, and snippets.

@mattbloomfield
Created May 21, 2026 22:30
Show Gist options
  • Select an option

  • Save mattbloomfield/9da043594087b91eb1867d4cd7a71452 to your computer and use it in GitHub Desktop.

Select an option

Save mattbloomfield/9da043594087b91eb1867d4cd7a71452 to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
## Description: Sync database and/or files between two remote environments
## Usage: sync:remote
## Example: "ddev sync:remote production staging db files"
source "$(dirname "$0")/.helpers.sh"
ddev_setup_error_handling
BUCKET_PREFIX="mysite-assets"
ENTRIES_TO_ENABLE="3177012,3177117,4210469,4348080" # we have a few test entries that are disabled in prod but we enable everywhere else
# ─── Help ─────────────────────────────────────────────────────────────────────
if ddev_check_help "$@"; then
ddev_print_help \
"Usage: ddev sync:remote [source] [destination] [db|-db] [files|assets|-files|-assets]" \
"" \
"Sync database and/or files between two remote environments." \
"" \
"Arguments:" \
" source Source environment: staging or production" \
" destination Destination environment: development or staging" \
" db Pull the database" \
" -db Skip the database" \
" files, assets Pull assets" \
" -files, -assets Skip assets" \
"" \
"Examples:" \
" ddev sync:remote # Interactive prompts" \
" ddev sync:remote production development db # Pull db from prod to dev" \
" ddev sync:remote production staging db files # Pull both to staging"
fi
# ─── Parse arguments ──────────────────────────────────────────────────────────
SOURCE_ENV=""
DESTINATION_ENV=""
PULL_DB=""
PULL_FILES=""
# Parse positional environments and flags
_SOURCE=""
_DEST=""
for arg in "$@"; do
case $arg in
development|staging|production)
if [[ -z "$_SOURCE" ]]; then
_SOURCE=$arg
elif [[ -z "$_DEST" ]]; then
_DEST=$arg
fi
;;
db) PULL_DB="y" ;;
-db) PULL_DB="n" ;;
files|assets) PULL_FILES="y" ;;
-files|-assets) PULL_FILES="n" ;;
esac
done
# Apply parsed positional environments
if [[ -n "$_SOURCE" ]]; then
SOURCE_ENV="$_SOURCE"
fi
if [[ -n "$_DEST" ]]; then
DESTINATION_ENV="$_DEST"
fi
# ─── Prompt for missing choices ───────────────────────────────────────────────
if [[ -z "$SOURCE_ENV" ]]; then
ddev_prompt_choice "Source environment" "production" "staging production"
SOURCE_ENV="$REPLY"
fi
if [[ -z "$DESTINATION_ENV" ]]; then
ddev_prompt_choice "Destination environment" "development" "development staging"
DESTINATION_ENV="$REPLY"
fi
# Validate source != destination
if [[ "$SOURCE_ENV" == "$DESTINATION_ENV" ]]; then
ddev_error "Source and destination cannot be the same (${SOURCE_ENV})"
exit 1
fi
# Validate source can only be staging or production
if [[ "$SOURCE_ENV" != "staging" && "$SOURCE_ENV" != "production" ]]; then
ddev_error "Source must be 'staging' or 'production', got '${SOURCE_ENV}'"
exit 1
fi
# Validate destination can only be development or staging
if [[ "$DESTINATION_ENV" != "development" && "$DESTINATION_ENV" != "staging" ]]; then
ddev_error "Destination must be 'development' or 'staging', got '${DESTINATION_ENV}'"
exit 1
fi
if [[ -z "$PULL_DB" ]]; then
ddev_prompt_yn "Pull database from ${YELLOW}${SOURCE_ENV}${RESET} to ${YELLOW}${DESTINATION_ENV}${RESET}?" "y"
PULL_DB="$REPLY"
fi
if [[ -z "$PULL_FILES" ]]; then
ddev_prompt_yn "Pull assets from ${YELLOW}${SOURCE_ENV}${RESET} to ${YELLOW}${DESTINATION_ENV}${RESET}?" "n"
PULL_FILES="$REPLY"
fi
# ─── Summary + Confirm ───────────────────────────────────────────────────────
ddev_header "Remote Sync Summary"
ddev_summary_line "Source" "$SOURCE_ENV"
ddev_summary_line "Destination" "$DESTINATION_ENV"
ddev_summary_line "Pull database" "$( [[ "$PULL_DB" == "y" ]] && echo "yes" || echo "no" )"
ddev_summary_line "Pull assets" "$( [[ "$PULL_FILES" == "y" ]] && echo "yes" || echo "no" )"
echo ""
ddev_warn "This will modify the ${YELLOW}${DESTINATION_ENV}${RESET} environment"
ddev_confirm_or_abort "Proceed with remote sync?"
# ─── Sync database ───────────────────────────────────────────────────────────
if [[ "$PULL_DB" == "y" ]]; then
ddev_step "Cloning database from ${YELLOW}${SOURCE_ENV}${RESET} to ${YELLOW}${DESTINATION_ENV}${RESET}"
(
echo "$PULL_DB"
echo "$PULL_FILES"
echo "no"
echo "no"
) | ./craft servd-asset-storage/clone --from="${SOURCE_ENV}" --to="${DESTINATION_ENV}" --wait
ddev_step "Enabling entries on ${YELLOW}${DESTINATION_ENV}${RESET}"
echo "yes" | ./craft servd-asset-storage/command "resave/entries --element-id $ENTRIES_TO_ENABLE --set enabled --to '=true'" --environment="${DESTINATION_ENV}"
ddev_success "Database synced from ${SOURCE_ENV} to ${DESTINATION_ENV}"
fi
# ─── Sync files ──────────────────────────────────────────────────────────────
if [[ "$PULL_FILES" == "y" ]]; then
get_bucket() {
local env=$1
case $env in
development|master) echo "s3://${BUCKET_PREFIX}-development/" ;;
staging) echo "s3://${BUCKET_PREFIX}-staging/" ;;
production) echo "s3://${BUCKET_PREFIX}/" ;;
esac
}
SOURCE_BUCKET=$(get_bucket "$SOURCE_ENV")
DESTINATION_BUCKET=$(get_bucket "$DESTINATION_ENV")
ddev_step "Syncing assets from ${YELLOW}${SOURCE_ENV}${RESET} to ${YELLOW}${DESTINATION_ENV}${RESET}"
aws s3 sync --acl public-read "${SOURCE_BUCKET}" "${DESTINATION_BUCKET}"
ddev_success "Assets synced from ${SOURCE_ENV} to ${DESTINATION_ENV}"
fi
ddev_success "Remote sync complete"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment