mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-18 00:46:45 +00:00
fix(supersync): harden deploy database migration
This commit is contained in:
parent
0c93e8492c
commit
88961485fd
5 changed files with 80 additions and 16 deletions
|
|
@ -51,6 +51,11 @@ are disabled by default so app restarts cannot race the deploy migrator.
|
|||
`./scripts/deploy.sh` runs `prisma migrate deploy` once before replacing the app
|
||||
container, then brings the stack up and verifies the health endpoint.
|
||||
|
||||
Leave `DATABASE_URL` unset when using the bundled Postgres service. The default
|
||||
connection uses `postgres:5432`; existing installs that already set
|
||||
`DATABASE_URL` with `db:5432` keep working because the Compose service exposes
|
||||
`db` as a network alias.
|
||||
|
||||
> **Upgrade note:** because `RUN_MIGRATIONS_ON_STARTUP` defaults to `false`,
|
||||
> `docker compose pull && docker compose up -d` can leave the app running
|
||||
> against unapplied migrations. Use `./scripts/deploy.sh` for production
|
||||
|
|
|
|||
|
|
@ -79,10 +79,10 @@ services:
|
|||
- postgres-data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
'CMD-SHELL',
|
||||
'pg_isready -U ${POSTGRES_USER:-supersync} -d ${POSTGRES_DB:-supersync}',
|
||||
]
|
||||
- CMD-SHELL
|
||||
- >
|
||||
pg_isready -U "$$POSTGRES_USER" -d "$$POSTGRES_DB" &&
|
||||
psql -U "$$POSTGRES_USER" -d "$$POSTGRES_DB" -c "SELECT 1" > /dev/null 2>&1
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
|
@ -91,7 +91,10 @@ services:
|
|||
- no-new-privileges:true
|
||||
mem_limit: 1g
|
||||
networks:
|
||||
- internal
|
||||
internal:
|
||||
aliases:
|
||||
# Keep existing .env files with DATABASE_URL=...@db:5432/... working.
|
||||
- db
|
||||
logging: *default-logging
|
||||
|
||||
caddy:
|
||||
|
|
|
|||
|
|
@ -28,7 +28,9 @@ POSTGRES_PASSWORD=your-secure-postgres-password
|
|||
# POSTGRES_DB=supersync
|
||||
|
||||
# External PostgreSQL connection string. Leave unset to use the bundled compose
|
||||
# postgres service. If set, also set POSTGRES_SERVICE= below.
|
||||
# postgres service. The bundled service is reachable as postgres:5432; db:5432
|
||||
# is also kept as a compatibility alias for older .env files. If you point this
|
||||
# at an external host, also set POSTGRES_SERVICE= below.
|
||||
# DATABASE_URL=postgresql://supersync:password@example.com:5432/supersync
|
||||
|
||||
# =============================================================================
|
||||
|
|
|
|||
|
|
@ -59,13 +59,31 @@ echo "==> Pulling latest code..."
|
|||
git pull --ff-only || { echo "WARNING: git pull failed — continuing with current files"; }
|
||||
echo ""
|
||||
|
||||
# Load GHCR credentials from .env (for private images)
|
||||
if [ -f ".env" ]; then
|
||||
GHCR_VARS=$(grep -E '^(GHCR_USER|GHCR_TOKEN)=' ".env" 2>/dev/null | xargs || true)
|
||||
if [ -n "$GHCR_VARS" ]; then
|
||||
export $GHCR_VARS
|
||||
# Load deploy-script settings from .env when they were not already exported.
|
||||
load_env_value() {
|
||||
local key="$1"
|
||||
local line
|
||||
|
||||
if [ -n "${!key+x}" ] || [ ! -f ".env" ]; then
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
line=$(grep -E "^${key}=" ".env" 2>/dev/null | tail -n 1 || true)
|
||||
if [ -z "$line" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
local value="${line#*=}"
|
||||
value="${value%\"}"
|
||||
value="${value#\"}"
|
||||
value="${value%\'}"
|
||||
value="${value#\'}"
|
||||
export "$key=$value"
|
||||
}
|
||||
|
||||
for env_key in GHCR_USER GHCR_TOKEN DATABASE_URL POSTGRES_SERVICE POSTGRES_WAIT_TIMEOUT MIGRATION_TIMEOUT DEPLOY_WAIT_TIMEOUT; do
|
||||
load_env_value "$env_key"
|
||||
done
|
||||
|
||||
# Login to GHCR if credentials provided
|
||||
if [ -n "${GHCR_TOKEN:-}" ] && [ -n "${GHCR_USER:-}" ]; then
|
||||
|
|
@ -111,7 +129,11 @@ fi
|
|||
# running app available while online index builds run, and it fails the deploy
|
||||
# before the app is restarted if Prisma cannot apply a migration.
|
||||
POSTGRES_WAIT_TIMEOUT="${POSTGRES_WAIT_TIMEOUT:-60}"
|
||||
POSTGRES_SERVICE="${POSTGRES_SERVICE:-postgres}"
|
||||
POSTGRES_SERVICE="${POSTGRES_SERVICE-postgres}"
|
||||
if [ "$POSTGRES_SERVICE" = "postgres" ] && [[ "${DATABASE_URL:-}" == *@db:5432/* ]]; then
|
||||
export DATABASE_URL="${DATABASE_URL/@db:5432/@postgres:5432}"
|
||||
echo "==> Rewriting legacy bundled DATABASE_URL host db to postgres for this deploy"
|
||||
fi
|
||||
echo ""
|
||||
if [ -n "$POSTGRES_SERVICE" ]; then
|
||||
echo "==> Ensuring $POSTGRES_SERVICE is running (wait timeout: ${POSTGRES_WAIT_TIMEOUT}s)..."
|
||||
|
|
@ -125,10 +147,31 @@ echo ""
|
|||
# for arbitrarily long. Wrap the migrator with a timeout so a stuck deploy fails
|
||||
# loudly instead of hanging this script forever. Exit code 124 = timed out.
|
||||
MIGRATION_TIMEOUT="${MIGRATION_TIMEOUT:-900}"
|
||||
MIGRATOR_RUN="docker compose $COMPOSE_FILES run --rm --no-deps --interactive=false -T supersync"
|
||||
echo "==> Verifying database connectivity from the supersync image..."
|
||||
set +e
|
||||
timeout "$POSTGRES_WAIT_TIMEOUT" \
|
||||
$MIGRATOR_RUN sh -ec 'printf "SELECT 1;" | npx prisma db execute --schema prisma/schema.prisma --stdin > /dev/null'
|
||||
DB_CHECK_STATUS=$?
|
||||
set -e
|
||||
if [ "$DB_CHECK_STATUS" -eq 124 ]; then
|
||||
echo ""
|
||||
echo "ERROR: database connectivity check timed out after ${POSTGRES_WAIT_TIMEOUT}s."
|
||||
echo " Check DATABASE_URL and the compose Postgres service health."
|
||||
exit 1
|
||||
fi
|
||||
if [ "$DB_CHECK_STATUS" -ne 0 ]; then
|
||||
echo ""
|
||||
echo "ERROR: database connectivity check failed (exit $DB_CHECK_STATUS)."
|
||||
echo " Check DATABASE_URL. For the bundled database, leave it unset or use postgres:5432."
|
||||
exit "$DB_CHECK_STATUS"
|
||||
fi
|
||||
echo " Database reachable"
|
||||
echo ""
|
||||
echo "==> Applying database migrations before app restart (timeout: ${MIGRATION_TIMEOUT}s)..."
|
||||
set +e
|
||||
timeout "$MIGRATION_TIMEOUT" \
|
||||
docker compose $COMPOSE_FILES run --rm --no-deps supersync npx prisma migrate deploy
|
||||
$MIGRATOR_RUN sh -ec 'echo " Migrator container started"; npx prisma migrate deploy'
|
||||
MIGRATE_STATUS=$?
|
||||
set -e
|
||||
if [ "$MIGRATE_STATUS" -eq 124 ]; then
|
||||
|
|
|
|||
|
|
@ -61,14 +61,20 @@ describe('performance migrations', () => {
|
|||
const deployScript = readFileSync(join(currentDir, '../scripts/deploy.sh'), 'utf8');
|
||||
const dockerfile = readFileSync(join(currentDir, '../Dockerfile'), 'utf8');
|
||||
const composeFile = readFileSync(join(currentDir, '../docker-compose.yml'), 'utf8');
|
||||
const migrationCommand = 'run --rm --no-deps supersync npx prisma migrate deploy';
|
||||
const migrationCommand = 'npx prisma migrate deploy';
|
||||
const startCommand = 'up -d --wait --wait-timeout "$WAIT_TIMEOUT"';
|
||||
const externalDbStartCommand =
|
||||
'up -d --wait --wait-timeout "$WAIT_TIMEOUT" --no-deps supersync caddy';
|
||||
|
||||
expect(deployScript).toContain('POSTGRES_WAIT_TIMEOUT');
|
||||
expect(deployScript).toContain('POSTGRES_SERVICE="${POSTGRES_SERVICE:-postgres}"');
|
||||
expect(deployScript).toContain('load_env_value()');
|
||||
expect(deployScript).toContain('POSTGRES_SERVICE="${POSTGRES_SERVICE-postgres}"');
|
||||
expect(deployScript).toContain('@db:5432');
|
||||
expect(deployScript).toContain('@postgres:5432');
|
||||
expect(deployScript).toContain('run --rm --no-deps --interactive=false -T supersync');
|
||||
expect(deployScript).toContain('prisma db execute');
|
||||
expect(deployScript).toContain(migrationCommand);
|
||||
expect(deployScript).toContain('Migrator container started');
|
||||
expect(deployScript).toContain(externalDbStartCommand);
|
||||
expect(deployScript).toContain('RUN_MIGRATIONS_ON_STARTUP');
|
||||
expect(deployScript.indexOf(migrationCommand)).toBeLessThan(
|
||||
|
|
@ -78,5 +84,10 @@ describe('performance migrations', () => {
|
|||
expect(composeFile).toContain(
|
||||
'RUN_MIGRATIONS_ON_STARTUP=${RUN_MIGRATIONS_ON_STARTUP:-false}',
|
||||
);
|
||||
expect(composeFile).toContain(
|
||||
'psql -U "$$POSTGRES_USER" -d "$$POSTGRES_DB" -c "SELECT 1"',
|
||||
);
|
||||
expect(composeFile).toContain('aliases:');
|
||||
expect(composeFile).toContain('- db');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue