Skip to content

Deploy Layer-based Infrastructure #29

Deploy Layer-based Infrastructure

Deploy Layer-based Infrastructure #29

# 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