11name : Deploy Review App to Control Plane
22
3- run-name : ${{ github.event_name == 'issue_comment' && 'Deploying Review App' || format('Updating Review App for {0}', github.ref_name) }}
3+ run-name : Deploy Review App - ${{ github.ref_name }}
44
55on :
66 pull_request :
77 types : [opened, synchronize, reopened]
8+ push :
9+ branches :
10+ - ' **' # Any branch
11+ - ' !main' # Except main
12+ - ' !master' # Except master
813 issue_comment :
914 types : [created]
15+ workflow_dispatch :
16+ inputs :
17+ pr_number :
18+ description : ' Pull Request number to deploy'
19+ required : true
20+ type : number
1021
11- # Use concurrency to cancel in-progress runs
1222concurrency :
13- group : deploy-pr-${{ github.event.pull_request.number || github.event.issue.number }}
23+ group : deploy-pr-${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }}
1424 cancel-in-progress : true
1525
1626env :
17- APP_NAME : qa-react-webpack-rails-tutorial-pr-${{ github.event.pull_request.number || github.event.issue.number }}
27+ APP_NAME : qa-react-webpack-rails-tutorial-pr-${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }}
1828 CPLN_TOKEN : ${{ secrets.CPLN_TOKEN_STAGING }}
1929 CPLN_ORG : ${{ vars.CPLN_ORG_STAGING }}
20- PR_NUMBER : ${{ github.event.pull_request.number || github.event.issue.number }}
30+ PR_NUMBER : ${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }}
2131
2232jobs :
2333 Process-Deployment-Command :
2434 if : |
2535 (github.event_name == 'pull_request') ||
36+ (github.event_name == 'push') ||
37+ (github.event_name == 'workflow_dispatch') ||
2638 (github.event_name == 'issue_comment' &&
2739 github.event.issue.pull_request &&
2840 github.event.comment.body == '/deploy-review-app')
@@ -34,252 +46,211 @@ jobs:
3446 issues : write
3547
3648 steps :
37- - uses : actions/checkout@v4
49+ # Initial checkout only for pull_request and push events
50+ - name : Checkout code
51+ if : github.event_name == 'pull_request' || github.event_name == 'push'
52+ uses : actions/checkout@v4
3853 with :
3954 fetch-depth : 0
40- ref : ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || steps.getRef.outputs.PR_REF || github.ref }}
55+ ref : ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
4156
42- - name : Setup Environment
43- uses : ./.github/actions/setup-environment
57+ # Basic checkout for other events (workflow_dispatch, issue_comment)
58+ # We'll do proper checkout after getting PR info
59+ - name : Initial checkout
60+ if : github.event_name == 'workflow_dispatch' || github.event_name == 'issue_comment'
61+ uses : actions/checkout@v4
4462 with :
45- token : ${{ env.CPLN_TOKEN }}
46- org : ${{ env.CPLN_ORG }}
63+ fetch-depth : 0
4764
4865 - name : Get PR HEAD Ref
49- if : github.event_name == 'issue_comment'
50- run : |
51- echo "PR_NUMBER=${{ github.event.issue.number }}" >> $GITHUB_ENV
52- echo "APP_NAME=qa-react-webpack-rails-tutorial-pr-${{ github.event.issue.number }}" >> $GITHUB_ENV
53- # For PR comments, get the actual PR head commit
54- PR_DATA=$(gh pr view $PR_NUMBER --repo ${{ github.repository }} --json headRefName,headRefOid)
55- echo "PR_REF=$(echo "$PR_DATA" | jq -r '.headRefName')" >> $GITHUB_OUTPUT
56- echo "PR_SHA=$(echo "$PR_DATA" | jq -r '.headRefOid')" >> $GITHUB_OUTPUT
66+ id : getRef
5767 env :
58- GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
68+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
69+ run : |
70+ if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
71+ PR_NUMBER="${{ github.event.inputs.pr }}"
72+ elif [[ "${{ github.event_name }}" == "issue_comment" ]]; then
73+ PR_NUMBER="${{ github.event.issue.number }}"
74+ elif [[ "${{ github.event_name }}" == "pull_request" ]]; then
75+ PR_NUMBER="${{ github.event.pull_request.number }}"
76+ elif [[ "${{ github.event_name }}" == "push" ]]; then
77+ # For push events, find associated PR
78+ PR_DATA=$(gh pr list --head "${{ github.ref_name }}" --json number --jq '.[0].number')
79+ if [[ -n "$PR_DATA" ]]; then
80+ PR_NUMBER="$PR_DATA"
81+ else
82+ echo "Error: No PR found for branch ${{ github.ref_name }}"
83+ exit 1
84+ fi
85+ fi
86+
87+ if [[ -z "$PR_NUMBER" ]]; then
88+ echo "Error: Could not determine PR number"
89+ exit 1
90+ fi
91+
92+ # Set environment variables
93+ echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV
94+ echo "APP_NAME=qa-react-webpack-rails-tutorial-pr-$PR_NUMBER" >> $GITHUB_ENV
95+
96+ # Get PR data using GitHub CLI
97+ PR_DATA=$(gh pr view $PR_NUMBER --repo shakacode/react-webpack-rails-tutorial --json headRefName,headRefOid)
98+ if [[ $? -eq 0 ]]; then
99+ echo "PR_REF=$(echo $PR_DATA | jq -r .headRefName)" >> $GITHUB_OUTPUT
100+ echo "PR_SHA=$(echo $PR_DATA | jq -r .headRefOid)" >> $GITHUB_ENV
101+ else
102+ echo "Error: Could not fetch PR data for PR #$PR_NUMBER"
103+ exit 1
104+ fi
105+
106+ - name : Checkout PR code
107+ if : github.event_name == 'workflow_dispatch' || github.event_name == 'issue_comment'
108+ uses : actions/checkout@v4
109+ with :
110+ fetch-depth : 0
111+ ref : ${{ steps.getRef.outputs.PR_SHA }}
112+
113+ - name : Setup Environment
114+ uses : ./.github/actions/setup-environment
115+ with :
116+ token : ${{ secrets.CPLN_TOKEN_STAGING }}
117+ org : ${{ vars.CPLN_ORG_STAGING }}
59118
60119 - name : Check if Review App Exists
61120 id : check-app
62- if : github.event_name == 'push '
121+ if : github.event_name == 'pull_request '
63122 env :
64- CPLN_TOKEN : ${{ secrets.CPLN_TOKEN }}
123+ CPLN_TOKEN : ${{ secrets.CPLN_TOKEN_STAGING }}
65124 run : |
125+ # First check if cpflow exists
126+ if ! command -v cpflow &> /dev/null; then
127+ echo "Error: cpflow command not found"
128+ exit 1
129+ fi
130+
131+ # Then check if app exists
66132 if ! cpflow exists -a ${{ env.APP_NAME }}; then
67133 echo "No review app exists for this PR"
68- exit 0
134+ echo "DO_DEPLOY=false" >> $GITHUB_ENV
135+ else
136+ echo "DO_DEPLOY=true" >> $GITHUB_ENV
69137 fi
70- echo "app_exists=true" >> $GITHUB_OUTPUT
71138
72-
73- - name : Set Workflow URL
74- id : workflow-url
75- uses : actions/github-script@v7
76- with :
77- script : |
78- async function getWorkflowUrl(runId) {
79- const jobs = await github.rest.actions.listJobsForWorkflowRun({
80- owner: context.repo.owner,
81- repo: context.repo.repo,
82- run_id: runId
83- });
84-
85- const currentJob = jobs.data.jobs.find(job => job.status === 'in_progress');
86- const jobId = currentJob?.id;
87-
88- if (!jobId) {
89- return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`;
90- }
91-
92- return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}/job/${jobId}`;
93- }
94-
95- const workflowUrl = await getWorkflowUrl(context.runId);
96- core.exportVariable('WORKFLOW_URL', workflowUrl);
97- core.exportVariable('GET_CONSOLE_LINK', `
98- function getConsoleLink(prNumber) {
99- return '🎮 [Control Plane Console](' +
100- 'https://console.cpln.io/console/org/' + process.env.CPLN_ORG + '/gvc/' + process.env.APP_NAME + '/-info)';
101- }
102- `);
139+ - name : Validate Deployment Request
140+ id : validate
141+ if : env.DO_DEPLOY != 'false'
142+ run : |
143+ if ! [[ "${{ github.event_name }}" == "workflow_dispatch" || \
144+ ("${{ github.event_name }}" == "issue_comment" && "${{ github.event.comment.body }}" == "/deploy-review-app") || \
145+ "${{ github.event_name }}" == "pull_request" ]]; then
146+ echo "Skipping deployment - not a valid trigger (event: ${{ github.event_name }})"
147+ exit 1
148+ fi
103149
104150 - name : Create Initial Comment
105- if : |
106- (github.event_name == 'issue_comment' &&
107- github.event.issue.pull_request &&
108- github.event.comment.body == '/deploy-review-app') ||
109- ( steps.check-app.outputs.app_exists == 'true')
110- id : create-comment
151+ if : env.DO_DEPLOY != 'false'
111152 uses : actions/github-script@v7
112153 with :
113154 script : |
114155 const result = await github.rest.issues.createComment({
115156 owner: context.repo.owner,
116157 repo: context.repo.repo,
117158 issue_number: process.env.PR_NUMBER,
118- body: '🚀 Starting deployment process...'
159+ body: '🚀 Starting deployment process...\n\n' + process.env.CONSOLE_LINK
119160 });
120- console.log('Created comment:', result.data.id);
121- return { commentId: result.data.id };
161+ core.setOutput('comment-id', result.data.id);
122162
123- - name : Set Comment ID
124- if : |
125- (github.event_name == 'issue_comment' &&
126- github.event.issue.pull_request &&
127- github.event.comment.body == '/deploy-review-app') ||
128- (steps.check-app.outputs.app_exists == 'true')
129- run : echo "COMMENT_ID=${{ fromJSON(steps.create-comment.outputs.result).commentId }}" >> $GITHUB_ENV
130-
131- - name : Initialize Deployment
132- id : init-deployment
163+ - name : Set Deployment URLs
164+ id : set-urls
165+ if : env.DO_DEPLOY != 'false'
133166 uses : actions/github-script@v7
134167 with :
135168 script : |
136- async function getWorkflowUrl(runId) {
137- const jobs = await github.rest.actions.listJobsForWorkflowRun({
169+ // Set workflow URL for logs
170+ const getWorkflowUrl = async (runId) => {
171+ const { data: run } = await github.rest.actions.getWorkflowRun({
138172 owner: context.repo.owner,
139173 repo: context.repo.repo,
140174 run_id: runId
141175 });
142-
143- const currentJob = jobs.data.jobs.find(job => job.status === 'in_progress');
144- const jobId = currentJob?.id;
145-
146- if (!jobId) {
147- console.log('Warning: Could not find current job ID');
148- return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`;
149- }
150-
151- return `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}/job/${jobId}`;
152- }
153-
154- // Create initial deployment comment
155- const comment = await github.rest.issues.createComment({
156- owner: context.repo.owner,
157- repo: context.repo.repo,
158- issue_number: process.env.PR_NUMBER,
159- body: '⏳ Initializing deployment...'
160- });
176+ return run.html_url;
177+ };
161178
162- // Create GitHub deployment
163- const deployment = await github.rest.repos.createDeployment({
164- owner: context.repo.owner,
165- repo: context.repo.repo,
166- ref: context.sha,
167- environment: 'review',
168- auto_merge: false,
169- required_contexts: []
170- });
171-
172179 const workflowUrl = await getWorkflowUrl(context.runId);
173-
174- return {
175- deploymentId: deployment.data.id,
176- commentId: comment.data.id,
177- workflowUrl
178- };
179-
180- - name : Set comment ID and workflow URL
181- run : |
182- echo "COMMENT_ID=${{ fromJSON(steps.init-deployment.outputs.result).commentId }}" >> $GITHUB_ENV
183- echo "WORKFLOW_URL=${{ fromJSON(steps.init-deployment.outputs.result).workflowUrl }}" >> $GITHUB_ENV
184-
185- - name : Set commit hash
186- run : |
187- FULL_COMMIT="${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || steps.getRef.outputs.PR_SHA || github.sha }}"
188- echo "COMMIT_HASH=${FULL_COMMIT:0:7}" >> $GITHUB_ENV
180+ core.exportVariable('WORKFLOW_URL', workflowUrl);
181+ core.exportVariable('CONSOLE_LINK',
182+ '🎮 [Control Plane Console](' +
183+ 'https://console.cpln.io/console/org/' + process.env.CPLN_ORG_STAGING + '/gvc/' + process.env.APP_NAME + '/-info)'
184+ );
189185
190186 - name : Update Status - Building
191- if : |
192- (github.event_name == 'issue_comment' &&
193- github.event.issue.pull_request &&
194- github.event.comment.body == '/deploy-review-app') ||
195- (steps.check-app.outputs.app_exists == 'true')
187+ if : env.DO_DEPLOY != 'false'
196188 uses : actions/github-script@v7
197189 with :
198190 script : |
199- eval(process.env.GET_CONSOLE_LINK);
200-
201191 const buildingMessage = [
202192 '🏗️ Building Docker image for PR #' + process.env.PR_NUMBER + ', commit ' + '${{ env.COMMIT_HASH }}',
203- '🏗️ Building Docker image...',
204193 '',
205194 '📝 [View Build Logs](' + process.env.WORKFLOW_URL + ')',
206195 '',
207- getConsoleLink( process.env.PR_NUMBER)
196+ process.env.CONSOLE_LINK
208197 ].join('\n');
209198
210199 await github.rest.issues.updateComment({
211200 owner: context.repo.owner,
212201 repo: context.repo.repo,
213- comment_id: process.env.COMMENT_ID ,
202+ comment_id: ${{ steps.create-comment.outputs.comment-id }} ,
214203 body: buildingMessage
215204 });
216205
217206 - name : Checkout PR Branch
218- if : |
219- (github.event_name == 'issue_comment' &&
220- github.event.issue.pull_request &&
221- github.event.comment.body == '/deploy-review-app') ||
222- (steps.check-app.outputs.app_exists == 'true')
207+ if : env.DO_DEPLOY != 'false'
223208 run : git checkout ${{ steps.getRef.outputs.PR_REF }}
224209
225210 - name : Build Docker Image
226- if : |
227- (github.event_name == 'issue_comment' &&
228- github.event.issue.pull_request &&
229- github.event.comment.body == '/deploy-review-app') ||
230- (steps.check-app.outputs.app_exists == 'true')
211+ if : env.DO_DEPLOY != 'false'
231212 uses : ./.github/actions/build-docker-image
232213 with :
233214 app_name : ${{ env.APP_NAME }}
234- org : ${{ env.CPLN_ORG }}
215+ org : ${{ env.CPLN_ORG_STAGING }}
235216 commit : ${{ env.COMMIT_HASH }}
236217 PR_NUMBER : ${{ env.PR_NUMBER }}
237218
238219 - name : Update Status - Deploying
239- if : |
240- (github.event_name == 'issue_comment' &&
241- github.event.issue.pull_request &&
242- github.event.comment.body == '/deploy-review-app') ||
243- (steps.check-app.outputs.app_exists == 'true')
220+ if : env.DO_DEPLOY != 'false'
244221 uses : actions/github-script@v7
245222 with :
246223 script : |
247- eval(process.env.GET_CONSOLE_LINK);
248-
249224 const deployingMessage = [
250225 '🚀 Deploying to Control Plane...',
251226 '',
252227 '⏳ Waiting for deployment to be ready...',
253228 '',
254229 '📝 [View Deploy Logs](' + process.env.WORKFLOW_URL + ')',
255230 '',
256- getConsoleLink( process.env.PR_NUMBER)
231+ process.env.CONSOLE_LINK
257232 ].join('\n');
258233
259234 await github.rest.issues.updateComment({
260235 owner: context.repo.owner,
261236 repo: context.repo.repo,
262- comment_id: process.env.COMMENT_ID ,
237+ comment_id: ${{ steps.create-comment.outputs.comment-id }} ,
263238 body: deployingMessage
264239 });
265240
266241 - name : Deploy to Control Plane
267- if : |
268- (github.event_name == 'issue_comment' &&
269- github.event.issue.pull_request &&
270- github.event.comment.body == '/deploy-review-app') ||
271- (steps.check-app.outputs.app_exists == 'true')
242+ if : env.DO_DEPLOY != 'false'
272243 uses : ./.github/actions/deploy-to-control-plane
273244 with :
274245 app_name : ${{ env.APP_NAME }}
275- org : ${{ env.CPLN_ORG }}
246+ org : ${{ env.CPLN_ORG_STAGING }}
276247 github_token : ${{ secrets.GITHUB_TOKEN }}
277248 wait_timeout : ${{ vars.WAIT_TIMEOUT || 900 }}
278- env :
279- CPLN_TOKEN : ${{ env.CPLN_TOKEN }}
280- PR_NUMBER : ${{ env.PR_NUMBER }}
249+ cpln_token : ${{ secrets.CPLN_TOKEN_STAGING }}
250+ pr_number : ${{ env.PR_NUMBER }}
281251
282252 - name : Update Status - Deployment Complete
253+ if : env.DO_DEPLOY != 'false'
283254 uses : actions/github-script@v7
284255 with :
285256 script : |
@@ -288,8 +259,7 @@ jobs:
288259 const workflowUrl = process.env.WORKFLOW_URL;
289260 const isSuccess = '${{ job.status }}' === 'success';
290261
291- const consoleLink = '🎮 [Control Plane Console](https://console.cpln.io/console/org/' +
292- process.env.CPLN_ORG + '/gvc/' + process.env.APP_NAME + '/-info)';
262+ const consoleLink = process.env.CONSOLE_LINK;
293263
294264 // Create GitHub deployment status
295265 const deploymentStatus = {
@@ -326,6 +296,6 @@ jobs:
326296 await github.rest.issues.updateComment({
327297 owner: context.repo.owner,
328298 repo: context.repo.repo,
329- comment_id: process.env.COMMENT_ID ,
299+ comment_id: ${{ steps.create-comment.outputs.comment-id }} ,
330300 body: isSuccess ? successMessage : failureMessage
331301 });
0 commit comments