version: 2.1

aliases:
  # Workflow filters
  - &filter-only-release
    branches:
      only: chore/test-release-pipeline
    tags:
      only: /^v[0-9]+(\.[0-9]+){2}(-.+|[^-.]*)$/
  - &filter-only-master
    branches:
      only: master
  - &filter-release
    branches:
      only:
        - chore/test-release-pipeline
    tags:
      only: /^v[0-9]+(\.[0-9]+){2}(-.+|[^-.]*)$/

executors:
  base:
    docker:
      - image: cimg/base:stable
  node:
    docker:
      - image: cimg/node:12.18
  go:
    docker:
      - image: cimg/go:1.14
  e2e:
    docker:
      - image: srclosson/grafana-plugin-ci-e2e:latest
  grafana-build:
    docker:
      - image: grafana/build-container:1.2.27
  grafana-publish:
    docker:
      - image: grafana/grafana-ci-deploy:1.2.5
  windows-installer:
    docker:
      - image: grafana/wix-toolset-ci:v3

commands:
  install-grabpl:
    description: "Install Grafana build pipeline tool"
    steps:
      - run:
          name: "Install Grafana build pipeline tool"
          command: |
            VERSION=0.5.15
            curl -fLO https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v${VERSION}/grabpl
            chmod +x grabpl
            mv grabpl /tmp

jobs:
  build-backend:
    description: "Build a certain variant of Grafana back-end binaries"
    parameters:
      edition:
        type: string
      variant:
        type: string
    executor: grafana-build
    steps:
      - run:
          name: Exit if enterprise and forked PR
          command: |
            if [[ "<< parameters.edition >>" == "enterprise" && -n "$CIRCLE_PR_NUMBER" ]]; then
              echo "Nothing to do for forked PRs, so marking this step successful"
              circleci step halt
            fi
      - checkout
      - run:
          name: CI job started
          command: ./scripts/ci-job-started.sh
      - install-grabpl
      - run:
          name: Build Grafana backend
          command: |
            if [[ -n $CIRCLE_TAG ]]; then
              # A release build
              /tmp/grabpl build-backend --github-token "${GITHUB_GRAFANABOT_TOKEN}" --edition << parameters.edition >> \
                --variants << parameters.variant >> $CIRCLE_TAG
            elif [[ $CIRCLE_BRANCH == "chore/test-release-pipeline" ]]; then
              # We're testing the release pipeline
              /tmp/grabpl build-backend --github-token "${GITHUB_GRAFANABOT_TOKEN}" --edition << parameters.edition >> \
                --variants << parameters.variant >> v7.0.0-test
            fi
      - run:
          name: Move artifacts
          command: mkdir -p << parameters.edition >> && mv bin << parameters.edition >>/
      - persist_to_workspace:
          root: .
          paths:
            - << parameters.edition >>/bin/*
      - run:
          name: CI job failed
          command: ./scripts/ci-job-failed.sh
          when: on_fail
      - run:
          name: CI job succeeded
          command: ./scripts/ci-job-succeeded.sh
          when: on_success

  build-frontend:
    description: "Build Grafana front-end artifacts"
    parameters:
      edition:
        type: string
    resource_class: large
    executor: grafana-build
    steps:
      - run:
          name: Exit if enterprise and forked PR
          command: |
            if [[ "<< parameters.edition >>" == "enterprise" && -n "$CIRCLE_PR_NUMBER" ]]; then
              echo "Nothing to do for forked PRs, so marking this step successful"
              circleci step halt
            fi
      - checkout
      - restore_cache:
          keys:
            # Use own cache for front-end builds, since for some reason it can't be unpacked for parts of the
            # pipeline
            - v1-yarn-build-{{ checksum "yarn.lock" }}
            # Used if checksum fails
            - v1-yarn-build-
      - run:
          name: Increase node memory
          command: |
            export NODE_OPTIONS=--max_old_space_size=4096
      - run:
          name: CI job started
          command: ./scripts/ci-job-started.sh
      - install-grabpl
      - run:
          name: Install dependencies
          command: yarn install --frozen-lockfile --no-progress
      - save_cache:
          key: v1-yarn-build-{{ checksum "yarn.lock" }}
          paths:
            - node_modules
      - run:
          name: Build Grafana frontend
          command: |
            if [[ -n $CIRCLE_TAG ]]; then
              # A release build
              /tmp/grabpl build-frontend --github-token "${GITHUB_GRAFANABOT_TOKEN}" --edition << parameters.edition >> $CIRCLE_TAG
            elif [[ $CIRCLE_BRANCH == "chore/test-release-pipeline" ]]; then
              # We're testing the release pipeline
              /tmp/grabpl build-frontend --github-token "${GITHUB_GRAFANABOT_TOKEN}" --edition << parameters.edition >> 7.0.0-test
            fi
      - run:
          name: Move artifacts
          command: mkdir << parameters.edition >> && cp -r public << parameters.edition >>/
      - persist_to_workspace:
          root: .
          paths:
            - << parameters.edition >>/public/*
      - run:
          name: CI job failed
          command: ./scripts/ci-job-failed.sh
          when: on_fail
      - run:
          name: CI job succeeded
          command: ./scripts/ci-job-succeeded.sh
          when: on_success

  build-plugins:
    description: "Build internal Grafana plug-ins"
    parameters:
      edition:
        type: string
    executor: grafana-build
    steps:
      - run:
          name: Exit if enterprise and forked PR
          command: |
            if [[ "<< parameters.edition >>" == "enterprise" && -n "$CIRCLE_PR_NUMBER" ]]; then
              echo "Nothing to do for forked PRs, so marking this step successful"
              circleci step halt
            fi
      - checkout
      - run:
          name: CI job started
          command: ./scripts/ci-job-started.sh
      - install-grabpl
      - run:
          name: Build internal Grafana plug-ins
          command: |
            if [[ -n "$CIRCLE_PR_NUMBER" ]]; then
              # This is a forked PR, so don't sign as it requires an API secret
              /tmp/grabpl build-plugins --jobs 2 --edition << parameters.edition >>
            else
              export GRAFANA_API_KEY=$GRAFANA_COM_API_KEY
              /tmp/grabpl build-plugins --jobs 2 --edition << parameters.edition >> --sign --signing-admin
            fi
      - run:
          name: Move artifacts
          command: |
            mkdir -p << parameters.edition >>/plugins-bundled
            mv plugins-bundled/dist << parameters.edition >>/plugins-bundled/
      - persist_to_workspace:
          root: .
          paths:
            - << parameters.edition >>/plugins-bundled/dist/*
      - run:
          name: CI job failed
          command: ./scripts/ci-job-failed.sh
          when: on_fail
      - run:
          name: CI job succeeded
          command: ./scripts/ci-job-succeeded.sh
          when: on_success

  build-release-publisher:
    description: "Build release-publisher"
    executor: go
    steps:
      - checkout
      - run:
          name: CI job started
          command: ./scripts/ci-job-started.sh
      - run:
          name: Test and build Grafana.com release publisher
          command: |
            cd scripts/build/release_publisher
            go test .
            go build -o release_publisher .
      - persist_to_workspace:
          root: .
          paths:
            - scripts/build/release_publisher/release_publisher
      - run:
          name: CI job failed
          command: ./scripts/ci-job-failed.sh
          when: on_fail
      - run:
          name: CI job succeeded
          command: ./scripts/ci-job-succeeded.sh
          when: on_success

  shellcheck:
    executor: base
    steps:
      - checkout
      - run:
          name: CI job started
          command: ./scripts/ci-job-started.sh
      - install-grabpl
      - run:
          name: Install ShellCheck
          command: |
            VERSION=0.7.1
            CHKSUM=beca3d7819a6bdcfbd044576df4fc284053b48f468b2f03428fe66f4ceb2c05d9b5411357fa15003cb0311406c255084cf7283a3b8fce644c340c2f6aa910b9f
            curl -fLO http://storage.googleapis.com/grafana-downloads/ci-dependencies/shellcheck-v${VERSION}.linux.x86_64.tar.xz
            echo $CHKSUM shellcheck-v${VERSION}.linux.x86_64.tar.xz | sha512sum --check --strict --status
            tar xf shellcheck-v${VERSION}.linux.x86_64.tar.xz
            sudo mv shellcheck-v${VERSION}/shellcheck /usr/local/bin/
      - run:
          name: ShellCheck
          command: |
            /tmp/grabpl shellcheck
      - run:
          name: CI job failed
          command: ./scripts/ci-job-failed.sh
          when: on_fail
      - run:
          name: CI job succeeded
          command: ./scripts/ci-job-succeeded.sh
          when: on_success

  build-oss-windows-installer:
    executor: windows-installer
    steps:
      - attach_workspace:
          at: /tmp/workspace
      - checkout
      - run:
          name: CI job started
          command: ./scripts/ci-job-started.sh
      - run:
          name: Copy artifacts from workspace
          command: cp -r /tmp/workspace/oss/dist .
      - run:
          name: Build Windows installer
          command: ./scripts/build/ci-msi-build/ci-msi-build-oss.sh
      - run:
          name: Move artifacts
          command: mkdir oss && mv dist oss/
      - persist_to_workspace:
          root: .
          paths:
            - oss/dist/grafana-*.msi
            - oss/dist/grafana-*.msi.sha256
      - run:
          name: CI job failed
          command: ./scripts/ci-job-failed.sh
          when: on_fail
      - run:
          name: CI job succeeded
          command: ./scripts/ci-job-succeeded.sh
          when: on_success

  build-enterprise-windows-installer:
    executor: windows-installer
    steps:
      - run:
          name: Exit if forked PR
          command: |
            if [[ -n "$CIRCLE_PR_NUMBER" ]]; then
              echo "Nothing to do for forked PRs, so marking this step successful"
              circleci step halt
            fi
      - attach_workspace:
          at: /tmp/workspace
      - checkout
      - run:
          name: CI job started
          command: "./scripts/ci-job-started.sh"
      - run:
          name: Copy artifacts from workspace
          command: cp -r /tmp/workspace/enterprise/dist enterprise-dist
      - run:
          name: Build Windows installer
          command: ./scripts/build/ci-msi-build/ci-msi-build-ee.sh
      - run:
          name: Move artifacts
          command: mkdir enterprise && mv enterprise-dist enterprise/dist
      - persist_to_workspace:
          root: .
          paths:
            - enterprise/dist/grafana-*.msi
            - enterprise/dist/grafana-*.msi.sha256
      - run:
          name: CI job failed
          command: "./scripts/ci-job-failed.sh"
          when: on_fail
      - run:
          name: CI job succeeded
          command: "./scripts/ci-job-succeeded.sh"
          when: on_success

  release-next-packages:
    executor: grafana-build
    steps:
      - run:
          name: Exit if forked PR
          command: |
            if [[ -n "$CIRCLE_PR_NUMBER" ]]; then
              echo "Nothing to do for forked PRs, so marking this step successful"
              circleci step halt
            fi
      - checkout
      - run:
          name: CI job started
          command: ./scripts/ci-job-started.sh
      - run:
          name: Bootstrap lerna
          command: npx lerna bootstrap
      - run:
          name: npm - Prepare auth token
          command: "echo //registry.npmjs.org/:_authToken=$NPM_TOKEN >> ~/.npmrc"
      - run:
          name: Release next packages
          command: ./scripts/circle-release-next-packages.sh
      - run:
          name: CI job failed
          command: ./scripts/ci-job-failed.sh
          when: on_fail
      - run:
          name: CI job succeeded
          command: ./scripts/ci-job-succeeded.sh
          when: on_success

  package-oss:
    executor: grafana-build
    steps:
      - attach_workspace:
          at: /tmp/workspace
      - checkout
      - run:
          name: CI job started
          command: ./scripts/ci-job-started.sh
      - install-grabpl
      - run:
          name: Copy artifacts from workspace
          command: cp -r /tmp/workspace/oss/* .
      - run:
          name: Package Grafana
          command: |
            if [[ -n "$CIRCLE_PR_NUMBER" ]]; then
              echo Using test GPG key pair since building a forked PR
              source scripts/build/gpg-test-vars.sh
            fi

            # Necessary for signing bundled plugins
            export GRAFANA_API_KEY=$GRAFANA_COM_API_KEY
            if [[ -n $CIRCLE_TAG ]]; then
              # A release build
              /tmp/grabpl package --github-token "${GITHUB_GRAFANABOT_TOKEN}" --jobs 2 --edition oss --sign $CIRCLE_TAG
            elif [[ $CIRCLE_BRANCH == "chore/test-release-pipeline" ]]; then
              # We're testing the release pipeline
              /tmp/grabpl package --github-token "${GITHUB_GRAFANABOT_TOKEN}" --jobs 2 --edition oss --sign v7.0.0-test
            fi
      - run:
          name: Move artifacts
          command: |
            mkdir -p oss
            mv dist oss/
      - persist_to_workspace:
          root: .
          paths:
            - oss/dist/*
      - run:
          name: CI job failed
          command: ./scripts/ci-job-failed.sh
          when: on_fail
      - run:
          name: CI job succeeded
          command: ./scripts/ci-job-succeeded.sh
          when: on_success

  package-enterprise:
    executor: grafana-build
    steps:
      - run:
          name: Exit if forked PR
          command: |
            if [[ -n "$CIRCLE_PR_NUMBER" ]]; then
              echo "Nothing to do for forked PRs, so marking this step successful"
              circleci step halt
            fi
      - attach_workspace:
          at: /tmp/workspace
      - checkout
      - run:
          name: CI job started
          command: ./scripts/ci-job-started.sh
      - install-grabpl
      - run:
          name: Copy artifacts from workspace
          command: cp -r /tmp/workspace/enterprise/* .
      - run:
          name: Package Grafana
          command: |
            # Necessary for signing bundled plugins
            export GRAFANA_API_KEY=$GRAFANA_COM_API_KEY

            if [[ -n $CIRCLE_TAG ]]; then
              # A release build
              /tmp/grabpl package --github-token "${GITHUB_GRAFANABOT_TOKEN}" --jobs 2 --edition enterprise --sign $CIRCLE_TAG
            elif [[ $CIRCLE_BRANCH == "chore/test-release-pipeline" ]]; then
              # We're testing the release pipeline
              /tmp/grabpl package --github-token "${GITHUB_GRAFANABOT_TOKEN}" --jobs 2 --edition enterprise --sign v7.0.0-test
            fi
      - run:
          name: Move artifacts
          command: |
            mkdir -p enterprise
            mv dist enterprise/
      - persist_to_workspace:
          root: .
          paths:
            - enterprise/dist/*
      - run:
          name: CI job failed
          command: ./scripts/ci-job-failed.sh
          when: on_fail
      - run:
          name: CI job succeeded
          command: ./scripts/ci-job-succeeded.sh
          when: on_success

  publish-packages:
    description: "Publish packages"
    parameters:
      edition:
        type: string
    executor: grafana-publish
    steps:
      - run:
          name: Exit if forked PR
          command: |
            if [[ -n "$CIRCLE_PR_NUMBER" ]]; then
              echo "Nothing to do for forked PRs, so marking this step successful"
              circleci step halt
            fi
      - attach_workspace:
          at: /tmp/workspace
      - checkout
      - run:
          name: CI job started
          command: ./scripts/ci-job-started.sh
      - install-grabpl
      - run:
          name: Publish packages
          command: |
            cp -r /tmp/workspace/<< parameters.edition >>/dist .
            if [[ $CIRCLE_BRANCH == "chore/test-release-pipeline" ]]; then
              # We're testing the release pipeline
              /tmp/grabpl publish-packages --edition << parameters.edition >> \
                --deb-db-bucket grafana-testing-aptly-db --deb-repo-bucket grafana-testing-repo --packages-bucket \
                grafana-downloads-test --rpm-repo-bucket grafana-testing-repo --simulate-release
            else
              /tmp/grabpl publish-packages --edition << parameters.edition >>
            fi
      - run:
          name: CI job failed
          command: ./scripts/ci-job-failed.sh
          when: on_fail
      - run:
          name: CI job succeeded
          command: ./scripts/ci-job-succeeded.sh
          when: on_success

  publish-storybook:
    description: "Publish Storybook"
    executor: grafana-publish
    steps:
      - checkout
      - run:
          name: CI job started
          command: ./scripts/ci-job-started.sh
      - run:
          name: Publish Storybook
          command: |
            yarn install --frozen-lockfile --no-progress
            yarn storybook:build

            if [[ -n "$CIRCLE_PR_NUMBER" ]]; then
              echo "Nothing to do for forked PRs, so marking this step successful"
              circleci step halt
            fi

            if [[ $CIRCLE_BRANCH == "chore/test-release-pipeline" ]]; then
              # We're testing the release pipeline
              echo Testing release
            elif [[ -n $CIRCLE_TAG ]]; then
              echo $GCP_GRAFANA_UPLOAD_KEY > /tmp/gcpkey.json
              gcloud auth activate-service-account --key-file=/tmp/gcpkey.json
              gsutil -m rsync -d -r ./packages/grafana-ui/dist/storybook gs://grafana-storybook/latest
              gsutil -m rsync -d -r ./packages/grafana-ui/dist/storybook gs://grafana-storybook/$CIRCLE_TAG
            fi
      - run:
          name: CI job failed
          command: ./scripts/ci-job-failed.sh
          when: on_fail
      - run:
          name: CI job succeeded
          command: ./scripts/ci-job-succeeded.sh
          when: on_success

  build-docker-images:
    description: "Build/publish Docker images"
    parameters:
      edition:
        type: string
      ubuntu:
        type: boolean
    executor: base
    environment:
      # Required for building cross-platform images
      DOCKER_BUILDKIT: 1
    steps:
      - run:
          name: Exit if enterprise and forked PR
          command: |
            if [[ "<< parameters.edition >>" == "enterprise" && -n "$CIRCLE_PR_NUMBER" ]]; then
              echo "Nothing to do for forked PRs, so marking this step successful"
              circleci step halt
            fi
      - attach_workspace:
          at: /tmp/workspace
      - checkout
      - run:
          name: CI job started
          command: ./scripts/ci-job-started.sh
      - setup_remote_docker:
          # This version is necessary for building cross-platform images
          version: 18.09.3
      - install-grabpl
      - run:
          name: Install gcloud SDK
          command: |
            VERSION=298.0.0
            curl -fLO https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-${VERSION}-linux-x86_64.tar.gz
            echo 0d58f451331abf43d080fa997c8e580d64897627e30be74f6d8f983ccfabef1e \
              google-cloud-sdk-${VERSION}-linux-x86_64.tar.gz | sha256sum --check --strict --status
            tar xf google-cloud-sdk-${VERSION}-linux-x86_64.tar.gz
            ./google-cloud-sdk/install.sh -q
      # XXX: Is this necessary?
      - run: docker run --privileged linuxkit/binfmt:v0.6
      - run:
          name: Copy Grafana archives
          command: |
            cp -r /tmp/workspace/<< parameters.edition >>/dist .
      - run:
          name: Build Docker images
          command: |
            source google-cloud-sdk/path.bash.inc
            if [[ -n $CIRCLE_TAG || $CIRCLE_BRANCH == "chore/test-release-pipeline" ]]; then
              # It's a full build
              /tmp/grabpl build-docker --jobs 4 --edition << parameters.edition >> \
                --ubuntu=<< parameters.ubuntu >>
            else
              # We're testing a branch
              /tmp/grabpl build-docker --jobs 4 --edition << parameters.edition >> \
                --ubuntu=<< parameters.ubuntu >> --archs amd64
            fi
      - run:
          name: Exit if PR
          command: |
            if [[ -z $CIRCLE_TAG && $CIRCLE_BRANCH != "chore/test-release-pipeline" ]]; then
              echo "Nothing to do for PRs, so marking this step successful"
              circleci step halt
            fi
      - run:
          name: Publish Docker images
          command: |
            if [[ $CIRCLE_BRANCH == "chore/test-release-pipeline" ]]; then
              # We're testing the release pipeline
              /tmp/grabpl publish-docker --jobs 4 --edition << parameters.edition >> --ubuntu=<< parameters.ubuntu >> --dry-run
            elif [[ -n $CIRCLE_TAG ]]; then
              # This is a release
              /tmp/grabpl publish-docker --jobs 4 --edition << parameters.edition >> --ubuntu=<< parameters.ubuntu >>
            else
              /tmp/grabpl publish-docker --jobs 4 --edition << parameters.edition >> --ubuntu=<< parameters.ubuntu >>
            fi
      - run:
          name: CI job failed
          command: ./scripts/ci-job-failed.sh
          when: on_fail
      - run:
          name: CI job succeeded
          command: ./scripts/ci-job-succeeded.sh
          when: on_success

  end-to-end-tests:
    executor: e2e
    steps:
      - attach_workspace:
          at: /tmp/workspace
      - checkout
      - restore_cache:
          keys:
            - v2-yarn-{{ checksum "yarn.lock" }}
            # Used if checksum fails
            - v2-yarn-
      - run:
          name: yarn install
          command: yarn install --frozen-lockfile --no-progress
          no_output_timeout: 5m
      - save_cache:
          key: v2-yarn-{{ checksum "yarn.lock" }}
          paths:
            - node_modules
      - run:
          name: Copy artifacts from workspace
          command: |
            mkdir -p dist
            cp -r /tmp/workspace/oss/dist/*.tar.gz dist/
      - run:
          name: Start grafana-server
          command: ./e2e/start-server
          background: true
      - run:
          name: "Wait for Grafana to start"
          command: './e2e/wait-for-grafana'
      - run:
          name: Run end-to-end tests
          command: ./e2e/run-suite
          no_output_timeout: 5m
      - store_artifacts:
          path: e2e/suite1/screenshots
          destination: screenshots
      - store_artifacts:
          path: e2e/suite1/videos
          destination: output-videos
      - store_artifacts:
          path: e2e/tmp/data/log
          destination: logs

  mysql-integration-test:
    docker:
      - image: cimg/go:1.14
      - image: circleci/mysql:5.6-ram
        environment:
          MYSQL_ROOT_PASSWORD: rootpass
          MYSQL_DATABASE: grafana_tests
          MYSQL_USER: grafana
          MYSQL_PASSWORD: password
    steps:
      - checkout
      - run:
          name: ci job started
          command: "./scripts/ci-job-started.sh"
      - run: sudo apt update
      - run: sudo apt install -y default-mysql-client
      - run: dockerize -wait tcp://127.0.0.1:3306 -timeout 120s
      - run: cat devenv/docker/blocks/mysql_tests/setup.sql | mysql -h 127.0.0.1 -P 3306 -u root -prootpass
      - run:
          name: mysql integration tests
          command: "./scripts/circle-test-mysql.sh"
      - run:
          name: ci job failed
          command: "./scripts/ci-job-failed.sh"
          when: on_fail
      - run:
          name: ci job succeeded
          command: "./scripts/ci-job-succeeded.sh"
          when: on_success

  postgres-integration-test:
    docker:
      - image: cimg/go:1.14
      - image: circleci/postgres:9.3-ram
        environment:
          POSTGRES_USER: grafanatest
          POSTGRES_PASSWORD: grafanatest
          POSTGRES_DB: grafanatest
    steps:
      - checkout
      - run:
          name: ci job started
          command: "./scripts/ci-job-started.sh"
      - run: sudo apt update
      - run: sudo apt install -y postgresql-client
      - run: dockerize -wait tcp://127.0.0.1:5432 -timeout 120s
      - run: "PGPASSWORD=grafanatest psql -p 5432 -h 127.0.0.1 -U grafanatest -d grafanatest -f devenv/docker/blocks/postgres_tests/setup.sql"
      - run:
          name: postgres integration tests
          command: "./scripts/circle-test-postgres.sh"
      - run:
          name: ci job failed
          command: "./scripts/ci-job-failed.sh"
          when: on_fail
      - run:
          name: ci job succeeded
          command: "./scripts/ci-job-succeeded.sh"
          when: on_success

  codespell:
    docker:
      - image: cimg/python:3.8
    steps:
      - checkout
      - run:
          name: Install codespell
          command: "pip install codespell"
      - run:
          # Important: all words have to be in lowercase, and separated by "\n".
          name: exclude known exceptions
          command: 'echo -e "unknwon\nreferer\nerrorstring\neror\niam" > words_to_ignore.txt'
      - run:
          name: check documentation spelling errors
          command: "codespell -I ./words_to_ignore.txt docs/"

  lint-go:
    executor: go
    environment:
      # we need CGO because of go-sqlite3
      CGO_ENABLED: 1
      # Reduce golangci-lint memory usage (default is 100)
      GOGC: 20
    steps:
      - checkout
      - run:
          name: Install Go linters
          command: |
            pushd /tmp
            VERSION=1.28.0
            curl -fLO https://github.com/golangci/golangci-lint/releases/download/v${VERSION}/golangci-lint-${VERSION}-linux-amd64.tar.gz
            echo 179d34edf4baf6454a7081fbaaf74dc99397a3be8e1a535dee04d835a977bf76 \
              golangci-lint-${VERSION}-linux-amd64.tar.gz | sha256sum --check --strict --status
            tar -xf golangci-lint-${VERSION}-linux-amd64.tar.gz
            sudo mv golangci-lint-${VERSION}-linux-amd64/golangci-lint /usr/local/bin/
            popd
            make scripts/go/bin/revive
      - run:
          name: Lint Go
          command: |
            # To save memory, run in two batches
            golangci-lint run -v -j 4 --config scripts/go/configs/ci/.golangci.toml -E deadcode -E depguard -E dogsled \
              -E errcheck -E goconst -E golint -E gosec -E gosimple -E govet -E exportloopref -E whitespace \
              -E goprintffuncname ./pkg/...
            golangci-lint run -v -j 4 --config scripts/go/configs/ci/.golangci.toml -E ineffassign -E gocritic -E nakedret \
              -E rowserrcheck -E staticcheck -E structcheck -E typecheck -E unconvert -E unused -E varcheck ./pkg/...
            ./scripts/go/bin/revive -formatter stylish -config ./scripts/go/configs/revive.toml ./pkg/...
            ./scripts/go/bin/revive -formatter stylish -config ./scripts/go/configs/revive-strict.toml \
              -exclude ./pkg/plugins/backendplugin/pluginextensionv2/... \
              ./pkg/services/alerting/... \
              ./pkg/services/provisioning/datasources/... \
              ./pkg/services/provisioning/dashboards/... \
              ./pkg/services/provisioning/notifiers/... \
              ./pkg/services/provisioning/values/... \
              ./pkg/plugins/backendplugin/...
            ./scripts/tidy-check.sh

  test-frontend:
    parameters:
      edition:
        type: string
    executor: node
    steps:
      - checkout
      - run:
          name: CI job started
          command: "./scripts/ci-job-started.sh"
      - restore_cache:
          keys:
            - v2-yarn-{{ checksum "yarn.lock" }}
            # Used if checksum fails
            - v2-yarn-
      - run:
          name: yarn install
          command: "yarn install --frozen-lockfile --no-progress"
          no_output_timeout: 15m
      - save_cache:
          key: v2-yarn-{{ checksum "yarn.lock" }}
          paths:
            - node_modules
      - install-grabpl
      - run:
          name: frontend tests
          command: |
            if [[ -n $CIRCLE_TAG ]]; then
              # A release build
              ./scripts/circle-test-frontend.sh --edition << parameters.edition >> $CIRCLE_TAG
            elif [[ $CIRCLE_BRANCH == "chore/test-release-pipeline" ]]; then
              # We're testing the release pipeline
              ./scripts/circle-test-frontend.sh --edition << parameters.edition >> v7.0.0-test
            else
              # A master build
              ./scripts/circle-test-frontend.sh --edition << parameters.edition >> --build-id $CIRCLE_WORKFLOW_ID
            fi
      - store_test_results:
          path: reports/junit
      - run:
          name: CI job failed
          command: "./scripts/ci-job-failed.sh"
          when: on_fail
      - run:
          name: CI job succeeded
          command: "./scripts/ci-job-succeeded.sh"
          when: on_success

  test-backend:
    executor: go
    parameters:
      edition:
        type: string
    steps:
      - checkout
      - run:
          name: CI job started
          command: "./scripts/ci-job-started.sh"
      - install-grabpl
      - run:
          name: back-end tests
          command: |
            if [[ -n $CIRCLE_TAG ]]; then
              # A release build
              /tmp/grabpl test-backend --github-token "${GITHUB_GRAFANABOT_TOKEN}" --edition << parameters.edition >> \
                $CIRCLE_TAG
            elif [[ $CIRCLE_BRANCH == "chore/test-release-pipeline" ]]; then
              # We're testing the release pipeline
              /tmp/grabpl test-backend --github-token "${GITHUB_GRAFANABOT_TOKEN}" --edition << parameters.edition >> \
                v7.0.0-test
            else
              # A master build
              /tmp/grabpl test-backend --github-token "${GITHUB_GRAFANABOT_TOKEN}" --edition << parameters.edition >> \
                --build-id $CIRCLE_WORKFLOW_ID
            fi
      - run:
          name: CI job failed
          command: "./scripts/ci-job-failed.sh"
          when: on_fail
      - run:
          name: CI job succeeded
          command: "./scripts/ci-job-succeeded.sh"
          when: on_success

  build-docs-website:
    executor: base
    steps:
      - checkout
      - setup_remote_docker
      - run:
          name: CI job started
          command: "./scripts/ci-job-started.sh"
      - run:
          name: Build Grafana docs website
          command: |
            # Use latest revision here, since we want to catch if it breaks
            IMAGE=grafana/docs-base:latest

            # In order to copy sources into the remote container, we need to employ a trick of creating a container
            # with a volume, that we copy the sources into. Then, we launch the build container, with the volume
            # from the other container
            docker create -v /hugo/content/docs/grafana --name docs alpine:3.11 /bin/true
            docker cp ${PWD}/docs/sources docs:/hugo/content/docs/grafana/latest
            docker run --volumes-from docs $IMAGE /bin/bash -c 'make prod'
      - run:
          name: CI job failed
          command: "./scripts/ci-job-failed.sh"
          when: on_fail
      - run:
          name: CI job succeeded
          command: "./scripts/ci-job-succeeded.sh"
          when: on_success

  release-packages:
    executor: node
    steps:
      - run:
          name: Exit if forked PR
          command: |
            if [[ -n "$CIRCLE_PR_NUMBER" ]]; then
              echo "Nothing to do for forked PRs, so marking this step successful"
              circleci step halt
            fi
      - checkout
      - run:
          name: CI job started
          command: "./scripts/ci-job-started.sh"
      - run:
          name: Bootstrap lerna
          command: "npx lerna bootstrap"
      - run:
          name: npm - Prepare auth token
          command: "echo //registry.npmjs.org/:_authToken=$NPM_TOKEN >> ~/.npmrc"
      - run:
          name: Exit if release pipeline test
          command: |
            if [[ $CIRCLE_BRANCH == "chore/test-release-pipeline" ]]; then
              # We're testing the release pipeline
              echo "We're testing the release pipeline, so stopping before publishing"
              circleci step halt
            fi
      - run:
          name: Release packages
          command: ./scripts/build/release-packages.sh "${CIRCLE_TAG}"
      - run:
          name: CI job failed
          command: "./scripts/ci-job-failed.sh"
          when: on_fail
      - run:
          name: CI job succeeded
          command: "./scripts/ci-job-succeeded.sh"
          when: on_success

  scan-docker-image:
    description: "Scans a docker image for vulnerabilities using trivy"
    parameters:
      image:
        type: string
      tag:
        type: string
    docker:
      - image: circleci/buildpack-deps:stretch
    steps:
      - setup_remote_docker
      - restore_cache:
          key: vulnerability-db
      - run:
          name: Install trivy
          command: |
            VERSION=$(
                curl --silent "https://api.github.com/repos/aquasecurity/trivy/releases/latest" | \
                grep '"tag_name":' | \
                sed -E 's/.*"v([^"]+)".*/\1/'
            )

            wget https://github.com/aquasecurity/trivy/releases/download/v${VERSION}/trivy_${VERSION}_Linux-64bit.tar.gz
            tar zxvf trivy_${VERSION}_Linux-64bit.tar.gz
            sudo mv trivy /usr/local/bin
      - run:
          name: Clear trivy cache
          command: trivy --clear-cache
      - run:
          name: Scan Docker image for unkown/low/medium vulnerabilities
          command: trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM << parameters.image >>:<< parameters.tag >>
      - run:
          name: Scan Docker image for high/critical vulnerabilities
          command: trivy --exit-code 1 --severity HIGH,CRITICAL << parameters.image >>:<< parameters.tag >>
      - save_cache:
          key: vulnerability-db
          paths:
            - $HOME/.cache/trivy

workflows:
  build-pipeline:
    jobs:
      - build-backend:
          filters: *filter-release
          edition: oss
          variant: armv6
          name: build-oss-backend-armv6
          requires:
            - lint-go
      - build-backend:
          filters: *filter-release
          edition: oss
          variant: armv7
          name: build-oss-backend-armv7
          requires:
            - lint-go
      - build-backend:
          filters: *filter-release
          edition: oss
          variant: armv7-musl
          name: build-oss-backend-armv7-musl
          requires:
            - lint-go
      - build-backend:
          filters: *filter-release
          edition: oss
          variant: arm64
          name: build-oss-backend-arm64
          requires:
            - lint-go
      - build-backend:
          filters: *filter-release
          edition: oss
          variant: arm64-musl
          name: build-oss-backend-arm64-musl
          requires:
            - lint-go
      - build-backend:
          filters: *filter-release
          edition: oss
          variant: osx64
          name: build-oss-backend-osx64
          requires:
            - lint-go
      - build-backend:
          filters: *filter-release
          edition: oss
          variant: win64
          name: build-oss-backend-win64
          requires:
            - lint-go
      - build-backend:
          filters: *filter-release
          edition: oss
          variant: linux-x64
          name: build-oss-backend-linux-x64
          requires:
            - lint-go
      - build-backend:
          filters: *filter-release
          edition: oss
          variant: linux-x64-musl
          name: build-oss-backend-linux-x64-musl
          requires:
            - lint-go
      - build-frontend:
          filters: *filter-release
          name: build-oss-frontend
          edition: oss
      - build-plugins:
          filters: *filter-release
          name: build-oss-plugins
          edition: oss
          requires:
            - lint-go
      - build-backend:
          filters: *filter-release
          name: build-enterprise-backend-armv6
          edition: enterprise
          variant: armv6
          requires:
            - lint-go
      - build-backend:
          filters: *filter-release
          name: build-enterprise-backend-armv7
          edition: enterprise
          variant: armv7
          requires:
            - lint-go
      - build-backend:
          filters: *filter-release
          name: build-enterprise-backend-armv7-musl
          edition: enterprise
          variant: armv7-musl
          requires:
            - lint-go
      - build-backend:
          filters: *filter-release
          name: build-enterprise-backend-arm64
          edition: enterprise
          variant: arm64
          requires:
            - lint-go
      - build-backend:
          filters: *filter-release
          name: build-enterprise-backend-arm64-musl
          edition: enterprise
          variant: arm64-musl
          requires:
            - lint-go
      - build-backend:
          filters: *filter-release
          name: build-enterprise-backend-osx64
          edition: enterprise
          variant: osx64
          requires:
            - lint-go
      - build-backend:
          filters: *filter-release
          name: build-enterprise-backend-win64
          edition: enterprise
          variant: win64
          requires:
            - lint-go
      - build-backend:
          filters: *filter-release
          name: build-enterprise-backend-linux-x64
          edition: enterprise
          variant: linux-x64
          requires:
            - lint-go
      - build-backend:
          filters: *filter-release
          name: build-enterprise-backend-linux-x64-musl
          edition: enterprise
          variant: linux-x64-musl
          requires:
            - lint-go
      - build-frontend:
          filters: *filter-release
          name: build-enterprise-frontend
          edition: enterprise
      - build-plugins:
          filters: *filter-release
          name: build-enterprise-plugins
          edition: enterprise
          requires:
            - lint-go
      - build-release-publisher:
          filters: *filter-release
      - codespell:
          filters: *filter-release
      - lint-go:
          filters: *filter-release
      - shellcheck:
          filters: *filter-release
      - test-backend:
          filters: *filter-release
          name: test-oss-backend
          edition: oss
          requires:
            - lint-go
      - test-backend:
          filters: *filter-release
          name: test-enterprise-backend
          edition: enterprise
          requires:
            - lint-go
      - test-frontend:
          filters: *filter-release
          name: test-oss-frontend
          edition: oss
      - test-frontend:
          filters: *filter-release
          name: test-enterprise-frontend
          edition: enterprise
      - mysql-integration-test:
          filters: *filter-release
          requires:
            - lint-go
            - test-oss-backend
            - test-oss-frontend
      - postgres-integration-test:
          filters: *filter-release
          requires:
            - lint-go
            - test-oss-backend
            - test-oss-frontend
      - package-oss:
          filters: *filter-release
          requires:
            - build-oss-backend-armv6
            - build-oss-backend-armv7
            - build-oss-backend-armv7-musl
            - build-oss-backend-arm64
            - build-oss-backend-arm64-musl
            - build-oss-backend-osx64
            - build-oss-backend-win64
            - build-oss-backend-linux-x64
            - build-oss-backend-linux-x64-musl
            - build-oss-frontend
            - test-oss-backend
            - test-oss-frontend
            - codespell
            - shellcheck
            - build-oss-plugins
      - package-enterprise:
          filters: *filter-release
          requires:
            - build-enterprise-backend-armv6
            - build-enterprise-backend-armv7
            - build-enterprise-backend-armv7-musl
            - build-enterprise-backend-arm64
            - build-enterprise-backend-arm64-musl
            - build-enterprise-backend-osx64
            - build-enterprise-backend-win64
            - build-enterprise-backend-linux-x64
            - build-enterprise-backend-linux-x64-musl
            - build-enterprise-frontend
            - test-enterprise-backend
            - test-enterprise-frontend
            - codespell
            - shellcheck
            - build-enterprise-plugins
      - build-oss-windows-installer:
          filters: *filter-release
          requires:
            - package-oss
      - build-enterprise-windows-installer:
          filters: *filter-release
          requires:
            - package-enterprise
      - release-packages:
          filters: *filter-only-release
          requires:
            - end-to-end-tests
            - mysql-integration-test
            - postgres-integration-test
      - publish-packages:
          filters: *filter-release
          name: publish-oss-packages
          edition: oss
          requires:
            - package-oss
            - build-oss-windows-installer
            - end-to-end-tests
            - mysql-integration-test
            - postgres-integration-test
            - build-release-publisher
      - publish-packages:
          filters: *filter-release
          name: publish-enterprise-packages
          edition: enterprise
          requires:
            - package-enterprise
            - build-enterprise-windows-installer
            - end-to-end-tests
            - mysql-integration-test
            - postgres-integration-test
            - build-release-publisher
      - publish-storybook:
          filters: *filter-release
          requires:
            - test-oss-backend
            - test-oss-frontend
      - build-docker-images:
          filters: *filter-release
          name: build-oss-docker-images
          edition: oss
          ubuntu: false
          requires:
            - end-to-end-tests
            - mysql-integration-test
            - postgres-integration-test
            - package-oss
            - build-oss-windows-installer
      - build-docker-images:
          filters: *filter-release
          name: build-oss-ubuntu-docker-images
          edition: oss
          ubuntu: true
          requires:
            - end-to-end-tests
            - mysql-integration-test
            - postgres-integration-test
            - package-oss
            - build-oss-windows-installer
      - build-docker-images:
          filters: *filter-release
          name: build-enterprise-docker-images
          edition: enterprise
          ubuntu: false
          requires:
            - end-to-end-tests
            - mysql-integration-test
            - postgres-integration-test
            - package-enterprise
            - build-enterprise-windows-installer
      - build-docker-images:
          filters: *filter-release
          name: build-enterprise-ubuntu-docker-images
          edition: enterprise
          ubuntu: true
          requires:
            - end-to-end-tests
            - mysql-integration-test
            - postgres-integration-test
            - package-enterprise
            - build-enterprise-windows-installer
      - end-to-end-tests:
          filters: *filter-release
          requires:
            - package-oss

  nightly:
    triggers:
      - schedule:
          cron: "0 0 * * *"
          filters: *filter-only-master
    jobs:
      - scan-docker-image:
          matrix:
            parameters:
              image: [grafana/grafana, grafana/grafana-enterprise]
              tag: [latest, master, latest-ubuntu, master-ubuntu]
