Skip to content
This repository was archived by the owner on Feb 24, 2026. It is now read-only.

Deploy API to AWS

Deploy API to AWS #73

Workflow file for this run

name: Deploy API to AWS
on:
push:
branches: [dev-dspot]
tags:
- 'v*'
paths:
- 'apps/api/**/*.*'
- 'packages/auth/**/*.*'
- 'packages/common/**/*.*'
- 'packages/config/**/*.*'
- 'packages/contracts/**/*.*'
- 'packages/core/**/*.*'
- 'packages/desktop-lib/**/*.*'
- 'packages/plugin/**/*.*'
- 'packages/utils/**/*.*'
- 'packages/plugins/changelog/**/*.*'
- 'packages/plugins/integration-ai/**/*.*'
- 'packages/plugins/integration-github/**/*.*'
- 'packages/plugins/integration-hubstaff/**/*.*'
- 'packages/plugins/integration-jira/**/*.*'
- 'packages/plugins/integration-upwork/**/*.*'
- 'packages/plugins/integration-wakatime/**/*.*'
- 'packages/plugins/jitsu-analytics/**/*.*'
- 'packages/plugins/job-proposal/**/*.*'
- 'packages/plugins/job-search/**/*.*'
- 'packages/plugins/knowledge-base/**/*.*'
- 'packages/plugins/product-reviews/**/*.*'
- 'packages/plugins/sentry-tracing/**/*.*'
- 'packages/plugins/videos/**/*.*'
- '.deploy/api/**/*'
- '.github/workflows/aws-deploy-api.yml'
- '.github/workflows/api-dependencies.yml'
- 'package.json'
- 'yarn.lock'
workflow_dispatch:
inputs:
environment:
description: 'Environment to deploy to'
type: choice
required: true
default: 'staging'
options:
- staging
- production
jobs:
check-dependencies:
name: Check and Build Dependencies
uses: ./.github/workflows/api-dependencies.yml
secrets: inherit
sync-infrastructure-config:
name: Sync Infrastructure Configuration
runs-on: ubuntu-latest
environment: ${{ startsWith(github.ref, 'refs/tags/v') && 'production' || 'staging' }}
permissions:
contents: read
id-token: write
outputs:
# Database outputs
db_host: ${{ steps.get-iac-outputs.outputs.db_host }}
db_port: ${{ steps.get-iac-outputs.outputs.db_port }}
db_name: ${{ steps.get-iac-outputs.outputs.db_name }}
db_user: ${{ steps.get-iac-outputs.outputs.db_user }}
# ECS outputs
ecs_cluster: ${{ steps.get-iac-outputs.outputs.ecs_cluster }}
ecs_service_api: ${{ steps.get-iac-outputs.outputs.ecs_service_api }}
ecs_service_webapp: ${{ steps.get-iac-outputs.outputs.ecs_service_webapp }}
ecr_repository_api: ${{ steps.get-iac-outputs.outputs.ecr_repository_api }}
ecr_repository_webapp: ${{ steps.get-iac-outputs.outputs.ecr_repository_webapp }}
ecr_repository_dependencies: ${{ steps.get-iac-outputs.outputs.ecr_repository_dependencies }}
# SSM Parameter paths
db_pass_parameter: ${{ steps.get-iac-outputs.outputs.db_pass_parameter }}
env:
AWS_REGION: ${{ vars.AWS_REGION }}
TERRAFORM_BACKEND_BUCKET: ${{ vars.OPENTOFU_BACKEND_BUCKET }}
TERRAFORM_BACKEND_REGION: ${{ vars.OPENTOFU_BACKEND_REGION }}
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}
- name: Setup OpenTofu
uses: opentofu/setup-opentofu@v1
with:
tofu_version: "~1.9"
tofu_wrapper: false
- name: Retrieve All Infrastructure Configuration from OpenTofu State
id: get-iac-outputs
run: |
# Determine environment-specific backend configuration
ENVIRONMENT="${{ startsWith(github.ref, 'refs/tags/v') && 'production' || 'staging' }}"
TERRAFORM_BACKEND_KEY="hubstaff_to_gauzy_migration/$ENVIRONMENT/terraform.tfstate"
echo "🔧 Initializing OpenTofu for $ENVIRONMENT environment..."
echo "Backend bucket: ${{ env.TERRAFORM_BACKEND_BUCKET }}"
echo "Backend key: $TERRAFORM_BACKEND_KEY"
echo "Backend region: ${{ env.TERRAFORM_BACKEND_REGION }}"
# Create minimal OpenTofu configuration for remote state access
mkdir -p /tmp/tofu-config
cd /tmp/tofu-config
cat > backend.tf << EOF
terraform {
backend "s3" {
bucket = "${{ env.TERRAFORM_BACKEND_BUCKET }}"
key = "$TERRAFORM_BACKEND_KEY"
region = "${{ env.TERRAFORM_BACKEND_REGION }}"
}
}
EOF
# Initialize OpenTofu with remote backend
tofu init
echo "📋 Retrieving all infrastructure outputs..."
# Database configuration
DB_HOST=$(tofu output -raw db_instance_address 2>/dev/null || echo "")
DB_PORT=$(tofu output -raw db_instance_port 2>/dev/null || echo "5432")
DB_NAME=$(tofu output -raw db_instance_name 2>/dev/null || echo "")
DB_USER=$(tofu output -raw db_instance_username 2>/dev/null || echo "")
# ECS configuration
ECS_CLUSTER=$(tofu output -raw ecs_cluster_name 2>/dev/null || echo "")
ECS_SERVICE_API=$(tofu output -raw ecs_backend_service_name 2>/dev/null || echo "")
ECS_SERVICE_WEBAPP=$(tofu output -raw ecs_frontend_service_name 2>/dev/null || echo "")
# ECR repository names (direct from OpenTofu outputs)
ECR_REPOSITORY_API=$(tofu output -raw ecr_api_repository_name 2>/dev/null || echo "")
ECR_REPOSITORY_WEBAPP=$(tofu output -raw ecr_webapp_repository_name 2>/dev/null || echo "")
ECR_REPOSITORY_DEPENDENCIES=$(tofu output -raw ecr_dependency_repository_name 2>/dev/null || echo "")
# SSM Parameter Store paths
DB_PASS_PARAMETER="/hubstaff_to_gauzy_migration/$ENVIRONMENT/db_password"
# Validate critical outputs
if [ -z "$DB_HOST" ] || [ -z "$ECS_CLUSTER" ] || [ -z "$ECR_REPOSITORY_API" ]; then
echo "❌ Critical infrastructure outputs are missing!"
echo "DB_HOST: '$DB_HOST'"
echo "ECS_CLUSTER: '$ECS_CLUSTER'"
echo "ECR_REPOSITORY_API: '$ECR_REPOSITORY_API'"
exit 1
fi
# Set outputs for other jobs
echo "db_host=$DB_HOST" >> $GITHUB_OUTPUT
echo "db_port=$DB_PORT" >> $GITHUB_OUTPUT
echo "db_name=$DB_NAME" >> $GITHUB_OUTPUT
echo "db_user=$DB_USER" >> $GITHUB_OUTPUT
echo "ecs_cluster=$ECS_CLUSTER" >> $GITHUB_OUTPUT
echo "ecs_service_api=$ECS_SERVICE_API" >> $GITHUB_OUTPUT
echo "ecs_service_webapp=$ECS_SERVICE_WEBAPP" >> $GITHUB_OUTPUT
echo "ecr_repository_api=$ECR_REPOSITORY_API" >> $GITHUB_OUTPUT
echo "ecr_repository_webapp=$ECR_REPOSITORY_WEBAPP" >> $GITHUB_OUTPUT
echo "ecr_repository_dependencies=$ECR_REPOSITORY_DEPENDENCIES" >> $GITHUB_OUTPUT
echo "db_pass_parameter=$DB_PASS_PARAMETER" >> $GITHUB_OUTPUT
# Log retrieved configuration (without sensitive data)
echo "✅ Successfully retrieved infrastructure configuration:"
echo "Environment: $ENVIRONMENT"
echo "Database Host: $DB_HOST"
echo "Database Port: $DB_PORT"
echo "Database Name: $DB_NAME"
echo "Database User: $DB_USER"
echo "ECS Cluster: $ECS_CLUSTER"
echo "ECS API Service: $ECS_SERVICE_API"
echo "ECS WebApp Service: $ECS_SERVICE_WEBAPP"
echo "ECR API Repository: $ECR_REPOSITORY_API"
echo "ECR WebApp Repository: $ECR_REPOSITORY_WEBAPP"
echo "ECR Dependencies Repository: $ECR_REPOSITORY_DEPENDENCIES"
# Cleanup
cd /
rm -rf /tmp/tofu-config
run-migrations:
name: Run Database Migrations
needs: [check-dependencies, sync-infrastructure-config]
runs-on: ubuntu-latest
environment: ${{ startsWith(github.ref, 'refs/tags/v') && 'production' || 'staging' }}
permissions:
contents: read
id-token: write
env:
# Static configuration
DB_TYPE: postgres
NODE_ENV: production
DB_SSL_MODE: true
DB_POOL_SIZE: 10
AWS_REGION: ${{ vars.AWS_REGION }}
DB_HOST: ${{ needs.sync-infrastructure-config.outputs.db_host }}
DB_PORT: ${{ needs.sync-infrastructure-config.outputs.db_port }}
DB_NAME: ${{ needs.sync-infrastructure-config.outputs.db_name }}
DB_USER: ${{ needs.sync-infrastructure-config.outputs.db_user }}
DB_PASS_PARAMETER: ${{ needs.sync-infrastructure-config.outputs.db_pass_parameter }}
ECR_REGISTRY: ${{ vars.ECR_REGISTRY }}
ECR_REPOSITORY_DEPENDENCIES: ${{ needs.sync-infrastructure-config.outputs.ecr_repository_dependencies }}
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Retrieve Database Password from Parameter Store
id: get-db-password
run: |
echo "🔐 Retrieving database password from Parameter Store..."
DB_PASS=$(aws ssm get-parameter --name "${{ env.DB_PASS_PARAMETER }}" --with-decryption --query 'Parameter.Value' --output text)
echo "::add-mask::$DB_PASS"
echo "DB_PASS=$DB_PASS" >> $GITHUB_ENV
- name: Run Database Migrations using Dependencies Image
run: |
echo "🚀 Running database migrations..."
docker run --rm \
-e DB_TYPE="${{ env.DB_TYPE }}" \
-e DB_HOST="${{ env.DB_HOST }}" \
-e DB_PORT="${{ env.DB_PORT }}" \
-e DB_NAME="${{ env.DB_NAME }}" \
-e DB_USER="${{ env.DB_USER }}" \
-e DB_PASS="${{ env.DB_PASS }}" \
-e DB_SSL_MODE="${{ env.DB_SSL_MODE }}" \
-e DB_POOL_SIZE="${{ env.DB_POOL_SIZE }}" \
-e NODE_ENV="${{ env.NODE_ENV }}" \
-v ${{ github.workspace }}:/workspace \
-w /workspace \
${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY_DEPENDENCIES }}:latest-api-dev \
sh -c "yarn migration:run"
deploy-api:
name: Deploy API to AWS
needs: [check-dependencies, sync-infrastructure-config, run-migrations]
runs-on: ubuntu-latest
environment: ${{ startsWith(github.ref, 'refs/tags/v') && 'production' || 'staging' }}
permissions:
contents: read
id-token: write
env:
AWS_REGION: ${{ vars.AWS_REGION }}
ECR_REGISTRY: ${{ vars.ECR_REGISTRY }}
ECR_REPOSITORY_API: ${{ needs.sync-infrastructure-config.outputs.ecr_repository_api }}
ECR_REPOSITORY_DEPENDENCIES: ${{ needs.sync-infrastructure-config.outputs.ecr_repository_dependencies }}
ECS_CLUSTER: ${{ needs.sync-infrastructure-config.outputs.ecs_cluster }}
ECS_SERVICE_API: ${{ needs.sync-infrastructure-config.outputs.ecs_service_api }}
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Build and tag API image
env:
IMAGE_TAG: ${{ github.sha }}
run: |
echo "🔨 Building API Docker image..."
docker build \
--build-arg GIT_HASH="${{ github.sha }}" \
--build-arg DEPENDENCIES_IMAGE="${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY_DEPENDENCIES }}" \
-t ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY_API }}:$IMAGE_TAG \
-t ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY_API }}:latest \
-f .deploy/api/Dockerfile \
.
- name: Push API image to Amazon ECR
env:
IMAGE_TAG: ${{ github.sha }}
run: |
echo "📤 Pushing API image to ECR..."
docker push ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY_API }} --all-tags
echo "api_image=${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY_API }}:$IMAGE_TAG" >> $GITHUB_ENV
- name: Update API ECS service
run: |
echo "🚀 Deploying API to ECS..."
echo "Cluster: ${{ env.ECS_CLUSTER }}"
echo "Service: ${{ env.ECS_SERVICE_API }}"
aws ecs update-service \
--cluster ${{ env.ECS_CLUSTER }} \
--service ${{ env.ECS_SERVICE_API }} \
--force-new-deployment
echo "⏳ Waiting for deployment to stabilize..."
aws ecs wait services-stable \
--cluster ${{ env.ECS_CLUSTER }} \
--services ${{ env.ECS_SERVICE_API }}
echo "✅ API deployment completed successfully!"