Skip to content

refactor(infrastructure): Optimize CDN and ECS configuration for fron… #50

refactor(infrastructure): Optimize CDN and ECS configuration for fron…

refactor(infrastructure): Optimize CDN and ECS configuration for fron… #50

name: Deploy Infrastructure
on:
push:
branches:
- main
paths:
- 'frontend/**'
- 'backend/**'
- 'infrastructure/**'
- 'lambdas/**'
- 'agent-creator/**'
- '.github/workflows/deploy-infrastructure.yml'
workflow_dispatch:
inputs:
environment:
description: 'Environment to deploy'
required: true
default: 'staging'
type: choice
options:
- staging
- production
force_docker_build:
description: 'Force Docker image rebuild (ignore change detection)'
required: false
default: false
type: boolean
env:
AWS_REGION: us-east-1
ECR_AGENTCREATOR_REPO: oratio-agentcreator
ECR_BACKEND_REPO: oratio-backend
ECR_FRONTEND_REPO: oratio-frontend
ECR_CHAMELEON_REPO: oratio-chameleon
jobs:
deploy:
name: Deploy to AWS
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.environment || (github.ref == 'refs/heads/main' && 'production' || 'staging') }}
permissions:
id-token: write # Required for OIDC
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
# Detect which files changed to skip unnecessary Docker builds
- name: Detect changed files
uses: dorny/paths-filter@v3
id: changes
with:
filters: |
agentcreator:
- 'agent-creator/**'
backend:
- 'backend/**'
frontend:
- 'frontend/**'
chameleon:
- 'agent-creator/chameleon/**'
infrastructure:
- 'infrastructure/**'
- 'lambdas/**'
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}
role-session-name: GitHubActions-${{ github.run_id }}
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true
cache-dependency-glob: "infrastructure/pyproject.toml"
- name: Set up Node.js (for CDK CLI)
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install CDK CLI
run: npm install -g aws-cdk
- name: Install Python dependencies with uv
run: |
cd infrastructure
uv sync --frozen
echo "✓ Infrastructure dependencies installed"
# Step 1: Set up Docker Buildx for ARM64 builds
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# Step 2: Login to ECR
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
# Step 3: Create ECR repositories if they don't exist
- name: Create ECR repositories
run: |
.github/scripts/create-ecr-repo.sh ${{ env.ECR_AGENTCREATOR_REPO }} ${{ env.AWS_REGION }}
.github/scripts/create-ecr-repo.sh ${{ env.ECR_BACKEND_REPO }} ${{ env.AWS_REGION }}
.github/scripts/create-ecr-repo.sh ${{ env.ECR_FRONTEND_REPO }} ${{ env.AWS_REGION }}
# Chameleon ECR repo is auto-created by starter toolkit
echo "✓ ECR repositories ready"
# Step 4: Build and push AgentCreator Docker image (ARM64)
- name: Build and push AgentCreator image
if: steps.changes.outputs.agentcreator == 'true' || inputs.force_docker_build
uses: docker/build-push-action@v5
with:
context: ./agent-creator
file: ./agent-creator/Dockerfile
platforms: linux/arm64
push: true
tags: |
${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_AGENTCREATOR_REPO }}:${{ github.sha }}
${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_AGENTCREATOR_REPO }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Set AgentCreator image URI
run: |
if [ "${{ steps.changes.outputs.agentcreator }}" == "true" ] || [ "${{ inputs.force_docker_build }}" == "true" ]; then
echo "AGENTCREATOR_IMAGE_URI=${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_AGENTCREATOR_REPO }}:${{ github.sha }}" >> $GITHUB_ENV
echo "✓ AgentCreator image pushed: ${{ github.sha }}"
else
echo "AGENTCREATOR_IMAGE_URI=${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_AGENTCREATOR_REPO }}:latest" >> $GITHUB_ENV
echo "⏭️ AgentCreator unchanged, using latest image"
fi
# Step 5: Build and push Backend Docker image (ARM64)
- name: Build and push Backend image
if: steps.changes.outputs.backend == 'true' || inputs.force_docker_build
uses: docker/build-push-action@v5
with:
context: ./backend
file: ./backend/Dockerfile.dev
platforms: linux/arm64
push: true
tags: |
${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_BACKEND_REPO }}:${{ github.sha }}
${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_BACKEND_REPO }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Set Backend image URI
run: |
if [ "${{ steps.changes.outputs.backend }}" == "true" ] || [ "${{ inputs.force_docker_build }}" == "true" ]; then
echo "BACKEND_IMAGE_URI=${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_BACKEND_REPO }}:${{ github.sha }}" >> $GITHUB_ENV
echo "✓ Backend image pushed: ${{ github.sha }}"
else
echo "BACKEND_IMAGE_URI=${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_BACKEND_REPO }}:latest" >> $GITHUB_ENV
echo "⏭️ Backend unchanged, using latest image"
fi
# Step 5b: Build and push Frontend image (ARM64)
- name: Build and push Frontend image
if: steps.changes.outputs.frontend == 'true' || steps.changes.outputs.infrastructure == 'true' || inputs.force_docker_build
uses: docker/build-push-action@v5
with:
context: ./frontend
file: ./frontend/Dockerfile.prod
platforms: linux/arm64
push: true
tags: |
${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_FRONTEND_REPO }}:${{ github.sha }}
${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_FRONTEND_REPO }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Set Frontend image URI
run: |
if [ "${{ steps.changes.outputs.frontend }}" == "true" ] || [ "${{ inputs.force_docker_build }}" == "true" ]; then
echo "FRONTEND_IMAGE_URI=${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_FRONTEND_REPO }}:${{ github.sha }}" >> $GITHUB_ENV
echo "✓ Frontend image pushed: ${{ github.sha }}"
else
echo "FRONTEND_IMAGE_URI=${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_FRONTEND_REPO }}:latest" >> $GITHUB_ENV
echo "⏭️ Frontend unchanged, using latest image"
fi
# Step 6: Chameleon will be deployed using starter toolkit (no Docker build needed)
# The toolkit handles Docker build, ECR push, and AgentCore deployment automatically
# Step 7: CDK Bootstrap (if needed)
- name: CDK Bootstrap
working-directory: infrastructure
run: |
uv run cdk bootstrap aws://${{ secrets.AWS_ACCOUNT_ID }}/${{ env.AWS_REGION }} || true
# Step 7: CDK Deploy (creates IAM roles for AgentCore)
- name: CDK Deploy
working-directory: infrastructure
run: |
uv run cdk deploy --all --require-approval never
env:
CDK_DEFAULT_ACCOUNT: ${{ secrets.AWS_ACCOUNT_ID }}
CDK_DEFAULT_REGION: ${{ env.AWS_REGION }}
ENVIRONMENT: ${{ github.event.inputs.environment || (github.ref == 'refs/heads/main' && 'production' || 'staging') }}
# Step 8: Export IAM Role ARNs from CloudFormation
- name: Export IAM Role ARNs
id: export_roles
run: |
AGENTCREATOR_ROLE_ARN=$(aws cloudformation describe-stacks \
--region ${{ env.AWS_REGION }} \
--stack-name OratioStack \
--query "Stacks[0].Outputs[?OutputKey=='AgentCreatorExecutionRoleArn'].OutputValue" \
--output text)
CHAMELEON_ROLE_ARN=$(aws cloudformation describe-stacks \
--region ${{ env.AWS_REGION }} \
--stack-name OratioStack \
--query "Stacks[0].Outputs[?OutputKey=='ChameleonExecutionRoleArn'].OutputValue" \
--output text)
echo "agentcreator_role_arn=$AGENTCREATOR_ROLE_ARN" >> $GITHUB_OUTPUT
echo "chameleon_role_arn=$CHAMELEON_ROLE_ARN" >> $GITHUB_OUTPUT
echo "✅ Exported IAM Role ARNs:"
echo " AgentCreator: $AGENTCREATOR_ROLE_ARN"
echo " Chameleon: $CHAMELEON_ROLE_ARN"
# Step 9: Deploy AgentCreator to Bedrock AgentCore Runtime (AFTER CDK)
- name: Deploy AgentCreator to AgentCore
if: steps.changes.outputs.agentcreator == 'true' || inputs.force_docker_build
working-directory: infrastructure
run: |
OUTPUT=$(uv run python scripts/deploy_agentcore.py \
--name oratio-agentcreator \
--image "${{ env.AGENTCREATOR_IMAGE_URI }}" \
--role-arn "${{ steps.export_roles.outputs.agentcreator_role_arn }}" \
--description "Meta-agent that generates Strands agents from SOPs" \
--region ${{ env.AWS_REGION }})
RUNTIME_ARN=$(echo "$OUTPUT" | grep "RUNTIME_ARN=" | cut -d'=' -f2)
echo "AGENTCREATOR_RUNTIME_ARN=$RUNTIME_ARN" >> $GITHUB_ENV
echo "✓ AgentCreator deployed: $RUNTIME_ARN"
# Step 10: Store AgentCreator Runtime ARN in Parameter Store
- name: Store AgentCreator Runtime ARN in Parameter Store
if: steps.changes.outputs.agentcreator == 'true' || inputs.force_docker_build
run: |
aws ssm put-parameter \
--name "/oratio/agentcreator/runtime-arn" \
--value "${{ env.AGENTCREATOR_RUNTIME_ARN }}" \
--type "String" \
--overwrite \
--region ${{ env.AWS_REGION }}
echo "✓ Runtime ARN stored: /oratio/agentcreator/runtime-arn"
# Step 11: Deploy Chameleon using Starter Toolkit
# Deploy if: (1) Chameleon files changed, OR (2) force build requested
# The toolkit automatically builds Docker image, pushes to ECR, and deploys to AgentCore
# Note: The toolkit handles both create and update operations automatically
- name: Deploy Chameleon to AgentCore with Starter Toolkit
if: steps.changes.outputs.chameleon == 'true' || inputs.force_docker_build
env:
CODE_BUCKET: ${{ secrets.CODE_BUCKET_NAME || 'oratio-generated-code' }}
AGENTS_TABLE: ${{ secrets.AGENTS_TABLE_NAME || 'oratio-agents' }}
run: |
cd infrastructure
OUTPUT=$(uv run python scripts/deploy_agentcore_toolkit.py \
--name oratio-chameleon \
--entrypoint generic_loader.py \
--role-arn "${{ steps.export_roles.outputs.chameleon_role_arn }}" \
--region ${{ env.AWS_REGION }} \
--working-dir ../agent-creator/chameleon \
--env "CODE_BUCKET=${CODE_BUCKET}" \
--env "AGENTS_TABLE=${AGENTS_TABLE}" \
--env "AWS_REGION=${{ env.AWS_REGION }}")
RUNTIME_ARN=$(echo "$OUTPUT" | grep "RUNTIME_ARN=" | cut -d'=' -f2)
echo "CHAMELEON_RUNTIME_ARN=$RUNTIME_ARN" >> $GITHUB_ENV
echo "✓ Chameleon deployed with starter toolkit: $RUNTIME_ARN"
# Step 12: Store Chameleon Runtime ARN in Parameter Store
- name: Store Chameleon Runtime ARN in Parameter Store
if: steps.changes.outputs.chameleon == 'true' || inputs.force_docker_build
run: |
aws ssm put-parameter \
--name "/oratio/chameleon/runtime-arn" \
--value "${{ env.CHAMELEON_RUNTIME_ARN }}" \
--type "String" \
--overwrite \
--region ${{ env.AWS_REGION }}
echo "✓ Chameleon ARN stored: /oratio/chameleon/runtime-arn"
echo "✓ Lambda will read ARN from SSM Parameter Store automatically"
# Step 13: Create deployment tag
- name: Create deployment tag
if: success()
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "deploy-$(date +%Y%m%d-%H%M%S)" -m "Deployment to ${{ github.event.inputs.environment || 'staging' }}"
git push origin --tags || true
# Step 14: Notify success
- name: Deployment Summary
if: success()
run: |
echo "## ✅ Deployment Successful" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Environment:** ${{ github.event.inputs.environment || (github.ref == 'refs/heads/main' && 'production' || 'staging') }}" >> $GITHUB_STEP_SUMMARY
echo "**Region:** ${{ env.AWS_REGION }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Docker Images" >> $GITHUB_STEP_SUMMARY
echo "- AgentCreator: \`${{ env.AGENTCREATOR_IMAGE_URI }}\`" >> $GITHUB_STEP_SUMMARY
echo "- Backend: \`${{ env.BACKEND_IMAGE_URI }}\`" >> $GITHUB_STEP_SUMMARY
echo "- Chameleon: \`${{ env.CHAMELEON_IMAGE_URI }}\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### AgentCore Runtimes" >> $GITHUB_STEP_SUMMARY
echo "- AgentCreator: \`${{ env.AGENTCREATOR_RUNTIME_ARN }}\`" >> $GITHUB_STEP_SUMMARY
echo "- Chameleon: \`${{ env.CHAMELEON_RUNTIME_ARN }}\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### IAM Execution Roles" >> $GITHUB_STEP_SUMMARY
echo "- AgentCreator: \`${{ steps.export_roles.outputs.agentcreator_role_arn }}\`" >> $GITHUB_STEP_SUMMARY
echo "- Chameleon: \`${{ steps.export_roles.outputs.chameleon_role_arn }}\`" >> $GITHUB_STEP_SUMMARY
- name: Notify failure
if: failure()
run: |
echo "## ❌ Deployment Failed" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Check the logs above for details." >> $GITHUB_STEP_SUMMARY