#!/bin/bash # deploy.sh - Deploy services to production # Usage: deploy.sh [--dry-run] # service: md-discord | md-database | pd-discord | pd-database # bump-type: major | minor | patch # --dry-run: Show what would happen without making changes set -e # Color codes for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' # No Color # Detect SSH session and disable BuildKit to avoid credential helper GUI prompts if [[ -n "$SSH_CLIENT" ]] || [[ -n "$SSH_TTY" ]] || [[ -n "$SSH_CONNECTION" ]]; then export DOCKER_BUILDKIT=0 echo -e "${YELLOW}SSH session detected - BuildKit disabled to avoid credential prompts${NC}" fi # ============================================================================= # SERVICE CONFIGURATIONS # ============================================================================= # Local source paths declare -A SERVICE_PATHS=( ["md-discord"]="/mnt/NV2/Development/major-domo/discord-app-v2" ["md-database"]="/mnt/NV2/Development/major-domo/database" ["pd-discord"]="/mnt/NV2/Development/paper-dynasty/discord-app" ["pd-database"]="/mnt/NV2/Development/paper-dynasty/database" ) # Docker Hub image names declare -A DOCKER_IMAGES=( ["md-discord"]="manticorum67/major-domo-discordapp" ["md-database"]="manticorum67/major-domo-database" ["pd-discord"]="manticorum67/paper-dynasty-discordapp" ["pd-database"]="manticorum67/paper-dynasty-database" ) # Production SSH hosts declare -A PROD_HOSTS=( ["md-discord"]="akamai" ["md-database"]="akamai" ["pd-discord"]="sba-bots" ["pd-database"]="akamai" ) # Production paths (on remote host) declare -A PROD_PATHS=( ["md-discord"]="/root/container-data/major-domo" ["md-database"]="/root/container-data/sba-database" ["pd-discord"]="/home/cal/container-data/paper-dynasty" ["pd-database"]="/root/container-data/paper-dynasty" ) # Docker Compose service names declare -A COMPOSE_SERVICES=( ["md-discord"]="discord-app" ["md-database"]="api" ["pd-discord"]="discord-app" ["pd-database"]="api" ) # Container names (for verification) declare -A CONTAINER_NAMES=( ["md-discord"]="major-domo-discord-app-1" ["md-database"]="sba_db_api" ["pd-discord"]="paper-dynasty_discord-app_1" ["pd-database"]="pd_api" ) # Friendly names for display declare -A FRIENDLY_NAMES=( ["md-discord"]="Major Domo Discord Bot" ["md-database"]="Major Domo Database API" ["pd-discord"]="Paper Dynasty Discord Bot" ["pd-database"]="Paper Dynasty Database API" ) # ============================================================================= # FUNCTIONS # ============================================================================= # Parse version and increment increment_version() { local version=$1 local bump_type=$2 IFS='.' read -r major minor patch <<< "$version" # Handle versions without patch (e.g., "1.5" -> "1.5.0") patch=${patch:-0} case $bump_type in major) echo "$((major + 1)).0.0" ;; minor) echo "$major.$((minor + 1)).0" ;; patch) echo "$major.$minor.$((patch + 1))" ;; *) echo "Invalid bump type: $bump_type" >&2 exit 1 ;; esac } # Display usage usage() { echo "Usage: $0 [--dry-run]" echo "" echo "Services:" echo " md-discord - Major Domo Discord Bot" echo " md-database - Major Domo Database API" echo " pd-discord - Paper Dynasty Discord Bot" echo " pd-database - Paper Dynasty Database API" echo "" echo "Bump types:" echo " major - Increment major version (x.0.0)" echo " minor - Increment minor version (0.x.0)" echo " patch - Increment patch version (0.0.x)" echo "" echo "Options:" echo " --dry-run Show what would happen without making changes" echo "" echo "Examples:" echo " $0 md-discord patch" echo " $0 pd-database minor" echo " $0 md-discord patch --dry-run" exit 1 } # ============================================================================= # ARGUMENT PARSING # ============================================================================= DRY_RUN=false SERVICE="" BUMP_TYPE="" for arg in "$@"; do case $arg in --dry-run) DRY_RUN=true ;; md-discord|md-database|pd-discord|pd-database) SERVICE=$arg ;; major|minor|patch) BUMP_TYPE=$arg ;; *) echo -e "${RED}Unknown argument: $arg${NC}" usage ;; esac done # Validate required arguments if [[ -z "$SERVICE" ]] || [[ -z "$BUMP_TYPE" ]]; then usage fi # Validate service exists in config if [[ ! ${SERVICE_PATHS[$SERVICE]+_} ]]; then echo -e "${RED}Error: Invalid service '$SERVICE'${NC}" usage fi # ============================================================================= # GET CONFIGURATION # ============================================================================= LOCAL_PATH="${SERVICE_PATHS[$SERVICE]}" DOCKER_IMAGE="${DOCKER_IMAGES[$SERVICE]}" PROD_HOST="${PROD_HOSTS[$SERVICE]}" PROD_PATH="${PROD_PATHS[$SERVICE]}" COMPOSE_SERVICE="${COMPOSE_SERVICES[$SERVICE]}" CONTAINER_NAME="${CONTAINER_NAMES[$SERVICE]}" FRIENDLY_NAME="${FRIENDLY_NAMES[$SERVICE]}" # ============================================================================= # DEPLOYMENT # ============================================================================= echo -e "${BLUE}========================================${NC}" if [[ "$DRY_RUN" == true ]]; then echo -e "${CYAN} Deploy: $FRIENDLY_NAME (DRY RUN)${NC}" else echo -e "${BLUE} Deploy: $FRIENDLY_NAME${NC}" fi echo -e "${BLUE}========================================${NC}" echo "" # Change to service directory cd "$LOCAL_PATH" # Check git status echo -e "${YELLOW}Checking git status...${NC}" if [[ -n $(git status --porcelain) ]]; then echo -e "${RED}Warning: Working directory has uncommitted changes${NC}" git status --short if [[ "$DRY_RUN" == true ]]; then echo -e "${CYAN}(Dry run - would prompt for confirmation)${NC}" else read -p "Continue anyway? (y/N) " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then echo "Aborted." exit 1 fi fi fi # Read current version CURRENT_VERSION=$(cat VERSION | tr -d '\n') echo -e "${GREEN}Current version: $CURRENT_VERSION${NC}" # Calculate new version NEW_VERSION=$(increment_version "$CURRENT_VERSION" "$BUMP_TYPE") echo -e "${GREEN}New version: $NEW_VERSION${NC}" echo "" # Deployment Plan Summary echo -e "${YELLOW}Deployment Plan:${NC}" echo " Service: $FRIENDLY_NAME" echo " Version: $CURRENT_VERSION -> $NEW_VERSION" echo " Docker: $DOCKER_IMAGE:$NEW_VERSION" echo " Host: $PROD_HOST" echo " Path: $PROD_PATH" echo "" # DRY RUN: Just show what would happen if [[ "$DRY_RUN" == true ]]; then echo -e "${CYAN}=== DRY RUN - No changes will be made ===${NC}" echo "" echo -e "${CYAN}Step 1/6: Would update VERSION file${NC}" echo " echo \"$NEW_VERSION\" > VERSION" echo "" echo -e "${CYAN}Step 2/6: Would build Docker image${NC}" echo " docker build -t $DOCKER_IMAGE:$NEW_VERSION -t $DOCKER_IMAGE:latest ." echo "" echo -e "${CYAN}Step 3/6: Would push to Docker Hub${NC}" echo " docker push $DOCKER_IMAGE:$NEW_VERSION" echo " docker push $DOCKER_IMAGE:latest" echo "" echo -e "${CYAN}Step 4/6: Would deploy to production${NC}" echo " ssh $PROD_HOST \"cd $PROD_PATH && docker compose pull $COMPOSE_SERVICE && docker compose up -d $COMPOSE_SERVICE\"" echo "" echo -e "${CYAN}Step 5/6: Would commit version bump${NC}" echo " git add VERSION" echo " git commit -m \"Bump version to $NEW_VERSION\"" echo " git tag v$NEW_VERSION" echo "" echo -e "${CYAN}Step 6/6: Would push to git${NC}" echo " git push" echo " git push --tags" echo "" echo -e "${GREEN}========================================${NC}" echo -e "${GREEN} DRY RUN Complete${NC}" echo -e "${GREEN} Run without --dry-run to execute${NC}" echo -e "${GREEN}========================================${NC}" exit 0 fi # REAL DEPLOYMENT: Confirm and execute read -p "Proceed with deployment? (y/N) " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then echo "Aborted." exit 1 fi echo "" echo -e "${BLUE}Step 1/6: Updating VERSION file${NC}" echo "$NEW_VERSION" > VERSION echo -e "${GREEN}VERSION updated to $NEW_VERSION${NC}" echo "" echo -e "${BLUE}Step 2/6: Building Docker image${NC}" docker build -t "$DOCKER_IMAGE:$NEW_VERSION" -t "$DOCKER_IMAGE:latest" . echo "" echo -e "${BLUE}Step 3/6: Pushing to Docker Hub${NC}" docker push "$DOCKER_IMAGE:$NEW_VERSION" docker push "$DOCKER_IMAGE:latest" echo "" echo -e "${BLUE}Step 4/6: Deploying to production ($PROD_HOST)${NC}" ssh "$PROD_HOST" "cd $PROD_PATH && docker compose pull $COMPOSE_SERVICE && docker compose up -d $COMPOSE_SERVICE" echo "" echo -e "${BLUE}Step 5/6: Committing version bump${NC}" git add VERSION git commit -m "Bump version to $NEW_VERSION" git tag "v$NEW_VERSION" echo "" echo -e "${BLUE}Step 6/6: Pushing to git${NC}" git push git push --tags echo "" echo -e "${BLUE}Verifying deployment...${NC}" sleep 3 ssh "$PROD_HOST" "docker ps --filter name=$CONTAINER_NAME --format 'table {{.Names}}\t{{.Status}}\t{{.Image}}'" echo "" echo -e "${GREEN}========================================${NC}" echo -e "${GREEN} Deployment Complete!${NC}" echo -e "${GREEN} $FRIENDLY_NAME v$NEW_VERSION is now live${NC}" echo -e "${GREEN}========================================${NC}" echo "" echo "To view logs:" echo " ssh $PROD_HOST \"docker logs $CONTAINER_NAME --tail 100 -f\""