7
7
inputs :
8
8
pr_number :
9
9
description : " PR number"
10
+ type : number
10
11
required : true
11
12
skip_build :
12
13
description : " Skip build job"
13
14
required : false
15
+ type : boolean
14
16
default : false
17
+ experiments :
18
+ description : " Experiments to enable"
19
+ required : false
20
+ type : string
21
+ default : " *"
15
22
16
23
env :
17
24
REPO : ghcr.io/coder/coder-preview
@@ -22,8 +29,8 @@ permissions:
22
29
pull-requests : write
23
30
24
31
concurrency :
25
- group : ${{ github.workflow }}-${{ github.event.issue.number || github.run_id }}
26
- cancel-in-progress : false
32
+ group : ${{ github.workflow }}-${{ github.repository }}-${{ github.ref }}
33
+ cancel-in-progress : true
27
34
28
35
jobs :
29
36
pr_commented :
@@ -136,7 +143,7 @@ jobs:
136
143
PR_TITLE : ${{ needs.pr_commented.outputs.PR_TITLE }}
137
144
PR_URL : ${{ needs.pr_commented.outputs.PR_URL }}
138
145
PR_BRANCH : ${{ needs.pr_commented.outputs.PR_BRANCH }}
139
- PR_DEPLOYMENT_ACCESS_URL : " https:// pr${{ needs.pr_commented.outputs.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}"
146
+ PR_DEPLOYMENT_ACCESS_URL : " pr${{ needs.pr_commented.outputs.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}"
140
147
steps :
141
148
- name : Check if image exists
142
149
run : |
@@ -145,10 +152,19 @@ jobs:
145
152
if [ -z "$foundTag" ]; then
146
153
echo "Image not found"
147
154
echo "${{ env.CODER_IMAGE_TAG }} not found in ghcr.io/coder/coder-preview"
148
- echo "Please remove --skip-build from the comment or ./scripts/deploy-pr.sh "
155
+ echo "Please remove --skip-build from the comment and try again "
149
156
exit 1
150
157
fi
151
158
159
+ - name : Add DNS record to Cloudflare
160
+ 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
+ )
167
+
152
168
- name : Checkout
153
169
uses : actions/checkout@v3
154
170
with :
@@ -168,35 +184,36 @@ jobs:
168
184
kubectl delete namespace "pr${{ env.PR_NUMBER }}" || true
169
185
kubectl create namespace "pr${{ env.PR_NUMBER }}"
170
186
171
- - name : Setup ingress
187
+ - name : Check and Create Certificate
172
188
run : |
173
- cat <<EOF > ingress.yaml
174
- apiVersion: networking.k8s.io/v1
175
- kind: Ingress
176
- metadata:
177
- name: pr${{ env.PR_NUMBER }}
178
- namespace: pr${{ env.PR_NUMBER }}
179
- annotations:
180
- cert-manager.io/cluster-issuer: letsencrypt
181
- spec:
182
- tls:
183
- - hosts:
184
- - "${{ secrets.PR_DEPLOYMENTS_DOMAIN }}"
185
- - "*.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}"
189
+ # Using kubectl to check if a Certificate resource already exists
190
+ # we are doing this to avoid letsenrypt rate limits
191
+ if ! kubectl get certificate pr${{ env.PR_NUMBER }}-tls -n pr-deployment-certs > /dev/null 2>&1; then
192
+ echo "Certificate doesn't exist. Creating a new one."
193
+ cat <<EOF | kubectl apply -f -
194
+ apiVersion: cert-manager.io/v1
195
+ kind: Certificate
196
+ metadata:
197
+ name: pr${{ env.PR_NUMBER }}-tls
198
+ namespace: pr-deployment-certs
199
+ spec:
186
200
secretName: pr${{ env.PR_NUMBER }}-tls
187
- rules:
188
- - host: "pr${{ env.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}"
189
- http:
190
- paths:
191
- - pathType: Prefix
192
- path: "/"
193
- backend:
194
- service:
195
- name: coder
196
- port:
197
- number: 80
201
+ issuerRef:
202
+ name: letsencrypt
203
+ kind: ClusterIssuer
204
+ dnsNames:
205
+ - "${{ env.PR_DEPLOYMENT_ACCESS_URL }}"
206
+ - "*.${{ env.PR_DEPLOYMENT_ACCESS_URL }}"
198
207
EOF
199
- kubectl apply -f ingress.yaml
208
+ else
209
+ echo "Certificate exists. Skipping certificate creation."
210
+ echo "Copy certificate from pr-deployment-certs to pr${{ env.PR_NUMBER }} namespace"
211
+ (
212
+ kubectl get secret pr${{ env.PR_NUMBER }}-tls -n pr-deployment-certs -o json |
213
+ jq 'del(.metadata.namespace,.metadata.creationTimestamp,.metadata.resourceVersion,.metadata.selfLink,.metadata.uid,.metadata.managedFields)' |
214
+ kubectl -n pr${{ env.PR_NUMBER }} apply -f -
215
+ )
216
+ fi
200
217
201
218
- name : Set up PostgreSQL database
202
219
run : |
@@ -210,6 +227,17 @@ jobs:
210
227
kubectl create secret generic coder-db-url -n pr${{ env.PR_NUMBER }} \
211
228
--from-literal=url="postgres://coder:coder@coder-db-postgresql.pr${{ env.PR_NUMBER }}.svc.cluster.local:5432/coder?sslmode=disable"
212
229
230
+ - name : Get experiments
231
+ id : get_experiments
232
+ run : |
233
+ set -euxo pipefail
234
+ if [[ ${{ github.event_name }} == "workflow_dispatch" ]]; then
235
+ experiments=${{ github.event.inputs.experiments }}
236
+ else
237
+ experiments=$(echo "${{ github.event.comment.body }}" | grep -oP '(?<=--experiments )[^ ]+')
238
+ fi
239
+ echo "experiments=$experiments" >> $GITHUB_OUTPUT
240
+
213
241
- name : Create values.yaml
214
242
run : |
215
243
cat <<EOF > pr-deploy-values.yaml
@@ -220,13 +248,22 @@ jobs:
220
248
pullPolicy: Always
221
249
service:
222
250
type: ClusterIP
251
+ ingress:
252
+ enable: true
253
+ className: traefik
254
+ host: ${{ env.PR_DEPLOYMENT_ACCESS_URL }}
255
+ wildcardHost: "*.${{ env.PR_DEPLOYMENT_ACCESS_URL }}"
256
+ tls:
257
+ enable: true
258
+ secretName: pr${{ env.PR_NUMBER }}-tls
259
+ wildcardSecretName: pr${{ env.PR_NUMBER }}-tls
223
260
env:
224
261
- name: "CODER_ACCESS_URL"
225
- value: "https://pr ${{ env.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}"
262
+ value: "https://${{ env.PR_DEPLOYMENT_ACCESS_URL }}"
226
263
- name: "CODER_WILDCARD_ACCESS_URL"
227
- value: "*--pr ${{ env.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}"
264
+ value: "*. ${{ env.PR_DEPLOYMENT_ACCESS_URL }}"
228
265
- name: "CODER_EXPERIMENTS"
229
- value: "*"
266
+ value: "*,${{ steps.get_experiments.outputs.experiments }} "
230
267
- name: CODER_PG_CONNECTION_URL
231
268
valueFrom:
232
269
secretKeyRef:
@@ -261,7 +298,7 @@ jobs:
261
298
set -euxo pipefail
262
299
263
300
DEST="${HOME}/coder"
264
- URL="${{ env.PR_DEPLOYMENT_ACCESS_URL }}/bin/coder-linux-amd64"
301
+ URL="https:// ${{ env.PR_DEPLOYMENT_ACCESS_URL }}/bin/coder-linux-amd64"
265
302
266
303
mkdir -p "$(dirname ${DEST})"
267
304
@@ -279,6 +316,7 @@ jobs:
279
316
curl -fsSL "$URL" -o "${DEST}"
280
317
chmod +x "${DEST}"
281
318
"${DEST}" version
319
+ mv "${DEST}" /usr/local/bin/coder
282
320
283
321
- name : Create first user, template and workspace
284
322
id : setup_deployment
@@ -294,16 +332,16 @@ jobs:
294
332
echo "::add-mask::$password"
295
333
echo "password=$password" >> $GITHUB_OUTPUT
296
334
297
- /home/runner/ coder login \
298
- --first-user-username pr${{ env.PR_NUMBER }} \
299
- --first-user-email ${{ env.PR_NUMBER }}@coder.com \
335
+ coder login \
336
+ --first-user-username test \
337
+ --first-user-email pr ${{ env.PR_NUMBER }}@coder.com \
300
338
--first-user-password $password \
301
339
--first-user-trial \
302
340
--use-token-as-session \
303
- ${{ env.PR_DEPLOYMENT_ACCESS_URL }}
341
+ https:// ${{ env.PR_DEPLOYMENT_ACCESS_URL }}
304
342
305
343
# Create template
306
- /home/runner/ coder templates init --id kubernetes && cd ./kubernetes/ && /home/runner/ coder templates create -y --variable namespace=pr${{ env.PR_NUMBER }}
344
+ coder templates init --id kubernetes && cd ./kubernetes/ && coder templates create -y --variable namespace=pr${{ env.PR_NUMBER }}
307
345
308
346
# Create workspace
309
347
cat <<EOF > workspace.yaml
@@ -312,8 +350,8 @@ jobs:
312
350
home_disk_size: "2"
313
351
EOF
314
352
315
- /home/runner/ coder create --template="kubernetes" pr${{ env.PR_NUMBER }} --rich-parameter-file ./workspace.yaml -y
316
- /home/runner/ coder stop pr${{ env.PR_NUMBER }} -y
353
+ coder create --template="kubernetes" test --rich-parameter-file ./workspace.yaml -y
354
+ coder stop test -y
317
355
318
356
- name : Send Slack notification
319
357
run : |
@@ -323,9 +361,9 @@ jobs:
323
361
"pr_number": "'"${{ env.PR_NUMBER }}"'",
324
362
"pr_url": "'"${{ env.PR_URL }}"'",
325
363
"pr_title": "'"${{ env.PR_TITLE }}"'",
326
- "pr_access_url": "'"${{ env.PR_DEPLOYMENT_ACCESS_URL }}"'",
327
- "pr_username": "'"pr${{ env.PR_NUMBER }} "'",
328
- "pr_email": "'"${{ env.PR_NUMBER }}@coder.com"'",
364
+ "pr_access_url": "'"https:// ${{ env.PR_DEPLOYMENT_ACCESS_URL }}"'",
365
+ "pr_username": "'"test "'",
366
+ "pr_email": "'"pr ${{ env.PR_NUMBER }}@coder.com"'",
329
367
"pr_password": "'"${{ steps.setup_deployment.outputs.password }}"'",
330
368
"pr_actor": "'"${{ github.actor }}"'"
331
369
}' \
@@ -351,6 +389,6 @@ jobs:
351
389
comment-id : ${{ steps.fc.outputs.comment-id }}
352
390
body : |
353
391
:heavy_check_mark: Deployed PR ${{ env.PR_NUMBER }} successfully.
354
- :rocket: Access the deployment link [here](${{ env.PR_DEPLOYMENT_ACCESS_URL }}).
392
+ :rocket: Access the deployment link [here](https:// ${{ env.PR_DEPLOYMENT_ACCESS_URL }}).
355
393
:warning: This deployment will be deleted when the PR is closed.
356
394
reactions : rocket
0 commit comments