From 7465a5ffa6b015012d2425213f881f49ea387feb Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 24 Nov 2025 23:14:06 +0000 Subject: [PATCH] Fix: Auto-cleanup existing database containers before running prep When running `pushkin-dev prep` with existing database containers from a previous run, the command would fail with a misleading "password authentication failed" error. This happened because the existing containers were using different credentials than the current pushkin.yaml config. This commit implements Option 1 from the issue: automatic detection and cleanup of stale database containers. Changes: - Added `ensureCleanState()` function that checks for existing database containers before setup - If containers exist, displays a warning and removes them cleanly - Uses `docker ps -a` to detect both running and stopped containers - Follows the same pattern as `killLocal()` using compose.stop() and compose.rm() - Includes proper error handling to avoid failing the entire setup Benefits: - Makes `prep` command idempotent - safe to run multiple times - Eliminates confusing password authentication errors - Improves developer experience by avoiding manual cleanup steps - No waiting through 10 failed connection attempts (~2+ minutes) Fixes pushkin-dev prep fails cryptically when database containers already exist --- .../pushkin-cli/src/commands/setupdb/index.js | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/packages/pushkin-cli/src/commands/setupdb/index.js b/packages/pushkin-cli/src/commands/setupdb/index.js index acbd99fe..8433b8b6 100644 --- a/packages/pushkin-cli/src/commands/setupdb/index.js +++ b/packages/pushkin-cli/src/commands/setupdb/index.js @@ -336,10 +336,73 @@ export async function migrateTransactionsDB(coreDBs, verbose) { }); } +/** + * Ensures a clean state by detecting and removing existing database containers + * This prevents issues with stale containers that may have different credentials + * @param {boolean} verbose Output extra debugging info + * @returns {Promise} + */ +async function ensureCleanState(verbose) { + if (verbose) console.log("--verbose flag set inside ensureCleanState()"); + + try { + // Check if database containers are running or stopped + const { stdout } = await exec( + `docker ps -a --format "{{.Names}}" | grep -E "pushkin[-_](test_db|test_transaction_db)[-_]"` + ); + + if (stdout.trim()) { + // Found existing containers - clean them up + console.log('⚠️ Found existing database containers. Cleaning up...'); + + const dockerPath = path.join(process.cwd(), "pushkin"); + const dockerConfig = "docker-compose.dev.yml"; + + try { + // Stop containers if running + await compose.stop({ + cwd: dockerPath, + config: dockerConfig, + }); + } catch (err) { + // Containers might already be stopped, which is fine + if (verbose) console.log("Containers already stopped or error stopping:", err.message); + } + + try { + // Remove containers + await compose.rm({ + cwd: dockerPath, + config: dockerConfig, + commandOptions: ["-f", "-v"], // -f: force, -v: remove volumes + }); + } catch (err) { + if (verbose) console.warn("Warning removing containers:", err.message); + } + + console.log('✓ Cleanup complete. Starting fresh databases...'); + } else { + if (verbose) console.log("No existing database containers found. Proceeding with fresh setup."); + } + } catch (e) { + // If grep finds nothing, it returns exit code 1, which throws an error + // This is expected when no containers exist, so we can safely ignore it + if (e.code === 1 && e.stderr === '') { + if (verbose) console.log("No existing database containers found (grep returned no matches)."); + } else { + // Actual error - log it but don't fail the entire setup + if (verbose) console.warn("Warning: Could not check for existing containers:", e.message); + } + } +} + export async function setupdb(coreDBs, mainExpDir, verbose) { if (verbose) console.log("--verbose flag set inside setupdb()"); // load up all migrations for same dbs to be run at same time (knex requires this) + // Ensure clean state before starting databases + await ensureCleanState(verbose); + let dbPromise; if (verbose) console.log("Spooling up databases."); try {