fixup! chore: rework RPC version negotiation #20799
Workflow file for this run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This action will trigger when | |
# 1. when the workflow is manually triggered | |
# 2. ./scripts/deploy_pr.sh is run locally | |
# 3. when a PR is updated | |
name: Deploy PR | |
on: | |
push: | |
branches-ignore: | |
- main | |
workflow_dispatch: | |
inputs: | |
experiments: | |
description: "Experiments to enable" | |
required: false | |
type: string | |
default: "*" | |
build: | |
description: "Force new build" | |
required: false | |
type: boolean | |
default: false | |
deploy: | |
description: "Force new deployment" | |
required: false | |
type: boolean | |
default: false | |
env: | |
REPO: ghcr.io/coder/coder-preview | |
permissions: | |
contents: read | |
jobs: | |
check_pr: | |
runs-on: ubuntu-latest | |
outputs: | |
PR_OPEN: ${{ steps.check_pr.outputs.pr_open }} | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 | |
with: | |
egress-policy: audit | |
- name: Checkout | |
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 | |
- name: Check if PR is open | |
id: check_pr | |
run: | | |
set -euo pipefail | |
pr_open=true | |
if [[ "$(gh pr view --json state | jq -r '.state')" != "OPEN" ]]; then | |
echo "PR doesn't exist or is closed." | |
pr_open=false | |
fi | |
echo "pr_open=$pr_open" >> $GITHUB_OUTPUT | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
get_info: | |
needs: check_pr | |
if: ${{ needs.check_pr.outputs.PR_OPEN == 'true' }} | |
outputs: | |
PR_NUMBER: ${{ steps.pr_info.outputs.PR_NUMBER }} | |
PR_TITLE: ${{ steps.pr_info.outputs.PR_TITLE }} | |
PR_URL: ${{ steps.pr_info.outputs.PR_URL }} | |
CODER_BASE_IMAGE_TAG: ${{ steps.set_tags.outputs.CODER_BASE_IMAGE_TAG }} | |
CODER_IMAGE_TAG: ${{ steps.set_tags.outputs.CODER_IMAGE_TAG }} | |
NEW: ${{ steps.check_deployment.outputs.NEW }} | |
BUILD: ${{ steps.build_conditionals.outputs.first_or_force_build == 'true' || steps.build_conditionals.outputs.automatic_rebuild == 'true' }} | |
runs-on: "ubuntu-latest" | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 | |
with: | |
egress-policy: audit | |
- name: Checkout | |
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 | |
with: | |
fetch-depth: 0 | |
- name: Get PR number, title, and branch name | |
id: pr_info | |
run: | | |
set -euo pipefail | |
PR_NUMBER=$(gh pr view --json number | jq -r '.number') | |
PR_TITLE=$(gh pr view --json title | jq -r '.title') | |
PR_URL=$(gh pr view --json url | jq -r '.url') | |
echo "PR_URL=$PR_URL" >> $GITHUB_OUTPUT | |
echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT | |
echo "PR_TITLE=$PR_TITLE" >> $GITHUB_OUTPUT | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
- name: Set required tags | |
id: set_tags | |
run: | | |
set -euo pipefail | |
echo "CODER_BASE_IMAGE_TAG=$CODER_BASE_IMAGE_TAG" >> $GITHUB_OUTPUT | |
echo "CODER_IMAGE_TAG=$CODER_IMAGE_TAG" >> $GITHUB_OUTPUT | |
env: | |
CODER_BASE_IMAGE_TAG: ghcr.io/coder/coder-preview-base:pr${{ steps.pr_info.outputs.PR_NUMBER }} | |
CODER_IMAGE_TAG: ghcr.io/coder/coder-preview:pr${{ steps.pr_info.outputs.PR_NUMBER }} | |
- name: Set up kubeconfig | |
run: | | |
set -euo pipefail | |
mkdir -p ~/.kube | |
echo "${{ secrets.PR_DEPLOYMENTS_KUBECONFIG_BASE64 }}" | base64 --decode > ~/.kube/config | |
chmod 600 ~/.kube/config | |
export KUBECONFIG=~/.kube/config | |
- name: Check if the helm deployment already exists | |
id: check_deployment | |
run: | | |
set -euo pipefail | |
if helm status "pr${{ steps.pr_info.outputs.PR_NUMBER }}" --namespace "pr${{ steps.pr_info.outputs.PR_NUMBER }}" > /dev/null 2>&1; then | |
echo "Deployment already exists. Skipping deployment." | |
NEW=false | |
else | |
echo "Deployment doesn't exist." | |
NEW=true | |
fi | |
echo "NEW=$NEW" >> $GITHUB_OUTPUT | |
- name: Check changed files | |
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 | |
id: filter | |
with: | |
base: ${{ github.ref }} | |
filters: | | |
all: | |
- "**" | |
ignored: | |
- "docs/**" | |
- "README.md" | |
- "examples/web-server/**" | |
- "examples/monitoring/**" | |
- "examples/lima/**" | |
- ".github/**" | |
- "offlinedocs/**" | |
- ".devcontainer/**" | |
- "helm/**" | |
- "*[^g][^o][^.][^s][^u][^m]*" | |
- "*[^g][^o][^.][^m][^o][^d]*" | |
- "*[^M][^a][^k][^e][^f][^i][^l][^e]*" | |
- "scripts/**/*[^D][^o][^c][^k][^e][^r][^f][^i][^l][^e]*" | |
- "scripts/**/*[^D][^o][^c][^k][^e][^r][^f][^i][^l][^e][.][b][^a][^s][^e]*" | |
- name: Print number of changed files | |
run: | | |
set -euo pipefail | |
echo "Total number of changed files: ${{ steps.filter.outputs.all_count }}" | |
echo "Number of ignored files: ${{ steps.filter.outputs.ignored_count }}" | |
- name: Build conditionals | |
id: build_conditionals | |
run: | | |
set -euo pipefail | |
# build if the workflow is manually triggered and the deployment doesn't exist (first build or force rebuild) | |
echo "first_or_force_build=${{ (github.event_name == 'workflow_dispatch' && steps.check_deployment.outputs.NEW == 'true') || github.event.inputs.build == 'true' }}" >> $GITHUB_OUTPUT | |
# build if the deployment already exist and there are changes in the files that we care about (automatic updates) | |
echo "automatic_rebuild=${{ steps.check_deployment.outputs.NEW == 'false' && steps.filter.outputs.all_count > steps.filter.outputs.ignored_count }}" >> $GITHUB_OUTPUT | |
comment-pr: | |
needs: get_info | |
if: needs.get_info.outputs.BUILD == 'true' || github.event.inputs.deploy == 'true' | |
runs-on: "ubuntu-latest" | |
permissions: | |
pull-requests: write # needed for commenting on PRs | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 | |
with: | |
egress-policy: audit | |
- name: Find Comment | |
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0 | |
id: fc | |
with: | |
issue-number: ${{ needs.get_info.outputs.PR_NUMBER }} | |
comment-author: "github-actions[bot]" | |
body-includes: ":rocket:" | |
direction: last | |
- name: Comment on PR | |
id: comment_id | |
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0 | |
with: | |
comment-id: ${{ steps.fc.outputs.comment-id }} | |
issue-number: ${{ needs.get_info.outputs.PR_NUMBER }} | |
edit-mode: replace | |
body: | | |
--- | |
:rocket: Deploying PR ${{ needs.get_info.outputs.PR_NUMBER }} ... | |
--- | |
reactions: eyes | |
reactions-edit-mode: replace | |
build: | |
needs: get_info | |
# Run build job only if there are changes in the files that we care about or if the workflow is manually triggered with --build flag | |
if: needs.get_info.outputs.BUILD == 'true' | |
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} | |
permissions: | |
# Necessary to push docker images to ghcr.io. | |
packages: write | |
# This concurrency only cancels build jobs if a new build is triggred. It will avoid cancelling the current deployemtn in case of docs changes. | |
concurrency: | |
group: build-${{ github.workflow }}-${{ github.ref }}-${{ needs.get_info.outputs.BUILD }} | |
cancel-in-progress: true | |
env: | |
DOCKER_CLI_EXPERIMENTAL: "enabled" | |
CODER_IMAGE_TAG: ${{ needs.get_info.outputs.CODER_IMAGE_TAG }} | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 | |
with: | |
egress-policy: audit | |
- name: Checkout | |
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 | |
with: | |
fetch-depth: 0 | |
- name: Setup Node | |
uses: ./.github/actions/setup-node | |
- name: Setup Go | |
uses: ./.github/actions/setup-go | |
- name: Setup sqlc | |
uses: ./.github/actions/setup-sqlc | |
- name: GHCR Login | |
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 | |
with: | |
registry: ghcr.io | |
username: ${{ github.actor }} | |
password: ${{ secrets.GITHUB_TOKEN }} | |
- name: Build and push Linux amd64 Docker image | |
run: | | |
set -euo pipefail | |
go mod download | |
make gen/mark-fresh | |
export DOCKER_IMAGE_NO_PREREQUISITES=true | |
version="$(./scripts/version.sh)" | |
export CODER_IMAGE_BUILD_BASE_TAG="$(CODER_IMAGE_BASE=coder-base ./scripts/image_tag.sh --version "$version")" | |
make -j build/coder_linux_amd64 | |
./scripts/build_docker.sh \ | |
--arch amd64 \ | |
--target ${{ env.CODER_IMAGE_TAG }} \ | |
--version $version \ | |
--push \ | |
build/coder_linux_amd64 | |
deploy: | |
needs: [build, get_info] | |
# Run deploy job only if build job was successful or skipped | |
if: | | |
always() && (needs.build.result == 'success' || needs.build.result == 'skipped') && | |
(needs.get_info.outputs.BUILD == 'true' || github.event.inputs.deploy == 'true') | |
runs-on: "ubuntu-latest" | |
permissions: | |
pull-requests: write # needed for commenting on PRs | |
env: | |
CODER_IMAGE_TAG: ${{ needs.get_info.outputs.CODER_IMAGE_TAG }} | |
PR_NUMBER: ${{ needs.get_info.outputs.PR_NUMBER }} | |
PR_TITLE: ${{ needs.get_info.outputs.PR_TITLE }} | |
PR_URL: ${{ needs.get_info.outputs.PR_URL }} | |
PR_HOSTNAME: "pr${{ needs.get_info.outputs.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}" | |
steps: | |
- name: Harden Runner | |
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 | |
with: | |
egress-policy: audit | |
- name: Set up kubeconfig | |
run: | | |
set -euo pipefail | |
mkdir -p ~/.kube | |
echo "${{ secrets.PR_DEPLOYMENTS_KUBECONFIG_BASE64 }}" | base64 --decode > ~/.kube/config | |
chmod 600 ~/.kube/config | |
export KUBECONFIG=~/.kube/config | |
- name: Check if image exists | |
run: | | |
set -euo pipefail | |
foundTag=$( | |
gh api /orgs/coder/packages/container/coder-preview/versions | | |
jq -r --arg tag "pr${{ env.PR_NUMBER }}" '.[] | | |
select(.metadata.container.tags == [$tag]) | | |
.metadata.container.tags[0]' | |
) | |
if [ -z "$foundTag" ]; then | |
echo "Image not found" | |
echo "${{ env.CODER_IMAGE_TAG }} not found in ghcr.io/coder/coder-preview" | |
exit 1 | |
else | |
echo "Image found" | |
echo "$foundTag tag found in ghcr.io/coder/coder-preview" | |
fi | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
- name: Add DNS record to Cloudflare | |
if: needs.get_info.outputs.NEW == 'true' | |
run: | | |
curl -X POST "https://api.cloudflare.com/client/v4/zones/${{ secrets.PR_DEPLOYMENTS_ZONE_ID }}/dns_records" \ | |
-H "Authorization: Bearer ${{ secrets.PR_DEPLOYMENTS_CLOUDFLARE_API_TOKEN }}" \ | |
-H "Content-Type:application/json" \ | |
--data '{"type":"CNAME","name":"*.${{ env.PR_HOSTNAME }}","content":"${{ env.PR_HOSTNAME }}","ttl":1,"proxied":false}' | |
- name: Create PR namespace | |
if: needs.get_info.outputs.NEW == 'true' || github.event.inputs.deploy == 'true' | |
run: | | |
set -euo pipefail | |
# try to delete the namespace, but don't fail if it doesn't exist | |
kubectl delete namespace "pr${{ env.PR_NUMBER }}" || true | |
kubectl create namespace "pr${{ env.PR_NUMBER }}" | |
- name: Checkout | |
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 | |
- name: Check and Create Certificate | |
if: needs.get_info.outputs.NEW == 'true' || github.event.inputs.deploy == 'true' | |
run: | | |
# Using kubectl to check if a Certificate resource already exists | |
# we are doing this to avoid letsenrypt rate limits | |
if ! kubectl get certificate pr${{ env.PR_NUMBER }}-tls -n pr-deployment-certs > /dev/null 2>&1; then | |
echo "Certificate doesn't exist. Creating a new one." | |
envsubst < ./.github/pr-deployments/certificate.yaml | kubectl apply -f - | |
else | |
echo "Certificate exists. Skipping certificate creation." | |
fi | |
echo "Copy certificate from pr-deployment-certs to pr${{ env.PR_NUMBER }} namespace" | |
until kubectl get secret pr${{ env.PR_NUMBER }}-tls -n pr-deployment-certs &> /dev/null | |
do | |
echo "Waiting for secret pr${{ env.PR_NUMBER }}-tls to be created..." | |
sleep 5 | |
done | |
( | |
kubectl get secret pr${{ env.PR_NUMBER }}-tls -n pr-deployment-certs -o json | | |
jq 'del(.metadata.namespace,.metadata.creationTimestamp,.metadata.resourceVersion,.metadata.selfLink,.metadata.uid,.metadata.managedFields)' | | |
kubectl -n pr${{ env.PR_NUMBER }} apply -f - | |
) | |
- name: Set up PostgreSQL database | |
if: needs.get_info.outputs.NEW == 'true' || github.event.inputs.deploy == 'true' | |
run: | | |
helm repo add bitnami https://charts.bitnami.com/bitnami | |
helm install coder-db bitnami/postgresql \ | |
--namespace pr${{ env.PR_NUMBER }} \ | |
--set auth.username=coder \ | |
--set auth.password=coder \ | |
--set auth.database=coder \ | |
--set persistence.size=10Gi | |
kubectl create secret generic coder-db-url -n pr${{ env.PR_NUMBER }} \ | |
--from-literal=url="postgres://coder:[email protected]${{ env.PR_NUMBER }}.svc.cluster.local:5432/coder?sslmode=disable" | |
- name: Create a service account, role, and rolebinding for the PR namespace | |
if: needs.get_info.outputs.NEW == 'true' || github.event.inputs.deploy == 'true' | |
run: | | |
set -euo pipefail | |
# Create service account, role, rolebinding | |
envsubst < ./.github/pr-deployments/rbac.yaml | kubectl apply -f - | |
- name: Create values.yaml | |
env: | |
EXPERIMENTS: ${{ github.event.inputs.experiments }} | |
PR_DEPLOYMENTS_GITHUB_OAUTH_CLIENT_ID: ${{ secrets.PR_DEPLOYMENTS_GITHUB_OAUTH_CLIENT_ID }} | |
PR_DEPLOYMENTS_GITHUB_OAUTH_CLIENT_SECRET: ${{ secrets.PR_DEPLOYMENTS_GITHUB_OAUTH_CLIENT_SECRET }} | |
run: | | |
set -euo pipefail | |
envsubst < ./.github/pr-deployments/values.yaml > ./pr-deploy-values.yaml | |
- name: Install/Upgrade Helm chart | |
run: | | |
set -euo pipefail | |
helm dependency update --skip-refresh ./helm/coder | |
helm upgrade --install "pr${{ env.PR_NUMBER }}" ./helm/coder \ | |
--namespace "pr${{ env.PR_NUMBER }}" \ | |
--values ./pr-deploy-values.yaml \ | |
--force | |
- name: Install coder-logstream-kube | |
if: needs.get_info.outputs.NEW == 'true' || github.event.inputs.deploy == 'true' | |
run: | | |
helm repo add coder-logstream-kube https://helm.coder.com/logstream-kube | |
helm upgrade --install coder-logstream-kube coder-logstream-kube/coder-logstream-kube \ | |
--namespace "pr${{ env.PR_NUMBER }}" \ | |
--set url="https://${{ env.PR_HOSTNAME }}" | |
- name: Get Coder binary | |
if: needs.get_info.outputs.NEW == 'true' || github.event.inputs.deploy == 'true' | |
run: | | |
set -euo pipefail | |
DEST="${HOME}/coder" | |
URL="https://${{ env.PR_HOSTNAME }}/bin/coder-linux-amd64" | |
mkdir -p "$(dirname ${DEST})" | |
COUNT=0 | |
until $(curl --output /dev/null --silent --head --fail "$URL"); do | |
printf '.' | |
sleep 5 | |
COUNT=$((COUNT 1)) | |
if [ $COUNT -ge 60 ]; then | |
echo "Timed out waiting for URL to be available" | |
exit 1 | |
fi | |
done | |
curl -fsSL "$URL" -o "${DEST}" | |
chmod x "${DEST}" | |
"${DEST}" version | |
mv "${DEST}" /usr/local/bin/coder | |
- name: Create first user | |
if: needs.get_info.outputs.NEW == 'true' || github.event.inputs.deploy == 'true' | |
id: setup_deployment | |
env: | |
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
run: | | |
set -euo pipefail | |
# create a masked random password 12 characters long | |
password=$(openssl rand -base64 16 | tr -d "= /" | cut -c1-12) | |
# add mask so that the password is not printed to the logs | |
echo "::add-mask::$password" | |
echo "password=$password" >> $GITHUB_OUTPUT | |
coder login \ | |
--first-user-username pr${{ env.PR_NUMBER }}-admin \ | |
--first-user-email pr${{ env.PR_NUMBER }}@coder.com \ | |
--first-user-password $password \ | |
--first-user-trial=false \ | |
--use-token-as-session \ | |
https://${{ env.PR_HOSTNAME }} | |
# Create a user for the github.actor | |
# TODO: update once https://github.com/coder/coder/issues/15466 is resolved | |
# coder users create \ | |
# --username ${{ github.actor }} \ | |
# --login-type github | |
# promote the user to admin role | |
# coder org members edit-role ${{ github.actor }} organization-admin | |
# TODO: update once https://github.com/coder/internal/issues/207 is resolved | |
- name: Send Slack notification | |
if: needs.get_info.outputs.NEW == 'true' || github.event.inputs.deploy == 'true' | |
run: | | |
curl -s -o /dev/null -X POST -H 'Content-type: application/json' \ | |
-d \ | |
'{ | |
"pr_number": "'"${{ env.PR_NUMBER }}"'", | |
"pr_url": "'"${{ env.PR_URL }}"'", | |
"pr_title": "'"${{ env.PR_TITLE }}"'", | |
"pr_access_url": "'"https://${{ env.PR_HOSTNAME }}"'", | |
"pr_username": "'"pr${{ env.PR_NUMBER }}-admin"'", | |
"pr_email": "'"pr${{ env.PR_NUMBER }}@coder.com"'", | |
"pr_password": "'"${{ steps.setup_deployment.outputs.password }}"'", | |
"pr_actor": "'"${{ github.actor }}"'" | |
}' \ | |
${{ secrets.PR_DEPLOYMENTS_SLACK_WEBHOOK }} | |
echo "Slack notification sent" | |
- name: Find Comment | |
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0 | |
id: fc | |
with: | |
issue-number: ${{ env.PR_NUMBER }} | |
comment-author: "github-actions[bot]" | |
body-includes: ":rocket:" | |
direction: last | |
- name: Comment on PR | |
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0 | |
env: | |
STATUS: ${{ needs.get_info.outputs.NEW == 'true' && 'Created' || 'Updated' }} | |
with: | |
issue-number: ${{ env.PR_NUMBER }} | |
edit-mode: replace | |
comment-id: ${{ steps.fc.outputs.comment-id }} | |
body: | | |
--- | |
:heavy_check_mark: PR ${{ env.PR_NUMBER }} ${{ env.STATUS }} successfully. | |
:rocket: Access the credentials [here](${{ secrets.PR_DEPLOYMENTS_SLACK_CHANNEL_URL }}). | |
--- | |
cc: @${{ github.actor }} | |
reactions: rocket | |
reactions-edit-mode: replace | |
- name: Create template and workspace | |
if: needs.get_info.outputs.NEW == 'true' || github.event.inputs.deploy == 'true' | |
run: | | |
set -euo pipefail | |
cd .github/pr-deployments/template | |
coder templates push -y --variable namespace=pr${{ env.PR_NUMBER }} kubernetes | |
# Create workspace | |
coder create --template="kubernetes" kube --parameter cpu=2 --parameter memory=4 --parameter home_disk_size=2 -y | |
coder stop kube -y |