From 40f72440fc6ea761ab33d617656b8c5a87f8cc45 Mon Sep 17 00:00:00 2001 From: Judah Meek Date: Tue, 18 Mar 2025 18:08:28 -0500 Subject: [PATCH 1/7] Revert "Enhance GitHub Actions workflows for review app and staging deployments" This reverts commit 586dac315ff32374fb89b424e58155b9b325df05. --- .github/actions/build-docker-image/action.yml | 15 +- .../delete-control-plane-app/action.yml | 20 +++ .../delete-control-plane-app/delete-app.sh | 36 ++++ .github/actions/help-command/action.yml | 98 +++++++++++ .../actions/validate-required-vars/action.yml | 28 --- .github/workflows/delete-review-app.yml | 40 +++-- .../deploy-to-control-plane-staging.yml | 86 ++-------- ...ew-app.yml => deploy-to-control-plane.yml} | 160 +++++++++-------- .github/workflows/help-command.yml | 161 +++--------------- .github/workflows/review-app-help.yml | 4 +- 10 files changed, 314 insertions(+), 334 deletions(-) create mode 100644 .github/actions/delete-control-plane-app/action.yml create mode 100755 .github/actions/delete-control-plane-app/delete-app.sh create mode 100644 .github/actions/help-command/action.yml delete mode 100644 .github/actions/validate-required-vars/action.yml rename .github/workflows/{deploy-to-control-plane-review-app.yml => deploy-to-control-plane.yml} (84%) diff --git a/.github/actions/build-docker-image/action.yml b/.github/actions/build-docker-image/action.yml index 45a12434c..e1b5df73f 100644 --- a/.github/actions/build-docker-image/action.yml +++ b/.github/actions/build-docker-image/action.yml @@ -13,7 +13,7 @@ inputs: required: true PR_NUMBER: description: 'PR number' - required: false + required: true runs: using: "composite" @@ -22,18 +22,11 @@ runs: id: build shell: bash run: | - PR_INFO="" - if [ -n "${PR_NUMBER}" ]; then - PR_INFO=" for PR #${PR_NUMBER}" - fi - - echo "🏗️ Building Docker image${PR_INFO} (commit ${{ inputs.commit }})..." + echo "🏗️ Building Docker image for PR #${PR_NUMBER} (commit ${{ inputs.commit }})..." if cpflow build-image -a "${{ inputs.app_name }}" --commit="${{ inputs.commit }}" --org="${{ inputs.org }}"; then - image_tag="${{ inputs.org }}/${{ inputs.app_name }}:${{ inputs.commit }}" - echo "image_tag=${image_tag}" >> $GITHUB_OUTPUT - echo "✅ Docker image build successful${PR_INFO} (commit ${{ inputs.commit }})" + echo "✅ Docker image build successful for PR #${PR_NUMBER} (commit ${{ inputs.commit }})" else - echo "❌ Docker image build failed${PR_INFO} (commit ${{ inputs.commit }})" + echo "❌ Docker image build failed for PR #${PR_NUMBER} (commit ${{ inputs.commit }})" exit 1 fi 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 000000000..9c6993c6a --- /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 + org: + description: 'Organization name' + required: true + +runs: + using: "composite" + steps: + - name: Delete Application + shell: bash + run: ${{ github.action_path }}/scripts/delete-app.sh + env: + APP_NAME: ${{ inputs.app_name }} + CPLN_ORG: ${{ inputs.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 000000000..92e8fbc32 --- /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"; 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" --force; then + echo "❌ Failed to delete application: $APP_NAME" >&2 + exit 1 +fi + +echo "✅ Successfully deleted application: $APP_NAME" diff --git a/.github/actions/help-command/action.yml b/.github/actions/help-command/action.yml new file mode 100644 index 000000000..9988479c5 --- /dev/null +++ b/.github/actions/help-command/action.yml @@ -0,0 +1,98 @@ +name: 'Show Help Command' +description: 'Displays help information for available commands in PR comments' + +inputs: + github-token: + description: 'GitHub token for posting comments' + required: true + issue-number: + description: 'PR/Issue number to post the comment to (optional, defaults to event context)' + required: false + +runs: + using: "composite" + steps: + - name: Show Available Commands + uses: actions/github-script@v7 + with: + github-token: ${{ inputs.github-token }} + script: | + const helpText = [ + '# Available Commands', + '', + '## `/deploy`', + '**Purpose:** Deploy a review app for your pull request', + '', + '**What it does:**', + '- Creates a new review app in Control Plane', + '- Deploys your changes to the review environment', + '- Provides a unique URL to preview your changes', + '- Shows build and deployment progress in real-time', + '', + '**Optional Configuration:**', + '- `WAIT_TIMEOUT`: Deployment timeout in seconds (default: 900)', + ' - Must be a positive integer', + ' - Example: `/deploy timeout=1800`', + '', + '## `/destroy`', + '**Purpose:** Remove the review app for your pull request', + '', + '**What it does:**', + '- Deletes the review app from Control Plane', + '- Cleans up associated resources', + '- Updates PR with deletion status', + '', + '---', + '## Environment Setup', + '', + '**Required Environment Secrets:**', + '- `CPLN_TOKEN_STAGING`: Control Plane authentication token', + '- `CPLN_TOKEN_PRODUCTION`: Control Plane authentication token', + '', + '**Required GitHub Actions Variables:**', + '- `CPLN_ORG_STAGING`: Control Plane authentication token', + '- `CPLN_ORG_PRODUCTION`: Control Plane authentication token', + '', + '**Required GitHub Actions Variables (these need to match your control_plane.yml file:**', + '- `PRODUCTION_APP_NAME`: Control Plane production app name', + '- `STAGING_APP_NAME`: Control Plane staging app name', + '- `REVIEW_APP_PREFIX`: Control Plane review app prefix', + '', + 'Optional: Configure `WAIT_TIMEOUT` in GitHub Actions variables to customize deployment timeout', + '', + '## Control Plane Integration', + '', + '1. Review app naming convention:', + ' ```', + ' ${{ vars.REVIEW_APP_PREFIX }}-', + ' ```', + '2. Console URL: `https://console.cpln.io/console/org/{CPLN_ORG}/gvc/{APP_NAME}/-info`', + '', + '## Automatic Cleanup', + '', + 'Review apps are automatically destroyed when:', + '1. The pull request is closed', + '2. The `/destroy` command is used', + '3. A new deployment is requested (old one is cleaned up first)', + '', + '## Need Help?', + '', + 'For additional assistance:', + '1. Check the [Control Plane documentation](https://docs.controlplane.com/)', + '2. Contact the infrastructure team', + '3. Open an issue in this repository', + ].join('\n'); + + const issueNumber = inputs['issue-number'] || + (context.eventName === 'issue_comment' ? context.payload.issue.number : null); + + if (issueNumber) { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + body: helpText + }); + } else { + console.log(helpText); + } diff --git a/.github/actions/validate-required-vars/action.yml b/.github/actions/validate-required-vars/action.yml deleted file mode 100644 index c370039d0..000000000 --- a/.github/actions/validate-required-vars/action.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: 'Validate Required Variables' -description: 'Validates that all required secrets and variables for Control Plane operations' - -runs: - using: 'composite' - steps: - - name: Validate Required Secrets and Variables - shell: bash - run: | - missing=() - - # Check required secrets - if [ -z "$CPLN_TOKEN_STAGING" ]; then - missing+=("Secret: CPLN_TOKEN_STAGING") - fi - - # Check required variables - if [ -z "$CPLN_ORG_STAGING" ]; then - missing+=("Variable: CPLN_ORG_STAGING") - fi - if [ -z "$REVIEW_APP_PREFIX" ]; then - missing+=("Variable: REVIEW_APP_PREFIX") - fi - - if [ ${#missing[@]} -ne 0 ]; then - echo "Required secrets/variables are not set: ${missing[*]}" - exit 1 - fi diff --git a/.github/workflows/delete-review-app.yml b/.github/workflows/delete-review-app.yml index a0b3611a8..754b5cd1d 100644 --- a/.github/workflows/delete-review-app.yml +++ b/.github/workflows/delete-review-app.yml @@ -5,12 +5,6 @@ on: types: [closed] issue_comment: types: [created] - workflow_dispatch: - inputs: - pr_number: - description: 'PR number to delete review app for' - required: true - type: string permissions: contents: read @@ -21,8 +15,8 @@ permissions: env: 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 }} - PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number || inputs.pr_number }} + APP_NAME: qa-react-webpack-rails-tutorial-pr-${{ github.event.pull_request.number || github.event.issue.number }} + PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }} jobs: debug: @@ -35,15 +29,37 @@ jobs: github.event.issue.pull_request && github.event.comment.body == '/delete-review-app') || (github.event_name == 'pull_request' && - github.event.action == 'closed') || - github.event_name == 'workflow_dispatch' + github.event.action == 'closed') runs-on: ubuntu-latest steps: + - name: Get PR number + id: pr + uses: actions/github-script@v7 + with: + script: | + const prNumber = context.payload.issue.number; + core.setOutput('pr_number', prNumber); + core.exportVariable('PR_NUMBER', prNumber); + + - name: Set App Name + run: echo "APP_NAME=qa-react-webpack-rails-tutorial-pr-${{ env.PR_NUMBER }}" >> $GITHUB_ENV + - uses: actions/checkout@v4 - - name: Validate Required Secrets and Variables - uses: ./.github/actions/validate-required-vars + - name: Validate Required Secrets + run: | + missing_secrets=() + for secret in "CPLN_TOKEN" "CPLN_ORG"; do + if [ -z "${!secret}" ]; then + missing_secrets+=("$secret") + fi + done + + if [ ${#missing_secrets[@]} -ne 0 ]; then + echo "Required secrets are not set: ${missing_secrets[*]}" + exit 1 + fi - name: Setup Environment uses: ./.github/actions/setup-environment diff --git a/.github/workflows/deploy-to-control-plane-staging.yml b/.github/workflows/deploy-to-control-plane-staging.yml index d789d1eeb..898c447c1 100644 --- a/.github/workflows/deploy-to-control-plane-staging.yml +++ b/.github/workflows/deploy-to-control-plane-staging.yml @@ -1,96 +1,32 @@ # Control Plane GitHub Action -name: Deploy to Control Plane Staging -run-name: Deploy Control Plane Staging App +name: Deploy Main Branch to Control Plane Staging # Controls when the workflow will run on: + # Uncomment the lines you want actions that will cause the workflow to Triggers the workflow on push or pull request events but only for the main branch push: - branches: - - '*' + branches: [master] + + # Allows you to run this workflow manually from the Actions tab workflow_dispatch: # Convert the GitHub secret variables to environment variables for use by the Control Plane CLI env: - APP_NAME: ${{ vars.STAGING_APP_NAME }} - CPLN_TOKEN: ${{ secrets.CPLN_TOKEN_STAGING }} CPLN_ORG: ${{ vars.CPLN_ORG_STAGING }} - STAGING_APP_BRANCH: ${{ vars.STAGING_APP_BRANCH }} - -concurrency: - group: deploy-staging - cancel-in-progress: true + CPLN_TOKEN: ${{ secrets.CPLN_TOKEN_STAGING }} jobs: - debug: - uses: ./.github/workflows/debug-workflow.yml - with: - debug_enabled: false - - validate-branch: + deploy-to-control-plane-staging: runs-on: ubuntu-latest - outputs: - is_deployable: ${{ steps.check_branch.outputs.is_deployable }} - steps: - - name: Check if allowed branch - id: check_branch - run: | - if [[ -n "${STAGING_APP_BRANCH}" ]]; then - if [[ "${GITHUB_REF#refs/heads/}" == "${STAGING_APP_BRANCH}" ]]; then - echo "is_deployable=true" >> $GITHUB_OUTPUT - else - echo "Branch '${GITHUB_REF#refs/heads/}' is not the configured deployment branch '${STAGING_APP_BRANCH}'" - echo "is_deployable=false" >> $GITHUB_OUTPUT - fi - elif [[ "${GITHUB_REF}" == "refs/heads/main" || "${GITHUB_REF}" == "refs/heads/master" ]]; then - echo "is_deployable=true" >> $GITHUB_OUTPUT - else - echo "Branch '${GITHUB_REF#refs/heads/}' is not main/master (no STAGING_APP_BRANCH configured)" - echo "is_deployable=false" >> $GITHUB_OUTPUT - fi - build: - needs: validate-branch - if: needs.validate-branch.outputs.is_deployable == 'true' - runs-on: ubuntu-latest steps: - - name: Checkout code - uses: actions/checkout@v4 + - uses: actions/checkout@v4 with: - fetch-depth: 0 - - - name: Setup Environment - uses: ./.github/actions/setup-environment - with: - token: ${{ secrets.CPLN_TOKEN_STAGING }} - org: ${{ vars.CPLN_ORG_STAGING }} - - - name: Build Docker Image - id: build - uses: ./.github/actions/build-docker-image - with: - app_name: ${{ env.APP_NAME }} - org: ${{ vars.CPLN_ORG_STAGING }} - commit: ${{ github.sha }} - - deploy: - needs: build - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Environment - uses: ./.github/actions/setup-environment - with: - token: ${{ secrets.CPLN_TOKEN_STAGING }} - org: ${{ vars.CPLN_ORG_STAGING }} + fetch-depth: 0 # Fetch all history for proper SHA handling + ref: master # Explicitly checkout master branch - - name: Deploy to Control Plane - uses: ./.github/actions/deploy-to-control-plane + - uses: ./.github/actions/deploy-to-control-plane with: app_name: ${{ vars.STAGING_APP_NAME }} org: ${{ vars.CPLN_ORG_STAGING }} - github_token: ${{ secrets.GITHUB_TOKEN }} - wait_timeout: ${{ vars.WAIT_TIMEOUT || 900 }} - cpln_token: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/deploy-to-control-plane-review-app.yml b/.github/workflows/deploy-to-control-plane.yml similarity index 84% rename from .github/workflows/deploy-to-control-plane-review-app.yml rename to .github/workflows/deploy-to-control-plane.yml index ea05f98b0..d8a9783f1 100644 --- a/.github/workflows/deploy-to-control-plane-review-app.yml +++ b/.github/workflows/deploy-to-control-plane.yml @@ -24,7 +24,7 @@ concurrency: cancel-in-progress: true env: - 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 }} @@ -33,10 +33,10 @@ jobs: debug: uses: ./.github/workflows/debug-workflow.yml with: - debug_enabled: false - - process-deployment: - needs: debug + debug_enabled: false # Will still run if vars.DEBUG_WORKFLOW is true + + Process-Deployment-Command: + needs: debug # Add this to ensure debug runs first if: | (github.event_name == 'pull_request') || (github.event_name == 'push') || @@ -45,13 +45,12 @@ jobs: github.event.issue.pull_request && contains(github.event.comment.body, '/deploy-review-app')) runs-on: ubuntu-latest - outputs: - pr_number: ${{ env.PR_NUMBER }} - pr_sha: ${{ env.PR_SHA }} - pr_ref: ${{ steps.getRef.outputs.PR_REF }} - do_deploy: ${{ env.DO_DEPLOY }} - comment_id: ${{ steps.create-comment.outputs.comment-id }} - deployment_id: ${{ steps.init-deployment.outputs.result }} + permissions: + contents: read + deployments: write + pull-requests: write + issues: write + steps: # Initial checkout only for pull_request and push events - name: Checkout code @@ -70,7 +69,28 @@ jobs: fetch-depth: 0 - name: Validate Required Secrets and Variables - uses: ./.github/actions/validate-required-vars + shell: bash + run: | + missing=() + + # Check secrets + if [ -z "${{ secrets.CPLN_TOKEN_STAGING }}" ]; then + missing+=("Secret: CPLN_TOKEN_STAGING") + fi + + # Check variables + if [ -z "${{ vars.CPLN_ORG_STAGING }}" ]; then + missing+=("Variable: CPLN_ORG_STAGING") + fi + + if [ -z "${{ vars.REVIEW_APP_PREFIX }}" ]; then + missing+=("Variable: REVIEW_APP_PREFIX") + fi + + if [ ${#missing[@]} -ne 0 ]; then + echo "Required secrets/variables are not set: ${missing[*]}" + exit 1 + fi - name: Get PR HEAD Ref id: getRef @@ -137,6 +157,13 @@ jobs: echo "PR_REF=$(echo $PR_DATA | jq -r .headRefName)" >> $GITHUB_OUTPUT echo "PR_SHA=$(echo $PR_DATA | jq -r .headRefOid)" >> $GITHUB_ENV + - name: Checkout PR code + if: github.event_name == 'workflow_dispatch' || github.event_name == 'issue_comment' + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ steps.getRef.outputs.PR_SHA }} + - name: Setup Environment uses: ./.github/actions/setup-environment with: @@ -226,6 +253,26 @@ jobs: }); core.setOutput('comment-id', result.data.id); + - name: Update Comment - Building + if: env.DO_DEPLOY != 'false' + uses: actions/github-script@v7 + with: + script: | + const buildingMessage = [ + `🏗️ Building Docker image for PR #${process.env.PR_NUMBER}, commit ${process.env.PR_SHA}`, + '', + `📝 [View Build Logs](${process.env.WORKFLOW_URL})`, + '', + process.env.CONSOLE_LINK + ].join('\n'); + + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: ${{ steps.create-comment.outputs.comment-id }}, + body: buildingMessage + }); + - name: Set Deployment URLs id: set-urls if: env.DO_DEPLOY != 'false' @@ -258,6 +305,30 @@ jobs: 'https://console.cpln.io/console/org/' + process.env.CPLN_ORG + '/gvc/' + process.env.APP_NAME + '/-info)' ); + - name: Update Status - Building + if: env.DO_DEPLOY != 'false' + uses: actions/github-script@v7 + with: + script: | + const buildingMessage = [ + '🏗️ Building Docker image for PR #' + process.env.PR_NUMBER + ', commit ' + '${{ env.PR_SHA }}', + '', + '📝 [View Build Logs](' + process.env.WORKFLOW_URL + ')', + '', + process.env.CONSOLE_LINK + ].join('\n'); + + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: ${{ steps.create-comment.outputs.comment-id }}, + body: buildingMessage + }); + + - name: Checkout PR Branch + if: env.DO_DEPLOY != 'false' + run: git checkout ${{ steps.getRef.outputs.PR_REF }} + - name: Initialize GitHub Deployment if: env.DO_DEPLOY != 'false' uses: actions/github-script@v7 @@ -288,70 +359,17 @@ jobs: return deployment.data.id; - build: - needs: process-deployment - if: needs.process-deployment.outputs.do_deploy != 'false' - runs-on: ubuntu-latest - outputs: - image_tag: ${{ steps.build.outputs.image_tag }} - comment_id: ${{ needs.process-deployment.outputs.comment_id }} - pr_number: ${{ needs.process-deployment.outputs.pr_number }} - do_deploy: ${{ needs.process-deployment.outputs.do_deploy }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - ref: ${{ needs.process-deployment.outputs.pr_ref }} - - - name: Setup Environment - uses: ./.github/actions/setup-environment - with: - token: ${{ secrets.CPLN_TOKEN_STAGING }} - org: ${{ vars.CPLN_ORG_STAGING }} - - - name: Update Status - Building - uses: actions/github-script@v7 - with: - script: | - const buildingMessage = [ - '🏗️ Building Docker image for PR #${{ needs.process-deployment.outputs.pr_number }}, commit ${{ needs.process-deployment.outputs.pr_sha }}', - '', - '📝 [View Build Logs](${{ env.WORKFLOW_URL }})', - '', - process.env.CONSOLE_LINK - ].join('\n'); - - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: ${{ needs.process-deployment.outputs.comment_id }}, - body: buildingMessage - }); - - name: Build Docker Image - id: build + if: env.DO_DEPLOY != 'false' uses: ./.github/actions/build-docker-image with: app_name: ${{ env.APP_NAME }} org: ${{ vars.CPLN_ORG_STAGING }} - commit: ${{ needs.process-deployment.outputs.pr_sha }} - PR_NUMBER: ${{ needs.process-deployment.outputs.pr_number }} - - deploy: - needs: build - if: needs.build.outputs.do_deploy != 'false' - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Environment - uses: ./.github/actions/setup-environment - with: - token: ${{ secrets.CPLN_TOKEN_STAGING }} - org: ${{ vars.CPLN_ORG_STAGING }} + commit: ${{ env.PR_SHA }} + PR_NUMBER: ${{ env.PR_NUMBER }} - name: Update Status - Deploying + if: env.DO_DEPLOY != 'false' uses: actions/github-script@v7 with: script: | @@ -360,7 +378,7 @@ jobs: '', '⏳ Waiting for deployment to be ready...', '', - '📝 [View Deploy Logs](${{ env.WORKFLOW_URL }})', + '📝 [View Deploy Logs](' + process.env.WORKFLOW_URL + ')', '', process.env.CONSOLE_LINK ].join('\n'); diff --git a/.github/workflows/help-command.yml b/.github/workflows/help-command.yml index 51ce25663..121d4712b 100644 --- a/.github/workflows/help-command.yml +++ b/.github/workflows/help-command.yml @@ -1,151 +1,42 @@ -name: Help Command +name: Show Help for Commands on: + push: + branches: + - "**" # Trigger on all branches, including feature branches issue_comment: types: [created] workflow_dispatch: inputs: - pr_number: - description: 'Pull Request number to post help comment on' + issue-number: + description: 'PR/Issue number to post the help comment to' required: true - type: string + type: number permissions: issues: write pull-requests: write jobs: - help: - if: ${{ (github.event.issue.pull_request && github.event.comment.body == '/help') || github.event_name == 'workflow_dispatch' }} + debug: + uses: ./.github/workflows/debug-workflow.yml + with: + debug_enabled: false # Will still run if vars.DEBUG_WORKFLOW is true + + show-help: + if: | + github.event_name == 'workflow_dispatch' || + (github.event_name == 'issue_comment' && + github.event.issue.pull_request && + github.event.comment.body == '/help') runs-on: ubuntu-latest - - steps: - - name: Show Available Commands - uses: actions/github-script@v7 - with: - script: | - const sections = { - commands: { - deploy: { - title: '## `/deploy`', - purpose: '**Purpose:** Deploy a review app for your pull request', - details: [ - '**What it does:**', - '- Creates a new review app in Control Plane', - '- Deploys your changes to the review environment', - '- Provides a unique URL to preview your changes', - '- Shows build and deployment progress in real-time', - '', - '**Optional Configuration:**', - '- `WAIT_TIMEOUT`: Deployment timeout in seconds (default: 900)', - ' - Must be a positive integer', - ' - Example: `/deploy timeout=1800`' - ] - }, - destroy: { - title: '## `/destroy`', - purpose: '**Purpose:** Remove the review app for your pull request', - details: [ - '**What it does:**', - '- Deletes the review app from Control Plane', - '- Cleans up associated resources', - '- Updates PR with deletion status' - ] - } - }, - setup: { - title: '## Environment Setup', - sections: [ - { - title: '**Required Environment Secrets:**', - items: [ - '- `CPLN_TOKEN_STAGING`: Control Plane authentication token', - '- `CPLN_TOKEN_PRODUCTION`: Control Plane authentication token' - ] - }, - { - title: '**Required GitHub Actions Variables:**', - items: [ - '- `CPLN_ORG_STAGING`: Control Plane authentication token', - '- `CPLN_ORG_PRODUCTION`: Control Plane authentication token' - ] - }, - { - title: '**Required GitHub Actions Variables (these need to match your control_plane.yml file:**', - items: [ - '- `PRODUCTION_APP_NAME`: Control Plane production app name', - '- `STAGING_APP_NAME`: Control Plane staging app name', - '- `REVIEW_APP_PREFIX`: Control Plane review app prefix' - ] - } - ], - note: 'Optional: Configure `WAIT_TIMEOUT` in GitHub Actions variables to customize deployment timeout' - }, - integration: { - title: '## Control Plane Integration', - details: [ - '1. Review app naming convention:', - ' ```', - ' ${{ vars.REVIEW_APP_PREFIX }}-', - ' ```', - '2. Console URL: `https://console.cpln.io/console/org/{CPLN_ORG}/gvc/{APP_NAME}/-info`' - ] - }, - cleanup: { - title: '## Automatic Cleanup', - details: [ - 'Review apps are automatically destroyed when:', - '1. The pull request is closed', - '2. The `/destroy` command is used', - '3. A new deployment is requested (old one is cleaned up first)' - ] - }, - help: { - title: '## Need Help?', - details: [ - 'For additional assistance:', - '1. Check the [Control Plane documentation](https://docs.controlplane.com/)', - '2. Contact the infrastructure team', - '3. Open an issue in this repository' - ] - } - }; - const generateHelpText = () => { - const parts = ['# Available Commands', '']; - - // Add commands - Object.values(sections.commands).forEach(cmd => { - parts.push(cmd.title, cmd.purpose, '', ...cmd.details, ''); - }); - - parts.push('---'); - - // Add setup section - parts.push(sections.setup.title, ''); - sections.setup.sections.forEach(section => { - parts.push(section.title, ...section.items, ''); - }); - parts.push(sections.setup.note, ''); - - // Add remaining sections - ['integration', 'cleanup', 'help'].forEach(section => { - parts.push(sections[section].title, '', ...sections[section].details, ''); - }); - - return parts.join('\n'); - }; + steps: + - name: Checkout + uses: actions/checkout - const helpText = generateHelpText(); - - const prNumber = context.eventName === 'workflow_dispatch' - ? parseInt(context.payload.inputs.pr_number) - : context.issue.number; - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - body: helpText - }); - \ No newline at end of file + - name: Process Help Command + uses: ./.github/actions/help-command + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + issue-number: ${{ github.event.inputs.issue-number }} \ No newline at end of file diff --git a/.github/workflows/review-app-help.yml b/.github/workflows/review-app-help.yml index d5eed23c1..a54bfdf0e 100644 --- a/.github/workflows/review-app-help.yml +++ b/.github/workflows/review-app-help.yml @@ -37,9 +37,9 @@ jobs: 'Remove the review app when done', '', '### `/help`', - 'Show detailed instructions, environment setup, and configuration options.', + 'Show detailed instructions, environment setup, and configuration options.' '', - '---' + '---', ].join('\n'); await github.rest.issues.createComment({ From fb23490a510b8c83da84ee60e86fcebcdeb04958 Mon Sep 17 00:00:00 2001 From: Judah Meek Date: Tue, 18 Mar 2025 18:08:35 -0500 Subject: [PATCH 2/7] Revert "get PR review apps working again (#623)" This reverts commit f0c726f44b71187da690f7ad5c40dad45ae38afe. --- .../delete-control-plane-app/action.yml | 2 +- .../deploy-to-control-plane/action.yml | 74 +++++- .../scripts}/delete-app.sh | 0 .../deploy-to-control-plane/scripts/deploy.sh | 14 +- .../scripts/get-commit-sha.sh | 34 +++ .github/workflows/debug-workflow.yml | 36 --- .github/workflows/delete-review-app.yml | 4 - .github/workflows/deploy-to-control-plane.yml | 248 ++++-------------- .github/workflows/help-command.yml | 5 - .github/workflows/review-app-help.yml | 5 - 10 files changed, 160 insertions(+), 262 deletions(-) rename .github/actions/{delete-control-plane-app => deploy-to-control-plane/scripts}/delete-app.sh (100%) create mode 100755 .github/actions/deploy-to-control-plane/scripts/get-commit-sha.sh delete mode 100644 .github/workflows/debug-workflow.yml diff --git a/.github/actions/delete-control-plane-app/action.yml b/.github/actions/delete-control-plane-app/action.yml index 9c6993c6a..d5d13ef74 100644 --- a/.github/actions/delete-control-plane-app/action.yml +++ b/.github/actions/delete-control-plane-app/action.yml @@ -14,7 +14,7 @@ runs: steps: - name: Delete Application shell: bash - run: ${{ github.action_path }}/scripts/delete-app.sh + run: ${{ github.action_path }}/../deploy-to-control-plane/scripts/delete-app.sh env: APP_NAME: ${{ inputs.app_name }} CPLN_ORG: ${{ inputs.org }} diff --git a/.github/actions/deploy-to-control-plane/action.yml b/.github/actions/deploy-to-control-plane/action.yml index fcb684396..3ec17068a 100644 --- a/.github/actions/deploy-to-control-plane/action.yml +++ b/.github/actions/deploy-to-control-plane/action.yml @@ -32,16 +32,80 @@ outputs: runs: using: "composite" steps: + - name: Validate Required Secrets + shell: bash + run: | + missing_secrets=() + for secret in "CPLN_TOKEN" "CPLN_ORG"; do + if [ -z "${!secret}" ]; then + missing_secrets+=("$secret") + fi + done + + if [ ${#missing_secrets[@]} -ne 0 ]; then + echo "Required secrets are not set: ${missing_secrets[*]}" + exit 1 + fi + + - name: Setup Environment + uses: ./.github/actions/setup-environment + + - name: Get Commit SHA + id: get_sha + shell: bash + run: ${{ github.action_path }}/scripts/get-commit-sha.sh + env: + GITHUB_TOKEN: ${{ inputs.github_token }} + PR_NUMBER: ${{ inputs.pr_number }} + - name: Deploy to Control Plane id: deploy shell: bash env: - APP_NAME: ${{ inputs.app_name }} - CPLN_ORG: ${{ inputs.org }} CPLN_TOKEN: ${{ inputs.cpln_token }} - WAIT_TIMEOUT: ${{ inputs.wait_timeout }} + PR_NUMBER: ${{ inputs.pr_number }} run: | - # Run the deployment script - if ! ${{ github.action_path }}/scripts/deploy.sh; then + echo "🚀 Deploying app for PR #${PR_NUMBER}..." + + # Create temp file for output + TEMP_OUTPUT=$(mktemp) + trap 'rm -f "${TEMP_OUTPUT}"' EXIT + + # Deploy the application and show output in real-time while capturing it + if ! cpflow deploy-image -a "${{ inputs.app_name }}" --run-release-phase --org "${{ inputs.org }}" 2>&1 | tee "${TEMP_OUTPUT}"; then + echo "❌ Deployment failed for PR #${PR_NUMBER}" + echo "Error output:" + cat "${TEMP_OUTPUT}" + exit 1 + fi + + # Extract app URL from captured output + REVIEW_APP_URL=$(grep -oP 'https://rails-[^[:space:]]*\.cpln\.app(?=\s|$)' "${TEMP_OUTPUT}" | head -n1) + if [ -z "${REVIEW_APP_URL}" ]; then + echo "❌ Failed to get app URL from deployment output" + echo "Deployment output:" + cat "${TEMP_OUTPUT}" + exit 1 + fi + + # Wait for all workloads to be ready + WAIT_TIMEOUT=${WAIT_TIMEOUT:-${{ inputs.wait_timeout }}} + echo "⏳ Waiting for all workloads to be ready (timeout: ${WAIT_TIMEOUT}s)..." + + # Use timeout command with ps:wait and show output in real-time + if ! timeout "${WAIT_TIMEOUT}" bash -c "cpflow ps:wait -a \"${{ inputs.app_name }}\"" 2>&1 | tee -a "${TEMP_OUTPUT}"; then + TIMEOUT_EXIT=$? + if [ ${TIMEOUT_EXIT} -eq 124 ]; then + echo "❌ Timed out waiting for workloads after ${WAIT_TIMEOUT} seconds" + else + echo "❌ Workloads did not become ready for PR #${PR_NUMBER} (exit code: ${TIMEOUT_EXIT})" + fi + echo "Full output:" + cat "${TEMP_OUTPUT}" exit 1 fi + + echo "✅ Deployment successful for PR #${PR_NUMBER}" + echo "🌐 App URL: ${REVIEW_APP_URL}" + echo "review_app_url=${REVIEW_APP_URL}" >> $GITHUB_OUTPUT + echo "REVIEW_APP_URL=${REVIEW_APP_URL}" >> $GITHUB_ENV diff --git a/.github/actions/delete-control-plane-app/delete-app.sh b/.github/actions/deploy-to-control-plane/scripts/delete-app.sh similarity index 100% rename from .github/actions/delete-control-plane-app/delete-app.sh rename to .github/actions/deploy-to-control-plane/scripts/delete-app.sh diff --git a/.github/actions/deploy-to-control-plane/scripts/deploy.sh b/.github/actions/deploy-to-control-plane/scripts/deploy.sh index f90ede239..73bb8968c 100755 --- a/.github/actions/deploy-to-control-plane/scripts/deploy.sh +++ b/.github/actions/deploy-to-control-plane/scripts/deploy.sh @@ -11,7 +11,7 @@ # Must be a positive integer # # Outputs: -# - ENV APP_URL: URL of the deployed application +# - rails_url: URL of the deployed Rails application set -e @@ -39,9 +39,11 @@ if ! timeout "${WAIT_TIMEOUT}" cpflow deploy-image -a "$APP_NAME" --run-release- fi # Extract app URL from deployment output -APP_URL=$(grep -oP 'https://[^[:space:]]*\.cpln\.app(?=\s|$)' "$TEMP_OUTPUT" | head -n1) -if [ -z "$APP_URL" ]; then - echo "❌ Error: Could not find app URL in deployment output" +RAILS_URL=$(grep -oP 'https://rails-[^[:space:]]*\.cpln\.app(?=\s|$)' "$TEMP_OUTPUT" | head -n1) +if [ -z "$RAILS_URL" ]; then + echo "❌ Failed to get app URL from deployment output" + echo "Full output:" + cat "$TEMP_OUTPUT" exit 1 fi @@ -60,5 +62,5 @@ if ! timeout "${WAIT_TIMEOUT}" bash -c "cpflow ps:wait -a \"$APP_NAME\"" 2>&1 | fi echo "✅ Deployment successful" -echo "🌐 App URL: $APP_URL" -echo "APP_URL=$APP_URL" >> "$GITHUB_OUTPUT" +echo "🌐 Rails URL: $RAILS_URL" +echo "rails_url=$RAILS_URL" >> "$GITHUB_OUTPUT" diff --git a/.github/actions/deploy-to-control-plane/scripts/get-commit-sha.sh b/.github/actions/deploy-to-control-plane/scripts/get-commit-sha.sh new file mode 100755 index 000000000..9dd32cd0f --- /dev/null +++ b/.github/actions/deploy-to-control-plane/scripts/get-commit-sha.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# This script retrieves the commit SHA for deployment +# It handles both PR and direct branch deployments +# +# Required environment variables: +# - PR_NUMBER: Pull request number (optional) +# - GITHUB_TOKEN: GitHub token for API access +# +# Outputs: +# - sha: Full commit SHA +# - sha_short: Short (7 char) commit SHA + +set -e + +if [ -n "${PR_NUMBER}" ]; then + # If PR_NUMBER is set, get the PR's head SHA + if ! PR_SHA=$(gh pr view "${PR_NUMBER}" --json headRefOid --jq '.headRefOid'); then + echo "Failed to get PR head SHA" >&2 + exit 1 + fi + echo "sha=${PR_SHA}" >> "$GITHUB_OUTPUT" + echo "sha_short=${PR_SHA:0:7}" >> "$GITHUB_OUTPUT" + echo "Using PR head commit SHA: ${PR_SHA:0:7}" +else + # For direct branch deployments, use the current commit SHA + if ! CURRENT_SHA=$(git rev-parse HEAD); then + echo "Failed to get current SHA" >&2 + exit 1 + fi + echo "sha=${CURRENT_SHA}" >> "$GITHUB_OUTPUT" + echo "sha_short=${CURRENT_SHA:0:7}" >> "$GITHUB_OUTPUT" + echo "Using branch commit SHA: ${CURRENT_SHA:0:7}" +fi diff --git a/.github/workflows/debug-workflow.yml b/.github/workflows/debug-workflow.yml deleted file mode 100644 index 4ee0071a1..000000000 --- a/.github/workflows/debug-workflow.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Debug Workflow Information - -on: - workflow_call: - inputs: - debug_enabled: - required: false - type: boolean - default: false - description: 'Enable debug logging (defaults to false)' - -jobs: - debug-info: - runs-on: ubuntu-latest - if: inputs.debug_enabled || vars.DEBUG_WORKFLOW == 'true' - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Log Branch Info - run: | - echo "Branch for this run:" - if [ "${{ github.event_name }}" == "pull_request" ]; then - echo "Pull Request Source Branch: ${{ github.head_ref }}" - else - echo "Branch: ${{ github.ref_name }}" - fi - - - name: Debug GitHub Context - run: | - echo "Event name: ${{ github.event_name }}" - echo "Event path: ${{ github.event_path }}" - echo "Repository: ${{ github.repository }}" - echo "Full GitHub context:" - echo '${{ toJson(github) }}' \ No newline at end of file diff --git a/.github/workflows/delete-review-app.yml b/.github/workflows/delete-review-app.yml index 754b5cd1d..b45282e81 100644 --- a/.github/workflows/delete-review-app.yml +++ b/.github/workflows/delete-review-app.yml @@ -19,10 +19,6 @@ env: PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }} jobs: - debug: - uses: ./.github/workflows/debug-workflow.yml - with: - debug_enabled: false # Will still run if vars.DEBUG_WORKFLOW is true Process-Delete-Command: if: | (github.event_name == 'issue_comment' && diff --git a/.github/workflows/deploy-to-control-plane.yml b/.github/workflows/deploy-to-control-plane.yml index d8a9783f1..60f081c35 100644 --- a/.github/workflows/deploy-to-control-plane.yml +++ b/.github/workflows/deploy-to-control-plane.yml @@ -1,6 +1,6 @@ -name: Deploy PR Review App to Control Plane +name: Deploy Review App to Control Plane -run-name: Deploy PR Review App - PR #${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }} +run-name: Deploy Review App - ${{ github.ref_name }} on: pull_request: @@ -24,26 +24,20 @@ concurrency: cancel-in-progress: true env: - APP_NAME: ${{ vars.REVIEW_APP_PREFIX }}-${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }} + APP_NAME: qa-react-webpack-rails-tutorial-pr-${{ 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 }} jobs: - debug: - uses: ./.github/workflows/debug-workflow.yml - with: - debug_enabled: false # Will still run if vars.DEBUG_WORKFLOW is true - Process-Deployment-Command: - needs: debug # Add this to ensure debug runs first if: | (github.event_name == 'pull_request') || (github.event_name == 'push') || (github.event_name == 'workflow_dispatch') || (github.event_name == 'issue_comment' && github.event.issue.pull_request && - contains(github.event.comment.body, '/deploy-review-app')) + github.event.comment.body == '/deploy-review-app') runs-on: ubuntu-latest permissions: contents: read @@ -68,94 +62,46 @@ jobs: with: fetch-depth: 0 - - name: Validate Required Secrets and Variables - shell: bash - run: | - missing=() - - # Check secrets - if [ -z "${{ secrets.CPLN_TOKEN_STAGING }}" ]; then - missing+=("Secret: CPLN_TOKEN_STAGING") - fi - - # Check variables - if [ -z "${{ vars.CPLN_ORG_STAGING }}" ]; then - missing+=("Variable: CPLN_ORG_STAGING") - fi - - if [ -z "${{ vars.REVIEW_APP_PREFIX }}" ]; then - missing+=("Variable: REVIEW_APP_PREFIX") - fi - - if [ ${#missing[@]} -ne 0 ]; then - echo "Required secrets/variables are not set: ${missing[*]}" - exit 1 - fi - - name: Get PR HEAD Ref id: getRef env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - # For push events, try to find associated PR first - if [[ "${{ github.event_name }}" == "push" ]]; then - PR_DATA=$(gh pr list --head "${{ github.ref_name }}" --json number,headRefName,headRefOid --jq '.[0]') + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + PR_NUMBER="${{ github.event.inputs.pr }}" + elif [[ "${{ github.event_name }}" == "issue_comment" ]]; then + PR_NUMBER="${{ github.event.issue.number }}" + elif [[ "${{ github.event_name }}" == "pull_request" ]]; then + PR_NUMBER="${{ github.event.pull_request.number }}" + elif [[ "${{ github.event_name }}" == "push" ]]; then + # For push events, find associated PR + PR_DATA=$(gh pr list --head "${{ github.ref_name }}" --json number --jq '.[0].number') if [[ -n "$PR_DATA" ]]; then - PR_NUMBER=$(echo "$PR_DATA" | jq -r .number) + PR_NUMBER="$PR_DATA" else - echo "No PR found for branch ${{ github.ref_name }}, skipping deployment" - echo "DO_DEPLOY=false" >> $GITHUB_ENV - exit 0 + echo "Error: No PR found for branch ${{ github.ref_name }}" + exit 1 fi - else - # Get PR number based on event type - case "${{ github.event_name }}" in - "workflow_dispatch") - PR_NUMBER="${{ github.event.inputs.pr_number }}" - ;; - "issue_comment") - PR_NUMBER="${{ github.event.issue.number }}" - ;; - "pull_request") - PR_NUMBER="${{ github.event.pull_request.number }}" - ;; - *) - echo "Error: Unsupported event type ${{ github.event_name }}" - exit 1 - ;; - esac fi - + if [[ -z "$PR_NUMBER" ]]; then echo "Error: Could not determine PR number" - echo "Event type: ${{ github.event_name }}" - echo "Event action: ${{ github.event.action }}" - echo "Ref name: ${{ github.ref_name }}" - echo "Available event data:" - echo "- PR number from inputs: ${{ github.event.inputs.pr_number }}" - echo "- PR number from issue: ${{ github.event.issue.number }}" - echo "- PR number from pull_request: ${{ github.event.pull_request.number }}" exit 1 fi - # Get PR data - if [[ -z "$PR_DATA" ]]; then - PR_DATA=$(gh pr view "$PR_NUMBER" --json headRefName,headRefOid) - if [[ -z "$PR_DATA" ]]; then - echo "Error: PR DATA for PR #$PR_NUMBER not found" - echo "Event type: ${{ github.event_name }}" - echo "Event action: ${{ github.event.action }}" - echo "Ref name: ${{ github.ref_name }}" - echo "Attempted to fetch PR data with: gh pr view $PR_NUMBER" - exit 1 - fi - fi - - # Extract and set PR data + # Set environment variables echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV - echo "APP_NAME=${{ vars.REVIEW_APP_PREFIX }}-$PR_NUMBER" >> $GITHUB_ENV - echo "PR_REF=$(echo $PR_DATA | jq -r .headRefName)" >> $GITHUB_OUTPUT - echo "PR_SHA=$(echo $PR_DATA | jq -r .headRefOid)" >> $GITHUB_ENV + echo "APP_NAME=qa-react-webpack-rails-tutorial-pr-$PR_NUMBER" >> $GITHUB_ENV + + # Get PR data using GitHub CLI + PR_DATA=$(gh pr view $PR_NUMBER --repo shakacode/react-webpack-rails-tutorial --json headRefName,headRefOid) + if [[ $? -eq 0 ]]; then + echo "PR_REF=$(echo $PR_DATA | jq -r .headRefName)" >> $GITHUB_OUTPUT + echo "PR_SHA=$(echo $PR_DATA | jq -r .headRefOid)" >> $GITHUB_ENV + else + echo "Error: Could not fetch PR data for PR #$PR_NUMBER" + exit 1 + fi - name: Checkout PR code if: github.event_name == 'workflow_dispatch' || github.event_name == 'issue_comment' @@ -172,6 +118,7 @@ jobs: - name: Check if Review App Exists id: check-app + if: github.event_name == 'pull_request' env: CPLN_TOKEN: ${{ secrets.CPLN_TOKEN_STAGING }} run: | @@ -181,68 +128,28 @@ jobs: exit 1 fi - # Check if app exists and save state + # Then check if app exists if ! cpflow exists -a ${{ env.APP_NAME }}; then - echo "APP_EXISTS=false" >> $GITHUB_ENV + echo "No review app exists for this PR" + echo "DO_DEPLOY=false" >> $GITHUB_ENV else - echo "APP_EXISTS=true" >> $GITHUB_ENV + echo "DO_DEPLOY=true" >> $GITHUB_ENV fi - name: Validate Deployment Request id: validate + if: env.DO_DEPLOY != 'false' run: | - # Skip validation if deployment is already disabled - if [[ "${{ env.DO_DEPLOY }}" == "false" ]]; then - echo "Skipping validation - deployment already disabled" - exit 0 - fi - if ! [[ "${{ github.event_name }}" == "workflow_dispatch" || \ - "${{ github.event_name }}" == "issue_comment" || \ - "${{ github.event_name }}" == "pull_request" || \ - "${{ github.event_name }}" == "push" ]]; then - echo "Error: Unsupported event type ${{ github.event_name }}" + ("${{ github.event_name }}" == "issue_comment" && "${{ github.event.comment.body }}" == "/deploy-review-app") || \ + "${{ github.event_name }}" == "pull_request" ]]; then + echo "Skipping deployment - not a valid trigger (event: ${{ github.event_name }})" exit 1 fi - # Set DO_DEPLOY based on event type and conditions - if [[ "${{ github.event_name }}" == "pull_request" && \ - ("${{ github.event.action }}" == "opened" || \ - "${{ github.event.action }}" == "synchronize" || \ - "${{ github.event.action }}" == "reopened") ]]; then - echo "DO_DEPLOY=true" >> $GITHUB_ENV - elif [[ "${{ github.event_name }}" == "push" ]]; then - echo "DO_DEPLOY=true" >> $GITHUB_ENV - elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then - echo "DO_DEPLOY=true" >> $GITHUB_ENV - elif [[ "${{ github.event_name }}" == "issue_comment" ]]; then - if [[ "${{ github.event.issue.pull_request }}" ]]; then - # Trim spaces and check for exact command - COMMENT_BODY=$(echo "${{ github.event.comment.body }}" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') - if [[ "$COMMENT_BODY" == "/deploy-review-app" ]]; then - echo "DO_DEPLOY=true" >> $GITHUB_ENV - else - echo "DO_DEPLOY=false" >> $GITHUB_ENV - echo "Skipping deployment - comment '$COMMENT_BODY' does not match '/deploy-review-app'" - fi - else - echo "DO_DEPLOY=false" >> $GITHUB_ENV - echo "Skipping deployment for non-PR comment" - fi - fi - - - name: Setup Control Plane App if Not Existing - if: env.DO_DEPLOY == 'true' && env.APP_EXISTS == 'false' - env: - CPLN_TOKEN: ${{ secrets.CPLN_TOKEN_STAGING }} - run: | - echo "🔧 Setting up new Control Plane app..." - 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: script: | const result = await github.rest.issues.createComment({ @@ -253,26 +160,6 @@ jobs: }); core.setOutput('comment-id', result.data.id); - - name: Update Comment - Building - if: env.DO_DEPLOY != 'false' - uses: actions/github-script@v7 - with: - script: | - const buildingMessage = [ - `🏗️ Building Docker image for PR #${process.env.PR_NUMBER}, commit ${process.env.PR_SHA}`, - '', - `📝 [View Build Logs](${process.env.WORKFLOW_URL})`, - '', - process.env.CONSOLE_LINK - ].join('\n'); - - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: ${{ steps.create-comment.outputs.comment-id }}, - body: buildingMessage - }); - - name: Set Deployment URLs id: set-urls if: env.DO_DEPLOY != 'false' @@ -286,23 +173,14 @@ jobs: repo: context.repo.repo, run_id: runId }); - - // Get the job ID for this specific job - const { data: jobs } = await github.rest.actions.listJobsForWorkflowRun({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: runId - }); - - const currentJob = jobs.jobs.find(job => job.name === context.job); - return `${run.html_url}/job/${currentJob.id}`; + return run.html_url; }; const workflowUrl = await getWorkflowUrl(context.runId); core.exportVariable('WORKFLOW_URL', workflowUrl); core.exportVariable('CONSOLE_LINK', '🎮 [Control Plane Console](' + - 'https://console.cpln.io/console/org/' + process.env.CPLN_ORG + '/gvc/' + process.env.APP_NAME + '/-info)' + 'https://console.cpln.io/console/org/' + process.env.CPLN_ORG_STAGING + '/gvc/' + process.env.APP_NAME + '/-info)' ); - name: Update Status - Building @@ -311,7 +189,7 @@ jobs: with: script: | const buildingMessage = [ - '🏗️ Building Docker image for PR #' + process.env.PR_NUMBER + ', commit ' + '${{ env.PR_SHA }}', + '🏗️ Building Docker image for PR #' + process.env.PR_NUMBER + ', commit ' + '${{ env.COMMIT_HASH }}', '', '📝 [View Build Logs](' + process.env.WORKFLOW_URL + ')', '', @@ -329,43 +207,13 @@ jobs: if: env.DO_DEPLOY != 'false' run: git checkout ${{ steps.getRef.outputs.PR_REF }} - - name: Initialize GitHub Deployment - if: env.DO_DEPLOY != 'false' - uses: actions/github-script@v7 - id: init-deployment - with: - script: | - const ref = process.env.PR_SHA; - const environment = process.env.ENVIRONMENT_NAME || 'review-app'; - - const deployment = await github.rest.repos.createDeployment({ - owner: context.repo.owner, - repo: context.repo.repo, - ref: ref, - environment: environment, - auto_merge: false, - required_contexts: [], - description: `Deployment for PR #${process.env.PR_NUMBER}` - }); - - // Create initial deployment status - await github.rest.repos.createDeploymentStatus({ - owner: context.repo.owner, - repo: context.repo.repo, - deployment_id: deployment.data.id, - state: 'in_progress', - description: 'Deployment started' - }); - - return deployment.data.id; - - name: Build Docker Image if: env.DO_DEPLOY != 'false' uses: ./.github/actions/build-docker-image with: app_name: ${{ env.APP_NAME }} - org: ${{ vars.CPLN_ORG_STAGING }} - commit: ${{ env.PR_SHA }} + org: ${{ env.CPLN_ORG_STAGING }} + commit: ${{ env.COMMIT_HASH }} PR_NUMBER: ${{ env.PR_NUMBER }} - name: Update Status - Deploying @@ -395,7 +243,7 @@ jobs: uses: ./.github/actions/deploy-to-control-plane with: app_name: ${{ env.APP_NAME }} - org: ${{ vars.CPLN_ORG_STAGING }} + org: ${{ env.CPLN_ORG_STAGING }} github_token: ${{ secrets.GITHUB_TOKEN }} wait_timeout: ${{ vars.WAIT_TIMEOUT || 900 }} cpln_token: ${{ secrets.CPLN_TOKEN_STAGING }} @@ -407,7 +255,7 @@ jobs: with: script: | const prNumber = process.env.PR_NUMBER; - const appUrl = process.env.APP_URL; + const appUrl = process.env.REVIEW_APP_URL; const workflowUrl = process.env.WORKFLOW_URL; const isSuccess = '${{ job.status }}' === 'success'; @@ -417,7 +265,7 @@ jobs: const deploymentStatus = { owner: context.repo.owner, repo: context.repo.repo, - deployment_id: ${{ steps.init-deployment.outputs.result }}, + deployment_id: ${{ fromJSON(steps.init-deployment.outputs.result).deploymentId }}, state: isSuccess ? 'success' : 'failure', environment_url: isSuccess ? appUrl : undefined, log_url: workflowUrl, @@ -428,7 +276,7 @@ jobs: // Define messages based on deployment status const successMessage = [ - '✅ Deployment complete for PR #' + prNumber + ', commit ' + '${{ env.PR_SHA }}', + '✅ Deployment complete for PR #' + prNumber + ', commit ' + '${{ env.COMMIT_HASH }}', '', '🚀 [Review App for PR #' + prNumber + '](' + appUrl + ')', consoleLink, @@ -437,7 +285,7 @@ jobs: ].join('\n'); const failureMessage = [ - '❌ Deployment failed for PR #' + prNumber + ', commit ' + '${{ env.PR_SHA }}', + '❌ Deployment failed for PR #' + prNumber + ', commit ' + '${{ env.COMMIT_HASH }}', '', consoleLink, '', diff --git a/.github/workflows/help-command.yml b/.github/workflows/help-command.yml index 121d4712b..37330a3e1 100644 --- a/.github/workflows/help-command.yml +++ b/.github/workflows/help-command.yml @@ -18,11 +18,6 @@ permissions: pull-requests: write jobs: - debug: - uses: ./.github/workflows/debug-workflow.yml - with: - debug_enabled: false # Will still run if vars.DEBUG_WORKFLOW is true - show-help: if: | github.event_name == 'workflow_dispatch' || diff --git a/.github/workflows/review-app-help.yml b/.github/workflows/review-app-help.yml index a54bfdf0e..fbe5d1986 100644 --- a/.github/workflows/review-app-help.yml +++ b/.github/workflows/review-app-help.yml @@ -9,11 +9,6 @@ permissions: pull-requests: write jobs: - debug: - uses: ./.github/workflows/debug-workflow.yml - with: - debug_enabled: false # Will still run if vars.DEBUG_WORKFLOW is true - show-quick-help: if: github.event_name == 'pull_request' runs-on: ubuntu-latest From eda7219f8a7385c0b9b082cd4dcde4eb8644dcc8 Mon Sep 17 00:00:00 2001 From: Judah Meek Date: Tue, 18 Mar 2025 18:08:38 -0500 Subject: [PATCH 3/7] Revert "More work on actions (#622)" This reverts commit ebbc8d86c99236388b4579f2396d2ab675e77387. --- .../deploy-to-control-plane/action.yml | 11 +- .github/actions/help-command/action.yml | 13 +- .github/actions/setup-environment/action.yml | 2 +- .github/readme.md | 85 ----- .github/workflows/deploy-to-control-plane.yml | 296 ++++++++++-------- .github/workflows/help-command.yml | 8 +- 6 files changed, 170 insertions(+), 245 deletions(-) delete mode 100644 .github/readme.md diff --git a/.github/actions/deploy-to-control-plane/action.yml b/.github/actions/deploy-to-control-plane/action.yml index 3ec17068a..b5282ce81 100644 --- a/.github/actions/deploy-to-control-plane/action.yml +++ b/.github/actions/deploy-to-control-plane/action.yml @@ -17,12 +17,6 @@ inputs: description: 'Timeout in seconds for waiting for workloads to be ready' required: false default: '900' - cpln_token: - description: 'Control Plane token' - required: true - pr_number: - description: 'Pull Request number' - required: true outputs: review_app_url: @@ -56,14 +50,11 @@ runs: run: ${{ github.action_path }}/scripts/get-commit-sha.sh env: GITHUB_TOKEN: ${{ inputs.github_token }} - PR_NUMBER: ${{ inputs.pr_number }} + PR_NUMBER: ${{ env.PR_NUMBER }} - name: Deploy to Control Plane id: deploy shell: bash - env: - CPLN_TOKEN: ${{ inputs.cpln_token }} - PR_NUMBER: ${{ inputs.pr_number }} run: | echo "🚀 Deploying app for PR #${PR_NUMBER}..." diff --git a/.github/actions/help-command/action.yml b/.github/actions/help-command/action.yml index 9988479c5..a84169a9b 100644 --- a/.github/actions/help-command/action.yml +++ b/.github/actions/help-command/action.yml @@ -5,15 +5,12 @@ inputs: github-token: description: 'GitHub token for posting comments' required: true - issue-number: - description: 'PR/Issue number to post the comment to (optional, defaults to event context)' - required: false runs: using: "composite" steps: - name: Show Available Commands - uses: actions/github-script@v7 + uses: actions/github-script with: github-token: ${{ inputs.github-token }} script: | @@ -83,14 +80,12 @@ runs: '3. Open an issue in this repository', ].join('\n'); - const issueNumber = inputs['issue-number'] || - (context.eventName === 'issue_comment' ? context.payload.issue.number : null); - - if (issueNumber) { + const context = github.context; + if (context.eventName === 'issue_comment') { await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, - issue_number: issueNumber, + issue_number: context.payload.issue.number, body: helpText }); } else { diff --git a/.github/actions/setup-environment/action.yml b/.github/actions/setup-environment/action.yml index 710b6c71e..1a086ee37 100644 --- a/.github/actions/setup-environment/action.yml +++ b/.github/actions/setup-environment/action.yml @@ -22,7 +22,7 @@ runs: - name: Install Control Plane CLI and cpflow gem shell: bash run: | - sudo npm install -g @controlplane/cli@3.3.1 + sudo npm install -g @controlplane/cli@3.3.0 cpln --version gem install cpflow -v 4.1.0 cpflow --version diff --git a/.github/readme.md b/.github/readme.md deleted file mode 100644 index 3b10fedc3..000000000 --- a/.github/readme.md +++ /dev/null @@ -1,85 +0,0 @@ -# Developing and Testing Github Actions - -Testing Github Actions on an existing repository is tricky. - -The main issue boils down to the fact that Github Actions uses the workflow files in the branch where the event originates. This is fine for push events, but it becomes a problem when you want to test workflows that are triggered by comments on a pull request. - -Here's a summary of the behavior: - -Behavior of push and pull_request Events - 1. Push on a Branch: - • When you push changes to a branch (e.g., feature-branch), GitHub Actions uses the workflow files in that same branch. - • This is why changes to workflows work seamlessly when testing with push events. - 2. Pull Request Events: - • For pull_request events (e.g., a PR from feature-branch into master), GitHub Actions will always use the workflow files from the target branch (e.g., master), not the source branch (e.g., feature-branch). - • This is a security feature to prevent someone from introducing malicious code in a PR that modifies the workflow files themselves. - -Impact on Comment-Triggered Workflows - -When you want to trigger workflows via comments (issue_comment) in a pull request: - • The workflow code used will always come from the master branch (or the default branch), regardless of the branch where the PR originates. - • This means the PR’s changes to the workflow won’t be used, and the action invoked by the comment will also use code from master. - -Workarounds to Test Comment-Triggered Workflows - -If you want to test workflows in a way that uses the changes in the pull request, here are your options: - -1. Use Push Events for Testing - • Test your changes on a branch with push triggers. - • Use workflow_dispatch to simulate the events you need (like invoking actions via comments). - -This allows you to confirm that your changes to the workflow file or actions behave as expected before merging into master. - -2. Merge the Workflow to master Temporarily - -If you absolutely need the workflow to run as part of a pull_request event: - 1. Merge your workflow changes into master temporarily. - 2. Open a PR to test your comment-triggered workflows. - 3. Revert the changes in master if necessary. - -This ensures the workflow changes are active in master while still testing with the pull_request context. - -3. Add Logic to Detect the Source Branch - -Use github.event.pull_request.head.ref to add custom logic in your workflow that behaves differently based on the source branch. - • Example: - -jobs: - test-pr: - runs-on: ubuntu-latest - if: ${{ github.event.pull_request.head.ref == 'feature-branch' }} - steps: - - name: Checkout Code - uses: actions/checkout@v3 - - - name: Debug - run: echo "Testing workflow changes in feature-branch" - -However, this still requires the workflow itself to exist in master. - -4. Use a Fork or a Temporary Repo - -Create a temporary repository or a fork to test workflows in isolation: - • Push your workflow changes to master in the test repository. - • Open a PR in the fork to test how workflows behave with issue_comment events and PR contexts. - -Once confirmed, you can replicate the changes in your main repository. - -6. Alternative Approach: Split Workflows - -If your workflow includes comment-based triggers (issue_comment), consider splitting your workflows: - • A base workflow in master that handles triggering. - • A test-specific workflow for validating changes on a branch. - -For example: - 1. The base workflow triggers when a comment like /run-tests is added. - 2. The test-specific workflow runs in response to the base workflow but uses the branch’s code. - -Summary - • For push events: The branch-specific workflow is used, so testing changes is easy. - • For pull_request and issue_comment events: GitHub always uses workflows from the master branch, and there’s no direct way to bypass this. - -To test comment-triggered workflows: - 1. Use push or workflow_dispatch to validate changes. - 2. Merge workflow changes temporarily into master to test with pull_request events. - 3. Use tools like act for local simulation. diff --git a/.github/workflows/deploy-to-control-plane.yml b/.github/workflows/deploy-to-control-plane.yml index 60f081c35..dd6110e21 100644 --- a/.github/workflows/deploy-to-control-plane.yml +++ b/.github/workflows/deploy-to-control-plane.yml @@ -1,40 +1,28 @@ name: Deploy Review App to Control Plane -run-name: Deploy Review App - ${{ github.ref_name }} +run-name: ${{ github.event_name == 'issue_comment' && 'Deploying Review App' || format('Updating Review App for {0}', github.ref_name) }} on: pull_request: types: [opened, synchronize, reopened] - push: - branches: - - '**' # Any branch - - '!main' # Except main - - '!master' # Except master issue_comment: types: [created] - workflow_dispatch: - inputs: - pr_number: - description: 'Pull Request number to deploy' - required: true - type: number +# Use concurrency to cancel in-progress runs concurrency: - group: deploy-pr-${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }} + group: deploy-pr-${{ github.event.pull_request.number || github.event.issue.number }} cancel-in-progress: true env: - APP_NAME: qa-react-webpack-rails-tutorial-pr-${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }} + APP_NAME: qa-react-webpack-rails-tutorial-pr-${{ github.event.pull_request.number || github.event.issue.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 }} + PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }} jobs: Process-Deployment-Command: if: | (github.event_name == 'pull_request') || - (github.event_name == 'push') || - (github.event_name == 'workflow_dispatch') || (github.event_name == 'issue_comment' && github.event.issue.pull_request && github.event.comment.body == '/deploy-review-app') @@ -46,109 +34,80 @@ jobs: issues: write steps: - # Initial checkout only for pull_request and push events - - name: Checkout code - if: github.event_name == 'pull_request' || github.event_name == 'push' - uses: actions/checkout@v4 + - uses: actions/checkout@v4 with: fetch-depth: 0 - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }} + ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || steps.getRef.outputs.PR_REF || github.ref }} - # Basic checkout for other events (workflow_dispatch, issue_comment) - # We'll do proper checkout after getting PR info - - name: Initial checkout - if: github.event_name == 'workflow_dispatch' || github.event_name == 'issue_comment' - uses: actions/checkout@v4 + - name: Setup Environment + uses: ./.github/actions/setup-environment with: - fetch-depth: 0 + token: ${{ env.CPLN_TOKEN }} + org: ${{ env.CPLN_ORG }} - name: Get PR HEAD Ref - id: getRef - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + if: github.event_name == 'issue_comment' run: | - if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then - PR_NUMBER="${{ github.event.inputs.pr }}" - elif [[ "${{ github.event_name }}" == "issue_comment" ]]; then - PR_NUMBER="${{ github.event.issue.number }}" - elif [[ "${{ github.event_name }}" == "pull_request" ]]; then - PR_NUMBER="${{ github.event.pull_request.number }}" - elif [[ "${{ github.event_name }}" == "push" ]]; then - # For push events, find associated PR - PR_DATA=$(gh pr list --head "${{ github.ref_name }}" --json number --jq '.[0].number') - if [[ -n "$PR_DATA" ]]; then - PR_NUMBER="$PR_DATA" - else - echo "Error: No PR found for branch ${{ github.ref_name }}" - exit 1 - fi - fi - - if [[ -z "$PR_NUMBER" ]]; then - echo "Error: Could not determine PR number" - exit 1 - fi - - # Set environment variables - echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV - echo "APP_NAME=qa-react-webpack-rails-tutorial-pr-$PR_NUMBER" >> $GITHUB_ENV - - # Get PR data using GitHub CLI - PR_DATA=$(gh pr view $PR_NUMBER --repo shakacode/react-webpack-rails-tutorial --json headRefName,headRefOid) - if [[ $? -eq 0 ]]; then - echo "PR_REF=$(echo $PR_DATA | jq -r .headRefName)" >> $GITHUB_OUTPUT - echo "PR_SHA=$(echo $PR_DATA | jq -r .headRefOid)" >> $GITHUB_ENV - else - echo "Error: Could not fetch PR data for PR #$PR_NUMBER" - exit 1 - fi - - - name: Checkout PR code - if: github.event_name == 'workflow_dispatch' || github.event_name == 'issue_comment' - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ steps.getRef.outputs.PR_SHA }} - - - name: Setup Environment - uses: ./.github/actions/setup-environment - with: - token: ${{ secrets.CPLN_TOKEN_STAGING }} - org: ${{ vars.CPLN_ORG_STAGING }} + echo "PR_NUMBER=${{ github.event.issue.number }}" >> $GITHUB_ENV + echo "APP_NAME=qa-react-webpack-rails-tutorial-pr-${{ github.event.issue.number }}" >> $GITHUB_ENV + # For PR comments, get the actual PR head commit + PR_DATA=$(gh pr view $PR_NUMBER --repo ${{ github.repository }} --json headRefName,headRefOid) + echo "PR_REF=$(echo "$PR_DATA" | jq -r '.headRefName')" >> $GITHUB_OUTPUT + echo "PR_SHA=$(echo "$PR_DATA" | jq -r '.headRefOid')" >> $GITHUB_OUTPUT + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Check if Review App Exists id: check-app - if: github.event_name == 'pull_request' + if: github.event_name == 'push' env: - CPLN_TOKEN: ${{ secrets.CPLN_TOKEN_STAGING }} + CPLN_TOKEN: ${{ secrets.CPLN_TOKEN }} run: | - # First check if cpflow exists - if ! command -v cpflow &> /dev/null; then - echo "Error: cpflow command not found" - exit 1 - fi - - # Then check if app exists if ! cpflow exists -a ${{ env.APP_NAME }}; then echo "No review app exists for this PR" - echo "DO_DEPLOY=false" >> $GITHUB_ENV - else - echo "DO_DEPLOY=true" >> $GITHUB_ENV + exit 0 fi + echo "app_exists=true" >> $GITHUB_OUTPUT - - name: Validate Deployment Request - id: validate - if: env.DO_DEPLOY != 'false' - run: | - if ! [[ "${{ github.event_name }}" == "workflow_dispatch" || \ - ("${{ github.event_name }}" == "issue_comment" && "${{ github.event.comment.body }}" == "/deploy-review-app") || \ - "${{ github.event_name }}" == "pull_request" ]]; then - echo "Skipping deployment - not a valid trigger (event: ${{ github.event_name }})" - exit 1 - fi + + - name: Set Workflow URL + id: workflow-url + uses: actions/github-script@v7 + with: + script: | + async function getWorkflowUrl(runId) { + const jobs = await github.rest.actions.listJobsForWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: runId + }); + + const currentJob = jobs.data.jobs.find(job => job.status === 'in_progress'); + const jobId = currentJob?.id; + + if (!jobId) { + return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`; + } + + return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}/job/${jobId}`; + } + + const workflowUrl = await getWorkflowUrl(context.runId); + core.exportVariable('WORKFLOW_URL', workflowUrl); + core.exportVariable('GET_CONSOLE_LINK', ` + function getConsoleLink(prNumber) { + return '🎮 [Control Plane Console](' + + 'https://console.cpln.io/console/org/' + process.env.CPLN_ORG + '/gvc/' + process.env.APP_NAME + '/-info)'; + } + `); - name: Create Initial Comment - if: env.DO_DEPLOY != 'false' + if: | + (github.event_name == 'issue_comment' && + github.event.issue.pull_request && + github.event.comment.body == '/deploy-review-app') || + ( steps.check-app.outputs.app_exists == 'true') + id: create-comment uses: actions/github-script@v7 with: script: | @@ -156,71 +115,137 @@ 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...' }); - core.setOutput('comment-id', result.data.id); + console.log('Created comment:', result.data.id); + return { commentId: result.data.id }; - - name: Set Deployment URLs - id: set-urls - if: env.DO_DEPLOY != 'false' + - name: Set Comment ID + if: | + (github.event_name == 'issue_comment' && + github.event.issue.pull_request && + github.event.comment.body == '/deploy-review-app') || + (steps.check-app.outputs.app_exists == 'true') + run: echo "COMMENT_ID=${{ fromJSON(steps.create-comment.outputs.result).commentId }}" >> $GITHUB_ENV + + - name: Initialize Deployment + id: init-deployment uses: actions/github-script@v7 with: script: | - // Set workflow URL for logs - const getWorkflowUrl = async (runId) => { - const { data: run } = await github.rest.actions.getWorkflowRun({ + async function getWorkflowUrl(runId) { + const jobs = await github.rest.actions.listJobsForWorkflowRun({ owner: context.repo.owner, repo: context.repo.repo, run_id: runId }); - return run.html_url; - }; + + const currentJob = jobs.data.jobs.find(job => job.status === 'in_progress'); + const jobId = currentJob?.id; + + if (!jobId) { + console.log('Warning: Could not find current job ID'); + return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`; + } + + return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}/job/${jobId}`; + } + // Create initial deployment comment + const comment = await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: process.env.PR_NUMBER, + body: '⏳ Initializing deployment...' + }); + + // Create GitHub deployment + const deployment = await github.rest.repos.createDeployment({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: context.sha, + environment: 'review', + auto_merge: false, + required_contexts: [] + }); + const workflowUrl = await getWorkflowUrl(context.runId); - core.exportVariable('WORKFLOW_URL', workflowUrl); - core.exportVariable('CONSOLE_LINK', - '🎮 [Control Plane Console](' + - 'https://console.cpln.io/console/org/' + process.env.CPLN_ORG_STAGING + '/gvc/' + process.env.APP_NAME + '/-info)' - ); + + return { + deploymentId: deployment.data.id, + commentId: comment.data.id, + workflowUrl + }; + + - name: Set comment ID and workflow URL + run: | + echo "COMMENT_ID=${{ fromJSON(steps.init-deployment.outputs.result).commentId }}" >> $GITHUB_ENV + echo "WORKFLOW_URL=${{ fromJSON(steps.init-deployment.outputs.result).workflowUrl }}" >> $GITHUB_ENV + + - name: Set commit hash + run: | + FULL_COMMIT="${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || steps.getRef.outputs.PR_SHA || github.sha }}" + echo "COMMIT_HASH=${FULL_COMMIT:0:7}" >> $GITHUB_ENV - name: Update Status - Building - if: env.DO_DEPLOY != 'false' + if: | + (github.event_name == 'issue_comment' && + github.event.issue.pull_request && + github.event.comment.body == '/deploy-review-app') || + (steps.check-app.outputs.app_exists == 'true') uses: actions/github-script@v7 with: script: | + eval(process.env.GET_CONSOLE_LINK); + const buildingMessage = [ '🏗️ Building Docker image for PR #' + process.env.PR_NUMBER + ', commit ' + '${{ env.COMMIT_HASH }}', + '🏗️ Building Docker image...', '', '📝 [View Build Logs](' + process.env.WORKFLOW_URL + ')', '', - process.env.CONSOLE_LINK + getConsoleLink(process.env.PR_NUMBER) ].join('\n'); await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, - comment_id: ${{ steps.create-comment.outputs.comment-id }}, + comment_id: process.env.COMMENT_ID, body: buildingMessage }); - name: Checkout PR Branch - if: env.DO_DEPLOY != 'false' + if: | + (github.event_name == 'issue_comment' && + github.event.issue.pull_request && + github.event.comment.body == '/deploy-review-app') || + (steps.check-app.outputs.app_exists == 'true') run: git checkout ${{ steps.getRef.outputs.PR_REF }} - name: Build Docker Image - if: env.DO_DEPLOY != 'false' + if: | + (github.event_name == 'issue_comment' && + github.event.issue.pull_request && + github.event.comment.body == '/deploy-review-app') || + (steps.check-app.outputs.app_exists == 'true') uses: ./.github/actions/build-docker-image with: app_name: ${{ env.APP_NAME }} - org: ${{ env.CPLN_ORG_STAGING }} + org: ${{ env.CPLN_ORG }} commit: ${{ env.COMMIT_HASH }} PR_NUMBER: ${{ env.PR_NUMBER }} - name: Update Status - Deploying - if: env.DO_DEPLOY != 'false' + if: | + (github.event_name == 'issue_comment' && + github.event.issue.pull_request && + github.event.comment.body == '/deploy-review-app') || + (steps.check-app.outputs.app_exists == 'true') uses: actions/github-script@v7 with: script: | + eval(process.env.GET_CONSOLE_LINK); + const deployingMessage = [ '🚀 Deploying to Control Plane...', '', @@ -228,29 +253,33 @@ jobs: '', '📝 [View Deploy Logs](' + process.env.WORKFLOW_URL + ')', '', - process.env.CONSOLE_LINK + getConsoleLink(process.env.PR_NUMBER) ].join('\n'); await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, - comment_id: ${{ steps.create-comment.outputs.comment-id }}, + comment_id: process.env.COMMENT_ID, body: deployingMessage }); - name: Deploy to Control Plane - if: env.DO_DEPLOY != 'false' + if: | + (github.event_name == 'issue_comment' && + github.event.issue.pull_request && + github.event.comment.body == '/deploy-review-app') || + (steps.check-app.outputs.app_exists == 'true') uses: ./.github/actions/deploy-to-control-plane with: app_name: ${{ env.APP_NAME }} - org: ${{ env.CPLN_ORG_STAGING }} + org: ${{ env.CPLN_ORG }} github_token: ${{ secrets.GITHUB_TOKEN }} wait_timeout: ${{ vars.WAIT_TIMEOUT || 900 }} - cpln_token: ${{ secrets.CPLN_TOKEN_STAGING }} - pr_number: ${{ env.PR_NUMBER }} + env: + CPLN_TOKEN: ${{ env.CPLN_TOKEN }} + PR_NUMBER: ${{ env.PR_NUMBER }} - name: Update Status - Deployment Complete - if: env.DO_DEPLOY != 'false' uses: actions/github-script@v7 with: script: | @@ -259,7 +288,8 @@ jobs: const workflowUrl = process.env.WORKFLOW_URL; const isSuccess = '${{ job.status }}' === 'success'; - const consoleLink = process.env.CONSOLE_LINK; + const consoleLink = '🎮 [Control Plane Console](https://console.cpln.io/console/org/' + + process.env.CPLN_ORG + '/gvc/' + process.env.APP_NAME + '/-info)'; // Create GitHub deployment status const deploymentStatus = { @@ -296,6 +326,6 @@ jobs: await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, - comment_id: ${{ steps.create-comment.outputs.comment-id }}, + comment_id: process.env.COMMENT_ID, body: isSuccess ? successMessage : failureMessage }); diff --git a/.github/workflows/help-command.yml b/.github/workflows/help-command.yml index 37330a3e1..4d92f6674 100644 --- a/.github/workflows/help-command.yml +++ b/.github/workflows/help-command.yml @@ -7,11 +7,6 @@ on: issue_comment: types: [created] workflow_dispatch: - inputs: - issue-number: - description: 'PR/Issue number to post the help comment to' - required: true - type: number permissions: issues: write @@ -30,8 +25,7 @@ jobs: - name: Checkout uses: actions/checkout - - name: Process Help Command + - name: Show Help Information uses: ./.github/actions/help-command with: github-token: ${{ secrets.GITHUB_TOKEN }} - issue-number: ${{ github.event.inputs.issue-number }} \ No newline at end of file From fbfa9eea679537341e17e94f3b795ab1408083d9 Mon Sep 17 00:00:00 2001 From: Judah Meek Date: Tue, 18 Mar 2025 18:08:41 -0500 Subject: [PATCH 4/7] Revert "More work on github actions (#621)" This reverts commit 6add0864ed7c06b577160013c904fca9939e5abd. --- .github/actions/help-command/action.yml | 93 ------------------ .github/workflows/delete-review-app.yml | 4 +- .../deploy-to-control-plane-staging.yml | 2 +- .github/workflows/deploy-to-control-plane.yml | 6 +- .github/workflows/help-command.yml | 95 ++++++++++++++++++- .../nightly-remove-stale-review-apps.yml | 2 +- .../promote-staging-to-production.yml | 2 +- .github/workflows/review-app-help.yml | 3 +- 8 files changed, 100 insertions(+), 107 deletions(-) delete mode 100644 .github/actions/help-command/action.yml diff --git a/.github/actions/help-command/action.yml b/.github/actions/help-command/action.yml deleted file mode 100644 index a84169a9b..000000000 --- a/.github/actions/help-command/action.yml +++ /dev/null @@ -1,93 +0,0 @@ -name: 'Show Help Command' -description: 'Displays help information for available commands in PR comments' - -inputs: - github-token: - description: 'GitHub token for posting comments' - required: true - -runs: - using: "composite" - steps: - - name: Show Available Commands - uses: actions/github-script - with: - github-token: ${{ inputs.github-token }} - script: | - const helpText = [ - '# Available Commands', - '', - '## `/deploy`', - '**Purpose:** Deploy a review app for your pull request', - '', - '**What it does:**', - '- Creates a new review app in Control Plane', - '- Deploys your changes to the review environment', - '- Provides a unique URL to preview your changes', - '- Shows build and deployment progress in real-time', - '', - '**Optional Configuration:**', - '- `WAIT_TIMEOUT`: Deployment timeout in seconds (default: 900)', - ' - Must be a positive integer', - ' - Example: `/deploy timeout=1800`', - '', - '## `/destroy`', - '**Purpose:** Remove the review app for your pull request', - '', - '**What it does:**', - '- Deletes the review app from Control Plane', - '- Cleans up associated resources', - '- Updates PR with deletion status', - '', - '---', - '## Environment Setup', - '', - '**Required Environment Secrets:**', - '- `CPLN_TOKEN_STAGING`: Control Plane authentication token', - '- `CPLN_TOKEN_PRODUCTION`: Control Plane authentication token', - '', - '**Required GitHub Actions Variables:**', - '- `CPLN_ORG_STAGING`: Control Plane authentication token', - '- `CPLN_ORG_PRODUCTION`: Control Plane authentication token', - '', - '**Required GitHub Actions Variables (these need to match your control_plane.yml file:**', - '- `PRODUCTION_APP_NAME`: Control Plane production app name', - '- `STAGING_APP_NAME`: Control Plane staging app name', - '- `REVIEW_APP_PREFIX`: Control Plane review app prefix', - '', - 'Optional: Configure `WAIT_TIMEOUT` in GitHub Actions variables to customize deployment timeout', - '', - '## Control Plane Integration', - '', - '1. Review app naming convention:', - ' ```', - ' ${{ vars.REVIEW_APP_PREFIX }}-', - ' ```', - '2. Console URL: `https://console.cpln.io/console/org/{CPLN_ORG}/gvc/{APP_NAME}/-info`', - '', - '## Automatic Cleanup', - '', - 'Review apps are automatically destroyed when:', - '1. The pull request is closed', - '2. The `/destroy` command is used', - '3. A new deployment is requested (old one is cleaned up first)', - '', - '## Need Help?', - '', - 'For additional assistance:', - '1. Check the [Control Plane documentation](https://docs.controlplane.com/)', - '2. Contact the infrastructure team', - '3. Open an issue in this repository', - ].join('\n'); - - const context = github.context; - if (context.eventName === 'issue_comment') { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.payload.issue.number, - body: helpText - }); - } else { - console.log(helpText); - } diff --git a/.github/workflows/delete-review-app.yml b/.github/workflows/delete-review-app.yml index b45282e81..3f547579d 100644 --- a/.github/workflows/delete-review-app.yml +++ b/.github/workflows/delete-review-app.yml @@ -58,7 +58,7 @@ jobs: fi - name: Setup Environment - uses: ./.github/actions/setup-environment + uses: ./.github/actions/setup-environment@justin808-working-for-deploys with: org: ${{ env.CPLN_ORG }} token: ${{ env.CPLN_TOKEN }} @@ -132,7 +132,7 @@ jobs: return { commentId: comment.data.id }; - name: Delete Review App - uses: ./.github/actions/delete-control-plane-app + uses: ./.github/actions/delete-control-plane-app@justin808-working-for-deploys with: app_name: ${{ env.APP_NAME }} org: ${{ env.CPLN_ORG }} diff --git a/.github/workflows/deploy-to-control-plane-staging.yml b/.github/workflows/deploy-to-control-plane-staging.yml index 898c447c1..b56511b11 100644 --- a/.github/workflows/deploy-to-control-plane-staging.yml +++ b/.github/workflows/deploy-to-control-plane-staging.yml @@ -26,7 +26,7 @@ jobs: fetch-depth: 0 # Fetch all history for proper SHA handling ref: master # Explicitly checkout master branch - - uses: ./.github/actions/deploy-to-control-plane + - uses: ./.github/actions/deploy-to-control-plane@justin808-working-for-deploys with: app_name: ${{ vars.STAGING_APP_NAME }} org: ${{ vars.CPLN_ORG_STAGING }} diff --git a/.github/workflows/deploy-to-control-plane.yml b/.github/workflows/deploy-to-control-plane.yml index dd6110e21..35e26082f 100644 --- a/.github/workflows/deploy-to-control-plane.yml +++ b/.github/workflows/deploy-to-control-plane.yml @@ -40,7 +40,7 @@ jobs: ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || steps.getRef.outputs.PR_REF || github.ref }} - name: Setup Environment - uses: ./.github/actions/setup-environment + uses: ./.github/actions/setup-environment@justin808-working-for-deploys with: token: ${{ env.CPLN_TOKEN }} org: ${{ env.CPLN_ORG }} @@ -228,7 +228,7 @@ jobs: github.event.issue.pull_request && github.event.comment.body == '/deploy-review-app') || (steps.check-app.outputs.app_exists == 'true') - uses: ./.github/actions/build-docker-image + uses: ./.github/actions/build-docker-image@justin808-working-for-deploys with: app_name: ${{ env.APP_NAME }} org: ${{ env.CPLN_ORG }} @@ -269,7 +269,7 @@ jobs: github.event.issue.pull_request && github.event.comment.body == '/deploy-review-app') || (steps.check-app.outputs.app_exists == 'true') - uses: ./.github/actions/deploy-to-control-plane + uses: ./.github/actions/deploy-to-control-plane@justin808-working-for-deploys with: app_name: ${{ env.APP_NAME }} org: ${{ env.CPLN_ORG }} diff --git a/.github/workflows/help-command.yml b/.github/workflows/help-command.yml index 4d92f6674..90f19a2c5 100644 --- a/.github/workflows/help-command.yml +++ b/.github/workflows/help-command.yml @@ -22,10 +22,95 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout + - name: Identify Workflow Branch + run: | + echo "Workflow branch ref: ${{ github.ref }}" - - name: Show Help Information - uses: ./.github/actions/help-command + - name: Show Available Commands + uses: actions/github-script@v7 with: - github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + try { + console.log('Creating detailed help message...'); + const helpMessage = [ + '# 📚 Detailed Review App Commands Guide', + '', + 'This is a detailed guide to using review app commands. For a quick reference, see the message posted when your PR was created.', + '', + '## Available Commands', + '', + '### `/deploy-review-app`', + 'Deploys your PR branch to a review environment on Control Plane.', + '- Creates a new review app if one doesn\'t exist', + '- Updates the existing review app if it already exists', + '- Provides a unique URL to preview your changes', + '- Shows build and deployment progress in real-time', + '', + '**Required Environment Secrets:**', + '- `CPLN_TOKEN_STAGING`: Control Plane authentication token', + '- `CPLN_TOKEN_PRODUCTION`: Control Plane authentication token', + '', + '**Required GitHub Actions Variables:**', + '- `CPLN_ORG_STAGING`: Control Plane authentication token', + '- `CPLN_ORG_PRODUCTION`: Control Plane authentication token', + '', + '**Required GitHub Actions Variables (these need to match your control_plane.yml file:**', + '- `PRODUCTION_APP_NAME`: Control Plane production app name', + '- `STAGING_APP_NAME`: Control Plane staging app name', + '- `REVIEW_APP_PREFIX`: Control Plane review app prefix', + '', + '**Optional Configuration:**', + '- `WAIT_TIMEOUT`: Deployment timeout in seconds (default: 900)', + ' - Must be a positive integer', + ' - Can be set in GitHub Actions variables', + ' - Applies to both deployment and workload readiness checks', + '', + '### `/delete-review-app`', + 'Deletes the review app associated with this PR.', + '- Removes all resources from Control Plane', + '- Helpful for cleaning up when you\'re done testing', + '- Can be re-deployed later using `/deploy-review-app`', + '', + '**Required Environment Variables:**', + '- `CPLN_TOKEN`: Control Plane authentication token', + '- `CPLN_ORG`: Control Plane organization name', + '', + '### `/help`', + 'Shows this detailed help message.', + '', + '---', + '## Environment Setup', + '', + '1. Set required secrets in your repository settings:', + ' - `CPLN_TOKEN`', + ' - `CPLN_ORG`', + '', + '2. Optional: Configure `WAIT_TIMEOUT` in GitHub Actions variables to customize deployment timeout', + '', + '## Control Plane Integration', + '', + 'Review apps are deployed to Control Plane with the following configuration:', + '- App Name Format: `qa-react-webpack-rails-tutorial-pr-{PR_NUMBER}`', + '- Console URL: `https://console.cpln.io/console/org/{CPLN_ORG}/gvc/{APP_NAME}/-info`', + '', + '## Automatic Cleanup', + '', + 'Review apps are automatically deleted when:', + '- The PR is closed (merged or not merged)', + '- The PR is stale (via nightly cleanup job)', + '', + 'For more information, see the [React on Rails Tutorial documentation](https://github.com/shakacode/react-on-rails/tree/master/react-webpack-rails-tutorial)' + ].join('\n'); + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: helpMessage + }); + + console.log('Help message posted successfully'); + } catch (error) { + console.error('Error posting help message:', error); + core.setFailed(`Failed to post help message: ${error.message}`); + } diff --git a/.github/workflows/nightly-remove-stale-review-apps.yml b/.github/workflows/nightly-remove-stale-review-apps.yml index c5f0376ae..0e8630f35 100644 --- a/.github/workflows/nightly-remove-stale-review-apps.yml +++ b/.github/workflows/nightly-remove-stale-review-apps.yml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Environment - uses: ./.github/actions/setup-environment + uses: ./.github/actions/setup-environment@justin808-working-for-deploys - name: Get Stale PRs id: stale_prs diff --git a/.github/workflows/promote-staging-to-production.yml b/.github/workflows/promote-staging-to-production.yml index 041480671..06cea9cdd 100644 --- a/.github/workflows/promote-staging-to-production.yml +++ b/.github/workflows/promote-staging-to-production.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Environment - uses: ./.github/actions/setup-environment + uses: ./.github/actions/setup-environment@justin808-working-for-deploys env: CPLN_TOKEN: ${{ secrets.CPLN_TOKEN }} diff --git a/.github/workflows/review-app-help.yml b/.github/workflows/review-app-help.yml index fbe5d1986..313101e4b 100644 --- a/.github/workflows/review-app-help.yml +++ b/.github/workflows/review-app-help.yml @@ -32,9 +32,10 @@ jobs: 'Remove the review app when done', '', '### `/help`', - 'Show detailed instructions, environment setup, and configuration options.' + 'Show detailed documentation', '', '---', + '**Note:** Type `/help` for detailed instructions, environment setup, and configuration options.' ].join('\n'); await github.rest.issues.createComment({ From fcc5caf8b17ad9931c6c4da36d0149c11a6719af Mon Sep 17 00:00:00 2001 From: Judah Meek Date: Tue, 18 Mar 2025 18:08:42 -0500 Subject: [PATCH 5/7] Revert "Building on working for review apps (#620)" This reverts commit 098b88618be65ae8d129907721c804db9e0822f1. --- .controlplane/controlplane.yml | 2 +- .../deploy-to-control-plane/action.yml | 248 ++++++++++++++---- .github/actions/setup-environment/action.yml | 25 +- .github/workflows/delete-review-app.yml | 29 +- .../deploy-to-control-plane-staging.yml | 10 +- .github/workflows/deploy-to-control-plane.yml | 214 ++++++--------- .github/workflows/help-command.yml | 43 ++- .../nightly-remove-stale-review-apps.yml | 2 +- .../promote-staging-to-production.yml | 2 +- 9 files changed, 311 insertions(+), 264 deletions(-) diff --git a/.controlplane/controlplane.yml b/.controlplane/controlplane.yml index f394fe1cb..53f687300 100644 --- a/.controlplane/controlplane.yml +++ b/.controlplane/controlplane.yml @@ -38,7 +38,7 @@ aliases: release_script: release_script.sh apps: - react-webpack-rails-tutorial-production: + react-webpack-rails-tutorial: # Simulate Production Version <<: *common # Don't allow overriding the org and app by ENV vars b/c production is sensitive! diff --git a/.github/actions/deploy-to-control-plane/action.yml b/.github/actions/deploy-to-control-plane/action.yml index b5282ce81..b8f9b7332 100644 --- a/.github/actions/deploy-to-control-plane/action.yml +++ b/.github/actions/deploy-to-control-plane/action.yml @@ -44,59 +44,205 @@ runs: - name: Setup Environment uses: ./.github/actions/setup-environment - - name: Get Commit SHA - id: get_sha + - name: Set shared functions + id: shared-functions + uses: actions/github-script@v7 + with: + script: | + core.exportVariable('GET_CONSOLE_LINK', ` + function getConsoleLink(prNumber) { + return ' [Control Plane Console for Review App with PR #' + prNumber + '](' + + 'https://console.cpln.io/org/' + process.env.CPLN_ORG + '/workloads/' + process.env.APP_NAME + ')'; + } + `); + + - name: Initialize Deployment + id: init-deployment + uses: actions/github-script@v7 + with: + script: | + eval(process.env.GET_CONSOLE_LINK); + + async function getWorkflowUrl(runId) { + // Get the current job ID + const jobs = await github.rest.actions.listJobsForWorkflowRun({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: runId + }); + + const currentJob = jobs.data.jobs.find(job => job.status === 'in_progress'); + const jobId = currentJob?.id; + + if (!jobId) { + console.log('Warning: Could not find current job ID'); + return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`; + } + + return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}/job/${jobId}`; + } + + // Create initial deployment comment + const comment = await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: process.env.PR_NUMBER, + body: ' Initializing deployment...' + }); + + // Create GitHub deployment + const deployment = await github.rest.repos.createDeployment({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: context.sha, + environment: 'review', + auto_merge: false, + required_contexts: [] + }); + + const workflowUrl = await getWorkflowUrl(context.runId); + + core.exportVariable('WORKFLOW_URL', workflowUrl); + core.exportVariable('COMMENT_ID', comment.data.id); + core.exportVariable('DEPLOYMENT_ID', deployment.data.id); + + - name: Set commit hash shell: bash - run: ${{ github.action_path }}/scripts/get-commit-sha.sh - env: - GITHUB_TOKEN: ${{ inputs.github_token }} - PR_NUMBER: ${{ env.PR_NUMBER }} + run: | + FULL_COMMIT=$(git rev-parse HEAD) + echo "COMMIT_HASH=${FULL_COMMIT:0:7}" >> $GITHUB_ENV - - name: Deploy to Control Plane - id: deploy + - name: Update Status - Setting Up + uses: actions/github-script@v7 + with: + script: | + eval(process.env.GET_CONSOLE_LINK); + + const setupMessage = [ + '🔧 Setting up Control Plane app...', + '', + ' [View Setup Logs](' + process.env.WORKFLOW_URL + ')', + '', + getConsoleLink(process.env.PR_NUMBER) + ].join('\n'); + + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: process.env.COMMENT_ID, + body: setupMessage + }); + + - name: Setup Control Plane App shell: bash run: | - echo "🚀 Deploying app for PR #${PR_NUMBER}..." - - # Create temp file for output - TEMP_OUTPUT=$(mktemp) - trap 'rm -f "${TEMP_OUTPUT}"' EXIT - - # Deploy the application and show output in real-time while capturing it - if ! cpflow deploy-image -a "${{ inputs.app_name }}" --run-release-phase --org "${{ inputs.org }}" 2>&1 | tee "${TEMP_OUTPUT}"; then - echo "❌ Deployment failed for PR #${PR_NUMBER}" - echo "Error output:" - cat "${TEMP_OUTPUT}" - exit 1 + echo "🔧 Checking if app exists..." + if ! cpflow exists -a ${{ inputs.app_name }} ; then + echo "📦 Setting up new Control Plane app..." + cpflow setup-app -a ${{ inputs.app_name }} fi - - # Extract app URL from captured output - REVIEW_APP_URL=$(grep -oP 'https://rails-[^[:space:]]*\.cpln\.app(?=\s|$)' "${TEMP_OUTPUT}" | head -n1) - if [ -z "${REVIEW_APP_URL}" ]; then - echo "❌ Failed to get app URL from deployment output" - echo "Deployment output:" - cat "${TEMP_OUTPUT}" - exit 1 - fi - - # Wait for all workloads to be ready - WAIT_TIMEOUT=${WAIT_TIMEOUT:-${{ inputs.wait_timeout }}} - echo "⏳ Waiting for all workloads to be ready (timeout: ${WAIT_TIMEOUT}s)..." - - # Use timeout command with ps:wait and show output in real-time - if ! timeout "${WAIT_TIMEOUT}" bash -c "cpflow ps:wait -a \"${{ inputs.app_name }}\"" 2>&1 | tee -a "${TEMP_OUTPUT}"; then - TIMEOUT_EXIT=$? - if [ ${TIMEOUT_EXIT} -eq 124 ]; then - echo "❌ Timed out waiting for workloads after ${WAIT_TIMEOUT} seconds" - else - echo "❌ Workloads did not become ready for PR #${PR_NUMBER} (exit code: ${TIMEOUT_EXIT})" - fi - echo "Full output:" - cat "${TEMP_OUTPUT}" - exit 1 - fi - - echo "✅ Deployment successful for PR #${PR_NUMBER}" - echo "🌐 App URL: ${REVIEW_APP_URL}" - echo "review_app_url=${REVIEW_APP_URL}" >> $GITHUB_OUTPUT - echo "REVIEW_APP_URL=${REVIEW_APP_URL}" >> $GITHUB_ENV + + - name: Update Status - Building + uses: actions/github-script@v7 + with: + script: | + eval(process.env.GET_CONSOLE_LINK); + + const buildingMessage = [ + '🏗️ Building Docker image for PR #' + process.env.PR_NUMBER + ', commit ' + process.env.COMMIT_HASH, + '', + ' [View Build Logs](' + process.env.WORKFLOW_URL + ')', + '', + getConsoleLink(process.env.PR_NUMBER) + ].join('\n'); + + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: process.env.COMMENT_ID, + body: buildingMessage + }); + + - name: Update Status - Deploying + uses: actions/github-script@v7 + with: + script: | + eval(process.env.GET_CONSOLE_LINK); + + const deployingMessage = [ + '🚀 Deploying to Control Plane...', + '', + '⏳ Waiting for deployment to be ready...', + '', + ' [View Deploy Logs](' + process.env.WORKFLOW_URL + ')', + '', + getConsoleLink(process.env.PR_NUMBER) + ].join('\n'); + + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: process.env.COMMENT_ID, + body: deployingMessage + }); + + - name: Deploy to Control Plane + id: deploy + shell: bash + run: ${{ github.action_path }}/scripts/deploy.sh + env: + APP_NAME: ${{ inputs.app_name }} + CPLN_ORG: ${{ inputs.org }} + WAIT_TIMEOUT: ${{ inputs.wait_timeout }} + + - name: Update Status - Deployment Complete + if: always() + uses: actions/github-script@v7 + with: + script: | + eval(process.env.GET_CONSOLE_LINK); + + const prNumber = process.env.PR_NUMBER; + const appUrl = process.env.REVIEW_APP_URL; + const workflowUrl = process.env.WORKFLOW_URL; + const isSuccess = '${{ job.status }}' === 'success'; + + // Create GitHub deployment status + const deploymentStatus = { + owner: context.repo.owner, + repo: context.repo.repo, + deployment_id: process.env.DEPLOYMENT_ID, + state: isSuccess ? 'success' : 'failure', + environment_url: isSuccess ? appUrl : undefined, + log_url: workflowUrl, + environment: 'review' + }; + + await github.rest.repos.createDeploymentStatus(deploymentStatus); + + // Define messages based on deployment status + const successMessage = [ + '✅ Deployment complete for PR #' + prNumber + ', commit ' + process.env.COMMIT_HASH, + '', + '🌐 [Review App for PR #' + prNumber + '](' + appUrl + ')', + '', + ' [View Completed Action Build and Deploy Logs](' + workflowUrl + ')', + '', + getConsoleLink(prNumber) + ].join('\n'); + + const failureMessage = [ + '❌ Deployment failed for PR #' + prNumber + ', commit ' + process.env.COMMIT_HASH, + '', + ' [View Deployment Logs with Errors](' + workflowUrl + ')', + '', + getConsoleLink(prNumber) + ].join('\n'); + + // Update the existing comment + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: process.env.COMMENT_ID, + body: isSuccess ? successMessage : failureMessage + }); diff --git a/.github/actions/setup-environment/action.yml b/.github/actions/setup-environment/action.yml index 1a086ee37..829a9498c 100644 --- a/.github/actions/setup-environment/action.yml +++ b/.github/actions/setup-environment/action.yml @@ -3,14 +3,6 @@ name: 'Setup Environment' description: 'Sets up Ruby, installs Control Plane CLI, cpflow gem, and sets up the default profile' -inputs: - token: - description: 'Control Plane token' - required: true - org: - description: 'Control Plane organization' - required: true - runs: using: 'composite' steps: @@ -30,22 +22,19 @@ runs: - name: Setup Control Plane Profile shell: bash run: | - TOKEN="${{ inputs.token }}" - ORG="${{ inputs.org }}" - - if [ -z "$TOKEN" ]; then - echo " Error: Control Plane token not provided" + if [ -z "$CPLN_TOKEN" ]; then + echo " Error: CPLN_TOKEN environment variable is not set" exit 1 fi - if [ -z "$ORG" ]; then - echo " Error: Control Plane organization not provided" + if [ -z "$CPLN_ORG" ]; then + echo " Error: CPLN_ORG environment variable is not set" exit 1 fi echo "Setting up Control Plane profile..." - echo "Organization: $ORG" - cpln profile update default --org "$ORG" --token "$TOKEN" + echo "Organization: $CPLN_ORG" + cpln profile update default --org "$CPLN_ORG" --token "$CPLN_TOKEN" echo "Setting up Docker login for Control Plane registry..." - cpln image docker-login --org "$ORG" + cpln image docker-login --org "$CPLN_ORG" diff --git a/.github/workflows/delete-review-app.yml b/.github/workflows/delete-review-app.yml index 3f547579d..fa24902cf 100644 --- a/.github/workflows/delete-review-app.yml +++ b/.github/workflows/delete-review-app.yml @@ -13,8 +13,8 @@ permissions: issues: write env: - CPLN_ORG: ${{ vars.CPLN_ORG_STAGING }} - CPLN_TOKEN: ${{ secrets.CPLN_TOKEN_STAGING }} + CPLN_ORG: ${{ secrets.CPLN_ORG }} + CPLN_TOKEN: ${{ secrets.CPLN_TOKEN }} APP_NAME: qa-react-webpack-rails-tutorial-pr-${{ github.event.pull_request.number || github.event.issue.number }} PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }} @@ -29,18 +29,6 @@ jobs: runs-on: ubuntu-latest steps: - - name: Get PR number - id: pr - uses: actions/github-script@v7 - with: - script: | - const prNumber = context.payload.issue.number; - core.setOutput('pr_number', prNumber); - core.exportVariable('PR_NUMBER', prNumber); - - - name: Set App Name - run: echo "APP_NAME=qa-react-webpack-rails-tutorial-pr-${{ env.PR_NUMBER }}" >> $GITHUB_ENV - - uses: actions/checkout@v4 - name: Validate Required Secrets @@ -58,10 +46,7 @@ jobs: fi - name: Setup Environment - uses: ./.github/actions/setup-environment@justin808-working-for-deploys - with: - org: ${{ env.CPLN_ORG }} - token: ${{ env.CPLN_TOKEN }} + uses: ./.github/actions/setup-environment - name: Set shared functions id: shared-functions @@ -120,11 +105,10 @@ jobs: issue_number: process.env.PR_NUMBER, owner: context.repo.owner, repo: context.repo.repo, - body: '🗑️ Starting app deletion...' body: [ message, '', - ' 🗑️ [View Delete Logs](' + process.env.WORKFLOW_URL + ')', + ' [View Delete Logs](' + process.env.WORKFLOW_URL + ')', '', getConsoleLink(process.env.PR_NUMBER) ].join('\n') @@ -132,14 +116,12 @@ jobs: return { commentId: comment.data.id }; - name: Delete Review App - uses: ./.github/actions/delete-control-plane-app@justin808-working-for-deploys + 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 }} - name: Update Delete Status @@ -151,7 +133,6 @@ jobs: const success = '${{ job.status }}' === 'success'; const prNumber = process.env.PR_NUMBER; - const cpConsoleUrl = `https://console.cpln.io/org/${process.env.CPLN_ORG}/workloads/${process.env.APP_NAME}`; const successMessage = [ '✅ Review app for PR #' + prNumber + ' was successfully deleted', diff --git a/.github/workflows/deploy-to-control-plane-staging.yml b/.github/workflows/deploy-to-control-plane-staging.yml index b56511b11..095c635a7 100644 --- a/.github/workflows/deploy-to-control-plane-staging.yml +++ b/.github/workflows/deploy-to-control-plane-staging.yml @@ -13,8 +13,8 @@ on: # Convert the GitHub secret variables to environment variables for use by the Control Plane CLI env: - CPLN_ORG: ${{ vars.CPLN_ORG_STAGING }} - CPLN_TOKEN: ${{ secrets.CPLN_TOKEN_STAGING }} + CPLN_ORG: ${{secrets.CPLN_ORG_STAGING}} + CPLN_TOKEN: ${{secrets.CPLN_TOKEN_STAGING}} jobs: deploy-to-control-plane-staging: @@ -26,7 +26,7 @@ jobs: fetch-depth: 0 # Fetch all history for proper SHA handling ref: master # Explicitly checkout master branch - - uses: ./.github/actions/deploy-to-control-plane@justin808-working-for-deploys + - uses: ./.github/actions/deploy-to-control-plane with: - app_name: ${{ vars.STAGING_APP_NAME }} - org: ${{ vars.CPLN_ORG_STAGING }} + app_name: ${{ secrets.APP_NAME_STAGING }} + org: ${{ secrets.CPLN_ORG_STAGING }} diff --git a/.github/workflows/deploy-to-control-plane.yml b/.github/workflows/deploy-to-control-plane.yml index 35e26082f..9cdc45995 100644 --- a/.github/workflows/deploy-to-control-plane.yml +++ b/.github/workflows/deploy-to-control-plane.yml @@ -3,29 +3,31 @@ name: Deploy Review App to Control Plane run-name: ${{ github.event_name == 'issue_comment' && 'Deploying Review App' || format('Updating Review App for {0}', github.ref_name) }} on: - pull_request: - types: [opened, synchronize, reopened] issue_comment: types: [created] + push: + branches-ignore: + - main # Don't run on main branch pushes + - master # Don't run on master branch pushes # Use concurrency to cancel in-progress runs concurrency: - group: deploy-pr-${{ github.event.pull_request.number || github.event.issue.number }} + group: deploy-pr-${{ github.event.issue.number }} cancel-in-progress: true env: - APP_NAME: qa-react-webpack-rails-tutorial-pr-${{ github.event.pull_request.number || github.event.issue.number }} - CPLN_TOKEN: ${{ secrets.CPLN_TOKEN_STAGING }} - CPLN_ORG: ${{ vars.CPLN_ORG_STAGING }} - PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }} + CPLN_ORG: ${{ secrets.CPLN_ORG }} + CPLN_TOKEN: ${{ secrets.CPLN_TOKEN }} jobs: Process-Deployment-Command: + # For issue comments, only run on /deploy-review-app command + # For push events, only run if PR exists and has a review app if: | - (github.event_name == 'pull_request') || (github.event_name == 'issue_comment' && github.event.issue.pull_request && - github.event.comment.body == '/deploy-review-app') + github.event.comment.body == '/deploy-review-app') || + github.event_name == 'push' runs-on: ubuntu-latest permissions: contents: read @@ -37,29 +39,36 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || steps.getRef.outputs.PR_REF || github.ref }} - name: Setup Environment - uses: ./.github/actions/setup-environment@justin808-working-for-deploys - with: - token: ${{ env.CPLN_TOKEN }} - org: ${{ env.CPLN_ORG }} + uses: ./.github/actions/setup-environment - - name: Get PR HEAD Ref + - name: Get PR Number for Push Event + if: github.event_name == 'push' + id: get-pr + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Get PR number from branch + PR_NUMBER=$(gh pr list --head ${{ github.ref_name }} --json number --jq '.[0].number') + if [ -n "$PR_NUMBER" ]; then + echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV + echo "APP_NAME=qa-react-webpack-rails-tutorial-pr-$PR_NUMBER" >> $GITHUB_ENV + echo "has_pr=true" >> $GITHUB_OUTPUT + else + echo "No PR found for this branch" + exit 0 + fi + + - name: Set PR Number for Comment Event if: github.event_name == 'issue_comment' run: | echo "PR_NUMBER=${{ github.event.issue.number }}" >> $GITHUB_ENV echo "APP_NAME=qa-react-webpack-rails-tutorial-pr-${{ github.event.issue.number }}" >> $GITHUB_ENV - # For PR comments, get the actual PR head commit - PR_DATA=$(gh pr view $PR_NUMBER --repo ${{ github.repository }} --json headRefName,headRefOid) - echo "PR_REF=$(echo "$PR_DATA" | jq -r '.headRefName')" >> $GITHUB_OUTPUT - echo "PR_SHA=$(echo "$PR_DATA" | jq -r '.headRefOid')" >> $GITHUB_OUTPUT - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Check if Review App Exists id: check-app - if: github.event_name == 'push' + if: github.event_name == 'push' && steps.get-pr.outputs.has_pr == 'true' env: CPLN_TOKEN: ${{ secrets.CPLN_TOKEN }} run: | @@ -69,44 +78,26 @@ jobs: fi echo "app_exists=true" >> $GITHUB_OUTPUT - - - name: Set Workflow URL - id: workflow-url - uses: actions/github-script@v7 - with: - script: | - async function getWorkflowUrl(runId) { - const jobs = await github.rest.actions.listJobsForWorkflowRun({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: runId - }); - - const currentJob = jobs.data.jobs.find(job => job.status === 'in_progress'); - const jobId = currentJob?.id; - - if (!jobId) { - return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`; - } - - return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}/job/${jobId}`; - } - - const workflowUrl = await getWorkflowUrl(context.runId); - core.exportVariable('WORKFLOW_URL', workflowUrl); - core.exportVariable('GET_CONSOLE_LINK', ` - function getConsoleLink(prNumber) { - return '🎮 [Control Plane Console](' + - 'https://console.cpln.io/console/org/' + process.env.CPLN_ORG + '/gvc/' + process.env.APP_NAME + '/-info)'; - } - `); + - name: Get PR HEAD Ref + id: getRef + if: | + (github.event_name == 'issue_comment' && + github.event.issue.pull_request && + github.event.comment.body == '/deploy-review-app') || + (steps.get-pr.outputs.has_pr == 'true' && steps.check-app.outputs.app_exists == 'true') + run: | + PR_DATA=$(gh pr view ${{ env.PR_NUMBER }} --repo ${{ github.repository }} --json headRefName,headRefOid) + echo "PR_REF=$(echo "$PR_DATA" | jq -r '.headRefName')" >> $GITHUB_OUTPUT + echo "PR_SHA=$(echo "$PR_DATA" | jq -r '.headRefOid')" >> $GITHUB_OUTPUT + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Create Initial Comment if: | (github.event_name == 'issue_comment' && github.event.issue.pull_request && github.event.comment.body == '/deploy-review-app') || - ( steps.check-app.outputs.app_exists == 'true') + (steps.get-pr.outputs.has_pr == 'true' && steps.check-app.outputs.app_exists == 'true') id: create-comment uses: actions/github-script@v7 with: @@ -125,11 +116,16 @@ jobs: (github.event_name == 'issue_comment' && github.event.issue.pull_request && github.event.comment.body == '/deploy-review-app') || - (steps.check-app.outputs.app_exists == 'true') + (steps.get-pr.outputs.has_pr == 'true' && steps.check-app.outputs.app_exists == 'true') run: echo "COMMENT_ID=${{ fromJSON(steps.create-comment.outputs.result).commentId }}" >> $GITHUB_ENV - - name: Initialize Deployment - id: init-deployment + - name: Set Workflow URL + if: | + (github.event_name == 'issue_comment' && + github.event.issue.pull_request && + github.event.comment.body == '/deploy-review-app') || + (steps.get-pr.outputs.has_pr == 'true' && steps.check-app.outputs.app_exists == 'true') + id: workflow-url uses: actions/github-script@v7 with: script: | @@ -144,62 +140,33 @@ jobs: const jobId = currentJob?.id; if (!jobId) { - console.log('Warning: Could not find current job ID'); return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`; } return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}/job/${jobId}`; } - // Create initial deployment comment - const comment = await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: process.env.PR_NUMBER, - body: '⏳ Initializing deployment...' - }); - - // Create GitHub deployment - const deployment = await github.rest.repos.createDeployment({ - owner: context.repo.owner, - repo: context.repo.repo, - ref: context.sha, - environment: 'review', - auto_merge: false, - required_contexts: [] - }); - const workflowUrl = await getWorkflowUrl(context.runId); - - return { - deploymentId: deployment.data.id, - commentId: comment.data.id, - workflowUrl - }; - - - name: Set comment ID and workflow URL - run: | - echo "COMMENT_ID=${{ fromJSON(steps.init-deployment.outputs.result).commentId }}" >> $GITHUB_ENV - echo "WORKFLOW_URL=${{ fromJSON(steps.init-deployment.outputs.result).workflowUrl }}" >> $GITHUB_ENV - - - name: Set commit hash - run: | - FULL_COMMIT="${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || steps.getRef.outputs.PR_SHA || github.sha }}" - echo "COMMIT_HASH=${FULL_COMMIT:0:7}" >> $GITHUB_ENV + core.exportVariable('WORKFLOW_URL', workflowUrl); + core.exportVariable('GET_CONSOLE_LINK', ` + function getConsoleLink(prNumber) { + return '🎮 [Control Plane Console](' + + 'https://console.cpln.io/console/org/' + process.env.CPLN_ORG + '/gvc/' + process.env.APP_NAME + '/-info)'; + } + `); - name: Update Status - Building if: | (github.event_name == 'issue_comment' && github.event.issue.pull_request && github.event.comment.body == '/deploy-review-app') || - (steps.check-app.outputs.app_exists == 'true') + (steps.get-pr.outputs.has_pr == 'true' && steps.check-app.outputs.app_exists == 'true') uses: actions/github-script@v7 with: script: | eval(process.env.GET_CONSOLE_LINK); const buildingMessage = [ - '🏗️ Building Docker image for PR #' + process.env.PR_NUMBER + ', commit ' + '${{ env.COMMIT_HASH }}', '🏗️ Building Docker image...', '', '📝 [View Build Logs](' + process.env.WORKFLOW_URL + ')', @@ -219,7 +186,7 @@ jobs: (github.event_name == 'issue_comment' && github.event.issue.pull_request && github.event.comment.body == '/deploy-review-app') || - (steps.check-app.outputs.app_exists == 'true') + (steps.get-pr.outputs.has_pr == 'true' && steps.check-app.outputs.app_exists == 'true') run: git checkout ${{ steps.getRef.outputs.PR_REF }} - name: Build Docker Image @@ -227,12 +194,12 @@ jobs: (github.event_name == 'issue_comment' && github.event.issue.pull_request && github.event.comment.body == '/deploy-review-app') || - (steps.check-app.outputs.app_exists == 'true') - uses: ./.github/actions/build-docker-image@justin808-working-for-deploys + (steps.get-pr.outputs.has_pr == 'true' && steps.check-app.outputs.app_exists == 'true') + uses: ./.github/actions/build-docker-image with: app_name: ${{ env.APP_NAME }} org: ${{ env.CPLN_ORG }} - commit: ${{ env.COMMIT_HASH }} + commit: ${{ steps.getRef.outputs.PR_SHA }} PR_NUMBER: ${{ env.PR_NUMBER }} - name: Update Status - Deploying @@ -240,7 +207,7 @@ jobs: (github.event_name == 'issue_comment' && github.event.issue.pull_request && github.event.comment.body == '/deploy-review-app') || - (steps.check-app.outputs.app_exists == 'true') + (steps.get-pr.outputs.has_pr == 'true' && steps.check-app.outputs.app_exists == 'true') uses: actions/github-script@v7 with: script: | @@ -268,8 +235,8 @@ jobs: (github.event_name == 'issue_comment' && github.event.issue.pull_request && github.event.comment.body == '/deploy-review-app') || - (steps.check-app.outputs.app_exists == 'true') - uses: ./.github/actions/deploy-to-control-plane@justin808-working-for-deploys + (steps.get-pr.outputs.has_pr == 'true' && steps.check-app.outputs.app_exists == 'true') + uses: ./.github/actions/deploy-to-control-plane with: app_name: ${{ env.APP_NAME }} org: ${{ env.CPLN_ORG }} @@ -279,53 +246,26 @@ jobs: CPLN_TOKEN: ${{ env.CPLN_TOKEN }} PR_NUMBER: ${{ env.PR_NUMBER }} - - name: Update Status - Deployment Complete + - name: Update Status - Success + if: success() uses: actions/github-script@v7 with: script: | - const prNumber = process.env.PR_NUMBER; - const appUrl = process.env.REVIEW_APP_URL; - const workflowUrl = process.env.WORKFLOW_URL; - const isSuccess = '${{ job.status }}' === 'success'; - - const consoleLink = '🎮 [Control Plane Console](https://console.cpln.io/console/org/' + - process.env.CPLN_ORG + '/gvc/' + process.env.APP_NAME + '/-info)'; - - // Create GitHub deployment status - const deploymentStatus = { - owner: context.repo.owner, - repo: context.repo.repo, - deployment_id: ${{ fromJSON(steps.init-deployment.outputs.result).deploymentId }}, - state: isSuccess ? 'success' : 'failure', - environment_url: isSuccess ? appUrl : undefined, - log_url: workflowUrl, - environment: 'review' - }; - - await github.rest.repos.createDeploymentStatus(deploymentStatus); - - // Define messages based on deployment status + eval(process.env.GET_CONSOLE_LINK); + const successMessage = [ - '✅ Deployment complete for PR #' + prNumber + ', commit ' + '${{ env.COMMIT_HASH }}', + '✅ Deployment successful!', '', - '🚀 [Review App for PR #' + prNumber + '](' + appUrl + ')', - consoleLink, + '🌐 Review app is ready at: ${{ env.REVIEW_APP_URL }}', '', - '📋 [View Completed Action Build and Deploy Logs](' + workflowUrl + ')' - ].join('\n'); - - const failureMessage = [ - '❌ Deployment failed for PR #' + prNumber + ', commit ' + '${{ env.COMMIT_HASH }}', + '📝 [View App Logs](' + process.env.WORKFLOW_URL + ')', '', - consoleLink, - '', - '📋 [View Deployment Logs with Errors](' + workflowUrl + ')' + getConsoleLink(process.env.PR_NUMBER) ].join('\n'); - // Update the existing comment await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: process.env.COMMENT_ID, - body: isSuccess ? successMessage : failureMessage + body: successMessage }); diff --git a/.github/workflows/help-command.yml b/.github/workflows/help-command.yml index 90f19a2c5..8894d034f 100644 --- a/.github/workflows/help-command.yml +++ b/.github/workflows/help-command.yml @@ -1,12 +1,13 @@ -name: Show Help for Commands +name: Show Detailed Help on: - push: - branches: - - "**" # Trigger on all branches, including feature branches issue_comment: types: [created] - workflow_dispatch: + +# Use concurrency to prevent duplicate runs +concurrency: + group: help-${{ github.event.issue.number }} + cancel-in-progress: true permissions: issues: write @@ -15,17 +16,12 @@ permissions: jobs: show-help: if: | - github.event_name == 'workflow_dispatch' || - (github.event_name == 'issue_comment' && - github.event.issue.pull_request && - github.event.comment.body == '/help') + github.event_name == 'issue_comment' && + github.event.issue.pull_request && + github.event.comment.body == '/help' runs-on: ubuntu-latest steps: - - name: Identify Workflow Branch - run: | - echo "Workflow branch ref: ${{ github.ref }}" - - name: Show Available Commands uses: actions/github-script@v7 with: @@ -46,18 +42,9 @@ jobs: '- Provides a unique URL to preview your changes', '- Shows build and deployment progress in real-time', '', - '**Required Environment Secrets:**', - '- `CPLN_TOKEN_STAGING`: Control Plane authentication token', - '- `CPLN_TOKEN_PRODUCTION`: Control Plane authentication token', - '', - '**Required GitHub Actions Variables:**', - '- `CPLN_ORG_STAGING`: Control Plane authentication token', - '- `CPLN_ORG_PRODUCTION`: Control Plane authentication token', - '', - '**Required GitHub Actions Variables (these need to match your control_plane.yml file:**', - '- `PRODUCTION_APP_NAME`: Control Plane production app name', - '- `STAGING_APP_NAME`: Control Plane staging app name', - '- `REVIEW_APP_PREFIX`: Control Plane review app prefix', + '**Required Environment Variables:**', + '- `CPLN_TOKEN`: Control Plane authentication token', + '- `CPLN_ORG`: Control Plane organization name', '', '**Optional Configuration:**', '- `WAIT_TIMEOUT`: Deployment timeout in seconds (default: 900)', @@ -102,10 +89,14 @@ jobs: 'For more information, see the [React on Rails Tutorial documentation](https://github.com/shakacode/react-on-rails/tree/master/react-webpack-rails-tutorial)' ].join('\n'); + console.log('Issue number:', github.context.payload.issue.number); + console.log('Owner:', context.repo.owner); + console.log('Repo:', context.repo.repo); + await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, - issue_number: context.issue.number, + issue_number: github.context.payload.issue.number, body: helpMessage }); diff --git a/.github/workflows/nightly-remove-stale-review-apps.yml b/.github/workflows/nightly-remove-stale-review-apps.yml index 0e8630f35..c5f0376ae 100644 --- a/.github/workflows/nightly-remove-stale-review-apps.yml +++ b/.github/workflows/nightly-remove-stale-review-apps.yml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Environment - uses: ./.github/actions/setup-environment@justin808-working-for-deploys + uses: ./.github/actions/setup-environment - name: Get Stale PRs id: stale_prs diff --git a/.github/workflows/promote-staging-to-production.yml b/.github/workflows/promote-staging-to-production.yml index 06cea9cdd..041480671 100644 --- a/.github/workflows/promote-staging-to-production.yml +++ b/.github/workflows/promote-staging-to-production.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Environment - uses: ./.github/actions/setup-environment@justin808-working-for-deploys + uses: ./.github/actions/setup-environment env: CPLN_TOKEN: ${{ secrets.CPLN_TOKEN }} From 7b1b0108a9a4a49747db7b46739840cd2fdad85d Mon Sep 17 00:00:00 2001 From: Judah Meek Date: Tue, 18 Mar 2025 18:08:44 -0500 Subject: [PATCH 6/7] Revert "Enhance GitHub Actions workflows with new features and improvements (#618)" This reverts commit 151c5272379039dae0a13c1bfc8cce467465dc4e. --- .../deploy-to-control-plane/action.yml | 267 ++++------------ .../deploy-to-control-plane/scripts/deploy.sh | 51 ++- .../workflows/add-comment-on-pr-creation.yml | 36 +-- .github/workflows/delete-review-app.yml | 52 +-- .github/workflows/deploy-to-control-plane.yml | 302 ++++++++++-------- .github/workflows/help-command.yml | 36 +-- .github/workflows/review-app-help.yml | 52 --- 7 files changed, 295 insertions(+), 501 deletions(-) delete mode 100644 .github/workflows/review-app-help.yml diff --git a/.github/actions/deploy-to-control-plane/action.yml b/.github/actions/deploy-to-control-plane/action.yml index b8f9b7332..1e5c9fa78 100644 --- a/.github/actions/deploy-to-control-plane/action.yml +++ b/.github/actions/deploy-to-control-plane/action.yml @@ -26,223 +26,66 @@ outputs: runs: using: "composite" steps: - - name: Validate Required Secrets - shell: bash - run: | - missing_secrets=() - for secret in "CPLN_TOKEN" "CPLN_ORG"; do - if [ -z "${!secret}" ]; then - missing_secrets+=("$secret") - fi - done - - if [ ${#missing_secrets[@]} -ne 0 ]; then - echo "Required secrets are not set: ${missing_secrets[*]}" - exit 1 - fi - - name: Setup Environment uses: ./.github/actions/setup-environment - - name: Set shared functions - id: shared-functions - uses: actions/github-script@v7 - with: - script: | - core.exportVariable('GET_CONSOLE_LINK', ` - function getConsoleLink(prNumber) { - return ' [Control Plane Console for Review App with PR #' + prNumber + '](' + - 'https://console.cpln.io/org/' + process.env.CPLN_ORG + '/workloads/' + process.env.APP_NAME + ')'; - } - `); - - - name: Initialize Deployment - id: init-deployment - uses: actions/github-script@v7 - with: - script: | - eval(process.env.GET_CONSOLE_LINK); - - async function getWorkflowUrl(runId) { - // Get the current job ID - const jobs = await github.rest.actions.listJobsForWorkflowRun({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: runId - }); - - const currentJob = jobs.data.jobs.find(job => job.status === 'in_progress'); - const jobId = currentJob?.id; - - if (!jobId) { - console.log('Warning: Could not find current job ID'); - return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`; - } - - return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}/job/${jobId}`; - } - - // Create initial deployment comment - const comment = await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: process.env.PR_NUMBER, - body: ' Initializing deployment...' - }); - - // Create GitHub deployment - const deployment = await github.rest.repos.createDeployment({ - owner: context.repo.owner, - repo: context.repo.repo, - ref: context.sha, - environment: 'review', - auto_merge: false, - required_contexts: [] - }); - - const workflowUrl = await getWorkflowUrl(context.runId); - - core.exportVariable('WORKFLOW_URL', workflowUrl); - core.exportVariable('COMMENT_ID', comment.data.id); - core.exportVariable('DEPLOYMENT_ID', deployment.data.id); - - - name: Set commit hash + - name: Get Commit SHA + id: get_sha shell: bash - run: | - FULL_COMMIT=$(git rev-parse HEAD) - echo "COMMIT_HASH=${FULL_COMMIT:0:7}" >> $GITHUB_ENV - - - name: Update Status - Setting Up - uses: actions/github-script@v7 - with: - script: | - eval(process.env.GET_CONSOLE_LINK); - - const setupMessage = [ - '🔧 Setting up Control Plane app...', - '', - ' [View Setup Logs](' + process.env.WORKFLOW_URL + ')', - '', - getConsoleLink(process.env.PR_NUMBER) - ].join('\n'); - - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: process.env.COMMENT_ID, - body: setupMessage - }); - - - name: Setup Control Plane App - shell: bash - run: | - echo "🔧 Checking if app exists..." - if ! cpflow exists -a ${{ inputs.app_name }} ; then - echo "📦 Setting up new Control Plane app..." - cpflow setup-app -a ${{ inputs.app_name }} - fi - - - name: Update Status - Building - uses: actions/github-script@v7 - with: - script: | - eval(process.env.GET_CONSOLE_LINK); - - const buildingMessage = [ - '🏗️ Building Docker image for PR #' + process.env.PR_NUMBER + ', commit ' + process.env.COMMIT_HASH, - '', - ' [View Build Logs](' + process.env.WORKFLOW_URL + ')', - '', - getConsoleLink(process.env.PR_NUMBER) - ].join('\n'); - - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: process.env.COMMENT_ID, - body: buildingMessage - }); - - - name: Update Status - Deploying - uses: actions/github-script@v7 - with: - script: | - eval(process.env.GET_CONSOLE_LINK); - - const deployingMessage = [ - '🚀 Deploying to Control Plane...', - '', - '⏳ Waiting for deployment to be ready...', - '', - ' [View Deploy Logs](' + process.env.WORKFLOW_URL + ')', - '', - getConsoleLink(process.env.PR_NUMBER) - ].join('\n'); - - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: process.env.COMMENT_ID, - body: deployingMessage - }); + run: ${{ github.action_path }}/scripts/get-commit-sha.sh + env: + GITHUB_TOKEN: ${{ inputs.github_token }} + PR_NUMBER: ${{ env.PR_NUMBER }} - name: Deploy to Control Plane id: deploy shell: bash - run: ${{ github.action_path }}/scripts/deploy.sh - env: - APP_NAME: ${{ inputs.app_name }} - CPLN_ORG: ${{ inputs.org }} - WAIT_TIMEOUT: ${{ inputs.wait_timeout }} - - - name: Update Status - Deployment Complete - if: always() - uses: actions/github-script@v7 - with: - script: | - eval(process.env.GET_CONSOLE_LINK); - - const prNumber = process.env.PR_NUMBER; - const appUrl = process.env.REVIEW_APP_URL; - const workflowUrl = process.env.WORKFLOW_URL; - const isSuccess = '${{ job.status }}' === 'success'; - - // Create GitHub deployment status - const deploymentStatus = { - owner: context.repo.owner, - repo: context.repo.repo, - deployment_id: process.env.DEPLOYMENT_ID, - state: isSuccess ? 'success' : 'failure', - environment_url: isSuccess ? appUrl : undefined, - log_url: workflowUrl, - environment: 'review' - }; - - await github.rest.repos.createDeploymentStatus(deploymentStatus); - - // Define messages based on deployment status - const successMessage = [ - '✅ Deployment complete for PR #' + prNumber + ', commit ' + process.env.COMMIT_HASH, - '', - '🌐 [Review App for PR #' + prNumber + '](' + appUrl + ')', - '', - ' [View Completed Action Build and Deploy Logs](' + workflowUrl + ')', - '', - getConsoleLink(prNumber) - ].join('\n'); - - const failureMessage = [ - '❌ Deployment failed for PR #' + prNumber + ', commit ' + process.env.COMMIT_HASH, - '', - ' [View Deployment Logs with Errors](' + workflowUrl + ')', - '', - getConsoleLink(prNumber) - ].join('\n'); - - // Update the existing comment - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: process.env.COMMENT_ID, - body: isSuccess ? successMessage : failureMessage - }); + run: | + echo "🚀 Deploying app for PR #${PR_NUMBER}..." + + # Create temp file for output + TEMP_OUTPUT=$(mktemp) + trap 'rm -f "${TEMP_OUTPUT}"' EXIT + + # Deploy the application and show output in real-time while capturing it + if ! cpflow deploy-image -a "${{ inputs.app_name }}" --run-release-phase --org "${{ inputs.org }}" 2>&1 | tee "${TEMP_OUTPUT}"; then + echo "❌ Deployment failed for PR #${PR_NUMBER}" + echo "Error output:" + cat "${TEMP_OUTPUT}" + exit 1 + fi + + # Extract app URL from captured output + REVIEW_APP_URL=$(grep -oP 'https://rails-[^[:space:]]*\.cpln\.app(?=\s|$)' "${TEMP_OUTPUT}" | head -n1) + if [ -z "${REVIEW_APP_URL}" ]; then + echo "❌ Failed to get app URL from deployment output" + echo "Deployment output:" + cat "${TEMP_OUTPUT}" + exit 1 + fi + + # Wait for all workloads to be ready + WAIT_TIMEOUT=${WAIT_TIMEOUT:-${{ inputs.wait_timeout }}} + if ! [[ "${WAIT_TIMEOUT}" =~ ^[0-9]+$ ]]; then + echo "❌ Invalid timeout value: ${WAIT_TIMEOUT}" + exit 1 + fi + echo "⏳ Waiting for all workloads to be ready (timeout: ${WAIT_TIMEOUT}s)" + + # Use timeout command with ps:wait and show output in real-time + if ! timeout "${WAIT_TIMEOUT}" bash -c "cpflow ps:wait -a \"${{ inputs.app_name }}\"" 2>&1 | tee -a "${TEMP_OUTPUT}"; then + TIMEOUT_EXIT=$? + if [ ${TIMEOUT_EXIT} -eq 124 ]; then + echo "❌ Timed out waiting for workloads after ${WAIT_TIMEOUT} seconds" + else + echo "❌ Workloads did not become ready for PR #${PR_NUMBER} (exit code: ${TIMEOUT_EXIT})" + fi + echo "Full output:" + cat "${TEMP_OUTPUT}" + exit 1 + fi + + echo "✅ Deployment successful for PR #${PR_NUMBER}" + echo "🌐 App URL: ${REVIEW_APP_URL}" + echo "review_app_url=${REVIEW_APP_URL}" >> $GITHUB_OUTPUT + echo "REVIEW_APP_URL=${REVIEW_APP_URL}" >> $GITHUB_ENV diff --git a/.github/actions/deploy-to-control-plane/scripts/deploy.sh b/.github/actions/deploy-to-control-plane/scripts/deploy.sh index 73bb8968c..9d070b64a 100755 --- a/.github/actions/deploy-to-control-plane/scripts/deploy.sh +++ b/.github/actions/deploy-to-control-plane/scripts/deploy.sh @@ -1,7 +1,7 @@ #!/bin/bash # This script handles the deployment to Control Plane and extracts the Rails URL -# +# # Required environment variables: # - APP_NAME: Name of the application to deploy # - CPLN_ORG: Control Plane organization @@ -31,36 +31,21 @@ trap 'rm -f "$TEMP_OUTPUT"' EXIT # Deploy the application echo "🚀 Deploying to Control Plane (timeout: ${WAIT_TIMEOUT}s)" -if ! timeout "${WAIT_TIMEOUT}" cpflow deploy-image -a "$APP_NAME" --run-release-phase --org "$CPLN_ORG" --verbose 2>&1 | tee "$TEMP_OUTPUT"; then - echo "❌ Deployment failed" - echo "Full output:" - cat "$TEMP_OUTPUT" - exit 1 -fi - -# Extract app URL from deployment output -RAILS_URL=$(grep -oP 'https://rails-[^[:space:]]*\.cpln\.app(?=\s|$)' "$TEMP_OUTPUT" | head -n1) -if [ -z "$RAILS_URL" ]; then - echo "❌ Failed to get app URL from deployment output" - echo "Full output:" - cat "$TEMP_OUTPUT" - exit 1 +if timeout "$WAIT_TIMEOUT" cpflow deploy-image -a "$APP_NAME" --run-release-phase --org "$CPLN_ORG" --verbose | tee "$TEMP_OUTPUT"; then + # Extract Rails URL from deployment output + RAILS_URL=$(grep -oP 'https://rails-[^[:space:]]*\.cpln\.app(?=\s|$)' "$TEMP_OUTPUT" | head -n1) + if [ -n "$RAILS_URL" ]; then + echo "rails_url=$RAILS_URL" >> "$GITHUB_OUTPUT" + echo "✅ Deployment successful" + echo "🚀 Rails URL: $RAILS_URL" + else + echo "❌ Failed to extract Rails URL from deployment output" + exit 1 + fi +elif [ $? -eq 124 ]; then + echo "❌ Deployment timed out after $WAIT_TIMEOUT seconds" + exit 1 +else + echo "❌ Deployment to Control Plane failed" + exit 1 fi - -# Wait for all workloads to be ready -echo "⏳ Waiting for all workloads to be ready (timeout: ${WAIT_TIMEOUT}s)" -if ! timeout "${WAIT_TIMEOUT}" bash -c "cpflow ps:wait -a \"$APP_NAME\"" 2>&1 | tee -a "$TEMP_OUTPUT"; then - TIMEOUT_EXIT=$? - if [ ${TIMEOUT_EXIT} -eq 124 ]; then - echo "❌ Timed out waiting for workloads after ${WAIT_TIMEOUT} seconds" - else - echo "❌ Workloads did not become ready" - fi - echo "Full output:" - cat "$TEMP_OUTPUT" - exit 1 -fi - -echo "✅ Deployment successful" -echo "🌐 Rails URL: $RAILS_URL" -echo "rails_url=$RAILS_URL" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/add-comment-on-pr-creation.yml b/.github/workflows/add-comment-on-pr-creation.yml index fa686305b..eef586a69 100644 --- a/.github/workflows/add-comment-on-pr-creation.yml +++ b/.github/workflows/add-comment-on-pr-creation.yml @@ -10,21 +10,21 @@ jobs: permissions: pull-requests: write steps: - - uses: actions/github-script@v7 - name: Add GitHub Comment for review app instructions - with: - script: | - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.payload.pull_request.number, - body: [ - "Hi 👋 Here are the commands available for this PR:", - "", - "- `/deploy-review-app`: Deploy your changes to a review environment", - "- `/delete-review-app`: Clean up the review environment when you're done", - "- `/help`: Show detailed information about all commands", - "", - "Use `/help` to see full documentation, including configuration options." - ].join("\n") - }); + name: Add GitHub Comment for review app instructions + uses: actions/github-script@v7 + with: + script: | + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + body: [ + "Hi 👋 Here are the commands available for this PR:", + "", + "- `/deploy-review-app`: Deploy your changes to a review environment", + "- `/delete-review-app`: Clean up the review environment when you're done", + "- `/help`: Show detailed information about all commands", + "", + "Use `/help` to see full documentation, including configuration options." + ].join("\n") + }); diff --git a/.github/workflows/delete-review-app.yml b/.github/workflows/delete-review-app.yml index fa24902cf..caf90b18e 100644 --- a/.github/workflows/delete-review-app.yml +++ b/.github/workflows/delete-review-app.yml @@ -55,16 +55,18 @@ jobs: script: | core.exportVariable('GET_CONSOLE_LINK', ` function getConsoleLink(prNumber) { - return '🎮 [Control Plane Console](' + - 'https://console.cpln.io/console/org/' + process.env.CPLN_ORG + '/gvc/' + process.env.APP_NAME + '/-info)'; + return ' [Control Plane Console for Review App with PR #' + prNumber + '](' + + 'https://console.cpln.io/org/' + process.env.CPLN_ORG + '/workloads/' + process.env.APP_NAME + ')'; } `); - - name: Setup Workflow URL - id: setup-workflow-url + - name: Initialize Delete + id: init-delete uses: actions/github-script@v7 with: script: | + eval(process.env.GET_CONSOLE_LINK); + async function getWorkflowUrl(runId) { // Get the current job ID const jobs = await github.rest.actions.listJobsForWorkflowRun({ @@ -85,16 +87,34 @@ jobs: } const workflowUrl = await getWorkflowUrl(context.runId); - core.exportVariable('WORKFLOW_URL', workflowUrl); - return { workflowUrl }; + + const comment = await github.rest.issues.createComment({ + issue_number: process.env.PR_NUMBER, + owner: context.repo.owner, + repo: context.repo.repo, + body: [ + ' Starting app deletion...', + '', + ' [View Delete Logs](' + workflowUrl + ')', + '', + getConsoleLink(process.env.PR_NUMBER) + ].join('\n') + }); + + return { + commentId: comment.data.id, + workflowUrl + }; + + - name: Set workflow URL + run: | + echo "WORKFLOW_URL=${{ fromJSON(steps.init-delete.outputs.result).workflowUrl }}" >> $GITHUB_ENV - name: Create Initial Delete Comment - id: create-delete-comment + id: init-delete uses: actions/github-script@v7 with: script: | - eval(process.env.GET_CONSOLE_LINK); - let message = '🗑️ Starting app deletion'; if ('${{ github.event_name }}' === 'pull_request') { const merged = '${{ github.event.pull_request.merged }}' === 'true'; @@ -105,13 +125,7 @@ jobs: issue_number: process.env.PR_NUMBER, owner: context.repo.owner, repo: context.repo.repo, - body: [ - message, - '', - ' [View Delete Logs](' + process.env.WORKFLOW_URL + ')', - '', - getConsoleLink(process.env.PR_NUMBER) - ].join('\n') + body: message }); return { commentId: comment.data.id }; @@ -137,9 +151,7 @@ jobs: const successMessage = [ '✅ Review app for PR #' + prNumber + ' was successfully deleted', '', - ' [View Completed Delete Logs](' + process.env.WORKFLOW_URL + ')', - '', - ' [Control Plane Organization](https://console.cpln.io/console/org/' + process.env.CPLN_ORG + '/-info)' + ' [View Completed Delete Logs](' + process.env.WORKFLOW_URL + ')' ].join('\n'); const failureMessage = [ @@ -153,6 +165,6 @@ jobs: await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, - comment_id: ${{ fromJSON(steps.create-delete-comment.outputs.result).commentId }}, + comment_id: ${{ fromJSON(steps.init-delete.outputs.result).commentId }}, body: success ? successMessage : failureMessage }); diff --git a/.github/workflows/deploy-to-control-plane.yml b/.github/workflows/deploy-to-control-plane.yml index 9cdc45995..2adad3d72 100644 --- a/.github/workflows/deploy-to-control-plane.yml +++ b/.github/workflows/deploy-to-control-plane.yml @@ -1,33 +1,31 @@ name: Deploy Review App to Control Plane -run-name: ${{ github.event_name == 'issue_comment' && 'Deploying Review App' || format('Updating Review App for {0}', github.ref_name) }} +run-name: ${{ (github.event_name == 'pull_request' || (github.event_name == 'issue_comment' && github.event.issue.pull_request)) && 'Deploying Review App' || format('Deploying {0} to Staging App', github.ref_name) }} on: + pull_request: + types: [opened, synchronize, reopened] issue_comment: types: [created] - push: - branches-ignore: - - main # Don't run on main branch pushes - - master # Don't run on master branch pushes # Use concurrency to cancel in-progress runs concurrency: - group: deploy-pr-${{ github.event.issue.number }} + group: deploy-${{ github.event.pull_request.number || github.event.issue.number }} cancel-in-progress: true env: + APP_NAME: qa-react-webpack-rails-tutorial-pr-${{ github.event.pull_request.number || github.event.issue.number }} CPLN_ORG: ${{ secrets.CPLN_ORG }} CPLN_TOKEN: ${{ secrets.CPLN_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }} jobs: Process-Deployment-Command: - # For issue comments, only run on /deploy-review-app command - # For push events, only run if PR exists and has a review app if: | + (github.event_name == 'pull_request') || (github.event_name == 'issue_comment' && github.event.issue.pull_request && - github.event.comment.body == '/deploy-review-app') || - github.event_name == 'push' + github.event.comment.body == '/deploy-review-app') runs-on: ubuntu-latest permissions: contents: read @@ -36,100 +34,60 @@ jobs: issues: write steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup Environment - uses: ./.github/actions/setup-environment - - - name: Get PR Number for Push Event - if: github.event_name == 'push' - id: get-pr - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - # Get PR number from branch - PR_NUMBER=$(gh pr list --head ${{ github.ref_name }} --json number --jq '.[0].number') - if [ -n "$PR_NUMBER" ]; then - echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV - echo "APP_NAME=qa-react-webpack-rails-tutorial-pr-$PR_NUMBER" >> $GITHUB_ENV - echo "has_pr=true" >> $GITHUB_OUTPUT - else - echo "No PR found for this branch" - exit 0 - fi - - - name: Set PR Number for Comment Event - if: github.event_name == 'issue_comment' - run: | - echo "PR_NUMBER=${{ github.event.issue.number }}" >> $GITHUB_ENV - echo "APP_NAME=qa-react-webpack-rails-tutorial-pr-${{ github.event.issue.number }}" >> $GITHUB_ENV - - - name: Check if Review App Exists - id: check-app - if: github.event_name == 'push' && steps.get-pr.outputs.has_pr == 'true' - env: - CPLN_TOKEN: ${{ secrets.CPLN_TOKEN }} - run: | - if ! cpflow exists -a ${{ env.APP_NAME }}; then - echo "No review app exists for this PR" - exit 0 - fi - echo "app_exists=true" >> $GITHUB_OUTPUT - - name: Get PR HEAD Ref + if: github.event_name == 'issue_comment' id: getRef - if: | - (github.event_name == 'issue_comment' && - github.event.issue.pull_request && - github.event.comment.body == '/deploy-review-app') || - (steps.get-pr.outputs.has_pr == 'true' && steps.check-app.outputs.app_exists == 'true') run: | - PR_DATA=$(gh pr view ${{ env.PR_NUMBER }} --repo ${{ github.repository }} --json headRefName,headRefOid) + # For PR comments, get the actual PR head commit + PR_DATA=$(gh pr view $PR_NUMBER --repo ${{ github.repository }} --json headRefName,headRefOid) echo "PR_REF=$(echo "$PR_DATA" | jq -r '.headRefName')" >> $GITHUB_OUTPUT echo "PR_SHA=$(echo "$PR_DATA" | jq -r '.headRefOid')" >> $GITHUB_OUTPUT env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Create Initial Comment - if: | - (github.event_name == 'issue_comment' && - github.event.issue.pull_request && - github.event.comment.body == '/deploy-review-app') || - (steps.get-pr.outputs.has_pr == 'true' && steps.check-app.outputs.app_exists == 'true') - id: create-comment + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || steps.getRef.outputs.PR_REF || github.ref }} + + - name: Validate Required Secrets + run: | + missing_secrets=() + for secret in "CPLN_TOKEN" "CPLN_ORG"; do + if [ -z "${!secret}" ]; then + missing_secrets+=("$secret") + fi + done + + if [ ${#missing_secrets[@]} -ne 0 ]; then + echo "Required secrets are not set: ${missing_secrets[*]}" + exit 1 + fi + + - name: Setup Environment + uses: ./.github/actions/setup-environment + + - name: Set shared functions + id: shared-functions uses: actions/github-script@v7 with: script: | - const result = await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: process.env.PR_NUMBER, - body: '🚀 Starting deployment process...' - }); - console.log('Created comment:', result.data.id); - return { commentId: result.data.id }; - - - name: Set Comment ID - if: | - (github.event_name == 'issue_comment' && - github.event.issue.pull_request && - github.event.comment.body == '/deploy-review-app') || - (steps.get-pr.outputs.has_pr == 'true' && steps.check-app.outputs.app_exists == 'true') - run: echo "COMMENT_ID=${{ fromJSON(steps.create-comment.outputs.result).commentId }}" >> $GITHUB_ENV + core.exportVariable('GET_CONSOLE_LINK', ` + function getConsoleLink(prNumber) { + return ' [Control Plane Console for Review App with PR #' + prNumber + '](' + + 'https://console.cpln.io/org/' + process.env.CPLN_ORG + '/workloads/' + process.env.APP_NAME + ')'; + } + `); - - name: Set Workflow URL - if: | - (github.event_name == 'issue_comment' && - github.event.issue.pull_request && - github.event.comment.body == '/deploy-review-app') || - (steps.get-pr.outputs.has_pr == 'true' && steps.check-app.outputs.app_exists == 'true') - id: workflow-url + - name: Initialize Deployment + id: init-deployment uses: actions/github-script@v7 with: script: | + eval(process.env.GET_CONSOLE_LINK); + async function getWorkflowUrl(runId) { + // Get the current job ID const jobs = await github.rest.actions.listJobsForWorkflowRun({ owner: context.repo.owner, repo: context.repo.repo, @@ -140,36 +98,59 @@ jobs: const jobId = currentJob?.id; if (!jobId) { + console.log('Warning: Could not find current job ID'); return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`; } return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}/job/${jobId}`; } + // Create initial deployment comment + const comment = await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: process.env.PR_NUMBER, + body: ' Initializing deployment...' + }); + + // Create GitHub deployment + const deployment = await github.rest.repos.createDeployment({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: context.sha, + environment: 'review', + auto_merge: false, + required_contexts: [] + }); + const workflowUrl = await getWorkflowUrl(context.runId); - core.exportVariable('WORKFLOW_URL', workflowUrl); - core.exportVariable('GET_CONSOLE_LINK', ` - function getConsoleLink(prNumber) { - return '🎮 [Control Plane Console](' + - 'https://console.cpln.io/console/org/' + process.env.CPLN_ORG + '/gvc/' + process.env.APP_NAME + '/-info)'; - } - `); + + return { + deploymentId: deployment.data.id, + commentId: comment.data.id, + workflowUrl + }; + + - name: Set comment ID and workflow URL + run: | + echo "COMMENT_ID=${{ fromJSON(steps.init-deployment.outputs.result).commentId }}" >> $GITHUB_ENV + echo "WORKFLOW_URL=${{ fromJSON(steps.init-deployment.outputs.result).workflowUrl }}" >> $GITHUB_ENV + + - name: Set commit hash + run: | + FULL_COMMIT="${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || steps.getRef.outputs.PR_SHA || github.sha }}" + echo "COMMIT_HASH=${FULL_COMMIT:0:7}" >> $GITHUB_ENV - name: Update Status - Building - if: | - (github.event_name == 'issue_comment' && - github.event.issue.pull_request && - github.event.comment.body == '/deploy-review-app') || - (steps.get-pr.outputs.has_pr == 'true' && steps.check-app.outputs.app_exists == 'true') uses: actions/github-script@v7 with: script: | eval(process.env.GET_CONSOLE_LINK); const buildingMessage = [ - '🏗️ Building Docker image...', + ' Building Docker image for PR #' + process.env.PR_NUMBER + ', commit ' + '${{ env.COMMIT_HASH }}', '', - '📝 [View Build Logs](' + process.env.WORKFLOW_URL + ')', + ' [View Build Logs](' + process.env.WORKFLOW_URL + ')', '', getConsoleLink(process.env.PR_NUMBER) ].join('\n'); @@ -181,44 +162,26 @@ jobs: body: buildingMessage }); - - name: Checkout PR Branch - if: | - (github.event_name == 'issue_comment' && - github.event.issue.pull_request && - github.event.comment.body == '/deploy-review-app') || - (steps.get-pr.outputs.has_pr == 'true' && steps.check-app.outputs.app_exists == 'true') - run: git checkout ${{ steps.getRef.outputs.PR_REF }} - - name: Build Docker Image - if: | - (github.event_name == 'issue_comment' && - github.event.issue.pull_request && - github.event.comment.body == '/deploy-review-app') || - (steps.get-pr.outputs.has_pr == 'true' && steps.check-app.outputs.app_exists == 'true') uses: ./.github/actions/build-docker-image with: app_name: ${{ env.APP_NAME }} org: ${{ env.CPLN_ORG }} - commit: ${{ steps.getRef.outputs.PR_SHA }} + commit: ${{ env.COMMIT_HASH }} PR_NUMBER: ${{ env.PR_NUMBER }} - name: Update Status - Deploying - if: | - (github.event_name == 'issue_comment' && - github.event.issue.pull_request && - github.event.comment.body == '/deploy-review-app') || - (steps.get-pr.outputs.has_pr == 'true' && steps.check-app.outputs.app_exists == 'true') uses: actions/github-script@v7 with: script: | eval(process.env.GET_CONSOLE_LINK); const deployingMessage = [ - '🚀 Deploying to Control Plane...', + ' Deploying to Control Plane...', '', - '⏳ Waiting for deployment to be ready...', + ' Waiting for deployment to be ready...', '', - '📝 [View Deploy Logs](' + process.env.WORKFLOW_URL + ')', + ' [View Deploy Logs](' + process.env.WORKFLOW_URL + ')', '', getConsoleLink(process.env.PR_NUMBER) ].join('\n'); @@ -231,41 +194,102 @@ jobs: }); - name: Deploy to Control Plane - if: | - (github.event_name == 'issue_comment' && - github.event.issue.pull_request && - github.event.comment.body == '/deploy-review-app') || - (steps.get-pr.outputs.has_pr == 'true' && steps.check-app.outputs.app_exists == 'true') uses: ./.github/actions/deploy-to-control-plane with: app_name: ${{ env.APP_NAME }} org: ${{ env.CPLN_ORG }} github_token: ${{ secrets.GITHUB_TOKEN }} wait_timeout: ${{ vars.WAIT_TIMEOUT || 900 }} - env: - CPLN_TOKEN: ${{ env.CPLN_TOKEN }} - PR_NUMBER: ${{ env.PR_NUMBER }} - - name: Update Status - Success - if: success() + - name: Update Status - Deployment Complete uses: actions/github-script@v7 with: script: | eval(process.env.GET_CONSOLE_LINK); + const prNumber = process.env.PR_NUMBER; + const appUrl = process.env.REVIEW_APP_URL; + const workflowUrl = process.env.WORKFLOW_URL; + const isSuccess = '${{ job.status }}' === 'success'; + + // Create GitHub deployment status + const deploymentStatus = { + owner: context.repo.owner, + repo: context.repo.repo, + deployment_id: ${{ fromJSON(steps.init-deployment.outputs.result).deploymentId }}, + state: isSuccess ? 'success' : 'failure', + environment_url: isSuccess ? appUrl : undefined, + log_url: workflowUrl, + environment: 'review' + }; + + await github.rest.repos.createDeploymentStatus(deploymentStatus); + + // Define messages based on deployment status const successMessage = [ - '✅ Deployment successful!', + ' Deployment complete for PR #' + prNumber + ', commit ' + '${{ env.COMMIT_HASH }}', '', - '🌐 Review app is ready at: ${{ env.REVIEW_APP_URL }}', + ' [Review App for PR #' + prNumber + '](' + appUrl + ')', '', - '📝 [View App Logs](' + process.env.WORKFLOW_URL + ')', + ' [View Completed Action Build and Deploy Logs](' + workflowUrl + ')', '', - getConsoleLink(process.env.PR_NUMBER) + getConsoleLink(prNumber) + ].join('\n'); + + const failureMessage = [ + ' Deployment failed for PR #' + prNumber + ', commit ' + '${{ env.COMMIT_HASH }}', + '', + ' [View Deployment Logs with Errors](' + workflowUrl + ')', + '', + getConsoleLink(prNumber) ].join('\n'); + // Update the existing comment await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: process.env.COMMENT_ID, - body: successMessage + body: isSuccess ? successMessage : failureMessage }); + + show-help: + if: | + github.event_name == 'issue_comment' && + github.event.issue.pull_request && + github.event.comment.body == '/help' + runs-on: ubuntu-latest + + steps: + - name: Show Available Commands + uses: actions/github-script@v7 + with: + script: | + const helpMessage = [ + '## Available Commands', + '', + '### `/deploy-review-app`', + 'Deploys your PR branch to a review environment on Control Plane.', + '- Creates a new review app if one doesn\'t exist', + '- Updates the existing review app if it already exists', + '- Provides a unique URL to preview your changes', + '- Shows build and deployment progress in real-time', + '', + '### `/delete-review-app`', + 'Deletes the review app associated with this PR.', + '- Removes all resources from Control Plane', + '- Helpful for cleaning up when you\'re done testing', + '- Can be re-deployed later using `/deploy-review-app`', + '', + '### `/help`', + 'Shows this help message explaining available commands.', + '', + '---', + '_Note: These commands only work in pull request comments._' + ].join('\n'); + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.issue.number, + body: helpMessage + }); \ No newline at end of file diff --git a/.github/workflows/help-command.yml b/.github/workflows/help-command.yml index 8894d034f..ff59b65f6 100644 --- a/.github/workflows/help-command.yml +++ b/.github/workflows/help-command.yml @@ -1,4 +1,4 @@ -name: Show Detailed Help +name: Show Help for Commands on: issue_comment: @@ -27,15 +27,11 @@ jobs: with: script: | try { - console.log('Creating detailed help message...'); + console.log('Creating help message...'); const helpMessage = [ - '# 📚 Detailed Review App Commands Guide', + '## 📚 Available Commands', '', - 'This is a detailed guide to using review app commands. For a quick reference, see the message posted when your PR was created.', - '', - '## Available Commands', - '', - '### `/deploy-review-app`', + '### `/deploy`', 'Deploys your PR branch to a review environment on Control Plane.', '- Creates a new review app if one doesn\'t exist', '- Updates the existing review app if it already exists', @@ -56,37 +52,23 @@ jobs: 'Deletes the review app associated with this PR.', '- Removes all resources from Control Plane', '- Helpful for cleaning up when you\'re done testing', - '- Can be re-deployed later using `/deploy-review-app`', + '- Can be re-deployed later using `/deploy`', '', '**Required Environment Variables:**', '- `CPLN_TOKEN`: Control Plane authentication token', '- `CPLN_ORG`: Control Plane organization name', '', '### `/help`', - 'Shows this detailed help message.', + 'Shows this help message explaining available commands and configuration.', '', '---', - '## Environment Setup', + '**Note:** These commands only work in pull request comments.', '', + '**Environment Setup:**', '1. Set required secrets in your repository settings:', ' - `CPLN_TOKEN`', ' - `CPLN_ORG`', - '', - '2. Optional: Configure `WAIT_TIMEOUT` in GitHub Actions variables to customize deployment timeout', - '', - '## Control Plane Integration', - '', - 'Review apps are deployed to Control Plane with the following configuration:', - '- App Name Format: `qa-react-webpack-rails-tutorial-pr-{PR_NUMBER}`', - '- Console URL: `https://console.cpln.io/console/org/{CPLN_ORG}/gvc/{APP_NAME}/-info`', - '', - '## Automatic Cleanup', - '', - 'Review apps are automatically deleted when:', - '- The PR is closed (merged or not merged)', - '- The PR is stale (via nightly cleanup job)', - '', - 'For more information, see the [React on Rails Tutorial documentation](https://github.com/shakacode/react-on-rails/tree/master/react-webpack-rails-tutorial)' + '2. Optional: Configure `WAIT_TIMEOUT` in GitHub Actions variables to customize deployment timeout' ].join('\n'); console.log('Issue number:', github.context.payload.issue.number); diff --git a/.github/workflows/review-app-help.yml b/.github/workflows/review-app-help.yml deleted file mode 100644 index 313101e4b..000000000 --- a/.github/workflows/review-app-help.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Show Quick Help on PR Creation - -on: - pull_request: - types: [opened] - -permissions: - issues: write - pull-requests: write - -jobs: - show-quick-help: - if: github.event_name == 'pull_request' - runs-on: ubuntu-latest - - steps: - - name: Show Quick Reference - uses: actions/github-script@v7 - with: - script: | - try { - console.log('Creating quick reference message...'); - const helpMessage = [ - '# 🚀 Quick Review App Commands', - '', - 'Welcome! Here are the commands you can use in this PR:', - '', - '### `/deploy-review-app`', - 'Deploy your PR branch for testing', - '', - '### `/delete-review-app`', - 'Remove the review app when done', - '', - '### `/help`', - 'Show detailed documentation', - '', - '---', - '**Note:** Type `/help` for detailed instructions, environment setup, and configuration options.' - ].join('\n'); - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - body: helpMessage - }); - - console.log('Quick reference posted successfully'); - } catch (error) { - console.error('Error posting quick reference:', error); - core.setFailed(`Failed to post quick reference: ${error.message}`); - } From 28a6761ffe35000525536839e1ab1d7dffba3610 Mon Sep 17 00:00:00 2001 From: Judah Meek Date: Tue, 18 Mar 2025 18:08:45 -0500 Subject: [PATCH 7/7] Revert "Enhance GitHub Actions workflows for pull request and issue management (#617)" This reverts commit 4bf2c03e309075b3c8fa6a4d5bb63c23ad65288a. --- .../workflows/add-comment-on-pr-creation.yml | 24 +--- .github/workflows/delete-review-app.yml | 129 +++++------------ .github/workflows/deploy-to-control-plane.yml | 115 ++++++++++++++- .github/workflows/help-command.yml | 133 +++++++++--------- 4 files changed, 220 insertions(+), 181 deletions(-) diff --git a/.github/workflows/add-comment-on-pr-creation.yml b/.github/workflows/add-comment-on-pr-creation.yml index eef586a69..bd4b69ded 100644 --- a/.github/workflows/add-comment-on-pr-creation.yml +++ b/.github/workflows/add-comment-on-pr-creation.yml @@ -1,30 +1,20 @@ -name: Add Comment on PR Creation +name: Add helper Comment on PR creation on: pull_request: types: [opened] jobs: - add-comment: + comment-on-pr: runs-on: ubuntu-latest - permissions: - pull-requests: write steps: - name: Add GitHub Comment for review app instructions + - name: Add GitHub Comment for review app instructions uses: actions/github-script@v7 with: script: | - await github.rest.issues.createComment({ + github.rest.issues.createComment({ + issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, - issue_number: context.payload.pull_request.number, - body: [ - "Hi 👋 Here are the commands available for this PR:", - "", - "- `/deploy-review-app`: Deploy your changes to a review environment", - "- `/delete-review-app`: Clean up the review environment when you're done", - "- `/help`: Show detailed information about all commands", - "", - "Use `/help` to see full documentation, including configuration options." - ].join("\n") - }); + body: "Hi 👋 To deploy a review app, please comment `/deploy-review-app`" + }) diff --git a/.github/workflows/delete-review-app.yml b/.github/workflows/delete-review-app.yml index caf90b18e..074b7c7e8 100644 --- a/.github/workflows/delete-review-app.yml +++ b/.github/workflows/delete-review-app.yml @@ -1,8 +1,6 @@ name: Delete Review App on: - pull_request: - types: [closed] issue_comment: types: [created] @@ -15,17 +13,33 @@ permissions: env: CPLN_ORG: ${{ secrets.CPLN_ORG }} CPLN_TOKEN: ${{ secrets.CPLN_TOKEN }} - APP_NAME: qa-react-webpack-rails-tutorial-pr-${{ github.event.pull_request.number || github.event.issue.number }} - PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }} + APP_NAME: qa-react-webpack-rails-tutorial-pr-${{ github.event.issue.number }} + PR_NUMBER: ${{ github.event.issue.number }} jobs: + debug-trigger: + if: always() + runs-on: ubuntu-latest + steps: + - name: Debug Trigger Conditions + env: + EVENT_NAME: ${{ github.event_name }} + IS_PR: ${{ toJSON(github.event.issue.pull_request) }} + COMMENT: ${{ github.event.comment.body }} + run: | + echo "Debug information for delete-review-app command:" + echo "Event name: $EVENT_NAME" + echo "Is PR (raw): $IS_PR" + echo "Comment body: $COMMENT" + echo "Raw event payload:" + echo '${{ toJSON(github.event) }}' + Process-Delete-Command: + needs: debug-trigger if: | - (github.event_name == 'issue_comment' && - github.event.issue.pull_request && - github.event.comment.body == '/delete-review-app') || - (github.event_name == 'pull_request' && - github.event.action == 'closed') + github.event_name == 'issue_comment' && + github.event.issue.pull_request && + github.event.comment.body == '/delete-review-app' runs-on: ubuntu-latest steps: @@ -41,91 +55,23 @@ jobs: done if [ ${#missing_secrets[@]} -ne 0 ]; then - echo "Required secrets are not set: ${missing_secrets[*]}" + echo " Required secrets are not set: ${missing_secrets[*]}" exit 1 fi - name: Setup Environment uses: ./.github/actions/setup-environment - - name: Set shared functions - id: shared-functions - uses: actions/github-script@v7 - with: - script: | - core.exportVariable('GET_CONSOLE_LINK', ` - function getConsoleLink(prNumber) { - return ' [Control Plane Console for Review App with PR #' + prNumber + '](' + - 'https://console.cpln.io/org/' + process.env.CPLN_ORG + '/workloads/' + process.env.APP_NAME + ')'; - } - `); - - - name: Initialize Delete - id: init-delete - uses: actions/github-script@v7 - with: - script: | - eval(process.env.GET_CONSOLE_LINK); - - async function getWorkflowUrl(runId) { - // Get the current job ID - const jobs = await github.rest.actions.listJobsForWorkflowRun({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: runId - }); - - const currentJob = jobs.data.jobs.find(job => job.status === 'in_progress'); - const jobId = currentJob?.id; - - if (!jobId) { - console.log('Warning: Could not find current job ID'); - return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`; - } - - return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}/job/${jobId}`; - } - - const workflowUrl = await getWorkflowUrl(context.runId); - - const comment = await github.rest.issues.createComment({ - issue_number: process.env.PR_NUMBER, - owner: context.repo.owner, - repo: context.repo.repo, - body: [ - ' Starting app deletion...', - '', - ' [View Delete Logs](' + workflowUrl + ')', - '', - getConsoleLink(process.env.PR_NUMBER) - ].join('\n') - }); - - return { - commentId: comment.data.id, - workflowUrl - }; - - - name: Set workflow URL - run: | - echo "WORKFLOW_URL=${{ fromJSON(steps.init-delete.outputs.result).workflowUrl }}" >> $GITHUB_ENV - - name: Create Initial Delete Comment id: init-delete uses: actions/github-script@v7 with: script: | - let message = '🗑️ Starting app deletion'; - if ('${{ github.event_name }}' === 'pull_request') { - const merged = '${{ github.event.pull_request.merged }}' === 'true'; - message += merged ? ' (PR merged)' : ' (PR closed)'; - } - const comment = await github.rest.issues.createComment({ issue_number: process.env.PR_NUMBER, owner: context.repo.owner, repo: context.repo.repo, - body: message + body: ' Starting app deletion...' }); return { commentId: comment.data.id }; @@ -143,28 +89,21 @@ jobs: uses: actions/github-script@v7 with: script: | - eval(process.env.GET_CONSOLE_LINK); - const success = '${{ job.status }}' === 'success'; const prNumber = process.env.PR_NUMBER; + const cpConsoleUrl = `https://console.cpln.io/org/${process.env.CPLN_ORG}/workloads/${process.env.APP_NAME}`; - const successMessage = [ - '✅ Review app for PR #' + prNumber + ' was successfully deleted', - '', - ' [View Completed Delete Logs](' + process.env.WORKFLOW_URL + ')' - ].join('\n'); - - const failureMessage = [ - '❌ Review app for PR #' + prNumber + ' failed to be deleted', - '', - ' [View Delete Logs with Errors](' + process.env.WORKFLOW_URL + ')', - '', - getConsoleLink(prNumber) - ].join('\n'); + const message = success + ? ' Review app for PR #' + prNumber + ' was successfully deleted' + : [ + ' Review app for PR #' + prNumber + ' failed to be deleted', + '', + '[Control Plane Console for Review App with PR #' + prNumber + '](' + cpConsoleUrl + ')' + ].join('\n'); await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: ${{ fromJSON(steps.init-delete.outputs.result).commentId }}, - body: success ? successMessage : failureMessage + body: message }); diff --git a/.github/workflows/deploy-to-control-plane.yml b/.github/workflows/deploy-to-control-plane.yml index 2adad3d72..ad1dbb490 100644 --- a/.github/workflows/deploy-to-control-plane.yml +++ b/.github/workflows/deploy-to-control-plane.yml @@ -25,7 +25,7 @@ jobs: (github.event_name == 'pull_request') || (github.event_name == 'issue_comment' && github.event.issue.pull_request && - github.event.comment.body == '/deploy-review-app') + github.event.comment.body == '/deploy') runs-on: ubuntu-latest permissions: contents: read @@ -252,7 +252,25 @@ jobs: body: isSuccess ? successMessage : failureMessage }); + debug-help: + if: always() + runs-on: ubuntu-latest + steps: + - name: Debug Trigger Conditions + env: + EVENT_NAME: ${{ github.event_name }} + IS_PR: ${{ toJSON(github.event.issue.pull_request) }} + COMMENT: ${{ github.event.comment.body }} + run: | + echo "Debug information for help command:" + echo "Event name: $EVENT_NAME" + echo "Is PR (raw): $IS_PR" + echo "Comment body: $COMMENT" + echo "Raw event payload:" + echo '${{ toJSON(github.event) }}' + show-help: + needs: debug-help if: | github.event_name == 'issue_comment' && github.event.issue.pull_request && @@ -267,7 +285,7 @@ jobs: const helpMessage = [ '## Available Commands', '', - '### `/deploy-review-app`', + '### `/deploy`', 'Deploys your PR branch to a review environment on Control Plane.', '- Creates a new review app if one doesn\'t exist', '- Updates the existing review app if it already exists', @@ -278,7 +296,7 @@ jobs: 'Deletes the review app associated with this PR.', '- Removes all resources from Control Plane', '- Helpful for cleaning up when you\'re done testing', - '- Can be re-deployed later using `/deploy-review-app`', + '- Can be re-deployed later using `/deploy`', '', '### `/help`', 'Shows this help message explaining available commands.', @@ -292,4 +310,95 @@ jobs: repo: context.repo.repo, issue_number: context.payload.issue.number, body: helpMessage + }); + + debug-delete: + if: always() + runs-on: ubuntu-latest + steps: + - name: Debug Trigger Conditions + env: + EVENT_NAME: ${{ github.event_name }} + IS_PR: ${{ toJSON(github.event.issue.pull_request) }} + COMMENT: ${{ github.event.comment.body }} + run: | + echo "Debug information for delete-review-app command:" + echo "Event name: $EVENT_NAME" + echo "Is PR (raw): $IS_PR" + echo "Comment body: $COMMENT" + echo "Raw event payload:" + echo '${{ toJSON(github.event) }}' + + Process-Delete-Command: + needs: debug-delete + if: | + github.event_name == 'issue_comment' && + github.event.issue.pull_request && + github.event.comment.body == '/delete-review-app' + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Validate Required Secrets + run: | + missing_secrets=() + for secret in "CPLN_TOKEN" "CPLN_ORG"; do + if [ -z "${!secret}" ]; then + missing_secrets+=("$secret") + fi + done + + if [ ${#missing_secrets[@]} -ne 0 ]; then + echo "Required secrets are not set: ${missing_secrets[*]}" + exit 1 + fi + + - name: Setup Environment + uses: ./.github/actions/setup-environment + + - name: Create Initial Delete Comment + id: init-delete + uses: actions/github-script@v7 + with: + script: | + const comment = await github.rest.issues.createComment({ + issue_number: process.env.PR_NUMBER, + owner: context.repo.owner, + repo: context.repo.repo, + body: ' Starting app deletion...' + }); + return { commentId: comment.data.id }; + + - name: Delete Review App + uses: ./.github/actions/delete-control-plane-app + with: + app_name: ${{ env.APP_NAME }} + org: ${{ env.CPLN_ORG }} + github_token: ${{ secrets.GITHUB_TOKEN }} + env: + CPLN_TOKEN: ${{ secrets.CPLN_TOKEN }} + + - name: Update Delete Status + if: always() + uses: actions/github-script@v7 + with: + script: | + const success = '${{ job.status }}' === 'success'; + const prNumber = process.env.PR_NUMBER; + const cpConsoleUrl = `https://console.cpln.io/org/${process.env.CPLN_ORG}/workloads/${process.env.APP_NAME}`; + + const message = success + ? ' Review app for PR #' + prNumber + ' was successfully deleted' + : [ + ' Review app for PR #' + prNumber + ' failed to be deleted', + '', + ' [Control Plane Console for Review App with PR #' + prNumber + '](' + cpConsoleUrl + ')' + ].join('\n'); + + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: ${{ fromJSON(steps.init-delete.outputs.result).commentId }}, + body: message }); \ No newline at end of file diff --git a/.github/workflows/help-command.yml b/.github/workflows/help-command.yml index ff59b65f6..20a0e8285 100644 --- a/.github/workflows/help-command.yml +++ b/.github/workflows/help-command.yml @@ -4,17 +4,30 @@ on: issue_comment: types: [created] -# Use concurrency to prevent duplicate runs -concurrency: - group: help-${{ github.event.issue.number }} - cancel-in-progress: true - permissions: issues: write pull-requests: write jobs: + debug-trigger: + if: always() + runs-on: ubuntu-latest + steps: + - name: Debug Trigger Conditions + env: + EVENT_NAME: ${{ github.event_name }} + IS_PR: ${{ toJSON(github.event.issue.pull_request) }} + COMMENT: ${{ github.event.comment.body }} + run: | + echo "Debug information for help command:" + echo "Event name: $EVENT_NAME" + echo "Is PR (raw): $IS_PR" + echo "Comment body: $COMMENT" + echo "Raw event payload:" + echo '${{ toJSON(github.event) }}' + show-help: + needs: debug-trigger if: | github.event_name == 'issue_comment' && github.event.issue.pull_request && @@ -26,64 +39,52 @@ jobs: uses: actions/github-script@v7 with: script: | - try { - console.log('Creating help message...'); - const helpMessage = [ - '## 📚 Available Commands', - '', - '### `/deploy`', - 'Deploys your PR branch to a review environment on Control Plane.', - '- Creates a new review app if one doesn\'t exist', - '- Updates the existing review app if it already exists', - '- Provides a unique URL to preview your changes', - '- Shows build and deployment progress in real-time', - '', - '**Required Environment Variables:**', - '- `CPLN_TOKEN`: Control Plane authentication token', - '- `CPLN_ORG`: Control Plane organization name', - '', - '**Optional Configuration:**', - '- `WAIT_TIMEOUT`: Deployment timeout in seconds (default: 900)', - ' - Must be a positive integer', - ' - Can be set in GitHub Actions variables', - ' - Applies to both deployment and workload readiness checks', - '', - '### `/delete-review-app`', - 'Deletes the review app associated with this PR.', - '- Removes all resources from Control Plane', - '- Helpful for cleaning up when you\'re done testing', - '- Can be re-deployed later using `/deploy`', - '', - '**Required Environment Variables:**', - '- `CPLN_TOKEN`: Control Plane authentication token', - '- `CPLN_ORG`: Control Plane organization name', - '', - '### `/help`', - 'Shows this help message explaining available commands and configuration.', - '', - '---', - '**Note:** These commands only work in pull request comments.', - '', - '**Environment Setup:**', - '1. Set required secrets in your repository settings:', - ' - `CPLN_TOKEN`', - ' - `CPLN_ORG`', - '2. Optional: Configure `WAIT_TIMEOUT` in GitHub Actions variables to customize deployment timeout' - ].join('\n'); - - console.log('Issue number:', github.context.payload.issue.number); - console.log('Owner:', context.repo.owner); - console.log('Repo:', context.repo.repo); - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: github.context.payload.issue.number, - body: helpMessage - }); - - console.log('Help message posted successfully'); - } catch (error) { - console.error('Error posting help message:', error); - core.setFailed(`Failed to post help message: ${error.message}`); - } + const helpMessage = [ + '## 📚 Available Commands', + '', + '### `/deploy`', + 'Deploys your PR branch to a review environment on Control Plane.', + '- Creates a new review app if one doesn\'t exist', + '- Updates the existing review app if it already exists', + '- Provides a unique URL to preview your changes', + '- Shows build and deployment progress in real-time', + '', + '**Required Environment Variables:**', + '- `CPLN_TOKEN`: Control Plane authentication token', + '- `CPLN_ORG`: Control Plane organization name', + '', + '**Optional Configuration:**', + '- `WAIT_TIMEOUT`: Deployment timeout in seconds (default: 900)', + ' - Must be a positive integer', + ' - Can be set in GitHub Actions variables', + ' - Applies to both deployment and workload readiness checks', + '', + '### `/delete-review-app`', + 'Deletes the review app associated with this PR.', + '- Removes all resources from Control Plane', + '- Helpful for cleaning up when you\'re done testing', + '- Can be re-deployed later using `/deploy`', + '', + '**Required Environment Variables:**', + '- `CPLN_TOKEN`: Control Plane authentication token', + '- `CPLN_ORG`: Control Plane organization name', + '', + '### `/help`', + 'Shows this help message explaining available commands and configuration.', + '', + '---', + '**Note:** These commands only work in pull request comments.', + '', + '**Environment Setup:**', + '1. Set required secrets in your repository settings:', + ' - `CPLN_TOKEN`', + ' - `CPLN_ORG`', + '2. Optional: Configure `WAIT_TIMEOUT` in GitHub Actions variables to customize deployment timeout' + ].join('\n'); + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.issue.number, + body: helpMessage + });