diff --git a/.github/workflows/deploy-ecs.yml b/.github/workflows/deploy-ecs.yml new file mode 100644 index 0000000000..05d93bba10 --- /dev/null +++ b/.github/workflows/deploy-ecs.yml @@ -0,0 +1,120 @@ +name: Deploy to AWS ECS +on: + push: + branches: + - staging-alt3 + +# used to configure IAM to trust Github's OIDC provider +permissions: + id-token: write + contents: read + +jobs: + set_environment: + name: Set environment for deployment + description: Sets the environment for the deployment, which is the same as the branch name + outputs: + current_env: ${{ steps.set_environment.outputs.current_env }} + runs-on: ubuntu-latest + steps: + - id: set_environment + run: echo "current_env=${{github.ref_name}}" >> $GITHUB_OUTPUT + + deploy: + name: Deploy to ECS + needs: set_environment + runs-on: ubuntu-latest + environment: ${{ needs.set_environment.outputs.current_env }} + env: + IMAGE_TAG: github-actions-${{ github.sha }}-${{ github.run_id }}-${{github.run_attempt}} + CURRENT_ENV: ${{ needs.set_environment.outputs.current_env }} + steps: + - name: Checkout branch source code into runner environment + description: Required for the frontend build env vars + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup secrets for datadog sourcemap deployment + run: | + echo "APP_VERSION=$(jq -r .version package.json)-$(echo ${GITHUB_REF##*/})-$(echo ${GITHUB_SHA} | cut -c1-8)" >> $GITHUB_ENV + + - name: Inject frontend build env vars + env: + VITE_APP_DD_RUM_APP_ID: ${{ secrets.DD_RUM_APP_ID }} + VITE_APP_DD_RUM_CLIENT_TOKEN: ${{ secrets.DD_RUM_CLIENT_TOKEN }} + VITE_APP_DD_RUM_ENV: ${{ secrets.DD_ENV }} + VITE_APP_DD_SAMPLE_RATE: ${{ secrets.DD_SAMPLE_RATE }} + VITE_APP_GA_TRACKING_ID: ${{ secrets.GA_TRACKING_ID }} + VITE_APP_FORMSG_SDK_MODE: ${{ secrets.REACT_APP_FORMSG_SDK_MODE }} + VITE_APP_URL: ${{ secrets.APP_URL }} + run: | + sed -i -e "s|@VITE_APP_URL|${{secrets.APP_URL}}|g" -e "s/@VITE_APP_DD_RUM_APP_ID/$VITE_APP_DD_RUM_APP_ID/g" -e "s/@VITE_APP_DD_RUM_CLIENT_TOKEN/$VITE_APP_DD_RUM_CLIENT_TOKEN/g" -e "s/@VITE_APP_DD_RUM_ENV/$VITE_APP_DD_RUM_ENV/g" -e "s/@VITE_APP_VERSION/${{env.APP_VERSION}}/g" -e "s/@VITE_APP_DD_SAMPLE_RATE/$VITE_APP_DD_SAMPLE_RATE/g" frontend/datadog-chunk.ts + echo VITE_APP_VERSION=${{env.APP_VERSION}} > frontend/.env + echo VITE_APP_URL=$VITE_APP_URL > frontend/.env + echo VITE_APP_GA_TRACKING_ID=$VITE_APP_GA_TRACKING_ID >> frontend/.env + echo VITE_APP_FORMSG_SDK_MODE=$VITE_APP_FORMSG_SDK_MODE >> frontend/.env + echo VITE_APP_DD_RUM_CLIENT_TOKEN=$VITE_APP_DD_RUM_CLIENT_TOKEN >> frontend/.env + echo VITE_APP_DD_RUM_ENV=$VITE_APP_DD_RUM_ENV >> frontend/.env + + - name: Configure AWS credentials + description: Configures the runner environment with AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + env: + AWS_REGION: ${{ secrets.DEFAULT_AWS_REGION }} + with: + role-to-assume: ${{ secrets.AWS_CI_ROLE_TO_ASSUME }} + aws-region: ${{ env.AWS_REGION }} + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push Docker image + uses: docker/build-push-action@v6 + env: + DD_API_KEY: ${{ secrets.DD_API_KEY }} + DD_ENV: ${{ secrets.DD_ENV }} + ECR_REPOSITORY: ${{ secrets.ECR_REPO }}-${{ env.CURRENT_ENV }} + with: + context: . + file: Dockerfile.production + push: true + tags: | + ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }} + ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:latest + build-args: | + APP_VERSION=${{ env.APP_VERSION }} + APP_URL=${{ secrets.APP_URL }} + REPO_URL=${{ github.server_url }}/${{ github.repository }} + secrets: | + "dd_api_key=${{ secrets.DD_API_KEY }}" + + - name: Fill in the new image ID in the Amazon ECS task definition + description: Create a new task definition file with the image to be deployed + id: task-def + env: + ECS_TASK_DEFINITION: ecs-task-definition.json + CONTAINER_NAME: formsg-app + uses: aws-actions/amazon-ecs-render-task-definition@c804dfbdd57f713b6c079302a4c01db7017a36fc + with: + task-definition: ${{ env.ECS_TASK_DEFINITION }} + container-name: ${{ env.CONTAINER_NAME }} + image: ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }} + environment-variables: | + ENV_TYPE=${{ contains(env.CURRENT_ENV, 'staging') && 'staging' || contains(env.CURRENT_ENV, 'prod') && 'prod' || contains(env.CURRENT_ENV, 'uat') && 'uat' || env.CURRENT_ENV }} + ENV_SITE_NAME=${{ env.CURRENT_ENV }} + + - name: Deploy Amazon ECS task definition + env: # For ECS deployment + ECS_SERVICE: ${{ sectets.ECS_SERVICE }} + ECS_CLUSTER: ${{ secrets.ECS_CLUSTER }} + uses: aws-actions/amazon-ecs-deploy-task-definition@df9643053eda01f169e64a0e60233aacca83799a + with: + task-definition: ${{ steps.task-def.outputs.task-definition }} + service: ${{ env.ECS_SERVICE }} + cluster: ${{ env.ECS_CLUSTER }} + wait-for-service-stability: true \ No newline at end of file diff --git a/ecs-task-definition.json b/ecs-task-definition.json new file mode 100644 index 0000000000..f8164adcf0 --- /dev/null +++ b/ecs-task-definition.json @@ -0,0 +1,17 @@ + +{ + "containerDefinitions": [ + { + "name": "formsg-app", + "essential": true, + "command": "/bin/sh -c \"./generate-env-from-ecs-params.sh && npm start\"", + "portMappings": [ + { "containerPort": 3000 } + ] + } + ], + "family": "formsg-app", + "requiresCompatibilities": [ + "FARGATE" + ] +} \ No newline at end of file diff --git a/generate-env-from-ecs-params.sh b/generate-env-from-ecs-params.sh new file mode 100644 index 0000000000..72100fade9 --- /dev/null +++ b/generate-env-from-ecs-params.sh @@ -0,0 +1,31 @@ +TARGET_DIR=/etc/formsg +ENV_TYPE=$ENV_TYPE +ENV_SITE_NAME=$ENV_SITE_NAME + +# create target dir if not exist +echo "Checking if ${TARGET_DIR} exists..." +if [ ! -d ${TARGET_DIR} ]; then + echo "Creating directory ${TARGET_DIR} ..." + mkdir -p ${TARGET_DIR} + if [ $? -ne 0 ]; then + echo 'ERROR: Directory creation failed!' + exit 1 + fi +else + echo "Directory ${TARGET_DIR} already exists!" +fi + +echo "${ENV_TYPE}-general" > $TARGET_DIR/.env +echo "${ENV_TYPE}-captcha" >> $TARGET_DIR/.env +echo "${ENV_TYPE}-turnstile" >> $TARGET_DIR/.env +echo "${ENV_TYPE}-ga" >> $TARGET_DIR/.env +echo "${ENV_TYPE}-intranet" >> $TARGET_DIR/.env +echo "${ENV_TYPE}-sms" >> $TARGET_DIR/.env +echo "${ENV_TYPE}-ndi" >> $TARGET_DIR/.env +echo "${ENV_TYPE}-verified-fields" >> $TARGET_DIR/.env +echo "${ENV_TYPE}-webhook-verified-content" >> $TARGET_DIR/.env +echo "${ENV_TYPE}-wogaa" >> $TARGET_DIR/.env +echo "${ENV_SITE_NAME}-sgid" >> $TARGET_DIR/.env +echo "${ENV_SITE_NAME}-payment" >> $TARGET_DIR/.env +echo "${ENV_SITE_NAME}-cron-payment" >> $TARGET_DIR/.env +echo "${ENV_SITE_NAME}-openai" >> $TARGET_DIR/.env