name: PR Deployment cleanup on: pull_request_target: types: [opened, synchronize, reopened, closed] permissions: contents: read jobs: cleanup: # Tearing a preview down is not a deployment - no deployment object. environment: name: pr-preview deployment: false if: github.event.action == 'closed' runs-on: ubuntu-latest permissions: contents: read # actions/checkout pull-requests: write issues: write # list/remove labels, list/delete comments steps: - name: Harden Runner uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 with: egress-policy: audit - name: Checkout PR uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Remove 'pr-deployed' label if present id: remove-label-comment uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: github-token: ${{ github.token }} script: | const prNumber = ${{ github.event.pull_request.number }}; const owner = context.repo.owner; const repo = context.repo.repo; // Get all labels on the PR const { data: labels } = await github.rest.issues.listLabelsOnIssue({ owner, repo, issue_number: prNumber }); const hasLabel = labels.some(label => label.name === 'pr-deployed'); if (hasLabel) { console.log("Label 'pr-deployed' found. Removing..."); await github.rest.issues.removeLabel({ owner, repo, issue_number: prNumber, name: 'pr-deployed' }); } else { console.log("Label 'pr-deployed' not found. Nothing to do."); } // Find existing bot comments about the deployment const { data: comments } = await github.rest.issues.listComments({ owner, repo, issue_number: prNumber }); const deploymentComments = comments.filter(c => c.body?.includes("## 🚀 PR Test Deployment") && c.user?.type === "Bot" ); if (deploymentComments.length > 0) { for (const comment of deploymentComments) { await github.rest.issues.deleteComment({ owner, repo, comment_id: comment.id }); console.log(`Deleted deployment comment (ID: ${comment.id})`); } } else { console.log("No matching deployment comments found."); } // Set flag if either label or comment was present const hasDeploymentComment = deploymentComments.length > 0; core.setOutput('present', (hasLabel || hasDeploymentComment) ? 'true' : 'false'); - name: Set up SSH if: steps.remove-label-comment.outputs.present == 'true' run: | mkdir -p ~/.ssh/ echo "${NEW_VPS_SSH_KEY}" > ../private.key sudo chmod 600 ../private.key env: NEW_VPS_SSH_KEY: ${{ secrets.NEW_VPS_SSH_KEY }} - name: Convert repository owner to lowercase id: repoowner run: echo "lowercase=$(echo ${{ github.repository_owner }} | awk '{print tolower($0)}')" >> $GITHUB_OUTPUT - name: Cleanup PR deployment if: steps.remove-label-comment.outputs.present == 'true' id: cleanup # ENDSSH heredoc is quoted, so its body is sent literally: secrets inside it # must stay as GitHub expressions, a shell var would be empty on the remote host. run: | ssh -i ../private.key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -T ${NEW_VPS_USERNAME}@${NEW_VPS_HOST} << 'ENDSSH' if [ -d "/stirling/PR-${{ github.event.pull_request.number }}" ]; then echo "Found PR directory, proceeding with cleanup..." # Stop and remove containers cd /stirling/PR-${{ github.event.pull_request.number }} docker-compose down || true # Go back to root before removal cd / # Remove PR-specific directories rm -rf /stirling/PR-${{ github.event.pull_request.number }} # Remove the Docker images docker rmi --no-prune ghcr.io/${{ steps.repoowner.outputs.lowercase }}/stirling-pdf-test:pr-${{ github.event.pull_request.number }} || true docker rmi --no-prune ghcr.io/${{ steps.repoowner.outputs.lowercase }}/stirling-pdf-test:engine-pr-${{ github.event.pull_request.number }} || true echo "PERFORMED_CLEANUP" else echo "PR directory not found, nothing to clean up" echo "NO_CLEANUP_NEEDED" fi ENDSSH env: NEW_VPS_USERNAME: ${{ secrets.NEW_VPS_USERNAME }} NEW_VPS_HOST: ${{ secrets.NEW_VPS_HOST }} - name: Cleanup temporary files if: always() run: | echo "Cleaning up temporary files..." rm -f ../private.key echo "Cleanup complete." continue-on-error: true