name: Tests
on:
  pull_request:
    branches: [ "master", "release/**" ]
  push:
    branches: [ "release/**" ]

jobs:
  snap-builds:
    runs-on: ubuntu-16.04
    steps:
    - name: Checkout code
      uses: actions/checkout@v2
    - name: Cache snapd snap build status
      id: cache-snapd-build-status
      uses: actions/cache@v1
      with:
        path: "${{ github.workspace }}/.test-results"
        key: "${{ github.run_id }}-${{ github.job }}-results"
    - name: Check cached snap build
      id: cached-results
      run: |
          CACHE_RESULT_STAMP="${{ github.workspace }}/.test-results/snap-build-success"
          echo "CACHE_RESULT_STAMP=$CACHE_RESULT_STAMP" >> $GITHUB_ENV
          if [ -e "$CACHE_RESULT_STAMP" ]; then
              has_cached_snap=0
              while read name; do
                  has_cached_snap=1
                  # bring back artifacts from the cache
                  cp -v "$name" "${{ github.workspace }}"
              done < <(find "$(dirname $CACHE_RESULT_STAMP)" -name "*.snap")
              if [ "$has_cached_snap" = "1" ]; then
                  # we have restored an artifact from the cache
                  echo "::set-output name=already-ran::true"
              fi
          fi
    - name: Build snapd snap
      if: steps.cached-results.outputs.already-ran != 'true'
      uses: snapcore/action-build@v1
    - name: Cache built artifact
      run: |
        mkdir -p $(dirname "$CACHE_RESULT_STAMP")
        cp -v *.snap "$(dirname $CACHE_RESULT_STAMP)/"
    - name: Uploading snapd snap artifact
      uses: actions/upload-artifact@v2
      with:
        name: snap-files
        path: "*.snap"
    - name: Mark successful snap build
      run: |
        mkdir -p $(dirname "$CACHE_RESULT_STAMP")
        touch "$CACHE_RESULT_STAMP"

  unit-tests:
    runs-on: ubuntu-16.04
    env:
      GOPATH: ${{ github.workspace }}
      GO111MODULE: off
      # Set PATH to ignore the load of magic binaries from /usr/local/bin And
      # to use the go snap automatically. Note that we install go from the
      # snap in a step below. Without this we get the GitHub-controlled latest
      # version of go.
      PATH: /snap/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
      GOROOT: ""
      # XXX: compat env for "check-pr-title.py" in "run-checks", can go
      #      once we switch away from that. Note that we cannot currently
      #      use a github action check (like deepakputhraya/action-pr-title)
      #      because an update of the PR title in the github UI is not visible
      #      to the github action.
      TRAVIS_PULL_REQUEST: ${{ github.event.number }}
    strategy:
      # we cache successful runs so it's fine to keep going
      fail-fast: false      
      matrix:
        gochannel:
          - 1.9
          - latest/stable
    steps:
    - name: Checkout code
      uses: actions/checkout@v2
      with:
        # NOTE: checkout the code in a fixed location, even for forks, as this
        # is relevant for go's import system.
        path: ./src/github.com/snapcore/snapd

    - name: Cache Debian dependencies
      id: cache-deb-downloads
      uses: actions/cache@v1
      with:
        path: /var/cache/apt
        key: var-cache-apt-{{ hashFiles('**/debian/control') }}
    - name: Run "apt update"
      run: |
          sudo apt update
    - name: Download Debian dependencies
      if: steps.cache-deb-downloads.outputs.cache-hit != 'true'
      run: |
          sudo apt clean
          sudo apt build-dep -d -y ${{ github.workspace }}/src/github.com/snapcore/snapd

    - name: Cache snapd test results
      id: cache-snapd-test-results
      uses: actions/cache@v1
      with:
        path: "${{ github.workspace }}/.test-results"
        # must include matrix or things get racy, i.e. when latest/edge
        # finishes after 1.9 it overrides the results from 1.9
        key: "${{ github.run_id }}-${{ github.job }}-${{ matrix.gochannel }}-results"
    - name: Check cached test results
      id: cached-results
      run: |
          CACHE_RESULT_STAMP="${{ github.workspace }}/.test-results/${{ matrix.gochannel }}-success"
          echo "CACHE_RESULT_STAMP=$CACHE_RESULT_STAMP" >> $GITHUB_ENV
          if [ -e "$CACHE_RESULT_STAMP" ]; then
              echo "::set-output name=already-ran::true"
          fi
    - name: Install Debian dependencies
      if: steps.cached-results.outputs.cached-resulsts != 'true'
      run: |
          sudo apt build-dep -y ${{ github.workspace }}/src/github.com/snapcore/snapd
    # golang latest ensures things work on the edge
    - name: Install the go snap
      if: steps.cached-results.outputs.already-ran != 'true'
      run: |
          sudo snap install --classic --channel=${{ matrix.gochannel }} go
    - name: Install ShellCheck as a snap
      if: steps.cached-results.outputs.already-ran != 'true'
      run: |
          sudo apt-get remove --purge shellcheck
          sudo snap install shellcheck
    - name: Install govendor
      run: go get -u github.com/kardianos/govendor
    - name: Cache Go dependencies
      id: cache-go-govendor
      uses: actions/cache@v1
      with:
        path: ${{ github.workspace }}/.cache/govendor
        key: go-govendor-{{ hashFiles('**/vendor.json') }}
    - name: Get Go dependencies
      run: cd ${{ github.workspace }}/src/github.com/snapcore/snapd && ${{ github.workspace }}/bin/govendor sync
    - name: Run static checks
      if: steps.cached-results.outputs.already-ran != 'true'
      run: |
          cd ${{ github.workspace }}/src/github.com/snapcore/snapd || exit 1
          # run gofmt checks only with Go 1.9 and 1.10
          if ! echo "${{ matrix.gochannel }}" | grep -E '1\.(9|10)' ; then
              # and skip with other versions
              export SKIP_GOFMT=1
              echo "Formatting checks will be skipped due to the use of Go version ${{ matrix.gochannel }}"
          fi
          ./run-checks --static
    - name: Build C
      if: steps.cached-results.outputs.already-ran != 'true'
      run: |
          cd ${{ github.workspace }}/src/github.com/snapcore/snapd/cmd/
          ./autogen.sh
          make -j2
    - name: Build Go
      if: steps.cached-results.outputs.already-ran != 'true'
      run: |
          go build github.com/snapcore/snapd/...
    - name: Test C
      if: steps.cached-results.outputs.already-ran != 'true'
      run: |
          cd ${{ github.workspace }}/src/github.com/snapcore/snapd/cmd/ && make check
    - name: Test Go
      if: steps.cached-results.outputs.already-ran != 'true'
      run: |
        cd ${{ github.workspace }}/src/github.com/snapcore/snapd || exit 1
        ./run-checks --unit
    - name: Test Go (nosecboot)
      if: steps.cached-results.outputs.already-ran != 'true'
      run: |
        cd ${{ github.workspace }}/src/github.com/snapcore/snapd || exit 1
        echo "Dropping github.com/snapcore/secboot"
        # use govendor remove so that a subsequent govendor sync does not
        # install secboot again
        ${{ github.workspace }}/bin/govendor remove github.com/snapcore/secboot
        ${{ github.workspace }}/bin/govendor remove +unused
        SKIP_DIRTY_CHECK=1 GO_BUILD_TAGS=nosecboot ./run-checks --unit
    - name: Cache successful run
      run: |
        mkdir -p $(dirname "$CACHE_RESULT_STAMP")
        touch "$CACHE_RESULT_STAMP"

  spread:
    needs: [unit-tests]
    runs-on: self-hosted
    strategy:
      # FIXME: enable fail-fast mode once spread can cancel an executing job.
      # Disable fail-fast mode as it doesn't function with spread. It seems
      # that cancelling tasks requires short, interruptible actions and
      # interrupting spread, notably, does not work today. As such disable
      # fail-fast while we tackle that problem upstream.
      fail-fast: false
      matrix:
        system:
        - amazon-linux-2-64
        - arch-linux-64
        - centos-7-64
        - centos-8-64
        - debian-9-64
        - debian-sid-64
        - fedora-32-64
        - fedora-33-64
        - opensuse-15.1-64
        - opensuse-15.2-64
        - opensuse-tumbleweed-64
        - ubuntu-14.04-64
        - ubuntu-16.04-32
        - ubuntu-16.04-64
        - ubuntu-18.04-64
        - ubuntu-20.04-64
        - ubuntu-20.10-64
        - ubuntu-core-16-64
        - ubuntu-core-18-64
        - ubuntu-core-20-64
        - ubuntu-secboot-20.04-64
    steps:
    - name: Checkout code
      uses: actions/checkout@v2
      with:
        # spread uses tags as delta reference
        fetch-depth: 0
    - name: Cache snapd test results
      id: cache-snapd-test-results
      uses: actions/cache@v1
      with:
        path: "${{ github.workspace }}/.test-results"
        key: "${{ github.run_id }}-${{ github.job }}-${{ matrix.system }}-results"
    - name: Check cached test results
      id: cached-results
      run: |
          CACHE_RESULT_STAMP="${{ github.workspace }}/.test-results/${{ matrix.system }}-success"
          echo "CACHE_RESULT_STAMP=$CACHE_RESULT_STAMP" >> $GITHUB_ENV
          if [ -e "$CACHE_RESULT_STAMP" ]; then
              echo "::set-output name=already-ran::true"
          fi
    - name: Run spread tests
      if: "!contains(github.event.pull_request.labels.*.name, 'Skip spread') && steps.cached-results.outputs.already-ran != 'true'"
      env:
          SPREAD_GOOGLE_KEY: ${{ secrets.SPREAD_GOOGLE_KEY }}
      run: |
          # Register a problem matcher to highlight spread failures
          echo "::add-matcher::.github/spread-problem-matcher.json"
          spread -abend google:${{ matrix.system }}:tests/...
    - name: Cache successful run
      run: |
        mkdir -p $(dirname "$CACHE_RESULT_STAMP")
        touch "$CACHE_RESULT_STAMP"
    - name: Discard spread workers
      if: always()
      run: |
        shopt -s nullglob;
        for r in .spread-reuse.*.yaml; do
          spread -discard -reuse-pid="$(echo "$r" | grep -o -E '[0-9]+')";
        done

  spread-nested:
    needs: [unit-tests]
    runs-on: self-hosted
    strategy:
      # FIXME: enable fail-fast mode once spread can cancel an executing job.
      # Disable fail-fast mode as it doesn't function with spread. It seems
      # that cancelling tasks requires short, interruptible actions and
      # interrupting spread, notably, does not work today. As such disable
      # fail-fast while we tackle that problem upstream.
      fail-fast: false
      matrix:
        system:
        - ubuntu-16.04-64
        - ubuntu-18.04-64
        - ubuntu-20.04-64
        - ubuntu-20.10-64
    steps:
    - name: Checkout code
      uses: actions/checkout@v2
    - name: Cache snapd test results
      id: cache-snapd-test-results
      uses: actions/cache@v1
      with:
        path: "${{ github.workspace }}/.test-results"
        key: "${{ github.run_id }}-${{ github.job }}-${{ matrix.system }}-nested-results"
    - name: Check cached test results
      id: cached-results
      run: |
          CACHE_RESULT_STAMP="${{ github.workspace }}/.test-results/${{ matrix.system }}-nested-success"
          echo "CACHE_RESULT_STAMP=$CACHE_RESULT_STAMP" >> $GITHUB_ENV
          if [ -e "$CACHE_RESULT_STAMP" ]; then
              echo "::set-output name=already-ran::true"
          fi
    - name: Run spread tests
      # run if the commit is pushed to the release/* branch or there is a 'Run
      # nested' label set on the PR
      if: "(contains(github.event.pull_request.labels.*.name, 'Run nested') || contains(github.ref, 'refs/heads/release/')) && steps.cached-results.outputs.already-ran != 'true'"
      env:
          SPREAD_GOOGLE_KEY: ${{ secrets.SPREAD_GOOGLE_KEY }}
      run: |
          # Register a problem matcher to highlight spread failures
          echo "::add-matcher::.github/spread-problem-matcher.json"
          export NESTED_BUILD_SNAPD_FROM_CURRENT=true
          export NESTED_ENABLE_KVM=true
          spread -abend google-nested:${{ matrix.system }}:tests/nested/...
    - name: Cache successful run
      run: |
        mkdir -p $(dirname "$CACHE_RESULT_STAMP")
        touch "$CACHE_RESULT_STAMP"
    - name: Discard spread workers
      if: always()
      run: |
        shopt -s nullglob;
        for r in .spread-reuse.*.yaml; do
          spread -discard -reuse-pid="$(echo "$r" | grep -o -E '[0-9]+')";
        done
