@@ -26,74 +26,240 @@ outputs:
26
26
runs :
27
27
using : " composite"
28
28
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
+
29
44
- name : Setup Environment
30
45
uses : ./.github/actions/setup-environment
31
46
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
34
110
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
39
114
40
- - name : Setup Control Plane App If It Doesn't Exist
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
41
137
shell : bash
42
138
run : |
139
+ echo "🔧 Checking if app exists..."
43
140
if ! cpflow exists -a ${{ inputs.app_name }} ; then
44
- echo " Setting up new Control Plane app for app ${{ inputs.app_name }} ..."
141
+ echo "📦 Setting up new Control Plane app..."
45
142
cpflow setup-app -a ${{ inputs.app_name }}
46
143
fi
47
144
145
+ echo "🔧 Ensuring rails template is applied..."
146
+ if ! cpflow list-workloads -a ${{ inputs.app_name }} --org ${{ inputs.org }} | grep -q "rails"; then
147
+ echo "📦 Applying rails template..."
148
+ if ! cpflow apply-template rails -a ${{ inputs.app_name }} --org ${{ inputs.org }}; then
149
+ echo "❌ Failed to apply rails template"
150
+ exit 1
151
+ fi
152
+ fi
153
+
154
+ - name : Update Status - Building
155
+ uses : actions/github-script@v7
156
+ with :
157
+ script : |
158
+ eval(process.env.GET_CONSOLE_LINK);
159
+
160
+ const buildingMessage = [
161
+ '🏗️ Building Docker image for PR #' + process.env.PR_NUMBER + ', commit ' + process.env.COMMIT_HASH,
162
+ '',
163
+ ' [View Build Logs](' + process.env.WORKFLOW_URL + ')',
164
+ '',
165
+ getConsoleLink(process.env.PR_NUMBER)
166
+ ].join('\n');
167
+
168
+ await github.rest.issues.updateComment({
169
+ owner: context.repo.owner,
170
+ repo: context.repo.repo,
171
+ comment_id: process.env.COMMENT_ID,
172
+ body: buildingMessage
173
+ });
174
+
175
+ - name : Build Docker Image
176
+ uses : ./.github/actions/build-docker-image
177
+ with :
178
+ app_name : ${{ inputs.app_name }}
179
+ org : ${{ inputs.org }}
180
+ commit : ${{ env.COMMIT_HASH }}
181
+ PR_NUMBER : ${{ env.PR_NUMBER }}
182
+
183
+ - name : Update Status - Deploying
184
+ uses : actions/github-script@v7
185
+ with :
186
+ script : |
187
+ eval(process.env.GET_CONSOLE_LINK);
188
+
189
+ const deployingMessage = [
190
+ '🚀 Deploying to Control Plane...',
191
+ '',
192
+ '⏳ Waiting for deployment to be ready...',
193
+ '',
194
+ ' [View Deploy Logs](' + process.env.WORKFLOW_URL + ')',
195
+ '',
196
+ getConsoleLink(process.env.PR_NUMBER)
197
+ ].join('\n');
198
+
199
+ await github.rest.issues.updateComment({
200
+ owner: context.repo.owner,
201
+ repo: context.repo.repo,
202
+ comment_id: process.env.COMMENT_ID,
203
+ body: deployingMessage
204
+ });
205
+
48
206
- name : Deploy to Control Plane
49
207
id : deploy
50
208
shell : bash
51
- run : |
52
- echo "🚀 Deploying app for PR #${PR_NUMBER}..."
53
-
54
- # Create temp file for output
55
- TEMP_OUTPUT=$(mktemp)
56
- trap 'rm -f "${TEMP_OUTPUT}"' EXIT
57
-
58
- # Deploy the application and show output in real-time while capturing it
59
- if ! cpflow deploy-image -a "${{ inputs.app_name }}" --run-release-phase --org "${{ inputs.org }}" 2>&1 | tee "${TEMP_OUTPUT}"; then
60
- echo "❌ Deployment failed for PR #${PR_NUMBER}"
61
- echo "Error output:"
62
- cat "${TEMP_OUTPUT}"
63
- exit 1
64
- fi
65
-
66
- # Extract app URL from captured output
67
- REVIEW_APP_URL=$(grep -oP 'https://rails-[^[:space:]]*\.cpln\.app(?=\s|$)' "${TEMP_OUTPUT}" | head -n1)
68
- if [ -z "${REVIEW_APP_URL}" ]; then
69
- echo "❌ Failed to get app URL from deployment output"
70
- echo "Deployment output:"
71
- cat "${TEMP_OUTPUT}"
72
- exit 1
73
- fi
74
-
75
- # Wait for all workloads to be ready
76
- WAIT_TIMEOUT=${WAIT_TIMEOUT:-${{ inputs.wait_timeout }}}
77
- if ! [[ "${WAIT_TIMEOUT}" =~ ^[0-9]+$ ]]; then
78
- echo "❌ Invalid timeout value: ${WAIT_TIMEOUT}"
79
- exit 1
80
- fi
81
- echo "⏳ Waiting for all workloads to be ready (timeout: ${WAIT_TIMEOUT}s)"
82
-
83
- # Use timeout command with ps:wait and show output in real-time
84
- if ! timeout "${WAIT_TIMEOUT}" bash -c "cpflow ps:wait -a \"${{ inputs.app_name }}\"" 2>&1 | tee -a "${TEMP_OUTPUT}"; then
85
- TIMEOUT_EXIT=$?
86
- if [ ${TIMEOUT_EXIT} -eq 124 ]; then
87
- echo "❌ Timed out waiting for workloads after ${WAIT_TIMEOUT} seconds"
88
- else
89
- echo "❌ Workloads did not become ready for PR #${PR_NUMBER} (exit code: ${TIMEOUT_EXIT})"
90
- fi
91
- echo "Full output:"
92
- cat "${TEMP_OUTPUT}"
93
- exit 1
94
- fi
95
-
96
- echo "✅ Deployment successful for PR #${PR_NUMBER}"
97
- echo "🌐 App URL: ${REVIEW_APP_URL}"
98
- echo "review_app_url=${REVIEW_APP_URL}" >> $GITHUB_OUTPUT
99
- echo "REVIEW_APP_URL=${REVIEW_APP_URL}" >> $GITHUB_ENV
209
+ run : ${{ github.action_path }}/scripts/deploy.sh
210
+ env :
211
+ APP_NAME : ${{ inputs.app_name }}
212
+ CPLN_ORG : ${{ inputs.org }}
213
+ WAIT_TIMEOUT : ${{ inputs.wait_timeout }}
214
+
215
+ - name : Update Status - Deployment Complete
216
+ if : always()
217
+ uses : actions/github-script@v7
218
+ with :
219
+ script : |
220
+ eval(process.env.GET_CONSOLE_LINK);
221
+
222
+ const prNumber = process.env.PR_NUMBER;
223
+ const appUrl = process.env.REVIEW_APP_URL;
224
+ const workflowUrl = process.env.WORKFLOW_URL;
225
+ const isSuccess = '${{ job.status }}' === 'success';
226
+
227
+ // Create GitHub deployment status
228
+ const deploymentStatus = {
229
+ owner: context.repo.owner,
230
+ repo: context.repo.repo,
231
+ deployment_id: process.env.DEPLOYMENT_ID,
232
+ state: isSuccess ? 'success' : 'failure',
233
+ environment_url: isSuccess ? appUrl : undefined,
234
+ log_url: workflowUrl,
235
+ environment: 'review'
236
+ };
237
+
238
+ await github.rest.repos.createDeploymentStatus(deploymentStatus);
239
+
240
+ // Define messages based on deployment status
241
+ const successMessage = [
242
+ '✅ Deployment complete for PR #' + prNumber + ', commit ' + process.env.COMMIT_HASH,
243
+ '',
244
+ '🌐 [Review App for PR #' + prNumber + '](' + appUrl + ')',
245
+ '',
246
+ ' [View Completed Action Build and Deploy Logs](' + workflowUrl + ')',
247
+ '',
248
+ getConsoleLink(prNumber)
249
+ ].join('\n');
250
+
251
+ const failureMessage = [
252
+ '❌ Deployment failed for PR #' + prNumber + ', commit ' + process.env.COMMIT_HASH,
253
+ '',
254
+ ' [View Deployment Logs with Errors](' + workflowUrl + ')',
255
+ '',
256
+ getConsoleLink(prNumber)
257
+ ].join('\n');
258
+
259
+ // Update the existing comment
260
+ await github.rest.issues.updateComment({
261
+ owner: context.repo.owner,
262
+ repo: context.repo.repo,
263
+ comment_id: process.env.COMMENT_ID,
264
+ body: isSuccess ? successMessage : failureMessage
265
+ });
0 commit comments