@@ -26,74 +26,240 @@ outputs:
2626runs :
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 : 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
41137 shell : bash
42138 run : |
139+ echo "🔧 Checking if app exists..."
43140 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..."
45142 cpflow setup-app -a ${{ inputs.app_name }}
46143 fi
47144
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+
48206 - name : Deploy to Control Plane
49207 id : deploy
50208 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