11#! /usr/bin/env bash
22set -euo pipefail
33
4- # Copies the production database (via Dokku) into the local staging database
4+ # Copies the production database(s) (via Dokku) into the local staging database(s)
55# and then boots the Rails server on port 4444.
66#
7+ # This script supports multi-shard setups. It will import:
8+ # 1. Primary database (required)
9+ # 2. Demo shard database (optional, if DOKKU_DEMO_SHARD_SERVICE is set)
10+ # 3. Tenant shard 1 database (optional, if DOKKU_TENANT_SHARD_1_SERVICE is set)
11+ #
712# Required environment variables:
813# PRODUCTION_SSH_HOST - e.g. root@example.com
9- # DOKKU_POSTGRES_SERVICE - Dokku Postgres service name to export
14+ # DOKKU_POSTGRES_SERVICE - Dokku Postgres service name for primary database
15+ #
16+ # Optional environment variables (shards):
17+ # DOKKU_DEMO_SHARD_SERVICE - Dokku Postgres service for demo shard
18+ # DOKKU_TENANT_SHARD_1_SERVICE - Dokku Postgres service for tenant shard 1
19+ # LOCAL_DEMO_SHARD_DB - local demo shard database (default: pwb_staging_demo_shard)
20+ # LOCAL_TENANT_SHARD_1_DB - local tenant shard 1 database (default: pwb_staging_shard_1)
1021#
11- # Optional environment variables:
22+ # Optional environment variables (general) :
1223# SSH_COMMAND - command used for ssh/rsync (default: ssh)
1324# RSYNC_RSH - overrides ssh command just for rsync (default: SSH_COMMAND)
1425# REMOTE_DUMP_PATH - remote path for the dump (default: /tmp/<service>.dump)
@@ -27,12 +38,15 @@ set -euo pipefail
2738# ./scripts/restore_staging_from_production.sh --import-only
2839# ./scripts/restore_staging_from_production.sh --server-only
2940# ./scripts/restore_staging_from_production.sh --skip-import --skip-server
41+ # ./scripts/restore_staging_from_production.sh --primary-only # skip shard imports
3042#
3143# Flags:
3244# --import-only Only refresh the database, do not start the server
3345# --server-only Only start the server using the existing database
3446# --skip-import Skip the import step (same as --server-only)
3547# --skip-server Skip starting the server (same as --import-only)
48+ # --primary-only Only import primary database, skip shards
49+ # --shards-only Only import shard databases, skip primary
3650# -h|--help Show this help and exit
3751
3852ROOT_DIR=" $( cd " $( dirname " ${BASH_SOURCE[0]} " ) /.." && pwd) "
@@ -47,7 +61,14 @@ BACKUP_DIR="${BACKUP_DIR:-$ROOT_DIR/db_imports}"
4761if [[ " $BACKUP_DIR " != /* ]]; then
4862 BACKUP_DIR=" $ROOT_DIR /${BACKUP_DIR# ./ } "
4963fi
64+
65+ # Primary database
5066LOCAL_DB_NAME=" ${LOCAL_STAGING_DB:- pwb_staging} "
67+
68+ # Shard databases
69+ LOCAL_DEMO_SHARD_DB=" ${LOCAL_DEMO_SHARD_DB:- pwb_staging_demo_shard} "
70+ LOCAL_TENANT_SHARD_1_DB=" ${LOCAL_TENANT_SHARD_1_DB:- pwb_staging_shard_1} "
71+
5172LOCAL_DB_HOST=" ${LOCAL_DB_HOST:- localhost} "
5273LOCAL_DB_PORT=" ${LOCAL_DB_PORT:- 5432} "
5374LOCAL_DB_USER=" ${LOCAL_DB_USER:- } "
@@ -57,6 +78,8 @@ RAILS_PORT="${RAILS_PORT:-4444}"
5778
5879ACTION_IMPORT=true
5980ACTION_SERVER=true
81+ IMPORT_PRIMARY=true
82+ IMPORT_SHARDS=true
6083
6184log () {
6285 printf ' [%s] %s\n' " $( date +' %Y-%m-%d %H:%M:%S' ) " " $* "
@@ -71,7 +94,18 @@ Options:
7194 --server-only Run server only (skip import)
7295 --skip-import Alias for --server-only
7396 --skip-server Alias for --import-only
97+ --primary-only Only import primary database, skip shards
98+ --shards-only Only import shard databases, skip primary
7499 -h, --help Show this help and exit
100+
101+ Shard Environment Variables:
102+ DOKKU_DEMO_SHARD_SERVICE Dokku service name for demo shard
103+ DOKKU_TENANT_SHARD_1_SERVICE Dokku service name for tenant shard 1
104+
105+ Example with shards:
106+ DOKKU_DEMO_SHARD_SERVICE=pwb-demo-shard \
107+ DOKKU_TENANT_SHARD_1_SERVICE=pwb-shard-1 \
108+ ./scripts/restore_staging_from_production.sh
75109EOF
76110}
77111
@@ -121,21 +155,27 @@ SQL
121155 fi
122156}
123157
124- perform_import () {
125- : " ${PRODUCTION_SSH_HOST:? Set PRODUCTION_SSH_HOST (e.g. root@ example.com)} "
126- : " ${DOKKU_POSTGRES_SERVICE:? Set DOKKU_POSTGRES_SERVICE (dokku postgres: export service name)} "
158+ # Import a single database from Dokku
159+ # Arguments: $1 = dokku service name, $2 = local database name, $3 = label (for logging)
160+ import_database () {
161+ local dokku_service=" $1 "
162+ local local_db=" $2 "
163+ local label=" ${3:- $dokku_service } "
164+
165+ log " === Importing $label ==="
127166
128167 mkdir -p " $BACKUP_DIR "
129168
130169 local timestamp
131170 timestamp=" $( date +%Y%m%d-%H%M%S) "
132- local remote_dump_path
133- remote_dump_path=" ${REMOTE_DUMP_PATH:-/ tmp/ ${DOKKU_POSTGRES_SERVICE} .dump} "
134- local local_dump_file
135- local_dump_file=" ${BACKUP_DIR} /${timestamp} -${DOKKU_POSTGRES_SERVICE} .dump"
171+ local remote_dump_path=" /tmp/${dokku_service} .dump"
172+ local local_dump_file=" ${BACKUP_DIR} /${timestamp} -${dokku_service} .dump"
136173
137- log " Exporting $DOKKU_POSTGRES_SERVICE from $PRODUCTION_SSH_HOST "
138- $SSH_COMMAND " $PRODUCTION_SSH_HOST " " dokku postgres:export $DOKKU_POSTGRES_SERVICE > $remote_dump_path "
174+ log " Exporting $dokku_service from $PRODUCTION_SSH_HOST "
175+ if ! $SSH_COMMAND " $PRODUCTION_SSH_HOST " " dokku postgres:export $dokku_service > $remote_dump_path " 2> /dev/null; then
176+ log " WARNING: Failed to export $dokku_service - service may not exist"
177+ return 1
178+ fi
139179
140180 log " Downloading dump to $local_dump_file "
141181 RSYNC_CMD=(rsync -az)
@@ -148,14 +188,61 @@ perform_import() {
148188 $SSH_COMMAND " $PRODUCTION_SSH_HOST " " rm -f $remote_dump_path "
149189
150190 ensure_role_exists " $LOCAL_DB_USER "
151- ensure_database_exists " $LOCAL_DB_NAME "
191+ ensure_database_exists " $local_db "
152192
153- log " Restoring into local database $LOCAL_DB_NAME "
154- PG_RESTORE_CMD=(pg_restore --clean --no-owner --host " $LOCAL_DB_HOST " --port " $LOCAL_DB_PORT " --dbname " $LOCAL_DB_NAME " )
193+ log " Restoring into local database $local_db "
194+ PG_RESTORE_CMD=(pg_restore --clean --no-owner --host " $LOCAL_DB_HOST " --port " $LOCAL_DB_PORT " --dbname " $local_db " )
155195 if [[ -n " $LOCAL_DB_USER " ]]; then
156196 PG_RESTORE_CMD+=(--username " $LOCAL_DB_USER " )
157197 fi
158- " ${PG_RESTORE_CMD[@]} " " $local_dump_file "
198+ # Use --if-exists to avoid errors on first restore when tables don't exist
199+ " ${PG_RESTORE_CMD[@]} " --if-exists " $local_dump_file " || {
200+ log " WARNING: pg_restore had some errors (this is often normal for --clean on first run)"
201+ }
202+
203+ log " Successfully imported $label into $local_db "
204+ return 0
205+ }
206+
207+ perform_import () {
208+ local imported_primary=false
209+ local imported_shards=0
210+
211+ # Import primary database
212+ if [[ " $IMPORT_PRIMARY " == true ]]; then
213+ if import_database " $DOKKU_POSTGRES_SERVICE " " $LOCAL_DB_NAME " " primary database" ; then
214+ imported_primary=true
215+ fi
216+ else
217+ log " Skipping primary database import"
218+ fi
219+
220+ # Import shard databases
221+ if [[ " $IMPORT_SHARDS " == true ]]; then
222+ # Demo shard
223+ if [[ -n " ${DOKKU_DEMO_SHARD_SERVICE:- } " ]]; then
224+ if import_database " $DOKKU_DEMO_SHARD_SERVICE " " $LOCAL_DEMO_SHARD_DB " " demo shard" ; then
225+ (( imported_shards++ )) || true
226+ fi
227+ else
228+ log " Skipping demo shard (DOKKU_DEMO_SHARD_SERVICE not set)"
229+ fi
230+
231+ # Tenant shard 1
232+ if [[ -n " ${DOKKU_TENANT_SHARD_1_SERVICE:- } " ]]; then
233+ if import_database " $DOKKU_TENANT_SHARD_1_SERVICE " " $LOCAL_TENANT_SHARD_1_DB " " tenant shard 1" ; then
234+ (( imported_shards++ )) || true
235+ fi
236+ else
237+ log " Skipping tenant shard 1 (DOKKU_TENANT_SHARD_1_SERVICE not set)"
238+ fi
239+ else
240+ log " Skipping shard imports"
241+ fi
242+
243+ log " === Import Summary ==="
244+ log " Primary database: $( [ " $imported_primary " == true ] && echo " imported" || echo " skipped" ) "
245+ log " Shards imported: $imported_shards "
159246}
160247
161248start_server () {
@@ -168,7 +255,20 @@ start_server() {
168255 fi
169256 fi
170257
258+ # Set up shard database URLs for staging environment
259+ # These match the database names used in config/database.yml for staging
260+ if [[ -n " ${DOKKU_DEMO_SHARD_SERVICE:- } " ]] || [[ -f " $BACKUP_DIR " /* -" ${DOKKU_DEMO_SHARD_SERVICE:- demo} " * .dump ]] 2> /dev/null; then
261+ export PWB_STAGING_DEMO_SHARD_DATABASE_URL=" postgresql://${LOCAL_DB_USER:- } @${LOCAL_DB_HOST} :${LOCAL_DB_PORT} /${LOCAL_DEMO_SHARD_DB} "
262+ log " Demo shard URL: $PWB_STAGING_DEMO_SHARD_DATABASE_URL "
263+ fi
264+
265+ if [[ -n " ${DOKKU_TENANT_SHARD_1_SERVICE:- } " ]] || [[ -f " $BACKUP_DIR " /* -" ${DOKKU_TENANT_SHARD_1_SERVICE:- shard} " * .dump ]] 2> /dev/null; then
266+ export PWB_STAGING_TENANT_SHARD_1_DATABASE_URL=" postgresql://${LOCAL_DB_USER:- } @${LOCAL_DB_HOST} :${LOCAL_DB_PORT} /${LOCAL_TENANT_SHARD_1_DB} "
267+ log " Tenant shard 1 URL: $PWB_STAGING_TENANT_SHARD_1_DATABASE_URL "
268+ fi
269+
171270 log " Starting Rails server on port $RAILS_PORT (Ctrl+C to stop)"
271+ log " Shard environment variables set for staging"
172272 RAILS_ENV=staging bundle exec rails server -p " $RAILS_PORT "
173273}
174274
@@ -180,6 +280,12 @@ while [[ $# -gt 0 ]]; do
180280 --server-only|--skip-import)
181281 ACTION_IMPORT=false
182282 ;;
283+ --primary-only)
284+ IMPORT_SHARDS=false
285+ ;;
286+ --shards-only)
287+ IMPORT_PRIMARY=false
288+ ;;
183289 -h|--help)
184290 usage
185291 exit 0
0 commit comments