Deploying Review App #47
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Deploy Review App 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: | | |
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); | |
return { | |
deploymentId: deployment.data.id, | |
commentId: comment.data.id, | |
workflowUrl | |
}; | |
- name: Set comment ID and workflow URL | |
if: steps.determine_action.outputs.action == 'deploy' | |
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 | |
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 buildingMessage = [ | |
'🏗️ Building Docker image for PR #' + process.env.PR_NUMBER + ', commit ' + '${{ env.COMMIT_HASH }}', | |
'', | |
'[View Build Logs](' + process.env.WORKFLOW_URL + ')' | |
].join('\n'); | |
await github.rest.issues.updateComment({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
comment_id: process.env.COMMENT_ID, | |
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 deployingMessage = [ | |
'🚀 Deploying to Control Plane...', | |
'', | |
'⏳ Waiting for deployment to be ready...', | |
'', | |
'[View Deploy Logs](' + process.env.WORKFLOW_URL + ')' | |
].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 | |
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 - Success | |
if: steps.determine_action.outputs.action == 'deploy' && success() | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
await github.rest.issues.updateComment({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
comment_id: process.env.COMMENT_ID, | |
body: `🚀 Review App for PR #${process.env.PR_NUMBER}: [\`${process.env.REVIEW_APP_URL}\`](${process.env.REVIEW_APP_URL})` | |
}); | |
- name: Update Status - Deployment Complete | |
if: steps.determine_action.outputs.action == 'deploy' | |
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'; | |
// 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 complete for PR #' + prNumber + ', commit ' + '${{ env.COMMIT_HASH }}', | |
'', | |
'🚀 Review App for PR #' + prNumber + ': [`' + appUrl + '`](' + appUrl + ')', | |
'', | |
'📋 [View Completed Action Build and Deploy Logs](' + workflowUrl + ')' | |
].join('\n'); | |
const failureMessage = [ | |
'❌ Deployment failed for PR #' + prNumber, | |
'', | |
'[View Workflow Status](' + workflowUrl + ')' | |
].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 | |
}); |