Skip to content

Deploying Review App #43

Deploying Review App

Deploying Review App #43

name: Deploy to Control Plane
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]
# Use concurrency to cancel in-progress runs
concurrency:
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_STAGING }}
CPLN_TOKEN: ${{ secrets.CPLN_TOKEN_STAGING }}
PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }}
jobs:
Process-Deployment-Command:
if: |
github.event_name == 'pull_request' ||
(github.event_name == 'issue_comment' &&
(github.event.comment.body == '/deploy-review-app' ||
github.event.comment.body == '/delete-review-app') &&
github.event.issue.pull_request)
runs-on: ubuntu-latest
permissions:
contents: read
deployments: write
pull-requests: write
issues: write
steps:
- name: Get PR HEAD Ref
if: github.event_name == 'issue_comment'
id: getRef
run: |
# 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: Determine Action
id: determine_action
run: |
if [[ "${{ github.event.comment.body }}" == "/delete-review-app" ]]; then
echo "action=delete" >> $GITHUB_OUTPUT
else
echo "action=deploy" >> $GITHUB_OUTPUT
fi
- uses: actions/checkout@v4
with:
fetch-depth: 0
# For PR events: use the head SHA
# For PR comments: use the PR head SHA
# For other events: use the push SHA
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || steps.getRef.outputs.PR_SHA || github.sha }}
- name: Setup Environment
uses: ./.github/actions/setup-environment
# Delete App Steps
- name: Create Initial Delete Comment
if: steps.determine_action.outputs.action == 'delete'
uses: actions/github-script@v7
id: init-delete
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 App
if: steps.determine_action.outputs.action == 'delete'
id: delete
run: |
echo "🗑️ Deleting review app: $APP_NAME"
${{ github.workspace }}/.github/actions/deploy-to-control-plane/scripts/delete-app.sh
- name: Update Delete Status
if: steps.determine_action.outputs.action == 'delete'
uses: actions/github-script@v7
with:
script: |
const success = '${{ steps.delete.outcome }}' === 'success';
const message = success
? [
'✅ Review app successfully deleted',
'',
'⚡ [Control Plane Console](https://console.cpln.io/console/org/' + process.env.CPLN_ORG + '/gvc/' + process.env.APP_NAME + '/workload)'
].join('\n')
: '❌ Review app deletion failed';
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: ${{ fromJSON(steps.init-delete.outputs.result).commentId }},
body: message
});
# Deploy Steps
- name: Initialize Deployment
if: steps.determine_action.outputs.action == 'deploy'
id: init-deployment
uses: actions/github-script@v7
with:
script: |
function getWorkflowUrl(runId) {
return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}/job/${context.job.id}`;
}
const prNumber = process.env.PR_NUMBER;
const getJobUrl = (runId, jobId, prNumber) =>
`${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}/job/${jobId}?pr=${prNumber}`;
// Create deployment
const deployment = await github.rest.repos.createDeployment({
owner: context.repo.owner,
repo: context.repo.repo,
ref: context.sha,
environment: 'review-' + prNumber,
auto_merge: false,
required_contexts: []
});
// Get job URL
const jobs = await github.rest.actions.listJobsForWorkflowRun({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.runId
});
const jobId = jobs.data.jobs.find(job => job.name === 'Process-Deployment-Command')?.id;
const jobUrl = getJobUrl(context.runId, jobId, prNumber);
// Create initial comment
const comment = await github.rest.issues.createComment({
issue_number: prNumber,
owner: context.repo.owner,
repo: context.repo.repo,
body: '🚀 Deploying to Control Plane...\n\n[View Deployment Progress](' + jobUrl + ')'
});
return {
deploymentId: deployment.data.id,
commentId: comment.data.id,
workflowUrl: getWorkflowUrl(context.runId)
};
- name: Set commit hash
if: steps.determine_action.outputs.action == 'deploy'
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: steps.determine_action.outputs.action == 'deploy'
uses: actions/github-script@v7
with:
script: |
const result = ${{ steps.init-deployment.outputs.result }};
const commentId = result.commentId;
const buildingMessage = [
'🏗️ Building Docker image for PR #' + process.env.PR_NUMBER + ', commit ' + '${{ env.COMMIT_HASH }}',
'',
'[View Build Logs](' + result.workflowUrl + ')'
].join('\n');
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: commentId,
body: buildingMessage
});
- name: Build Docker Image
if: steps.determine_action.outputs.action == 'deploy'
uses: ./.github/actions/build-docker-image
with:
app_name: ${{ env.APP_NAME }}
org: ${{ env.CPLN_ORG }}
commit: ${{ env.COMMIT_HASH }}
PR_NUMBER: ${{ env.PR_NUMBER }}
- name: Update Status - Deploying
if: steps.determine_action.outputs.action == 'deploy'
uses: actions/github-script@v7
with:
script: |
const result = ${{ steps.init-deployment.outputs.result }};
const commentId = result.commentId;
const deployingMessage = [
'🚀 Deploying to Control Plane for PR #' + process.env.PR_NUMBER + ', commit ' + '${{ env.COMMIT_HASH }}',
'',
'⏳ Waiting for deployment to be ready...',
'',
'[View Deploy Logs](' + result.workflowUrl + ')'
].join('\n');
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: commentId,
body: deployingMessage
});
- name: Deploy to Control Plane
if: steps.determine_action.outputs.action == 'deploy'
id: deploy
uses: ./.github/actions/deploy-to-control-plane
with:
app_name: ${{ env.APP_NAME }}
org: ${{ env.CPLN_ORG }}
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Update Status - Deployment Complete
if: steps.determine_action.outputs.action == 'deploy'
uses: actions/github-script@v7
with:
script: |
const isSuccess = '${{ job.status }}' === 'success';
const result = ${{ steps.init-deployment.outputs.result }};
const deploymentId = result.deploymentId;
const commentId = result.commentId;
const workflowUrl = result.workflowUrl;
const railsUrl = '${{ steps.deploy.outputs.rails_url }}';
const prNumber = process.env.PR_NUMBER;
// Update deployment status
const deploymentStatus = {
owner: context.repo.owner,
repo: context.repo.repo,
deployment_id: deploymentId,
state: isSuccess ? 'success' : 'failure',
description: isSuccess ? '✅ Deployment successful' : '❌ Deployment failed'
};
if (isSuccess) {
deploymentStatus.environment_url = railsUrl;
}
await github.rest.repos.createDeploymentStatus(deploymentStatus);
// Update the initial comment
const successMessage = [
'✅ Deployment successful for PR #' + prNumber + ', commit ' + '${{ env.COMMIT_HASH }}',
'',
'🚀 Rails App: [' + railsUrl + '](' + railsUrl + ')',
'',
'⚡ [Control Plane Console](https://console.cpln.io/console/org/' + process.env.CPLN_ORG + '/gvc/' + process.env.APP_NAME + '/workload)',
'',
'[View Completed Action Build and Deploy Logs](' + workflowUrl + ')'
].join('\n');
const failureMessage = [
'❌ Deployment failed for PR #' + prNumber,
'',
'[View Workflow Status](' + workflowUrl + ')'
].join('\n');
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: commentId,
body: isSuccess ? successMessage : failureMessage
});