betagouv/service-national-universel

View on GitHub
.github/workflows/deploy-ci.yml

Summary

Maintainability
Test Coverage
name: Build and deploy CI

on:
  workflow_dispatch:
  push:
    branches:
      - main

jobs:
  prepare:
    runs-on: ubuntu-latest
    if: github.ref_name == 'main'
    outputs:
      target_branch: ${{ steps.target_branch.outputs.target_branch }}
      api_image_tag: ${{ steps.prepare.outputs.api_image_tag }}
      admin_image_tag: ${{ steps.prepare.outputs.admin_image_tag }}
      app_image_tag: ${{ steps.prepare.outputs.app_image_tag }}
      antivirus_image_tag: ${{ steps.prepare.outputs.antivirus_image_tag }}

    steps:
      - name: Get target_branch
        id: target_branch
        run: |
          echo "target_branch=${{ github.ref_name }}" >> $GITHUB_OUTPUT

      - uses: actions/checkout@v4
        with:
          ref: ${{ steps.target_branch.outputs.target_branch }}
          fetch-depth: 0

      - name: Prepare
        id: prepare
        run: |
          api_image_tag=$(git log --max-count=1 --oneline -- api packages package-lock.json | cut -d " " -f 1)
          echo "api_image_tag: $api_image_tag"
          echo "api_image_tag=$api_image_tag" >> $GITHUB_OUTPUT

          admin_image_tag=$(git log --max-count=1 --oneline -- admin packages package-lock.json | cut -d " " -f 1)
          echo "admin_image_tag: $admin_image_tag"
          echo "admin_image_tag=$admin_image_tag" >> $GITHUB_OUTPUT

          app_image_tag=$(git log --max-count=1 --oneline -- app packages package-lock.json | cut -d " " -f 1)
          echo "app_image_tag: $app_image_tag"
          echo "app_image_tag=$app_image_tag" >> $GITHUB_OUTPUT

          antivirus_image_tag=$(git log --max-count=1 --oneline -- antivirus package-lock.json | cut -d " " -f 1)
          echo "antivirus_image_tag: $antivirus_image_tag"
          echo "antivirus_image_tag=$antivirus_image_tag" >> $GITHUB_OUTPUT

  run_tests_api:
    needs: prepare
    uses: ./.github/workflows/run-tests-api.yml
    with:
      branch_name: ${{ needs.prepare.outputs.target_branch }}
    secrets: inherit

  run_tests_front:
    needs: prepare
    uses: ./.github/workflows/run-tests-front.yml
    with:
      branch_name: ${{ needs.prepare.outputs.branch_name }}
    secrets: inherit

  run_tests_lib:
    needs: prepare
    uses: ./.github/workflows/run-tests-lib.yml
    with:
      branch_name: ${{ needs.prepare.outputs.branch_name }}
    secrets: inherit

  build:
    needs: [prepare, run_tests_api, run_tests_front, run_tests_lib]
    runs-on: ubuntu-latest
    strategy:
      matrix:
        app:
          [
            {
              name: api,
              path: api,
              tag: "${{needs.prepare.outputs.api_image_tag}}",
            },
            {
              name: app,
              path: app,
              tag: "${{needs.prepare.outputs.app_image_tag}}",
            },
            {
              name: admin,
              path: admin,
              tag: "${{needs.prepare.outputs.admin_image_tag}}",
            },
            {
              name: antivirus,
              path: devops/antivirus,
              tag: "${{needs.prepare.outputs.antivirus_image_tag}}",
            },
          ]

    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ needs.prepare.outputs.target_branch }}

      - name: Check if image exists
        id: check
        uses: ./.github/actions/check_docker_image_tag
        with:
          registry: ${{ secrets.SCW_CI_IMAGE_REGISTRY }}
          image_name: ${{ matrix.app.name }}
          image_tag: ${{ matrix.app.tag }}
          secret_key: ${{ secrets.SCW_CI_DEPLOY_SECRET_KEY }}

      - name: Docker Build & Publish
        uses: ./.github/actions/build_docker_image
        if: steps.check.outputs.tag_exists == 0
        with:
          username: nologin
          password: ${{ secrets.SCW_CI_DEPLOY_SECRET_KEY }}
          registry: ${{ secrets.SCW_CI_IMAGE_REGISTRY }}
          image_name: ${{ matrix.app.name }}
          dockerfile_path: ${{ matrix.app.path }}/Dockerfile
          image_tag: ${{ matrix.app.tag }}
          sentry_auth_token: ${{ secrets.SENTRY_AUTH_TOKEN }}

  deploy:
    runs-on: ubuntu-latest
    needs: [prepare, build]
    concurrency:
      group: ${{ github.workflow }}
      cancel-in-progress: false
    env:
      SCW_ACCESS_KEY: ${{ secrets.SCW_CI_DEPLOY_ACCESS_KEY }}
      SCW_SECRET_KEY: ${{ secrets.SCW_CI_DEPLOY_SECRET_KEY }}
      PG_CONN_STR: ${{ secrets.SCW_CI_BACKEND_CONN_STR }}
      PGUSER: ${{ secrets.SCW_CI_BACKEND_USER }}
      PGPASSWORD: ${{ secrets.SCW_CI_BACKEND_PASSWORD }}
      ci_directory: ./devops/terraform/environments/ci

    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ needs.prepare.outputs.target_branch }}

      - uses: hashicorp/setup-terraform@v3

      - name: Init terraform
        working-directory: ${{ env.ci_directory }}
        run: terraform init

      - name: Validate terraform
        working-directory: ${{ env.ci_directory }}
        run: terraform validate -no-color

      - name: Terraform plan
        working-directory: ${{ env.ci_directory }}
        # add -lock-timeout option until https://github.com/hashicorp/terraform/issues/33217 is fixed
        run: |
          terraform plan -no-color -input=false  -lock-timeout=15m \
            -var="api_image_tag=${{ needs.prepare.outputs.api_image_tag }}" \
            -var="admin_image_tag=${{ needs.prepare.outputs.admin_image_tag }}" \
            -var="app_image_tag=${{ needs.prepare.outputs.app_image_tag }}"

      - name: Terraform auto-apply
        working-directory: ${{ env.ci_directory }}
        run: |
          terraform apply -no-color -input=false -auto-approve -lock-timeout=15m \
            -var="api_image_tag=${{ needs.prepare.outputs.api_image_tag }}" \
            -var="admin_image_tag=${{ needs.prepare.outputs.admin_image_tag }}" \
            -var="app_image_tag=${{ needs.prepare.outputs.app_image_tag }}"

      - name: Healthchecks
        shell: bash
        working-directory: ${{ env.ci_directory }}
        run: |
          # Sleep because status can still be an error if the previous deployement failed
          sleep 10s

          failure=false
          while read name
          do
            status=$(terraform output -raw ${name}_container_status)
            echo $name $status
            if [[ $status != "ready" ]]
            then
                echo "Container ${name} is on '$status' state" 1>&2
                failure=true
            fi
          done <<< 'app
          admin
          api'

          if $failure
          then
            exit 1
          fi