diff --git a/.github/workflows/pr-deploy.yaml b/.github/workflows/pr-deploy.yaml index ee4d274341e3f..95485abfbe898 100644 --- a/.github/workflows/pr-deploy.yaml +++ b/.github/workflows/pr-deploy.yaml @@ -8,6 +8,10 @@ on: pr_number: description: "PR number" required: true + skip_build: + description: "Skip build job" + required: false + default: false env: REPO: ghcr.io/coder/coder-preview @@ -62,6 +66,7 @@ jobs: - name: Comment on PR id: comment_id + if: github.event_name == 'issue_comment' uses: peter-evans/create-or-update-comment@v3 with: issue-number: ${{ steps.pr_info.outputs.PR_NUMBER }} @@ -72,6 +77,9 @@ jobs: build: needs: pr_commented + # Skips the build job if the workflow was triggered by a workflow_dispatch event and the skip_build input is set to true + # or if the workflow was triggered by an issue_comment event and the comment body contains --skip-build + 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) runs-on: ${{ github.repository_owner == 'coder' && 'buildjet-8vcpu-ubuntu-2204' || 'ubuntu-latest' }} env: DOCKER_CLI_EXPERIMENTAL: "enabled" @@ -119,16 +127,32 @@ jobs: deploy: needs: [build, pr_commented] - if: needs.build.result == 'success' + # Run deploy job only if build job was successful or skipped + if: always() && (needs.build.result == 'success' || needs.build.result == 'skipped') runs-on: "ubuntu-latest" env: CODER_IMAGE_TAG: ${{ needs.pr_commented.outputs.CODER_IMAGE_TAG }} PR_NUMBER: ${{ needs.pr_commented.outputs.PR_NUMBER }} PR_TITLE: ${{ needs.pr_commented.outputs.PR_TITLE }} PR_URL: ${{ needs.pr_commented.outputs.PR_URL }} + PR_BRANCH: ${{ needs.pr_commented.outputs.PR_BRANCH }} + PR_DEPLOYMENT_ACCESS_URL: "https://pr${{ needs.pr_commented.outputs.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}" steps: + - name: Check if image exists + run: | + set -euxo pipefail + foundTag=$(curl -fsSL https://github.com/coder/coder/pkgs/container/coder-preview | grep -o ${{ env.CODER_IMAGE_TAG }} | head -n 1) + if [ -z "$foundTag" ]; then + echo "Image not found" + echo "${{ env.CODER_IMAGE_TAG }} not found in ghcr.io/coder/coder-preview" + echo "Please remove --skip-build from the comment or ./scripts/deploy-pr.sh" + exit 1 + fi + - name: Checkout uses: actions/checkout@v3 + with: + ref: ${{ env.PR_BRANCH }} - name: Set up kubeconfig run: | @@ -157,6 +181,7 @@ jobs: spec: tls: - hosts: + - "${{ secrets.PR_DEPLOYMENTS_DOMAIN }}" - "*.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}" secretName: pr${{ env.PR_NUMBER }}-tls rules: @@ -173,32 +198,39 @@ jobs: EOF kubectl apply -f ingress.yaml + - name: Create values.yaml + run: | + cat < pr-deploy-values.yaml + coder: + image: + repo: ${{ env.REPO }} + tag: pr${{ env.PR_NUMBER }} + pullPolicy: Always + service: + type: ClusterIP + env: + - name: "CODER_ACCESS_URL" + value: "https://pr${{ env.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}" + - name: "CODER_WILDCARD_ACCESS_URL" + value: "*--pr${{ env.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}" + - name: "CODER_EXPERIMENTS" + value: "*" + - name: "CODER_OAUTH2_GITHUB_ALLOW_SIGNUPS" + value: "true" + - name: "CODER_OAUTH2_GITHUB_CLIENT_ID" + value: "${{ secrets.PR_DEPLOYMENTS_GITHUB_OAUTH_CLIENT_ID }}" + - name: "CODER_OAUTH2_GITHUB_CLIENT_SECRET" + value: "${{ secrets.PR_DEPLOYMENTS_GITHUB_OAUTH_CLIENT_SECRET }}" + - name: "CODER_OAUTH2_GITHUB_ALLOWED_ORGS" + value: "coder" + EOF + - name: Install Helm chart run: | - helm upgrade --install pr${{ env.PR_NUMBER }} ./helm \ + helm upgrade --install "pr${{ env.PR_NUMBER }}" ./helm \ --namespace "pr${{ env.PR_NUMBER }}" \ - --set coder.image.repo=${{ env.REPO }} \ - --set coder.image.tag=pr${{ env.PR_NUMBER }} \ - --set coder.service.type=ClusterIP \ - --set coder.serviceAccount.enableDeployments=true \ - --set coder.env[0].name=CODER_ACCESS_URL \ - --set coder.env[0].value="https://pr${{ env.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}" \ - --set coder.env[1].name=CODER_WILDCARD_ACCESS_URL \ - --set coder.env[1].value="*--pr${{ env.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}" \ - --set coder.env[2].name=CODER_EXPERIMENTS \ - --set coder.env[2].value="*" \ + --values ./pr-deploy-values.yaml \ --force - # Uncomment this when https://github.com/coder/coder/issues/8714 is resolved - # --set coder.env[3].name=CODER_OAUTH2_GITHUB_ALLOW_SIGNUPS \ - # --set coder.env[3].value=true \ - # --set coder.env[4].name=CODER_OAUTH2_GITHUB_CLIENT_ID \ - # --set coder.env[4].value=${{ secrets.PR_DEPLOYMENTS_GITHUB_OAUTH_CLIENT_ID }} \ - # --set coder.env[5].name=CODER_OAUTH2_GITHUB_CLIENT_SECRET \ - # --set coder.env[5].value=${{ secrets.PR_DEPLOYMENTS_GITHUB_OAUTH_CLIENT_SECRET }} \ - # --set coder.env[6].name=CODER_OAUTH2_GITHUB_ALLOWED_ORGS \ - # --set coder.env[6].value=coder \ - # --set coder.env[7].name=CODER_OAUTH2_GITHUB_REDIRECT_URI \ - # --set coder.env[7].value="https://pr${{ env.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}/gitauth/github/callback - name: Install coder-logstream-kube run: | @@ -207,28 +239,95 @@ jobs: --namespace "pr${{ env.PR_NUMBER }}" \ --set url="https://pr${{ env.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}" + - name: Get Coder binary + run: | + set -euxo pipefail + + DEST="${HOME}/coder" + URL="${{ env.PR_DEPLOYMENT_ACCESS_URL }}/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 + + - name: Create first user, template and workspace + id: setup_deployment + run: | + set -euxo pipefail + + # Create first user + + # 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 + + /home/runner/coder login \ + --first-user-username pr${{ env.PR_NUMBER }} \ + --first-user-email ${{ env.PR_NUMBER }}@coder.com \ + --first-user-password $password \ + --first-user-trial \ + --use-token-as-session \ + ${{ env.PR_DEPLOYMENT_ACCESS_URL }} + + # Create template + /home/runner/coder templates init --id kubernetes && cd ./kubernetes/ && /home/runner/coder templates create -y --variable namespace=pr${{ env.PR_NUMBER }} + + # Create workspace + cat < workspace.yaml + cpu: "2" + memory: "4" + home_disk_size: "2" + EOF + + /home/runner/coder create --template="kubernetes" pr${{ env.PR_NUMBER }} --rich-parameter-file ./workspace.yaml -y + /home/runner/coder stop pr${{ env.PR_NUMBER }} -y + - name: Send Slack notification 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": "'"${{ env.PR_DEPLOYMENT_ACCESS_URL }}"'" }' ${{ secrets.PR_DEPLOYMENTS_SLACK_WEBHOOK }} + -d \ + '{ + "pr_number": "'"${{ env.PR_NUMBER }}"'", + "pr_url": "'"${{ env.PR_URL }}"'", + "pr_title": "'"${{ env.PR_TITLE }}"'", + "pr_access_url": "'"${{ env.PR_DEPLOYMENT_ACCESS_URL }}"'", + "pr_username": "'"pr${{ env.PR_NUMBER }}"'", + "pr_email": "'"${{ 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" - env: - PR_DEPLOYMENT_ACCESS_URL: "https://pr${{ env.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}" - name: Find Comment uses: peter-evans/find-comment@v2 + if: github.event_name == 'issue_comment' id: fc with: issue-number: ${{ env.PR_NUMBER }} comment-author: "github-actions[bot]" body-includes: This deployment will be deleted when the PR is closed + direction: last - name: Comment on PR uses: peter-evans/create-or-update-comment@v3 + if: github.event_name == 'issue_comment' with: issue-number: ${{ env.PR_NUMBER }} edit-mode: replace @@ -238,6 +337,3 @@ jobs: :rocket: Access the deployment link [here](${{ env.PR_DEPLOYMENT_ACCESS_URL }}). :warning: This deployment will be deleted when the PR is closed. reactions: rocket - - env: - PR_DEPLOYMENT_ACCESS_URL: "https://pr${{ env.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}" diff --git a/scripts/deploy-pr.sh b/scripts/deploy-pr.sh new file mode 100755 index 0000000000000..cd847a5b25591 --- /dev/null +++ b/scripts/deploy-pr.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# Usage: ./deploy-pr.sh --skip-build +# deploys the current branch to a PR environment and posts login credentials to +# [#pr-deployments](https://codercom.slack.com/archives/C05DNE982E8) Slack channel +# if --skip-build is passed, the build step will be skipped and the last build image will be used + +set -euox pipefail + +branchName=$(gh pr view --json headRefName | jq -r .headRefName) + +if [[ "$branchName" == "main" ]]; then + prNumber=$(git rev-parse --short HEAD) +else + prNumber=$(gh pr view --json number | jq -r .number) +fi + +# if --skip-build is passed, the build job will be skipped and the last built image will be used +if [[ "$*" == *--skip-build* ]]; then + skipBuild=true + #check if the image exists + foundTag=$(curl -fsSL https://github.com/coder/coder/pkgs/container/coder-preview | grep -o "$prNumber" | head -n 1) + if [ -z "${foundTag}" ]; then + echo "Image not found" + echo "${prNumber} tag not found in ghcr.io/coder/coder-preview" + echo "Please remove --skip-build and try again" + exit 1 + fi +else + skipBuild=false +fi + +gh workflow run pr-deploy.yaml --ref "${branchName}" -f pr_number="${prNumber}" -f skip_build="${skipBuild}"