1
- # This action will trigger when a PR is commented on with `/deploy-pr` or when the workflow is manually triggered.
1
+ # This action will trigger when
2
+ # 1. A PR is commented on with `/deploy-pr`
3
+ # 2. when the workflow is manually triggered
4
+ # 3. ./scripts/deploy_pr.sh is run locally
5
+ # 4. when a PR is updated
2
6
name : Deploy PR
3
7
on :
4
8
issue_comment :
5
- types : [created, edited]
9
+ types : created
10
+ pull_request :
11
+ types : synchronize
6
12
workflow_dispatch :
7
13
inputs :
8
14
pr_number :
@@ -33,7 +39,7 @@ concurrency:
33
39
cancel-in-progress : true
34
40
35
41
jobs :
36
- pr_commented :
42
+ get_info :
37
43
if : (github.event_name == 'issue_comment' && contains(github.event.comment.body, '/deploy-pr') && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'COLLABORATOR' || github.event.comment.author_association == 'OWNER')) || github.event_name == 'workflow_dispatch'
38
44
outputs :
39
45
PR_NUMBER : ${{ steps.pr_info.outputs.PR_NUMBER }}
@@ -42,18 +48,15 @@ jobs:
42
48
PR_BRANCH : ${{ steps.pr_info.outputs.PR_BRANCH }}
43
49
CODER_BASE_IMAGE_TAG : ${{ steps.set_tags.outputs.CODER_BASE_IMAGE_TAG }}
44
50
CODER_IMAGE_TAG : ${{ steps.set_tags.outputs.CODER_IMAGE_TAG }}
51
+ NEW : ${{ steps.check_deployment.outputs.new }}
45
52
46
53
runs-on : " ubuntu-latest"
47
54
steps :
48
55
- name : Get PR number, title, and branch name
49
56
id : pr_info
50
57
run : |
51
58
set -euxo pipefail
52
- if [[ ${{ github.event_name }} == "workflow_dispatch" ]]; then
53
- PR_NUMBER=${{ github.event.inputs.pr_number }}
54
- else
55
- PR_NUMBER=${{ github.event.issue.number }}
56
- fi
59
+ PR_NUMBER=${{ github.event.inputs.pr_number || github.event.pull_request.number || github.event.issue.number }}
57
60
PR_TITLE=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" https://api.github.com/repos/coder/coder/pulls/$PR_NUMBER | jq -r '.title')
58
61
PR_BRANCH=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" https://api.github.com/repos/coder/coder/pulls/$PR_NUMBER | jq -r '.head.ref')
59
62
echo "PR_URL=https://github.com/coder/coder/pull/$PR_NUMBER" >> $GITHUB_OUTPUT
@@ -71,28 +74,52 @@ jobs:
71
74
CODER_BASE_IMAGE_TAG : ghcr.io/coder/coder-preview-base:pr${{ steps.pr_info.outputs.PR_NUMBER }}
72
75
CODER_IMAGE_TAG : ghcr.io/coder/coder-preview:pr${{ steps.pr_info.outputs.PR_NUMBER }}
73
76
77
+ - name : Set up kubeconfig
78
+ run : |
79
+ set -euxo pipefail
80
+ mkdir -p ~/.kube
81
+ echo "${{ secrets.PR_DEPLOYMENTS_KUBECONFIG }}" > ~/.kube/config
82
+ export KUBECONFIG=~/.kube/config
83
+
84
+ - name : Check if the helm deployment already exists
85
+ id : check_deployment
86
+ run : |
87
+ set -euxo pipefail
88
+ if helm status "pr${{ steps.pr_info.outputs.PR_NUMBER }}" --namespace "pr${{ steps.pr_info.outputs.PR_NUMBER }}" > /dev/null 2>&1; then
89
+ echo "Deployment already exists. Skipping deployment."
90
+ new=false
91
+ else
92
+ echo "Deployment doesn't exist. Creating a new one."
93
+ new=true
94
+ fi
95
+ echo "new=$new" >> $GITHUB_OUTPUT
96
+
74
97
- name : Comment on PR
75
98
id : comment_id
76
99
if : github.event_name == 'issue_comment'
77
100
uses : peter-evans/create-or-update-comment@v3
78
101
with :
79
102
issue-number : ${{ steps.pr_info.outputs.PR_NUMBER }}
80
103
body : |
104
+ ---
81
105
:rocket: Deploying PR ${{ steps.pr_info.outputs.PR_NUMBER }} ...
82
- :warning: This deployment will be deleted when the PR is closed.
83
- reactions : " +1"
106
+ ---
84
107
85
108
build :
86
- needs : pr_commented
109
+ needs : get_info
87
110
# Skips the build job if the workflow was triggered by a workflow_dispatch event and the skip_build input is set to true
88
111
# or if the workflow was triggered by an issue_comment event and the comment body contains --skip-build
89
- if : (github.event_name == 'workflow_dispatch' && github.event.inputs.skip_build == 'false') || (github.event_name == 'issue_comment' && contains(github.event.comment.body, '--skip-build') != true)
112
+ # alwyas run the build job if the workflow was triggered by a pull_request event
113
+ if : |
114
+ (github.event_name == 'workflow_dispatch' && github.event.inputs.skip_build == 'false') ||
115
+ (github.event_name == 'issue_comment' && contains(github.event.comment.body, '--skip-build') == false) ||
116
+ (github.event_name == 'pull_request' && needs.get_info.outputs.NEW == 'false')
90
117
runs-on : ${{ github.repository_owner == 'coder' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }}
91
118
env :
92
119
DOCKER_CLI_EXPERIMENTAL : " enabled"
93
- CODER_IMAGE_TAG : ${{ needs.pr_commented .outputs.CODER_IMAGE_TAG }}
94
- PR_NUMBER : ${{ needs.pr_commented .outputs.PR_NUMBER }}
95
- PR_BRANCH : ${{ needs.pr_commented .outputs.PR_BRANCH }}
120
+ CODER_IMAGE_TAG : ${{ needs.get_info .outputs.CODER_IMAGE_TAG }}
121
+ PR_NUMBER : ${{ needs.get_info .outputs.PR_NUMBER }}
122
+ PR_BRANCH : ${{ needs.get_info .outputs.PR_BRANCH }}
96
123
steps :
97
124
- name : Checkout
98
125
uses : actions/checkout@v3
@@ -133,19 +160,27 @@ jobs:
133
160
build/coder_linux_amd64
134
161
135
162
deploy :
136
- needs : [build, pr_commented ]
163
+ needs : [build, get_info ]
137
164
# Run deploy job only if build job was successful or skipped
138
- if : always() && (needs.build.result == 'success' || needs.build.result == 'skipped') && needs.pr_commented .result == 'success'
165
+ if : always() && (needs.build.result == 'success' || needs.build.result == 'skipped') && needs.get_info .result == 'success'
139
166
runs-on : " ubuntu-latest"
140
167
env :
141
- CODER_IMAGE_TAG : ${{ needs.pr_commented .outputs.CODER_IMAGE_TAG }}
142
- PR_NUMBER : ${{ needs.pr_commented .outputs.PR_NUMBER }}
143
- PR_TITLE : ${{ needs.pr_commented .outputs.PR_TITLE }}
144
- PR_URL : ${{ needs.pr_commented .outputs.PR_URL }}
145
- PR_BRANCH : ${{ needs.pr_commented .outputs.PR_BRANCH }}
146
- PR_DEPLOYMENT_ACCESS_URL : " pr${{ needs.pr_commented .outputs.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}"
168
+ CODER_IMAGE_TAG : ${{ needs.get_info .outputs.CODER_IMAGE_TAG }}
169
+ PR_NUMBER : ${{ needs.get_info .outputs.PR_NUMBER }}
170
+ PR_TITLE : ${{ needs.get_info .outputs.PR_TITLE }}
171
+ PR_URL : ${{ needs.get_info .outputs.PR_URL }}
172
+ PR_BRANCH : ${{ needs.get_info .outputs.PR_BRANCH }}
173
+ PR_DEPLOYMENT_ACCESS_URL : " pr${{ needs.get_info .outputs.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}"
147
174
steps :
175
+ - name : Set up kubeconfig
176
+ run : |
177
+ set -euxo pipefail
178
+ mkdir -p ~/.kube
179
+ echo "${{ secrets.PR_DEPLOYMENTS_KUBECONFIG }}" > ~/.kube/config
180
+ export KUBECONFIG=~/.kube/config
181
+
148
182
- name : Check if image exists
183
+ if : needs.get_info.outputs.NEW == 'true'
149
184
run : |
150
185
set -euxo pipefail
151
186
foundTag=$(curl -fsSL https://github.com/coder/coder/pkgs/container/coder-preview | grep -o ${{ env.CODER_IMAGE_TAG }} | head -n 1)
@@ -157,34 +192,28 @@ jobs:
157
192
fi
158
193
159
194
- name : Add DNS record to Cloudflare
195
+ if : needs.get_info.outputs.NEW == 'true'
160
196
run : |
161
- (
162
- curl -X POST "https://api.cloudflare.com/client/v4/zones/${{ secrets.PR_DEPLOYMENTS_ZONE_ID }}/dns_records" \
163
- -H "Authorization: Bearer ${{ secrets.PR_DEPLOYMENTS_CLOUDFLARE_API_TOKEN }}" \
164
- -H "Content-Type:application/json" \
165
- --data '{"type":"CNAME","name":"*.${{ env.PR_DEPLOYMENT_ACCESS_URL }}","content":"${{ env.PR_DEPLOYMENT_ACCESS_URL }}","ttl":1,"proxied":false}'
166
- )
197
+ curl -X POST "https://api.cloudflare.com/client/v4/zones/${{ secrets.PR_DEPLOYMENTS_ZONE_ID }}/dns_records" \
198
+ -H "Authorization: Bearer ${{ secrets.PR_DEPLOYMENTS_CLOUDFLARE_API_TOKEN }}" \
199
+ -H "Content-Type:application/json" \
200
+ --data '{"type":"CNAME","name":"*.${{ env.PR_DEPLOYMENT_ACCESS_URL }}","content":"${{ env.PR_DEPLOYMENT_ACCESS_URL }}","ttl":1,"proxied":false}'
167
201
168
202
- name : Checkout
169
203
uses : actions/checkout@v3
170
204
with :
171
205
ref : ${{ env.PR_BRANCH }}
172
206
173
- - name : Set up kubeconfig
174
- run : |
175
- set -euxo pipefail
176
- mkdir -p ~/.kube
177
- echo "${{ secrets.PR_DEPLOYMENTS_KUBECONFIG }}" > ~/.kube/config
178
- export KUBECONFIG=~/.kube/config
179
-
180
207
- name : Create PR namespace
208
+ if : needs.get_info.outputs.NEW == 'true'
181
209
run : |
182
210
set -euxo pipefail
183
211
# try to delete the namespace, but don't fail if it doesn't exist
184
212
kubectl delete namespace "pr${{ env.PR_NUMBER }}" || true
185
213
kubectl create namespace "pr${{ env.PR_NUMBER }}"
186
214
187
215
- name : Check and Create Certificate
216
+ if : needs.get_info.outputs.NEW == 'true'
188
217
run : |
189
218
# Using kubectl to check if a Certificate resource already exists
190
219
# we are doing this to avoid letsenrypt rate limits
@@ -216,6 +245,7 @@ jobs:
216
245
)
217
246
218
247
- name : Set up PostgreSQL database
248
+ if : needs.get_info.outputs.NEW == 'true'
219
249
run : |
220
250
helm repo add bitnami https://charts.bitnami.com/bitnami
221
251
helm install coder-db bitnami/postgresql \
@@ -228,6 +258,7 @@ jobs:
228
258
--from-literal=url="postgres://coder:coder@coder-db-postgresql.pr${{ env.PR_NUMBER }}.svc.cluster.local:5432/coder?sslmode=disable"
229
259
230
260
- name : Get experiments
261
+ if : needs.get_info.outputs.NEW == 'true'
231
262
id : get_experiments
232
263
run : |
233
264
set -euxo pipefail
@@ -249,6 +280,7 @@ jobs:
249
280
COMMENT_BODY : ${{ github.event.comment.body || '' }}
250
281
251
282
- name : Create values.yaml
283
+ if : needs.get_info.outputs.NEW == 'true'
252
284
run : |
253
285
cat <<EOF > pr-deploy-values.yaml
254
286
coder:
@@ -291,19 +323,30 @@ jobs:
291
323
292
324
- name : Install Helm chart
293
325
run : |
294
- helm upgrade --install "pr${{ env.PR_NUMBER }}" ./helm \
295
- --namespace "pr${{ env.PR_NUMBER }}" \
296
- --values ./pr-deploy-values.yaml \
297
- --force
326
+ set -euxo pipefail
327
+ # if the deployment already exists, we need to upgrade it by resuing the existing values
328
+ if [[ ${{ needs.get_info.outputs.NEW }} == "false" ]]; then
329
+ helm upgrade --install "pr${{ env.PR_NUMBER }}" ./helm \
330
+ --namespace "pr${{ env.PR_NUMBER }}" \
331
+ --reuse-values \
332
+ --force
333
+ else
334
+ helm upgrade --install "pr${{ env.PR_NUMBER }}" ./helm \
335
+ --namespace "pr${{ env.PR_NUMBER }}" \
336
+ --values ./pr-deploy-values.yaml \
337
+ --force
338
+ fi
298
339
299
340
- name : Install coder-logstream-kube
341
+ if : needs.get_info.outputs.NEW == 'true'
300
342
run : |
301
343
helm repo add coder-logstream-kube https://helm.coder.com/logstream-kube
302
344
helm upgrade --install coder-logstream-kube coder-logstream-kube/coder-logstream-kube \
303
345
--namespace "pr${{ env.PR_NUMBER }}" \
304
346
--set url="https://pr${{ env.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}"
305
347
306
348
- name : Get Coder binary
349
+ if : needs.get_info.outputs.NEW == 'true'
307
350
run : |
308
351
set -euxo pipefail
309
352
@@ -329,6 +372,7 @@ jobs:
329
372
mv "${DEST}" /usr/local/bin/coder
330
373
331
374
- name : Create first user, template and workspace
375
+ if : needs.get_info.outputs.NEW == 'true'
332
376
id : setup_deployment
333
377
run : |
334
378
set -euxo pipefail
@@ -364,6 +408,7 @@ jobs:
364
408
coder stop test -y
365
409
366
410
- name : Send Slack notification
411
+ if : needs.get_info.outputs.NEW == 'true'
367
412
run : |
368
413
curl -s -o /dev/null -X POST -H 'Content-type: application/json' \
369
414
-d \
@@ -387,18 +432,21 @@ jobs:
387
432
with :
388
433
issue-number : ${{ env.PR_NUMBER }}
389
434
comment-author : " github-actions[bot]"
390
- body-includes : This deployment will be deleted when the PR is closed
435
+ body-includes : ---
391
436
direction : last
392
437
393
438
- name : Comment on PR
394
439
uses : peter-evans/create-or-update-comment@v3
395
440
if : github.event_name == 'issue_comment'
441
+ env :
442
+ STATUS : ${{ needs.get_info.outputs.NEW == 'true' && 'Created' || 'Updated' }}
396
443
with :
397
444
issue-number : ${{ env.PR_NUMBER }}
398
445
edit-mode : replace
399
446
comment-id : ${{ steps.fc.outputs.comment-id }}
400
447
body : |
401
- :heavy_check_mark: Deployed PR ${{ env.PR_NUMBER }} successfully.
402
- :rocket: Access the deployment link [here](https://${{ env.PR_DEPLOYMENT_ACCESS_URL }}).
403
- :warning: This deployment will be deleted when the PR is closed.
448
+ ---
449
+ :heavy_check_mark: PR ${{ env.PR_NUMBER }} ${{ env.STATUS }} successfully.
450
+ :rocket: Access the credentials [here]( {{ secrets.PR_DEPLOYMENTS_SLACK_CHANNEL_URL }} ).
451
+ ---
404
452
reactions : rocket
0 commit comments