Skip to content

Commit 151c527

Browse files
authored
Enhance GitHub Actions workflows with new features and improvements (#618)
Summary: Added validation for required secrets in deployment workflows. Introduced multiple status update steps during deployment for better feedback. Enhanced help command to reflect updated deployment command names. Refined deployment triggers to focus on issue comments and push events. Enhanced error handling and logging in deployment scripts. Streamlined deployment process with checks for existing review apps. Simplified workflow configuration for better readability and management.
1 parent 4bf2c03 commit 151c527

File tree

7 files changed

+501
-295
lines changed

7 files changed

+501
-295
lines changed

.github/actions/deploy-to-control-plane/action.yml

+212-55
Original file line numberDiff line numberDiff line change
@@ -26,66 +26,223 @@ outputs:
2626
runs:
2727
using: "composite"
2828
steps:
29+
- name: Validate Required Secrets
30+
shell: bash
31+
run: |
32+
missing_secrets=()
33+
for secret in "CPLN_TOKEN" "CPLN_ORG"; do
34+
if [ -z "${!secret}" ]; then
35+
missing_secrets+=("$secret")
36+
fi
37+
done
38+
39+
if [ ${#missing_secrets[@]} -ne 0 ]; then
40+
echo "Required secrets are not set: ${missing_secrets[*]}"
41+
exit 1
42+
fi
43+
2944
- name: Setup Environment
3045
uses: ./.github/actions/setup-environment
3146

32-
- name: Get Commit SHA
33-
id: get_sha
47+
- name: Set shared functions
48+
id: shared-functions
49+
uses: actions/github-script@v7
50+
with:
51+
script: |
52+
core.exportVariable('GET_CONSOLE_LINK', `
53+
function getConsoleLink(prNumber) {
54+
return ' [Control Plane Console for Review App with PR #' + prNumber + '](' +
55+
'https://console.cpln.io/org/' + process.env.CPLN_ORG + '/workloads/' + process.env.APP_NAME + ')';
56+
}
57+
`);
58+
59+
- name: Initialize Deployment
60+
id: init-deployment
61+
uses: actions/github-script@v7
62+
with:
63+
script: |
64+
eval(process.env.GET_CONSOLE_LINK);
65+
66+
async function getWorkflowUrl(runId) {
67+
// Get the current job ID
68+
const jobs = await github.rest.actions.listJobsForWorkflowRun({
69+
owner: context.repo.owner,
70+
repo: context.repo.repo,
71+
run_id: runId
72+
});
73+
74+
const currentJob = jobs.data.jobs.find(job => job.status === 'in_progress');
75+
const jobId = currentJob?.id;
76+
77+
if (!jobId) {
78+
console.log('Warning: Could not find current job ID');
79+
return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`;
80+
}
81+
82+
return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}/job/${jobId}`;
83+
}
84+
85+
// Create initial deployment comment
86+
const comment = await github.rest.issues.createComment({
87+
owner: context.repo.owner,
88+
repo: context.repo.repo,
89+
issue_number: process.env.PR_NUMBER,
90+
body: ' Initializing deployment...'
91+
});
92+
93+
// Create GitHub deployment
94+
const deployment = await github.rest.repos.createDeployment({
95+
owner: context.repo.owner,
96+
repo: context.repo.repo,
97+
ref: context.sha,
98+
environment: 'review',
99+
auto_merge: false,
100+
required_contexts: []
101+
});
102+
103+
const workflowUrl = await getWorkflowUrl(context.runId);
104+
105+
core.exportVariable('WORKFLOW_URL', workflowUrl);
106+
core.exportVariable('COMMENT_ID', comment.data.id);
107+
core.exportVariable('DEPLOYMENT_ID', deployment.data.id);
108+
109+
- name: Set commit hash
34110
shell: bash
35-
run: ${{ github.action_path }}/scripts/get-commit-sha.sh
36-
env:
37-
GITHUB_TOKEN: ${{ inputs.github_token }}
38-
PR_NUMBER: ${{ env.PR_NUMBER }}
111+
run: |
112+
FULL_COMMIT=$(git rev-parse HEAD)
113+
echo "COMMIT_HASH=${FULL_COMMIT:0:7}" >> $GITHUB_ENV
39114
40-
- name: Deploy to Control Plane
41-
id: deploy
115+
- name: Update Status - Setting Up
116+
uses: actions/github-script@v7
117+
with:
118+
script: |
119+
eval(process.env.GET_CONSOLE_LINK);
120+
121+
const setupMessage = [
122+
'🔧 Setting up Control Plane app...',
123+
'',
124+
' [View Setup Logs](' + process.env.WORKFLOW_URL + ')',
125+
'',
126+
getConsoleLink(process.env.PR_NUMBER)
127+
].join('\n');
128+
129+
await github.rest.issues.updateComment({
130+
owner: context.repo.owner,
131+
repo: context.repo.repo,
132+
comment_id: process.env.COMMENT_ID,
133+
body: setupMessage
134+
});
135+
136+
- name: Setup Control Plane App
42137
shell: bash
43138
run: |
44-
echo "🚀 Deploying app for PR #${PR_NUMBER}..."
45-
46-
# Create temp file for output
47-
TEMP_OUTPUT=$(mktemp)
48-
trap 'rm -f "${TEMP_OUTPUT}"' EXIT
49-
50-
# Deploy the application and show output in real-time while capturing it
51-
if ! cpflow deploy-image -a "${{ inputs.app_name }}" --run-release-phase --org "${{ inputs.org }}" 2>&1 | tee "${TEMP_OUTPUT}"; then
52-
echo "❌ Deployment failed for PR #${PR_NUMBER}"
53-
echo "Error output:"
54-
cat "${TEMP_OUTPUT}"
55-
exit 1
56-
fi
57-
58-
# Extract app URL from captured output
59-
REVIEW_APP_URL=$(grep -oP 'https://rails-[^[:space:]]*\.cpln\.app(?=\s|$)' "${TEMP_OUTPUT}" | head -n1)
60-
if [ -z "${REVIEW_APP_URL}" ]; then
61-
echo "❌ Failed to get app URL from deployment output"
62-
echo "Deployment output:"
63-
cat "${TEMP_OUTPUT}"
64-
exit 1
65-
fi
66-
67-
# Wait for all workloads to be ready
68-
WAIT_TIMEOUT=${WAIT_TIMEOUT:-${{ inputs.wait_timeout }}}
69-
if ! [[ "${WAIT_TIMEOUT}" =~ ^[0-9]+$ ]]; then
70-
echo "❌ Invalid timeout value: ${WAIT_TIMEOUT}"
71-
exit 1
139+
echo "🔧 Checking if app exists..."
140+
if ! cpflow exists -a ${{ inputs.app_name }} ; then
141+
echo "📦 Setting up new Control Plane app..."
142+
cpflow setup-app -a ${{ inputs.app_name }}
72143
fi
73-
echo "⏳ Waiting for all workloads to be ready (timeout: ${WAIT_TIMEOUT}s)"
74-
75-
# Use timeout command with ps:wait and show output in real-time
76-
if ! timeout "${WAIT_TIMEOUT}" bash -c "cpflow ps:wait -a \"${{ inputs.app_name }}\"" 2>&1 | tee -a "${TEMP_OUTPUT}"; then
77-
TIMEOUT_EXIT=$?
78-
if [ ${TIMEOUT_EXIT} -eq 124 ]; then
79-
echo "❌ Timed out waiting for workloads after ${WAIT_TIMEOUT} seconds"
80-
else
81-
echo "❌ Workloads did not become ready for PR #${PR_NUMBER} (exit code: ${TIMEOUT_EXIT})"
82-
fi
83-
echo "Full output:"
84-
cat "${TEMP_OUTPUT}"
85-
exit 1
86-
fi
87-
88-
echo "✅ Deployment successful for PR #${PR_NUMBER}"
89-
echo "🌐 App URL: ${REVIEW_APP_URL}"
90-
echo "review_app_url=${REVIEW_APP_URL}" >> $GITHUB_OUTPUT
91-
echo "REVIEW_APP_URL=${REVIEW_APP_URL}" >> $GITHUB_ENV
144+
145+
- name: Update Status - Building
146+
uses: actions/github-script@v7
147+
with:
148+
script: |
149+
eval(process.env.GET_CONSOLE_LINK);
150+
151+
const buildingMessage = [
152+
'🏗️ Building Docker image for PR #' + process.env.PR_NUMBER + ', commit ' + process.env.COMMIT_HASH,
153+
'',
154+
' [View Build Logs](' + process.env.WORKFLOW_URL + ')',
155+
'',
156+
getConsoleLink(process.env.PR_NUMBER)
157+
].join('\n');
158+
159+
await github.rest.issues.updateComment({
160+
owner: context.repo.owner,
161+
repo: context.repo.repo,
162+
comment_id: process.env.COMMENT_ID,
163+
body: buildingMessage
164+
});
165+
166+
- name: Update Status - Deploying
167+
uses: actions/github-script@v7
168+
with:
169+
script: |
170+
eval(process.env.GET_CONSOLE_LINK);
171+
172+
const deployingMessage = [
173+
'🚀 Deploying to Control Plane...',
174+
'',
175+
'⏳ Waiting for deployment to be ready...',
176+
'',
177+
' [View Deploy Logs](' + process.env.WORKFLOW_URL + ')',
178+
'',
179+
getConsoleLink(process.env.PR_NUMBER)
180+
].join('\n');
181+
182+
await github.rest.issues.updateComment({
183+
owner: context.repo.owner,
184+
repo: context.repo.repo,
185+
comment_id: process.env.COMMENT_ID,
186+
body: deployingMessage
187+
});
188+
189+
- name: Deploy to Control Plane
190+
id: deploy
191+
shell: bash
192+
run: ${{ github.action_path }}/scripts/deploy.sh
193+
env:
194+
APP_NAME: ${{ inputs.app_name }}
195+
CPLN_ORG: ${{ inputs.org }}
196+
WAIT_TIMEOUT: ${{ inputs.wait_timeout }}
197+
198+
- name: Update Status - Deployment Complete
199+
if: always()
200+
uses: actions/github-script@v7
201+
with:
202+
script: |
203+
eval(process.env.GET_CONSOLE_LINK);
204+
205+
const prNumber = process.env.PR_NUMBER;
206+
const appUrl = process.env.REVIEW_APP_URL;
207+
const workflowUrl = process.env.WORKFLOW_URL;
208+
const isSuccess = '${{ job.status }}' === 'success';
209+
210+
// Create GitHub deployment status
211+
const deploymentStatus = {
212+
owner: context.repo.owner,
213+
repo: context.repo.repo,
214+
deployment_id: process.env.DEPLOYMENT_ID,
215+
state: isSuccess ? 'success' : 'failure',
216+
environment_url: isSuccess ? appUrl : undefined,
217+
log_url: workflowUrl,
218+
environment: 'review'
219+
};
220+
221+
await github.rest.repos.createDeploymentStatus(deploymentStatus);
222+
223+
// Define messages based on deployment status
224+
const successMessage = [
225+
'✅ Deployment complete for PR #' + prNumber + ', commit ' + process.env.COMMIT_HASH,
226+
'',
227+
'🌐 [Review App for PR #' + prNumber + '](' + appUrl + ')',
228+
'',
229+
' [View Completed Action Build and Deploy Logs](' + workflowUrl + ')',
230+
'',
231+
getConsoleLink(prNumber)
232+
].join('\n');
233+
234+
const failureMessage = [
235+
'❌ Deployment failed for PR #' + prNumber + ', commit ' + process.env.COMMIT_HASH,
236+
'',
237+
' [View Deployment Logs with Errors](' + workflowUrl + ')',
238+
'',
239+
getConsoleLink(prNumber)
240+
].join('\n');
241+
242+
// Update the existing comment
243+
await github.rest.issues.updateComment({
244+
owner: context.repo.owner,
245+
repo: context.repo.repo,
246+
comment_id: process.env.COMMENT_ID,
247+
body: isSuccess ? successMessage : failureMessage
248+
});

.github/actions/deploy-to-control-plane/scripts/deploy.sh

+33-18
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/bin/bash
22

33
# This script handles the deployment to Control Plane and extracts the Rails URL
4-
#
4+
#
55
# Required environment variables:
66
# - APP_NAME: Name of the application to deploy
77
# - CPLN_ORG: Control Plane organization
@@ -31,21 +31,36 @@ trap 'rm -f "$TEMP_OUTPUT"' EXIT
3131

3232
# Deploy the application
3333
echo "🚀 Deploying to Control Plane (timeout: ${WAIT_TIMEOUT}s)"
34-
if timeout "$WAIT_TIMEOUT" cpflow deploy-image -a "$APP_NAME" --run-release-phase --org "$CPLN_ORG" --verbose | tee "$TEMP_OUTPUT"; then
35-
# Extract Rails URL from deployment output
36-
RAILS_URL=$(grep -oP 'https://rails-[^[:space:]]*\.cpln\.app(?=\s|$)' "$TEMP_OUTPUT" | head -n1)
37-
if [ -n "$RAILS_URL" ]; then
38-
echo "rails_url=$RAILS_URL" >> "$GITHUB_OUTPUT"
39-
echo "✅ Deployment successful"
40-
echo "🚀 Rails URL: $RAILS_URL"
41-
else
42-
echo "❌ Failed to extract Rails URL from deployment output"
43-
exit 1
44-
fi
45-
elif [ $? -eq 124 ]; then
46-
echo "❌ Deployment timed out after $WAIT_TIMEOUT seconds"
47-
exit 1
48-
else
49-
echo "❌ Deployment to Control Plane failed"
50-
exit 1
34+
if ! timeout "${WAIT_TIMEOUT}" cpflow deploy-image -a "$APP_NAME" --run-release-phase --org "$CPLN_ORG" --verbose 2>&1 | tee "$TEMP_OUTPUT"; then
35+
echo "❌ Deployment failed"
36+
echo "Full output:"
37+
cat "$TEMP_OUTPUT"
38+
exit 1
39+
fi
40+
41+
# Extract app URL from deployment output
42+
RAILS_URL=$(grep -oP 'https://rails-[^[:space:]]*\.cpln\.app(?=\s|$)' "$TEMP_OUTPUT" | head -n1)
43+
if [ -z "$RAILS_URL" ]; then
44+
echo "❌ Failed to get app URL from deployment output"
45+
echo "Full output:"
46+
cat "$TEMP_OUTPUT"
47+
exit 1
5148
fi
49+
50+
# Wait for all workloads to be ready
51+
echo "⏳ Waiting for all workloads to be ready (timeout: ${WAIT_TIMEOUT}s)"
52+
if ! timeout "${WAIT_TIMEOUT}" bash -c "cpflow ps:wait -a \"$APP_NAME\"" 2>&1 | tee -a "$TEMP_OUTPUT"; then
53+
TIMEOUT_EXIT=$?
54+
if [ ${TIMEOUT_EXIT} -eq 124 ]; then
55+
echo "❌ Timed out waiting for workloads after ${WAIT_TIMEOUT} seconds"
56+
else
57+
echo "❌ Workloads did not become ready"
58+
fi
59+
echo "Full output:"
60+
cat "$TEMP_OUTPUT"
61+
exit 1
62+
fi
63+
64+
echo "✅ Deployment successful"
65+
echo "🌐 Rails URL: $RAILS_URL"
66+
echo "rails_url=$RAILS_URL" >> "$GITHUB_OUTPUT"

.github/workflows/add-comment-on-pr-creation.yml

+18-18
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,21 @@ jobs:
1010
permissions:
1111
pull-requests: write
1212
steps:
13-
name: Add GitHub Comment for review app instructions
14-
uses: actions/github-script@v7
15-
with:
16-
script: |
17-
await github.rest.issues.createComment({
18-
owner: context.repo.owner,
19-
repo: context.repo.repo,
20-
issue_number: context.payload.pull_request.number,
21-
body: [
22-
"Hi 👋 Here are the commands available for this PR:",
23-
"",
24-
"- `/deploy-review-app`: Deploy your changes to a review environment",
25-
"- `/delete-review-app`: Clean up the review environment when you're done",
26-
"- `/help`: Show detailed information about all commands",
27-
"",
28-
"Use `/help` to see full documentation, including configuration options."
29-
].join("\n")
30-
});
13+
- uses: actions/github-script@v7
14+
name: Add GitHub Comment for review app instructions
15+
with:
16+
script: |
17+
await github.rest.issues.createComment({
18+
owner: context.repo.owner,
19+
repo: context.repo.repo,
20+
issue_number: context.payload.pull_request.number,
21+
body: [
22+
"Hi 👋 Here are the commands available for this PR:",
23+
"",
24+
"- `/deploy-review-app`: Deploy your changes to a review environment",
25+
"- `/delete-review-app`: Clean up the review environment when you're done",
26+
"- `/help`: Show detailed information about all commands",
27+
"",
28+
"Use `/help` to see full documentation, including configuration options."
29+
].join("\n")
30+
});

0 commit comments

Comments
 (0)