diff --git a/.env.example b/.env.example index 62142a7..73bbd51 100644 --- a/.env.example +++ b/.env.example @@ -27,7 +27,7 @@ SKIP_WP_INIT=false # Final site URL used by WordPress # In production this should be: https:// -SITE_URL=http://${SERVER_NAME}:${HTTP_PORT} +SITE_URL=http://${SERVER_NAME}:${HTTP_PORT:-8000} # Columns to skip during WP-CLI search-replace # Comma-separated, no spaces (e.g. guid,post_content) diff --git a/Makefile b/Makefile index 0274c50..165bb7c 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ db-backup: @$(COMPOSE_DEV) exec -T db-cli sh /scripts/db-backup/run-db-backup-once.sh db-restore: - @$(COMPOSE_DEV) exec -T db-cli sh -c "/scripts/db-cli/run-db-restore.sh '$(SQLFILE)'" + @$(COMPOSE_DEV) exec -T db-cli sh -c "/scripts/db-cli/run-db-restore.sh '$(SQL_FILE)'" # -------------------------------------------------- # Production targets diff --git a/README.md b/README.md index 2db71df..1644b9e 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,7 @@ The `Makefile` provides a stable, minimal interface for common operations: - `make db-backup`: Execute a one-off database backup. -- `make db-restore SQLFILE=x.sql`: Restore a specific dump from the `db/` directory. +- `make db-restore SQL_FILE=x.sql`: Restore a specific dump from the `db/init` directory. ## Architecture & Workflow diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 55ae02b..207d560 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -1,3 +1,17 @@ +x-mysql-app-env: &mysql-app-env + MYSQL_HOST: database + MYSQL_PORT: "3306" + MYSQL_DATABASE: ${DATABASE_NAME} + MYSQL_USER: ${DATABASE_USER} + MYSQL_PASSWORD: ${DATABASE_PASSWORD} + +x-wordpress-db-env: &wordpress-db-env + WORDPRESS_DB_HOST: database:3306 + WORDPRESS_DB_NAME: ${DATABASE_NAME} + WORDPRESS_DB_USER: ${DATABASE_USER} + WORDPRESS_DB_PASSWORD: ${DATABASE_PASSWORD} + WORDPRESS_PATH: /var/www/html + services: database: volumes: @@ -19,26 +33,20 @@ services: volumes: - ./src:/var/www/html/wp-content - ./scripts:/scripts:ro - entrypoint: ["/scripts/wp-init/entrypoint.sh"] db-backup: volumes: - ./db/backups:/backups - ./scripts:/scripts:ro - entrypoint: ["/scripts/db-backup/entrypoint.sh"] wp-cli: depends_on: - wordpress - database - image: wordpress:cli - env_file: .env + image: wordpress:cli-2.10.0 environment: - WORDPRESS_DB_HOST: database:3306 - WORDPRESS_DB_NAME: ${DATABASE_NAME} - WORDPRESS_DB_USER: ${DATABASE_USER} - WORDPRESS_DB_PASSWORD: ${DATABASE_PASSWORD} - WORDPRESS_PATH: /var/www/html + <<: *wordpress-db-env + SITE_URL: ${SITE_URL} volumes: - ./src:/var/www/html/wp-content - ./scripts:/scripts:ro @@ -51,12 +59,8 @@ services: depends_on: - database image: mysql:8.4.7 - env_file: .env environment: - MYSQL_DATABASE: ${DATABASE_NAME} - MYSQL_USER: ${DATABASE_USER} - MYSQL_PASSWORD: ${DATABASE_PASSWORD} - MYSQL_ROOT_PASSWORD: ${DATABASE_ROOT_PASSWORD} + <<: *mysql-app-env tmpfs: - /var/lib/mysql volumes: @@ -72,7 +76,6 @@ services: - database image: phpmyadmin:5.2.3 restart: unless-stopped - env_file: .env environment: PMA_HOST: database PMA_PORT: 3306 diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 8dce94d..90becd0 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -59,7 +59,6 @@ services: restart: "no" volumes: - ./scripts:/scripts:ro - entrypoint: ["/scripts/wp-init/entrypoint.sh"] deploy: resources: limits: @@ -72,7 +71,6 @@ services: volumes: - ./scripts:/scripts:ro - db_backups:/backups - entrypoint: ["/scripts/db-backup/entrypoint.sh"] deploy: resources: limits: diff --git a/docker-compose.yml b/docker-compose.yml index eb2b9e2..9b36265 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,12 +1,23 @@ +x-mysql-app-env: &mysql-app-env + MYSQL_HOST: database + MYSQL_PORT: "3306" + MYSQL_DATABASE: ${DATABASE_NAME} + MYSQL_USER: ${DATABASE_USER} + MYSQL_PASSWORD: ${DATABASE_PASSWORD} + +x-wordpress-db-env: &wordpress-db-env + WORDPRESS_DB_HOST: database:3306 + WORDPRESS_DB_NAME: ${DATABASE_NAME} + WORDPRESS_DB_USER: ${DATABASE_USER} + WORDPRESS_DB_PASSWORD: ${DATABASE_PASSWORD} + WORDPRESS_PATH: /var/www/html + services: database: image: mysql:8.4.7 restart: unless-stopped - env_file: .env environment: - MYSQL_DATABASE: ${DATABASE_NAME} - MYSQL_USER: ${DATABASE_USER} - MYSQL_PASSWORD: ${DATABASE_PASSWORD} + <<: *mysql-app-env MYSQL_ROOT_PASSWORD: ${DATABASE_ROOT_PASSWORD} healthcheck: test: @@ -24,12 +35,8 @@ services: - database image: wordpress:6.9.0-fpm-alpine restart: unless-stopped - env_file: .env environment: - WORDPRESS_DB_HOST: database:3306 - WORDPRESS_DB_NAME: ${DATABASE_NAME} - WORDPRESS_DB_USER: ${DATABASE_USER} - WORDPRESS_DB_PASSWORD: ${DATABASE_PASSWORD} + <<: *wordpress-db-env volumes: - wordpress:/var/www/html networks: @@ -40,9 +47,8 @@ services: - wordpress image: nginx:1.29.2-alpine restart: unless-stopped - env_file: .env environment: - - SERVER_NAME=${SERVER_NAME} + SERVER_NAME: ${SERVER_NAME} volumes: - wordpress:/var/www/html networks: @@ -52,16 +58,15 @@ services: depends_on: - wordpress - database - image: wordpress:cli - env_file: .env + image: wordpress:cli-2.10.0 environment: - WORDPRESS_DB_HOST: database:3306 - WORDPRESS_DB_NAME: ${DATABASE_NAME} - WORDPRESS_DB_USER: ${DATABASE_USER} - WORDPRESS_DB_PASSWORD: ${DATABASE_PASSWORD} - WORDPRESS_PATH: /var/www/html + <<: *wordpress-db-env + SITE_URL: ${SITE_URL} + SKIP_COLUMNS: ${SKIP_COLUMNS} + SKIP_WP_INIT: ${SKIP_WP_INIT} volumes: - wordpress:/var/www/html + entrypoint: ["/scripts/wp-init/entrypoint.sh"] networks: - internal @@ -69,16 +74,15 @@ services: depends_on: - database image: mysql:8.4.7 - env_file: .env environment: - MYSQL_DATABASE: ${DATABASE_NAME} - MYSQL_USER: ${DATABASE_USER} - MYSQL_PASSWORD: ${DATABASE_PASSWORD} - DB_BACKUP_MAX_FILES: ${DATABASE_BACKUP_MAX_FILES:-3} - DB_BACKUP_INITIAL_DELAY: ${DATABASE_BACKUP_INITIAL_DELAY:-60} - DB_BACKUP_INTERVAL: ${DATABASE_BACKUP_INTERVAL:-3600} + <<: *mysql-app-env + DB_BACKUP_INITIAL_DELAY: ${DATABASE_BACKUP_INITIAL_DELAY} + DB_BACKUP_INTERVAL: ${DATABASE_BACKUP_INTERVAL} + DB_BACKUP_MAX_FILES: ${DATABASE_BACKUP_MAX_FILES} + SKIP_DB_BACKUP: ${SKIP_DB_BACKUP} tmpfs: - /var/lib/mysql + entrypoint: ["/scripts/db-backup/entrypoint.sh"] networks: - internal diff --git a/scripts/db-backup/entrypoint.sh b/scripts/db-backup/entrypoint.sh index a9fe2e0..9bee22d 100755 --- a/scripts/db-backup/entrypoint.sh +++ b/scripts/db-backup/entrypoint.sh @@ -1,7 +1,9 @@ #!/bin/sh set -e -if [ "${SKIP_DB_BACKUP}" = "true" ]; then +SKIP_DB_BACKUP="${SKIP_DB_BACKUP:-false}" + +if [ "$SKIP_DB_BACKUP" = "true" ]; then echo "DB backup skipped" exit 0 fi diff --git a/scripts/db-backup/run-db-backup-once.sh b/scripts/db-backup/run-db-backup-once.sh index e917dc3..09a05fb 100755 --- a/scripts/db-backup/run-db-backup-once.sh +++ b/scripts/db-backup/run-db-backup-once.sh @@ -4,11 +4,14 @@ set -e . /scripts/db-common/create-db-client-config.sh . /scripts/db-backup/lib/backup.sh -DB_NAME="${MYSQL_DATABASE}" +DB_HOST="${MYSQL_HOST:-database}" +DB_PORT="${MYSQL_PORT:-3306}" +DB_NAME="${MYSQL_DATABASE:-wordpress}" + BACKUP_DIR="/backups" echo "Creating DB client config" -create_db_client_config "${MYSQL_HOST:-}" "${MYSQL_PORT:-}" "${MYSQL_USER}" "${MYSQL_PASSWORD}" +create_db_client_config "$DB_HOST" "$DB_PORT" "${MYSQL_USER}" "${MYSQL_PASSWORD}" echo "Running one-shot backup..." do_backup "$DB_NAME" "$BACKUP_DIR" diff --git a/scripts/db-backup/run-db-backup-periodic.sh b/scripts/db-backup/run-db-backup-periodic.sh index 2aca747..e1a6bc1 100755 --- a/scripts/db-backup/run-db-backup-periodic.sh +++ b/scripts/db-backup/run-db-backup-periodic.sh @@ -6,15 +6,20 @@ set -e . /scripts/db-backup/lib/backup.sh . /scripts/db-backup/lib/rotation.sh -DB_NAME="${MYSQL_DATABASE}" -BACKUP_DIR="/backups" -BACKUP_MAX_FILES="${DB_BACKUP_MAX_FILES}" +DB_HOST="${MYSQL_HOST:-database}" +DB_PORT="${MYSQL_PORT:-3306}" +DB_NAME="${MYSQL_DATABASE:-wordpress}" + +BACKUP_INITIAL_DELAY="${DB_BACKUP_INITIAL_DELAY:-60s}" +BACKUP_INTERVAL="${DB_BACKUP_INTERVAL:-3600s}" +BACKUP_MAX_FILES="${DB_BACKUP_MAX_FILES:-3}" -BACKUP_INITIAL_DELAY_SEC=$(parse_interval "${DB_BACKUP_INITIAL_DELAY}") -BACKUP_INTERVAL_SEC=$(parse_interval "${DB_BACKUP_INTERVAL}") +BACKUP_DIR="/backups" +BACKUP_INITIAL_DELAY_SEC=$(parse_interval "$BACKUP_INITIAL_DELAY") +BACKUP_INTERVAL_SEC=$(parse_interval "$BACKUP_INTERVAL") echo "Creating DB client config" -create_db_client_config "${MYSQL_HOST:-}" "${MYSQL_PORT:-}" "${MYSQL_USER}" "${MYSQL_PASSWORD}" +create_db_client_config "$DB_HOST" "$DB_PORT" "${MYSQL_USER}" "${MYSQL_PASSWORD}" echo "Initial delay: $(format_interval "$BACKUP_INITIAL_DELAY_SEC")" sleep "$BACKUP_INITIAL_DELAY_SEC" diff --git a/scripts/db-cli/run-db-restore.sh b/scripts/db-cli/run-db-restore.sh index c61c0df..c3c8ed2 100755 --- a/scripts/db-cli/run-db-restore.sh +++ b/scripts/db-cli/run-db-restore.sh @@ -1,9 +1,10 @@ #!/bin/sh set -e +. /scripts/utils/check-required-vars.sh . /scripts/db-common/create-db-client-config.sh -FILE="$1" +FILE="$(basename "$1")" if [ -z "$FILE" ]; then echo "Error: no SQL file provided" >&2 @@ -18,8 +19,13 @@ if [ ! -f "$SQL_PATH" ]; then exit 1 fi +check_required_vars "MYSQL_DATABASE MYSQL_USER MYSQL_PASSWORD" + +DB_HOST="${MYSQL_HOST:-database}" +DB_PORT="${MYSQL_PORT:-3306}" + echo "Creating DB client config" -create_db_client_config "${MYSQL_HOST:-}" "${MYSQL_PORT:-}" "${MYSQL_USER}" "${MYSQL_PASSWORD}" +create_db_client_config "$DB_HOST" "$DB_PORT" "${MYSQL_USER}" "${MYSQL_PASSWORD}" echo "Restoring $FILE into database $MYSQL_DATABASE..." mysql "$MYSQL_DATABASE" < "$SQL_PATH" diff --git a/scripts/wp-init/entrypoint.sh b/scripts/wp-init/entrypoint.sh index c24d0ed..64745f3 100755 --- a/scripts/wp-init/entrypoint.sh +++ b/scripts/wp-init/entrypoint.sh @@ -1,6 +1,8 @@ #!/bin/sh set -e +SKIP_WP_INIT="${SKIP_WP_INIT:-false}" + if [ "${SKIP_WP_INIT}" = "true" ]; then echo "WP initialization skipped" exit 0 diff --git a/scripts/wp-init/site-url/get-current-site-url.sh b/scripts/wp-init/site-url/get-current-site-url.sh index ab894d0..17cb839 100755 --- a/scripts/wp-init/site-url/get-current-site-url.sh +++ b/scripts/wp-init/site-url/get-current-site-url.sh @@ -1,11 +1,17 @@ #!/bin/sh set -e +. /scripts/utils/check-required-vars.sh . /scripts/wp-cli/check-wp-path.sh . /scripts/wp-cli/check-wp-cli.sh . /scripts/wp-cli/check-wp-installed.sh . /scripts/db-common/wait-for-db.sh +REQUIRED_VARS="WORDPRESS_DB_HOST WORDPRESS_DB_NAME WORDPRESS_DB_USER WORDPRESS_DB_PASSWORD \ + WORDPRESS_PATH" + +check_required_vars "$REQUIRED_VARS" + check_wp_path check_wp_cli diff --git a/scripts/wp-init/site-url/update-site-url.sh b/scripts/wp-init/site-url/update-site-url.sh index 4cbf2cc..01f658b 100755 --- a/scripts/wp-init/site-url/update-site-url.sh +++ b/scripts/wp-init/site-url/update-site-url.sh @@ -7,7 +7,10 @@ set -e . /scripts/wp-cli/check-wp-installed.sh . /scripts/db-common/wait-for-db.sh -check_required_vars "CURRENT_SITE_URL SITE_URL" +REQUIRED_VARS="WORDPRESS_DB_HOST WORDPRESS_DB_NAME WORDPRESS_DB_USER WORDPRESS_DB_PASSWORD WORDPRESS_PATH \ + CURRENT_SITE_URL SITE_URL" + +check_required_vars "$REQUIRED_VARS" check_wp_path check_wp_cli @@ -16,7 +19,7 @@ check_wp_cli wait_for_db check_wp_installed || exit 0 -echo "Starting site URL update: ${CURRENT_SITE_URL} → ${SITE_URL}" +echo "Starting site URL update: ${CURRENT_SITE_URL} -> ${SITE_URL}" SKIP_COLUMNS_FLAG="" if [ -n "$SKIP_COLUMNS" ]; then @@ -29,7 +32,7 @@ fi # shellcheck disable=SC2086 if wp search-replace "$CURRENT_SITE_URL" "$SITE_URL" \ - $SKIP_COLUMNS_FLAG \ + ${SKIP_COLUMNS_FLAG} \ --all-tables \ --precise \ --allow-root; then