diff --git a/.github/actions/delete-control-plane-app/action.yml b/.github/actions/delete-control-plane-app/action.yml new file mode 100644 index 00000000..caaef272 --- /dev/null +++ b/.github/actions/delete-control-plane-app/action.yml @@ -0,0 +1,20 @@ +name: Delete Control Plane App +description: 'Deletes a Control Plane application and all its resources' + +inputs: + app_name: + description: 'Name of the application to delete' + required: true + cpln_org: + description: 'Organization name' + required: true + +runs: + using: "composite" + steps: + - name: Delete Application + shell: bash + run: ${{ github.action_path }}/delete-app.sh + env: + APP_NAME: ${{ inputs.app_name }} + CPLN_ORG: ${{ inputs.cpln_org }} diff --git a/.github/actions/delete-control-plane-app/delete-app.sh b/.github/actions/delete-control-plane-app/delete-app.sh new file mode 100755 index 00000000..6bc92bfc --- /dev/null +++ b/.github/actions/delete-control-plane-app/delete-app.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Script to delete a Control Plane application +# Required environment variables: +# - APP_NAME: Name of the application to delete +# - CPLN_ORG: Organization name + +set -e + +# Validate required environment variables +: "${APP_NAME:?APP_NAME environment variable is required}" +: "${CPLN_ORG:?CPLN_ORG environment variable is required}" + +# Safety check: prevent deletion of production or staging apps +if echo "$APP_NAME" | grep -iqE '(production|staging)'; then + echo "❌ ERROR: Cannot delete apps containing 'production' or 'staging' in their name" >&2 + echo "🛑 This is a safety measure to prevent accidental deletion of production or staging environments" >&2 + echo " App name: $APP_NAME" >&2 + exit 1 +fi + +# Check if app exists before attempting to delete +echo "🔍 Checking if application exists: $APP_NAME" +if ! cpflow exists -a "$APP_NAME" --org "$CPLN_ORG"; then + echo "⚠️ Application does not exist: $APP_NAME" + exit 0 +fi + +# Delete the application +echo "🗑️ Deleting application: $APP_NAME" +if ! cpflow delete -a "$APP_NAME" --org "$CPLN_ORG" --yes; then + echo "❌ Failed to delete application: $APP_NAME" >&2 + exit 1 +fi + +echo "✅ Successfully deleted application: $APP_NAME" diff --git a/.github/workflows/delete-review-app.yml b/.github/workflows/delete-review-app.yml index 3f1451bc..ada1a5e7 100644 --- a/.github/workflows/delete-review-app.yml +++ b/.github/workflows/delete-review-app.yml @@ -22,7 +22,7 @@ env: PREFIX: ${{ vars.REVIEW_APP_PREFIX }} CPLN_ORG: ${{ vars.CPLN_ORG_STAGING }} CPLN_TOKEN: ${{ secrets.CPLN_TOKEN_STAGING }} - APP_NAME: ${{ vars.REVIEW_APP_PREFIX }}-pr-${{ github.event.pull_request.number || github.event.issue.number || inputs.pr_number }} + APP_NAME: ${{ vars.REVIEW_APP_PREFIX }}-${{ github.event.pull_request.number || github.event.issue.number || inputs.pr_number }} PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number || inputs.pr_number }} jobs: @@ -58,7 +58,7 @@ jobs: missing+=("Variable: CPLN_ORG_STAGING") fi - if [ -z "$"PREFIX" }} ]; then + if [ -z "$PREFIX" ]; then missing+=("Variable: REVIEW_APP_PREFIX") fi @@ -131,13 +131,6 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, body: '🗑️ Starting app deletion...' - body: [ - message, - '', - ' 🗑️ [View Delete Logs](' + process.env.WORKFLOW_URL + ')', - '', - getConsoleLink(process.env.PR_NUMBER) - ].join('\n') }); return { commentId: comment.data.id }; @@ -145,15 +138,9 @@ jobs: uses: ./.github/actions/delete-control-plane-app with: app_name: ${{ env.APP_NAME }} - org: ${{ env.CPLN_ORG }} - github_token: ${{ secrets.GITHUB_TOKEN }} - env: - APP_NAME: ${{ env.APP_NAME }} - CPLN_ORG: ${{ secrets.CPLN_ORG }} - CPLN_TOKEN: ${{ secrets.CPLN_TOKEN }} + cpln_org: ${{ vars.CPLN_ORG_STAGING }} - name: Update Delete Status - if: always() uses: actions/github-script@v7 with: script: | diff --git a/.github/workflows/deploy-to-control-plane-review-app.yml b/.github/workflows/deploy-to-control-plane-review-app.yml index b04a1ed2..8388bfc1 100644 --- a/.github/workflows/deploy-to-control-plane-review-app.yml +++ b/.github/workflows/deploy-to-control-plane-review-app.yml @@ -21,7 +21,7 @@ concurrency: env: PREFIX: ${{ vars.REVIEW_APP_PREFIX }} - APP_NAME: ${{ vars.REVIEW_APP_PREFIX }}-pr-${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }} + APP_NAME: ${{ vars.REVIEW_APP_PREFIX }}-${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }} CPLN_TOKEN: ${{ secrets.CPLN_TOKEN_STAGING }} CPLN_ORG: ${{ vars.CPLN_ORG_STAGING }} PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }} @@ -181,9 +181,12 @@ jobs: echo "Skipping deployment for non-PR comment" fi fi + if [[ "${{ env.DO_DEPLOY }}" == "false" ]]; then + exit 0 + fi - name: Setup Control Plane App if Not Existing - if: env.DO_DEPLOY == 'true' && env.APP_EXISTS == 'false' + if: env.APP_EXISTS == 'false' env: CPLN_TOKEN: ${{ secrets.CPLN_TOKEN_STAGING }} run: | @@ -191,7 +194,6 @@ jobs: cpflow setup-app -a ${{ env.APP_NAME }} --org ${{ vars.CPLN_ORG_STAGING }} - name: Create Initial Comment - if: env.DO_DEPLOY != 'false' uses: actions/github-script@v7 id: create-comment with: @@ -200,13 +202,12 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, issue_number: process.env.PR_NUMBER, - body: '🚀 Starting deployment process...\n\n' + process.env.CONSOLE_LINK + body: '🚀 Starting deployment process...\n\n' }); core.setOutput('comment-id', result.data.id); - name: Set Deployment URLs id: set-urls - if: env.DO_DEPLOY != 'false' uses: actions/github-script@v7 with: script: | @@ -237,7 +238,6 @@ jobs: ); - name: Initialize GitHub Deployment - if: env.DO_DEPLOY != 'false' uses: actions/github-script@v7 id: init-deployment with: @@ -316,7 +316,6 @@ jobs: }); - name: Deploy to Control Plane - if: env.DO_DEPLOY != 'false' run: cpflow deploy-image -a ${{ env.APP_NAME }} --run-release-phase --org ${{ vars.CPLN_ORG_STAGING }} --verbose - name: Retrieve App URL @@ -324,7 +323,6 @@ jobs: run: echo "WORKLOAD_URL=$(cpln workload get rails --gvc ${{ env.APP_NAME }} | tee | grep -oP 'https://[^[:space:]]*\.cpln\.app(?=\s|$)' | head -n1)" >> "$GITHUB_OUTPUT" - name: Update Status - Deployment Complete - if: env.DO_DEPLOY != 'false' uses: actions/github-script@v7 with: script: | diff --git a/.github/workflows/nightly-remove-stale-review-apps.yml b/.github/workflows/nightly-remove-stale-review-apps.yml index e511d9ac..d57c3e6e 100644 --- a/.github/workflows/nightly-remove-stale-review-apps.yml +++ b/.github/workflows/nightly-remove-stale-review-apps.yml @@ -20,40 +20,5 @@ jobs: token: ${{ secrets.CPLN_TOKEN_STAGING }} org: ${{ vars.CPLN_ORG_STAGING }} - - name: Get Stale PRs - id: stale_prs - uses: actions/github-script@v7 - with: - script: | - const thirtyDaysAgo = new Date(); - thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); - - const prs = await github.rest.pulls.list({ - owner: context.repo.owner, - repo: context.repo.repo, - state: 'closed', - sort: 'updated', - direction: 'desc' - }); - - const stalePRs = prs.data - .filter(pr => new Date(pr.updated_at) < thirtyDaysAgo) - .map(pr => pr.number); - - console.log('Found stale PRs:', stalePRs); - return stalePRs; - - name: Delete Stale Review Apps - if: ${{ steps.stale_prs.outputs.result != '[]' }} - run: | - for pr in $(echo "${{ steps.stale_prs.outputs.result }}" | jq -r '.[]'); do - APP_NAME="qa-react-webpack-rails-tutorial-pr-$pr" - echo "🗑️ Deleting stale review app for PR #$pr: $APP_NAME" - ${{ github.workspace }}/.github/actions/deploy-to-control-plane/scripts/delete-app.sh - done - env: - APP_NAME: qa-react-webpack-rails-tutorial-pr-${{ steps.stale_prs.outputs.result }} - - - name: Run cleanup-images script - run: | - cpflow cleanup-images -a qa-react-webpack-rails-tutorial -y + run: cpflow cleanup-stale-apps -a "qa-react-webpack-rails-tutorial" --yes