Skip to Content
InfraGitHubCleanup DO app

Cleanup DigitalOcean App Workflow

This GitHub Action workflow automatically cleans up preview environments from DigitalOcean App Platform when a pull request is closed or a branch is deleted.

Trigger Conditions

  • Runs when pull requests are closed
  • Runs when branches are deleted

Workflow Steps

1. Environment Setup

  • Checks out code
  • Installs and authenticates doctl CLI
  • Sanitizes branch name to match the naming convention used during deployment

2. Database Cleanup

  • Drops the branch-specific database from the droplet
  • Database name follows pattern: {SANITIZED_BRANCH}_iba

3. App Service Cleanup

  • Finds the app service using the branch name
  • Force deletes the app if it exists

4. Admin Service Cleanup

  • Finds the admin service using the branch name
  • Force deletes the admin app if it exists

5. Error Handling

  • Notifies on failure if deletion encounters errors

Complete Workflow

name: Clean Up DigitalOcean App Platform on: pull_request: types: [closed] delete: refs: - branches/** jobs: clean_up: runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@v3 - name: Install doctl uses: digitalocean/action-doctl@v2 with: token: ${{ secrets.DO_API_TOKEN }} - name: Authenticate doctl run: doctl auth init -t ${{ secrets.DO_API_TOKEN }} - name: Sanitize Branch Name id: sanitize_branch run: | ORIGINAL_BRANCH="${{ github.head_ref }}" SANITIZED_BRANCH=$(echo "${ORIGINAL_BRANCH}" | sed 's/[^a-zA-Z0-9]/-/g') SANITIZED_BRANCH=${SANITIZED_BRANCH:0:18} echo "SANITIZED_BRANCH=${SANITIZED_BRANCH}" >> $GITHUB_OUTPUT - name: Drop Database env: SANITIZED_BRANCH: ${{ steps.sanitize_branch.outputs.SANITIZED_BRANCH }} MANAGED_DB_HOST: ${{ secrets.MANAGED_DB_HOST }} MANAGED_DB_PORT: ${{ secrets.MANAGED_DB_PORT }} MANAGED_DB_USER: ${{ secrets.MANAGED_DB_USER }} MANAGED_DB_PASSWORD: ${{ secrets.MANAGED_DB_PASSWORD }} DB_PASSWORD: ${{ secrets.DB_PASSWORD }} DO_DROPLET_IP: ${{ secrets.DO_DROPLET_IP }} DO_DROPLET_PASSWORD: ${{ secrets.DO_DROPLET_PASSWORD }} run: | NEW_DB="${SANITIZED_BRANCH}_iba" echo "New database name: ${NEW_DB}" sshpass -p "${DO_DROPLET_PASSWORD}" ssh -o StrictHostKeyChecking=no root@${DO_DROPLET_IP} << EOF set -ex echo "Dropping existing database \${NEW_DB} (if exists) and creating new database..." docker exec -i mysql-container mysql -uroot -p"${DB_PASSWORD}" -e "DROP DATABASE IF EXISTS \`${NEW_DB}\`" EOF - name: Get App ID id: get_app run: | APP_NAME="${{ steps.sanitize_branch.outputs.SANITIZED_BRANCH }}-app-preview" APP_ID=$(doctl apps list --format Spec.Name,ID --no-header | awk -v app="$APP_NAME" '$1 == app {print $2}') echo "APP_ID=${APP_ID}" >> $GITHUB_OUTPUT - name: Delete App if: steps.get_app.outputs.APP_ID != '' run: | APP_ID="${{ steps.get_app.outputs.APP_ID }}" doctl apps delete "$APP_ID" --force echo "App with ID $APP_ID has been deleted." - name: Get Admin ID id: get_admin run: | ADMIN_NAME="${{ steps.sanitize_branch.outputs.SANITIZED_BRANCH }}-admin-preview" ADMIN_ID=$(doctl apps list --format Spec.Name,ID --no-header | awk -v app="$ADMIN_NAME" '$1 == app {print $2}') echo "ADMIN_ID=${ADMIN_ID}" >> $GITHUB_OUTPUT - name: Delete Admin if: steps.get_admin.outputs.ADMIN_ID != '' run: | echo "${{ steps.get_admin.outputs.ADMIN_ID }}" ADMIN_ID="${{ steps.get_admin.outputs.ADMIN_ID }}" doctl apps delete "$ADMIN_ID" --force echo "App with ID $ADMIN_ID has been deleted." - name: Notify Failure on Deletion Error if: failure() run: | echo "Failed to delete the app. Please check the logs for more details."

Required Secrets

The workflow requires the following GitHub secrets:

  • DigitalOcean: DO_API_TOKEN, DO_DROPLET_IP, DO_DROPLET_PASSWORD
  • Database: MANAGED_DB_HOST, MANAGED_DB_PORT, MANAGED_DB_USER, MANAGED_DB_PASSWORD, DB_PASSWORD

Cleanup Process

  1. Database Cleanup: Removes the branch-specific database to free up resources
  2. App Cleanup: Deletes both app and admin services from DigitalOcean App Platform
  3. Force Deletion: Uses --force flag to skip confirmation prompts
  4. Error Handling: Continues cleanup even if some components don’t exist

Notes

  • The workflow runs automatically when PRs are closed or branches are deleted
  • It uses the same branch name sanitization logic as the deployment workflow
  • Cleanup is idempotent - it safely handles cases where resources don’t exist
Last updated on