Skip to content

Commit bc55ffd

Browse files
authored
ci: improve deploy-pr workflow (coder#8735)
1 parent 3282908 commit bc55ffd

File tree

2 files changed

+161
-33
lines changed

2 files changed

+161
-33
lines changed

.github/workflows/pr-deploy.yaml

+129-33
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ on:
88
pr_number:
99
description: "PR number"
1010
required: true
11+
skip_build:
12+
description: "Skip build job"
13+
required: false
14+
default: false
1115

1216
env:
1317
REPO: ghcr.io/coder/coder-preview
@@ -62,6 +66,7 @@ jobs:
6266

6367
- name: Comment on PR
6468
id: comment_id
69+
if: github.event_name == 'issue_comment'
6570
uses: peter-evans/create-or-update-comment@v3
6671
with:
6772
issue-number: ${{ steps.pr_info.outputs.PR_NUMBER }}
@@ -72,6 +77,9 @@ jobs:
7277

7378
build:
7479
needs: pr_commented
80+
# Skips the build job if the workflow was triggered by a workflow_dispatch event and the skip_build input is set to true
81+
# or if the workflow was triggered by an issue_comment event and the comment body contains --skip-build
82+
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)
7583
runs-on: ${{ github.repository_owner == 'coder' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }}
7684
env:
7785
DOCKER_CLI_EXPERIMENTAL: "enabled"
@@ -119,16 +127,32 @@ jobs:
119127
120128
deploy:
121129
needs: [build, pr_commented]
122-
if: needs.build.result == 'success'
130+
# Run deploy job only if build job was successful or skipped
131+
if: always() && (needs.build.result == 'success' || needs.build.result == 'skipped')
123132
runs-on: "ubuntu-latest"
124133
env:
125134
CODER_IMAGE_TAG: ${{ needs.pr_commented.outputs.CODER_IMAGE_TAG }}
126135
PR_NUMBER: ${{ needs.pr_commented.outputs.PR_NUMBER }}
127136
PR_TITLE: ${{ needs.pr_commented.outputs.PR_TITLE }}
128137
PR_URL: ${{ needs.pr_commented.outputs.PR_URL }}
138+
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 }}"
129140
steps:
141+
- name: Check if image exists
142+
run: |
143+
set -euxo pipefail
144+
foundTag=$(curl -fsSL https://github.com/coder/coder/pkgs/container/coder-preview | grep -o ${{ env.CODER_IMAGE_TAG }} | head -n 1)
145+
if [ -z "$foundTag" ]; then
146+
echo "Image not found"
147+
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"
149+
exit 1
150+
fi
151+
130152
- name: Checkout
131153
uses: actions/checkout@v3
154+
with:
155+
ref: ${{ env.PR_BRANCH }}
132156

133157
- name: Set up kubeconfig
134158
run: |
@@ -157,6 +181,7 @@ jobs:
157181
spec:
158182
tls:
159183
- hosts:
184+
- "${{ secrets.PR_DEPLOYMENTS_DOMAIN }}"
160185
- "*.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}"
161186
secretName: pr${{ env.PR_NUMBER }}-tls
162187
rules:
@@ -173,32 +198,39 @@ jobs:
173198
EOF
174199
kubectl apply -f ingress.yaml
175200
201+
- name: Create values.yaml
202+
run: |
203+
cat <<EOF > pr-deploy-values.yaml
204+
coder:
205+
image:
206+
repo: ${{ env.REPO }}
207+
tag: pr${{ env.PR_NUMBER }}
208+
pullPolicy: Always
209+
service:
210+
type: ClusterIP
211+
env:
212+
- name: "CODER_ACCESS_URL"
213+
value: "https://pr${{ env.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}"
214+
- name: "CODER_WILDCARD_ACCESS_URL"
215+
value: "*--pr${{ env.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}"
216+
- name: "CODER_EXPERIMENTS"
217+
value: "*"
218+
- name: "CODER_OAUTH2_GITHUB_ALLOW_SIGNUPS"
219+
value: "true"
220+
- name: "CODER_OAUTH2_GITHUB_CLIENT_ID"
221+
value: "${{ secrets.PR_DEPLOYMENTS_GITHUB_OAUTH_CLIENT_ID }}"
222+
- name: "CODER_OAUTH2_GITHUB_CLIENT_SECRET"
223+
value: "${{ secrets.PR_DEPLOYMENTS_GITHUB_OAUTH_CLIENT_SECRET }}"
224+
- name: "CODER_OAUTH2_GITHUB_ALLOWED_ORGS"
225+
value: "coder"
226+
EOF
227+
176228
- name: Install Helm chart
177229
run: |
178-
helm upgrade --install pr${{ env.PR_NUMBER }} ./helm \
230+
helm upgrade --install "pr${{ env.PR_NUMBER }}" ./helm \
179231
--namespace "pr${{ env.PR_NUMBER }}" \
180-
--set coder.image.repo=${{ env.REPO }} \
181-
--set coder.image.tag=pr${{ env.PR_NUMBER }} \
182-
--set coder.service.type=ClusterIP \
183-
--set coder.serviceAccount.enableDeployments=true \
184-
--set coder.env[0].name=CODER_ACCESS_URL \
185-
--set coder.env[0].value="https://pr${{ env.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}" \
186-
--set coder.env[1].name=CODER_WILDCARD_ACCESS_URL \
187-
--set coder.env[1].value="*--pr${{ env.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}" \
188-
--set coder.env[2].name=CODER_EXPERIMENTS \
189-
--set coder.env[2].value="*" \
232+
--values ./pr-deploy-values.yaml \
190233
--force
191-
# Uncomment this when https://github.com/coder/coder/issues/8714 is resolved
192-
# --set coder.env[3].name=CODER_OAUTH2_GITHUB_ALLOW_SIGNUPS \
193-
# --set coder.env[3].value=true \
194-
# --set coder.env[4].name=CODER_OAUTH2_GITHUB_CLIENT_ID \
195-
# --set coder.env[4].value=${{ secrets.PR_DEPLOYMENTS_GITHUB_OAUTH_CLIENT_ID }} \
196-
# --set coder.env[5].name=CODER_OAUTH2_GITHUB_CLIENT_SECRET \
197-
# --set coder.env[5].value=${{ secrets.PR_DEPLOYMENTS_GITHUB_OAUTH_CLIENT_SECRET }} \
198-
# --set coder.env[6].name=CODER_OAUTH2_GITHUB_ALLOWED_ORGS \
199-
# --set coder.env[6].value=coder \
200-
# --set coder.env[7].name=CODER_OAUTH2_GITHUB_REDIRECT_URI \
201-
# --set coder.env[7].value="https://pr${{ env.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}/gitauth/github/callback
202234
203235
- name: Install coder-logstream-kube
204236
run: |
@@ -207,28 +239,95 @@ jobs:
207239
--namespace "pr${{ env.PR_NUMBER }}" \
208240
--set url="https://pr${{ env.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}"
209241
242+
- name: Get Coder binary
243+
run: |
244+
set -euxo pipefail
245+
246+
DEST="${HOME}/coder"
247+
URL="${{ env.PR_DEPLOYMENT_ACCESS_URL }}/bin/coder-linux-amd64"
248+
249+
mkdir -p "$(dirname ${DEST})"
250+
251+
COUNT=0
252+
until $(curl --output /dev/null --silent --head --fail "$URL"); do
253+
printf '.'
254+
sleep 5
255+
COUNT=$((COUNT+1))
256+
if [ $COUNT -ge 60 ]; then
257+
echo "Timed out waiting for URL to be available"
258+
exit 1
259+
fi
260+
done
261+
262+
curl -fsSL "$URL" -o "${DEST}"
263+
chmod +x "${DEST}"
264+
"${DEST}" version
265+
266+
- name: Create first user, template and workspace
267+
id: setup_deployment
268+
run: |
269+
set -euxo pipefail
270+
271+
# Create first user
272+
273+
# create a masked random password 12 characters long
274+
password=$(openssl rand -base64 16 | tr -d "=+/" | cut -c1-12)
275+
276+
# add mask so that the password is not printed to the logs
277+
echo "::add-mask::$password"
278+
echo "password=$password" >> $GITHUB_OUTPUT
279+
280+
/home/runner/coder login \
281+
--first-user-username pr${{ env.PR_NUMBER }} \
282+
--first-user-email ${{ env.PR_NUMBER }}@coder.com \
283+
--first-user-password $password \
284+
--first-user-trial \
285+
--use-token-as-session \
286+
${{ env.PR_DEPLOYMENT_ACCESS_URL }}
287+
288+
# Create template
289+
/home/runner/coder templates init --id kubernetes && cd ./kubernetes/ && /home/runner/coder templates create -y --variable namespace=pr${{ env.PR_NUMBER }}
290+
291+
# Create workspace
292+
cat <<EOF > workspace.yaml
293+
cpu: "2"
294+
memory: "4"
295+
home_disk_size: "2"
296+
EOF
297+
298+
/home/runner/coder create --template="kubernetes" pr${{ env.PR_NUMBER }} --rich-parameter-file ./workspace.yaml -y
299+
/home/runner/coder stop pr${{ env.PR_NUMBER }} -y
300+
210301
- name: Send Slack notification
211302
run: |
212303
curl -s -o /dev/null -X POST -H 'Content-type: application/json' \
213-
-d '{
214-
"pr_number": "'"${{ env.PR_NUMBER }}"'",
215-
"pr_url": "'"${{ env.PR_URL }}"'",
216-
"pr_title": "'"${{ env.PR_TITLE }}"'",
217-
"pr_access_url": "'"${{ env.PR_DEPLOYMENT_ACCESS_URL }}"'" }' ${{ secrets.PR_DEPLOYMENTS_SLACK_WEBHOOK }}
304+
-d \
305+
'{
306+
"pr_number": "'"${{ env.PR_NUMBER }}"'",
307+
"pr_url": "'"${{ env.PR_URL }}"'",
308+
"pr_title": "'"${{ env.PR_TITLE }}"'",
309+
"pr_access_url": "'"${{ env.PR_DEPLOYMENT_ACCESS_URL }}"'",
310+
"pr_username": "'"pr${{ env.PR_NUMBER }}"'",
311+
"pr_email": "'"${{ env.PR_NUMBER }}@coder.com"'",
312+
"pr_password": "'"${{ steps.setup_deployment.outputs.password }}"'",
313+
"pr_actor": "'"${{ github.actor }}"'"
314+
}' \
315+
${{ secrets.PR_DEPLOYMENTS_SLACK_WEBHOOK }}
218316
echo "Slack notification sent"
219-
env:
220-
PR_DEPLOYMENT_ACCESS_URL: "https://pr${{ env.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}"
221317
222318
- name: Find Comment
223319
uses: peter-evans/find-comment@v2
320+
if: github.event_name == 'issue_comment'
224321
id: fc
225322
with:
226323
issue-number: ${{ env.PR_NUMBER }}
227324
comment-author: "github-actions[bot]"
228325
body-includes: This deployment will be deleted when the PR is closed
326+
direction: last
229327

230328
- name: Comment on PR
231329
uses: peter-evans/create-or-update-comment@v3
330+
if: github.event_name == 'issue_comment'
232331
with:
233332
issue-number: ${{ env.PR_NUMBER }}
234333
edit-mode: replace
@@ -238,6 +337,3 @@ jobs:
238337
:rocket: Access the deployment link [here](${{ env.PR_DEPLOYMENT_ACCESS_URL }}).
239338
:warning: This deployment will be deleted when the PR is closed.
240339
reactions: rocket
241-
242-
env:
243-
PR_DEPLOYMENT_ACCESS_URL: "https://pr${{ env.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}"

scripts/deploy-pr.sh

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/usr/bin/env bash
2+
# Usage: ./deploy-pr.sh --skip-build
3+
# deploys the current branch to a PR environment and posts login credentials to
4+
# [#pr-deployments](https://codercom.slack.com/archives/C05DNE982E8) Slack channel
5+
# if --skip-build is passed, the build step will be skipped and the last build image will be used
6+
7+
set -euox pipefail
8+
9+
branchName=$(gh pr view --json headRefName | jq -r .headRefName)
10+
11+
if [[ "$branchName" == "main" ]]; then
12+
prNumber=$(git rev-parse --short HEAD)
13+
else
14+
prNumber=$(gh pr view --json number | jq -r .number)
15+
fi
16+
17+
# if --skip-build is passed, the build job will be skipped and the last built image will be used
18+
if [[ "$*" == *--skip-build* ]]; then
19+
skipBuild=true
20+
#check if the image exists
21+
foundTag=$(curl -fsSL https://github.com/coder/coder/pkgs/container/coder-preview | grep -o "$prNumber" | head -n 1)
22+
if [ -z "${foundTag}" ]; then
23+
echo "Image not found"
24+
echo "${prNumber} tag not found in ghcr.io/coder/coder-preview"
25+
echo "Please remove --skip-build and try again"
26+
exit 1
27+
fi
28+
else
29+
skipBuild=false
30+
fi
31+
32+
gh workflow run pr-deploy.yaml --ref "${branchName}" -f pr_number="${prNumber}" -f skip_build="${skipBuild}"

0 commit comments

Comments
 (0)