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
doctlCLI - 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
- Database Cleanup: Removes the branch-specific database to free up resources
- App Cleanup: Deletes both app and admin services from DigitalOcean App Platform
- Force Deletion: Uses
--forceflag to skip confirmation prompts - 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