Added deploy and migrate scripts and made sure they were consistent
Some checks are pending
CI/CD Pipeline / Test Backend (push) Waiting to run
CI/CD Pipeline / Test Frontend (push) Waiting to run
CI/CD Pipeline / Build and Push Docker Images (push) Blocked by required conditions
CI/CD Pipeline / Deploy to Production (push) Blocked by required conditions

This commit is contained in:
continuist 2025-06-28 01:26:38 -04:00
parent 299b8d4a33
commit 4988a397f1
5 changed files with 504 additions and 5 deletions

View file

@ -137,7 +137,7 @@ jobs:
script: |
cd /opt/${{ secrets.APP_NAME || 'sharenet' }}
# Pull latest code from repository (includes docker-compose.yml and nginx config)
# Pull latest code from repository (includes scripts and docker-compose.yml)
git pull origin main
# Create environment file for this deployment
@ -145,9 +145,14 @@ jobs:
echo "REGISTRY=${{ secrets.CI_HOST }}:5000" >> .env
echo "IMAGE_NAME=${{ secrets.APP_NAME || 'sharenet' }}" >> .env
echo "POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD || 'your_secure_password_here' }}" >> .env
echo "POSTGRES_USER=${{ secrets.POSTGRES_USER || 'sharenet' }}" >> .env
echo "POSTGRES_DB=${{ secrets.POSTGRES_DB || 'sharenet' }}" >> .env
echo "DATABASE_URL=postgresql://${{ secrets.POSTGRES_USER || 'sharenet' }}:${{ secrets.POSTGRES_PASSWORD || 'your_secure_password_here' }}@postgres:5432/${{ secrets.POSTGRES_DB || 'sharenet' }}" >> .env
echo "NODE_ENV=production" >> .env
echo "RUST_LOG=info" >> .env
# Run deployment using the repository's docker-compose.yml
sudo ./deploy.sh
# Make scripts executable
chmod +x scripts/*.sh
# Run deployment using the new deployment script
./scripts/deploy.sh deploy

View file

@ -30,6 +30,7 @@ FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y \
ca-certificates \
libssl3 \
wget \
&& rm -rf /var/lib/apt/lists/*
# Create non-root user
@ -55,7 +56,7 @@ EXPOSE 3001
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3001/health || exit 1
CMD wget --no-verbose --tries=1 --spider http://localhost:3001/health || exit 1
# Run the application
CMD ["./sharenet-api"]

View file

@ -36,7 +36,7 @@ services:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/health"]
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:3001/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3

176
scripts/deploy.sh Normal file
View file

@ -0,0 +1,176 @@
#!/bin/bash
# Sharenet Production Deployment Script
# This script handles safe production deployments with migrations
set -e
# Configuration
APP_NAME="sharenet"
BACKUP_DIR="backups"
MIGRATION_SCRIPT="./scripts/migrate.sh"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Pre-deployment checks
pre_deployment_checks() {
log_info "Running pre-deployment checks..."
# Check if migration script exists
if [ ! -f "$MIGRATION_SCRIPT" ]; then
log_error "Migration script not found: $MIGRATION_SCRIPT"
log_error "Current directory: $(pwd)"
log_error "Available files in scripts/:"
ls -la scripts/ 2>/dev/null || log_error "scripts/ directory not found"
exit 1
fi
# Check if migration script is executable
if [ ! -x "$MIGRATION_SCRIPT" ]; then
log_info "Making migration script executable"
chmod +x "$MIGRATION_SCRIPT"
fi
# Check database connection
if ! "$MIGRATION_SCRIPT" status >/dev/null 2>&1; then
log_error "Cannot connect to database"
log_error "Running migration script with verbose output:"
"$MIGRATION_SCRIPT" status
exit 1
fi
log_success "Pre-deployment checks passed"
}
# Create backup
create_backup() {
log_info "Creating database backup..."
mkdir -p "$BACKUP_DIR"
if "$MIGRATION_SCRIPT" backup; then
log_success "Backup created successfully"
else
log_error "Backup failed"
exit 1
fi
}
# Run migrations
run_migrations() {
log_info "Running database migrations..."
if "$MIGRATION_SCRIPT" run; then
log_success "Migrations completed successfully"
else
log_error "Migrations failed"
log_warning "Consider restoring from backup"
exit 1
fi
}
# Deploy application
deploy_application() {
log_info "Deploying application..."
# Pull latest images
docker-compose pull
# Stop current services
docker-compose down
# Start services with new images
docker-compose up -d
# Wait for services to be healthy
log_info "Waiting for services to be healthy..."
local max_attempts=60 # 5 minutes with 5-second intervals
local attempt=0
while [ $attempt -lt $max_attempts ]; do
if docker-compose ps | grep -q "healthy"; then
log_success "All services are healthy"
return 0
fi
log_info "Waiting for services to become healthy... (attempt $((attempt + 1))/$max_attempts)"
sleep 5
attempt=$((attempt + 1))
done
log_error "Services failed to become healthy within timeout"
log_error "Current service status:"
docker-compose ps
log_error "Recent logs:"
docker-compose logs --tail=50
exit 1
}
# Rollback function
rollback() {
log_warning "Rolling back deployment..."
# Stop services
docker-compose down
# Restore from backup
local latest_backup=$(ls -t "$BACKUP_DIR"/backup_*.sql 2>/dev/null | head -1)
if [ -n "$latest_backup" ]; then
log_info "Restoring from backup: $latest_backup"
"$MIGRATION_SCRIPT" restore "$latest_backup"
else
log_warning "No backup found for rollback"
fi
# Start services with previous images
docker-compose up -d
log_warning "Rollback completed"
}
# Main deployment process
main() {
local command="${1:-deploy}"
case "$command" in
deploy)
log_info "Starting production deployment..."
pre_deployment_checks
create_backup
run_migrations
deploy_application
log_success "Production deployment completed successfully"
;;
rollback)
log_warning "Starting rollback process..."
rollback
log_success "Rollback completed successfully"
;;
check)
log_info "Running deployment checks..."
pre_deployment_checks
"$MIGRATION_SCRIPT" status
log_success "All checks passed"
;;
*)
log_error "Unknown command: $command"
echo "Usage: $0 {deploy|rollback|check}"
exit 1
;;
esac
}
# Handle interrupts gracefully
trap 'log_error "Deployment interrupted"; exit 1' INT TERM
# Run main function
main "$@"

317
scripts/migrate.sh Normal file
View file

@ -0,0 +1,317 @@
#!/bin/bash
# Sharenet Database Migration Script
# This script handles database migrations in production environments
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration
MIGRATIONS_DIR="backend/migrations"
BACKUP_DIR="backups"
DATABASE_URL="${DATABASE_URL:-}"
DRY_RUN="${DRY_RUN:-false}"
VERBOSE="${VERBOSE:-false}"
# Functions
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
show_help() {
cat << EOF
Sharenet Database Migration Script
Usage: $0 [COMMAND] [OPTIONS]
Commands:
run Run pending migrations
status Show migration status
create Create a new migration file
revert Revert the last migration
backup Create a backup before migration
restore Restore from backup
Options:
--dry-run Show what would be done without executing
--verbose Show detailed output
--help Show this help message
Environment Variables:
DATABASE_URL Database connection string
DRY_RUN Set to 'true' for dry run mode
VERBOSE Set to 'true' for verbose output
Examples:
$0 run # Run pending migrations
$0 status # Show migration status
$0 create add_user_table # Create new migration
DRY_RUN=true $0 run # Dry run migrations
EOF
}
check_dependencies() {
log_info "Checking dependencies..."
if ! command -v sqlx &> /dev/null; then
log_error "sqlx CLI not found. Install with: cargo install sqlx-cli"
exit 1
fi
if [ -z "$DATABASE_URL" ]; then
log_error "DATABASE_URL environment variable is required"
exit 1
fi
log_success "Dependencies check passed"
}
check_connection() {
log_info "Testing database connection..."
# Check if we're running in Docker environment
if [ -f /.dockerenv ] || [ -f /proc/1/cgroup ] && grep -q docker /proc/1/cgroup; then
log_info "Detected Docker environment"
# In Docker, use the service name as host
if [[ "$DATABASE_URL" == *"localhost"* ]] || [[ "$DATABASE_URL" == *"127.0.0.1"* ]]; then
log_info "Updating DATABASE_URL for Docker environment"
export DATABASE_URL=$(echo "$DATABASE_URL" | sed 's/localhost/postgres/g' | sed 's/127.0.0.1/postgres/g')
fi
fi
if ! sqlx database create --database-url "$DATABASE_URL" 2>/dev/null; then
log_warning "Database might already exist, continuing..."
fi
if ! sqlx migrate info --database-url "$DATABASE_URL" >/dev/null 2>&1; then
log_error "Cannot connect to database. Check DATABASE_URL and network connectivity"
log_error "DATABASE_URL: $DATABASE_URL"
exit 1
fi
log_success "Database connection successful"
}
run_migrations() {
log_info "Running database migrations..."
if [ "$DRY_RUN" = "true" ]; then
log_warning "DRY RUN MODE - No changes will be made"
sqlx migrate info --database-url "$DATABASE_URL"
return
fi
# Show current status
sqlx migrate info --database-url "$DATABASE_URL"
# Run migrations
if sqlx migrate run --database-url "$DATABASE_URL"; then
log_success "Migrations completed successfully"
# Show final status
log_info "Final migration status:"
sqlx migrate info --database-url "$DATABASE_URL"
else
log_error "Migration failed"
exit 1
fi
}
show_status() {
log_info "Migration status:"
sqlx migrate info --database-url "$DATABASE_URL"
}
create_migration() {
if [ $# -eq 0 ]; then
log_error "Migration name is required"
echo "Usage: $0 create <migration_name>"
exit 1
fi
local migration_name="$1"
log_info "Creating migration: $migration_name"
if [ "$DRY_RUN" = "true" ]; then
log_warning "DRY RUN MODE - Migration file would be created"
echo "sqlx migrate add $migration_name"
return
fi
if sqlx migrate add --database-url "$DATABASE_URL" "$migration_name"; then
log_success "Migration file created successfully"
log_info "Edit the generated file in $MIGRATIONS_DIR/"
else
log_error "Failed to create migration file"
exit 1
fi
}
revert_migration() {
log_warning "Reverting last migration..."
if [ "$DRY_RUN" = "true" ]; then
log_warning "DRY RUN MODE - No changes will be made"
return
fi
if sqlx migrate revert --database-url "$DATABASE_URL"; then
log_success "Migration reverted successfully"
else
log_error "Failed to revert migration"
exit 1
fi
}
backup_database() {
# Create backup directory if it doesn't exist
mkdir -p "$BACKUP_DIR"
local backup_file="$BACKUP_DIR/backup_$(date +%Y%m%d_%H%M%S).sql"
log_info "Creating database backup: $backup_file"
if [ "$DRY_RUN" = "true" ]; then
log_warning "DRY RUN MODE - Backup would be created"
return
fi
# Extract connection details from DATABASE_URL
local db_host=$(echo "$DATABASE_URL" | sed -n 's/.*@\([^:]*\).*/\1/p')
local db_port=$(echo "$DATABASE_URL" | sed -n 's/.*:\([0-9]*\)\/.*/\1/p')
local db_name=$(echo "$DATABASE_URL" | sed -n 's/.*\/\([^?]*\).*/\1/p')
local db_user=$(echo "$DATABASE_URL" | sed -n 's/.*:\/\/\([^:]*\):.*/\1/p')
local db_pass=$(echo "$DATABASE_URL" | sed -n 's/.*:\/\/[^:]*:\([^@]*\)@.*/\1/p')
# Set PGPASSWORD for pg_dump
export PGPASSWORD="$db_pass"
if pg_dump -h "$db_host" -p "$db_port" -U "$db_user" -d "$db_name" > "$backup_file"; then
log_success "Backup created: $backup_file"
else
log_error "Backup failed"
exit 1
fi
}
restore_database() {
if [ $# -eq 0 ]; then
log_error "Backup file is required"
echo "Usage: $0 restore <backup_file>"
exit 1
fi
local backup_file="$1"
# If backup file doesn't have a path, assume it's in BACKUP_DIR
if [[ "$backup_file" != */* ]]; then
backup_file="$BACKUP_DIR/$backup_file"
fi
if [ ! -f "$backup_file" ]; then
log_error "Backup file not found: $backup_file"
exit 1
fi
log_warning "Restoring database from: $backup_file"
log_warning "This will overwrite the current database!"
if [ "$DRY_RUN" = "true" ]; then
log_warning "DRY RUN MODE - No changes will be made"
return
fi
read -p "Are you sure you want to continue? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log_info "Restore cancelled"
exit 0
fi
# Extract connection details from DATABASE_URL
local db_host=$(echo "$DATABASE_URL" | sed -n 's/.*@\([^:]*\).*/\1/p')
local db_port=$(echo "$DATABASE_URL" | sed -n 's/.*:\([0-9]*\)\/.*/\1/p')
local db_name=$(echo "$DATABASE_URL" | sed -n 's/.*\/\([^?]*\).*/\1/p')
local db_user=$(echo "$DATABASE_URL" | sed -n 's/.*:\/\/\([^:]*\):.*/\1/p')
local db_pass=$(echo "$DATABASE_URL" | sed -n 's/.*:\/\/[^:]*:\([^@]*\)@.*/\1/p')
# Set PGPASSWORD for psql
export PGPASSWORD="$db_pass"
if psql -h "$db_host" -p "$db_port" -U "$db_user" -d "$db_name" < "$backup_file"; then
log_success "Database restored successfully"
else
log_error "Restore failed"
exit 1
fi
}
# Main script logic
main() {
local command="${1:-}"
case "$command" in
run)
check_dependencies
check_connection
run_migrations
;;
status)
check_dependencies
check_connection
show_status
;;
create)
check_dependencies
create_migration "${@:2}"
;;
revert)
check_dependencies
check_connection
revert_migration
;;
backup)
check_dependencies
check_connection
backup_database
;;
restore)
check_dependencies
check_connection
restore_database "${@:2}"
;;
help|--help|-h)
show_help
;;
"")
log_error "No command provided"
show_help
exit 1
;;
*)
log_error "Unknown command: $command"
show_help
exit 1
;;
esac
}
# Run main function with all arguments
main "$@"