name: Integration tests
on: 
  push:
    branches:
      - 'master'
      - 'release-*'
      - '!release-1.4'
      - '!release-1.5'
  pull_request:
    branches:
      - 'master'

env:
  # Golang version to use across CI steps
  GOLANG_VERSION: '1.18'

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  check-go:
    name: Ensure Go modules synchronicity
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
      - name: Setup Golang
        uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
        with:
          go-version: ${{ env.GOLANG_VERSION }}
      - name: Download all Go modules
        run: |
          go mod download
      - name: Check for tidyness of go.mod and go.sum
        run: |
          go mod tidy
          git diff --exit-code -- .

  build-go:
    name: Build & cache Go code
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
      - name: Setup Golang
        uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
        with:
          go-version: ${{ env.GOLANG_VERSION }}
      - name: Restore go build cache
        uses: actions/cache@58c146cc91c5b9e778e71775dfe9bf1442ad9a12 # v3.2.3
        with:
          path: ~/.cache/go-build
          key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
      - name: Download all Go modules
        run: |
          go mod download
      - name: Compile all packages
        run: make build-local

  lint-go:
    permissions:
      contents: read  # for actions/checkout to fetch code
      pull-requests: read  # for golangci/golangci-lint-action to fetch pull requests
    name: Lint Go code
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
      - name: Run golangci-lint
        uses: golangci/golangci-lint-action@0ad9a0988b3973e851ab0a07adf248ec2e100376 # v3.3.1
        with:
          version: v1.46.2
          args: --timeout 10m --exclude SA5011 --verbose

  test-go:
    name: Run unit tests for Go packages
    runs-on: ubuntu-latest
    needs:
      - build-go
    steps:
      - name: Create checkout directory
        run: mkdir -p ~/go/src/github.com/argoproj
      - name: Checkout code
        uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
      - name: Create symlink in GOPATH
        run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
      - name: Setup Golang
        uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
        with:
          go-version: ${{ env.GOLANG_VERSION }}
      - name: Install required packages
        run: |
          sudo apt-get install git -y
      - name: Switch to temporal branch so we re-attach head
        run: |
          git switch -c temporal-pr-branch
          git status
      - name: Fetch complete history for blame information
        run: |
          git fetch --prune --no-tags --depth=1 origin +refs/heads/*:refs/remotes/origin/*
      - name: Add ~/go/bin to PATH
        run: |
          echo "/home/runner/go/bin" >> $GITHUB_PATH
      - name: Add /usr/local/bin to PATH
        run: |
          echo "/usr/local/bin" >> $GITHUB_PATH
      - name: Restore go build cache
        uses: actions/cache@58c146cc91c5b9e778e71775dfe9bf1442ad9a12 # v3.2.3
        with:
          path: ~/.cache/go-build
          key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
      - name: Install all tools required for building & testing
        run: |
          make install-test-tools-local
      - name: Setup git username and email
        run: |
          git config --global user.name "John Doe"
          git config --global user.email "john.doe@example.com"
      - name: Download and vendor all required packages
        run: |
          go mod download
      - name: Run all unit tests
        run: make test-local
      - name: Generate code coverage artifacts
        uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
        with:
          name: code-coverage
          path: coverage.out
      - name: Generate test results artifacts
        uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
        with:
          name: test-results
          path: test-results/

  test-go-race:
    name: Run unit tests with -race, for Go packages
    runs-on: ubuntu-latest
    needs:
      - build-go
    steps:
      - name: Create checkout directory
        run: mkdir -p ~/go/src/github.com/argoproj
      - name: Checkout code
        uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
      - name: Create symlink in GOPATH
        run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
      - name: Setup Golang
        uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
        with:
          go-version: ${{ env.GOLANG_VERSION }}
      - name: Install required packages
        run: |
          sudo apt-get install git -y
      - name: Switch to temporal branch so we re-attach head
        run: |
          git switch -c temporal-pr-branch
          git status
      - name: Fetch complete history for blame information
        run: |
          git fetch --prune --no-tags --depth=1 origin +refs/heads/*:refs/remotes/origin/*
      - name: Add ~/go/bin to PATH
        run: |
          echo "/home/runner/go/bin" >> $GITHUB_PATH
      - name: Add /usr/local/bin to PATH
        run: |
          echo "/usr/local/bin" >> $GITHUB_PATH
      - name: Restore go build cache
        uses: actions/cache@58c146cc91c5b9e778e71775dfe9bf1442ad9a12 # v3.2.3
        with:
          path: ~/.cache/go-build
          key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
      - name: Install all tools required for building & testing
        run: |
          make install-test-tools-local
      - name: Setup git username and email
        run: |
          git config --global user.name "John Doe"
          git config --global user.email "john.doe@example.com"
      - name: Download and vendor all required packages
        run: |
          go mod download
      - name: Run all unit tests
        run: make test-race-local
      - name: Generate test results artifacts
        uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
        with:
          name: race-results
          path: test-results/

  codegen:
    name: Check changes to generated code
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
      - name: Setup Golang
        uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
        with:
          go-version: ${{ env.GOLANG_VERSION }}
      - name: Create symlink in GOPATH
        run: |
          mkdir -p ~/go/src/github.com/argoproj
          cp -a ../argo-cd ~/go/src/github.com/argoproj
      - name: Add ~/go/bin to PATH
        run: |
          echo "/home/runner/go/bin" >> $GITHUB_PATH
      - name: Add /usr/local/bin to PATH
        run: |
          echo "/usr/local/bin" >> $GITHUB_PATH
      - name: Download & vendor dependencies
        run: |
          # We need to vendor go modules for codegen yet
          go mod download
          go mod vendor -v
        working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
      - name: Install toolchain for codegen
        run: |
          make install-codegen-tools-local
          make install-go-tools-local
        working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
      - name: Initialize local Helm
        run: |
          helm2 init --client-only
      - name: Run codegen
        run: |
          set -x
          export GOPATH=$(go env GOPATH)
          git checkout -- go.mod go.sum
          make codegen-local
        working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
      - name: Check nothing has changed
        run: |
          set -xo pipefail
          git diff --exit-code -- . ':!go.sum' ':!go.mod' ':!assets/swagger.json' | tee codegen.patch
        working-directory: /home/runner/go/src/github.com/argoproj/argo-cd

  build-ui:
    name: Build, test & lint UI code
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
      - name: Setup NodeJS
        uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
        with:
          node-version: '12.18.4'
      - name: Restore node dependency cache
        id: cache-dependencies
        uses: actions/cache@58c146cc91c5b9e778e71775dfe9bf1442ad9a12 # v3.2.3
        with:
          path: ui/node_modules
          key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
      - name: Install node dependencies
        run: |
          cd ui && yarn install --frozen-lockfile --ignore-optional --non-interactive
      - name: Build UI code
        run: |
          yarn test
          yarn build
        env:
          NODE_ENV: production
          NODE_ONLINE_ENV: online
          HOST_ARCH: amd64
        working-directory: ui/
      - name: Run ESLint
        run: yarn lint
        working-directory: ui/

  analyze:
    name: Process & analyze test artifacts
    runs-on: ubuntu-latest
    needs:
      - test-go
      - build-ui
    env:
      sonar_secret: ${{ secrets.SONAR_TOKEN }}
    steps:
      - name: Checkout code
        uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
        with:
          fetch-depth: 0
      - name: Restore node dependency cache
        id: cache-dependencies
        uses: actions/cache@58c146cc91c5b9e778e71775dfe9bf1442ad9a12 # v3.2.3
        with:
          path: ui/node_modules
          key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
      - name: Remove other node_modules directory
        run: |
          rm -rf ui/node_modules/argo-ui/node_modules
      - name: Create test-results directory
        run: |
          mkdir -p test-results
      - name: Get code coverage artifiact
        uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
        with:
          name: code-coverage
      - name: Get test result artifact
        uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
        with:
          name: test-results
          path: test-results
      - name: Upload code coverage information to codecov.io
        uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1
        with:
          file: coverage.out
      - name: Perform static code analysis using SonarCloud
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
          SCANNER_VERSION: 4.2.0.1873
          SCANNER_PATH: /tmp/cache/scanner
          OS: linux
        run: |
            # We do not use the provided action, because it does contain an old
            # version of the scanner, and also takes time to build.
            set -e
            mkdir -p ${SCANNER_PATH}
            export SONAR_USER_HOME=${SCANNER_PATH}/.sonar
            if [[ ! -x "${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/bin/sonar-scanner" ]]; then
              curl -Ol https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SCANNER_VERSION}-${OS}.zip
              unzip -qq -o sonar-scanner-cli-${SCANNER_VERSION}-${OS}.zip -d ${SCANNER_PATH}
            fi

            chmod +x ${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/bin/sonar-scanner
            chmod +x ${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/jre/bin/java

            # Explicitly set NODE_MODULES
            export NODE_MODULES=${PWD}/ui/node_modules
            export NODE_PATH=${PWD}/ui/node_modules

            ${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/bin/sonar-scanner
        if: env.sonar_secret != ''

  test-e2e:
    name: Run end-to-end tests
    runs-on: ubuntu-latest
    strategy:
      matrix:
        k3s-version: [v1.23.3, v1.22.6, v1.21.2]
    needs: 
      - build-go
    env:
      GOPATH: /home/runner/go
      ARGOCD_FAKE_IN_CLUSTER: "true"
      ARGOCD_SSH_DATA_PATH: "/tmp/argo-e2e/app/config/ssh"
      ARGOCD_TLS_DATA_PATH: "/tmp/argo-e2e/app/config/tls"
      ARGOCD_E2E_SSH_KNOWN_HOSTS: "../fixture/certs/ssh_known_hosts"
      ARGOCD_E2E_K3S: "true"
      ARGOCD_IN_CI: "true"
      ARGOCD_E2E_APISERVER_PORT: "8088"
      ARGOCD_SERVER: "127.0.0.1:8088"
    steps:
      - name: Checkout code
        uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
      - name: Setup Golang
        uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3.5.0
        with:
          go-version: ${{ env.GOLANG_VERSION }}
      - name: GH actions workaround - Kill XSP4 process
        run: |
          sudo pkill mono || true
      # ubuntu-22.04 comes with kubectl, but the version is not pinned. The version as of 2022-12-05 is 1.26.0 which
      # breaks the `TestNamespacedResourceDiffing` e2e test. So we'll pin to 1.25 and then fix the underlying issue.
      - name: Install kubectl
        run: |
          rm /usr/local/bin/kubectl
          curl -LO https://dl.k8s.io/release/v1.25.4/bin/linux/amd64/kubectl
          mv kubectl /usr/local/bin/kubectl
          chmod +x /usr/local/bin/kubectl
      - name: Install K3S
        env:
          INSTALL_K3S_VERSION: ${{ matrix.k3s-version }}+k3s1
        run: |
          set -x
          curl -sfL https://get.k3s.io | sh -
          sudo chmod -R a+rw /etc/rancher/k3s
          sudo mkdir -p $HOME/.kube && sudo chown -R runner $HOME/.kube
          sudo k3s kubectl config view --raw > $HOME/.kube/config
          sudo chown runner $HOME/.kube/config
          kubectl version
      - name: Restore go build cache
        uses: actions/cache@58c146cc91c5b9e778e71775dfe9bf1442ad9a12 # v3.2.3
        with:
          path: ~/.cache/go-build
          key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
      - name: Add ~/go/bin to PATH
        run: |
          echo "/home/runner/go/bin" >> $GITHUB_PATH
      - name: Add /usr/local/bin to PATH
        run: |
          echo "/usr/local/bin" >> $GITHUB_PATH
      - name: Add ./dist to PATH
        run: |
          echo "$(pwd)/dist" >> $GITHUB_PATH
      - name: Download Go dependencies
        run: |
          go mod download
          go install github.com/mattn/goreman@latest
      - name: Install all tools required for building & testing
        run: |
          make install-test-tools-local
      - name: Setup git username and email
        run: |
          git config --global user.name "John Doe"
          git config --global user.email "john.doe@example.com"
      - name: Pull Docker image required for tests
        run: |
          docker pull ghcr.io/dexidp/dex:v2.35.3
          docker pull argoproj/argo-cd-ci-builder:v1.0.0
          docker pull redis:6.2.8-alpine
      - name: Create target directory for binaries in the build-process
        run: |
          mkdir -p dist
          chown runner dist
      - name: Run E2E server and wait for it being available
        timeout-minutes: 30
        run: |
          set -x
          # Something is weird in GH runners -- there's a phantom listener for
          # port 8080 which is not visible in netstat -tulpen, but still there
          # with a HTTP listener. We have API server listening on port 8088
          # instead.
          make start-e2e-local 2>&1 | sed -r "s/[[:cntrl:]]\[[0-9]{1,3}m//g" > /tmp/e2e-server.log &
          count=1
          until curl -f http://127.0.0.1:8088/healthz; do
            sleep 10;
            if test $count -ge 60; then
              echo "Timeout"
              exit 1
            fi
            count=$((count+1))
          done
      - name: Run E2E testsuite
        run: |
          set -x
          make test-e2e-local
      - name: Upload e2e-server logs
        uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
        with:
          name: e2e-server-k8s${{ matrix.k3s-version }}.log
          path: /tmp/e2e-server.log
        if: ${{ failure() }}
