This repository was archived by the owner on Feb 24, 2026. It is now read-only.
Deploy API to AWS #74
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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!" |