Initial commit: Complete NodeJS-native setup

- Migrated from Python pre-commit to NodeJS-native solution
- Reorganized documentation structure
- Set up Husky + lint-staged for efficient pre-commit hooks
- Fixed Dockerfile healthcheck issue
- Added comprehensive documentation index
This commit is contained in:
William Valentin
2025-09-06 01:42:48 -07:00
commit e48adbcb00
159 changed files with 24405 additions and 0 deletions
+221
View File
@@ -0,0 +1,221 @@
#!/bin/bash
# 🧪 Deployment Validation Script
# Validates complete deployment with all environment variables and health checks
set -e
echo "🚀 Starting deployment validation..."
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to print colored output
print_status() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Cleanup function
cleanup() {
print_status "Cleaning up test containers..."
docker stop meds-validation-test 2>/dev/null || true
docker rm meds-validation-test 2>/dev/null || true
docker compose -f docker/docker-compose.yaml -p meds-validation down 2>/dev/null || true
}
# Set trap for cleanup
trap cleanup EXIT
print_status "1. Validating environment files..."
# Check if required environment files exist
if [[ ! -f .env ]]; then
print_error ".env file not found. Run 'cp .env.example .env' and configure it."
exit 1
fi
if [[ ! -f .env.example ]]; then
print_error ".env.example file not found."
exit 1
fi
print_success "Environment files exist"
# Validate environment consistency
print_status "2. Checking environment variable consistency..."
./validate-env.sh
print_status "3. Setting up Docker Buildx..."
# Ensure buildx is available
if ! docker buildx version >/dev/null 2>&1; then
print_error "Docker Buildx is not available. Please update Docker to a version that supports Buildx."
exit 1
fi
# Create a new builder instance if it doesn't exist
if ! docker buildx ls | grep -q "meds-builder"; then
print_status "Creating new buildx builder instance..."
docker buildx create --name meds-builder --driver docker-container --bootstrap
fi
# Use the builder
docker buildx use meds-builder
print_status "4. Building multi-platform Docker image with buildx..."
# Build the image with buildx for multiple platforms
docker buildx build --no-cache \
--platform linux/amd64,linux/arm64 \
--build-arg COUCHDB_USER="${COUCHDB_USER:-admin}" \
--build-arg COUCHDB_PASSWORD="${COUCHDB_PASSWORD:-change-this-secure-password}" \
--build-arg VITE_COUCHDB_URL="${VITE_COUCHDB_URL:-http://localhost:5984}" \
--build-arg VITE_COUCHDB_USER="${VITE_COUCHDB_USER:-admin}" \
--build-arg VITE_COUCHDB_PASSWORD="${VITE_COUCHDB_PASSWORD:-change-this-secure-password}" \
--build-arg APP_BASE_URL="${APP_BASE_URL:-http://localhost:8080}" \
--build-arg VITE_GOOGLE_CLIENT_ID="${VITE_GOOGLE_CLIENT_ID:-}" \
--build-arg VITE_GITHUB_CLIENT_ID="${VITE_GITHUB_CLIENT_ID:-}" \
--build-arg MAILGUN_API_KEY="${MAILGUN_API_KEY:-}" \
--build-arg MAILGUN_DOMAIN="${MAILGUN_DOMAIN:-}" \
--build-arg MAILGUN_FROM_EMAIL="${MAILGUN_FROM_EMAIL:-}" \
--build-arg NODE_ENV="${NODE_ENV:-production}" \
-t meds-validation \
--load \
.
print_success "Docker image built successfully"
print_status "5. Testing container startup and health..."
# Run container in background
docker run --rm -d \
-p 8083:80 \
--name meds-validation-test \
meds-validation
# Wait for container to start
sleep 5
# Check if container is running
if ! docker ps | grep -q meds-validation-test; then
print_error "Container failed to start"
docker logs meds-validation-test
exit 1
fi
print_success "Container started successfully"
# Test health endpoint
print_status "5. Testing health endpoint..."
for i in {1..10}; do
if curl -s -f http://localhost:8083/health > /dev/null; then
print_success "Health endpoint responding"
break
elif [[ $i -eq 10 ]]; then
print_error "Health endpoint not responding after 10 attempts"
exit 1
else
print_warning "Health endpoint not ready, retrying... ($i/10)"
sleep 2
fi
done
# Test main application
print_status "6. Testing main application..."
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8083)
if [[ $HTTP_CODE -eq 200 ]]; then
print_success "Main application responding (HTTP $HTTP_CODE)"
else
print_error "Main application not responding properly (HTTP $HTTP_CODE)"
exit 1
fi
# Test docker-compose build
print_status "7. Testing Docker Compose build..."
docker compose -f docker/docker-compose.yaml build frontend --no-cache
print_success "Docker Compose build successful"
# Test docker-compose with validation project name
print_status "8. Testing Docker Compose deployment..."
docker compose -f docker/docker-compose.yaml -p meds-validation up -d --build
# Wait for services to start
sleep 10
# Check service health
if docker compose -f docker/docker-compose.yaml -p meds-validation ps | grep -q "Up"; then
print_success "Docker Compose services started successfully"
else
print_error "Docker Compose services failed to start"
docker compose -f docker/docker-compose.yaml -p meds-validation logs
exit 1
fi
# Test health of compose deployment
if curl -s -f http://localhost:8080/health > /dev/null; then
print_success "Docker Compose health endpoint responding"
else
print_warning "Docker Compose health endpoint not responding (may need CouchDB)"
fi
print_status "9. Checking image size..."
IMAGE_SIZE=$(docker image inspect meds-validation --format='{{.Size}}' | numfmt --to=iec)
print_success "Image size: $IMAGE_SIZE"
print_status "10. Validating security configuration..."
# Check if image runs as non-root
USER_INFO=$(docker run --rm meds-validation whoami)
if [[ "$USER_INFO" != "root" ]]; then
print_success "Container runs as non-root user: $USER_INFO"
else
print_warning "Container runs as root user (security consideration)"
fi
# Check nginx configuration
if docker run --rm meds-validation nginx -t 2>/dev/null; then
print_success "Nginx configuration is valid"
else
print_error "Nginx configuration has issues"
exit 1
fi
print_status "11. Final validation complete!"
echo
echo "🎉 Deployment validation successful!"
echo
echo "Summary:"
echo "✅ Environment files validated"
echo "✅ Docker image builds successfully"
echo "✅ Container starts and runs healthy"
echo "✅ Health endpoints respond correctly"
echo "✅ Docker Compose deployment works"
echo "✅ Security configuration validated"
echo "✅ Image size optimized ($IMAGE_SIZE)"
echo
echo "Your deployment is ready for production! 🚀"
echo
echo "Next steps:"
echo "1. Configure production environment variables in .env"
echo "2. Run './deploy.sh production' for production deployment"
echo "3. Set up monitoring and backups"
echo "4. Configure SSL/TLS certificates"
echo
+274
View File
@@ -0,0 +1,274 @@
#!/bin/bash
# Kubernetes deployment script with environment variable substitution
# This script processes template files and applies them to Kubernetes
set -euo pipefail
# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Script directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
K8S_DIR="$SCRIPT_DIR/k8s"
TEMP_DIR="/tmp/meds-k8s-deploy"
# Function to print colored output
print_info() {
echo -e "${BLUE}$1${NC}"
}
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}"
}
# Function to load environment variables
load_env() {
local env_file="$1"
if [[ -f "$env_file" ]]; then
print_info "Loading environment from $env_file"
# Export variables from .env file
set -a
source "$env_file"
set +a
else
print_warning "Environment file $env_file not found"
fi
}
# Function to substitute environment variables in template files
substitute_templates() {
print_info "Processing template files..."
# Create temporary directory
mkdir -p "$TEMP_DIR"
# Process each template file
for template_file in "$K8S_DIR"/*.template; do
if [[ -f "$template_file" ]]; then
local filename=$(basename "$template_file" .template)
local output_file="$TEMP_DIR/$filename"
print_info "Processing template: $filename"
# Substitute environment variables
envsubst < "$template_file" > "$output_file"
print_success "Generated: $output_file"
fi
done
}
# Function to validate required environment variables
validate_env() {
local required_vars=("INGRESS_HOST")
local missing_vars=()
for var in "${required_vars[@]}"; do
if [[ -z "${!var:-}" ]]; then
missing_vars+=("$var")
fi
done
if [[ ${#missing_vars[@]} -gt 0 ]]; then
print_error "Missing required environment variables:"
for var in "${missing_vars[@]}"; do
echo " - $var"
done
echo ""
echo "Please set these variables in your .env file or environment."
exit 1
fi
}
# Function to apply Kubernetes manifests
apply_manifests() {
local manifest_dir="$1"
print_info "Applying Kubernetes manifests from $manifest_dir"
# Apply non-template files first
for manifest_file in "$K8S_DIR"/*.yaml; do
if [[ -f "$manifest_file" && ! "$manifest_file" =~ \.template$ ]]; then
print_info "Applying: $(basename "$manifest_file")"
kubectl apply -f "$manifest_file"
fi
done
# Apply processed template files
if [[ -d "$TEMP_DIR" ]]; then
for manifest_file in "$TEMP_DIR"/*.yaml; do
if [[ -f "$manifest_file" ]]; then
print_info "Applying: $(basename "$manifest_file")"
kubectl apply -f "$manifest_file"
fi
done
fi
}
# Function to cleanup temporary files
cleanup() {
if [[ -d "$TEMP_DIR" ]]; then
print_info "Cleaning up temporary files..."
rm -rf "$TEMP_DIR"
fi
}
# Function to show deployment status
show_status() {
print_info "Deployment Status:"
echo ""
print_info "Pods:"
kubectl get pods -l app=rxminder
echo ""
print_info "Services:"
kubectl get services -l app=rxminder
echo ""
print_info "Ingress:"
kubectl get ingress -l app=rxminder
echo ""
if [[ -n "${INGRESS_HOST:-}" ]]; then
print_success "Application should be available at: http://$INGRESS_HOST"
fi
}
# Function to show usage
usage() {
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Options:"
echo " -e, --env FILE Environment file to load (default: .env)"
echo " -d, --dry-run Show what would be applied without applying"
echo " -s, --status Show deployment status only"
echo " -c, --cleanup Cleanup temporary files and exit"
echo " -h, --help Show this help message"
echo ""
echo "Examples:"
echo " $0 Deploy with default .env file"
echo " $0 -e .env.prod Deploy with production environment"
echo " $0 --dry-run Preview what would be deployed"
echo " $0 --status Check deployment status"
}
# Main function
main() {
local env_file=".env"
local dry_run=false
local status_only=false
local cleanup_only=false
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-e|--env)
env_file="$2"
shift 2
;;
-d|--dry-run)
dry_run=true
shift
;;
-s|--status)
status_only=true
shift
;;
-c|--cleanup)
cleanup_only=true
shift
;;
-h|--help)
usage
exit 0
;;
*)
print_error "Unknown option: $1"
usage
exit 1
;;
esac
done
# Handle cleanup only
if [[ "$cleanup_only" == true ]]; then
cleanup
exit 0
fi
# Handle status only
if [[ "$status_only" == true ]]; then
show_status
exit 0
fi
# Check if kubectl is available
if ! command -v kubectl &> /dev/null; then
print_error "kubectl is not installed or not in PATH"
exit 1
fi
# Check if we can connect to Kubernetes cluster
if ! kubectl cluster-info &> /dev/null; then
print_error "Cannot connect to Kubernetes cluster"
print_info "Make sure your kubectl is configured correctly"
exit 1
fi
print_info "🚀 Deploying Medication Reminder App to Kubernetes"
echo ""
# Load environment variables
load_env "$env_file"
# Validate required environment variables
validate_env
# Process templates
substitute_templates
if [[ "$dry_run" == true ]]; then
print_info "Dry run mode - showing generated manifests:"
echo ""
for manifest_file in "$TEMP_DIR"/*.yaml; do
if [[ -f "$manifest_file" ]]; then
echo "=== $(basename "$manifest_file") ==="
cat "$manifest_file"
echo ""
fi
done
else
# Apply manifests
apply_manifests "$K8S_DIR"
print_success "Deployment completed!"
echo ""
# Show status
show_status
fi
# Cleanup
cleanup
}
# Trap to ensure cleanup happens
trap cleanup EXIT
# Run main function
main "$@"
+221
View File
@@ -0,0 +1,221 @@
#!/bin/bash
# 🧪 Deployment Validation Script
# Validates complete deployment with all environment variables and health checks
set -e
echo "🚀 Starting deployment validation..."
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to print colored output
print_status() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Cleanup function
cleanup() {
print_status "Cleaning up test containers..."
docker stop meds-validation-test 2>/dev/null || true
docker rm meds-validation-test 2>/dev/null || true
docker compose -f docker/docker-compose.yaml -p meds-validation down 2>/dev/null || true
}
# Set trap for cleanup
trap cleanup EXIT
print_status "1. Validating environment files..."
# Check if required environment files exist
if [[ ! -f .env ]]; then
print_error ".env file not found. Run 'cp .env.example .env' and configure it."
exit 1
fi
if [[ ! -f .env.example ]]; then
print_error ".env.example file not found."
exit 1
fi
print_success "Environment files exist"
# Validate environment consistency
print_status "2. Checking environment variable consistency..."
./validate-env.sh
print_status "3. Setting up Docker Buildx..."
# Ensure buildx is available
if ! docker buildx version >/dev/null 2>&1; then
print_error "Docker Buildx is not available. Please update Docker to a version that supports Buildx."
exit 1
fi
# Create a new builder instance if it doesn't exist
if ! docker buildx ls | grep -q "meds-builder"; then
print_status "Creating new buildx builder instance..."
docker buildx create --name meds-builder --driver docker-container --bootstrap
fi
# Use the builder
docker buildx use meds-builder
print_status "4. Building multi-platform Docker image with buildx..."
# Build the image with buildx for multiple platforms
docker buildx build --no-cache \
--platform linux/amd64,linux/arm64 \
--build-arg COUCHDB_USER="${COUCHDB_USER:-admin}" \
--build-arg COUCHDB_PASSWORD="${COUCHDB_PASSWORD:-change-this-secure-password}" \
--build-arg VITE_COUCHDB_URL="${VITE_COUCHDB_URL:-http://localhost:5984}" \
--build-arg VITE_COUCHDB_USER="${VITE_COUCHDB_USER:-admin}" \
--build-arg VITE_COUCHDB_PASSWORD="${VITE_COUCHDB_PASSWORD:-change-this-secure-password}" \
--build-arg APP_BASE_URL="${APP_BASE_URL:-http://localhost:8080}" \
--build-arg VITE_GOOGLE_CLIENT_ID="${VITE_GOOGLE_CLIENT_ID:-}" \
--build-arg VITE_GITHUB_CLIENT_ID="${VITE_GITHUB_CLIENT_ID:-}" \
--build-arg MAILGUN_API_KEY="${MAILGUN_API_KEY:-}" \
--build-arg MAILGUN_DOMAIN="${MAILGUN_DOMAIN:-}" \
--build-arg MAILGUN_FROM_EMAIL="${MAILGUN_FROM_EMAIL:-}" \
--build-arg NODE_ENV="${NODE_ENV:-production}" \
-t meds-validation \
--load \
.
print_success "Docker image built successfully"
print_status "5. Testing container startup and health..."
# Run container in background
docker run --rm -d \
-p 8083:80 \
--name meds-validation-test \
meds-validation
# Wait for container to start
sleep 5
# Check if container is running
if ! docker ps | grep -q meds-validation-test; then
print_error "Container failed to start"
docker logs meds-validation-test
exit 1
fi
print_success "Container started successfully"
# Test health endpoint
print_status "5. Testing health endpoint..."
for i in {1..10}; do
if curl -s -f http://localhost:8083/health > /dev/null; then
print_success "Health endpoint responding"
break
elif [[ $i -eq 10 ]]; then
print_error "Health endpoint not responding after 10 attempts"
exit 1
else
print_warning "Health endpoint not ready, retrying... ($i/10)"
sleep 2
fi
done
# Test main application
print_status "6. Testing main application..."
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8083)
if [[ $HTTP_CODE -eq 200 ]]; then
print_success "Main application responding (HTTP $HTTP_CODE)"
else
print_error "Main application not responding properly (HTTP $HTTP_CODE)"
exit 1
fi
# Test docker-compose build
print_status "7. Testing Docker Compose build..."
docker compose -f docker/docker-compose.yaml build frontend --no-cache
print_success "Docker Compose build successful"
# Test docker-compose with validation project name
print_status "8. Testing Docker Compose deployment..."
docker compose -f docker/docker-compose.yaml -p meds-validation up -d --build
# Wait for services to start
sleep 10
# Check service health
if docker compose -f docker/docker-compose.yaml -p meds-validation ps | grep -q "Up"; then
print_success "Docker Compose services started successfully"
else
print_error "Docker Compose services failed to start"
docker compose -f docker/docker-compose.yaml -p meds-validation logs
exit 1
fi
# Test health of compose deployment
if curl -s -f http://localhost:8080/health > /dev/null; then
print_success "Docker Compose health endpoint responding"
else
print_warning "Docker Compose health endpoint not responding (may need CouchDB)"
fi
print_status "9. Checking image size..."
IMAGE_SIZE=$(docker image inspect meds-validation --format='{{.Size}}' | numfmt --to=iec)
print_success "Image size: $IMAGE_SIZE"
print_status "10. Validating security configuration..."
# Check if image runs as non-root
USER_INFO=$(docker run --rm meds-validation whoami)
if [[ "$USER_INFO" != "root" ]]; then
print_success "Container runs as non-root user: $USER_INFO"
else
print_warning "Container runs as root user (security consideration)"
fi
# Check nginx configuration
if docker run --rm meds-validation nginx -t 2>/dev/null; then
print_success "Nginx configuration is valid"
else
print_error "Nginx configuration has issues"
exit 1
fi
print_status "11. Final validation complete!"
echo
echo "🎉 Deployment validation successful!"
echo
echo "Summary:"
echo "✅ Environment files validated"
echo "✅ Docker image builds successfully"
echo "✅ Container starts and runs healthy"
echo "✅ Health endpoints respond correctly"
echo "✅ Docker Compose deployment works"
echo "✅ Security configuration validated"
echo "✅ Image size optimized ($IMAGE_SIZE)"
echo
echo "Your deployment is ready for production! 🚀"
echo
echo "Next steps:"
echo "1. Configure production environment variables in .env"
echo "2. Run './deploy.sh production' for production deployment"
echo "3. Set up monitoring and backups"
echo "4. Configure SSL/TLS certificates"
echo
+382
View File
@@ -0,0 +1,382 @@
#!/bin/bash
# 🧪 Deployment Validation Script
# Validates complete deployment with all environment variables and health checks
set -e
echo "🚀 Starting deploymif docker compose -f docker/docker-compose.yaml -p rxminder-validation ps | grep -q "Up"; then
print_success "Docker Compose setup completed successfully!"
else
print_error "Docker Compose services failed to start"
docker compose -f docker/docker-compose.yaml -p rxminder-validation logsalidation..."
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to print colored output
print_status() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Cleanup function
cleanup() {
print_status "Cleaning up test containers..."
docker stop rxminder-validation-test 2>/dev/null || true
docker rm rxminder-validation-test 2>/dev/null || true
docker compose -f docker/docker-compose.yaml -p rxminder-validation down 2>/dev/null || true
}
# Set trap for cleanup
trap cleanup EXIT
print_status "1. Validating environment files..."
# Check if required environment files exist
if [[ ! -f .env ]]; then
print_error ".env file not found. Run 'cp .env.example .env' and configure it."
exit 1
fi
if [[ ! -f .env.example ]]; then
print_error ".env.example file not found."
exit 1
fi
print_success "Environment files exist"
# Validate environment consistency
print_status "2. Checking environment variable consistency..."
./validate-env.sh
print_status "3. Setting up Docker Buildx..."
# Ensure buildx is available
if ! docker buildx version >/dev/null 2>&1; then
print_error "Docker Buildx is not available. Please update Docker to a version that supports Buildx."
exit 1
fi
# Create a new builder instance if it doesn't exist
if ! docker buildx ls | grep -q "rxminder-builder"; then
print_status "Creating new buildx builder instance..."
docker buildx create --name rxminder-builder --driver docker-container --bootstrap
fi
# Use the builder
docker buildx use rxminder-builder
print_status "4. Building multi-platform Docker image with buildx..."
# Build the image with buildx for multiple platforms
docker buildx build --no-cache \
--platform linux/amd64,linux/arm64 \
--build-arg COUCHDB_USER="${COUCHDB_USER:-admin}" \
--build-arg COUCHDB_PASSWORD="${COUCHDB_PASSWORD:-change-this-secure-password}" \
--build-arg VITE_COUCHDB_URL="${VITE_COUCHDB_URL:-http://localhost:5984}" \
--build-arg VITE_COUCHDB_USER="${VITE_COUCHDB_USER:-admin}" \
--build-arg VITE_COUCHDB_PASSWORD="${VITE_COUCHDB_PASSWORD:-change-this-secure-password}" \
--build-arg APP_BASE_URL="${APP_BASE_URL:-http://localhost:8080}" \
--build-arg VITE_GOOGLE_CLIENT_ID="${VITE_GOOGLE_CLIENT_ID:-}" \
--build-arg VITE_GITHUB_CLIENT_ID="${VITE_GITHUB_CLIENT_ID:-}" \
--build-arg MAILGUN_API_KEY="${MAILGUN_API_KEY:-}" \
--build-arg MAILGUN_DOMAIN="${MAILGUN_DOMAIN:-}" \
--build-arg MAILGUN_FROM_EMAIL="${MAILGUN_FROM_EMAIL:-}" \
--build-arg NODE_ENV="${NODE_ENV:-production}" \
-t rxminder-validation \
--load \
.
print_success "Docker image built successfully"
print_status "5. Testing container startup and health..."
# Run container in background
docker run --rm -d \
-p 8083:80 \
--name rxminder-validation-test \
rxminder-validation
# Wait for container to start
sleep 5
# Check if container is running
if ! docker ps | grep -q rxminder-validation-test; then
print_error "Container failed to start"
docker logs rxminder-validation-test
exit 1
fi
print_success "Container started successfully"
# Test health endpoint
print_status "5. Testing health endpoint..."
for i in {1..10}; do
if curl -s -f http://localhost:8083/health > /dev/null; then
print_success "Health endpoint responding"
break
elif [[ $i -eq 10 ]]; then
print_error "Health endpoint not responding after 10 attempts"
exit 1
else
print_warning "Health endpoint not ready, retrying... ($i/10)"
sleep 2
fi
done
# Test main application
print_status "6. Testing main application..."
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8083)
if [[ $HTTP_CODE -eq 200 ]]; then
print_success "Main application responding (HTTP $HTTP_CODE)"
else
print_error "Main application not responding properly (HTTP $HTTP_CODE)"
exit 1
fi
# Test docker-compose build
print_status "7. Testing Docker Compose build..."
docker compose -f docker/docker-compose.yaml build frontend --no-cache
print_success "Docker Compose build successful"
# Test docker-compose with validation project name
print_status "8. Testing Docker Compose deployment..."
docker compose -f docker/docker-compose.yaml -p rxminder-validation up -d --build
# Wait for services to start
sleep 10
# Check service health
if docker compose -f docker/docker-compose.yaml -p meds-validation ps | grep -q "Up"; then
print_success "Docker Compose services started successfully"
else
print_error "Docker Compose services failed to start"
docker compose -f docker/docker-compose.yaml -p meds-validation logs
exit 1
fi
# Test health of compose deployment
if curl -s -f http://localhost:8080/health > /dev/null; then
print_success "Docker Compose health endpoint responding"
else
print_warning "Docker Compose health endpoint not responding (may need CouchDB)"
fi
print_status "9. Checking image size..."
IMAGE_SIZE=$(docker image inspect rxminder-validation --format='{{.Size}}' | numfmt --to=iec)
print_success "Image size: $IMAGE_SIZE"
print_status "10. Validating security configuration..."
# Check if image runs as non-root
USER_INFO=$(docker run --rm rxminder-validation whoami)
if [[ "$USER_INFO" != "root" ]]; then
print_success "Container runs as non-root user: $USER_INFO"
else
print_warning "Container runs as root user (security consideration)"
fi
# Check nginx configuration
if docker run --rm rxminder-validation nginx -t 2>/dev/null; then
print_success "Nginx configuration is valid"
else
print_error "Nginx configuration has issues"
exit 1
fi
print_status "11. Final validation complete!"
echo
echo "🎉 Deployment validation successful!"
echo
echo "Summary:"
echo "✅ Environment files validated"
echo "✅ Docker image builds successfully"
echo "✅ Container starts and runs healthy"
echo "✅ Health endpoints respond correctly"
echo "✅ Docker Compose deployment works"
echo "✅ Security configuration validated"
echo "✅ Image size optimized ($IMAGE_SIZE)"
echo
echo "Your deployment is ready for production! 🚀"
echo
echo "Next steps:"
echo "1. Configure production environment variables in .env"
echo "2. Run './deploy.sh production' for production deployment"
echo "3. Set up monitoring and backups"
echo "4. Configure SSL/TLS certificates"
echo
# Load Gitea-specific environment variables
REGISTRY=${GITEA_SERVER_URL#https://}
IMAGE_NAME=${GITEA_REPOSITORY}
IMAGE_TAG=${GITEA_SHA:0:8}
print_status "Registry: $REGISTRY"
print_status "Image: $IMAGE_NAME:$IMAGE_TAG"
fi
# Check if .env file exists
if [ ! -f ".env" ]; then
print_warning ".env file not found, using defaults"
# Create minimal .env for Gitea deployment
cat > .env << EOF
COUCHDB_USER=admin
COUCHDB_PASSWORD=change-this-secure-password
VITE_COUCHDB_URL=http://couchdb:5984
VITE_COUCHDB_USER=admin
VITE_COUCHDB_PASSWORD=change-this-secure-password
APP_BASE_URL=http://localhost:8080
NODE_ENV=production
EOF
print_warning "Created default .env file - please update with your credentials"
fi
# Load environment variables
print_status "Loading environment variables from .env file..."
export $(cat .env | grep -v '^#' | xargs)
# Validate required environment variables
REQUIRED_VARS=("COUCHDB_USER" "COUCHDB_PASSWORD" "VITE_COUCHDB_URL")
for var in "${REQUIRED_VARS[@]}"; do
if [ -z "${!var}" ]; then
print_error "Required environment variable $var is not set"
exit 1
fi
done
print_success "Environment variables validated"
# Function to deploy via Docker Compose
deploy_compose() {
print_status "Deploying with Docker Compose..."
# Export image variables for compose
export IMAGE_TAG
export REGISTRY
export IMAGE_NAME
# Use the built image from registry if available
if [ "$GITEA_ACTIONS" = "true" ]; then
# Override the image in docker-compose
export FRONTEND_IMAGE="$REGISTRY/$IMAGE_NAME:$IMAGE_TAG"
print_status "Using Gitea Actions built image: $FRONTEND_IMAGE"
fi
# Pull the latest images
print_status "Pulling latest images..."
docker-compose -f docker/docker-compose.yaml pull || print_warning "Failed to pull some images"
# Start services
print_status "Starting services..."
docker-compose -f docker/docker-compose.yaml up -d
# Wait for services
print_status "Waiting for services to be ready..."
sleep 10
# Health check
print_status "Checking service health..."
if curl -f http://localhost:8080/health > /dev/null 2>&1; then
print_success "Frontend service is healthy"
else
print_warning "Frontend health check failed, checking logs..."
docker-compose -f docker/docker-compose.yaml logs frontend
fi
if curl -f http://localhost:5984/_up > /dev/null 2>&1; then
print_success "CouchDB service is healthy"
else
print_warning "CouchDB health check failed"
fi
}
# Function to deploy via Kubernetes
deploy_k8s() {
print_status "Deploying to Kubernetes..."
if ! command -v kubectl &> /dev/null; then
print_error "kubectl is not installed"
exit 1
fi
# Update image in k8s manifests
if [ "$GITEA_ACTIONS" = "true" ]; then
print_status "Updating Kubernetes manifests with new image..."
sed -i "s|image:.*rxminder.*|image: $REGISTRY/$IMAGE_NAME:$IMAGE_TAG|g" k8s/frontend-deployment.yaml
fi
# Apply manifests
print_status "Applying Kubernetes manifests..."
kubectl apply -f k8s/
# Wait for rollout
print_status "Waiting for deployment rollout..."
kubectl rollout status deployment/frontend-deployment
print_success "Kubernetes deployment completed"
}
# Main deployment logic
case "$ENVIRONMENT" in
"production"|"prod")
print_status "Deploying to production environment"
deploy_compose
;;
"kubernetes"|"k8s")
print_status "Deploying to Kubernetes environment"
deploy_k8s
;;
"staging")
print_status "Deploying to staging environment"
# Use staging-specific configurations
export APP_BASE_URL="http://staging.localhost:8080"
deploy_compose
;;
*)
print_error "Unknown environment: $ENVIRONMENT"
echo "Available environments: production, kubernetes, staging"
exit 1
;;
esac
print_success "Deployment to $ENVIRONMENT completed successfully! 🎉"
# Post-deployment tasks
print_status "Running post-deployment tasks..."
# Cleanup old images (optional)
if [ "$CLEANUP_OLD_IMAGES" = "true" ]; then
print_status "Cleaning up old Docker images..."
docker image prune -f --filter "until=72h" || print_warning "Image cleanup failed"
fi
# Send notification (if configured)
if [ -n "$DEPLOYMENT_WEBHOOK_URL" ]; then
print_status "Sending deployment notification..."
curl -X POST "$DEPLOYMENT_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "{\"text\":\"✅ RxMinder deployed to $ENVIRONMENT\", \"environment\":\"$ENVIRONMENT\", \"image\":\"$REGISTRY/$IMAGE_NAME:$IMAGE_TAG\"}" \
|| print_warning "Failed to send notification"
fi
print_success "All tasks completed! 🚀"
+518
View File
@@ -0,0 +1,518 @@
#!/bin/bash
# gitea-deploy.sh - Gitea-specific deployment script
# Usage: ./gitea-deploy.sh [environment] [image-tag]
set -e # Exit on any error
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
# Load environment variables from .env file if it exists
if [ -f "$PROJECT_DIR/.env" ]; then
export $(cat "$PROJECT_DIR/.env" | grep -v '^#' | grep -E '^[A-Z_]+=.*' | xargs)
fi
ENVIRONMENT=${1:-production}
IMAGE_TAG=${2:-latest}
REGISTRY=${GITEA_REGISTRY:-${CONTAINER_REGISTRY:-"ghcr.io"}}
IMAGE_NAME=${GITEA_REPOSITORY:-${CONTAINER_REPOSITORY:-"rxminder"}}
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
print_status() {
echo -e "${BLUE}$1${NC}"
}
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}"
}
echo "🚀 Deploying RxMinder from Gitea to $ENVIRONMENT environment..."
# Check if running in Gitea Actions
if [ "$GITEA_ACTIONS" = "true" ]; then
print_status "Running in Gitea Actions environment"
# Load Gitea-specific environment variables
REGISTRY=${GITEA_SERVER_URL#https://}
IMAGE_NAME=${GITEA_REPOSITORY}
IMAGE_TAG=${GITEA_SHA:0:8}
print_status "Registry: $REGISTRY"
print_status "Image: $IMAGE_NAME:$IMAGE_TAG"
fi
# Check if .env file exists
if [ ! -f ".env" ]; then
print_warning ".env file not found, using defaults"
# Create minimal .env for Gitea deployment
cat > .env << EOF
COUCHDB_USER=admin
COUCHDB_PASSWORD=change-this-secure-password
VITE_COUCHDB_URL=http://couchdb:5984
VITE_COUCHDB_USER=admin
VITE_COUCHDB_PASSWORD=change-this-secure-password
APP_BASE_URL=http://localhost:8080
NODE_ENV=production
EOF
print_warning "Created default .env file - please update with your credentials"
fi
# Load environment variables
print_status "Loading environment variables from .env file..."
export $(cat .env | grep -v '^#' | xargs)
# Validate required environment variables
REQUIRED_VARS=("COUCHDB_USER" "COUCHDB_PASSWORD" "VITE_COUCHDB_URL")
for var in "${REQUIRED_VARS[@]}"; do
if [ -z "${!var}" ]; then
print_error "Required environment variable $var is not set"
exit 1
fi
done
print_success "Environment variables validated"
# Function to deploy via Docker Compose
deploy_compose() {
print_status "Deploying with Docker Compose..."
# Export image variables for compose
export IMAGE_TAG
export REGISTRY
export IMAGE_NAME
# Use the built image from registry if available
if [ "$GITEA_ACTIONS" = "true" ]; then
# Override the image in docker-compose
export FRONTEND_IMAGE="$REGISTRY/$IMAGE_NAME:$IMAGE_TAG"
print_status "Using Gitea Actions built image: $FRONTEND_IMAGE"
fi
# Pull the latest images
print_status "Pulling latest images..."
docker-compose -f docker/docker-compose.yaml pull || print_warning "Failed to pull some images"
# Start services
print_status "Starting services..."
docker-compose -f docker/docker-compose.yaml up -d
# Wait for services
print_status "Waiting for services to be ready..."
sleep 10
# Health check
print_status "Checking service health..."
if curl -f http://localhost:8080/health > /dev/null 2>&1; then
print_success "Frontend service is healthy"
else
print_warning "Frontend health check failed, checking logs..."
docker-compose -f docker/docker-compose.yaml logs frontend
fi
if curl -f http://localhost:5984/_up > /dev/null 2>&1; then
print_success "CouchDB service is healthy"
else
print_warning "CouchDB health check failed"
fi
}
# Function to deploy via Kubernetes
deploy_k8s() {
print_status "Deploying to Kubernetes..."
if ! command -v kubectl &> /dev/null; then
print_error "kubectl is not installed"
exit 1
fi
# Update image in k8s manifests
if [ "$GITEA_ACTIONS" = "true" ]; then
print_status "Updating Kubernetes manifests with new image..."
sed -i "s|image:.*rxminder.*|image: $REGISTRY/$IMAGE_NAME:$IMAGE_TAG|g" k8s/frontend-deployment.yaml
fi
# Apply manifests
print_status "Applying Kubernetes manifests..."
kubectl apply -f k8s/
# Wait for rollout
print_status "Waiting for deployment rollout..."
kubectl rollout status deployment/frontend-deployment
print_success "Kubernetes deployment completed"
}
# Main deployment logic
case "$ENVIRONMENT" in
"production"|"prod")
print_status "Deploying to production environment"
deploy_compose
;;
"kubernetes"|"k8s")
print_status "Deploying to Kubernetes environment"
deploy_k8s
;;
"staging")
print_status "Deploying to staging environment"
# Use staging-specific configurations
export APP_BASE_URL="http://staging.localhost:8080"
deploy_compose
;;
*)
print_error "Unknown environment: $ENVIRONMENT"
echo "Available environments: production, kubernetes, staging"
exit 1
;;
esac
print_success "Deployment to $ENVIRONMENT completed successfully! 🎉"
# Post-deployment tasks
print_status "Running post-deployment tasks..."
# Cleanup old images (optional)
if [ "$CLEANUP_OLD_IMAGES" = "true" ]; then
print_status "Cleaning up old Docker images..."
docker image prune -f --filter "until=72h" || print_warning "Image cleanup failed"
fi
# Send notification (if configured)
if [ -n "$DEPLOYMENT_WEBHOOK_URL" ]; then
print_status "Sending deployment notification..."
curl -X POST "$DEPLOYMENT_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "{\"text\":\"✅ RxMinder deployed to $ENVIRONMENT\", \"environment\":\"$ENVIRONMENT\", \"image\":\"$REGISTRY/$IMAGE_NAME:$IMAGE_TAG\"}" \
|| print_warning "Failed to send notification"
fi
print_success "All tasks completed! 🚀"
print_error "Docker is not installed"
exit 1
fi
# Check Docker Buildx
if ! docker buildx version >/dev/null 2>&1; then
print_error "Docker Buildx is not available"
exit 1
fi
# Check if in Gitea Actions environment
if [ "$GITEA_ACTIONS" = "true" ]; then
print_status "Running in Gitea Actions environment"
GITEA_REGISTRY=${GITEA_SERVER_URL#https://}
GITEA_REPOSITORY=${GITEA_REPOSITORY}
fi
print_success "All requirements met"
}
setup_buildx() {
print_status "Setting up Docker Buildx for Gitea..."
# Create builder if it doesn't exist
if ! docker buildx ls | grep -q "gitea-builder"; then
print_status "Creating Gitea buildx builder..."
docker buildx create \
--name gitea-builder \
--driver docker-container \
--bootstrap \
--use
print_success "Gitea builder created"
else
docker buildx use gitea-builder
print_success "Using existing Gitea builder"
fi
}
login_registry() {
print_status "Logging into Gitea registry..."
if [ -z "$GITEA_TOKEN" ]; then
print_error "GITEA_TOKEN environment variable is required"
print_status "Set it with: export GITEA_TOKEN=your_token"
exit 1
fi
# Login to Gitea registry
echo "$GITEA_TOKEN" | docker login "$GITEA_REGISTRY" -u "$GITEA_ACTOR" --password-stdin
print_success "Logged into Gitea registry"
}
build_local() {
print_status "Building for local development..."
cd "$PROJECT_DIR"
# Load environment variables
if [ -f ".env" ]; then
export $(cat .env | grep -v '^#' | xargs)
fi
# Build with Gitea bake file
docker buildx bake \
-f .gitea/gitea-bake.hcl \
--set="*.platform=linux/amd64" \
--load \
dev
print_success "Local build completed"
}
build_multiplatform() {
local tag=${1:-$DEFAULT_TAG}
print_status "Building multi-platform image with tag: $tag..."
cd "$PROJECT_DIR"
# Load environment variables
if [ -f ".env" ]; then
export $(cat .env | grep -v '^#' | xargs)
fi
# Export variables for bake
export TAG="$tag"
export GITEA_SHA=${GITEA_SHA:-$(git rev-parse --short HEAD 2>/dev/null || echo "dev")}
# Build with Gitea bake file
docker buildx bake \
-f .gitea/gitea-bake.hcl \
app-ci
print_success "Multi-platform build completed"
}
build_staging() {
print_status "Building staging image..."
cd "$PROJECT_DIR"
# Load environment variables
if [ -f ".env.staging" ]; then
export $(cat .env.staging | grep -v '^#' | xargs)
elif [ -f ".env" ]; then
export $(cat .env | grep -v '^#' | xargs)
fi
# Export variables for bake
export TAG="staging-$(date +%Y%m%d-%H%M%S)"
export GITEA_SHA=${GITEA_SHA:-$(git rev-parse --short HEAD 2>/dev/null || echo "staging")}
# Build staging target
docker buildx bake \
-f .gitea/gitea-bake.hcl \
staging
print_success "Staging build completed"
}
build_production() {
local tag=${1:-$DEFAULT_TAG}
print_status "Building production image with tag: $tag..."
cd "$PROJECT_DIR"
# Load production environment variables
if [ -f ".env.production" ]; then
export $(cat .env.production | grep -v '^#' | xargs)
elif [ -f ".env" ]; then
export $(cat .env | grep -v '^#' | xargs)
fi
# Export variables for bake
export TAG="$tag"
export GITEA_SHA=${GITEA_SHA:-$(git rev-parse --short HEAD 2>/dev/null || echo "prod")}
# Build production target with full attestations
docker buildx bake \
-f .gitea/gitea-bake.hcl \
prod
print_success "Production build completed"
}
test_local() {
print_status "Running tests locally..."
cd "$PROJECT_DIR"
# Install dependencies if needed
if [ ! -d "node_modules" ]; then
print_status "Installing dependencies..."
bun install --frozen-lockfile
fi
# Run linting
print_status "Running linter..."
bun run lint
# Run type checking
print_status "Running type checker..."
bun run type-check
# Run tests
print_status "Running tests..."
bun run test
print_success "All tests passed"
}
deploy() {
local environment=${1:-production}
local tag=${2:-latest}
print_status "Deploying to $environment with tag $tag..."
# Use the gitea-deploy script
"$SCRIPT_DIR/gitea-deploy.sh" "$environment" "$tag"
}
cleanup() {
print_status "Cleaning up Gitea builder and images..."
# Remove builder
if docker buildx ls | grep -q "gitea-builder"; then
docker buildx rm gitea-builder
print_success "Gitea builder removed"
fi
# Clean up old images (keep last 3 tags)
print_status "Cleaning up old images..."
docker image prune -f --filter "until=72h" || print_warning "Image cleanup failed"
print_success "Cleanup completed"
}
show_status() {
print_status "Gitea CI/CD Status"
echo
# Check environment
if [ "$GITEA_ACTIONS" = "true" ]; then
echo "🏃 Running in Gitea Actions"
echo "📦 Registry: $GITEA_REGISTRY"
echo "📋 Repository: $GITEA_REPOSITORY"
echo "🏷️ SHA: ${GITEA_SHA:-unknown}"
echo "🌿 Branch: ${GITEA_REF_NAME:-unknown}"
else
echo "💻 Running locally"
echo "📦 Registry: $GITEA_REGISTRY"
echo "📋 Repository: $GITEA_REPOSITORY"
fi
echo
# Check Docker and buildx
echo "🐳 Docker version: $(docker --version)"
echo "🔧 Buildx version: $(docker buildx version)"
# Check builders
echo
echo "🏗️ Available builders:"
docker buildx ls
}
show_help() {
cat << EOF
Gitea CI/CD Helper for RxMinder
Usage: $0 [command] [options]
Commands:
setup - Setup buildx builder for Gitea
login - Login to Gitea registry
build-local - Build for local development
build-multi [tag] - Build multi-platform image
build-staging - Build staging image
build-prod [tag] - Build production image
test - Run tests locally
deploy [env] [tag] - Deploy to environment
cleanup - Cleanup builders and images
status - Show CI/CD status
help - Show this help
Examples:
$0 setup
$0 build-local
$0 build-multi v1.2.3
$0 build-staging
$0 build-prod v1.2.3
$0 test
$0 deploy production v1.2.3
$0 deploy staging
$0 status
Environment Variables:
GITEA_REGISTRY - Gitea registry URL (default: gitea.example.com)
GITEA_REPOSITORY - Repository name (default: user/rxminder)
GITEA_TOKEN - Gitea access token (required for registry)
GITEA_ACTOR - Gitea username (for registry login)
EOF
}
# Main command handling
case "${1:-help}" in
"setup")
check_requirements
setup_buildx
;;
"login")
check_requirements
login_registry
;;
"build-local")
check_requirements
setup_buildx
build_local
;;
"build-multi")
check_requirements
setup_buildx
login_registry
build_multiplatform "$2"
;;
"build-staging")
check_requirements
setup_buildx
login_registry
build_staging
;;
"build-prod")
check_requirements
setup_buildx
login_registry
build_production "$2"
;;
"test")
test_local
;;
"deploy")
deploy "$2" "$3"
;;
"cleanup")
cleanup
;;
"status")
show_status
;;
"help"|*)
show_help
;;
esac
+330
View File
@@ -0,0 +1,330 @@
#!/usr/bin/env bash
# 🚀 RxMinder Template-based Kubernetes Deployment Script
# This script processes template files and applies them to Kubernetes
set -euo pipefail
# Script configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
K8S_DIR="$(dirname "$SCRIPT_DIR")/k8s"
ENV_FILE="$(dirname "$SCRIPT_DIR")/.env"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
print_status() {
echo -e "${BLUE}$1${NC}"
}
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}"
}
# Function to load environment variables
load_env() {
if [[ -f "$ENV_FILE" ]]; then
print_status "Loading environment variables from .env..."
# Export variables from .env file
set -a
source "$ENV_FILE"
set +a
print_success "Environment variables loaded"
else
print_warning ".env file not found at $ENV_FILE"
print_warning "Using default values. Copy .env.example to .env and customize."
fi
}
# Function to substitute environment variables in templates
substitute_template() {
local template_file="$1"
local output_file="$2"
print_status "Processing template: $template_file"
# Use envsubst to substitute environment variables
if command -v envsubst >/dev/null 2>&1; then
envsubst < "$template_file" > "$output_file"
else
print_error "envsubst not found. Please install gettext package."
exit 1
fi
print_success "Generated: $output_file"
}
# Function to apply Kubernetes resources
apply_k8s_resource() {
local resource_file="$1"
if [[ -f "$resource_file" ]]; then
print_status "Applying Kubernetes resource: $resource_file"
if kubectl apply -f "$resource_file"; then
print_success "Applied: $resource_file"
else
print_error "Failed to apply: $resource_file"
return 1
fi
else
print_warning "Resource file not found: $resource_file"
fi
}
# Function to validate required environment variables
validate_env() {
local required_vars=(
"APP_NAME"
"DOCKER_IMAGE"
"COUCHDB_USER"
"COUCHDB_PASSWORD"
"INGRESS_HOST"
"STORAGE_CLASS"
"STORAGE_SIZE"
)
local missing_vars=()
for var in "${required_vars[@]}"; do
if [[ -z "${!var:-}" ]]; then
missing_vars+=("$var")
fi
done
if [[ ${#missing_vars[@]} -gt 0 ]]; then
print_error "Missing required environment variables:"
for var in "${missing_vars[@]}"; do
echo -e " ${RED}- $var${NC}"
done
print_warning "Please update your .env file with these variables."
exit 1
fi
print_success "All required environment variables are set"
}
# Function to process all templates
process_templates() {
local temp_dir="/tmp/rxminder-k8s-$$"
mkdir -p "$temp_dir"
print_status "Processing Kubernetes templates..."
# Find all template files
local template_files=(
"$K8S_DIR/couchdb-secret.yaml.template"
"$K8S_DIR/ingress.yaml.template"
)
# Add any additional template files
for template_file in "$K8S_DIR"/*.template; do
if [[ -f "$template_file" ]]; then
template_files+=("$template_file")
fi
done
# Process each template
for template_file in "${template_files[@]}"; do
if [[ -f "$template_file" ]]; then
local base_name
base_name="$(basename "$template_file" .template)"
local output_file="$temp_dir/$base_name"
substitute_template "$template_file" "$output_file"
fi
done
echo "$temp_dir"
}
# Function to deploy resources in correct order
deploy_resources() {
local resource_dir="$1"
print_status "Deploying Kubernetes resources..."
# Deploy in specific order for dependencies
local deployment_order=(
"couchdb-secret.yaml"
"couchdb-pvc.yaml"
"couchdb-service.yaml"
"couchdb-statefulset.yaml"
"configmap.yaml"
"frontend-deployment.yaml"
"frontend-service.yaml"
"ingress.yaml"
"$K8S_DIR/network-policy.yaml"
"$K8S_DIR/hpa.yaml"
)
for resource in "${deployment_order[@]}"; do
if [[ "$resource" == *.yaml ]]; then
# Check if it's a template-generated file
if [[ -f "$resource_dir/$(basename "$resource")" ]]; then
apply_k8s_resource "$resource_dir/$(basename "$resource")"
else
# Apply directly from k8s directory
apply_k8s_resource "$resource"
fi
fi
done
}
# Function to run database seeding job
run_db_seed() {
print_status "Running database seed job..."
# Apply the db-seed-job (which uses environment variables from secret)
if kubectl apply -f "$K8S_DIR/db-seed-job.yaml"; then
print_success "Database seed job submitted"
# Wait for job completion
print_status "Waiting for database seed job to complete..."
if kubectl wait --for=condition=complete --timeout=300s job/db-seed-job; then
print_success "Database seeding completed successfully"
else
print_warning "Database seed job may have failed. Check logs:"
echo "kubectl logs job/db-seed-job"
fi
else
print_error "Failed to apply database seed job"
return 1
fi
}
# Function to display deployment status
show_status() {
print_status "Deployment Status:"
echo
print_status "Pods:"
kubectl get pods -l app="${APP_NAME:-rxminder}"
echo
print_status "Services:"
kubectl get services -l app="${APP_NAME:-rxminder}"
echo
print_status "Ingress:"
kubectl get ingress
echo
if [[ -n "${INGRESS_HOST:-}" ]]; then
print_success "Application should be available at: http://${INGRESS_HOST}"
fi
}
# Function to cleanup temporary files
cleanup() {
if [[ -n "${temp_dir:-}" && -d "$temp_dir" ]]; then
rm -rf "$temp_dir"
fi
}
# Main deployment function
main() {
local command="${1:-deploy}"
case "$command" in
"deploy"|"apply")
print_status "🚀 Starting RxMinder Kubernetes deployment..."
echo
# Set default values for required variables
export APP_NAME="${APP_NAME:-rxminder}"
export DOCKER_IMAGE="${DOCKER_IMAGE:-gitea-http.taildb3494.ts.net/will/meds:latest}"
export COUCHDB_USER="${COUCHDB_USER:-admin}"
export COUCHDB_PASSWORD="${COUCHDB_PASSWORD:-change-this-secure-password}"
export INGRESS_HOST="${INGRESS_HOST:-rxminder.local}"
export STORAGE_CLASS="${STORAGE_CLASS:-longhorn}"
export STORAGE_SIZE="${STORAGE_SIZE:-5Gi}"
load_env
validate_env
# Process templates
temp_dir=$(process_templates)
trap cleanup EXIT
# Deploy resources
deploy_resources "$temp_dir"
# Run database seeding
run_db_seed
# Show status
echo
show_status
print_success "🎉 RxMinder deployment completed!"
;;
"status")
load_env
show_status
;;
"delete"|"cleanup")
print_status "🗑️ Cleaning up RxMinder deployment..."
kubectl delete all,pvc,secret,configmap,ingress -l app="${APP_NAME:-rxminder}" || true
kubectl delete job db-seed-job || true
print_success "Cleanup completed"
;;
"help"|"-h"|"--help")
echo "RxMinder Kubernetes Deployment Script"
echo
echo "Usage: $0 [command]"
echo
echo "Commands:"
echo " deploy Deploy RxMinder to Kubernetes (default)"
echo " status Show deployment status"
echo " delete Delete all RxMinder resources"
echo " help Show this help message"
echo
echo "Environment variables (set in .env):"
echo " APP_NAME Application name (default: rxminder)"
echo " DOCKER_IMAGE Container image to deploy"
echo " COUCHDB_USER Database username (default: admin)"
echo " COUCHDB_PASSWORD Database password (required)"
echo " INGRESS_HOST Ingress hostname (required)"
echo " STORAGE_CLASS Storage class for PVCs (default: longhorn)"
echo " STORAGE_SIZE Storage size for database (default: 5Gi)"
;;
*)
print_error "Unknown command: $command"
echo "Use '$0 help' for usage information"
exit 1
;;
esac
}
# Check if kubectl is available
if ! command -v kubectl >/dev/null 2>&1; then
print_error "kubectl not found. Please install kubectl and configure it to connect to your cluster."
exit 1
fi
# Check if envsubst is available
if ! command -v envsubst >/dev/null 2>&1; then
print_error "envsubst not found. Please install the gettext package:"
echo " Ubuntu/Debian: sudo apt-get install gettext"
echo " macOS: brew install gettext"
echo " RHEL/CentOS: sudo yum install gettext"
exit 1
fi
# Run main function
main "$@"
+98
View File
@@ -0,0 +1,98 @@
#!/usr/bin/env node
// Production database seeder script
// This script seeds the production CouchDB with default admin user
import { readFileSync } from 'fs';
import { resolve, dirname } from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const projectDir = resolve(__dirname, '..');
console.log('🌱 Starting production database seeding...');
// Load environment variables from .env file if it exists
try {
const envFile = resolve(projectDir, '.env');
const envContent = readFileSync(envFile, 'utf8');
console.log('📄 Loading environment variables from .env file...');
envContent.split('\n').forEach(line => {
const trimmed = line.trim();
if (trimmed && !trimmed.startsWith('#') && trimmed.includes('=')) {
const [key, ...valueParts] = trimmed.split('=');
const value = valueParts.join('=');
if (!process.env[key]) {
process.env[key] = value;
}
}
});
} catch (error) {
console.log(
'️ No .env file found, using environment variables or defaults'
);
}
// Use environment variables with fallbacks
const COUCHDB_URL =
process.env.VITE_COUCHDB_URL ||
process.env.COUCHDB_URL ||
'http://localhost:5984';
const COUCHDB_USER =
process.env.VITE_COUCHDB_USER || process.env.COUCHDB_USER || 'admin';
const COUCHDB_PASSWORD =
process.env.VITE_COUCHDB_PASSWORD ||
process.env.COUCHDB_PASSWORD ||
'change-this-secure-password';
// Set environment variables for the seeder to use
process.env.VITE_COUCHDB_URL = COUCHDB_URL;
process.env.VITE_COUCHDB_USER = COUCHDB_USER;
process.env.VITE_COUCHDB_PASSWORD = COUCHDB_PASSWORD;
console.log('🔗 CouchDB Configuration:');
console.log(` URL: ${COUCHDB_URL}`);
console.log(` User: ${COUCHDB_USER}`);
console.log(` Password: ${'*'.repeat(COUCHDB_PASSWORD.length)}`);
// Validate required environment variables
if (!COUCHDB_URL || !COUCHDB_USER || !COUCHDB_PASSWORD) {
console.error('❌ Missing required environment variables:');
console.error(' VITE_COUCHDB_URL or COUCHDB_URL');
console.error(' VITE_COUCHDB_USER or COUCHDB_USER');
console.error(' VITE_COUCHDB_PASSWORD or COUCHDB_PASSWORD');
console.error('');
console.error('💡 Set these in your .env file or as environment variables');
process.exit(1);
}
async function seedDatabase() {
try {
// Import the seeder (this will use the production CouchDB due to env vars)
const { DatabaseSeeder } = await import('../services/database.seeder.ts');
// Wait a bit for databases to be initialized
console.log('⏳ Waiting for databases to initialize...');
await new Promise(resolve => setTimeout(resolve, 2000));
const seeder = new DatabaseSeeder();
console.log('📊 Seeding admin user...');
await seeder.seedDefaultAdmin();
console.log('🎉 Production database seeding completed successfully!');
console.log('🔐 You can now login with:');
console.log(' Email: admin@localhost');
console.log(' Password: change-this-secure-password');
process.exit(0);
} catch (error) {
console.error('❌ Seeding failed:', error);
process.exit(1);
}
}
seedDatabase();
+55
View File
@@ -0,0 +1,55 @@
#!/bin/bash
# 🎭 Playwright E2E Test Setup Script
echo "🎭 Setting up Playwright for E2E testing..."
# Check if we're in the right directory
if [[ ! -f "package.json" ]]; then
echo "❌ Error: Must be run from project root directory"
exit 1
fi
# Install Playwright if not already installed
echo "📦 Installing Playwright..."
if command -v bun &> /dev/null; then
bun add -D @playwright/test
else
npm install -D @playwright/test
fi
# Install browser binaries
echo "🌐 Installing browser binaries..."
npx playwright install
# Install system dependencies (Linux)
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
echo "🐧 Installing system dependencies for Linux..."
npx playwright install-deps
fi
# Create .gitignore entries for Playwright
echo "📝 Updating .gitignore for Playwright artifacts..."
if ! grep -q "test-results" .gitignore; then
echo "" >> .gitignore
echo "# Playwright artifacts" >> .gitignore
echo "test-results/" >> .gitignore
echo "playwright-report/" >> .gitignore
echo "playwright/.cache/" >> .gitignore
fi
# Verify installation
echo "✅ Verifying Playwright installation..."
npx playwright --version
echo ""
echo "🎉 Playwright setup complete!"
echo ""
echo "📋 Quick start commands:"
echo " bun run test:e2e # Run all E2E tests"
echo " bun run test:e2e:ui # Run tests in UI mode"
echo " bun run test:e2e:debug # Debug tests"
echo " bun run test:e2e:report # View test report"
echo ""
echo "📚 Documentation: tests/e2e/README.md"
echo "⚙️ Configuration: playwright.config.ts"
+133
View File
@@ -0,0 +1,133 @@
# Run lint-staged for file-specific checks
bunx lint-staged
# Run type checking (doesn't need file filtering)
bun run type-check
# Check for large files (similar to pre-commit check-added-large-files)
git diff --cached --name-only | while IFS= read -r file; do
if [ -f "$file" ]; then
size=$(stat -c%s "$file" 2>/dev/null || stat -f%z "$file" 2>/dev/null || echo 0)
if [ "$size" -gt 500000 ]; then # 500KB limit
echo "Error: Large file detected: $file ($(echo $size | awk '{print int($1/1024)}')KB)"
echo "Consider using Git LFS for large files."
exit 1
fi
fi
done
# Check for merge conflict markers
if git diff --cached | grep -E "^[<>=]{7}" >/dev/null; then
echo "Error: Merge conflict markers detected"
exit 1
fi
# Check for private keys (basic check)
if git diff --cached --name-only | xargs grep -l "BEGIN.*PRIVATE KEY" 2>/dev/null; then
echo "Error: Private key detected in staged files"
exit 1
fi
echo "✅ Pre-commit checks passed!"
NC='\033[0m' # No Color
echo -e "${GREEN}Setting up NodeJS-native pre-commit hooks and code formatters...${NC}"
# Check if we're in a git repository
if [ ! -d ".git" ]; then
echo -e "${RED}Error: Not a git repository. Please run this script from the project root.${NC}"
exit 1
fi
# Install dependencies
echo -e "${YELLOW}Installing dependencies...${NC}"
if command -v bun &> /dev/null; then
bun install
elif command -v npm &> /dev/null; then
npm install
else
echo -e "${RED}Error: Neither bun nor npm found. Please install one of them first.${NC}"
exit 1
fi
# Initialize Husky (NodeJS-native git hooks)
echo -e "${YELLOW}Setting up Husky git hooks...${NC}"
if command -v bun &> /dev/null; then
bunx husky init
elif command -v npm &> /dev/null; then
npx husky init
fi
# Make pre-commit hook executable
chmod +x .husky/pre-commit
# Run initial formatting
echo -e "${YELLOW}Running initial code formatting...${NC}"
if command -v bun &> /dev/null; then
bun run format
elif command -v npm &> /dev/null; then
npm run format
fi
# Run initial linting
echo -e "${YELLOW}Running initial linting...${NC}"
if command -v bun &> /dev/null; then
bun run lint:fix
elif command -v npm &> /dev/null; then
npm run lint:fix
fi
# Run initial markdown linting
echo -e "${YELLOW}Running initial markdown linting...${NC}"
if command -v bun &> /dev/null; then
bun run lint:markdown:fix || echo "Markdown linting completed with warnings"
elif command -v npm &> /dev/null; then
npm run lint:markdown:fix || echo "Markdown linting completed with warnings"
fi
# Fix EditorConfig issues
echo -e "${YELLOW}Fixing EditorConfig issues...${NC}"
if command -v bun &> /dev/null; then
bun run fix:editorconfig || echo "EditorConfig fixes completed"
elif command -v npm &> /dev/null; then
npm run fix:editorconfig || echo "EditorConfig fixes completed"
fi
echo -e "${GREEN}✅ NodeJS-native pre-commit hooks and code formatters have been set up successfully!${NC}"
echo ""
echo -e "${YELLOW}What was configured:${NC}"
echo " ✓ Husky git hooks (.husky/pre-commit)"
echo " ✓ Prettier code formatter (.prettierrc)"
echo " ✓ ESLint configuration (eslint.config.cjs)"
echo " ✓ EditorConfig (.editorconfig)"
echo " ✓ Markdownlint configuration (.markdownlint.json)"
echo " ✓ Secretlint configuration (.secretlintrc.json)"
echo " ✓ Lint-staged for efficient pre-commit formatting"
echo " ✓ Dockerfilelint for Docker file validation"
echo ""
echo -e "${YELLOW}Available commands:${NC}"
echo " • bun run format - Format all files with Prettier"
echo " • bun run lint - Run ESLint on TypeScript/JavaScript files"
echo " • bun run lint:fix - Run ESLint with auto-fix"
echo " • bun run lint:markdown - Check Markdown files"
echo " • bun run lint:markdown:fix - Fix Markdown files"
echo " • bun run lint:docker - Check Dockerfile"
echo " • bun run check:secrets - Check for secrets in files"
echo " • bun run check:editorconfig - Check EditorConfig compliance"
echo " • bun run fix:editorconfig - Fix EditorConfig issues"
echo " • bun run type-check - Run TypeScript type checking"
echo ""
echo -e "${YELLOW}What happens on commit:${NC}"
echo " 1. Lint-staged runs on changed files:"
echo " • ESLint auto-fix + Prettier formatting for JS/TS files"
echo " • Prettier formatting for JSON/YAML/MD/CSS files"
echo " • Markdownlint auto-fix for Markdown files"
echo " • Dockerfilelint for Dockerfile"
echo " • EditorConfig fixes for all files"
echo " 2. TypeScript type checking on entire project"
echo " 3. Large file detection (>500KB)"
echo " 4. Merge conflict marker detection"
echo " 5. Basic private key detection"
echo ""
echo -e "${GREEN}All commits will now be automatically checked and formatted! 🎉${NC}"
echo -e "${GREEN}This setup is 100% NodeJS-native - no Python dependencies required! 🚀${NC}"
+225
View File
@@ -0,0 +1,225 @@
#!/bin/bash
# 🧪 Deployment Validation Script
# Validates complete deployment with all environment variables and health checks
set -e
echo "🚀 Starting deploymif docker compose -f docker/docker-compose.yaml -p rxminder-validation ps | grep -q "Up"; then
print_success "Docker Compose setup completed successfully!"
else
print_error "Docker Compose services failed to start"
docker compose -f docker/docker-compose.yaml -p rxminder-validation logsalidation..."
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to print colored output
print_status() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Cleanup function
cleanup() {
print_status "Cleaning up test containers..."
docker stop rxminder-validation-test 2>/dev/null || true
docker rm rxminder-validation-test 2>/dev/null || true
docker compose -f docker/docker-compose.yaml -p rxminder-validation down 2>/dev/null || true
}
# Set trap for cleanup
trap cleanup EXIT
print_status "1. Validating environment files..."
# Check if required environment files exist
if [[ ! -f .env ]]; then
print_error ".env file not found. Run 'cp .env.example .env' and configure it."
exit 1
fi
if [[ ! -f .env.example ]]; then
print_error ".env.example file not found."
exit 1
fi
print_success "Environment files exist"
# Validate environment consistency
print_status "2. Checking environment variable consistency..."
./validate-env.sh
print_status "3. Setting up Docker Buildx..."
# Ensure buildx is available
if ! docker buildx version >/dev/null 2>&1; then
print_error "Docker Buildx is not available. Please update Docker to a version that supports Buildx."
exit 1
fi
# Create a new builder instance if it doesn't exist
if ! docker buildx ls | grep -q "rxminder-builder"; then
print_status "Creating new buildx builder instance..."
docker buildx create --name rxminder-builder --driver docker-container --bootstrap
fi
# Use the builder
docker buildx use rxminder-builder
print_status "4. Building multi-platform Docker image with buildx..."
# Build the image with buildx for multiple platforms
docker buildx build --no-cache \
--platform linux/amd64,linux/arm64 \
--build-arg COUCHDB_USER="${COUCHDB_USER:-admin}" \
--build-arg COUCHDB_PASSWORD="${COUCHDB_PASSWORD:-change-this-secure-password}" \
--build-arg VITE_COUCHDB_URL="${VITE_COUCHDB_URL:-http://localhost:5984}" \
--build-arg VITE_COUCHDB_USER="${VITE_COUCHDB_USER:-admin}" \
--build-arg VITE_COUCHDB_PASSWORD="${VITE_COUCHDB_PASSWORD:-change-this-secure-password}" \
--build-arg APP_BASE_URL="${APP_BASE_URL:-http://localhost:8080}" \
--build-arg VITE_GOOGLE_CLIENT_ID="${VITE_GOOGLE_CLIENT_ID:-}" \
--build-arg VITE_GITHUB_CLIENT_ID="${VITE_GITHUB_CLIENT_ID:-}" \
--build-arg MAILGUN_API_KEY="${MAILGUN_API_KEY:-}" \
--build-arg MAILGUN_DOMAIN="${MAILGUN_DOMAIN:-}" \
--build-arg MAILGUN_FROM_EMAIL="${MAILGUN_FROM_EMAIL:-}" \
--build-arg NODE_ENV="${NODE_ENV:-production}" \
-t rxminder-validation \
--load \
.
print_success "Docker image built successfully"
print_status "5. Testing container startup and health..."
# Run container in background
docker run --rm -d \
-p 8083:80 \
--name rxminder-validation-test \
rxminder-validation
# Wait for container to start
sleep 5
# Check if container is running
if ! docker ps | grep -q rxminder-validation-test; then
print_error "Container failed to start"
docker logs rxminder-validation-test
exit 1
fi
print_success "Container started successfully"
# Test health endpoint
print_status "5. Testing health endpoint..."
for i in {1..10}; do
if curl -s -f http://localhost:8083/health > /dev/null; then
print_success "Health endpoint responding"
break
elif [[ $i -eq 10 ]]; then
print_error "Health endpoint not responding after 10 attempts"
exit 1
else
print_warning "Health endpoint not ready, retrying... ($i/10)"
sleep 2
fi
done
# Test main application
print_status "6. Testing main application..."
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8083)
if [[ $HTTP_CODE -eq 200 ]]; then
print_success "Main application responding (HTTP $HTTP_CODE)"
else
print_error "Main application not responding properly (HTTP $HTTP_CODE)"
exit 1
fi
# Test docker-compose build
print_status "7. Testing Docker Compose build..."
docker compose -f docker/docker-compose.yaml build frontend --no-cache
print_success "Docker Compose build successful"
# Test docker-compose with validation project name
print_status "8. Testing Docker Compose deployment..."
docker compose -f docker/docker-compose.yaml -p rxminder-validation up -d --build
# Wait for services to start
sleep 10
# Check service health
if docker compose -f docker/docker-compose.yaml -p meds-validation ps | grep -q "Up"; then
print_success "Docker Compose services started successfully"
else
print_error "Docker Compose services failed to start"
docker compose -f docker/docker-compose.yaml -p meds-validation logs
exit 1
fi
# Test health of compose deployment
if curl -s -f http://localhost:8080/health > /dev/null; then
print_success "Docker Compose health endpoint responding"
else
print_warning "Docker Compose health endpoint not responding (may need CouchDB)"
fi
print_status "9. Checking image size..."
IMAGE_SIZE=$(docker image inspect rxminder-validation --format='{{.Size}}' | numfmt --to=iec)
print_success "Image size: $IMAGE_SIZE"
print_status "10. Validating security configuration..."
# Check if image runs as non-root
USER_INFO=$(docker run --rm rxminder-validation whoami)
if [[ "$USER_INFO" != "root" ]]; then
print_success "Container runs as non-root user: $USER_INFO"
else
print_warning "Container runs as root user (security consideration)"
fi
# Check nginx configuration
if docker run --rm rxminder-validation nginx -t 2>/dev/null; then
print_success "Nginx configuration is valid"
else
print_error "Nginx configuration has issues"
exit 1
fi
print_status "11. Final validation complete!"
echo
echo "🎉 Deployment validation successful!"
echo
echo "Summary:"
echo "✅ Environment files validated"
echo "✅ Docker image builds successfully"
echo "✅ Container starts and runs healthy"
echo "✅ Health endpoints respond correctly"
echo "✅ Docker Compose deployment works"
echo "✅ Security configuration validated"
echo "✅ Image size optimized ($IMAGE_SIZE)"
echo
echo "Your deployment is ready for production! 🚀"
echo
echo "Next steps:"
echo "1. Configure production environment variables in .env"
echo "2. Run './deploy.sh production' for production deployment"
echo "3. Set up monitoring and backups"
echo "4. Configure SSL/TLS certificates"
echo
+221
View File
@@ -0,0 +1,221 @@
#!/bin/bash
# 🧪 Deployment Validation Script
# Validates complete deployment with all environment variables and health checks
set -e
echo "🚀 Starting deployment validation..."
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to print colored output
print_status() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Cleanup function
cleanup() {
print_status "Cleaning up test containers..."
docker stop meds-validation-test 2>/dev/null || true
docker rm meds-validation-test 2>/dev/null || true
docker compose -f docker/docker-compose.yaml -p meds-validation down 2>/dev/null || true
}
# Set trap for cleanup
trap cleanup EXIT
print_status "1. Validating environment files..."
# Check if required environment files exist
if [[ ! -f .env ]]; then
print_error ".env file not found. Run 'cp .env.example .env' and configure it."
exit 1
fi
if [[ ! -f .env.example ]]; then
print_error ".env.example file not found."
exit 1
fi
print_success "Environment files exist"
# Validate environment consistency
print_status "2. Checking environment variable consistency..."
./validate-env.sh
print_status "3. Setting up Docker Buildx..."
# Ensure buildx is available
if ! docker buildx version >/dev/null 2>&1; then
print_error "Docker Buildx is not available. Please update Docker to a version that supports Buildx."
exit 1
fi
# Create a new builder instance if it doesn't exist
if ! docker buildx ls | grep -q "meds-builder"; then
print_status "Creating new buildx builder instance..."
docker buildx create --name meds-builder --driver docker-container --bootstrap
fi
# Use the builder
docker buildx use meds-builder
print_status "4. Building multi-platform Docker image with buildx..."
# Build the image with buildx for multiple platforms
docker buildx build --no-cache \
--platform linux/amd64,linux/arm64 \
--build-arg COUCHDB_USER="${COUCHDB_USER:-admin}" \
--build-arg COUCHDB_PASSWORD="${COUCHDB_PASSWORD:-change-this-secure-password}" \
--build-arg VITE_COUCHDB_URL="${VITE_COUCHDB_URL:-http://localhost:5984}" \
--build-arg VITE_COUCHDB_USER="${VITE_COUCHDB_USER:-admin}" \
--build-arg VITE_COUCHDB_PASSWORD="${VITE_COUCHDB_PASSWORD:-change-this-secure-password}" \
--build-arg APP_BASE_URL="${APP_BASE_URL:-http://localhost:8080}" \
--build-arg VITE_GOOGLE_CLIENT_ID="${VITE_GOOGLE_CLIENT_ID:-}" \
--build-arg VITE_GITHUB_CLIENT_ID="${VITE_GITHUB_CLIENT_ID:-}" \
--build-arg MAILGUN_API_KEY="${MAILGUN_API_KEY:-}" \
--build-arg MAILGUN_DOMAIN="${MAILGUN_DOMAIN:-}" \
--build-arg MAILGUN_FROM_EMAIL="${MAILGUN_FROM_EMAIL:-}" \
--build-arg NODE_ENV="${NODE_ENV:-production}" \
-t meds-validation \
--load \
.
print_success "Docker image built successfully"
print_status "5. Testing container startup and health..."
# Run container in background
docker run --rm -d \
-p 8083:80 \
--name meds-validation-test \
meds-validation
# Wait for container to start
sleep 5
# Check if container is running
if ! docker ps | grep -q meds-validation-test; then
print_error "Container failed to start"
docker logs meds-validation-test
exit 1
fi
print_success "Container started successfully"
# Test health endpoint
print_status "5. Testing health endpoint..."
for i in {1..10}; do
if curl -s -f http://localhost:8083/health > /dev/null; then
print_success "Health endpoint responding"
break
elif [[ $i -eq 10 ]]; then
print_error "Health endpoint not responding after 10 attempts"
exit 1
else
print_warning "Health endpoint not ready, retrying... ($i/10)"
sleep 2
fi
done
# Test main application
print_status "6. Testing main application..."
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8083)
if [[ $HTTP_CODE -eq 200 ]]; then
print_success "Main application responding (HTTP $HTTP_CODE)"
else
print_error "Main application not responding properly (HTTP $HTTP_CODE)"
exit 1
fi
# Test docker-compose build
print_status "7. Testing Docker Compose build..."
docker compose -f docker/docker-compose.yaml build frontend --no-cache
print_success "Docker Compose build successful"
# Test docker-compose with validation project name
print_status "8. Testing Docker Compose deployment..."
docker compose -f docker/docker-compose.yaml -p meds-validation up -d --build
# Wait for services to start
sleep 10
# Check service health
if docker compose -f docker/docker-compose.yaml -p meds-validation ps | grep -q "Up"; then
print_success "Docker Compose services started successfully"
else
print_error "Docker Compose services failed to start"
docker compose -f docker/docker-compose.yaml -p meds-validation logs
exit 1
fi
# Test health of compose deployment
if curl -s -f http://localhost:8080/health > /dev/null; then
print_success "Docker Compose health endpoint responding"
else
print_warning "Docker Compose health endpoint not responding (may need CouchDB)"
fi
print_status "9. Checking image size..."
IMAGE_SIZE=$(docker image inspect meds-validation --format='{{.Size}}' | numfmt --to=iec)
print_success "Image size: $IMAGE_SIZE"
print_status "10. Validating security configuration..."
# Check if image runs as non-root
USER_INFO=$(docker run --rm meds-validation whoami)
if [[ "$USER_INFO" != "root" ]]; then
print_success "Container runs as non-root user: $USER_INFO"
else
print_warning "Container runs as root user (security consideration)"
fi
# Check nginx configuration
if docker run --rm meds-validation nginx -t 2>/dev/null; then
print_success "Nginx configuration is valid"
else
print_error "Nginx configuration has issues"
exit 1
fi
print_status "11. Final validation complete!"
echo
echo "🎉 Deployment validation successful!"
echo
echo "Summary:"
echo "✅ Environment files validated"
echo "✅ Docker image builds successfully"
echo "✅ Container starts and runs healthy"
echo "✅ Health endpoints respond correctly"
echo "✅ Docker Compose deployment works"
echo "✅ Security configuration validated"
echo "✅ Image size optimized ($IMAGE_SIZE)"
echo
echo "Your deployment is ready for production! 🚀"
echo
echo "Next steps:"
echo "1. Configure production environment variables in .env"
echo "2. Run './deploy.sh production' for production deployment"
echo "3. Set up monitoring and backups"
echo "4. Configure SSL/TLS certificates"
echo
+274
View File
@@ -0,0 +1,274 @@
#!/bin/bash
# Environment validation script
# Validates all .env files for consistency and completeness
set -e
print_header() {
echo "🔍 Environment Configuration Validation"
echo "======================================"
echo ""
}
print_section() {
echo ""
echo "📋 $1"
echo "$(printf '%*s' ${#1} '' | tr ' ' '-')"
}
print_success() {
echo "$1"
}
print_warning() {
echo "⚠️ $1"
}
print_error() {
echo "$1"
}
# Required variables for each environment
CORE_VARS=(
"COUCHDB_USER"
"COUCHDB_PASSWORD"
"VITE_COUCHDB_URL"
"VITE_COUCHDB_USER"
"VITE_COUCHDB_PASSWORD"
"APP_BASE_URL"
"MAILGUN_API_KEY"
"MAILGUN_DOMAIN"
"MAILGUN_FROM_EMAIL"
)
K8S_VARS=(
"INGRESS_HOST"
)
OPTIONAL_VARS=(
"NODE_ENV"
"VITE_GOOGLE_CLIENT_ID"
"VITE_GITHUB_CLIENT_ID"
)
validate_file() {
local file="$1"
local file_type="$2"
print_section "Validating $file ($file_type)"
if [[ ! -f "$file" ]]; then
print_error "File not found: $file"
return 1
fi
local missing_vars=()
local found_vars=()
# Check core variables
for var in "${CORE_VARS[@]}"; do
if grep -q "^${var}=" "$file" || grep -q "^#.*${var}=" "$file"; then
found_vars+=("$var")
else
missing_vars+=("$var")
fi
done
# Check K8s variables for relevant files
if [[ "$file_type" != "template" ]]; then
for var in "${K8S_VARS[@]}"; do
if grep -q "^${var}=" "$file" || grep -q "^#.*${var}=" "$file"; then
found_vars+=("$var")
else
missing_vars+=("$var")
fi
done
fi
# Report results
print_success "Found ${#found_vars[@]} variables"
if [[ ${#missing_vars[@]} -gt 0 ]]; then
print_warning "Missing variables:"
for var in "${missing_vars[@]}"; do
echo " - $var"
done
fi
# Check for old VITE_MAILGUN variables
if grep -q "VITE_MAILGUN" "$file"; then
print_error "Found deprecated VITE_MAILGUN variables (should be MAILGUN_*)"
fi
# Check variable format
local malformed_vars=()
while IFS= read -r line; do
if [[ "$line" =~ ^[A-Z_]+=.* ]]; then
local var_name="${line%%=*}"
if [[ ! "$var_name" =~ ^[A-Z_][A-Z0-9_]*$ ]]; then
malformed_vars+=("$var_name")
fi
fi
done < "$file"
if [[ ${#malformed_vars[@]} -gt 0 ]]; then
print_warning "Malformed variable names:"
for var in "${malformed_vars[@]}"; do
echo " - $var"
done
fi
echo ""
}
validate_consistency() {
print_section "Cross-file Consistency Check"
# Extract variable names from each file
local example_vars=()
local env_vars=()
local prod_vars=()
if [[ -f ".env.example" ]]; then
while IFS= read -r line; do
if [[ "$line" =~ ^[A-Z_]+=.* ]]; then
example_vars+=("${line%%=*}")
fi
done < ".env.example"
fi
if [[ -f ".env" ]]; then
while IFS= read -r line; do
if [[ "$line" =~ ^[A-Z_]+=.* ]]; then
env_vars+=("${line%%=*}")
fi
done < ".env"
fi
if [[ -f ".env.production" ]]; then
while IFS= read -r line; do
if [[ "$line" =~ ^[A-Z_]+=.* ]]; then
prod_vars+=("${line%%=*}")
fi
done < ".env.production"
fi
# Check if .env and .env.production have all variables from .env.example
local missing_in_env=()
local missing_in_prod=()
for var in "${example_vars[@]}"; do
if [[ ! " ${env_vars[@]} " =~ " ${var} " ]]; then
missing_in_env+=("$var")
fi
if [[ ! " ${prod_vars[@]} " =~ " ${var} " ]]; then
missing_in_prod+=("$var")
fi
done
if [[ ${#missing_in_env[@]} -eq 0 ]]; then
print_success ".env has all variables from .env.example"
else
print_warning ".env missing variables from .env.example:"
for var in "${missing_in_env[@]}"; do
echo " - $var"
done
fi
if [[ ${#missing_in_prod[@]} -eq 0 ]]; then
print_success ".env.production has all variables from .env.example"
else
print_warning ".env.production missing variables from .env.example:"
for var in "${missing_in_prod[@]}"; do
echo " - $var"
done
fi
echo ""
}
validate_k8s_template() {
print_section "Kubernetes Template Validation"
local template_file="k8s/ingress.yaml.template"
if [[ ! -f "$template_file" ]]; then
print_error "Template file not found: $template_file"
return 1
fi
# Check for template variables
local template_vars=()
while IFS= read -r line; do
if [[ "$line" =~ \$\{([A-Z_][A-Z0-9_]*)\} ]]; then
local var_name="${BASH_REMATCH[1]}"
if [[ ! " ${template_vars[@]} " =~ " ${var_name} " ]]; then
template_vars+=("$var_name")
fi
fi
done < "$template_file"
print_success "Found ${#template_vars[@]} template variables:"
for var in "${template_vars[@]}"; do
echo " - \${$var}"
done
# Check if template variables are defined in env files
for var in "${template_vars[@]}"; do
local found_in_files=()
if grep -q "^${var}=" ".env.example" 2>/dev/null; then
found_in_files+=(".env.example")
fi
if grep -q "^${var}=" ".env" 2>/dev/null; then
found_in_files+=(".env")
fi
if grep -q "^${var}=" ".env.production" 2>/dev/null; then
found_in_files+=(".env.production")
fi
if [[ ${#found_in_files[@]} -gt 0 ]]; then
print_success "$var defined in: ${found_in_files[*]}"
else
print_error "$var not defined in any environment file"
fi
done
echo ""
}
main() {
print_header
# Validate individual files
if [[ -f ".env.example" ]]; then
validate_file ".env.example" "template"
fi
if [[ -f ".env" ]]; then
validate_file ".env" "development"
fi
if [[ -f ".env.production" ]]; then
validate_file ".env.production" "production"
fi
# Cross-file validation
validate_consistency
# Kubernetes template validation
validate_k8s_template
print_section "Summary"
print_success "Environment validation complete!"
echo ""
echo "💡 Tips:"
echo " - Copy .env.example to .env for local development"
echo " - Use .env.production for production deployments"
echo " - Run './deploy-k8s.sh --dry-run' to test Kubernetes deployment"
echo " - All Mailgun variables use server-side naming (no VITE_ prefix)"
echo ""
}
main "$@"