Skip to content

Use UV

Use UV #23

Workflow file for this run

name: Deploy to AWS ECS
on:
push:
branches:
- main
permissions:
id-token: write
contents: read
jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Setup Python
run: uv python install 3.11
- name: Install dependencies
run: uv pip install --system -e ".[dev]"
- name: Run tests
run: uv run pytest tests/test_models.py -v
deploy:
name: Deploy
runs-on: ubuntu-latest
needs: test
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ vars.AWS_REGION }}
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.6.0
- name: Terraform init
working-directory: ./terraform
run: terraform init
- name: Terraform plan
working-directory: ./terraform
env:
TF_VAR_supabase_url: ${{ secrets.SUPABASE_URL }}
TF_VAR_supabase_key: ${{ secrets.SUPABASE_KEY }}
TF_VAR_supabase_db_url: ${{ secrets.SUPABASE_DB_URL }}
TF_VAR_logfire_token: ${{ secrets.LOGFIRE_TOKEN }}
run: terraform plan
- name: Terraform apply
working-directory: ./terraform
env:
TF_VAR_supabase_url: ${{ secrets.SUPABASE_URL }}
TF_VAR_supabase_key: ${{ secrets.SUPABASE_KEY }}
TF_VAR_supabase_db_url: ${{ secrets.SUPABASE_DB_URL }}
TF_VAR_logfire_token: ${{ secrets.LOGFIRE_TOKEN }}
run: terraform apply -auto-approve
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build and push Docker image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: ${{ vars.ECR_REPOSITORY_NAME }}
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:latest
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
- name: Update ECS service (API)
run: |
aws ecs update-service \
--cluster ${{ vars.ECS_CLUSTER_NAME }} \
--service ${{ vars.ECS_API_SERVICE_NAME }} \
--force-new-deployment \
--region ${{ vars.AWS_REGION }}
- name: Update ECS service (Worker)
run: |
aws ecs update-service \
--cluster ${{ vars.ECS_CLUSTER_NAME }} \
--service ${{ vars.ECS_WORKER_SERVICE_NAME }} \
--force-new-deployment \
--region ${{ vars.AWS_REGION }}
- name: Monitor API deployment
run: |
echo "=== Monitoring API deployment ==="
for i in {1..30}; do
STATUS=$(aws ecs describe-services \
--cluster ${{ vars.ECS_CLUSTER_NAME }} \
--services ${{ vars.ECS_API_SERVICE_NAME }} \
--region ${{ vars.AWS_REGION }} \
--query 'services[0].deployments[0].{Status:rolloutState,Running:runningCount,Desired:desiredCount,Pending:pendingCount}' \
--output json)
echo "[$i/30] $(date +%H:%M:%S) - $STATUS"
# Get latest task and show its logs
TASK_ARN=$(aws ecs list-tasks \
--cluster ${{ vars.ECS_CLUSTER_NAME }} \
--service-name ${{ vars.ECS_API_SERVICE_NAME }} \
--region ${{ vars.AWS_REGION }} \
--query 'taskArns[0]' \
--output text)
if [ -n "$TASK_ARN" ] && [ "$TASK_ARN" != "None" ]; then
TASK_ID=$(echo $TASK_ARN | rev | cut -d'/' -f1 | rev)
echo "--- Recent logs from task $TASK_ID ---"
aws logs tail /ecs/policyengine-api-v2-alpha \
--log-stream-name-prefix "api/$TASK_ID" \
--since 30s \
--format short \
--region ${{ vars.AWS_REGION }} 2>&1 || echo "No logs available yet"
echo "---"
fi
# Check if stable
if aws ecs describe-services \
--cluster ${{ vars.ECS_CLUSTER_NAME }} \
--services ${{ vars.ECS_API_SERVICE_NAME }} \
--region ${{ vars.AWS_REGION }} \
--query 'services[0].deployments | length(@) == `1`' \
--output text | grep -q "True"; then
echo "API deployment completed!"
break
fi
sleep 20
done
- name: Monitor worker deployment
run: |
echo "=== Monitoring worker deployment ==="
for i in {1..30}; do
STATUS=$(aws ecs describe-services \
--cluster ${{ vars.ECS_CLUSTER_NAME }} \
--services ${{ vars.ECS_WORKER_SERVICE_NAME }} \
--region ${{ vars.AWS_REGION }} \
--query 'services[0].deployments[0].{Status:rolloutState,Running:runningCount,Desired:desiredCount,Pending:pendingCount}' \
--output json)
echo "[$i/30] $(date +%H:%M:%S) - $STATUS"
# Get latest task and show its logs
TASK_ARN=$(aws ecs list-tasks \
--cluster ${{ vars.ECS_CLUSTER_NAME }} \
--service-name ${{ vars.ECS_WORKER_SERVICE_NAME }} \
--region ${{ vars.AWS_REGION }} \
--query 'taskArns[0]' \
--output text)
if [ -n "$TASK_ARN" ] && [ "$TASK_ARN" != "None" ]; then
TASK_ID=$(echo $TASK_ARN | rev | cut -d'/' -f1 | rev)
echo "--- Recent logs from task $TASK_ID ---"
aws logs tail /ecs/policyengine-api-v2-alpha \
--log-stream-name-prefix "worker/$TASK_ID" \
--since 30s \
--format short \
--region ${{ vars.AWS_REGION }} 2>&1 || echo "No logs available yet"
echo "---"
fi
# Check if stable
if aws ecs describe-services \
--cluster ${{ vars.ECS_CLUSTER_NAME }} \
--services ${{ vars.ECS_WORKER_SERVICE_NAME }} \
--region ${{ vars.AWS_REGION }} \
--query 'services[0].deployments | length(@) == `1`' \
--output text | grep -q "True"; then
echo "Worker deployment completed!"
break
fi
sleep 20
done
- name: Final deployment status
run: |
echo "=== Final Deployment Status ==="
echo "API Service:"
aws ecs describe-services \
--cluster ${{ vars.ECS_CLUSTER_NAME }} \
--services ${{ vars.ECS_API_SERVICE_NAME }} \
--region ${{ vars.AWS_REGION }} \
--query 'services[0].{Status:status,Running:runningCount,Desired:desiredCount}' \
--output table
echo ""
echo "Worker Service:"
aws ecs describe-services \
--cluster ${{ vars.ECS_CLUSTER_NAME }} \
--services ${{ vars.ECS_WORKER_SERVICE_NAME }} \
--region ${{ vars.AWS_REGION }} \
--query 'services[0].{Status:status,Running:runningCount,Desired:desiredCount}' \
--output table
- name: Get API endpoint
run: |
echo "=== API Endpoint ==="
TASK_ARN=$(aws ecs list-tasks \
--cluster ${{ vars.ECS_CLUSTER_NAME }} \
--service-name ${{ vars.ECS_API_SERVICE_NAME }} \
--region ${{ vars.AWS_REGION }} \
--desired-status RUNNING \
--query 'taskArns[0]' \
--output text)
if [ -n "$TASK_ARN" ] && [ "$TASK_ARN" != "None" ]; then
TASK_DETAILS=$(aws ecs describe-tasks \
--cluster ${{ vars.ECS_CLUSTER_NAME }} \
--tasks $TASK_ARN \
--region ${{ vars.AWS_REGION }} \
--query 'tasks[0].attachments[0].details')
PUBLIC_IP=$(echo $TASK_DETAILS | jq -r '.[] | select(.name=="networkInterfaceId") | .value' | xargs -I {} aws ec2 describe-network-interfaces --network-interface-ids {} --region ${{ vars.AWS_REGION }} --query 'NetworkInterfaces[0].Association.PublicIp' --output text)
if [ -n "$PUBLIC_IP" ] && [ "$PUBLIC_IP" != "None" ]; then
echo "API is available at: http://$PUBLIC_IP"
echo "Health check: http://$PUBLIC_IP/health"
echo "Documentation: http://$PUBLIC_IP/docs"
else
echo "Could not retrieve public IP"
fi
else
echo "No running API tasks found"
fi