@@ -26,66 +26,223 @@ 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 : 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+ });
0 commit comments