name: coder

on:
  push:
    branches:
      - main
    tags:
      - "*"

  pull_request:

  workflow_dispatch:

permissions:
  actions: none
  checks: none
  contents: read
  deployments: none
  issues: none
  packages: none
  pull-requests: none
  repository-projects: none
  security-events: none
  statuses: none

# Cancel in-progress runs for pull requests when developers push
# additional changes
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
  style-lint-golangci:
    name: style/lint/golangci
    timeout-minutes: 5
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-go@v3
        with:
          go-version: "~1.18"
      - name: golangci-lint
        uses: golangci/golangci-lint-action@v3.2.0
        with:
          version: v1.46.0

  style-lint-shellcheck:
    name: style/lint/shellcheck
    timeout-minutes: 5
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run ShellCheck
        uses: ludeeus/action-shellcheck@1.1.0
        env:
          SHELLCHECK_OPTS: --external-sources
        with:
          ignore: node_modules

  style-lint-typescript:
    name: "style/lint/typescript"
    timeout-minutes: 5
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Cache Node
        id: cache-node
        uses: actions/cache@v3
        with:
          path: |
            **/node_modules
            .eslintcache
          key: js-${{ runner.os }}-test-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            js-${{ runner.os }}-

      - name: Install node_modules
        run: ./scripts/yarn_install.sh

      - name: "yarn lint"
        run: yarn lint
        working-directory: site

  gen:
    name: "style/gen"
    timeout-minutes: 5
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Cache Node
        id: cache-node
        uses: actions/cache@v3
        with:
          path: |
            **/node_modules
            .eslintcache
          key: js-${{ runner.os }}-test-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            js-${{ runner.os }}-

      - name: Install node_modules
        run: ./scripts/yarn_install.sh

      - name: Install Protoc
        uses: arduino/setup-protoc@v1
        with:
          version: "3.20.0"
      - uses: actions/setup-go@v3
        with:
          go-version: "~1.18"
      - run: curl -sSL
          https://github.com/kyleconroy/sqlc/releases/download/v1.13.0/sqlc_1.13.0_linux_amd64.tar.gz
          | sudo tar -C /usr/bin -xz sqlc

      - run: go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.26
      - run: go install storj.io/drpc/cmd/protoc-gen-go-drpc@v0.0.26
      - run: go install golang.org/x/tools/cmd/goimports@latest
      - run: "make --output-sync -j -B gen"
      - run: ./scripts/check_unstaged.sh

  style-fmt:
    name: "style/fmt"
    runs-on: ubuntu-latest
    timeout-minutes: 5
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 0
          submodules: true

      - name: Cache Node
        id: cache-node
        uses: actions/cache@v3
        with:
          path: |
            **/node_modules
            .eslintcache
          key: js-${{ runner.os }}-test-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            js-${{ runner.os }}-

      - name: Install node_modules
        run: ./scripts/yarn_install.sh

      - name: Install shfmt
        run: go install mvdan.cc/sh/v3/cmd/shfmt@v3.5.0

      - run: |
          export PATH=${PATH}:$(go env GOPATH)/bin
          make --output-sync -j -B fmt

  test-go:
    name: "test/go"
    runs-on: ${{ matrix.os }}
    timeout-minutes: 20
    strategy:
      matrix:
        os:
          - ubuntu-latest
          - macos-latest
          - windows-2022
    steps:
      - uses: actions/checkout@v3

      - uses: actions/setup-go@v3
        with:
          go-version: "~1.18"

      - name: Echo Go Cache Paths
        id: go-cache-paths
        run: |
          echo "::set-output name=go-build::$(go env GOCACHE)"
          echo "::set-output name=go-mod::$(go env GOMODCACHE)"

      - name: Go Build Cache
        uses: actions/cache@v3
        with:
          path: ${{ steps.go-cache-paths.outputs.go-build }}
          key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}

      - name: Go Mod Cache
        uses: actions/cache@v3
        with:
          path: ${{ steps.go-cache-paths.outputs.go-mod }}
          key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}

      - name: Install goreleaser
        uses: jaxxstorm/action-install-gh-release@v1.7.1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          repo: gotestyourself/gotestsum
          tag: v1.7.0

      - uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: 1.1.2
          terraform_wrapper: false

      - name: Test with Mock Database
        shell: bash
        env:
          GOCOUNT: ${{ runner.os == 'Windows' && 1 || 2 }}
          GOMAXPROCS: ${{ runner.os == 'Windows' && 1 || 2 }}
        run: gotestsum --junitfile="gotests.xml" --packages="./..." --
          -covermode=atomic -coverprofile="gotests.coverage"
          -coverpkg=./...,github.com/coder/coder/codersdk
          -timeout=3m -count=$GOCOUNT -short -failfast

      - name: Upload DataDog Trace
        if: always() && github.actor != 'dependabot[bot]' && !github.event.pull_request.head.repo.fork
        env:
          DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }}
          DD_DATABASE: fake
          DD_CATEGORY: unit
          GIT_COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
        run: go run scripts/datadog-cireport/main.go gotests.xml

      - uses: codecov/codecov-action@v3
        if: github.actor != 'dependabot[bot]' && !github.event.pull_request.head.repo.fork
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: ./gotests.coverage
          flags: unittest-go-${{ matrix.os }}
          # this flakes and sometimes fails the build 
          fail_ci_if_error: false

  test-go-postgres:
    name: "test/go/postgres"
    runs-on: ubuntu-latest
    timeout-minutes: 20
    steps:
      - uses: actions/checkout@v3

      - uses: actions/setup-go@v3
        with:
          go-version: "~1.18"

      - name: Echo Go Cache Paths
        id: go-cache-paths
        run: |
          echo "::set-output name=go-build::$(go env GOCACHE)"
          echo "::set-output name=go-mod::$(go env GOMODCACHE)"

      - name: Go Build Cache
        uses: actions/cache@v3
        with:
          path: ${{ steps.go-cache-paths.outputs.go-build }}
          key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}

      - name: Go Mod Cache
        uses: actions/cache@v3
        with:
          path: ${{ steps.go-cache-paths.outputs.go-mod }}
          key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}

      - name: Install goreleaser
        uses: jaxxstorm/action-install-gh-release@v1.7.1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          repo: gotestyourself/gotestsum
          tag: v1.7.0

      - uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: 1.1.2
          terraform_wrapper: false

      - name: Start PostgreSQL Database
        env:
          POSTGRES_PASSWORD: postgres
          POSTGRES_USER: postgres
          POSTGRES_DB: postgres
          PGDATA: /tmp
        run: |
          docker run \
            -e POSTGRES_PASSWORD=postgres \
            -e POSTGRES_USER=postgres \
            -e POSTGRES_DB=postgres \
            -e PGDATA=/tmp \
            -p 5432:5432 \
            -d postgres:11 \
            -c shared_buffers=1GB \
            -c max_connections=1000
          while ! pg_isready -h 127.0.0.1
          do
              echo "$(date) - waiting for database to start"
              sleep 0.5
          done

      - name: Test with PostgreSQL Database
        run: "make test-postgres"

      - name: Upload DataDog Trace
        if: always() && github.actor != 'dependabot[bot]' && !github.event.pull_request.head.repo.fork
        env:
          DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }}
          DD_DATABASE: postgresql
          GIT_COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
        run: go run scripts/datadog-cireport/main.go gotests.xml

      - uses: codecov/codecov-action@v3
        if: github.actor != 'dependabot[bot]' && !github.event.pull_request.head.repo.fork
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: ./gotests.coverage
          flags: unittest-go-postgres-${{ matrix.os }}
          # this flakes and sometimes fails the build 
          fail_ci_if_error: false

  deploy:
    name: "deploy"
    runs-on: ubuntu-latest
    timeout-minutes: 20
    if: github.ref == 'refs/heads/main' && !github.event.pull_request.head.repo.fork
    permissions:
      contents: read
      id-token: write
    steps:
      - uses: actions/checkout@v3

      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v0
        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@v0

      - uses: actions/setup-go@v3
        with:
          go-version: "~1.18"

      - name: Echo Go Cache Paths
        id: go-cache-paths
        run: |
          echo "::set-output name=go-build::$(go env GOCACHE)"
          echo "::set-output name=go-mod::$(go env GOMODCACHE)"

      - name: Go Build Cache
        uses: actions/cache@v3
        with:
          path: ${{ steps.go-cache-paths.outputs.go-build }}
          key: ${{ runner.os }}-release-go-build-${{ hashFiles('**/go.sum') }}

      - name: Go Mod Cache
        uses: actions/cache@v3
        with:
          path: ${{ steps.go-cache-paths.outputs.go-mod }}
          key: ${{ runner.os }}-release-go-mod-${{ hashFiles('**/go.sum') }}

      - name: Cache Node
        id: cache-node
        uses: actions/cache@v3
        with:
          path: |
            **/node_modules
            .eslintcache
          key: js-${{ runner.os }}-release-node-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            js-${{ runner.os }}-

      - uses: goreleaser/goreleaser-action@v3
        with:
          install-only: true

      - name: Build site
        run: make -B site/out/index.html

      - name: Build Release
        uses: goreleaser/goreleaser-action@v3
        with:
          version: latest
          args: release --snapshot --rm-dist --skip-sign

      - name: Install Release
        run: |
          gcloud config set project coder-dogfood
          gcloud config set compute/zone us-central1-a
          gcloud compute scp ./dist/coder_*_linux_amd64.deb coder:/tmp/coder.deb
          gcloud compute ssh coder -- sudo dpkg -i --force-confdef /tmp/coder.deb
          gcloud compute ssh coder -- sudo systemctl daemon-reload

      - name: Start
        run: gcloud compute ssh coder -- sudo service coder restart

      - uses: actions/upload-artifact@v3
        with:
          name: coder
          path: |
            ./dist/coder_*_linux_amd64.tar.gz
            ./dist/coder_*_windows_amd64.zip
          retention-days: 7

  test-js:
    name: "test/js"
    runs-on: ubuntu-latest
    timeout-minutes: 20
    steps:
      - uses: actions/checkout@v3

      - name: Cache Node
        id: cache-node
        uses: actions/cache@v3
        with:
          path: |
            **/node_modules
            .eslintcache
          key: js-${{ runner.os }}-test-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            js-${{ runner.os }}-

      # Go is required for uploading the test results to datadog
      - uses: actions/setup-go@v3
        with:
          go-version: "~1.18"

      - uses: actions/setup-node@v3
        with:
          node-version: "14"

      - name: Install node_modules
        run: ./scripts/yarn_install.sh

      - run: yarn test:coverage
        working-directory: site

      - uses: codecov/codecov-action@v3
        if: github.actor != 'dependabot[bot]' && !github.event.pull_request.head.repo.fork
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: ./site/coverage/lcov.info
          flags: unittest-js
          # this flakes and sometimes fails the build 
          fail_ci_if_error: false

      - name: Upload DataDog Trace
        if: always() && github.actor != 'dependabot[bot]' && !github.event.pull_request.head.repo.fork
        env:
          DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }}
          DD_CATEGORY: unit
          GIT_COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
        run: go run scripts/datadog-cireport/main.go site/test-results/junit.xml

  test-e2e:
    name: "test/e2e/${{ matrix.os }}"
    runs-on: ${{ matrix.os }}
    timeout-minutes: 20
    strategy:
      matrix:
        os:
          - ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Cache Node
        id: cache-node
        uses: actions/cache@v3
        with:
          path: |
            **/node_modules
            .eslintcache
          key: js-${{ runner.os }}-test-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            js-${{ runner.os }}-

      # Go is required for uploading the test results to datadog
      - uses: actions/setup-go@v3
        with:
          go-version: "~1.18"

      - uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: 1.1.2
          terraform_wrapper: false

      - uses: actions/setup-node@v3
        with:
          node-version: "14"

      - uses: goreleaser/goreleaser-action@v3
        with:
          install-only: true

      - name: Echo Go Cache Paths
        id: go-cache-paths
        run: |
          echo "::set-output name=go-build::$(go env GOCACHE)"
          echo "::set-output name=go-mod::$(go env GOMODCACHE)"

      - name: Go Build Cache
        uses: actions/cache@v3
        with:
          path: ${{ steps.go-cache-paths.outputs.go-build }}
          key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}

      - name: Go Mod Cache
        uses: actions/cache@v3
        with:
          path: ${{ steps.go-cache-paths.outputs.go-mod }}
          key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}

      - name: Build
        run: |
          make -B site/out/index.html

      - run: yarn playwright:install
        working-directory: site

      - run: yarn playwright:install-deps
        working-directory: site

      - run: yarn playwright:test
        env:
          DEBUG: pw:api
        working-directory: site

      - name: Upload DataDog Trace
        if: always() && github.actor != 'dependabot[bot]' && runner.os == 'Linux' && !github.event.pull_request.head.repo.fork
        env:
          DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }}
          DD_CATEGORY: e2e
          GIT_COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
        run: go run scripts/datadog-cireport/main.go site/test-results/junit.xml
