Skip to content

feat: add auto-bootstrap seed functionality for empty database tables…#18

Merged
xukki241 merged 1 commit intomainfrom
dev
Mar 28, 2026
Merged

feat: add auto-bootstrap seed functionality for empty database tables…#18
xukki241 merged 1 commit intomainfrom
dev

Conversation

@xukki241
Copy link
Copy Markdown
Collaborator

… post-deploy

Copilot AI review requested due to automatic review settings March 28, 2026 14:08
@xukki241 xukki241 merged commit baf924d into main Mar 28, 2026
5 checks passed
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds post-deploy logic to automatically seed the database when “critical” tables are empty, aiming to prevent a blank app state after deploy/reset.

Changes:

  • Query row counts for products, product_variants, and inventory_stock during the CD deploy job.
  • If counts indicate emptiness, run deploy/scripts/reset-db-and-seed.sh and then bring services back up.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +240 to +246
sudo docker compose -f docker-compose.prod.yml exec -T mysql \
mysql -N -s -uroot -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE" -e "SELECT COUNT(*) FROM $table;" 2>/dev/null | tr -d '\r'
}
PRODUCTS_COUNT="$(get_table_count products || echo 0)"
VARIANTS_COUNT="$(get_table_count product_variants || echo 0)"
STOCK_COUNT="$(get_table_count inventory_stock || echo 0)"
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_table_count suppresses errors and the pipeline’s exit status will be from tr, so failures like MySQL not ready / auth errors / table missing will produce an empty string that later gets treated as 0. That can incorrectly trigger the reset+seed path. Suggest: wait for the mysql service to be healthy before counting, and make get_table_count fail loudly (e.g., add set -o pipefail, capture/validate output is a number, and retry/abort on error rather than treating it as zero).

Suggested change
sudo docker compose -f docker-compose.prod.yml exec -T mysql \
mysql -N -s -uroot -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE" -e "SELECT COUNT(*) FROM $table;" 2>/dev/null | tr -d '\r'
}
PRODUCTS_COUNT="$(get_table_count products || echo 0)"
VARIANTS_COUNT="$(get_table_count product_variants || echo 0)"
STOCK_COUNT="$(get_table_count inventory_stock || echo 0)"
# Wait for MySQL to be ready and fail loudly on errors.
local attempt=0
local max_attempts=5
local sleep_seconds=3
local count
# Ensure pipeline failures propagate (mysql errors aren't hidden by `tr`).
set -o pipefail
while [ "$attempt" -lt "$max_attempts" ]; do
if count="$(
sudo docker compose -f docker-compose.prod.yml exec -T mysql \
mysql -N -s -uroot -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE" \
-e "SELECT COUNT(*) FROM $table;" | tr -d '\r'
)"; then
if printf '%s\n' "$count" | grep -Eq '^[0-9]+$'; then
echo "$count"
return 0
else
echo "Non-numeric row count '$count' returned for table '$table'" >&2
return 1
fi
fi
attempt=$((attempt + 1))
echo "Unable to query table '$table' (attempt $attempt/$max_attempts). Waiting for MySQL to become ready..." >&2
sleep "$sleep_seconds"
done
echo "Failed to query table '$table' after $max_attempts attempts; aborting." >&2
return 1
}
if ! PRODUCTS_COUNT="$(get_table_count products)"; then
echo "Error retrieving row count for 'products' table; aborting deployment." >&2
exit 1
fi
if ! VARIANTS_COUNT="$(get_table_count product_variants)"; then
echo "Error retrieving row count for 'product_variants' table; aborting deployment." >&2
exit 1
fi
if ! STOCK_COUNT="$(get_table_count inventory_stock)"; then
echo "Error retrieving row count for 'inventory_stock' table; aborting deployment." >&2
exit 1
fi

Copilot uses AI. Check for mistakes.
Comment on lines +250 to +256
if [ "${PRODUCTS_COUNT:-0}" = "0" ] || [ "${VARIANTS_COUNT:-0}" = "0" ]; then
echo "Detected empty critical tables. Running reset-db-and-seed.sh..."
if [ -f "./deploy/scripts/reset-db-and-seed.sh" ]; then
sudo chmod +x ./deploy/scripts/reset-db-and-seed.sh
sudo DEPLOY_PATH="$DEPLOY_PATH" COMPOSE_FILE="$DEPLOY_PATH/docker-compose.prod.yml" ENV_FILE="$ENV_FILE" bash ./deploy/scripts/reset-db-and-seed.sh
# Ensure services are up after seed
sudo env REGISTRY="$REGISTRY" IMAGE_NAMESPACE="$IMAGE_NAMESPACE" IMAGE_TAG="$TARGET_TAG" DB_USERNAME="$DB_USERNAME" DB_PASSWORD="$DB_PASSWORD" MYSQL_DATABASE="$MYSQL_DATABASE" MYSQL_ROOT_PASSWORD="$MYSQL_ROOT_PASSWORD" docker compose -f docker-compose.prod.yml up -d --remove-orphans
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition products == 0 || variants == 0 will run reset-db-and-seed.sh, which drops and recreates the database. This is risky because a legitimately empty table (or a transient/partial seed) would wipe an otherwise valid production DB. Consider tightening the guard to only bootstrap when the database is clearly fresh (e.g., all critical tables are 0 and/or schema just created), and/or gate behind an explicit flag/secret (e.g., AUTO_BOOTSTRAP_SEED=true) so normal deploys can’t trigger a destructive reset.

Copilot uses AI. Check for mistakes.
Comment on lines +240 to +241
sudo docker compose -f docker-compose.prod.yml exec -T mysql \
mysql -N -s -uroot -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE" -e "SELECT COUNT(*) FROM $table;" 2>/dev/null | tr -d '\r'
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MySQL root password is passed as a CLI argument (-p"$MYSQL_ROOT_PASSWORD"), which can be visible to other processes on the VM via process listings. Prefer using MYSQL_PWD (passed via docker compose exec -e MYSQL_PWD=...) or a defaults file inside the container so the password isn’t part of the command line.

Suggested change
sudo docker compose -f docker-compose.prod.yml exec -T mysql \
mysql -N -s -uroot -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE" -e "SELECT COUNT(*) FROM $table;" 2>/dev/null | tr -d '\r'
sudo docker compose -f docker-compose.prod.yml exec -T -e MYSQL_PWD="$MYSQL_ROOT_PASSWORD" mysql \
mysql -N -s -uroot "$MYSQL_DATABASE" -e "SELECT COUNT(*) FROM $table;" 2>/dev/null | tr -d '\r'

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants