From 9df0072dd732307650e2414ec15f70ec587cdfd0 Mon Sep 17 00:00:00 2001 From: vitezprchal Date: Tue, 17 Feb 2026 01:09:54 +0100 Subject: [PATCH] feat: add stop-database.sh script for stopping database containers and processes --- cli/src/helpers/logNextSteps.ts | 1 + cli/src/installers/dbContainer.ts | 9 + .../extras/start-database/stop-database.sh | 169 ++++++++++++++++++ 3 files changed, 179 insertions(+) create mode 100755 cli/template/extras/start-database/stop-database.sh diff --git a/cli/src/helpers/logNextSteps.ts b/cli/src/helpers/logNextSteps.ts index 9ba296b55d..cc47b64982 100644 --- a/cli/src/helpers/logNextSteps.ts +++ b/cli/src/helpers/logNextSteps.ts @@ -37,6 +37,7 @@ export const logNextSteps = async ({ if (["postgres", "mysql"].includes(databaseProvider)) { logger.info(" Start up a database, if needed using './start-database.sh'"); + logger.info(" Stop the database, if needed using './stop-database.sh'"); } if (packages?.prisma.inUse || packages?.drizzle.inUse) { diff --git a/cli/src/installers/dbContainer.ts b/cli/src/installers/dbContainer.ts index 0a13f198df..8e0951d8b3 100644 --- a/cli/src/installers/dbContainer.ts +++ b/cli/src/installers/dbContainer.ts @@ -35,4 +35,13 @@ export const dbContainerInstaller: Installer = ({ scriptText.replaceAll("project1", sanitizedProjectName) ); fs.chmodSync(scriptDest, "755"); + + // Copy stop-database.sh script (database-agnostic) + const stopScriptSrc = path.join( + PKG_ROOT, + "template/extras/start-database/stop-database.sh" + ); + const stopScriptDest = path.join(projectDir, "stop-database.sh"); + fs.copyFileSync(stopScriptSrc, stopScriptDest); + fs.chmodSync(stopScriptDest, "755"); }; diff --git a/cli/template/extras/start-database/stop-database.sh b/cli/template/extras/start-database/stop-database.sh new file mode 100755 index 0000000000..484a340044 --- /dev/null +++ b/cli/template/extras/start-database/stop-database.sh @@ -0,0 +1,169 @@ +#!/usr/bin/env bash +# Use this script to stop docker containers and processes running on the database port + +# TO RUN ON WINDOWS: +# 1. Install WSL (Windows Subsystem for Linux) - https://learn.microsoft.com/en-us/windows/wsl/install +# 2. Install Docker Desktop or Podman Deskop +# - Docker Desktop for Windows - https://docs.docker.com/docker-for-windows/install/ +# - Podman Desktop - https://podman.io/getting-started/installation +# 3. Open WSL - `wsl` +# 4. Run this script - `./stop-database.sh` + +# On Linux and macOS you can run this script directly - `./stop-database.sh` + +# import env variables from .env +set -a +source .env + +DB_PORT=$(echo "$DATABASE_URL" | awk -F':' '{print $4}' | awk -F'\/' '{print $1}') + +if [ -z "$DB_PORT" ]; then + echo "Error: Could not extract database port from DATABASE_URL" + exit 1 +fi + +# Check if port is actually in use (using same method as start-database.sh) +if command -v nc >/dev/null 2>&1; then + if ! nc -z localhost "$DB_PORT" 2>/dev/null; then + echo "Port $DB_PORT is not in use." + exit 0 + fi +else + echo "Warning: Unable to check if port $DB_PORT is in use (netcat not installed)" + read -p "Do you want to continue anyway? [y/N]: " -r REPLY + if ! [[ $REPLY =~ ^[Yy]$ ]]; then + echo "Aborting." + exit 0 + fi +fi + +echo "Port $DB_PORT is in use. Checking for Docker containers and processes..." + +# Check for Docker/Podman containers first +if [ -x "$(command -v docker)" ] || [ -x "$(command -v podman)" ]; then + if [ -x "$(command -v docker)" ]; then + DOCKER_CMD="docker" + else + DOCKER_CMD="podman" + fi + + # Check if Docker daemon is running + if ! $DOCKER_CMD info > /dev/null 2>&1; then + echo "Warning: $DOCKER_CMD daemon is not running. Skipping container checks." + else + # Get all running containers and check their port mappings + # Use docker ps to get containers with their port info directly + CONTAINERS_FOUND=false + + while IFS='|' read -r CONTAINER_ID CONTAINER_NAME PORT_INFO; do + # Check if port info contains the target port on the host side + # Format examples: "0.0.0.0:5432->5432/tcp" or "0.0.0.0:5432->5432/tcp, [::]:5432->5432/tcp" + if echo "$PORT_INFO" | grep -qE ":$DB_PORT->"; then + if [ "$CONTAINERS_FOUND" = false ]; then + echo "Found Docker containers using port $DB_PORT:" + CONTAINERS_FOUND=true + fi + echo " - $CONTAINER_NAME ($CONTAINER_ID)" + fi + done < <($DOCKER_CMD ps --format "{{.ID}}|{{.Names}}|{{.Ports}}") + + if [ "$CONTAINERS_FOUND" = true ]; then + read -p "Stop these containers? [y/N]: " -r REPLY + if [[ $REPLY =~ ^[Yy]$ ]]; then + while IFS='|' read -r CONTAINER_ID CONTAINER_NAME PORT_INFO; do + if echo "$PORT_INFO" | grep -qE ":$DB_PORT->"; then + if $DOCKER_CMD stop "$CONTAINER_ID" 2>/dev/null; then + echo "Stopped container $CONTAINER_NAME ($CONTAINER_ID)" + fi + fi + done < <($DOCKER_CMD ps --format "{{.ID}}|{{.Names}}|{{.Ports}}") + + # Verify port is now free + if command -v nc >/dev/null 2>&1; then + sleep 1 + if ! nc -z localhost "$DB_PORT" 2>/dev/null; then + echo "" + echo "Port $DB_PORT is now free." + exit 0 + fi + fi + fi + fi + fi +fi + +# Now check for other processes using the port +FOUND_PROCESSES=false + +# Using lsof if available (Linux/macOS) +if command -v lsof >/dev/null 2>&1; then + PIDS=$(lsof -ti ":$DB_PORT" 2>/dev/null) + + if [ -n "$PIDS" ]; then + FOUND_PROCESSES=true + echo "" + echo "Found processes on port $DB_PORT:" + lsof -i ":$DB_PORT" 2>/dev/null + + echo "" + read -p "Kill these processes? [y/N]: " -r REPLY + if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "$PIDS" | xargs kill -9 2>/dev/null + echo "Killed all processes on port $DB_PORT" + fi + fi + +# Fallback to ss if available +elif command -v ss >/dev/null 2>&1; then + PIDS=$(ss -lptn "sport = :$DB_PORT" 2>/dev/null | grep -oP 'pid=\K\d+' | sort -u) + + if [ -n "$PIDS" ]; then + FOUND_PROCESSES=true + echo "" + echo "Found processes on port $DB_PORT:" + for PID in $PIDS; do + ps -p "$PID" -o pid,cmd 2>/dev/null + done + + echo "" + read -p "Kill these processes? [y/N]: " -r REPLY + if [[ $REPLY =~ ^[Yy]$ ]]; then + for PID in $PIDS; do + kill -9 "$PID" 2>/dev/null + done + echo "Killed all processes on port $DB_PORT" + fi + fi + +# Fallback to fuser if available +elif command -v fuser >/dev/null 2>&1; then + PIDS=$(fuser "$DB_PORT/tcp" 2>/dev/null | awk '{print $1}') + + if [ -n "$PIDS" ]; then + FOUND_PROCESSES=true + echo "" + echo "Found processes on port $DB_PORT:" + for PID in $PIDS; do + ps -p "$PID" -o pid,cmd 2>/dev/null + done + + echo "" + read -p "Kill these processes? [y/N]: " -r REPLY + if [[ $REPLY =~ ^[Yy]$ ]]; then + fuser -k "$DB_PORT/tcp" 2>/dev/null + echo "Killed all processes on port $DB_PORT" + fi + fi +fi + +# Final verification +if command -v nc >/dev/null 2>&1; then + if ! nc -z localhost "$DB_PORT" 2>/dev/null; then + echo "" + echo "Port $DB_PORT is now free." + else + echo "" + echo "Warning: Port $DB_PORT is still in use. You may need to manually investigate." + fi +fi +