improvement(ci): ensure atomicity in trigger deploys, improve overall ci organization (#1477)

* improvement(chat): deployed chat no longer uses subdomains, uses sim.ai/chat/[identifier]

* improvement(ci): ensure atomicity in trigger deploys, improve overall ci organization

* Revert "improvement(chat): deployed chat no longer uses subdomains, uses sim.ai/chat/[identifier]"

This reverts commit c68c052601.

---------

Co-authored-by: waleed <waleed>
This commit is contained in:
Waleed
2025-09-27 22:11:06 -07:00
committed by GitHub
parent 5ee6345120
commit 38dd2d0f23
8 changed files with 340 additions and 686 deletions

View File

@@ -1,262 +0,0 @@
name: Build and Push to ECR
on:
workflow_call:
workflow_dispatch:
permissions:
id-token: write
contents: read
jobs:
build-and-push-ecr:
strategy:
fail-fast: false
matrix:
include:
- dockerfile: ./docker/app.Dockerfile
ecr_repo_secret: ECR_APP
service_type: app
- dockerfile: ./docker/db.Dockerfile
ecr_repo_secret: ECR_MIGRATIONS
service_type: core
- dockerfile: ./docker/realtime.Dockerfile
ecr_repo_secret: ECR_REALTIME
service_type: monitoring
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ github.ref == 'refs/heads/main' && secrets.AWS_ROLE_TO_ASSUME || secrets.STAGING_AWS_ROLE_TO_ASSUME }}
aws-region: ${{ github.ref == 'refs/heads/main' && secrets.AWS_REGION || secrets.STAGING_AWS_REGION }}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx
uses: useblacksmith/setup-docker-builder@v1
- name: Generate image tags
id: meta
run: |
ECR_REGISTRY="${{ steps.login-ecr.outputs.registry }}"
ECR_REPO="${{ secrets[matrix.ecr_repo_secret] }}"
# Simple tagging: :latest for main, :staging for staging
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
TAG="latest"
else
TAG="staging"
fi
FULL_IMAGE="${ECR_REGISTRY}/${ECR_REPO}:${TAG}"
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "full_image=$FULL_IMAGE" >> $GITHUB_OUTPUT
- name: Build and push Docker image
uses: useblacksmith/build-push-action@v2
with:
context: .
file: ${{ matrix.dockerfile }}
push: true
tags: ${{ steps.meta.outputs.full_image }}
platforms: linux/amd64
provenance: false
sbom: false
update-ecs-services:
needs: build-and-push-ecr
runs-on: blacksmith-4vcpu-ubuntu-2404
strategy:
fail-fast: false
matrix:
stack_type: [APP, CORE, MONITORING]
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ github.ref == 'refs/heads/main' && secrets.AWS_ROLE_TO_ASSUME || secrets.STAGING_AWS_ROLE_TO_ASSUME }}
aws-region: ${{ github.ref == 'refs/heads/main' && secrets.AWS_REGION || secrets.STAGING_AWS_REGION }}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Determine stack and image details
id: stack
run: |
ECR_REGISTRY="${{ steps.login-ecr.outputs.registry }}"
# Determine tag based on environment
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
TAG="latest"
else
TAG="staging"
fi
# Map stack type to ECR repo
case "${{ matrix.stack_type }}" in
APP)
ECR_REPO="${{ secrets.ECR_APP }}"
;;
CORE)
ECR_REPO="${{ secrets.ECR_MIGRATIONS }}"
;;
MONITORING)
ECR_REPO="${{ secrets.ECR_REALTIME }}"
;;
esac
# Full image URI with simple tag
IMAGE_URI="${ECR_REGISTRY}/${ECR_REPO}:${TAG}"
echo "image=$IMAGE_URI" >> $GITHUB_OUTPUT
- name: Get stack name
id: stack-name
run: |
# Use conditional expressions to get stack name based on branch and type
APP_STACK="${{ github.ref == 'refs/heads/main' && secrets.PROD_APP_STACK || secrets.STAGING_APP_STACK }}"
CORE_STACK="${{ github.ref == 'refs/heads/main' && secrets.PROD_CORE_STACK || secrets.STAGING_CORE_STACK }}"
MONITORING_STACK="${{ github.ref == 'refs/heads/main' && secrets.PROD_MONITORING_STACK || secrets.STAGING_MONITORING_STACK }}"
# Select the appropriate stack based on matrix type
case "${{ matrix.stack_type }}" in
APP)
STACK_NAME="$APP_STACK"
;;
CORE)
STACK_NAME="$CORE_STACK"
;;
MONITORING)
STACK_NAME="$MONITORING_STACK"
;;
esac
echo "Updating stack: $STACK_NAME"
echo "name=$STACK_NAME" >> $GITHUB_OUTPUT
- name: Get ECS services from stack
id: ecs-services
run: |
# Get all ECS services from the stack
SERVICES=$(aws cloudformation describe-stack-resources \
--stack-name "${{ steps.stack-name.outputs.name }}" \
--query "StackResources[?ResourceType=='AWS::ECS::Service'].PhysicalResourceId" \
--output text 2>/dev/null || echo "")
if [ -z "$SERVICES" ]; then
echo "No ECS services found in stack ${{ steps.stack-name.outputs.name }}"
echo "services=" >> $GITHUB_OUTPUT
else
echo "Found services: $SERVICES"
echo "services=$SERVICES" >> $GITHUB_OUTPUT
fi
- name: Update ECS services
if: steps.ecs-services.outputs.services != ''
run: |
SERVICES="${{ steps.ecs-services.outputs.services }}"
for SERVICE_ARN in $SERVICES; do
echo "Updating service: $SERVICE_ARN"
# Extract cluster name from service ARN
CLUSTER_NAME=$(echo $SERVICE_ARN | cut -d'/' -f2)
SERVICE_NAME=$(echo $SERVICE_ARN | cut -d'/' -f3)
# Get the current task definition
TASK_DEF_ARN=$(aws ecs describe-services \
--cluster "$CLUSTER_NAME" \
--services "$SERVICE_NAME" \
--query "services[0].taskDefinition" \
--output text)
# Get the task definition details
TASK_DEF=$(aws ecs describe-task-definition \
--task-definition "$TASK_DEF_ARN" \
--query "taskDefinition")
# Update the image in the task definition
# For Ubuntu ECS, container definitions may have multiple containers
NEW_TASK_DEF=$(echo "$TASK_DEF" | jq --arg IMAGE "${{ steps.stack.outputs.image }}" \
'.containerDefinitions |= map(
if .essential == true then
.image = $IMAGE
else . end
) |
del(.taskDefinitionArn) |
del(.revision) |
del(.status) |
del(.requiresAttributes) |
del(.compatibilities) |
del(.registeredAt) |
del(.registeredBy)')
# Register new task definition
NEW_TASK_ARN=$(aws ecs register-task-definition \
--cli-input-json "$NEW_TASK_DEF" \
--query "taskDefinition.taskDefinitionArn" \
--output text)
echo "Registered new task definition: $NEW_TASK_ARN"
# Update service with new task definition
aws ecs update-service \
--cluster "$CLUSTER_NAME" \
--service "$SERVICE_NAME" \
--task-definition "$NEW_TASK_ARN" \
--force-new-deployment
echo "Service update initiated for $SERVICE_NAME"
done
- name: Wait for service stability
if: steps.ecs-services.outputs.services != ''
run: |
SERVICES="${{ steps.ecs-services.outputs.services }}"
for SERVICE_ARN in $SERVICES; do
CLUSTER_NAME=$(echo $SERVICE_ARN | cut -d'/' -f2)
SERVICE_NAME=$(echo $SERVICE_ARN | cut -d'/' -f3)
echo "Waiting for service $SERVICE_NAME to stabilize..."
# Wait up to 30 minutes for service to stabilize
ATTEMPTS=0
MAX_ATTEMPTS=120
while [ $ATTEMPTS -lt $MAX_ATTEMPTS ]; do
DEPLOYMENT_STATUS=$(aws ecs describe-services \
--cluster "$CLUSTER_NAME" \
--services "$SERVICE_NAME" \
--query "services[0].deployments[?status=='PRIMARY'].rolloutState" \
--output text)
if [ "$DEPLOYMENT_STATUS" = "COMPLETED" ]; then
echo "✅ Service $SERVICE_NAME updated successfully!"
break
fi
echo "Deployment status: $DEPLOYMENT_STATUS (attempt $((ATTEMPTS+1))/$MAX_ATTEMPTS)"
sleep 15
ATTEMPTS=$((ATTEMPTS+1))
done
if [ $ATTEMPTS -eq $MAX_ATTEMPTS ]; then
echo "⚠️ Service $SERVICE_NAME did not stabilize within timeout"
fi
done

View File

@@ -1,329 +0,0 @@
name: Unified Build and Push
on:
workflow_call:
workflow_dispatch:
permissions:
contents: read
packages: write
id-token: write
jobs:
build-shared-amd64:
name: Build AMD64 (Shared for GHCR + ECR)
runs-on: linux-x64-8-core
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
strategy:
fail-fast: false
matrix:
include:
- dockerfile: ./docker/app.Dockerfile
ghcr_image: ghcr.io/simstudioai/simstudio
ecr_repo_secret: ECR_APP
- dockerfile: ./docker/db.Dockerfile
ghcr_image: ghcr.io/simstudioai/migrations
ecr_repo_secret: ECR_MIGRATIONS
- dockerfile: ./docker/realtime.Dockerfile
ghcr_image: ghcr.io/simstudioai/realtime
ecr_repo_secret: ECR_REALTIME
outputs:
registry: ${{ steps.login-ecr.outputs.registry }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: useblacksmith/setup-docker-builder@v1
- name: Generate tags
id: meta
run: |
ECR_REGISTRY="${{ steps.login-ecr.outputs.registry }}"
ECR_REPO="${{ secrets[matrix.ecr_repo_secret] }}"
GHCR_IMAGE="${{ matrix.ghcr_image }}"
# ECR tags
ECR_IMAGE="${ECR_REGISTRY}/${ECR_REPO}:latest"
# GHCR tags
GHCR_AMD64="${GHCR_IMAGE}:latest-amd64"
GHCR_SHA="${GHCR_IMAGE}:${{ github.sha }}-amd64"
echo "ecr_image=${ECR_IMAGE}" >> $GITHUB_OUTPUT
echo "ghcr_tags=${GHCR_AMD64},${GHCR_SHA}" >> $GITHUB_OUTPUT
- name: Build and push to both registries
uses: useblacksmith/build-push-action@v2
with:
context: .
file: ${{ matrix.dockerfile }}
platforms: linux/amd64
push: true
tags: |
${{ steps.meta.outputs.ecr_image }}
${{ steps.meta.outputs.ghcr_tags }}
provenance: false
sbom: false
build-ghcr-arm64:
name: Build ARM64 (GHCR Only)
runs-on: linux-arm64-8-core
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
strategy:
fail-fast: false
matrix:
include:
- dockerfile: ./docker/app.Dockerfile
image: ghcr.io/simstudioai/simstudio
- dockerfile: ./docker/db.Dockerfile
image: ghcr.io/simstudioai/migrations
- dockerfile: ./docker/realtime.Dockerfile
image: ghcr.io/simstudioai/realtime
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: useblacksmith/setup-docker-builder@v1
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ matrix.image }}
tags: |
type=raw,value=latest-arm64
type=sha,format=long,suffix=-arm64
- name: Build and push ARM64 to GHCR
uses: useblacksmith/build-push-action@v2
with:
context: .
file: ${{ matrix.dockerfile }}
platforms: linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
provenance: false
sbom: false
update-ecs-services:
name: Update ECS Services
needs: build-shared-amd64
runs-on: blacksmith-4vcpu-ubuntu-2404
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
strategy:
fail-fast: false
matrix:
stack_type: [APP, CORE, MONITORING]
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Determine stack and image details
id: stack
run: |
ECR_REGISTRY="${{ needs.build-shared-amd64.outputs.registry }}"
# Map stack type to ECR repo
case "${{ matrix.stack_type }}" in
APP)
ECR_REPO="${{ secrets.ECR_APP }}"
;;
CORE)
ECR_REPO="${{ secrets.ECR_MIGRATIONS }}"
;;
MONITORING)
ECR_REPO="${{ secrets.ECR_REALTIME }}"
;;
esac
IMAGE_URI="${ECR_REGISTRY}/${ECR_REPO}:latest"
echo "image=$IMAGE_URI" >> $GITHUB_OUTPUT
- name: Get stack name
id: stack-name
run: |
APP_STACK="${{ secrets.PROD_APP_STACK }}"
CORE_STACK="${{ secrets.PROD_CORE_STACK }}"
MONITORING_STACK="${{ secrets.PROD_MONITORING_STACK }}"
case "${{ matrix.stack_type }}" in
APP)
STACK_NAME="$APP_STACK"
;;
CORE)
STACK_NAME="$CORE_STACK"
;;
MONITORING)
STACK_NAME="$MONITORING_STACK"
;;
esac
echo "name=$STACK_NAME" >> $GITHUB_OUTPUT
- name: Get ECS services from stack
id: ecs-services
run: |
SERVICES=$(aws cloudformation describe-stack-resources \
--stack-name "${{ steps.stack-name.outputs.name }}" \
--query "StackResources[?ResourceType=='AWS::ECS::Service'].PhysicalResourceId" \
--output text 2>/dev/null || echo "")
if [ -z "$SERVICES" ]; then
echo "services=" >> $GITHUB_OUTPUT
else
echo "services=$SERVICES" >> $GITHUB_OUTPUT
fi
- name: Update ECS services
if: steps.ecs-services.outputs.services != ''
run: |
SERVICES="${{ steps.ecs-services.outputs.services }}"
for SERVICE_ARN in $SERVICES; do
CLUSTER_NAME=$(echo $SERVICE_ARN | cut -d'/' -f2)
SERVICE_NAME=$(echo $SERVICE_ARN | cut -d'/' -f3)
TASK_DEF_ARN=$(aws ecs describe-services \
--cluster "$CLUSTER_NAME" \
--services "$SERVICE_NAME" \
--query "services[0].taskDefinition" \
--output text)
TASK_DEF=$(aws ecs describe-task-definition \
--task-definition "$TASK_DEF_ARN" \
--query "taskDefinition")
NEW_TASK_DEF=$(echo "$TASK_DEF" | jq --arg IMAGE "${{ steps.stack.outputs.image }}" \
'.containerDefinitions |= map(
if .essential == true then
.image = $IMAGE
else . end
) |
del(.taskDefinitionArn) |
del(.revision) |
del(.status) |
del(.requiresAttributes) |
del(.compatibilities) |
del(.registeredAt) |
del(.registeredBy)')
NEW_TASK_ARN=$(aws ecs register-task-definition \
--cli-input-json "$NEW_TASK_DEF" \
--query "taskDefinition.taskDefinitionArn" \
--output text)
aws ecs update-service \
--cluster "$CLUSTER_NAME" \
--service "$SERVICE_NAME" \
--task-definition "$NEW_TASK_ARN" \
--force-new-deployment
done
- name: Wait for service stability
if: steps.ecs-services.outputs.services != ''
run: |
SERVICES="${{ steps.ecs-services.outputs.services }}"
for SERVICE_ARN in $SERVICES; do
CLUSTER_NAME=$(echo $SERVICE_ARN | cut -d'/' -f2)
SERVICE_NAME=$(echo $SERVICE_ARN | cut -d'/' -f3)
ATTEMPTS=0
MAX_ATTEMPTS=120
while [ $ATTEMPTS -lt $MAX_ATTEMPTS ]; do
DEPLOYMENT_STATUS=$(aws ecs describe-services \
--cluster "$CLUSTER_NAME" \
--services "$SERVICE_NAME" \
--query "services[0].deployments[?status=='PRIMARY'].rolloutState" \
--output text)
if [ "$DEPLOYMENT_STATUS" = "COMPLETED" ]; then
echo "✅ Service $SERVICE_NAME updated successfully!"
break
fi
sleep 15
ATTEMPTS=$((ATTEMPTS+1))
done
done
create-ghcr-manifests:
name: Create GHCR Manifests
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: [build-shared-amd64, build-ghcr-arm64]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
strategy:
matrix:
include:
- image: ghcr.io/simstudioai/simstudio
- image: ghcr.io/simstudioai/migrations
- image: ghcr.io/simstudioai/realtime
permissions:
contents: read
packages: write
steps:
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create and push manifest
run: |
IMAGE_BASE="${{ matrix.image }}"
# Create latest manifest
docker manifest create "${IMAGE_BASE}:latest" \
"${IMAGE_BASE}:latest-amd64" \
"${IMAGE_BASE}:latest-arm64"
docker manifest push "${IMAGE_BASE}:latest"
# Create SHA manifest
docker manifest create "${IMAGE_BASE}:${{ github.sha }}" \
"${IMAGE_BASE}:${{ github.sha }}-amd64" \
"${IMAGE_BASE}:${{ github.sha }}-arm64"
docker manifest push "${IMAGE_BASE}:${{ github.sha }}"

View File

@@ -11,81 +11,27 @@ concurrency:
cancel-in-progress: false
jobs:
test:
test-build:
name: Test and Build
runs-on: blacksmith-4vcpu-ubuntu-2404
uses: ./.github/workflows/test-build.yml
secrets: inherit
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: latest
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Run tests with coverage
env:
NODE_OPTIONS: '--no-warnings'
NEXT_PUBLIC_APP_URL: 'https://www.sim.ai'
DATABASE_URL: 'postgresql://postgres:postgres@localhost:5432/simstudio'
ENCRYPTION_KEY: '7cf672e460e430c1fba707575c2b0e2ad5a99dddf9b7b7e3b5646e630861db1c' # dummy key for CI only
run: bun run test
- name: Build application
env:
NODE_OPTIONS: '--no-warnings'
NEXT_PUBLIC_APP_URL: 'https://www.sim.ai'
DATABASE_URL: 'postgresql://postgres:postgres@localhost:5432/simstudio'
STRIPE_SECRET_KEY: 'dummy_key_for_ci_only'
STRIPE_WEBHOOK_SECRET: 'dummy_secret_for_ci_only'
RESEND_API_KEY: 'dummy_key_for_ci_only'
AWS_REGION: 'us-west-2'
ENCRYPTION_KEY: '7cf672e460e430c1fba707575c2b0e2ad5a99dddf9b7b7e3b5646e630861db1c' # dummy key for CI only
run: bun run build
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
directory: ./apps/sim/coverage
fail_ci_if_error: false
verbose: true
# Main branch: build (ECR + GHCR)
build:
name: Build (ECR + GHCR)
needs: test
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: ./.github/workflows/build.yml
# Build and push images (ECR for staging, ECR + GHCR for main)
build-images:
name: Build Images
needs: test-build
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging')
uses: ./.github/workflows/images.yml
secrets: inherit
permissions:
contents: read
packages: write
id-token: write
# Staging branch: ECR only
build-ecr-deploy:
name: Build ECR and Deploy (Staging)
needs: test
if: github.event_name == 'push' && github.ref == 'refs/heads/staging'
uses: ./.github/workflows/build-ecr.yml
secrets: inherit
permissions:
id-token: write
contents: read
# Call Trigger.dev deploy workflow (runs in parallel)
# Deploy Trigger.dev with skip-promotion (after builds complete to ensure atomicity)
trigger-deploy:
name: Deploy Trigger.dev
needs: test
needs: build-images
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging')
uses: ./.github/workflows/trigger-deploy.yml
secrets: inherit
@@ -93,37 +39,28 @@ jobs:
# Run database migrations (depends on build completion and trigger deployment)
migrations:
name: Apply Database Migrations
needs: [build-ecr-deploy, build, trigger-deploy]
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: [build-images, trigger-deploy]
if: |
always() &&
github.event_name == 'push' &&
(github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging') &&
(needs.build-ecr-deploy.result == 'success' || needs.build.result == 'success') &&
needs.build-images.result == 'success' &&
needs.trigger-deploy.result == 'success'
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: ./.github/workflows/migrations.yml
secrets: inherit
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install dependencies
run: bun install
- name: Apply migrations
working-directory: ./packages/db
env:
DATABASE_URL: ${{ github.ref == 'refs/heads/main' && secrets.DATABASE_URL || secrets.STAGING_DATABASE_URL }}
run: bunx drizzle-kit migrate --config=./drizzle.config.ts
# Promote Trigger.dev deployment after ECS completes
trigger-promote:
name: Promote Trigger.dev
needs: migrations
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging')
uses: ./.github/workflows/trigger-promote.yml
secrets: inherit
# Process docs embeddings if needed
process-docs:
name: Process Docs
needs: migrations
needs: trigger-promote
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging')
uses: ./.github/workflows/docs-embeddings.yml
secrets: inherit

184
.github/workflows/images.yml vendored Normal file
View File

@@ -0,0 +1,184 @@
name: Build and Push Images
on:
workflow_call:
workflow_dispatch:
permissions:
contents: read
packages: write
id-token: write
jobs:
build-amd64:
name: Build AMD64
runs-on: blacksmith-4vcpu-ubuntu-2404
strategy:
fail-fast: false
matrix:
include:
- dockerfile: ./docker/app.Dockerfile
ghcr_image: ghcr.io/simstudioai/simstudio
ecr_repo_secret: ECR_APP
- dockerfile: ./docker/db.Dockerfile
ghcr_image: ghcr.io/simstudioai/migrations
ecr_repo_secret: ECR_MIGRATIONS
- dockerfile: ./docker/realtime.Dockerfile
ghcr_image: ghcr.io/simstudioai/realtime
ecr_repo_secret: ECR_REALTIME
outputs:
registry: ${{ steps.login-ecr.outputs.registry }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ github.ref == 'refs/heads/main' && secrets.AWS_ROLE_TO_ASSUME || secrets.STAGING_AWS_ROLE_TO_ASSUME }}
aws-region: ${{ github.ref == 'refs/heads/main' && secrets.AWS_REGION || secrets.STAGING_AWS_REGION }}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR
if: github.ref == 'refs/heads/main'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: useblacksmith/setup-docker-builder@v1
- name: Generate tags
id: meta
run: |
ECR_REGISTRY="${{ steps.login-ecr.outputs.registry }}"
ECR_REPO="${{ secrets[matrix.ecr_repo_secret] }}"
GHCR_IMAGE="${{ matrix.ghcr_image }}"
# ECR tags (always build for ECR)
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
ECR_TAG="latest"
else
ECR_TAG="staging"
fi
ECR_IMAGE="${ECR_REGISTRY}/${ECR_REPO}:${ECR_TAG}"
# Build tags list
TAGS="${ECR_IMAGE}"
# Add GHCR tags only for main branch
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
GHCR_AMD64="${GHCR_IMAGE}:latest-amd64"
GHCR_SHA="${GHCR_IMAGE}:${{ github.sha }}-amd64"
TAGS="${TAGS},$GHCR_AMD64,$GHCR_SHA"
fi
echo "tags=${TAGS}" >> $GITHUB_OUTPUT
- name: Build and push images
uses: useblacksmith/build-push-action@v2
with:
context: .
file: ${{ matrix.dockerfile }}
platforms: linux/amd64
push: true
tags: ${{ steps.meta.outputs.tags }}
provenance: false
sbom: false
build-ghcr-arm64:
name: Build ARM64 (GHCR Only)
runs-on: linux-arm64-8-core
if: github.ref == 'refs/heads/main'
strategy:
fail-fast: false
matrix:
include:
- dockerfile: ./docker/app.Dockerfile
image: ghcr.io/simstudioai/simstudio
- dockerfile: ./docker/db.Dockerfile
image: ghcr.io/simstudioai/migrations
- dockerfile: ./docker/realtime.Dockerfile
image: ghcr.io/simstudioai/realtime
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: useblacksmith/setup-docker-builder@v1
- name: Generate ARM64 tags
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ matrix.image }}
tags: |
type=raw,value=latest-arm64
type=sha,format=long,suffix=-arm64
- name: Build and push ARM64 to GHCR
uses: useblacksmith/build-push-action@v2
with:
context: .
file: ${{ matrix.dockerfile }}
platforms: linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
provenance: false
sbom: false
create-ghcr-manifests:
name: Create GHCR Manifests
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: [build-amd64, build-ghcr-arm64]
if: github.ref == 'refs/heads/main'
strategy:
matrix:
include:
- image: ghcr.io/simstudioai/simstudio
- image: ghcr.io/simstudioai/migrations
- image: ghcr.io/simstudioai/realtime
steps:
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create and push manifests
run: |
IMAGE_BASE="${{ matrix.image }}"
# Create latest manifest
docker manifest create "${IMAGE_BASE}:latest" \
"${IMAGE_BASE}:latest-amd64" \
"${IMAGE_BASE}:latest-arm64"
docker manifest push "${IMAGE_BASE}:latest"
# Create SHA manifest
docker manifest create "${IMAGE_BASE}:${{ github.sha }}" \
"${IMAGE_BASE}:${{ github.sha }}-amd64" \
"${IMAGE_BASE}:${{ github.sha }}-arm64"
docker manifest push "${IMAGE_BASE}:${{ github.sha }}"

28
.github/workflows/migrations.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: Database Migrations
on:
workflow_call:
workflow_dispatch:
jobs:
migrate:
name: Apply Database Migrations
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install dependencies
run: bun install
- name: Apply migrations
working-directory: ./packages/db
env:
DATABASE_URL: ${{ github.ref == 'refs/heads/main' && secrets.DATABASE_URL || secrets.STAGING_DATABASE_URL }}
run: bunx drizzle-kit migrate --config=./drizzle.config.ts

54
.github/workflows/test-build.yml vendored Normal file
View File

@@ -0,0 +1,54 @@
name: Test and Build
on:
workflow_call:
workflow_dispatch:
jobs:
test-build:
name: Test and Build
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: latest
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Run tests with coverage
env:
NODE_OPTIONS: '--no-warnings'
NEXT_PUBLIC_APP_URL: 'https://www.sim.ai'
DATABASE_URL: 'postgresql://postgres:postgres@localhost:5432/simstudio'
ENCRYPTION_KEY: '7cf672e460e430c1fba707575c2b0e2ad5a99dddf9b7b7e3b5646e630861db1c' # dummy key for CI only
run: bun run test
- name: Build application
env:
NODE_OPTIONS: '--no-warnings'
NEXT_PUBLIC_APP_URL: 'https://www.sim.ai'
DATABASE_URL: 'postgresql://postgres:postgres@localhost:5432/simstudio'
STRIPE_SECRET_KEY: 'dummy_key_for_ci_only'
STRIPE_WEBHOOK_SECRET: 'dummy_secret_for_ci_only'
RESEND_API_KEY: 'dummy_key_for_ci_only'
AWS_REGION: 'us-west-2'
ENCRYPTION_KEY: '7cf672e460e430c1fba707575c2b0e2ad5a99dddf9b7b7e3b5646e630861db1c' # dummy key for CI only
run: bun run build
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
directory: ./apps/sim/coverage
fail_ci_if_error: false
verbose: true

View File

@@ -6,21 +6,22 @@ on:
jobs:
deploy:
name: Trigger.dev Deploy
name: Deploy to Trigger.dev
runs-on: blacksmith-4vcpu-ubuntu-2404
concurrency:
group: trigger-deploy-${{ github.ref }}
cancel-in-progress: false
env:
TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN }}
steps:
- name: Checkout repository
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 'lts/*'
node-version: latest
- name: Setup Bun
uses: oven-sh/setup-bun@v2
@@ -30,13 +31,12 @@ jobs:
- name: Install dependencies
run: bun install
- name: Deploy to Staging
- name: Deploy to Trigger.dev (Staging)
if: github.ref == 'refs/heads/staging'
working-directory: ./apps/sim
run: npx --yes trigger.dev@4.0.4 deploy -e staging
run: npx --yes trigger.dev@4.0.4 deploy -e staging --skip-promotion
- name: Deploy to Production
- name: Deploy to Trigger.dev (Production)
if: github.ref == 'refs/heads/main'
working-directory: ./apps/sim
run: npx --yes trigger.dev@4.0.4 deploy
run: npx --yes trigger.dev@4.0.4 deploy --skip-promotion

42
.github/workflows/trigger-promote.yml vendored Normal file
View File

@@ -0,0 +1,42 @@
name: Trigger.dev Promote
on:
workflow_call:
workflow_dispatch:
jobs:
promote:
name: Promote Trigger.dev
runs-on: blacksmith-4vcpu-ubuntu-2404
concurrency:
group: trigger-promote-${{ github.ref }}
cancel-in-progress: false
env:
TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: latest
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install dependencies
run: bun install
- name: Promote Trigger.dev (Staging)
if: github.ref == 'refs/heads/staging'
working-directory: ./apps/sim
run: npx --yes trigger.dev@4.0.4 deploy promote --latest -e staging
- name: Promote Trigger.dev (Production)
if: github.ref == 'refs/heads/main'
working-directory: ./apps/sim
run: npx --yes trigger.dev@4.0.4 deploy promote --latest