Files
bruno 2879e5a849 feat: optimize blueprint and add production-ready configurations
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>
2025-12-30 20:55:37 +08:00

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