Skip to content

Common Scripts

Ready-to-use script templates for development.

Development Setup

Project Setup

#!/bin/bash
# scripts/setup.sh - Initial project setup

set -euo pipefail

echo "Setting up development environment..."

# Check prerequisites
command -v docker >/dev/null 2>&1 || { echo "Docker required but not installed."; exit 1; }
command -v uv >/dev/null 2>&1 || { echo "uv required but not installed."; exit 1; }

# Create environment file
if [[ ! -f .env ]]; then
    echo "Creating .env from template..."
    cp .env.example .env
    echo "Please update .env with your settings"
fi

# Install Python dependencies
echo "Installing Python dependencies..."
uv sync

# Start services
echo "Starting Docker services..."
docker compose up -d db redis

# Wait for database
echo "Waiting for database..."
sleep 5

# Run migrations
echo "Running migrations..."
uv run alembic upgrade head

# Seed development data
if [[ "${1:-}" == "--seed" ]]; then
    echo "Seeding development data..."
    uv run python scripts/seed.py
fi

echo "Setup complete!"
echo "Run 'uv run uvicorn app.main:app --reload' to start the server"

Development Start

#!/bin/bash
# scripts/dev.sh - Start development environment

set -euo pipefail

# Start Docker services
docker compose up -d db redis

# Wait for services
echo "Waiting for services..."
until docker compose exec -T db pg_isready -U postgres > /dev/null 2>&1; do
    sleep 1
done

# Run any pending migrations
uv run alembic upgrade head

# Start the application
echo "Starting application..."
uv run uvicorn app.main:app --reload --host 0.0.0.0 --port 8000

Database Scripts

Database Reset

#!/bin/bash
# scripts/reset-db.sh - Reset database to clean state

set -euo pipefail

DB_NAME="${DB_NAME:-sartiq}"
DB_USER="${DB_USER:-postgres}"
DB_HOST="${DB_HOST:-localhost}"

echo "This will DELETE ALL DATA in $DB_NAME"
read -p "Continue? [y/N] " confirm
if [[ "$confirm" != "y" ]]; then
    echo "Aborted."
    exit 0
fi

echo "Terminating connections..."
psql -h "$DB_HOST" -U "$DB_USER" -d postgres -c "
    SELECT pg_terminate_backend(pid)
    FROM pg_stat_activity
    WHERE datname = '$DB_NAME' AND pid != pg_backend_pid();
" 2>/dev/null || true

echo "Dropping database..."
dropdb -h "$DB_HOST" -U "$DB_USER" --if-exists "$DB_NAME"

echo "Creating database..."
createdb -h "$DB_HOST" -U "$DB_USER" "$DB_NAME"

echo "Running migrations..."
uv run alembic upgrade head

echo "Database reset complete!"

Database Backup

#!/bin/bash
# scripts/backup-db.sh - Backup database

set -euo pipefail

DB_NAME="${DB_NAME:-sartiq}"
DB_USER="${DB_USER:-postgres}"
DB_HOST="${DB_HOST:-localhost}"
BACKUP_DIR="${BACKUP_DIR:-./backups}"
DATE=$(date +%Y%m%d_%H%M%S)

mkdir -p "$BACKUP_DIR"

BACKUP_FILE="$BACKUP_DIR/${DB_NAME}_${DATE}.dump"

echo "Backing up $DB_NAME to $BACKUP_FILE..."
pg_dump -h "$DB_HOST" -U "$DB_USER" -Fc "$DB_NAME" > "$BACKUP_FILE"

# Compress
gzip "$BACKUP_FILE"

# Clean old backups (keep last 7)
find "$BACKUP_DIR" -name "*.dump.gz" -mtime +7 -delete

echo "Backup complete: ${BACKUP_FILE}.gz"
ls -lh "${BACKUP_FILE}.gz"

Seed Data

#!/bin/bash
# scripts/seed.sh - Seed development data

set -euo pipefail

echo "Seeding development data..."

# Option 1: SQL file
psql -h localhost -U postgres -d sartiq -f seeds/dev_data.sql

# Option 2: Python script
# uv run python seeds/run.py

echo "Seeding complete!"

Testing Scripts

Run Tests

#!/bin/bash
# scripts/test.sh - Run tests with options

set -euo pipefail

# Default values
COVERAGE=false
VERBOSE=false
WATCH=false
FILTER=""

usage() {
    echo "Usage: $0 [-c|--coverage] [-v|--verbose] [-w|--watch] [filter]"
    exit 1
}

while [[ $# -gt 0 ]]; do
    case $1 in
        -c|--coverage) COVERAGE=true; shift ;;
        -v|--verbose) VERBOSE=true; shift ;;
        -w|--watch) WATCH=true; shift ;;
        -h|--help) usage ;;
        *) FILTER="$1"; shift ;;
    esac
done

# Build pytest arguments
ARGS=()

if $COVERAGE; then
    ARGS+=("--cov=app" "--cov-report=html" "--cov-report=term-missing")
fi

if $VERBOSE; then
    ARGS+=("-v")
fi

if [[ -n "$FILTER" ]]; then
    ARGS+=("-k" "$FILTER")
fi

if $WATCH; then
    # Use pytest-watch
    uv run ptw -- "${ARGS[@]}"
else
    uv run pytest "${ARGS[@]}"
fi

CI Test Script

#!/bin/bash
# scripts/ci-test.sh - CI testing with proper setup

set -euo pipefail

echo "=== Setting up test environment ==="

# Start test database
docker compose -f docker-compose.test.yml up -d

# Wait for database
echo "Waiting for database..."
until docker compose -f docker-compose.test.yml exec -T db pg_isready -U postgres > /dev/null 2>&1; do
    sleep 1
done

# Run migrations on test database
DATABASE_URL="postgresql://postgres:postgres@localhost:5433/test" \
    uv run alembic upgrade head

echo "=== Running tests ==="

# Run tests
DATABASE_URL="postgresql://postgres:postgres@localhost:5433/test" \
    uv run pytest --cov=app --cov-report=xml -v

EXIT_CODE=$?

echo "=== Cleanup ==="
docker compose -f docker-compose.test.yml down -v

exit $EXIT_CODE

Deployment Scripts

Build and Push

#!/bin/bash
# scripts/build.sh - Build and push Docker image

set -euo pipefail

REGISTRY="${REGISTRY:-ghcr.io/yourorg}"
IMAGE_NAME="${IMAGE_NAME:-sartiq}"
VERSION="${VERSION:-$(git rev-parse --short HEAD)}"

# Build image
echo "Building $REGISTRY/$IMAGE_NAME:$VERSION..."
docker build -t "$REGISTRY/$IMAGE_NAME:$VERSION" .
docker tag "$REGISTRY/$IMAGE_NAME:$VERSION" "$REGISTRY/$IMAGE_NAME:latest"

# Push image
if [[ "${PUSH:-false}" == "true" ]]; then
    echo "Pushing image..."
    docker push "$REGISTRY/$IMAGE_NAME:$VERSION"
    docker push "$REGISTRY/$IMAGE_NAME:latest"
fi

echo "Build complete: $REGISTRY/$IMAGE_NAME:$VERSION"

Deploy

#!/bin/bash
# scripts/deploy.sh - Deploy to environment

set -euo pipefail

ENVIRONMENT="${1:-}"

if [[ -z "$ENVIRONMENT" ]]; then
    echo "Usage: $0 <environment>"
    echo "  environment: staging, production"
    exit 1
fi

case "$ENVIRONMENT" in
    staging)
        DEPLOY_HOST="staging.example.com"
        ;;
    production)
        read -p "Deploy to PRODUCTION? [y/N] " confirm
        if [[ "$confirm" != "y" ]]; then
            echo "Aborted."
            exit 0
        fi
        DEPLOY_HOST="prod.example.com"
        ;;
    *)
        echo "Unknown environment: $ENVIRONMENT"
        exit 1
        ;;
esac

VERSION=$(git rev-parse --short HEAD)

echo "Deploying $VERSION to $ENVIRONMENT..."

# SSH deploy (example)
ssh "deploy@$DEPLOY_HOST" << EOF
    cd /app
    docker compose pull
    docker compose up -d
    docker compose exec -T api alembic upgrade head
EOF

echo "Deployed $VERSION to $ENVIRONMENT"

Utility Scripts

Cleanup

#!/bin/bash
# scripts/cleanup.sh - Clean temporary files and caches

set -euo pipefail

echo "Cleaning up..."

# Python cache
find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
find . -type d -name ".pytest_cache" -exec rm -rf {} + 2>/dev/null || true
find . -type d -name ".ruff_cache" -exec rm -rf {} + 2>/dev/null || true
find . -type d -name "*.egg-info" -exec rm -rf {} + 2>/dev/null || true
find . -type f -name "*.pyc" -delete 2>/dev/null || true

# Coverage
rm -rf htmlcov .coverage coverage.xml 2>/dev/null || true

# Node
rm -rf node_modules/.cache 2>/dev/null || true

# Build artifacts
rm -rf dist build .next out 2>/dev/null || true

# Docker
if [[ "${DOCKER:-false}" == "true" ]]; then
    echo "Pruning Docker..."
    docker system prune -f
fi

echo "Cleanup complete!"

Health Check

#!/bin/bash
# scripts/health.sh - Check service health

set -euo pipefail

API_URL="${API_URL:-http://localhost:8000}"
DB_HOST="${DB_HOST:-localhost}"

echo "Checking services..."

# API health
if curl -sf "$API_URL/health" > /dev/null; then
    echo "✓ API is healthy"
else
    echo "✗ API is down"
fi

# Database
if pg_isready -h "$DB_HOST" -U postgres > /dev/null 2>&1; then
    echo "✓ Database is healthy"
else
    echo "✗ Database is down"
fi

# Redis
if redis-cli -h localhost ping > /dev/null 2>&1; then
    echo "✓ Redis is healthy"
else
    echo "✗ Redis is down"
fi

# Docker services
echo ""
echo "Docker containers:"
docker compose ps

Generate API Client

#!/bin/bash
# scripts/generate-client.sh - Generate TypeScript API client

set -euo pipefail

API_URL="${API_URL:-http://localhost:8000}"
OUTPUT_DIR="${OUTPUT_DIR:-./frontend/src/api}"

echo "Fetching OpenAPI spec..."
curl -s "$API_URL/openapi.json" > /tmp/openapi.json

echo "Generating TypeScript client..."
npx openapi-typescript-codegen \
    --input /tmp/openapi.json \
    --output "$OUTPUT_DIR" \
    --client fetch

echo "Client generated at $OUTPUT_DIR"

Make Integration

If you prefer Makefiles:

# Makefile

.PHONY: dev test lint format migrate seed

dev:
    ./scripts/dev.sh

test:
    ./scripts/test.sh

test-cov:
    ./scripts/test.sh --coverage

lint:
    uv run ruff check .

format:
    uv run ruff format .

migrate:
    uv run alembic upgrade head

seed:
    ./scripts/seed.sh

reset-db:
    ./scripts/reset-db.sh

clean:
    ./scripts/cleanup.sh

Usage:

make dev
make test-cov
make reset-db