Official SonarCloud GitHub Action fails to resolve paths due to workspace difference

We are having trouble getting SonarCloud to resolve paths in our test coverage reports. We’re using GitHub and GitHub Actions for a TypeScript application with Jest tests. Sonar scans are running using the official SonarCloud Action. We’re able to generate an LCOV report and the scanner picks it up, but when it tries to resolve the paths within the report it fails. This leaves us without test coverage information.

This seems to be similar to the issue encountered here: Sonarcloud-github-action coverage percentage missing from sonar cloud - #6 by Colin

I can share the YAML we’re using for the Sonar-related job in Actions, but it’s not doing anything out of the ordinary so I’ll leave it out for brevity.

This is the error generated:

INFO: Analysing [/github/workspace/__tests__/results/lcov.info]
WARN: Could not resolve 1 file paths in [/github/workspace/__tests__/results/lcov.info]
WARN: First unresolved path: assets/Redacted.js (Run in DEBUG mode to get full list of unresolved paths)

This is potentially related to a GitHub Actions bug: https://github.com/actions/runner/issues/2058. The issue seems to be that the Sonar scanner action runs in an isolated workspace; it has this information during runs:

INFO: Base dir: /github/workspace
INFO: Working dir: /github/workspace/.scannerwork

This is potentially the problem because the rest of the workflow runs in /home/runner/work/reponame/reponame. I would think we could work around this problem by adjusting the projectBaseDir using either sonar-project.properties or the parameter in the scanner action. However, neither of those seem to be effective; the base dir is always reported as /github/workspace.

Is the official Sonar scanner Action meant to run in a separate workspace or is this a side effect of the GitHub Actions bug related to container-based jobs?

Somewhat related: should we be able to use projectBaseDir to ensure paths are resolvable? Is that working as intended given the action’s behavior?

1 Like

I kept digging and found some open issues that seem related to this:

That makes it seem like the workspace difference is the problem and can be worked around by converting the LCOV report’s paths to relative ones instead of absolute. However, the paths in our LCOV file are already relative. I also tried adding absolute paths within the container workspace (i.e. adding /github/workspace/ to the beginning of the file path) but that still ended up with essentially the same result:

INFO: Analysing [/github/workspace/__tests__/results/lcov.info]
WARN: Could not resolve 1 file paths in [/github/workspace/__tests__/results/lcov.info]
WARN: First unresolved path: /github/workspace/assets/Redacted.js (Run in DEBUG mode to get full list of unresolved paths)

Hi @edamonoomph ,

I am sorry for the long delay in contacting you about this issue. I would like to see if I can assist in resolving the problem. Firstly can I check if you are still experiencing this issue? I see that you have tried both relative and absolute paths for this. Can I ask how your project is setup in general? you have mentioned that the the working paths and the coverage are in different workspaces, can you clarify the structure of the project? I would like to see if I can create a similar project structure on my side to test this and see if we can identify a fix for you.

Hi Shane, yes we are still experiencing this issue.

To clarify the workspace difference, I’m referring to the “workspace” as used by GitHub Actions, not the structure of the project. The project is a very typical TypeScript project using Jest for tests. It’s being built and deployed using AWS SAM; I don’t think that makes a functional difference but in terms of getting a similar project set up something like this should be very similar: Building TypeScript projects with AWS SAM CLI | AWS Compute Blog. The Actions steps we have in the workflow don’t do anything out of the ordinary, essentially:

  1. npm install
  2. npm run test (Jest)
  3. npm prune --production
  4. sam build
  5. Run SonarCloud scan with official action: GitHub - SonarSource/sonarcloud-github-action: Integrate SonarCloud code analysis to GitHub Actions

The workspace issue seems to be a GitHub Actions problem. The “workspace” is just where the Actions workflow steps are performed. The problem is due to an inconsistency: when a step is run normally the workspace is in one location, but when a step uses a container-based action then workspace is in a different location. From what I can tell this is causing Sonar to be unable to find required files because the action is configured to run in a container, which means it runs in a different workspace than the rest of the workflow.

Thank you for the information @edamonoomph, I will try and recreate the issue on our side and come back to you with some information.

Hi @edamonoomph,

I have tried using a similar project to what you have suggested above, can I check what the content of your build.yml and the sonar-project.properties files are for you? You can redact the specific project ids if you would want to.

Are you using working-directories in the github actions?
Are you using sonar.javascript.lcov.reportPaths in the sonar-project.properties?

For me using these options in the action and sonar properties files allowed me to see the coverage.

Hi Shane, thanks for looking into this. Here’s the relevant job in our Actions workflow with some debugging added to it:

  sonar:
    name: SonarCloud scan
    runs-on: ubuntu-latest
    needs: [context, build]
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Retrieve build artifact
        uses: actions/download-artifact@v3
        with:
          name: sam-build.zip
          path: /tmp/

      - name: Extract the SAM build artifact
        run: unzip /tmp/sam-build.zip

      - name: Detect the most recent release tag
        uses: actions/github-script@v6
        id: tags
        with:
          result-encoding: string
          script: |
            // Retrieve the list of tags from this repo
            const tags = await github.rest.repos.listTags({
              owner: context.repo.owner,
              repo: context.repo.repo,
            })
            // Map the tag names into a separate array
            const names = tags.data.map(tag => tag.name)
            names.sort()
            // Get the latest semantic tag
            const latest = names.pop()
            console.log('Tag detected: ' + latest)
            return latest

      - name: Format LCOV report paths to be absolute and in container workspace
        run: sed -i 's|SF:|SF:/github/workspace/|g' __tests__/results/lcov.info

      - name: Debug LCOV report
        run: |
          echo "LCOV report:"
          cat __tests__/results/lcov.info
          echo "Segmentation.js (relative):"
          ls -l assets/Segmentation.js
          echo "Segmentation.js (absolute, envvar):"
          ls -l ${GITHUB_WORKSPACE}/assets/Segmentation.js
          echo "Segmentation.js (absolute, hard-coded, non-container):"
          ls -l /home/runner/work/[repo]/[repo]/assets/Segmentation.js
          echo "Segmentation.js (absolute, hard-coded, container):"
          ls -l /github/workspace/assets/Segmentation.js

      - name: Configure Sonar latest version and base directory
        run: |
          echo "sonar.projectVersion=${{ steps.tags.outputs.result }}" >> sonar-project.properties
          echo "sonar.projectBaseDir=${GITHUB_WORKSPACE}" >> sonar-project.properties
          cat sonar-project.properties

      - name: SonarCloud scan
        uses: sonarsource/sonarcloud-github-action@master
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

The full workflow is quite long. Here is our Sonar properties file, with some details redacted (also note that the workflow above modifies the file before it’s used):

# Subscription information
sonar.organization=[org]
sonar.projectKey=[project]

# Test result locations
sonar.javascript.lcov.reportPaths=__tests__/results/lcov.info

# Scan parameters
sonar.sources=src,assets
# Note: sonar.projectVersion is added during CI for comparison against latest release

As for the working directory, if you mean the jobs.<job_id>.steps[*].working-directory directive, no we are not using that anywhere. Everything is operating in the default workspace.

We are using sonar.javascript.lcov.reportPaths and have it set to __tests__/results/lcov.info.

Can you share the configuration that worked for you?

Hi @Evan
Can I check if you are still experiencing issues here? We tried to investigate the issue further but were unable to come to any useful conclusions. There has been a long delay since our last communication where I had tried to investigate further. I’m sorry that I had not updated you during this period, there was a lot of reorganization on our side.

If you are still experiencing issues here I would like to see if I can find someone who can assist you further however I appreciate it has been a long time.

1 Like

Hi @shane.findley, we are still experiencing this issue. We haven’t tried much since to work around it since my last update, we’ve just operated without the code coverage information.

@edamonoomph, thanks for letting me know. I’ll talk with the teams and see if we can get some support to resolve this issue for you.

Hey @edamonoomph,

Let’s try to start over here. As you figured indeed, the fact that the test+coverage and the analysis are running in different environments (essentially with different file paths) makes things not work as smoothly as they should.

Usually, this is not much of an issue from any of the tools involved, it’s simply that we have to find a solution to match their constraints.

I’d like to precisely understand the situation. For that could you please run your analysis in Debug mode (add -Dsonar.verbose=true in the args). This will output all files indexed by the scanner, with the path the scanner understands.
We can then easily compare to what we have in the LCOV report, and see what exactly the mismatch it. From here, I think we can manage to find a solution.

Could you please attach here the analysis debug logs and the full LCOV report so I can have a look?

Cheers,
Antoine

Hello @edamonoomph,

Were you able to gather these information? Analysis debug logs and the LCOV report?

Let me know.

Antoine

Hi @Antoine, thanks for your help here. I was able to run a scan with the verbose option enabled. The warning still appeared:

INFO: Analysing [/github/workspace/__tests__/results/lcov.info]
WARN: Could not resolve 1 file paths in [/github/workspace/__tests__/results/lcov.info]
DEBUG: Unresolved paths:
assets/Segmentation.js

The debug info does show this a few hundred lines before that, a bit after INFO: Preprocessing files... is shown:

DEBUG: File '/github/workspace/assets/Segmentation.js' is excluded by the scm ignore settings.
DEBUG: File '/github/workspace/assets/Segmentation.d.ts' is excluded by the scm ignore settings.
DEBUG: File '/github/workspace/assets/Segmentation.js.map' is excluded by the scm ignore settings.

We don’t have any ignore settings configured but I don’t know if this pertains to the LCOV report’s contents. The string assets/Segmentation.js doesn’t appear anywhere else in the logs.

I attached the LCOV report but it might not be that helpful. The workspace issue seems to be the problem still.

lcov.txt (1.1 KB)

Hey @edamonoomph,

I confirm that your LCOV report is fine and there is no issue related to paths in your analysis. If your Segmentation.js was not excluded from the analysis, the scanner would find it when parsing the LCOV report and you would not have the Unresolved paths error.

Testing details to confirm this:

Here is my project structure:

antoinevigneau@js$ tree -fa -I .git
.
├── ./.gitignore
├── ./assets
│   └── ./assets/Segmentation.js
└── ./reports
    └── ./reports/lcov.txt

My .gitignore states:

antoinevigneau@js$ cat .gitignore
# Scanner work dir
.scannerwork

# Reports dir
reports

Segmentation.js is simply:

antoinevigneau@js$ cat assets/Segmentation.js
alert(1);
alert(2);
alert(3);
alert(4);
alert(5);
alert(6);

And the LCOV report is:

antoinevigneau@js$ cat reports/lcov.txt
TN:
SF:assets/Segmentation.js
DA:1,1
DA:3,2
DA:5,3
end_of_record

When I analyze this project, here are the important pieces of logs:

antoinevigneau@js[main]$ sonar-scanner \
    -Dsonar.host.url=https://sonarcloud.io \
    -Dsonar.token=**** \
    -Dsonar.organization=antoine-vigneau-sonarsource-github \
    -Dsonar.projectKey=antoine-vigneau-sonarsource-github_js \
    -Dsonar.sources=assets \
    -Dsonar.javascript.lcov.reportPaths=reports/lcov.txt \
    -X

[...]

10:33:44.421 INFO: Base dir: /Users/antoinevigneau/Documents/Experiments/js
10:33:44.421 INFO: Working dir: /Users/antoinevigneau/Documents/Experiments/js/.scannerwork

[...]

10:33:51.396 INFO: Indexing files...
10:33:51.396 INFO: Project configuration:
10:33:51.396 INFO:   Excluded sources: **/build-wrapper-dump.json
10:33:51.424 DEBUG: 'assets/Segmentation.js' generated metadata with charset 'UTF-8'
10:33:51.425 DEBUG: Average line length for assets/Segmentation.js is 7
10:33:51.431 DEBUG: 'assets/Segmentation.js' indexed with language 'js'
10:33:51.436 INFO: 1 file indexed

[...]

10:34:00.260 INFO: Sensor JavaScript/TypeScript Coverage [javascript]
10:34:00.261 DEBUG: Property sonar.javascript.lcov.reportPaths is used.
10:34:00.261 DEBUG: Using 'reports/lcov.txt' to resolve LCOV files
10:34:00.261 INFO: Analysing [/Users/antoinevigneau/Documents/Experiments/js/reports/lcov.txt]
10:34:00.271 INFO: Sensor JavaScript/TypeScript Coverage [javascript] (done) | time=11ms

So the file is well indexed as assets/Segmentation.js and because the LCOV report referred it with the same path, it’s all ok (I can see the coverage on the SonarCloud UI)


However, when this file is excluded from the analysis, I can see the same output as you, basically the LCOV parsing fails because it can’t find this file.

Testing details

Now my .gitignore is:

antoinevigneau@js[main]$ cat .gitignore
# Scanner work dir
.scannerwork

# Reports dir
reports

# Assets files
assets/Segmentation.js

Note that I also had to add another random file assets/file.js so the analysis was not empty (otherwise pretty much nothing happens).

And the same analysis command outputs:

antoinevigneau@js[main]$ sonar-scanner \
    -Dsonar.host.url=https://sonarcloud.io \
    -Dsonar.token=**** \
    -Dsonar.organization=antoine-vigneau-sonarsource-github \
    -Dsonar.projectKey=antoine-vigneau-sonarsource-github_js \
    -Dsonar.sources=assets \
    -Dsonar.javascript.lcov.reportPaths=reports/lcov.txt \
    -X

[...]

10:41:53.377 INFO: Base dir: /Users/antoinevigneau/Documents/Experiments/js
10:41:53.377 INFO: Working dir: /Users/antoinevigneau/Documents/Experiments/js/.scannerwork

[...]

10:41:59.925 DEBUG: File '/Users/antoinevigneau/Documents/Experiments/js/assets/Segmentation.js' is excluded by the scm ignore settings.

[...]

10:42:01.754 INFO: Indexing files...
10:42:01.754 INFO: Project configuration:
10:42:01.755 INFO:   Excluded sources: **/build-wrapper-dump.json
10:42:01.786 DEBUG: 'assets/file.js' generated metadata with charset 'UTF-8'
10:42:01.787 DEBUG: Average line length for assets/file.js is 7
10:42:01.795 DEBUG: 'assets/file.js' indexed with language 'js'
10:42:01.802 INFO: 1 file indexed

[...]

10:42:10.511 INFO: Sensor JavaScript/TypeScript Coverage [javascript]
10:42:10.511 DEBUG: Property sonar.javascript.lcov.reportPaths is used.
10:42:10.511 DEBUG: Using 'reports/lcov.txt' to resolve LCOV files
10:42:10.511 INFO: Analysing [/Users/antoinevigneau/Documents/Experiments/js/reports/lcov.txt]
10:42:10.515 WARN: Could not resolve 1 file paths in [/Users/antoinevigneau/Documents/Experiments/js/reports/lcov.txt]
10:42:10.515 DEBUG: Unresolved paths:
assets/Segmentation.js
10:42:10.515 INFO: Sensor JavaScript/TypeScript Coverage [javascript] (done) | time=4ms

So in the end, the real question is why Segmentation.js file is considered excluded by the SCM config?

I know you said:

We don’t have any ignore settings configured

But before we dive into the potentially hard task of debugging this, could you please double-check if this is true? I don’t see how the scanner could take that decision by itself…

Let me know what you find!

Antoine

@Antoine, I’ve removed the sonar.organization and sonar.projectKey, but otherwise this is the entire sonar-project.properties file being used (it’s a very small and simple project):

# Test result locations
sonar.javascript.lcov.reportPaths=__tests__/results/lcov.info

# Scan parameters
sonar.sources=src,assets

I’m not sure why assets/Segmentation.js would be excluded based on that configuration.

Hi @edamonoomph,

Sorry for not making that clear earlier. When the scanner says:

is excluded by the scm ignore settings

It means that it looked at the .gitignore and the file was excluded from there. Could you check for this?
SCM means Source Code Management (ie. git in 99% cases)

Cheers,

Antoine

Any news @edamonoomph, have you checked the .gitignore?

Hi,

same issue here, I use upload-artifact / download-upload, but the path use by sonar-cli is not the home/runner/work/ but /github/workspace

16:06:08.080 INFO: Parsing report '/github/workspace/coverage.xml'

16:06:08.134 WARN: Invalid directory path in 'source' element: /home/runner/work/seedboxsync/seedboxsync/seedboxsync

My action:

    runs-on: ubuntu-latest
    name: SonarCloud analyse
    steps:
    - name: Git checkout
      uses: actions/checkout@v4
      with:
        fetch-depth: 0  # Shallow clones should be disabled for a better relevancy of analysis
    - uses: actions/download-artifact@v4
      with:
        name: coverage-${{ env.pythonLastVersion }}
    - name: Display structure of downloaded files
      run: |
        pwd
        ls -R
        cat coverage.xml
    - name: SonarCloud Scan
      uses: sonarsource/sonarcloud-github-action@master
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}  # Needed to get PR information, if any
        SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
      with:
        args: >
          -Dsonar.verbose=true

@Antoine, thanks for the pointer, this fixed the issue for me. I didn’t realize that Sonar would use the .gitignore file to decide what files should be included or excluded from a scan, I hadn’t seen that mentioned anywhere. I adjusted our Actions workflow to remove /assets/ from our .gitignore file during the scan and Sonar is seeing it now. We need to have that file ignored normally so I can’t remove it entirely but removing it for just the scanning portion has worked. Thanks!

Hi @Evan ,

Thanks for confirming. It is documented in our Analysis Scope doc: SonarQube respects ignored files.
This page is also a test to prove that you read it all :grin: just kidding but I’d agree that it’s a lot, especially when you don’t know what you are looking for (ie. because if you search for .gitignore, you’ll get there immediately)

Usually .gitignore content is for generated/build files, so it makes sense to exclude them but you might have a more specific case.

As you can see, you can disable this feature and then manage your exclusions yourself (with sonar.exclusions), maybe you’d rather do that than fiddling with the .gitignore content.

Cheers,
Antoine

1 Like