name: BCards Deployment Pipeline on: push: branches: - main - 'Release/*' # PRs apenas validam, não fazem deploy pull_request: branches: [ main ] types: [opened, synchronize, reopened] env: REGISTRY: registry.redecarneir.us IMAGE_NAME: bcards MONGODB_HOST: 192.168.0.100:27017 jobs: test: name: Run Tests runs-on: ubuntu-latest steps: - name: Test info run: | echo "🧪 Executando testes para ${{ github.ref_name }}" echo "🎯 Trigger: ${{ github.event_name }}" # Verificar se deve pular testes SKIP_TESTS="${{ github.event.inputs.skip_tests || vars.SKIP_TESTS }}" if [ "$SKIP_TESTS" == "true" ]; then echo "⚠️ Testes PULADOS" echo "TESTS_SKIPPED=true" >> $GITHUB_ENV else echo "✅ Executando testes" echo "TESTS_SKIPPED=false" >> $GITHUB_ENV fi - name: Checkout code if: env.TESTS_SKIPPED == 'false' uses: actions/checkout@v4 - name: Setup .NET 8 if: env.TESTS_SKIPPED == 'false' uses: actions/setup-dotnet@v4 with: dotnet-version: '8.0.x' - name: Cache dependencies if: env.TESTS_SKIPPED == 'false' uses: actions/cache@v3 with: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }} restore-keys: | ${{ runner.os }}-nuget- - name: Restore dependencies if: env.TESTS_SKIPPED == 'false' run: dotnet restore - name: Build solution if: env.TESTS_SKIPPED == 'false' run: dotnet build --no-restore --configuration Release - name: Run unit tests if: env.TESTS_SKIPPED == 'false' run: dotnet test --no-build --configuration Release --verbosity normal --collect:"XPlat Code Coverage" # Job específico para validação de PRs (sem deploy) pr-validation: name: PR Validation runs-on: ubuntu-latest needs: [test] if: github.event_name == 'pull_request' steps: - name: PR Validation Summary run: | echo "✅ Pull Request Validation Summary" echo "🎯 Target Branch: ${{ github.base_ref }}" echo "📂 Source Branch: ${{ github.head_ref }}" echo "🧪 Tests: ${{ needs.test.result }}" echo "👤 Author: ${{ github.event.pull_request.user.login }}" echo "📝 Title: ${{ github.event.pull_request.title }}" echo "" echo "✨ PR está pronto para merge!" build-and-push: name: Build and Push Image runs-on: ubuntu-latest needs: [test] # Só faz build/push em push (não em PR) if: github.event_name == 'push' && (needs.test.result == 'success' || needs.test.result == 'skipped') steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 with: platforms: linux/amd64,linux/arm64 - name: Determine build settings id: settings run: | BRANCH_NAME="${{ github.ref_name }}" if [ "$BRANCH_NAME" = "main" ]; then # Main = Produção (ARM64) - usando Dockerfile da raiz como QRRapido echo "tag=latest" >> $GITHUB_OUTPUT echo "platform=linux/arm64" >> $GITHUB_OUTPUT echo "environment=Production" >> $GITHUB_OUTPUT echo "dockerfile=Dockerfile" >> $GITHUB_OUTPUT echo "deploy_target=production" >> $GITHUB_OUTPUT elif [[ "$BRANCH_NAME" == Release/* ]]; then # Release = Staging (x86) VERSION_RAW=${BRANCH_NAME#Release/} VERSION=$(echo "$VERSION_RAW" | sed 's/^[Vv]//') [ -z "$VERSION" ] && VERSION="0.0.1" echo "tag=$VERSION" >> $GITHUB_OUTPUT echo "platform=linux/amd64" >> $GITHUB_OUTPUT echo "environment=Staging" >> $GITHUB_OUTPUT echo "dockerfile=Dockerfile.release" >> $GITHUB_OUTPUT echo "deploy_target=staging" >> $GITHUB_OUTPUT echo "version=$VERSION" >> $GITHUB_OUTPUT fi COMMIT_SHA=${{ github.sha }} SHORT_COMMIT=${COMMIT_SHA:0:7} echo "commit=$SHORT_COMMIT" >> $GITHUB_OUTPUT echo "📦 Tag: ${{ steps.settings.outputs.tag }}" echo "🏗️ Platform: ${{ steps.settings.outputs.platform }}" echo "🌍 Environment: ${{ steps.settings.outputs.environment }}" echo "🎯 Target: ${{ steps.settings.outputs.deploy_target }}" - name: Build and push image run: | echo "🏗️ Building image for ${{ steps.settings.outputs.deploy_target }}..." # Debug das variáveis echo "Platform: ${{ steps.settings.outputs.platform }}" echo "Dockerfile: ${{ steps.settings.outputs.dockerfile }}" echo "Tag: ${{ steps.settings.outputs.tag }}" # Verificar se o Dockerfile existe if [ ! -f "${{ steps.settings.outputs.dockerfile }}" ]; then echo "❌ Dockerfile não encontrado: ${{ steps.settings.outputs.dockerfile }}" echo "📂 Arquivos na raiz:" ls -la echo "📂 Arquivos em src/BCards.Web/:" ls -la src/BCards.Web/ || echo "Diretório não existe" exit 1 else echo "✅ Dockerfile encontrado: ${{ steps.settings.outputs.dockerfile }}" fi # Build para a plataforma correta if [ "${{ steps.settings.outputs.deploy_target }}" = "production" ]; then # Build para produção (main branch) docker buildx build \ --platform ${{ steps.settings.outputs.platform }} \ --file ${{ steps.settings.outputs.dockerfile }} \ --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.settings.outputs.tag }} \ --push \ --progress=plain \ . else # Build para staging (Release branches) docker buildx build \ --platform ${{ steps.settings.outputs.platform }} \ --file ${{ steps.settings.outputs.dockerfile }} \ --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.settings.outputs.tag }} \ --push \ --build-arg VERSION=${{ steps.settings.outputs.version || 'latest' }} \ --build-arg COMMIT=${{ steps.settings.outputs.commit }} \ --progress=plain \ . fi deploy-production: name: Deploy to Production (ARM - OCI) runs-on: ubuntu-latest needs: [build-and-push] if: github.ref_name == 'main' environment: production steps: - name: Create Production Configuration run: | echo "🔧 Creating appsettings.Production.json with environment variables..." # Cria o arquivo de configuração para produção cat > appsettings.Production.json << 'CONFIG_EOF' { "Stripe": { "PublishableKey": "${{ vars.STRIPE_PUBLISHABLE_KEY }}", "SecretKey": "${{ secrets.STRIPE_SECRET_KEY }}", "WebhookSecret": "${{ secrets.STRIPE_WEBHOOK_SECRET }}", "Environment": "${{ vars.STRIPE_ENVIRONMENT || 'test' }}" }, "Authentication": { "Google": { "ClientId": "${{ vars.GOOGLE_CLIENT_ID }}", "ClientSecret": "${{ secrets.GOOGLE_CLIENT_SECRET }}" }, "Microsoft": { "ClientId": "${{ vars.MICROSOFT_CLIENT_ID }}", "ClientSecret": "${{ secrets.MICROSOFT_CLIENT_SECRET }}" } }, "SendGrid": { "ApiKey": "${{ secrets.SENDGRID_API_KEY }}", "FromEmail": "${{ vars.SENDGRID_FROM_EMAIL || 'ricardo.carneiro@jobmaker.com.br' }}", "FromName": "${{ vars.SENDGRID_FROM_NAME || 'Ricardo Carneiro' }}" }, "Moderation": { "PriorityTimeframes": { "Trial": "7.00:00:00", "Basic": "7.00:00:00", "Professional": "3.00:00:00", "Premium": "1.00:00:00" }, "MaxAttempts": 3, "ModeratorEmail": "${{ vars.MODERATOR_EMAIL || 'ricardo.carneiro@jobmaker.com.br' }}", "ModeratorEmails": [ "${{ vars.MODERATOR_EMAIL_1 || 'rrcgoncalves@gmail.com' }}", "${{ vars.MODERATOR_EMAIL_2 || 'rirocarneiro@gmail.com' }}" ] }, "Serilog": { "OpenSearchUrl": "${{ vars.OPENSEARCH_URL || 'http://localhost:9201' }}" } } CONFIG_EOF echo "✅ Configuration file created!" echo "🔍 File content (sensitive data masked):" cat appsettings.Production.json | sed 's/"[^"]*_[0-9A-Za-z_]*"/"***MASKED***"/g' - name: Deploy to Production Servers run: | echo "🚀 Deploying to production servers (ARM64)..." # Configura SSH (igual ao QRRapido) mkdir -p ~/.ssh echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa # Adiciona hosts conhecidos ssh-keyscan -H 141.148.162.114 >> ~/.ssh/known_hosts ssh-keyscan -H 129.146.116.218 >> ~/.ssh/known_hosts # Testa a chave SSH ssh-add ~/.ssh/id_rsa 2>/dev/null || echo "SSH key loaded" # Upload configuration file to Server 1 echo "📤 Uploading configuration to Server 1..." scp -o StrictHostKeyChecking=no appsettings.Production.json ubuntu@141.148.162.114:/tmp/ # Deploy no Servidor 1 (ARM - OCI) ssh -o StrictHostKeyChecking=no ubuntu@141.148.162.114 << 'EOF' echo "🔄 Atualizando Servidor 1..." # Create app config directory mkdir -p /home/ubuntu/bcards-config mv /tmp/appsettings.Production.json /home/ubuntu/bcards-config/ # Remove containers bcards-infrastructure se existirem docker stop bcards-infrastructure bcards-test-app || true docker rm bcards-infrastructure bcards-test-app || true # Para o container BCards atual se existir docker stop bcards-prod || true docker rm bcards-prod || true # Remove imagem antiga docker rmi ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest || true # Puxa nova imagem docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest # Executa novo container BCards docker run -d \ --name bcards-prod \ --restart unless-stopped \ --network host \ -e ASPNETCORE_ENVIRONMENT=Production \ -e ASPNETCORE_URLS=http://+:8080 \ -e MongoDb__ConnectionString="mongodb://admin:c4rn31r0@129.146.116.218:27017,141.148.162.114:27017/BCardsDB?replicaSet=rs0&authSource=admin" \ -e MongoDb__DatabaseName="BCardsDB" \ -e Logging__LogLevel__Default=Debug \ -e Serilog__OpenSearchUrl="http://localhost:9201" \ -v /home/ubuntu/bcards-config/appsettings.Production.json:/app/appsettings.Production.json:ro \ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest # Debug: verificar configuração aplicada echo "🔍 Verificando configuração MongoDB no container..." docker logs bcards-prod | head -20 || echo "Container ainda iniciando..." # Recarrega NGINX sudo nginx -t && sudo systemctl reload nginx echo "✅ Servidor 1 atualizado" EOF # Upload configuration file to Server 2 echo "📤 Uploading configuration to Server 2..." scp -o StrictHostKeyChecking=no appsettings.Production.json ubuntu@129.146.116.218:/tmp/ # Deploy no Servidor 2 (ARM - OCI) ssh -o StrictHostKeyChecking=no ubuntu@129.146.116.218 << 'EOF' echo "🔄 Atualizando Servidor 2..." # Create app config directory mkdir -p /home/ubuntu/bcards-config mv /tmp/appsettings.Production.json /home/ubuntu/bcards-config/ # Remove containers bcards-infrastructure se existirem docker stop bcards-infrastructure bcards-test-app || true docker rm bcards-infrastructure bcards-test-app || true # Para o container BCards atual se existir docker stop bcards-prod || true docker rm bcards-prod || true # Remove imagem antiga docker rmi ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest || true # Puxa nova imagem docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest # Executa novo container BCards docker run -d \ --name bcards-prod \ --restart unless-stopped \ --network host \ -e ASPNETCORE_ENVIRONMENT=Production \ -e ASPNETCORE_URLS=http://+:8080 \ -e MongoDb__ConnectionString="mongodb://admin:c4rn31r0@129.146.116.218:27017,141.148.162.114:27017/BCardsDB?replicaSet=rs0&authSource=admin" \ -e MongoDb__DatabaseName="BCardsDB" \ -e Logging__LogLevel__Default=Debug \ -e Serilog__OpenSearchUrl="http://localhost:9202" \ -v /home/ubuntu/bcards-config/appsettings.Production.json:/app/appsettings.Production.json:ro \ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest # Debug: verificar configuração aplicada echo "🔍 Verificando configuração MongoDB no container..." docker logs bcards-prod | head -20 || echo "Container ainda iniciando..." echo "✅ Servidor 2 atualizado" EOF - name: Health Check Production run: | echo "🏥 Verificando saúde dos servidores de produção..." sleep 30 # Verifica Servidor 1 echo "Verificando Servidor 1 (ARM)..." ssh -o StrictHostKeyChecking=no ubuntu@141.148.162.114 'curl -f http://localhost:8080/health || echo "⚠️ Servidor 1 pode não estar respondendo"' # Verifica Servidor 2 echo "Verificando Servidor 2 (ARM)..." ssh -o StrictHostKeyChecking=no ubuntu@129.146.116.218 'curl -f http://localhost:8080/health || echo "⚠️ Servidor 2 pode não estar respondendo"' deploy-staging: name: Deploy to Staging (x86 - Local) runs-on: ubuntu-latest needs: [build-and-push] if: startsWith(github.ref_name, 'Release/') steps: - name: Extract version id: version run: | BRANCH_NAME="${{ github.ref_name }}" VERSION_RAW=${BRANCH_NAME#Release/} VERSION=$(echo "$VERSION_RAW" | sed 's/^[Vv]//') [ -z "$VERSION" ] && VERSION="0.0.1" echo "version=$VERSION" >> $GITHUB_OUTPUT echo "📦 Deploying version: $VERSION" - name: Deploy to Staging Server run: | echo "🚀 Deploying to staging server (x86)..." # Configura SSH (igual ao QRRapido) mkdir -p ~/.ssh echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa # Adiciona hosts conhecidos ssh-keyscan -H 141.148.162.114 >> ~/.ssh/known_hosts ssh-keyscan -H 129.146.116.218 >> ~/.ssh/known_hosts # Testa a chave SSH ssh-add ~/.ssh/id_rsa 2>/dev/null || echo "SSH key loaded" # Deploy no Servidor Local x86 ssh -o StrictHostKeyChecking=no ubuntu@192.168.0.100 << EOF echo "🔄 Atualizando Servidor Staging..." # Remove containers bcards-infrastructure se existirem docker stop bcards-infrastructure bcards-test-app || true docker rm bcards-infrastructure bcards-test-app || true # Para o container BCards atual se existir docker stop bcards-staging || true docker rm bcards-staging || true # Remove imagem antiga docker rmi ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }} || true # Puxa nova imagem docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }} # Executa novo container BCards docker run -d \ --name bcards-staging \ --restart unless-stopped \ --network host \ -e ASPNETCORE_ENVIRONMENT=Staging \ -e ASPNETCORE_URLS=http://+:8080 \ -e Serilog__OpenSearchUrl="http://192.168.0.100:9200" \ ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }} echo "✅ Servidor Staging atualizado" EOF - name: Health Check Staging run: | echo "🏥 Verificando saúde do servidor de staging..." sleep 30 echo "Verificando Servidor Staging (x86)..." ssh -o StrictHostKeyChecking=no ubuntu@192.168.0.100 'curl -f http://localhost:8080/health || echo "⚠️ Servidor staging pode não estar respondendo"' cleanup: name: Cleanup Old Resources runs-on: ubuntu-latest needs: [deploy-production, deploy-staging] if: always() && (needs.deploy-production.result == 'success' || needs.deploy-staging.result == 'success') steps: - name: Cleanup containers and images run: | echo "🧹 Limpando recursos antigos..." # Configura SSH (igual ao QRRapido) mkdir -p ~/.ssh echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa # Adiciona hosts conhecidos ssh-keyscan -H 141.148.162.114 >> ~/.ssh/known_hosts ssh-keyscan -H 129.146.116.218 >> ~/.ssh/known_hosts # Testa a chave SSH ssh-add ~/.ssh/id_rsa 2>/dev/null || echo "SSH key loaded" # Lista de servidores baseada na branch if [ "${{ github.ref_name }}" = "main" ]; then SERVERS=("141.148.162.114" "129.146.116.218") else SERVERS=("192.168.0.100") fi # Limpeza em cada servidor for server in "${SERVERS[@]}"; do echo "🧹 Limpando servidor $server..." ssh -o StrictHostKeyChecking=no ubuntu@$server << 'EOF' # Remove containers parados docker container prune -f # Remove imagens não utilizadas docker image prune -f # Remove redes não utilizadas docker network prune -f EOF done echo "✅ Limpeza concluída!" deployment-summary: name: Deployment Summary runs-on: ubuntu-latest needs: [deploy-production, deploy-staging] if: always() steps: - name: Summary run: | echo "📋 DEPLOYMENT SUMMARY" echo "====================" echo "🎯 Branch: ${{ github.ref_name }}" echo "🔑 Commit: ${{ github.sha }}" echo "🏗️ Registry: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" if [ "${{ github.ref_name }}" = "main" ]; then echo "🌍 Environment: Production (ARM64)" echo "🖥️ Servers: 141.148.162.114, 129.146.116.218" echo "📦 Tag: latest" echo "🔗 Status: ${{ needs.deploy-production.result }}" else echo "🌍 Environment: Staging (x86)" echo "🖥️ Server: 192.168.0.100" echo "📦 Tag: ${{ github.ref_name }}" echo "🔗 Status: ${{ needs.deploy-staging.result }}" fi echo "====================" echo "✅ Pipeline completed!"