Can't see code & coverage info from PR scan but works for local scans

I ran the following gradle tasks locally on my machine and successfully got the analysis results on sonar cloud for that branch:

./gradlew clean :app:assembleStaging variantCoverage lintStaging detekt -PignoreFailures sonar

where variantCoverage simply executes unit tests and generates the jacoco XML report.
Going to sonar cloud afterwards correctly shows the unit test information, any detect issues as well as the code scanned (correctly with both our /java dirs). However, when the same tasks are executed via GitHub actions, the scan completes successfully and I do see a result on sonar cloud, but this time, there does not seem to be any info on coverage, unit tests and the code tab is empty. Is that due to the previous scan that occurred (and no different results being available) or is sonar unable to scan the source dirs?

Here’s the YML file for reference and the sonar setup:

name: SonarCloud Analysis

on:
  pull_request:
    branches: [ "my branches" ]
    types: [ opened, synchronize, reopened, ready_for_review, assigned ]

  workflow_dispatch:
    inputs:
      variant:
        description: 'Android variant to analyze'
        required: false
        default: "staging"
        options:
          - Options..

permissions:
  pull-requests: write
  contents: read

env:
  VARIANT: ${{ github.event.inputs.variant || 'staging' }}
  SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
  SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

jobs:
  setup:
    name: Setup Environment & Cache
    runs-on: ubuntu-latest
    outputs:
      gradle-key: ${{ steps.gradle-cache-key.outputs.key }}
    steps:
      - uses: actions/checkout@v4

      - name: Validate Gradle Wrapper
        uses: gradle/wrapper-validation-action@v2

      - name: Compute Gradle Cache Key
        id: gradle-cache-key
        run: echo "key=${{ runner.os }}-gradle-$(sha1sum **/*.gradle* **/gradle-wrapper.properties | sha1sum | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"

      - name: Cache Gradle
        uses: actions/cache@v4
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
            ~/.gradle/caches/build-cache-1
          key: ${{ steps.gradle-cache-key.outputs.key }}
          restore-keys: |
            ${{ runner.os }}-gradle-

  build-and-test:
    name: Build & Test
    runs-on: 8vcpu-32GB-x64-ubuntu
    needs: setup
    timeout-minutes: 80
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          java-version: 17
          distribution: 'zulu'
      - uses: actions/cache@v4
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
            ~/.gradle/caches/build-cache-1
          key: ${{ needs.setup.outputs.gradle-key }}
      - run: ./gradlew clean
      - run: ./gradlew :app:assemble${{ env.VARIANT }} variantCoverage --no-daemon --stacktrace --info

      - name: Test Summary
        uses: test-summary/action@v2
        if: always()
        with:
          paths: "**/build/test-results/test**/TEST-*.xml"
          show: "always" # ensures summary appears even if tests fail
          comment_mode: "always" # posts or updates a PR comment automatically


      - name: Upload Test Results
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: test-results
          path: '**/build/test-results/test*/*.xml'
          retention-days: 1

      - name: Upload Coverage Reports
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: coverage-reports
          path: '**/build/reports/jacoco/**/*.xml'
          retention-days: 1

      - name: Upload Build Outputs
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: build-outputs
          path: |
            **/build/intermediates/javac/**/classes/**
            **/build/tmp/kotlin-classes/**
            **/build/intermediates/compile_*_jar_classes/**
            **/build/classes/**
          retention-days: 1

  lint:
    name: Android Lint
    runs-on: 8vcpu-32GB-x64-ubuntu
    needs: setup
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          java-version: 17
          distribution: 'zulu'
      - uses: actions/cache@v4
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
            ~/.gradle/caches/build-cache-1
          key: ${{ needs.setup.outputs.gradle-key }}
      - run: ./gradlew lint${{ env.VARIANT }} --no-daemon --stacktrace --info
      - name: Upload Lint Report
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: lint-report
          path: '**/build/reports/lint-results-*.xml'
          retention-days: 1

  detekt:
    name: Detekt Static Analysis
    runs-on: 8vcpu-32GB-x64-ubuntu
    needs: setup
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          java-version: 17
          distribution: 'zulu'
      - uses: actions/cache@v4
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
            ~/.gradle/caches/build-cache-1
          key: ${{ needs.setup.outputs.gradle-key }}
      - run: ./gradlew detekt -PignoreFailures --no-daemon --stacktrace --info
      - name: Upload Detekt Report
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: detekt-report
          path: '**/build/reports/detekt/detekt.xml'
          retention-days: 1

  sonar:
    name: SonarCloud Analysis
    needs: [ build-and-test, lint, detekt ]
    runs-on: ubuntu-latest
    env:
      SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
    steps:
      - uses: actions/checkout@v4
      - uses: gradle/wrapper-validation-action@v2
      - uses: actions/setup-java@v4
        with:
          java-version: 17
          distribution: 'zulu'
      - uses: actions/cache@v4
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
            ~/.gradle/caches/build-cache-1
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
          restore-keys: |
            ${{ runner.os }}-gradle-
      - uses: actions/cache@v4
        with:
          path: ~/.sonar/cache
          key: ${{ runner.os }}-sonar
          restore-keys: |
            ${{ runner.os }}-sonar

      - name: Download Test Results
        uses: actions/download-artifact@v4
        with:
          name: test-results
          path: .

      - name: Download Coverage Reports
        uses: actions/download-artifact@v4
        with:
          name: coverage-reports
          path: .

      - name: Download Build Outputs
        uses: actions/download-artifact@v4
        with:
          name: build-outputs
          path: .

      - name: Download Detekt Report
        uses: actions/download-artifact@v4
        with:
          name: detekt-report
          path: .

      - name: Download Lint Report
        uses: actions/download-artifact@v4
        with:
          name: lint-report
          path: .

      - name: Run SonarCloud Analysis
        run: |
          ./gradlew sonar \
            -Dsonar.kotlin.detekt.reportPaths="${GITHUB_WORKSPACE}/app/build/reports/detekt/detekt.xml" \
            -Dsonar.coverage.jacoco.xmlReportPaths="${GITHUB_WORKSPACE}/app/build/reports/jacoco/variantStaging/coverage.xml" \
            -Dsonar.android.lint.report="${GITHUB_WORKSPACE}/app/build/reports/lint-results-variantStaging.xml" \
            --no-daemon --stacktrace --info

Sonar setup in project-level build.gradle.kts script:


subprojects {
    sonar {
        setAndroidVariant("staging")
        properties {
            property(
                "sonar.kotlin.detekt.reportPaths",
                "${project.buildDir}/reports/detekt/detekt.xml"
            )
            property(
                "sonar.coverage.jacoco.xmlReportPaths",
                "${project.buildDir}/reports/jacoco/${androidVariant}/coverage.xml"
            )
            property(
                "sonar.android.lint.report",
                "${project.buildDir}/build/reports/lint-results-${androidVariant}.xml"
            )
            property(
                "sonar.tests",
                "src/test/java,src/testvariant/java"
            )
            property("sonar.test.inclusions", "**/com/{package}/**")
        }
    }
}

sonar {
    properties {
        property("sonar.projectKey", "mykey")
        property("sonar.organization", "myorg")
        property("sonar.projectName", "Android")
        property("sonar.host.url", "https://sonarcloud.io")
        property("sonar.token", System.getenv("SONAR_TOKEN"))
        property("sonar.sources", "app/src/main/java,app/src/variant/java")
    }
}

Some things found from the CI logs:

Importing …./detekt.xml
Sensor Import of detekt issues [kotlin] (done) | time=76ms
Sensor Import of Android Lint issues [kotlin]
2539/2539 source files have been analyzed
The property "sonar.tests" is not set. To improve the analysis accuracy… (--> which seems weird considering that property is set right above!)

Hi,

Welcome to the community!

Can you provide the GitHub Actions analysis log?

The analysis / scanner log is what’s output from the analysis command. Hopefully, the log you provide - redacted as necessary - will include that command as well.

This guide will help you find them.

I’d like to see whether the problem is that analysis isn’t finding the reports or that it finds them but it doesn’t recognize the paths in them.

 
Thx,
Ann