diff --git a/.github/actions/build-docker-image/action.yml b/.github/actions/build-docker-image/action.yml index e1b5df73..45a12434 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: true + required: false runs: using: "composite" @@ -22,11 +22,18 @@ runs: id: build shell: bash run: | - echo "🏗️ Building Docker image for PR #${PR_NUMBER} (commit ${{ inputs.commit }})..." + PR_INFO="" + if [ -n "${PR_NUMBER}" ]; then + PR_INFO=" for PR #${PR_NUMBER}" + fi + + echo "🏗️ Building Docker image${PR_INFO} (commit ${{ inputs.commit }})..." if cpflow build-image -a "${{ inputs.app_name }}" --commit="${{ inputs.commit }}" --org="${{ inputs.org }}"; then - echo "✅ Docker image build successful for PR #${PR_NUMBER} (commit ${{ inputs.commit }})" + 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 }})" else - echo "❌ Docker image build failed for PR #${PR_NUMBER} (commit ${{ inputs.commit }})" + echo "❌ Docker image build failed${PR_INFO} (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 deleted file mode 100644 index 9c6993c6..00000000 --- a/.github/actions/delete-control-plane-app/action.yml +++ /dev/null @@ -1,20 +0,0 @@ -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 deleted file mode 100755 index 92e8fbc3..00000000 --- a/.github/actions/delete-control-plane-app/delete-app.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/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 deleted file mode 100644 index 9988479c..00000000 --- a/.github/actions/help-command/action.yml +++ /dev/null @@ -1,98 +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 - 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 new file mode 100644 index 00000000..c370039d --- /dev/null +++ b/.github/actions/validate-required-vars/action.yml @@ -0,0 +1,28 @@ +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 754b5cd1..a0b3611a 100644 --- a/.github/workflows/delete-review-app.yml +++ b/.github/workflows/delete-review-app.yml @@ -5,6 +5,12 @@ 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 @@ -15,8 +21,8 @@ permissions: env: CPLN_ORG: ${{ vars.CPLN_ORG_STAGING }} CPLN_TOKEN: ${{ secrets.CPLN_TOKEN_STAGING }} - 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: ${{ 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 }} jobs: debug: @@ -29,37 +35,15 @@ jobs: github.event.issue.pull_request && github.event.comment.body == '/delete-review-app') || (github.event_name == 'pull_request' && - github.event.action == 'closed') + github.event.action == 'closed') || + github.event_name == 'workflow_dispatch' 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 - 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: Validate Required Secrets and Variables + uses: ./.github/actions/validate-required-vars - name: Setup Environment uses: ./.github/actions/setup-environment diff --git a/.github/workflows/deploy-to-control-plane.yml b/.github/workflows/deploy-to-control-plane-review-app.yml similarity index 84% rename from .github/workflows/deploy-to-control-plane.yml rename to .github/workflows/deploy-to-control-plane-review-app.yml index d8a9783f..ea05f98b 100644 --- a/.github/workflows/deploy-to-control-plane.yml +++ b/.github/workflows/deploy-to-control-plane-review-app.yml @@ -24,7 +24,7 @@ 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: ${{ vars.REVIEW_APP_PREFIX }}-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 }} @@ -33,10 +33,10 @@ 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 + debug_enabled: false + + process-deployment: + needs: debug if: | (github.event_name == 'pull_request') || (github.event_name == 'push') || @@ -45,12 +45,13 @@ jobs: github.event.issue.pull_request && contains(github.event.comment.body, '/deploy-review-app')) runs-on: ubuntu-latest - permissions: - contents: read - deployments: write - pull-requests: write - issues: write - + 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 }} steps: # Initial checkout only for pull_request and push events - name: Checkout code @@ -69,28 +70,7 @@ jobs: 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 + uses: ./.github/actions/validate-required-vars - name: Get PR HEAD Ref id: getRef @@ -157,13 +137,6 @@ 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: @@ -253,26 +226,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' @@ -305,30 +258,6 @@ 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 @@ -359,17 +288,70 @@ 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 - if: env.DO_DEPLOY != 'false' + id: build uses: ./.github/actions/build-docker-image with: app_name: ${{ env.APP_NAME }} org: ${{ vars.CPLN_ORG_STAGING }} - commit: ${{ env.PR_SHA }} - PR_NUMBER: ${{ env.PR_NUMBER }} + 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 }} - name: Update Status - Deploying - if: env.DO_DEPLOY != 'false' uses: actions/github-script@v7 with: script: | @@ -378,7 +360,7 @@ jobs: '', '⏳ Waiting for deployment to be ready...', '', - '📝 [View Deploy Logs](' + process.env.WORKFLOW_URL + ')', + '📝 [View Deploy Logs](${{ env.WORKFLOW_URL }})', '', process.env.CONSOLE_LINK ].join('\n'); diff --git a/.github/workflows/deploy-to-control-plane-staging.yml b/.github/workflows/deploy-to-control-plane-staging.yml index 898c447c..d789d1ee 100644 --- a/.github/workflows/deploy-to-control-plane-staging.yml +++ b/.github/workflows/deploy-to-control-plane-staging.yml @@ -1,32 +1,96 @@ # Control Plane GitHub Action -name: Deploy Main Branch to Control Plane Staging +name: Deploy to Control Plane Staging +run-name: Deploy Control Plane Staging App # 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: [master] - - # Allows you to run this workflow manually from the Actions tab + branches: + - '*' workflow_dispatch: # Convert the GitHub secret variables to environment variables for use by the Control Plane CLI env: - CPLN_ORG: ${{ vars.CPLN_ORG_STAGING }} + 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 jobs: - deploy-to-control-plane-staging: + debug: + uses: ./.github/workflows/debug-workflow.yml + with: + debug_enabled: false + + validate-branch: 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: - - uses: actions/checkout@v4 + - name: Checkout code + uses: actions/checkout@v4 with: - fetch-depth: 0 # Fetch all history for proper SHA handling - ref: master # Explicitly checkout master branch + 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 }} - - uses: ./.github/actions/deploy-to-control-plane + - name: 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/help-command.yml b/.github/workflows/help-command.yml index 121d4712..51ce2566 100644 --- a/.github/workflows/help-command.yml +++ b/.github/workflows/help-command.yml @@ -1,42 +1,151 @@ -name: Show Help for Commands +name: Help Command on: - push: - branches: - - "**" # Trigger on all branches, including feature branches issue_comment: types: [created] workflow_dispatch: inputs: - issue-number: - description: 'PR/Issue number to post the help comment to' + pr_number: + description: 'Pull Request number to post help comment on' required: true - type: number + type: string permissions: issues: write 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' || - (github.event_name == 'issue_comment' && - github.event.issue.pull_request && - github.event.comment.body == '/help') + help: + if: ${{ (github.event.issue.pull_request && github.event.comment.body == '/help') || github.event_name == 'workflow_dispatch' }} runs-on: ubuntu-latest - + steps: - - name: Checkout - uses: actions/checkout - - - name: Process Help Command - uses: ./.github/actions/help-command + - name: Show Available Commands + uses: actions/github-script@v7 with: - github-token: ${{ secrets.GITHUB_TOKEN }} - issue-number: ${{ github.event.inputs.issue-number }} \ No newline at end of file + 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'); + }; + + 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 diff --git a/.github/workflows/review-app-help.yml b/.github/workflows/review-app-help.yml index a54bfdf0..d5eed23c 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({