name: ci on: push: branches: - main pull_request: workflow_dispatch: permissions: contents: read # Cancel in-progress runs for pull requests when developers push # additional changes concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} jobs: changes: runs-on: ubuntu-latest outputs: docs-only: ${{ steps.filter.outputs.docs_count == steps.filter.outputs.all_count }} docs: ${{ steps.filter.outputs.docs }} go: ${{ steps.filter.outputs.go }} ts: ${{ steps.filter.outputs.ts }} k8s: ${{ steps.filter.outputs.k8s }} ci: ${{ steps.filter.outputs.ci }} db: ${{ steps.filter.outputs.db }} gomod: ${{ steps.filter.outputs.gomod }} offlinedocs-only: ${{ steps.filter.outputs.offlinedocs_count == steps.filter.outputs.all_count }} offlinedocs: ${{ steps.filter.outputs.offlinedocs }} tailnet-integration: ${{ steps.filter.outputs.tailnet-integration }} steps: - name: Harden Runner uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: egress-policy: audit - name: Checkout uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 1 # For pull requests it's not necessary to checkout the code - name: check changed files uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 id: filter with: filters: | all: - "**" docs: - "docs/**" - "README.md" - "examples/web-server/**" - "examples/monitoring/**" - "examples/lima/**" db: - "**.sql" - "coderd/database/**" go: - "**.sql" - "**.go" - "**.golden" - "go.mod" - "go.sum" # Other non-Go files that may affect Go code: - "**.rego" - "**.sh" - "**.tpl" - "**.gotmpl" - "**.gotpl" - "Makefile" - "site/static/error.html" # Main repo directories for completeness in case other files are # touched: - "agent/**" - "cli/**" - "cmd/**" - "coderd/**" - "enterprise/**" - "examples/**" - "helm/**" - "provisioner/**" - "provisionerd/**" - "provisionersdk/**" - "pty/**" - "scaletest/**" - "tailnet/**" - "testutil/**" gomod: - "go.mod" - "go.sum" ts: - "site/**" - "Makefile" k8s: - "helm/**" - "scripts/Dockerfile" - "scripts/Dockerfile.base" - "scripts/helm.sh" ci: - ".github/actions/**" - ".github/workflows/ci.yaml" offlinedocs: - "offlinedocs/**" tailnet-integration: - "tailnet/**" - "go.mod" - "go.sum" - id: debug run: | echo "${{ toJSON(steps.filter )}}" # Disabled due to instability. See: https://github.com/coder/coder/issues/14553 # Re-enable once the flake hash calculation is stable. # update-flake: # needs: changes # if: needs.changes.outputs.gomod == 'true' # runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} # steps: # - name: Checkout # uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 # with: # fetch-depth: 1 # # See: https://github.com/stefanzweifel/git-auto-commit-action?tab=readme-ov-file#commits-made-by-this-action-do-not-trigger-new-workflow-runs # token: ${{ secrets.CDRCI_GITHUB_TOKEN }} # - name: Setup Go # uses: ./.github/actions/setup-go # - name: Update Nix Flake SRI Hash # run: ./scripts/update-flake.sh # # auto update flake for dependabot # - uses: stefanzweifel/git-auto-commit-action@8621497c8c39c72f3e2a999a26b4ca1b5058a842 # v5.0.1 # if: github.actor == 'dependabot[bot]' # with: # # Allows dependabot to still rebase! # commit_message: "[dependabot skip] Update Nix Flake SRI Hash" # commit_user_name: "dependabot[bot]" # commit_user_email: "49699333+dependabot[bot]@users.noreply.github.com>" # commit_author: "dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>" # # require everyone else to update it themselves # - name: Ensure No Changes # if: github.actor != 'dependabot[bot]' # run: git diff --exit-code lint: needs: changes if: needs.changes.outputs.offlinedocs-only == 'false' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main' runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} steps: - name: Harden Runner uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: egress-policy: audit - name: Checkout uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 1 - name: Setup Node uses: ./.github/actions/setup-node - name: Setup Go uses: ./.github/actions/setup-go - name: Get golangci-lint cache dir run: | linter_ver=$(egrep -o 'GOLANGCI_LINT_VERSION=\S+' dogfood/contents/Dockerfile | cut -d '=' -f 2) go install github.com/golangci/golangci-lint/cmd/golangci-lint@v$linter_ver dir=$(golangci-lint cache status | awk '/Dir/ { print $2 }') echo "LINT_CACHE_DIR=$dir" >> $GITHUB_ENV - name: golangci-lint cache uses: actions/cache@2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2 # v4.1.0 with: path: | ${{ env.LINT_CACHE_DIR }} key: golangci-lint-${{ runner.os }}-${{ hashFiles('**/*.go') }} restore-keys: | golangci-lint-${{ runner.os }}- # Check for any typos - name: Check for typos uses: crate-ci/typos@11ca4583f2f3f74c7e7785c0ecb20fe2c99a4308 # v1.29.5 with: config: .github/workflows/typos.toml - name: Fix the typos if: ${{ failure() }} run: | echo "::notice:: you can automatically fix typos from your CLI: cargo install typos-cli typos -c .github/workflows/typos.toml -w" # Needed for helm chart linting - name: Install helm uses: azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v4.2.0 with: version: v3.9.2 - name: make lint run: | make --output-sync=line -j lint - name: Check workflow files run: | bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) 1.7.4 ./actionlint -color -shellcheck= -ignore "set-output" shell: bash - name: Check for unstaged files run: | rm -f ./actionlint ./typos ./scripts/check_unstaged.sh shell: bash gen: timeout-minutes: 8 runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} if: always() steps: - name: Harden Runner uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: egress-policy: audit - name: Checkout uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 1 - 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: Setup Terraform uses: ./.github/actions/setup-tf - name: go install tools run: | go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.30 go install storj.io/drpc/cmd/protoc-gen-go-drpc@v0.0.34 go install golang.org/x/tools/cmd/goimports@latest go install github.com/mikefarah/yq/v4@v4.44.3 go install go.uber.org/mock/mockgen@v0.5.0 - name: Install Protoc run: | mkdir -p /tmp/proto pushd /tmp/proto curl -L -o protoc.zip https://github.com/protocolbuffers/protobuf/releases/download/v23.4/protoc-23.4-linux-x86_64.zip unzip protoc.zip cp -r ./bin/* /usr/local/bin cp -r ./include /usr/local/bin/include popd - name: make gen # no `-j` flag as `make` fails with: # coderd/rbac/object_gen.go:1:1: syntax error: package statement must be first run: "make --output-sync -B gen" - name: make update-golden-files run: | make clean/golden-files # Notifications require DB, we could start a DB instance here but # let's just restore for now. git checkout -- coderd/notifications/testdata/rendered-templates # As above, skip `-j` flag. make --output-sync -B update-golden-files - name: Check for unstaged files run: ./scripts/check_unstaged.sh fmt: needs: changes if: needs.changes.outputs.offlinedocs-only == 'false' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main' runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} timeout-minutes: 7 steps: - name: Harden Runner uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: egress-policy: audit - name: Checkout uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 1 - name: Setup Node uses: ./.github/actions/setup-node # Use default Go version - name: Setup Go uses: ./.github/actions/setup-go - name: Install shfmt run: go install mvdan.cc/sh/v3/cmd/shfmt@v3.7.0 - name: make fmt run: | export PATH=${PATH}:$(go env GOPATH)/bin make --output-sync -j -B fmt - name: Check for unstaged files run: ./scripts/check_unstaged.sh test-go: runs-on: ${{ matrix.os == 'ubuntu-latest' && github.repository_owner == 'coder' && 'depot-ubuntu-22.04-4' || matrix.os == 'macos-latest' && github.repository_owner == 'coder' && 'depot-macos-latest' || matrix.os == 'windows-2022' && github.repository_owner == 'coder' && 'windows-latest-16-cores' || matrix.os }} needs: changes if: needs.changes.outputs.go == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main' timeout-minutes: 20 strategy: fail-fast: false matrix: os: - ubuntu-latest - macos-latest - windows-2022 steps: - name: Harden Runner uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: egress-policy: audit - name: Checkout uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 1 - name: Setup Go uses: ./.github/actions/setup-go - name: Setup Terraform uses: ./.github/actions/setup-tf - name: Test with Mock Database id: test shell: bash run: | # if macOS, install google-chrome for scaletests. As another concern, # should we really have this kind of external dependency requirement # on standard CI? if [ "${{ matrix.os }}" == "macos-latest" ]; then brew install google-chrome fi # By default Go will use the number of logical CPUs, which # is a fine default. PARALLEL_FLAG="" # macOS will output "The default interactive shell is now zsh" # intermittently in CI... if [ "${{ matrix.os }}" == "macos-latest" ]; then touch ~/.bash_profile && echo "export BASH_SILENCE_DEPRECATION_WARNING=1" >> ~/.bash_profile fi export TS_DEBUG_DISCO=true gotestsum --junitfile="gotests.xml" --jsonfile="gotests.json" \ --packages="./..." -- $PARALLEL_FLAG -short -failfast - name: Upload test stats to Datadog timeout-minutes: 1 continue-on-error: true uses: ./.github/actions/upload-datadog if: success() || failure() with: api-key: ${{ secrets.DATADOG_API_KEY }} # We don't run the full test-suite for Windows & MacOS, so we just run the CLI tests on every PR. # We run the test suite in test-go-pg, including CLI. test-cli: runs-on: ${{ matrix.os == 'macos-latest' && github.repository_owner == 'coder' && 'depot-macos-latest' || matrix.os == 'windows-2022' && github.repository_owner == 'coder' && 'windows-latest-16-cores' || matrix.os }} needs: changes if: needs.changes.outputs.go == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main' strategy: matrix: os: - macos-latest - windows-2022 steps: - name: Harden Runner uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: egress-policy: audit - name: Checkout uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 1 - name: Setup Go uses: ./.github/actions/setup-go - name: Setup Terraform uses: ./.github/actions/setup-tf # Sets up the ImDisk toolkit for Windows and creates a RAM disk on drive R:. - name: Setup ImDisk if: runner.os == 'Windows' uses: ./.github/actions/setup-imdisk - name: Test CLI env: TS_DEBUG_DISCO: "true" LC_CTYPE: "en_US.UTF-8" LC_ALL: "en_US.UTF-8" shell: bash run: | # By default Go will use the number of logical CPUs, which # is a fine default. PARALLEL_FLAG="" make test-cli - name: Upload test stats to Datadog timeout-minutes: 1 continue-on-error: true uses: ./.github/actions/upload-datadog if: success() || failure() with: api-key: ${{ secrets.DATADOG_API_KEY }} test-go-pg: runs-on: ${{ matrix.os == 'ubuntu-latest' && github.repository_owner == 'coder' && 'depot-ubuntu-22.04-4' || matrix.os }} needs: changes if: needs.changes.outputs.go == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main' # This timeout must be greater than the timeout set by `go test` in # `make test-postgres` to ensure we receive a trace of running # goroutines. Setting this to the timeout +5m should work quite well # even if some of the preceding steps are slow. timeout-minutes: 25 strategy: matrix: os: - ubuntu-latest steps: - name: Harden Runner uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: egress-policy: audit - name: Checkout uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 1 - name: Setup Go uses: ./.github/actions/setup-go - name: Setup Terraform uses: ./.github/actions/setup-tf # Sets up the ImDisk toolkit for Windows and creates a RAM disk on drive R:. - name: Setup ImDisk if: runner.os == 'Windows' uses: ./.github/actions/setup-imdisk - name: Test with PostgreSQL Database env: POSTGRES_VERSION: "13" TS_DEBUG_DISCO: "true" LC_CTYPE: "en_US.UTF-8" LC_ALL: "en_US.UTF-8" shell: bash run: | # By default Go will use the number of logical CPUs, which # is a fine default. PARALLEL_FLAG="" make test-postgres - name: Upload test stats to Datadog timeout-minutes: 1 continue-on-error: true uses: ./.github/actions/upload-datadog if: success() || failure() with: api-key: ${{ secrets.DATADOG_API_KEY }} # NOTE: this could instead be defined as a matrix strategy, but we want to # only block merging if tests on postgres 13 fail. Using a matrix strategy # here makes the check in the above `required` job rather complicated. test-go-pg-16: runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} needs: - changes if: needs.changes.outputs.go == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main' # This timeout must be greater than the timeout set by `go test` in # `make test-postgres` to ensure we receive a trace of running # goroutines. Setting this to the timeout +5m should work quite well # even if some of the preceding steps are slow. timeout-minutes: 25 steps: - name: Harden Runner uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: egress-policy: audit - name: Checkout uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 1 - name: Setup Go uses: ./.github/actions/setup-go - name: Setup Terraform uses: ./.github/actions/setup-tf - name: Test with PostgreSQL Database env: POSTGRES_VERSION: "16" TS_DEBUG_DISCO: "true" run: | make test-postgres - name: Upload test stats to Datadog timeout-minutes: 1 continue-on-error: true uses: ./.github/actions/upload-datadog if: success() || failure() with: api-key: ${{ secrets.DATADOG_API_KEY }} test-go-race: runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-16' || 'ubuntu-latest' }} needs: changes if: needs.changes.outputs.go == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main' timeout-minutes: 25 steps: - name: Harden Runner uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: egress-policy: audit - name: Checkout uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 1 - name: Setup Go uses: ./.github/actions/setup-go - name: Setup Terraform uses: ./.github/actions/setup-tf # We run race tests with reduced parallelism because they use more CPU and we were finding # instances where tests appear to hang for multiple seconds, resulting in flaky tests when # short timeouts are used. # c.f. discussion on https://github.com/coder/coder/pull/15106 - name: Run Tests run: | gotestsum --junitfile="gotests.xml" -- -race -parallel 4 -p 4 ./... - name: Upload test stats to Datadog timeout-minutes: 1 continue-on-error: true uses: ./.github/actions/upload-datadog if: always() with: api-key: ${{ secrets.DATADOG_API_KEY }} test-go-race-pg: runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-16' || 'ubuntu-latest' }} needs: changes if: needs.changes.outputs.go == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main' timeout-minutes: 25 steps: - name: Harden Runner uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: egress-policy: audit - name: Checkout uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 1 - name: Setup Go uses: ./.github/actions/setup-go - name: Setup Terraform uses: ./.github/actions/setup-tf # We run race tests with reduced parallelism because they use more CPU and we were finding # instances where tests appear to hang for multiple seconds, resulting in flaky tests when # short timeouts are used. # c.f. discussion on https://github.com/coder/coder/pull/15106 - name: Run Tests env: POSTGRES_VERSION: "16" run: | make test-postgres-docker DB=ci gotestsum --junitfile="gotests.xml" -- -race -parallel 4 -p 4 ./... - name: Upload test stats to Datadog timeout-minutes: 1 continue-on-error: true uses: ./.github/actions/upload-datadog if: always() with: api-key: ${{ secrets.DATADOG_API_KEY }} # Tailnet integration tests only run when the `tailnet` directory or `go.sum` # and `go.mod` are changed. These tests are to ensure we don't add regressions # to tailnet, either due to our code or due to updating dependencies. # # These tests are skipped in the main go test jobs because they require root # and mess with networking. test-go-tailnet-integration: runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} needs: changes # Unnecessary to run on main for now if: needs.changes.outputs.tailnet-integration == 'true' || needs.changes.outputs.ci == 'true' timeout-minutes: 20 steps: - name: Harden Runner uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: egress-policy: audit - name: Checkout uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 1 - name: Setup Go uses: ./.github/actions/setup-go # Used by some integration tests. - name: Install Nginx run: sudo apt-get update && sudo apt-get install -y nginx - name: Run Tests run: make test-tailnet-integration test-js: runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} needs: changes if: needs.changes.outputs.ts == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main' timeout-minutes: 20 steps: - name: Harden Runner uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: egress-policy: audit - name: Checkout uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 1 - name: Setup Node uses: ./.github/actions/setup-node - run: pnpm test:ci --max-workers $(nproc) working-directory: site test-e2e: runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-4' || 'ubuntu-latest' }} needs: changes strategy: fail-fast: false matrix: variant: - premium: false name: test-e2e - premium: true name: test-e2e-premium # Skip test-e2e on forks as they don't have access to CI secrets if: (needs.changes.outputs.go == 'true' || needs.changes.outputs.ts == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main') && !(github.event.pull_request.head.repo.fork) timeout-minutes: 20 name: ${{ matrix.variant.name }} steps: - name: Harden Runner uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: egress-policy: audit - name: Checkout uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 1 - name: Setup Node uses: ./.github/actions/setup-node - name: Setup Go uses: ./.github/actions/setup-go # Assume that the checked-in versions are up-to-date - run: make gen/mark-fresh name: make gen - run: make site/e2e/bin/coder name: make coder - run: pnpm build env: NODE_OPTIONS: ${{ github.repository_owner == 'coder' && '--max_old_space_size=8192' || '' }} working-directory: site - run: pnpm playwright:install working-directory: site # Run tests that don't require a premium license without a premium license - run: pnpm playwright:test --forbid-only --workers 1 if: ${{ !matrix.variant.premium }} env: DEBUG: pw:api working-directory: site # Run all of the tests with a premium license - run: pnpm playwright:test --forbid-only --workers 1 if: ${{ matrix.variant.premium }} env: DEBUG: pw:api CODER_E2E_LICENSE: ${{ secrets.CODER_E2E_LICENSE }} CODER_E2E_REQUIRE_PREMIUM_TESTS: "1" working-directory: site - name: Upload Playwright Failed Tests if: always() && github.actor != 'dependabot[bot]' && runner.os == 'Linux' && !github.event.pull_request.head.repo.fork uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 with: name: failed-test-videos${{ matrix.variant.premium && '-premium' || '' }} path: ./site/test-results/**/*.webm retention-days: 7 - name: Upload pprof dumps if: always() && github.actor != 'dependabot[bot]' && runner.os == 'Linux' && !github.event.pull_request.head.repo.fork uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 with: name: debug-pprof-dumps${{ matrix.variant.premium && '-premium' || '' }} path: ./site/test-results/**/debug-pprof-*.txt retention-days: 7 chromatic: # REMARK: this is only used to build storybook and deploy it to Chromatic. runs-on: ubuntu-latest needs: changes if: needs.changes.outputs.ts == 'true' || needs.changes.outputs.ci == 'true' steps: - name: Harden Runner uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: egress-policy: audit - name: Checkout uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: # Required by Chromatic for build-over-build history, otherwise we # only get 1 commit on shallow checkout. fetch-depth: 0 - name: Setup Node uses: ./.github/actions/setup-node # This step is not meant for mainline because any detected changes to # storybook snapshots will require manual approval/review in order for # the check to pass. This is desired in PRs, but not in mainline. - name: Publish to Chromatic (non-mainline) if: github.ref != 'refs/heads/main' && github.repository_owner == 'coder' uses: chromaui/action@30b6228aa809059d46219e0f556752e8672a7e26 # v11.11.0 env: NODE_OPTIONS: "--max_old_space_size=4096" STORYBOOK: true with: # Do a fast, testing build for change previews buildScriptName: "storybook:ci" exitOnceUploaded: true # This will prevent CI from failing when Chromatic detects visual changes exitZeroOnChanges: true # Chromatic states its fine to make this token public. See: # https://www.chromatic.com/docs/github-actions#forked-repositories projectToken: 695c25b6cb65 workingDir: "./site" storybookBaseDir: "./site" # Prevent excessive build runs on minor version changes skip: "@(renovate/**|dependabot/**)" # Run TurboSnap to trace file dependencies to related stories # and tell chromatic to only take snapshots of relevant stories onlyChanged: true # Avoid uploading single files, because that's very slow zip: true # This is a separate step for mainline only that auto accepts and changes # instead of holding CI up. Since we squash/merge, this is defensive to # avoid the same changeset from requiring review once squashed into # main. Chromatic is supposed to be able to detect that we use squash # commits, but it's good to be defensive in case, otherwise CI remains # infinitely "in progress" in mainline unless we re-review each build. - name: Publish to Chromatic (mainline) if: github.ref == 'refs/heads/main' && github.repository_owner == 'coder' uses: chromaui/action@30b6228aa809059d46219e0f556752e8672a7e26 # v11.11.0 env: NODE_OPTIONS: "--max_old_space_size=4096" STORYBOOK: true with: autoAcceptChanges: true # This will prevent CI from failing when Chromatic detects visual changes exitZeroOnChanges: true # Do a full build with documentation for mainline builds buildScriptName: "storybook:build" projectToken: 695c25b6cb65 workingDir: "./site" storybookBaseDir: "./site" # Run TurboSnap to trace file dependencies to related stories # and tell chromatic to only take snapshots of relevant stories onlyChanged: true # Avoid uploading single files, because that's very slow zip: true offlinedocs: name: offlinedocs needs: changes runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} if: needs.changes.outputs.offlinedocs == 'true' || needs.changes.outputs.ci == 'true' || needs.changes.outputs.docs == 'true' steps: - name: Harden Runner uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: egress-policy: audit - name: Checkout uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: # 0 is required here for version.sh to work. fetch-depth: 0 - name: Setup Node uses: ./.github/actions/setup-node with: directory: offlinedocs - name: Install Protoc run: | mkdir -p /tmp/proto pushd /tmp/proto curl -L -o protoc.zip https://github.com/protocolbuffers/protobuf/releases/download/v23.4/protoc-23.4-linux-x86_64.zip unzip protoc.zip cp -r ./bin/* /usr/local/bin cp -r ./include /usr/local/bin/include popd - name: Setup Go uses: ./.github/actions/setup-go - name: Install go tools run: | go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.30 go install storj.io/drpc/cmd/protoc-gen-go-drpc@v0.0.34 go install golang.org/x/tools/cmd/goimports@latest go install github.com/mikefarah/yq/v4@v4.44.3 go install go.uber.org/mock/mockgen@v0.5.0 - name: Setup sqlc uses: ./.github/actions/setup-sqlc - name: Format run: | cd offlinedocs pnpm format:check - name: Lint run: | cd offlinedocs pnpm lint - name: Build # no `-j` flag as `make` fails with: # coderd/rbac/object_gen.go:1:1: syntax error: package statement must be first run: | make build/coder_docs_"$(./scripts/version.sh)".tgz required: runs-on: ubuntu-latest needs: - fmt - lint - gen - test-go - test-go-pg - test-go-race - test-go-race-pg - test-js - test-e2e - offlinedocs - sqlc-vet # Allow this job to run even if the needed jobs fail, are skipped or # cancelled. if: always() steps: - name: Harden Runner uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: egress-policy: audit - name: Ensure required checks run: | echo "Checking required checks" echo "- fmt: ${{ needs.fmt.result }}" echo "- lint: ${{ needs.lint.result }}" echo "- gen: ${{ needs.gen.result }}" echo "- test-go: ${{ needs.test-go.result }}" echo "- test-go-pg: ${{ needs.test-go-pg.result }}" echo "- test-go-race: ${{ needs.test-go-race.result }}" echo "- test-go-race-pg: ${{ needs.test-go-race-pg.result }}" echo "- test-js: ${{ needs.test-js.result }}" echo "- test-e2e: ${{ needs.test-e2e.result }}" echo "- offlinedocs: ${{ needs.offlinedocs.result }}" echo # We allow skipped jobs to pass, but not failed or cancelled jobs. if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" || "${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then echo "One of the required checks has failed or has been cancelled" exit 1 fi echo "Required checks have passed" # Builds the dylibs and upload it as an artifact so it can be embedded in the main build build-dylib: needs: changes # We always build the dylibs on Go changes to verify we're not merging unbuildable code, # but they need only be signed and uploaded on coder/coder main. if: needs.changes.outputs.go == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main' runs-on: ${{ github.repository_owner == 'coder' && 'depot-macos-latest' || 'macos-latest' }} steps: - name: Harden Runner uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: egress-policy: audit - name: Checkout uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 0 - name: Setup build tools run: | brew install bash gnu-getopt make echo "$(brew --prefix bash)/bin" >> $GITHUB_PATH echo "$(brew --prefix gnu-getopt)/bin" >> $GITHUB_PATH echo "$(brew --prefix make)/libexec/gnubin" >> $GITHUB_PATH - name: Setup Go uses: ./.github/actions/setup-go - name: Install rcodesign if: ${{ github.repository_owner == 'coder' && github.ref == 'refs/heads/main' }} run: | set -euo pipefail wget -O /tmp/rcodesign.tar.gz https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign%2F0.22.0/apple-codesign-0.22.0-macos-universal.tar.gz sudo tar -xzf /tmp/rcodesign.tar.gz \ -C /usr/local/bin \ --strip-components=1 \ apple-codesign-0.22.0-macos-universal/rcodesign rm /tmp/rcodesign.tar.gz - name: Setup Apple Developer certificate and API key if: ${{ github.repository_owner == 'coder' && github.ref == 'refs/heads/main' }} run: | set -euo pipefail touch /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8} chmod 600 /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8} echo "$AC_CERTIFICATE_P12_BASE64" | base64 -d > /tmp/apple_cert.p12 echo "$AC_CERTIFICATE_PASSWORD" > /tmp/apple_cert_password.txt echo "$AC_APIKEY_P8_BASE64" | base64 -d > /tmp/apple_apikey.p8 env: AC_CERTIFICATE_P12_BASE64: ${{ secrets.AC_CERTIFICATE_P12_BASE64 }} AC_CERTIFICATE_PASSWORD: ${{ secrets.AC_CERTIFICATE_PASSWORD }} AC_APIKEY_P8_BASE64: ${{ secrets.AC_APIKEY_P8_BASE64 }} - name: Build dylibs run: | set -euxo pipefail go mod download make gen/mark-fresh make build/coder-dylib env: CODER_SIGN_DARWIN: ${{ github.ref == 'refs/heads/main' && '1' || '0' }} AC_CERTIFICATE_FILE: /tmp/apple_cert.p12 AC_CERTIFICATE_PASSWORD_FILE: /tmp/apple_cert_password.txt - name: Upload build artifacts if: ${{ github.repository_owner == 'coder' && github.ref == 'refs/heads/main' }} uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: dylibs path: | ./build/*.h ./build/*.dylib retention-days: 7 - name: Delete Apple Developer certificate and API key if: ${{ github.repository_owner == 'coder' && github.ref == 'refs/heads/main' }} run: rm -f /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8} build: # This builds and publishes ghcr.io/coder/coder-preview:main for each commit # to main branch. needs: - changes - build-dylib if: github.ref == 'refs/heads/main' && needs.changes.outputs.docs-only == 'false' && !github.event.pull_request.head.repo.fork runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-22.04' }} permissions: packages: write # Needed to push images to ghcr.io env: DOCKER_CLI_EXPERIMENTAL: "enabled" outputs: IMAGE: ghcr.io/coder/coder-preview:${{ steps.build-docker.outputs.tag }} steps: - name: Harden Runner uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: egress-policy: audit - name: Checkout uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 0 - name: GHCR Login uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Setup Node uses: ./.github/actions/setup-node - name: Setup Go uses: ./.github/actions/setup-go - name: Install nfpm run: go install github.com/goreleaser/nfpm/v2/cmd/nfpm@v2.35.1 - name: Install zstd run: sudo apt-get install -y zstd - name: Download dylibs uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: name: dylibs path: ./build - name: Insert dylibs run: | mv ./build/*amd64.dylib ./site/out/bin/coder-vpn-darwin-amd64.dylib mv ./build/*arm64.dylib ./site/out/bin/coder-vpn-darwin-arm64.dylib mv ./build/*arm64.h ./site/out/bin/coder-vpn-darwin-dylib.h - name: Build run: | set -euxo pipefail go mod download version="$(./scripts/version.sh)" tag="main-$(echo "$version" | sed 's/+/-/g')" echo "tag=$tag" >> $GITHUB_OUTPUT make gen/mark-fresh make -j \ build/coder_linux_{amd64,arm64,armv7} \ build/coder_"$version"_windows_amd64.zip \ build/coder_"$version"_linux_amd64.{tar.gz,deb} - name: Build Linux Docker images id: build-docker env: CODER_IMAGE_BASE: ghcr.io/coder/coder-preview CODER_IMAGE_TAG_PREFIX: main DOCKER_CLI_EXPERIMENTAL: "enabled" run: | set -euxo pipefail # build Docker images for each architecture version="$(./scripts/version.sh)" tag="main-$(echo "$version" | sed 's/+/-/g')" echo "tag=$tag" >> $GITHUB_OUTPUT # build images for each architecture # note: omitting the -j argument to avoid race conditions when pushing make build/coder_"$version"_linux_{amd64,arm64,armv7}.tag # only push if we are on main branch if [ "${{ github.ref }}" == "refs/heads/main" ]; then # build and push multi-arch manifest, this depends on the other images # being pushed so will automatically push them # note: omitting the -j argument to avoid race conditions when pushing make push/build/coder_"$version"_linux_{amd64,arm64,armv7}.tag # Define specific tags tags=("$tag" "main" "latest") # Create and push a multi-arch manifest for each tag # we are adding `latest` tag and keeping `main` for backward # compatibality for t in "${tags[@]}"; do ./scripts/build_docker_multiarch.sh \ --push \ --target "ghcr.io/coder/coder-preview:$t" \ --version $version \ $(cat build/coder_"$version"_linux_{amd64,arm64,armv7}.tag) done fi - name: Prune old images if: github.ref == 'refs/heads/main' uses: vlaurin/action-ghcr-prune@0cf7d39f88546edd31965acba78cdcb0be14d641 # v0.6.0 with: token: ${{ secrets.GITHUB_TOKEN }} organization: coder container: coder-preview keep-younger-than: 7 # days keep-tags: latest keep-tags-regexes: ^pr prune-tags-regexes: | ^main- ^v prune-untagged: true - name: Upload build artifacts if: github.ref == 'refs/heads/main' uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 with: name: coder path: | ./build/*.zip ./build/*.tar.gz ./build/*.deb retention-days: 7 deploy: name: "deploy" runs-on: ubuntu-latest timeout-minutes: 30 needs: - changes - build if: | github.ref == 'refs/heads/main' && !github.event.pull_request.head.repo.fork && needs.changes.outputs.docs-only == 'false' permissions: contents: read id-token: write steps: - name: Harden Runner uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: egress-policy: audit - name: Checkout uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 0 - name: Authenticate to Google Cloud uses: google-github-actions/auth@71f986410dfbc7added4569d411d040a91dc6935 # v2.1.8 with: workload_identity_provider: projects/573722524737/locations/global/workloadIdentityPools/github/providers/github service_account: coder-ci@coder-dogfood.iam.gserviceaccount.com - name: Set up Google Cloud SDK uses: google-github-actions/setup-gcloud@77e7a554d41e2ee56fc945c52dfd3f33d12def9a # v2.1.4 - name: Set up Flux CLI uses: fluxcd/flux2/action@5350425cdcd5fa015337e09fa502153c0275bd4b # v2.4.0 with: # Keep this and the github action up to date with the version of flux installed in dogfood cluster version: "2.2.1" - name: Get Cluster Credentials uses: google-github-actions/get-gke-credentials@7a108e64ed8546fe38316b4086e91da13f4785e1 # v2.3.1 with: cluster_name: dogfood-v2 location: us-central1-a project_id: coder-dogfood-v2 - name: Reconcile Flux run: | set -euxo pipefail flux --namespace flux-system reconcile source git flux-system flux --namespace flux-system reconcile source git coder-main flux --namespace flux-system reconcile kustomization flux-system flux --namespace flux-system reconcile kustomization coder flux --namespace flux-system reconcile source chart coder-coder flux --namespace flux-system reconcile source chart coder-coder-provisioner flux --namespace coder reconcile helmrelease coder flux --namespace coder reconcile helmrelease coder-provisioner # Just updating Flux is usually not enough. The Helm release may get # redeployed, but unless something causes the Deployment to update the # pods won't be recreated. It's important that the pods get recreated, # since we use `imagePullPolicy: Always` to ensure we're running the # latest image. - name: Rollout Deployment run: | set -euxo pipefail kubectl --namespace coder rollout restart deployment/coder kubectl --namespace coder rollout status deployment/coder kubectl --namespace coder rollout restart deployment/coder-provisioner kubectl --namespace coder rollout status deployment/coder-provisioner deploy-wsproxies: runs-on: ubuntu-latest needs: build if: github.ref == 'refs/heads/main' && !github.event.pull_request.head.repo.fork steps: - name: Harden Runner uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: egress-policy: audit - name: Checkout uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 0 - name: Setup flyctl uses: superfly/flyctl-actions/setup-flyctl@fc53c09e1bc3be6f54706524e3b82c4f462f77be # v1.5 - name: Deploy workspace proxies run: | flyctl deploy --image "$IMAGE" --app paris-coder --config ./.github/fly-wsproxies/paris-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_PARIS" --yes flyctl deploy --image "$IMAGE" --app sydney-coder --config ./.github/fly-wsproxies/sydney-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_SYDNEY" --yes flyctl deploy --image "$IMAGE" --app sao-paulo-coder --config ./.github/fly-wsproxies/sao-paulo-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_SAO_PAULO" --yes flyctl deploy --image "$IMAGE" --app jnb-coder --config ./.github/fly-wsproxies/jnb-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_JNB" --yes env: FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} IMAGE: ${{ needs.build.outputs.IMAGE }} TOKEN_PARIS: ${{ secrets.FLY_PARIS_CODER_PROXY_SESSION_TOKEN }} TOKEN_SYDNEY: ${{ secrets.FLY_SYDNEY_CODER_PROXY_SESSION_TOKEN }} TOKEN_SAO_PAULO: ${{ secrets.FLY_SAO_PAULO_CODER_PROXY_SESSION_TOKEN }} TOKEN_JNB: ${{ secrets.FLY_JNB_CODER_PROXY_SESSION_TOKEN }} # sqlc-vet runs a postgres docker container, runs Coder migrations, and then # runs sqlc-vet to ensure all queries are valid. This catches any mistakes # in migrations or sqlc queries that makes a query unable to be prepared. sqlc-vet: runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} needs: changes if: needs.changes.outputs.db == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main' steps: - name: Harden Runner uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: egress-policy: audit - name: Checkout uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 1 # We need golang to run the migration main.go - name: Setup Go uses: ./.github/actions/setup-go - name: Setup sqlc uses: ./.github/actions/setup-sqlc - name: Setup and run sqlc vet run: | make sqlc-vet notify-slack-on-failure: needs: - required runs-on: ubuntu-latest if: failure() && github.ref == 'refs/heads/main' steps: - name: Send Slack notification run: | curl -X POST -H 'Content-type: application/json' \ --data '{ "blocks": [ { "type": "header", "text": { "type": "plain_text", "text": "❌ CI Failure in main", "emoji": true } }, { "type": "section", "fields": [ { "type": "mrkdwn", "text": "*Workflow:*\n${{ github.workflow }}" }, { "type": "mrkdwn", "text": "*Committer:*\n${{ github.actor }}" }, { "type": "mrkdwn", "text": "*Commit:*\n${{ github.sha }}" } ] }, { "type": "section", "text": { "type": "mrkdwn", "text": "*View failure:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Click here>" } } ] }' ${{ secrets.CI_FAILURE_SLACK_WEBHOOK }}