feat: Added final clean up db step #8
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
| # =============================================== | ||
| # CI/CD Pipeline: Pull GHCR image -> Re-tag -> Push to DockerHub -> Deploy with Terraform | ||
| # =============================================== | ||
| # Description: | ||
| # This workflow takes a Docker image from GHCR (GitHub Container Registry) generated by a PR or push, | ||
| # retags it with the DockerHub username, pushes it to DockerHub, and then deploys the latest image | ||
| # to Cloud Run using Terraform. It also handles initial database table creation if necessary. | ||
| # | ||
| # Inputs: | ||
| # - deployment_env: Deployment environment (e.g., staging, production) | ||
| # - image_uri: URI of the image in GHCR (e.g., ghcr.io/org/repo:tag) | ||
| # | ||
| # Outputs: | ||
| # - dockerhub_image: URI of the retagged image pushed to DockerHub | ||
| # =============================================== | ||
| on: | ||
| workflow_call: | ||
| inputs: | ||
| deployment_env: | ||
| required: true | ||
| type: string | ||
| image_uri: # GHCR Docker image URI | ||
| required: true | ||
| type: string | ||
| jobs: | ||
| # ----------------------------------------------- | ||
| # Job: Pull image from GHCR, retag, and push to DockerHub | ||
| # ----------------------------------------------- | ||
| pull-tag-push: | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| dockerhub_image: ${{ steps.push.outputs.image_uri }} | ||
| steps: | ||
| - name: Log in Github container registry | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ghcr.io | ||
| username: ${{ github.actor }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Login to DockerHub | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| username: ${{ secrets.DOCKERHUB_USERNAME }} | ||
| password: ${{ secrets.DOCKERHUB_TOKEN }} | ||
| - name: Extract tag name from GHCR | ||
| id: extract | ||
| run: | | ||
| TAG=$(echo "${{ inputs.image_uri }}" | awk -F':' '{print $NF}') | ||
| echo "tag=${TAG}" >> $GITHUB_OUTPUT | ||
| APP_NAME=$(echo "${{ inputs.image_uri }}" | awk -F'/' '{print $NF}' | cut -d':' -f1) | ||
| echo "app_name=${APP_NAME}" >> $GITHUB_OUTPUT | ||
| - name: Pull -> re-tag -> push | ||
| id: push | ||
| run: | | ||
| PUBLIC_IMAGE="${{ steps.extract.outputs.app_name }}:${{ steps.extract.outputs.tag }}" | ||
| DOCKERHUB_IMAGE="${{ secrets.DOCKERHUB_USERNAME }}/${PUBLIC_IMAGE}" | ||
| docker pull ${{ inputs.image_uri }} | ||
| docker tag ${{ inputs.image_uri }} ${DOCKERHUB_IMAGE} | ||
| docker push ${DOCKERHUB_IMAGE} | ||
| echo "image_uri=${PUBLIC_IMAGE}" >> $GITHUB_OUTPUT # <-- output this way given that github actions will censor secrets | ||
| # ----------------------------------------------- | ||
| # Job: Deploy application to GCP using Terraform | ||
| # ----------------------------------------------- | ||
| deploy: | ||
| needs: pull-tag-push | ||
| runs-on: ubuntu-latest | ||
| environment: ${{ inputs.deployment_env }} | ||
| outputs: | ||
| db_host: ${{ steps.db.outputs.public_ip }} | ||
| api_url: ${{ steps.db.outputs.api_url }} | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - name: Setup Terraform | ||
| uses: hashicorp/setup-terraform@v3 | ||
| - name: Auth GCP | ||
| uses: google-github-actions/auth@v2 | ||
| with: | ||
| credentials_json: ${{ secrets.GCP_SA_KEY }} | ||
| - name: Init Terraform | ||
| working-directory: terraform | ||
| run: terraform init | ||
| - name: Use deployment env workspace | ||
| working-directory: terraform | ||
| run: | | ||
| terraform workspace select ${{ inputs.deployment_env }} || \ | ||
| terraform workspace new ${{ inputs.deployment_env }} | ||
| - name: Debug dockerhub_image | ||
| run: | | ||
| echo "DockerHub image from previous job: '${{ needs.pull-tag-push.outputs.dockerhub_image }}'" | ||
| - name: Terraform plan | ||
| working-directory: terraform | ||
| run: | | ||
| terraform plan \ | ||
| -var-file=environments/${{ inputs.deployment_env }}.tfvars \ | ||
| -var="project_id=${{ secrets.GCP_PROJECT_ID }}" \ | ||
| -var="db_name=${{ secrets.DB_NAME }}" \ | ||
| -var="db_user=${{ secrets.DB_USER }}" \ | ||
| -var="db_password=${{ secrets.DB_PASSWORD }}" \ | ||
| -var="image_uri=docker.io/${{ secrets.DOCKERHUB_USERNAME }}/${{ needs.pull-tag-push.outputs.dockerhub_image }}" \ | ||
| -out=tfplan | ||
| - name: Terraform Apply | ||
| working-directory: terraform | ||
| run: terraform apply tfplan | ||
| - name: Get Terraform outputs | ||
| id: db | ||
| working-directory: terraform | ||
| run: | | ||
| echo "public_ip=$(terraform output -raw db_host)" >> $GITHUB_OUTPUT | ||
| echo "api_url=$(terraform output -raw cloud_run_service_url)" >> $GITHUB_OUTPUT | ||
| - name: Check if tickets table exists | ||
| id: check_table | ||
| env: | ||
| PGPASSWORD: ${{ secrets.DB_PASSWORD }} | ||
| run: | | ||
| # install postgres client | ||
| sudo apt-get update && sudo apt-get install -y postgresql-client | ||
| TABLE_EXISTS=$(psql -h ${{ steps.db.outputs.public_ip }} \ | ||
| -U ${{ secrets.DB_USER }} \ | ||
| -d ${{ secrets.DB_NAME }} \ | ||
| -tAc "SELECT EXISTS (SELECT FROM pg_tables WHERE tablename = 'tickets');" \ | ||
| ) | ||
| echo "table_exist=$TABLE_EXISTS" >> $GITHUB_OUTPUT | ||
| - name: Create tables and populate (tickets table not existent) | ||
| if: steps.check_table.outputs.table_exist == 'f' | ||
| env: | ||
| PGPASSWORD: ${{ secrets.DB_PASSWORD }} | ||
| run: | | ||
| # if something fails stop | ||
| set -e | ||
| for file in $(ls src/db/scripts/init/*.sql | sort -V); do | ||
| echo "> Executing: $file" | ||
| psql -h ${{ steps.db.outputs.public_ip }} \ | ||
| -U ${{ secrets.DB_USER }} \ | ||
| -d ${{ secrets.DB_NAME }} \ | ||
| -f "$file" \ | ||
| -v DB_NAME=${{ secrets.DB_NAME }} \ | ||
| -v ON_ERROR_STOP=1 | ||
| done | ||
| - name: Skipped table creation (existent tickets table) | ||
| if: steps.check_table.outputs.table_exist == 't' | ||
| run: echo "" | ||
| run-integration-tests: | ||
| needs: deploy | ||
| uses: ./.github/workflows/integration-tests.yml | ||
| with: | ||
| deployment_env: ${{ inputs.deployment_env }} | ||
| db_host: ${{ needs.deploy.outputs.db_host }} | ||
| api_url: ${{ needs.deploy.outputs.api_url }} | ||
| secrets: inherit # pragma: allowlist secret | ||
| cleanup-database: | ||
| needs: run-integration-tests | ||
| runs-on: ubuntu-latest | ||
| secrets: inherit # pragma: allowlist secret | ||
| if: always() && github.event.inputs.deployment_env != 'local' # run even if integration test fail | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - name: Cleanup SPs and tables | ||
| env: | ||
| PGPASSWORD: ${{ secrets.DB_PASSWORD }} | ||
| run: | | ||
| # install postgres client | ||
| sudo apt-get update && sudo apt-get install -y postgresql-client | ||
| # if something fails stop | ||
| set -e | ||
| for file in $(ls src/db/scripts/cleanup/*.sql | sort -V); do | ||
| echo "> Executing: $file" | ||
| psql -h ${{ steps.db.outputs.public_ip }} \ | ||
| -U ${{ secrets.DB_USER }} \ | ||
| -d ${{ secrets.DB_NAME }} \ | ||
| -f "$file" \ | ||
| -v DB_NAME=${{ secrets.DB_NAME }} \ | ||
| -v ON_ERROR_STOP=1 | ||
| done | ||