Major Changes: - Create PROMPT_BLUEPRINT_V2.md (Claude Code-driven development guide) - Add Argo CD Application configurations (GitOps deployment) - Update CI/CD workflow to comply with GitOps requirements - Create blueprint-checker skill for compliance validation - Add migration-job template for database migrations - Add comprehensive deployment documentation New Files: - PROMPT_BLUEPRINT_V2.md - Production-grade development blueprint - argocd/ - Argo CD Application configurations (dev/prod) - .claude/skills/blueprint-checker/ - Compliance checking tool - deployment/helm/gold-template/templates/migration-job.yaml - PreSync migration job - deployment/helm/gold-template/values-infra-db.yaml - Infra database integration - docs/deployment/ - Deployment guides and best practices - OPTIMIZATION-SUMMARY.md - Optimization work summary Updated Files: - .github/workflows/complete-cicd.yml - GitOps-compliant deployment - backend/main.py - Enhanced observability (metrics, structured logs) - requirements.txt - Add prometheus-client - deployment/helm/gold-template/values-orbstack.yaml - Metrics configuration Blueprint Compliance: 83% (19/23 checks passed) Related Issues: - #26 Configure Alembic database migration - #27 Integrate OpenTelemetry tracing - #28 Create Grafana Dashboard 🤖 Generated with Claude Code Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
376 lines
11 KiB
Bash
Executable File
376 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Gold Template - OrbStack Deployment Script
|
|
# Optimized for Mac Studio + OrbStack environment
|
|
|
|
set -e
|
|
|
|
GREEN='\033[0;32m'
|
|
BLUE='\033[0;34m'
|
|
YELLOW='\033[1;33m'
|
|
RED='\033[0;31m'
|
|
NC='\033[0m'
|
|
|
|
NAMESPACE="gold-template"
|
|
HELM_RELEASE="gold-template"
|
|
IMAGE_NAME="localhost:5000/gold-template"
|
|
IMAGE_TAG="latest"
|
|
DEPLOY_MODE="local" # local or infra-db
|
|
PUSH_TO_GHCR="false"
|
|
PUSH_TO_HARBOR="false"
|
|
GHCR_REPO=""
|
|
HARBOR_REPO="harbor.omniverseai.net/ljwx/gold-template"
|
|
|
|
echo -e "${BLUE}🚀 Gold Template - OrbStack Deployment${NC}"
|
|
echo "=========================================="
|
|
echo ""
|
|
|
|
# Check OrbStack
|
|
check_orbstack() {
|
|
echo -e "${BLUE}📋 Checking OrbStack environment...${NC}"
|
|
|
|
if ! kubectl cluster-info &> /dev/null; then
|
|
echo -e "${RED}❌ Cannot connect to Kubernetes cluster${NC}"
|
|
echo -e "${YELLOW} Please ensure OrbStack is running${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
NODE_NAME=$(kubectl get nodes -o jsonpath='{.items[0].metadata.name}')
|
|
if [ "$NODE_NAME" != "orbstack" ]; then
|
|
echo -e "${YELLOW}⚠️ Warning: Expected 'orbstack' node, found '$NODE_NAME'${NC}"
|
|
fi
|
|
|
|
echo -e "${GREEN}✅ OrbStack cluster ready${NC}"
|
|
}
|
|
|
|
# Build Docker image
|
|
build_image() {
|
|
echo ""
|
|
echo -e "${BLUE}🔨 Building Docker image...${NC}"
|
|
|
|
# Create minimal backend structure if it doesn't exist
|
|
if [ ! -d "backend" ]; then
|
|
echo -e "${YELLOW}⚠️ Creating minimal backend structure...${NC}"
|
|
mkdir -p backend
|
|
cat > backend/main.py << 'EOF'
|
|
from fastapi import FastAPI
|
|
from fastapi.responses import JSONResponse
|
|
|
|
app = FastAPI(title="Gold Template", version="1.0.0")
|
|
|
|
@app.get("/")
|
|
async def root():
|
|
return {"message": "Gold Template API", "status": "running"}
|
|
|
|
@app.get("/health")
|
|
async def health():
|
|
return {"status": "healthy"}
|
|
|
|
@app.get("/health/ready")
|
|
async def ready():
|
|
return {"status": "ready"}
|
|
|
|
if __name__ == "__main__":
|
|
import uvicorn
|
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|
|
EOF
|
|
cat > backend/__init__.py << 'EOF'
|
|
# Gold Template Backend
|
|
EOF
|
|
fi
|
|
|
|
docker build -t ${IMAGE_NAME}:${IMAGE_TAG} .
|
|
|
|
if [ $? -eq 0 ]; then
|
|
echo -e "${GREEN}✅ Image built successfully: ${IMAGE_NAME}:${IMAGE_TAG}${NC}"
|
|
|
|
# Push to Harbor if requested
|
|
if [ "${PUSH_TO_HARBOR}" = "true" ]; then
|
|
echo ""
|
|
echo -e "${BLUE}📤 Pushing to Harbor (${HARBOR_REPO})...${NC}"
|
|
docker tag ${IMAGE_NAME}:${IMAGE_TAG} ${HARBOR_REPO}:${IMAGE_TAG}
|
|
docker push ${HARBOR_REPO}:${IMAGE_TAG}
|
|
if [ $? -eq 0 ]; then
|
|
echo -e "${GREEN}✅ Image pushed to Harbor: ${HARBOR_REPO}:${IMAGE_TAG}${NC}"
|
|
else
|
|
echo -e "${RED}❌ Failed to push to Harbor${NC}"
|
|
echo -e "${YELLOW} Make sure you're logged in: docker login harbor.omniverseai.net${NC}"
|
|
fi
|
|
fi
|
|
|
|
# Push to GHCR if requested
|
|
if [ "${PUSH_TO_GHCR}" = "true" ] && [ -n "${GHCR_REPO}" ]; then
|
|
echo ""
|
|
echo -e "${BLUE}📤 Pushing to GitHub Container Registry...${NC}"
|
|
docker tag ${IMAGE_NAME}:${IMAGE_TAG} ${GHCR_REPO}:${IMAGE_TAG}
|
|
docker push ${GHCR_REPO}:${IMAGE_TAG}
|
|
if [ $? -eq 0 ]; then
|
|
echo -e "${GREEN}✅ Image pushed to GHCR: ${GHCR_REPO}:${IMAGE_TAG}${NC}"
|
|
else
|
|
echo -e "${RED}❌ Failed to push to GHCR${NC}"
|
|
fi
|
|
fi
|
|
else
|
|
echo -e "${RED}❌ Image build failed${NC}"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Create namespace
|
|
create_namespace() {
|
|
echo ""
|
|
echo -e "${BLUE}📦 Creating namespace...${NC}"
|
|
|
|
kubectl create namespace ${NAMESPACE} --dry-run=client -o yaml | kubectl apply -f -
|
|
|
|
echo -e "${GREEN}✅ Namespace ready${NC}"
|
|
}
|
|
|
|
# Create secrets
|
|
create_secrets() {
|
|
echo ""
|
|
echo -e "${BLUE}🔐 Creating secrets...${NC}"
|
|
|
|
# Generate secret key if not exists
|
|
if [ -f ".env" ]; then
|
|
source .env
|
|
fi
|
|
|
|
SECRET_KEY="${SECRET_KEY:-$(openssl rand -hex 32)}"
|
|
GITHUB_TOKEN="${GITHUB_TOKEN:-}"
|
|
|
|
kubectl create secret generic gold-template-secret \
|
|
--from-literal=secret-key="${SECRET_KEY}" \
|
|
--from-literal=github-token="${GITHUB_TOKEN}" \
|
|
--namespace=${NAMESPACE} \
|
|
--dry-run=client -o yaml | kubectl apply -f -
|
|
|
|
# Create database secrets if using infra-db mode
|
|
if [ "${DEPLOY_MODE}" = "infra-db" ]; then
|
|
echo -e "${BLUE}🔐 Creating database secrets for infra integration...${NC}"
|
|
|
|
DB_USER="${DB_USER:-app_user}"
|
|
DB_PASSWORD="${DB_PASSWORD:-$(openssl rand -hex 16)}"
|
|
REDIS_PASSWORD="${REDIS_PASSWORD:-$(openssl rand -hex 16)}"
|
|
|
|
kubectl create secret generic gold-template-db-secret \
|
|
--from-literal=username="${DB_USER}" \
|
|
--from-literal=password="${DB_PASSWORD}" \
|
|
--namespace=${NAMESPACE} \
|
|
--dry-run=client -o yaml | kubectl apply -f -
|
|
|
|
kubectl create secret generic gold-template-redis-secret \
|
|
--from-literal=password="${REDIS_PASSWORD}" \
|
|
--namespace=${NAMESPACE} \
|
|
--dry-run=client -o yaml | kubectl apply -f -
|
|
|
|
echo -e "${YELLOW}⚠️ Database credentials:${NC}"
|
|
echo " DB User: ${DB_USER}"
|
|
echo " DB Password: ${DB_PASSWORD}"
|
|
echo " Redis Password: ${REDIS_PASSWORD}"
|
|
echo -e "${YELLOW} Please configure these in your infra namespace databases${NC}"
|
|
fi
|
|
|
|
echo -e "${GREEN}✅ Secrets created${NC}"
|
|
}
|
|
|
|
# Deploy with Helm
|
|
deploy_helm() {
|
|
echo ""
|
|
echo -e "${BLUE}🎯 Deploying with Helm (mode: ${DEPLOY_MODE})...${NC}"
|
|
|
|
VALUES_FILE="./deployment/helm/gold-template/values-orbstack.yaml"
|
|
|
|
# Add infra-db values if in infra-db mode
|
|
if [ "${DEPLOY_MODE}" = "infra-db" ]; then
|
|
VALUES_FILE="${VALUES_FILE} -f ./deployment/helm/gold-template/values-infra-db.yaml"
|
|
fi
|
|
|
|
helm upgrade --install ${HELM_RELEASE} \
|
|
./deployment/helm/gold-template \
|
|
--namespace=${NAMESPACE} \
|
|
-f ${VALUES_FILE} \
|
|
--set image.tag=${IMAGE_TAG} \
|
|
--wait \
|
|
--timeout=5m
|
|
|
|
if [ $? -eq 0 ]; then
|
|
echo -e "${GREEN}✅ Deployment successful${NC}"
|
|
else
|
|
echo -e "${RED}❌ Deployment failed${NC}"
|
|
kubectl get pods -n ${NAMESPACE}
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Verify deployment
|
|
verify_deployment() {
|
|
echo ""
|
|
echo -e "${BLUE}🔍 Verifying deployment...${NC}"
|
|
|
|
kubectl wait --for=condition=ready pod \
|
|
-l app.kubernetes.io/name=gold-template \
|
|
-n ${NAMESPACE} \
|
|
--timeout=300s
|
|
|
|
echo ""
|
|
kubectl get pods -n ${NAMESPACE} -l app.kubernetes.io/name=gold-template
|
|
|
|
echo -e "${GREEN}✅ Deployment verified${NC}"
|
|
}
|
|
|
|
# Setup port forwarding
|
|
setup_port_forward() {
|
|
echo ""
|
|
echo -e "${BLUE}🌐 Setting up port forwarding...${NC}"
|
|
|
|
# Kill existing port-forward if any
|
|
pkill -f "kubectl port-forward.*${NAMESPACE}.*gold-template" || true
|
|
|
|
# Start port-forward in background
|
|
kubectl port-forward -n ${NAMESPACE} svc/gold-template 8000:80 > /dev/null 2>&1 &
|
|
PF_PID=$!
|
|
|
|
sleep 2
|
|
|
|
if ps -p $PF_PID > /dev/null; then
|
|
echo -e "${GREEN}✅ Port forwarding active (PID: $PF_PID)${NC}"
|
|
echo " Access at: http://localhost:8000"
|
|
else
|
|
echo -e "${YELLOW}⚠️ Port forwarding failed, but deployment is successful${NC}"
|
|
fi
|
|
}
|
|
|
|
# Show access info
|
|
show_info() {
|
|
echo ""
|
|
echo "=========================================="
|
|
echo -e "${GREEN}🎉 Deployment Complete!${NC}"
|
|
echo "=========================================="
|
|
echo ""
|
|
echo "Deployment Configuration:"
|
|
echo " Mode: ${DEPLOY_MODE}"
|
|
echo " Namespace: ${NAMESPACE}"
|
|
echo " Image: ${IMAGE_NAME}:${IMAGE_TAG}"
|
|
if [ "${PUSH_TO_HARBOR}" = "true" ]; then
|
|
echo " Harbor: ${HARBOR_REPO}:${IMAGE_TAG}"
|
|
fi
|
|
if [ "${PUSH_TO_GHCR}" = "true" ]; then
|
|
echo " GHCR: ${GHCR_REPO}:${IMAGE_TAG}"
|
|
fi
|
|
echo ""
|
|
echo "Access Information:"
|
|
echo " Application: http://localhost:8000"
|
|
echo " API Docs: http://localhost:8000/docs"
|
|
echo " Health Check: http://localhost:8000/health"
|
|
echo " Metrics: http://localhost:8000/metrics"
|
|
echo ""
|
|
echo "Useful Commands:"
|
|
echo " View logs: kubectl logs -f -n ${NAMESPACE} -l app.kubernetes.io/name=gold-template"
|
|
echo " View metrics: curl http://localhost:8000/metrics"
|
|
echo " Get pods: kubectl get pods -n ${NAMESPACE}"
|
|
echo " Describe: kubectl describe pod -n ${NAMESPACE} -l app.kubernetes.io/name=gold-template"
|
|
echo " Shell: kubectl exec -it -n ${NAMESPACE} \$(kubectl get pod -n ${NAMESPACE} -l app.kubernetes.io/name=gold-template -o jsonpath='{.items[0].metadata.name}') -- /bin/bash"
|
|
echo " Uninstall: helm uninstall ${HELM_RELEASE} -n ${NAMESPACE}"
|
|
echo ""
|
|
echo "Test the deployment:"
|
|
echo " curl http://localhost:8000"
|
|
echo " curl http://localhost:8000/health"
|
|
echo " curl http://localhost:8000/metrics"
|
|
echo ""
|
|
}
|
|
|
|
# Main execution
|
|
main() {
|
|
check_orbstack
|
|
build_image
|
|
create_namespace
|
|
create_secrets
|
|
deploy_helm
|
|
verify_deployment
|
|
setup_port_forward
|
|
show_info
|
|
}
|
|
|
|
# Parse arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--skip-build)
|
|
SKIP_BUILD=true
|
|
shift
|
|
;;
|
|
--tag)
|
|
IMAGE_TAG="$2"
|
|
shift 2
|
|
;;
|
|
--mode)
|
|
DEPLOY_MODE="$2"
|
|
shift 2
|
|
;;
|
|
--push-ghcr)
|
|
PUSH_TO_GHCR="true"
|
|
GHCR_REPO="$2"
|
|
shift 2
|
|
;;
|
|
--push-harbor)
|
|
PUSH_TO_HARBOR="true"
|
|
shift
|
|
;;
|
|
--harbor-repo)
|
|
HARBOR_REPO="$2"
|
|
shift 2
|
|
;;
|
|
--namespace)
|
|
NAMESPACE="$2"
|
|
shift 2
|
|
;;
|
|
--help)
|
|
echo "Usage: $0 [OPTIONS]"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " --skip-build Skip Docker build"
|
|
echo " --tag TAG Docker image tag [default: latest]"
|
|
echo " --mode MODE Deployment mode: local or infra-db [default: local]"
|
|
echo " --push-ghcr REPO Push image to GHCR (e.g., ghcr.io/user/repo)"
|
|
echo " --push-harbor Push image to Harbor (harbor.omniverseai.net/ljwx/)"
|
|
echo " --harbor-repo REPO Custom Harbor repository [default: harbor.omniverseai.net/ljwx/gold-template]"
|
|
echo " --namespace NS Kubernetes namespace [default: gold-template]"
|
|
echo " --help Show this help"
|
|
echo ""
|
|
echo "Examples:"
|
|
echo " # Local deployment with SQLite"
|
|
echo " $0"
|
|
echo ""
|
|
echo " # Deploy with infra namespace databases"
|
|
echo " $0 --mode infra-db"
|
|
echo ""
|
|
echo " # Build and push to Harbor"
|
|
echo " $0 --push-harbor"
|
|
echo ""
|
|
echo " # Build and push to GHCR"
|
|
echo " $0 --push-ghcr ghcr.io/username/gold-template"
|
|
echo ""
|
|
echo " # Custom namespace"
|
|
echo " $0 --namespace my-app"
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Run
|
|
if [ "${SKIP_BUILD}" != "true" ]; then
|
|
main
|
|
else
|
|
check_orbstack
|
|
create_namespace
|
|
create_secrets
|
|
deploy_helm
|
|
verify_deployment
|
|
setup_port_forward
|
|
show_info
|
|
fi
|