Deploy Layer-based Infrastructure #29
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # CodeRipple Layer-based Infrastructure Deployment Workflow | |
| # Enhanced deployment workflow for layer-based architecture | |
| # Supports layer building, deployment, and validation | |
| name: Deploy Layer-based Infrastructure | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| action: | |
| description: 'Deployment action to perform' | |
| required: true | |
| type: choice | |
| options: | |
| - build-layers | |
| - plan | |
| - deploy | |
| - validate | |
| - rollback | |
| default: 'plan' | |
| confirm_deploy: | |
| description: 'Type "yes" to confirm deploy/rollback operations' | |
| required: false | |
| type: string | |
| default: '' | |
| validation_mode: | |
| description: 'Validation mode for testing' | |
| required: false | |
| type: choice | |
| options: | |
| - comprehensive | |
| - quick | |
| - performance-only | |
| default: 'comprehensive' | |
| env: | |
| AWS_DEFAULT_REGION: ${{ secrets.TF_VAR_aws_region }} | |
| TF_VAR_aws_region: ${{ secrets.TF_VAR_aws_region }} | |
| TF_VAR_environment: prod | |
| TF_VAR_project_name: coderipple | |
| # GitHub repository configuration | |
| TF_VAR_github_repo_owner: ${{ secrets.TF_VAR_github_repo_owner }} | |
| TF_VAR_github_repo_name: ${{ secrets.TF_VAR_github_repo_name }} | |
| TF_VAR_github_webhook_secret: ${{ secrets.TF_VAR_github_webhook_secret }} | |
| TF_VAR_coderipple_min_quality_score: ${{ secrets.TF_VAR_coderipple_min_quality_score }} | |
| # Layer-optimized Lambda configuration | |
| TF_VAR_lambda_function_name: coderipple-orchestrator | |
| TF_VAR_lambda_memory_size: 1536 # Reduced from 2048 due to layer optimization | |
| TF_VAR_lambda_timeout: 60 # Reduced from 900 due to improved performance | |
| TF_VAR_lambda_runtime: python3.12 # Downgraded from python3.13 for OpenTelemetry compatibility | |
| # API Gateway configuration | |
| TF_VAR_api_gateway_name: coderipple-webhook-api | |
| TF_VAR_api_gateway_stage: prod | |
| # CloudWatch configuration | |
| TF_VAR_log_retention_days: 14 | |
| jobs: | |
| build-layers: | |
| name: Build Lambda Layers | |
| runs-on: ubuntu-latest | |
| if: github.event.inputs.action == 'build-layers' || github.event.inputs.action == 'deploy' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Python 3.12 | |
| uses: actions/setup-python@v4 | |
| with: | |
| python-version: '3.12' | |
| - name: Build Dependencies Layer | |
| run: | | |
| echo "🔨 Building dependencies layer..." | |
| cd layers/dependencies | |
| ./1-build.sh | |
| # Verify layer artifact | |
| if [ -f "coderipple-dependencies-layer.zip" ]; then | |
| size=$(stat -c%s "coderipple-dependencies-layer.zip") | |
| size_mb=$((size / 1024 / 1024)) | |
| echo "✅ Dependencies layer built: ${size_mb}MB" | |
| echo "DEPS_LAYER_SIZE=${size_mb}" >> $GITHUB_ENV | |
| else | |
| echo "❌ Dependencies layer build failed" | |
| exit 1 | |
| fi | |
| - name: Build Package Layer | |
| run: | | |
| echo "🔨 Building package layer..." | |
| cd layers/coderipple-package | |
| ./1-build.sh | |
| # Verify layer artifact | |
| if [ -f "coderipple-package-layer.zip" ]; then | |
| size=$(stat -c%s "coderipple-package-layer.zip") | |
| size_kb=$((size / 1024)) | |
| echo "✅ Package layer built: ${size_kb}KB" | |
| echo "PKG_LAYER_SIZE=${size_kb}" >> $GITHUB_ENV | |
| else | |
| echo "❌ Package layer build failed" | |
| exit 1 | |
| fi | |
| - name: Build Function Package | |
| run: | | |
| echo "🔨 Building function package..." | |
| cd functions/orchestrator | |
| ./build-automation.sh | |
| # Verify function artifact | |
| if [ -f "function.zip" ]; then | |
| size=$(stat -c%s "function.zip") | |
| size_kb=$((size / 1024)) | |
| echo "✅ Function package built: ${size_kb}KB" | |
| echo "FUNC_PACKAGE_SIZE=${size_kb}" >> $GITHUB_ENV | |
| else | |
| echo "❌ Function package build failed" | |
| exit 1 | |
| fi | |
| - name: Layer Build Summary | |
| run: | | |
| echo "## 🏗️ Layer Build Results" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "✅ **Dependencies Layer:** ${DEPS_LAYER_SIZE}MB" >> $GITHUB_STEP_SUMMARY | |
| echo "✅ **Package Layer:** ${PKG_LAYER_SIZE}KB" >> $GITHUB_STEP_SUMMARY | |
| echo "✅ **Function Package:** ${FUNC_PACKAGE_SIZE}KB" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Calculate size reduction | |
| total_layer_size=$((DEPS_LAYER_SIZE * 1024 + PKG_LAYER_SIZE + FUNC_PACKAGE_SIZE)) | |
| monolithic_estimate=$((28 * 1024)) # 28MB estimated | |
| reduction_pct=$(echo "scale=1; (1 - $total_layer_size / $monolithic_estimate) * 100" | bc) | |
| echo "📊 **Size Optimization:**" >> $GITHUB_STEP_SUMMARY | |
| echo "- Estimated monolithic size: 28MB" >> $GITHUB_STEP_SUMMARY | |
| echo "- Layer-based total: ${total_layer_size}KB" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Size reduction: ${reduction_pct}%**" >> $GITHUB_STEP_SUMMARY | |
| - name: Upload Layer Artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: lambda-layers | |
| path: | | |
| layers/dependencies/coderipple-dependencies-layer.zip | |
| layers/coderipple-package/coderipple-package-layer.zip | |
| functions/orchestrator/function.zip | |
| retention-days: 7 | |
| validate-terraform-state: | |
| name: Early Terraform State Validation | |
| runs-on: ubuntu-latest | |
| if: always() && (github.event.inputs.action == 'plan' || github.event.inputs.action == 'deploy') | |
| defaults: | |
| run: | |
| working-directory: infra/terraform | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
| aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
| aws-region: ${{ env.AWS_DEFAULT_REGION }} | |
| - name: Setup Terraform | |
| uses: hashicorp/setup-terraform@v3 | |
| with: | |
| terraform_version: 1.0.11 | |
| - name: Initialize Terraform | |
| run: | | |
| terraform init \ | |
| -backend-config="bucket=coderipple-terraform-state" \ | |
| -backend-config="key=coderipple/terraform.tfstate" \ | |
| -backend-config="region=${{ env.AWS_DEFAULT_REGION }}" \ | |
| -backend-config="encrypt=true" | |
| - name: Early State Validation | |
| run: | | |
| echo "🔍 Early validation of Terraform state vs AWS resources..." | |
| # Check Lambda function early - this is the main conflict we're trying to catch | |
| echo "Checking Lambda function..." | |
| LAMBDA_EXISTS=$(aws lambda get-function --function-name coderipple-orchestrator --query 'Configuration.FunctionName' --output text 2>/dev/null || echo "NOT_FOUND") | |
| if [ "$LAMBDA_EXISTS" != "NOT_FOUND" ] && [ "$LAMBDA_EXISTS" != "None" ]; then | |
| echo "⚠️ Lambda function 'coderipple-orchestrator' exists in AWS" | |
| # Check if it's in Terraform state | |
| terraform show -json | jq -r '.values.root_module.resources[]? | select(.type=="aws_lambda_function" and .name=="coderipple_orchestrator") | .values.function_name' 2>/dev/null || { | |
| echo "❌ CRITICAL: Lambda function exists in AWS but not tracked in Terraform state" | |
| echo "🔄 Import logic will handle this, but flagging early to avoid build waste" | |
| echo "LAMBDA_IMPORT_NEEDED=true" >> $GITHUB_ENV | |
| } | |
| else | |
| echo "ℹ️ Lambda function 'coderipple-orchestrator' not found in AWS - will be created" | |
| fi | |
| # Quick check of other critical resources | |
| echo "Checking other critical resources..." | |
| # API Gateway | |
| REST_API_EXISTS=$(aws apigateway get-rest-apis --query 'items[?name==`coderipple-webhook-api`].id' --output text 2>/dev/null || echo "") | |
| if [ ! -z "$REST_API_EXISTS" ] && [ "$REST_API_EXISTS" != "None" ]; then | |
| echo "ℹ️ API Gateway 'coderipple-webhook-api' exists: $REST_API_EXISTS" | |
| fi | |
| # S3 bucket | |
| S3_EXISTS=$(aws s3api head-bucket --bucket coderipple-terraform-state 2>/dev/null && echo "EXISTS" || echo "NOT_FOUND") | |
| if [ "$S3_EXISTS" = "EXISTS" ]; then | |
| echo "ℹ️ S3 bucket 'coderipple-terraform-state' exists" | |
| fi | |
| echo "✅ Early validation complete - proceeding with parallel build process" | |
| terraform-deploy: | |
| name: Terraform Deployment | |
| runs-on: ubuntu-latest | |
| needs: [build-layers, validate-terraform-state] | |
| if: always() && (github.event.inputs.action == 'plan' || github.event.inputs.action == 'deploy') | |
| defaults: | |
| run: | |
| working-directory: infra/terraform | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Download Layer Artifacts | |
| if: github.event.inputs.action == 'deploy' | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: lambda-layers | |
| path: . | |
| - name: Setup Terraform | |
| uses: hashicorp/setup-terraform@v3 | |
| with: | |
| terraform_version: "~1.0" | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
| aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
| aws-region: ${{ env.AWS_DEFAULT_REGION }} | |
| - name: Validate confirmation for deploy operations | |
| if: github.event.inputs.action == 'deploy' | |
| run: | | |
| if [ "${{ github.event.inputs.confirm_deploy }}" != "yes" ]; then | |
| echo "❌ Confirmation required for deploy operation" | |
| echo "Please set 'confirm_deploy' input to 'yes' to proceed" | |
| exit 1 | |
| fi | |
| echo "✅ Confirmation received for deploy operation" | |
| - name: Initialize Terraform | |
| run: | | |
| terraform init \ | |
| -backend-config="bucket=coderipple-terraform-state" \ | |
| -backend-config="key=coderipple/terraform.tfstate" \ | |
| -backend-config="region=${{ env.AWS_DEFAULT_REGION }}" \ | |
| -backend-config="encrypt=true" | |
| - name: Import Existing AWS Resources | |
| run: | | |
| echo "🔄 Attempting to import existing AWS resources..." | |
| echo "🔍 Import process debugging enabled..." | |
| # Show AWS resources that exist for debugging | |
| echo "AWS Lambda functions matching 'coderipple':" | |
| aws lambda list-functions --query 'Functions[?starts_with(FunctionName, `coderipple`)].FunctionName' --output table || echo "No Lambda functions found" | |
| echo "AWS API Gateways matching 'coderipple':" | |
| aws apigateway get-rest-apis --query 'items[?starts_with(name, `coderipple`)].{Name:name,Id:id}' --output table || echo "No API Gateways found" | |
| # Import S3 buckets if they exist | |
| echo "Importing S3 buckets..." | |
| terraform import aws_s3_bucket.terraform_state coderipple-terraform-state || echo "S3 terraform state bucket import skipped (may not exist or already imported)" | |
| terraform import aws_s3_bucket.terraform_state_access_logs coderipple-terraform-state-access-logs || echo "S3 access logs bucket import skipped (may not exist or already imported)" | |
| # Import IAM roles if they exist | |
| echo "Importing IAM roles..." | |
| terraform import aws_iam_role.lambda_execution_role coderipple-lambda-execution-role || echo "Lambda execution role import skipped (may not exist or already imported)" | |
| terraform import aws_iam_role.api_gateway_cloudwatch_role coderipple-api-gateway-cloudwatch-role || echo "API Gateway CloudWatch role import skipped (may not exist or already imported)" | |
| # Import IAM policies if they exist | |
| echo "Importing IAM policies..." | |
| ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) | |
| terraform import aws_iam_policy.bedrock_access_policy arn:aws:iam::${ACCOUNT_ID}:policy/coderipple-bedrock-access || echo "Bedrock access policy import skipped (may not exist or already imported)" | |
| terraform import aws_iam_policy.cloudwatch_enhanced_policy arn:aws:iam::${ACCOUNT_ID}:policy/coderipple-cloudwatch-enhanced || echo "CloudWatch enhanced policy import skipped (may not exist or already imported)" | |
| terraform import aws_iam_policy.parameter_store_policy arn:aws:iam::${ACCOUNT_ID}:policy/coderipple-parameter-store-access || echo "Parameter store policy import skipped (may not exist or already imported)" | |
| terraform import aws_iam_policy.lambda_kms_policy arn:aws:iam::${ACCOUNT_ID}:policy/coderipple-lambda-kms-policy || echo "Lambda KMS policy import skipped (may not exist or already imported)" | |
| terraform import aws_iam_policy.lambda_xray_policy arn:aws:iam::${ACCOUNT_ID}:policy/coderipple-lambda-xray-policy || echo "Lambda X-Ray policy import skipped (may not exist or already imported)" | |
| terraform import aws_iam_policy.lambda_sqs_policy arn:aws:iam::${ACCOUNT_ID}:policy/coderipple-lambda-sqs-policy || echo "Lambda SQS policy import skipped (may not exist or already imported)" | |
| # Import SQS queue if it exists | |
| echo "Importing SQS queue..." | |
| terraform import aws_sqs_queue.lambda_dlq https://sqs.${{ env.AWS_DEFAULT_REGION }}.amazonaws.com/${ACCOUNT_ID}/coderipple-lambda-dlq || echo "SQS DLQ import skipped (may not exist or already imported)" | |
| # Import CloudWatch log groups if they exist | |
| echo "Importing CloudWatch log groups..." | |
| terraform import aws_cloudwatch_log_group.lambda_logs /aws/lambda/coderipple-orchestrator || echo "Lambda log group import skipped (may not exist or already imported)" | |
| terraform import aws_cloudwatch_log_group.api_gateway_logs /aws/apigateway/coderipple-webhook-api || echo "API Gateway log group import skipped (may not exist or already imported)" | |
| # Import API Gateway resources with duplicate handling | |
| echo "Checking for existing API Gateway..." | |
| REST_API_ID=$(aws apigateway get-rest-apis --query 'items[?name==`coderipple-webhook-api`] | sort_by(@, &createdDate) | [0].id' --output text 2>/dev/null || echo "") | |
| if [ ! -z "$REST_API_ID" ] && [ "$REST_API_ID" != "None" ] && [ "$REST_API_ID" != "null" ]; then | |
| echo "Found API Gateway with ID: $REST_API_ID" | |
| terraform import aws_api_gateway_rest_api.webhook_api $REST_API_ID || echo "API Gateway REST API import skipped (may not exist or already imported)" | |
| terraform import aws_api_gateway_stage.webhook_stage $REST_API_ID/${{ env.TF_VAR_environment }} || echo "API Gateway stage import skipped (may not exist or already imported)" | |
| # Import API Gateway resources and methods | |
| ROOT_RESOURCE_ID=$(aws apigateway get-resources --rest-api-id $REST_API_ID --query 'items[?path==`/`].id' --output text 2>/dev/null || echo "") | |
| if [ ! -z "$ROOT_RESOURCE_ID" ] && [ "$ROOT_RESOURCE_ID" != "None" ]; then | |
| # Find webhook resource | |
| WEBHOOK_RESOURCE_ID=$(aws apigateway get-resources --rest-api-id $REST_API_ID --query 'items[?pathPart==`webhook`].id' --output text 2>/dev/null || echo "") | |
| if [ ! -z "$WEBHOOK_RESOURCE_ID" ] && [ "$WEBHOOK_RESOURCE_ID" != "None" ]; then | |
| terraform import aws_api_gateway_resource.webhook_resource $REST_API_ID/$WEBHOOK_RESOURCE_ID || echo "API Gateway webhook resource import skipped" | |
| terraform import aws_api_gateway_method.webhook_post $REST_API_ID/$WEBHOOK_RESOURCE_ID/POST || echo "API Gateway POST method import skipped" | |
| terraform import aws_api_gateway_integration.webhook_integration $REST_API_ID/$WEBHOOK_RESOURCE_ID/POST || echo "API Gateway integration import skipped" | |
| fi | |
| fi | |
| else | |
| echo "No existing API Gateway found" | |
| fi | |
| # Import Lambda function if it exists | |
| echo "Importing Lambda function..." | |
| terraform import aws_lambda_function.coderipple_orchestrator coderipple-orchestrator || echo "Lambda function import skipped (may not exist or already imported)" | |
| terraform import aws_lambda_alias.coderipple_orchestrator_alias coderipple-orchestrator/${{ env.TF_VAR_environment }} || echo "Lambda alias import skipped (may not exist or already imported)" | |
| # Import Lambda permissions if they exist | |
| terraform import aws_lambda_permission.api_gateway_invoke coderipple-orchestrator/AllowExecutionFromAPIGateway || echo "Lambda permission import skipped (may not exist or already imported)" | |
| # Import KMS key and alias if they exist | |
| echo "Importing KMS resources..." | |
| KMS_KEY_ID=$(aws kms list-aliases --query 'Aliases[?AliasName==`alias/coderipple-encryption-key`].TargetKeyId' --output text 2>/dev/null || echo "") | |
| if [ ! -z "$KMS_KEY_ID" ] && [ "$KMS_KEY_ID" != "None" ]; then | |
| terraform import aws_kms_key.coderipple_key $KMS_KEY_ID || echo "KMS key import skipped (may not exist or already imported)" | |
| terraform import aws_kms_alias.coderipple_key_alias alias/coderipple-encryption-key || echo "KMS alias import skipped (may not exist or already imported)" | |
| fi | |
| # Import CloudWatch alarms if they exist | |
| echo "Importing CloudWatch alarms..." | |
| terraform import aws_cloudwatch_metric_alarm.dlq_messages coderipple-dlq-messages || echo "DLQ messages alarm import skipped (may not exist or already imported)" | |
| terraform import aws_cloudwatch_metric_alarm.lambda_throttles coderipple-lambda-throttles || echo "Lambda throttles alarm import skipped (may not exist or already imported)" | |
| echo "✅ Resource import attempt completed" | |
| working-directory: infra/terraform | |
| continue-on-error: true | |
| - name: Validate Terraform configuration | |
| run: terraform validate | |
| - name: Terraform Plan | |
| id: plan | |
| run: | | |
| terraform plan \ | |
| -out=tfplan \ | |
| -detailed-exitcode | |
| continue-on-error: true | |
| - name: Terraform Plan Summary | |
| run: | | |
| echo "## 📋 Terraform Plan Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Action:** ${{ github.event.inputs.action }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Environment:** production" >> $GITHUB_STEP_SUMMARY | |
| echo "**Architecture:** single-lambda-with-layers" >> $GITHUB_STEP_SUMMARY | |
| echo "**Region:** ${{ env.AWS_DEFAULT_REGION }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ "${{ steps.plan.outcome }}" == "success" ]; then | |
| if [ "${{ steps.plan.outputs.exitcode }}" == "0" ]; then | |
| echo "✅ **Plan Status:** No changes detected" >> $GITHUB_STEP_SUMMARY | |
| elif [ "${{ steps.plan.outputs.exitcode }}" == "2" ]; then | |
| echo "📋 **Plan Status:** Changes detected and ready to apply" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| else | |
| echo "❌ **Plan Status:** Plan failed - check logs for details" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| - name: Terraform Apply | |
| if: github.event.inputs.action == 'deploy' && steps.plan.outcome == 'success' | |
| run: | | |
| echo "🚀 Deploying layer-based infrastructure..." | |
| terraform apply tfplan | |
| echo "## 🎉 Deployment Results" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "✅ **Layer-based infrastructure deployed successfully**" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Infrastructure Outputs" >> $GITHUB_STEP_SUMMARY | |
| echo "\`\`\`" >> $GITHUB_STEP_SUMMARY | |
| terraform output >> $GITHUB_STEP_SUMMARY | |
| echo "\`\`\`" >> $GITHUB_STEP_SUMMARY | |
| - name: Save Terraform outputs | |
| if: github.event.inputs.action == 'deploy' && steps.plan.outcome == 'success' | |
| run: | | |
| terraform output -json > terraform-outputs.json | |
| echo "Terraform outputs saved for validation" | |
| - name: Validate Lambda API Gateway Integration | |
| if: github.event.inputs.action == 'deploy' && steps.plan.outcome == 'success' | |
| run: | | |
| echo "🔍 Validating Lambda API Gateway integration..." | |
| # Get the deployed API Gateway ID and Lambda function name | |
| API_ID=$(terraform output -raw api_gateway_id 2>/dev/null || echo "") | |
| LAMBDA_NAME=$(terraform output -raw lambda_function_name 2>/dev/null || echo "coderipple-orchestrator") | |
| if [ ! -z "$API_ID" ] && [ "$API_ID" != "null" ]; then | |
| echo "API Gateway ID: $API_ID" | |
| echo "Lambda Function: $LAMBDA_NAME" | |
| # Check if Lambda permission exists for this API Gateway | |
| echo "Checking Lambda permissions..." | |
| PERMISSION_EXISTS=$(aws lambda get-policy --function-name $LAMBDA_NAME --query 'Policy' --output text 2>/dev/null | grep -c "$API_ID" || echo "0") | |
| if [ "$PERMISSION_EXISTS" -eq "0" ]; then | |
| echo "⚠️ Lambda permission missing for API Gateway $API_ID" | |
| echo "🔧 Adding Lambda permission..." | |
| # Add the missing permission | |
| aws lambda add-permission \ | |
| --function-name $LAMBDA_NAME \ | |
| --statement-id AllowExecutionFromAPIGateway \ | |
| --action lambda:InvokeFunction \ | |
| --principal apigateway.amazonaws.com \ | |
| --source-arn "arn:aws:execute-api:${{ env.AWS_DEFAULT_REGION }}:$(aws sts get-caller-identity --query Account --output text):$API_ID/*/*" \ | |
| 2>/dev/null || echo "Permission add failed - may already exist with different statement ID" | |
| echo "✅ Lambda permission configuration attempted" | |
| else | |
| echo "✅ Lambda permission already configured for API Gateway $API_ID" | |
| fi | |
| # Test the integration | |
| echo "🧪 Testing API Gateway integration..." | |
| WEBHOOK_URL="https://$API_ID.execute-api.${{ env.AWS_DEFAULT_REGION }}.amazonaws.com/prod/webhook" | |
| echo "Testing URL: $WEBHOOK_URL" | |
| HTTP_STATUS=$(curl -X POST "$WEBHOOK_URL" \ | |
| -H "Content-Type: application/json" \ | |
| -H "X-GitHub-Event: push" \ | |
| -d '{"test": true, "source": "pipeline-validation", "timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' \ | |
| -w "%{http_code}" \ | |
| -s -o /tmp/webhook_response.json) | |
| echo "HTTP Status: $HTTP_STATUS" | |
| echo "Response:" | |
| cat /tmp/webhook_response.json || echo "No response body" | |
| if [ "$HTTP_STATUS" = "200" ]; then | |
| echo "✅ Webhook integration test PASSED" | |
| echo "## 🎉 Integration Validation" >> $GITHUB_STEP_SUMMARY | |
| echo "✅ **Webhook endpoint is working correctly**" >> $GITHUB_STEP_SUMMARY | |
| echo "- **URL**: \`$WEBHOOK_URL\`" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Status**: HTTP 200 OK" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "❌ Webhook integration test FAILED with HTTP $HTTP_STATUS" | |
| echo "## ⚠️ Integration Validation" >> $GITHUB_STEP_SUMMARY | |
| echo "❌ **Webhook endpoint test failed**" >> $GITHUB_STEP_SUMMARY | |
| echo "- **URL**: \`$WEBHOOK_URL\`" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Status**: HTTP $HTTP_STATUS" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Note**: Check Lambda logs for details" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| else | |
| echo "❌ Could not determine API Gateway ID from Terraform outputs" | |
| terraform output || echo "No Terraform outputs available" | |
| exit 1 | |
| fi | |
| working-directory: infra/terraform | |
| continue-on-error: true | |
| - name: Test Webhook Integration | |
| if: github.event.inputs.action == 'deploy' && steps.plan.outcome == 'success' | |
| run: | | |
| echo "🧪 Testing webhook integration..." | |
| # Check Lambda runtime compatibility | |
| echo "🔍 Validating Python runtime compatibility..." | |
| LAMBDA_NAME=$(terraform output -raw lambda_function_name 2>/dev/null || echo "coderipple-orchestrator") | |
| RUNTIME_VERSION=$(aws lambda get-function-configuration --function-name $LAMBDA_NAME --query 'Runtime' --output text 2>/dev/null || echo "unknown") | |
| echo "Lambda Runtime: $RUNTIME_VERSION" | |
| if [ "$RUNTIME_VERSION" = "python3.13" ]; then | |
| echo "⚠️ Python 3.13 runtime detected - known OpenTelemetry compatibility issues" | |
| echo "💡 Consider using Python 3.12 for better compatibility" | |
| elif [ "$RUNTIME_VERSION" = "python3.12" ]; then | |
| echo "✅ Python 3.12 runtime - good OpenTelemetry compatibility" | |
| else | |
| echo "ℹ️ Runtime: $RUNTIME_VERSION" | |
| fi | |
| # Get webhook URL from Terraform output | |
| WEBHOOK_URL=$(terraform output -raw webhook_url 2>/dev/null || echo "") | |
| if [ ! -z "$WEBHOOK_URL" ] && [ "$WEBHOOK_URL" != "null" ]; then | |
| echo "Testing: $WEBHOOK_URL" | |
| # Simple webhook test | |
| HTTP_STATUS=$(curl -X POST "$WEBHOOK_URL" \ | |
| -H "Content-Type: application/json" \ | |
| -H "X-GitHub-Event: push" \ | |
| -d '{"test": true, "source": "deployment-validation"}' \ | |
| -w "%{http_code}" \ | |
| -s -o /dev/null) | |
| echo "HTTP Status: $HTTP_STATUS" | |
| if [ "$HTTP_STATUS" = "200" ]; then | |
| echo "✅ Webhook test passed" | |
| echo "## 🎉 Webhook Validation" >> $GITHUB_STEP_SUMMARY | |
| echo "✅ **Webhook endpoint is working correctly**" >> $GITHUB_STEP_SUMMARY | |
| echo "- **URL**: \`$WEBHOOK_URL\`" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Status**: HTTP 200 OK" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ Webhook test failed with HTTP $HTTP_STATUS" | |
| echo "## ⚠️ Webhook Validation" >> $GITHUB_STEP_SUMMARY | |
| echo "⚠️ **Webhook endpoint test failed**" >> $GITHUB_STEP_SUMMARY | |
| echo "- **URL**: \`$WEBHOOK_URL\`" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Status**: HTTP $HTTP_STATUS" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Note**: Check Lambda logs for details" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| else | |
| echo "⚠️ Could not get webhook URL from Terraform outputs" | |
| echo "## ⚠️ Webhook Validation" >> $GITHUB_STEP_SUMMARY | |
| echo "⚠️ **Could not determine webhook URL**" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| working-directory: infra/terraform | |
| continue-on-error: true | |
| - name: Upload Terraform outputs | |
| if: github.event.inputs.action == 'deploy' && steps.plan.outcome == 'success' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: terraform-outputs-production | |
| path: infra/terraform/terraform-outputs.json | |
| retention-days: 30 | |
| validate-deployment: | |
| name: Validate Layer-based Deployment | |
| runs-on: ubuntu-latest | |
| needs: [terraform-deploy] | |
| if: always() && (github.event.inputs.action == 'deploy' || github.event.inputs.action == 'validate') | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Download Terraform outputs | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: terraform-outputs-production | |
| path: infra/terraform/ | |
| continue-on-error: true | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
| aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
| aws-region: ${{ env.AWS_DEFAULT_REGION }} | |
| - name: Setup validation tools | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y jq bc | |
| - name: Run End-to-End Validation | |
| env: | |
| VALIDATION_MODE: ${{ github.event.inputs.validation_mode }} | |
| ENVIRONMENT: production | |
| AWS_REGION: ${{ env.AWS_DEFAULT_REGION }} | |
| run: | | |
| echo "🔍 Running end-to-end validation..." | |
| chmod +x scripts/end-to-end-validation.sh | |
| ./scripts/end-to-end-validation.sh | |
| - name: Upload Validation Report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: e2e-validation-report | |
| path: e2e-validation-report.json | |
| retention-days: 30 | |
| - name: Validation Summary | |
| if: always() | |
| run: | | |
| if [ -f "e2e-validation-report.json" ]; then | |
| echo "## 🧪 End-to-End Validation Results" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Extract validation summary | |
| total_tests=$(jq -r '.e2e_validation_report.summary.total_tests' e2e-validation-report.json) | |
| passed=$(jq -r '.e2e_validation_report.summary.passed' e2e-validation-report.json) | |
| failed=$(jq -r '.e2e_validation_report.summary.failed' e2e-validation-report.json) | |
| success_rate=$(jq -r '.e2e_validation_report.summary.success_rate' e2e-validation-report.json) | |
| echo "📊 **Validation Summary:**" >> $GITHUB_STEP_SUMMARY | |
| echo "- Total Tests: $total_tests" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ Passed: $passed" >> $GITHUB_STEP_SUMMARY | |
| echo "- ❌ Failed: $failed" >> $GITHUB_STEP_SUMMARY | |
| echo "- 📈 Success Rate: ${success_rate}%" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Show performance baselines if available | |
| if jq -e '.e2e_validation_report.performance_baselines' e2e-validation-report.json > /dev/null; then | |
| echo "⚡ **Performance Baselines:**" >> $GITHUB_STEP_SUMMARY | |
| cold_start=$(jq -r '.e2e_validation_report.performance_baselines.cold_start.value' e2e-validation-report.json 2>/dev/null || echo "N/A") | |
| warm_start=$(jq -r '.e2e_validation_report.performance_baselines.warm_start.value' e2e-validation-report.json 2>/dev/null || echo "N/A") | |
| echo "- Cold Start: ${cold_start}ms" >> $GITHUB_STEP_SUMMARY | |
| echo "- Warm Start: ${warm_start}ms" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| else | |
| echo "⚠️ Validation report not generated" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| rollback: | |
| name: Rollback Deployment | |
| runs-on: ubuntu-latest | |
| if: github.event.inputs.action == 'rollback' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Validate confirmation for rollback | |
| run: | | |
| if [ "${{ github.event.inputs.confirm_deploy }}" != "yes" ]; then | |
| echo "❌ Confirmation required for rollback operation" | |
| echo "Please set 'confirm_deploy' input to 'yes' to proceed" | |
| exit 1 | |
| fi | |
| echo "✅ Confirmation received for rollback operation" | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
| aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
| aws-region: ${{ env.AWS_DEFAULT_REGION }} | |
| - name: Execute Rollback | |
| env: | |
| ROLLBACK_MODE: automatic | |
| ENVIRONMENT: production | |
| run: | | |
| echo "🔄 Executing automatic rollback..." | |
| chmod +x scripts/rollback-procedures.sh | |
| ./scripts/rollback-procedures.sh | |
| - name: Rollback Summary | |
| run: | | |
| echo "## 🔄 Rollback Results" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "✅ **Rollback completed**" >> $GITHUB_STEP_SUMMARY | |
| echo "- Previous infrastructure state restored" >> $GITHUB_STEP_SUMMARY | |
| echo "- Function versions reverted" >> $GITHUB_STEP_SUMMARY | |
| echo "- Monitoring configuration restored" >> $GITHUB_STEP_SUMMARY | |
| cost-and-security: | |
| name: Cost and Security Analysis | |
| runs-on: ubuntu-latest | |
| if: github.event.inputs.action != 'rollback' | |
| defaults: | |
| run: | |
| working-directory: infra/terraform | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Terraform | |
| uses: hashicorp/setup-terraform@v3 | |
| with: | |
| terraform_version: "~1.0" | |
| - name: Security scan with Checkov | |
| continue-on-error: true | |
| run: | | |
| pip install checkov | |
| checkov -f . --framework terraform --output cli --quiet | |
| - name: Layer-based Cost Analysis | |
| run: | | |
| echo "## 💰 Layer-based Architecture Cost Analysis" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Lambda Function (Layer-optimized)" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Memory:** 1536MB (reduced from 2048MB)" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Timeout:** 60s (reduced from 900s)" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Package size:** ~12KB (99.6% reduction)" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Est. monthly cost:** ~$3-15 (reduced due to optimization)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Lambda Layers" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Dependencies Layer:** ~30MB (shared across functions)" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Package Layer:** ~117KB (CodeRipple code)" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Layer storage:** Free (under 75GB limit)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### API Gateway" >> $GITHUB_STEP_SUMMARY | |
| echo "- **REST API:** ~$3.50 per million requests" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Typical usage:** <$1/month" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### CloudWatch" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Log storage (14 days):** ~$1-3/month" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Custom metrics:** First 10 free" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 🎯 **Total Estimated Cost**" >> $GITHUB_STEP_SUMMARY | |
| echo "**$5-20/month** (reduced from $10-30 due to layer optimization)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 📈 **Performance Benefits**" >> $GITHUB_STEP_SUMMARY | |
| echo "- ⚡ Faster cold starts (layer pre-loading)" >> $GITHUB_STEP_SUMMARY | |
| echo "- 🚀 Faster deployments (99.6% smaller packages)" >> $GITHUB_STEP_SUMMARY | |
| echo "- 💾 Better caching (shared layers)" >> $GITHUB_STEP_SUMMARY | |
| echo "- 🔄 Easier updates (layer separation)" >> $GITHUB_STEP_SUMMARY |