Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
300 changes: 300 additions & 0 deletions .github/workflows/deploy-data-dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,300 @@
name: Deploy Data Reply DEV Instance

on:
workflow_dispatch:
inputs:
force_build:
description: 'Force rebuild all images'
type: boolean
default: true


permissions:
id-token: write
contents: read

defaults:
run:
shell: bash
working-directory: ./terraform/litellm/aws/

env:
AWS_REGION: eu-central-1
IMAGE_TAG: ${{ github.sha }}

jobs:
terraform:
name: Terraform Deploy DEV
if: github.ref == 'refs/heads/datareply/dev'
runs-on: ubuntu-latest

steps:
- name: Checkout Code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
with:
fetch-depth: 0

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4.3.1
with:
audience: sts.amazonaws.com
role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/github-oidc-provider-role
role-session-name: github-actions
aws-region: ${{ env.AWS_REGION }}

- name: Setup Terraform
uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2
with:
terraform_version: "1.15.7"

- name: Detect image-related changes
id: image_changes
env:
BEFORE_SHA: ${{ github.event.before }}
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
if [ "${{ inputs.force_build }}" = "true" ]; then
base_ref="" # force all builds
else
base_ref="HEAD^"
fi
elif [ "${{ github.event_name }}" = "push" ] && \
[ -n "$BEFORE_SHA" ] && \
[ "$BEFORE_SHA" != "0000000000000000000000000000000000000000" ]; then
base_ref="$BEFORE_SHA"
elif git rev-parse --verify HEAD^ >/dev/null 2>&1; then
base_ref="HEAD^"
else
base_ref=""
fi

if [ -z "$base_ref" ]; then
{
echo "gateway_changed=true"
echo "backend_changed=true"
echo "ui_changed=true"
echo "migrations_changed=true"
} >> "$GITHUB_OUTPUT"
exit 0
fi

if git diff --quiet "$base_ref"...HEAD -- \
.dockerignore \
gateway/ \
litellm/ \
litellm-proxy-extras/ \
enterprise/ \
pyproject.toml \
uv.lock \
schema.prisma; then
gateway_changed=false
else
gateway_changed=true
fi

if git diff --quiet "$base_ref"...HEAD -- \
.dockerignore \
backend/ \
litellm/ \
litellm-proxy-extras/ \
enterprise/ \
pyproject.toml \
uv.lock \
schema.prisma; then
backend_changed=false
else
backend_changed=true
fi

if git diff --quiet "$base_ref"...HEAD -- \
.dockerignore \
ui/; then
ui_changed=false
else
ui_changed=true
fi

if git diff --quiet "$base_ref"...HEAD -- \
.dockerignore \
migrations/ \
litellm/ \
litellm-proxy-extras/ \
enterprise/ \
pyproject.toml \
uv.lock \
schema.prisma; then
migrations_changed=false
else
migrations_changed=true
fi

{
echo "gateway_changed=$gateway_changed"
echo "backend_changed=$backend_changed"
echo "ui_changed=$ui_changed"
echo "migrations_changed=$migrations_changed"
} >> "$GITHUB_OUTPUT"

- name: Terraform Init
run: terraform init -backend-config=config/dev/backend.tfbackend

- name: Terraform Validate
run: terraform validate

- name: Bootstrap ECR repositories
run: |
terraform apply -auto-approve \
-var-file=config/dev/terraform.tfvars \
-target=aws_ecr_repository.gateway \
-target=aws_ecr_repository.backend \
-target=aws_ecr_repository.ui \
-target=aws_ecr_repository.migrations

- name: Read ECR repository URLs
id: ecr
run: |
{
echo "gateway_repo=$(terraform output -raw gateway_ecr_repository_url)"
echo "backend_repo=$(terraform output -raw backend_ecr_repository_url)"
echo "ui_repo=$(terraform output -raw ui_ecr_repository_url)"
echo "migrations_repo=$(terraform output -raw migrations_ecr_repository_url)"
} >> "$GITHUB_OUTPUT"

- name: Resolve ECS image refs
id: image_refs
env:
GATEWAY_CHANGED: ${{ steps.image_changes.outputs.gateway_changed }}
BACKEND_CHANGED: ${{ steps.image_changes.outputs.backend_changed }}
UI_CHANGED: ${{ steps.image_changes.outputs.ui_changed }}
MIGRATIONS_CHANGED: ${{ steps.image_changes.outputs.migrations_changed }}
GATEWAY_CANDIDATE: ${{ steps.ecr.outputs.gateway_repo }}:${{ env.IMAGE_TAG }}
BACKEND_CANDIDATE: ${{ steps.ecr.outputs.backend_repo }}:${{ env.IMAGE_TAG }}
UI_CANDIDATE: ${{ steps.ecr.outputs.ui_repo }}:${{ env.IMAGE_TAG }}
MIGRATIONS_CANDIDATE: ${{ steps.ecr.outputs.migrations_repo }}:${{ env.IMAGE_TAG }}
run: |
state_resources="$(terraform state list 2>/dev/null || true)"

current_image() {
terraform show -json 2>/dev/null \
| jq -r --arg res "$1" '
.values.root_module.resources[]
| select(.address == $res)
| .values.container_definitions
| fromjson
| .[0].image // empty
' \
| head -n1
}

gateway_current_image="$(current_image aws_ecs_task_definition.gateway)"
backend_current_image="$(current_image aws_ecs_task_definition.backend)"
ui_current_image="$(current_image aws_ecs_task_definition.ui)"
migrations_current_image="$(current_image aws_ecs_task_definition.migrations)"

if [ "$GATEWAY_CHANGED" = "true" ] || [ -z "$gateway_current_image" ]; then
gateway_build=true
gateway_image="$GATEWAY_CANDIDATE"
else
gateway_build=false
gateway_image="$gateway_current_image"
fi

if [ "$BACKEND_CHANGED" = "true" ] || [ -z "$backend_current_image" ]; then
backend_build=true
backend_image="$BACKEND_CANDIDATE"
else
backend_build=false
backend_image="$backend_current_image"
fi

if [ "$UI_CHANGED" = "true" ] || [ -z "$ui_current_image" ]; then
ui_build=true
ui_image="$UI_CANDIDATE"
else
ui_build=false
ui_image="$ui_current_image"
fi

if [ "$MIGRATIONS_CHANGED" = "true" ] || [ -z "$migrations_current_image" ]; then
migrations_build=true
migrations_image="$MIGRATIONS_CANDIDATE"
else
migrations_build=false
migrations_image="$migrations_current_image"
fi

if [ "$gateway_build" = "true" ] || [ "$backend_build" = "true" ] || [ "$ui_build" = "true" ] || [ "$migrations_build" = "true" ]; then
any_build=true
else
any_build=false
fi

{
echo "any_build=$any_build"
echo "gateway_build=$gateway_build"
echo "backend_build=$backend_build"
echo "ui_build=$ui_build"
echo "migrations_build=$migrations_build"
echo "gateway_image=$gateway_image"
echo "backend_image=$backend_image"
echo "ui_image=$ui_image"
echo "migrations_image=$migrations_image"
} >> "$GITHUB_OUTPUT"

- name: Set up Docker Buildx
if: steps.image_refs.outputs.any_build == 'true'
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0

- name: Login to Amazon ECR
if: steps.image_refs.outputs.any_build == 'true'
uses: aws-actions/amazon-ecr-login@4625ce35226a7557230889aae2f52eb50ec3dcda # v2.0.1

- name: Build and push gateway image
if: steps.image_refs.outputs.gateway_build == 'true'
uses: docker/build-push-action@0adf9959216b96bec444f325f1e493d4aa344497 # v6.14.0
with:
context: .
file: ./gateway/Dockerfile
push: true
tags: ${{ steps.ecr.outputs.gateway_repo }}:${{ env.IMAGE_TAG }}

- name: Build and push backend image
if: steps.image_refs.outputs.backend_build == 'true'
uses: docker/build-push-action@0adf9959216b96bec444f325f1e493d4aa344497 # v6.14.0
with:
context: .
file: ./backend/Dockerfile
push: true
tags: ${{ steps.ecr.outputs.backend_repo }}:${{ env.IMAGE_TAG }}

- name: Build and push UI image
if: steps.image_refs.outputs.ui_build == 'true'
uses: docker/build-push-action@0adf9959216b96bec444f325f1e493d4aa344497 # v6.14.0
with:
context: .
file: ./ui/Dockerfile
push: true
tags: ${{ steps.ecr.outputs.ui_repo }}:${{ env.IMAGE_TAG }}

- name: Build and push migrations image
if: steps.image_refs.outputs.migrations_build == 'true'
uses: docker/build-push-action@0adf9959216b96bec444f325f1e493d4aa344497 # v6.14.0
with:
context: .
file: ./migrations/Dockerfile
push: true
tags: ${{ steps.ecr.outputs.migrations_repo }}:${{ env.IMAGE_TAG }}

- name: Terraform Plan
run: |
terraform plan \
-var-file=config/dev/terraform.tfvars \
-var="gateway_image=${{ steps.image_refs.outputs.gateway_image }}" \
-var="backend_image=${{ steps.image_refs.outputs.backend_image }}" \
-var="ui_image=${{ steps.image_refs.outputs.ui_image }}" \
-var="migrations_image=${{ steps.image_refs.outputs.migrations_image }}" \
-out=tfplan

- name: Terraform Apply
if: github.ref == 'refs/heads/datareply/dev'
run: terraform apply -auto-approve tfplan
4 changes: 4 additions & 0 deletions terraform/litellm/aws/config/dev/backend.tfbackend
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
bucket = "data-prod-ai-llm-gateway-terraform-751812493785-eu-central-1-an"
key = "litellm/dev/terraform.tfstate"
region = "eu-central-1"

69 changes: 69 additions & 0 deletions terraform/litellm/aws/config/dev/terraform.tfvars
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
region = "eu-central-1"
azs = ["eu-central-1a", "eu-central-1b"]

tenant = "data-reply"
env = "dev"

ecr_force_delete = true

vpc_id = ""
vpc_cidr = "10.41.0.0/16"

gateway_desired_count = 1
backend_desired_count = 1
ui_desired_count = 1
gateway_autoscaling_enabled = false
backend_autoscaling_enabled = false
ui_autoscaling_enabled = false

create_database = true
db_enable_reader = false
db_writer_instance_class = "db.serverless"
db_serverless_min_capacity = 0.5
db_serverless_max_capacity = 2
db_engine_version = "16.11"

db_randomize_final_snapshot_name = true

create_redis = true
redis_num_replicas = 0

acm_certificate_domain_name = "litellm-dev.datareply.de"
route53_zone_id = "Z0891217221E1PTCMOPWZ"

proxy_config = {
model_list = [
{
model_name = "gpt-6-luna"
litellm_params = {
model = "openai/gpt-6-luna"
api_key = "os.environ/OPENAI_API_KEY"
}
}
]
general_settings = {
master_key = "os.environ/LITELLM_MASTER_KEY"
database_url = "os.environ/DATABASE_URL"
store_prompts_in_spend_logs = false
}
litellm_settings = {
route_all_chat_openai_to_responses = true
redact_messages_in_exceptions = true
ui_theme_config = {
logo_url = "https://www.reply.com/favicon.ico"
favicon_url = "https://www.reply.com/favicon.ico"
}
}
}

backend_extra_env = {
PROXY_BASE_URL = "https://litellm-dev.datareply.de"
}

gateway_extra_secrets = {
OPENAI_API_KEY = "arn:aws:secretsmanager:eu-central-1:751812493785:secret:data-reply/litellm/openai-api-key-nNpCF1"
}

/*backend_extra_secrets = {
OPENAI_API_KEY = "arn:aws:secretsmanager:eu-central-1:751812493785:secret:data-reply/litellm/openai-api-key-nNpCF1"
}*/
Loading
Loading