Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,12 @@ The repository includes comprehensive migration tools:

- `scripts/migrate.sh` - Main migration script that:
- Backs up existing Ghost installation
- Automatically tries Ghost's database credentials first
- Only prompts for alternative credentials if needed
- Uses `--no-tablespaces` flag to avoid PROCESS privilege requirements
- Converts config.json to environment variables
- Preserves content and database
- Creates recovery script
- Creates recovery script with clear restoration instructions
- Sets up Docker Compose environment

- `scripts/config-to-env.js` - Converts Ghost JSON config to .env format
Expand Down
146 changes: 122 additions & 24 deletions scripts/migrate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,17 @@ cleanup() {

if [[ $exit_code -ne 0 && -f "$RECOVERY_SCRIPT" ]]; then
echo ""
echo "ERROR: Migration failed!"
echo "════════════════════════════════════════════════════════════"
echo "❌ MIGRATION FAILED!"
echo "════════════════════════════════════════════════════════════"
echo ""
echo "Don't worry - your data is safe!"
echo ""
echo "To restore your original Ghost installation, run:"
echo " bash $RECOVERY_SCRIPT"
echo ""
echo "Need help? Check the migration logs above for error details."
echo "════════════════════════════════════════════════════════════"
fi

exit $exit_code
Expand All @@ -66,6 +73,7 @@ trap cleanup EXIT INT TERM

# Create recovery script
create_recovery_script() {
local service_name="$1"
cat > "$RECOVERY_SCRIPT" << EOF
#!/usr/bin/env bash
# Recovery script generated by Ghost migration on $(date)
Expand All @@ -79,15 +87,19 @@ echo "Restoring original Ghost installation..."
docker compose down 2>/dev/null || true

# Re-enable and start the original Ghost service
systemctl enable "${ghost_service_name}"
systemctl start "${ghost_service_name}"

echo "Original Ghost installation has been restored."
echo "You can check the status with: systemctl status ${ghost_service_name}"
if [[ -n "${service_name}" ]]; then
systemctl enable "${service_name}" 2>/dev/null || true
systemctl start "${service_name}" 2>/dev/null || true
echo "Original Ghost installation has been restored."
echo "You can check the status with: systemctl status ${service_name}"
else
echo "Note: Ghost service was not yet stopped, so no restoration needed."
echo "Your original installation should still be running."
fi
EOF

chmod +x "$RECOVERY_SCRIPT"
echo "Recovery script created at: $RECOVERY_SCRIPT"
echo "Recovery script created at: $RECOVERY_SCRIPT"
}

# Validate MySQL connection
Expand Down Expand Up @@ -188,6 +200,21 @@ migrate_content() {
echo "✓ Content migration completed"
}

# Test if we can dump database with given credentials
test_mysql_dump() {
local host=$1
local database=$2
local user=$3
local password=$4

# Try a minimal dump to test permissions
if mysqldump --no-tablespaces --no-data -h"$host" -u"$user" -p"$password" "$database" >/dev/null 2>&1; then
return 0
else
return 1
fi
}

# Export and import database
migrate_database() {
local mysql_host
Expand All @@ -197,9 +224,38 @@ migrate_database() {

echo "Exporting database from $mysql_host..."

# Export database
if ! mysqldump -h"$mysql_host" -u"$mysql_user" -p"$mysql_password" "$mysql_database" > "$TEMP_SQL_FILE"; then
# Export database with proper error handling
local dump_output
local dump_status
dump_output=$(mysqldump --no-tablespaces -h"$mysql_host" -u"$mysql_user" -p"$mysql_password" "$mysql_database" 2>&1 > "$TEMP_SQL_FILE")
dump_status=$?

# Check for errors in output (mysqldump may return 0 even with some errors)
if [[ $dump_status -ne 0 ]] || [[ "$dump_output" =~ "Error:" ]]; then
echo ""
echo "ERROR: Failed to export database"
if [[ "$dump_output" =~ "PROCESS privilege" ]]; then
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "The MySQL user '$mysql_user' needs the PROCESS privilege."
echo ""
echo "To fix this, connect to MySQL as a privileged user and run:"
echo " GRANT PROCESS ON *.* TO '$mysql_user'@'%';"
echo " FLUSH PRIVILEGES;"
echo ""
echo "Or retry with a user that has sufficient privileges (e.g., root)."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
elif [[ -n "$dump_output" ]]; then
echo "Error details: $dump_output"
fi
exit 1
fi

# Verify the dump file exists and has content
if [[ ! -f "$TEMP_SQL_FILE" ]] || [[ ! -s "$TEMP_SQL_FILE" ]]; then
echo ""
echo "ERROR: Database dump file is empty or missing"
echo "This might indicate insufficient disk space or permissions issues."
exit 1
fi

Expand Down Expand Up @@ -286,8 +342,12 @@ main() {
# Get database configuration
local mysql_host
local mysql_database
local ghost_mysql_user
local ghost_mysql_password
mysql_host=$(jq -r < "${current_location}/config.production.json" '.database.connection.host')
mysql_database=$(jq -r < "${current_location}/config.production.json" '.database.connection.database')
ghost_mysql_user=$(jq -r < "${current_location}/config.production.json" '.database.connection.user')
ghost_mysql_password=$(jq -r < "${current_location}/config.production.json" '.database.connection.password')

# Check disk space
echo ""
Expand Down Expand Up @@ -321,23 +381,52 @@ main() {
echo "✓ Disk space check passed"
echo ""

# Get MySQL credentials and validate
read -rp "MySQL user for database export (default: root): " mysql_user
mysql_user=${mysql_user:-root}
# Try Ghost's own credentials first
echo "Testing database export with Ghost's credentials..."
if test_mysql_dump "$mysql_host" "$mysql_database" "$ghost_mysql_user" "$ghost_mysql_password"; then
echo "✓ Ghost's credentials have sufficient privileges"
mysql_user="$ghost_mysql_user"
mysql_password="$ghost_mysql_password"
else
echo "Ghost's database user doesn't have sufficient privileges for export."
echo "Please provide credentials for a MySQL user with dump privileges."
echo ""

# Get password securely
echo -n "MySQL password for ${mysql_user}: "
read -rs mysql_password
echo ""
# Get MySQL credentials and validate
read -rp "MySQL user for database export (default: root): " mysql_user
mysql_user=${mysql_user:-root}

# Validate connection
if ! validate_mysql_connection "$mysql_host" "$mysql_database" "$mysql_user" "$mysql_password"; then
echo "Please check your MySQL credentials and try again."
exit 1
# Get password securely
echo -n "MySQL password for ${mysql_user}: "
read -rs mysql_password
echo ""

# Validate connection
if ! validate_mysql_connection "$mysql_host" "$mysql_database" "$mysql_user" "$mysql_password"; then
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Could not connect to MySQL database."
echo ""
echo "Please verify:"
echo " • MySQL service is running"
echo " • Credentials are correct"
echo " • User has access from this host"
echo " • Database name is correct"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
exit 1
fi

# Test dump permissions
if ! test_mysql_dump "$mysql_host" "$mysql_database" "$mysql_user" "$mysql_password"; then
echo ""
echo "ERROR: The provided user doesn't have sufficient privileges for database export."
echo "Please ensure the user has the necessary privileges or try a different user."
exit 1
fi
fi

# Create recovery script
create_recovery_script
# Create recovery script (with empty service name since we haven't stopped anything yet)
create_recovery_script ""

# Final confirmation before stopping Ghost
echo ""
Expand Down Expand Up @@ -365,8 +454,17 @@ main() {
# Stop Ghost service
echo ""
echo "Stopping Ghost service..."
systemctl stop "$ghost_service_name"
systemctl disable "$ghost_service_name"

# Update recovery script with actual service name before stopping
create_recovery_script "$ghost_service_name"

if ! systemctl stop "$ghost_service_name"; then
echo "ERROR: Failed to stop Ghost service"
echo "Please check: systemctl status $ghost_service_name"
exit 1
fi

systemctl disable "$ghost_service_name" 2>/dev/null || true
echo "✓ Ghost service stopped"

# Migrate content
Expand Down