SonarSource / sonar-scanner-msbuild

SonarScanner for .NET
http://redirect.sonarsource.com/doc/msbuild-sq-runner.html
GNU Lesser General Public License v3.0
361 stars 143 forks source link

Root directory detection: OS root (`/` on Unix or `C:` on Windows) is identified as the project directory #2148

Closed wynandmurray closed 1 month ago

wynandmurray commented 1 month ago

Our privately-hosted SonarQube instance has been yielding the below error ever since we upgraded it to version 8.0.0 a few days ago:

Unhandled exception. System.UnauthorizedAccessException: Access to the path '/lost+found' is denied.

When forcing our pipeline to make use of version 7.1.1 everything still works.

Some additional stack trace information:

Unhandled exception. System.UnauthorizedAccessException: Access to the path '/lost+found' is denied.
2024-08-13T12:57:48.2148313Z  ---> System.IO.IOException: Permission denied
2024-08-13T12:57:48.2149706Z    --- End of inner exception stack trace ---
2024-08-13T12:57:48.2150810Z    at System.IO.Enumeration.FileSystemEnumerator'1.CreateDirectoryHandle(String path, Boolean ignoreNotFound)
2024-08-13T12:57:48.2152215Z    at System.IO.Enumeration.FileSystemEnumerator'1.Init()
2024-08-13T12:57:48.2153589Z    at System.IO.Enumeration.FileSystemEnumerable'1..ctor(String directory, FindTransform transform, EnumerationOptions options, Boolean isNormalized)
2024-08-13T12:57:48.2155421Z    at System.IO.Enumeration.FileSystemEnumerableFactory.FileInfos(String directory, String expression, EnumerationOptions options, Boolean isNormalized)
2024-08-13T12:57:48.2158249Z    at System.IO.DirectoryInfo.InternalEnumerateInfos(String path, String searchPattern, SearchTarget searchTarget, EnumerationOptions options)
2024-08-13T12:57:48.2160032Z    at System.IO.DirectoryInfo.EnumerateFiles(String searchPattern, SearchOption searchOption)
2024-08-13T12:57:48.2161421Z    at SonarScanner.MSBuild.Common.DirectoryWrapper.EnumerateFiles(DirectoryInfo path, String searchPattern, SearchOption searchOption)
2024-08-13T12:57:48.2162923Z    at SonarScanner.MSBuild.Shim.AdditionalFilesService.<>c__DisplayClass9_0.<GetAllFiles>b__1(DirectoryInfo x)
2024-08-13T12:57:48.2164024Z    at System.Linq.Enumerable.SelectManySingleSelectorIterator'2.MoveNext()
2024-08-13T12:57:48.2164855Z    at System.Linq.Enumerable.WhereEnumerableIterator'1.ToArray()
2024-08-13T12:57:48.2165995Z    at SonarScanner.MSBuild.Shim.AdditionalFilesService.GetAllFiles(IEnumerable'1 extensions, DirectoryInfo projectBaseDir)
2024-08-13T12:57:48.2167748Z    at SonarScanner.MSBuild.Shim.AdditionalFilesService.AdditionalFiles(AnalysisConfig analysisConfig, DirectoryInfo projectBaseDir)
2024-08-13T12:57:48.2170142Z    at SonarScanner.MSBuild.Shim.PropertiesFileGenerator.PutFilesToRightModuleOrRoot(IEnumerable'1 projects, DirectoryInfo baseDirectory)
2024-08-13T12:57:48.2171455Z    at SonarScanner.MSBuild.Shim.PropertiesFileGenerator.TryWriteProperties(PropertiesWriter writer, IEnumerable'1& allProjects)
2024-08-13T12:57:48.2172420Z    at SonarScanner.MSBuild.Shim.PropertiesFileGenerator.GenerateFile()
2024-08-13T12:57:48.2173319Z    at SonarScanner.MSBuild.PostProcessor.PostProcessor.GenerateAndValidatePropertiesFile(AnalysisConfig config)
2024-08-13T12:57:48.2174463Z    at SonarScanner.MSBuild.PostProcessor.PostProcessor.Execute(String[] args, AnalysisConfig config, IBuildSettings settings)
2024-08-13T12:57:48.2175644Z    at SonarScanner.MSBuild.BootstrapperClass.PostProcess()
2024-08-13T12:57:48.2176251Z    at SonarScanner.MSBuild.BootstrapperClass.Execute()
2024-08-13T12:57:48.2177041Z    at SonarScanner.MSBuild.Program.Execute(String[] args, ILogger logger)
2024-08-13T12:57:48.2177720Z    at SonarScanner.MSBuild.Program.Execute(String[] args)
2024-08-13T12:57:48.2178312Z    at SonarScanner.MSBuild.Program.Main(String[] args)
2024-08-13T12:57:48.2230287Z    at SonarScanner.MSBuild.Program.<Main>(String[] args)
2024-08-13T12:57:48.2512028Z ##[error]Process completed with exit code 134.
2024-08-13T12:57:48.2803048Z Post job cleanup.

Our YAML definition for the execution of this scan (with some omissions added):

- name: 'SonarQube Analysis'
      shell: bash
      if:  ${{ steps.changes.outputs.src == 'true'}}
      run: |
        export PATH="$PATH:/home/runner/.dotnet/tools"
        dotnet tool update --global dotnet-sonarscanner
        pushd '...omitted...'
        dotnet sonarscanner begin \
          /k:"...omitted..." \
          /d:sonar.scanner.skipJreProvisioning=true \
          /d:sonar.token="${{ ...omitted...}}" \
          /d:sonar.host.url="${{...omitted...}}" \
          /d:sonar.cs.vstest.reportsPaths="./**/...omitted.../*.trx" \
          /d:sonar.cs.vscoveragexml.reportsPaths="./**/...omitted.../*/*.xml"

        for proj in ./**/*...omitted....*proj
        do
          echo "run tests in $proj"
          dotnet test "$proj" --verbosity normal --logger GitHubActions /p:CollectCoverage=true /p:CoverletOutputFormat=opencover --collect "Code Coverage;Format=Xml"
        done
        for f in ./**/...omitted...*.csproj; do dotnet build $f --configuration Release ; done    
rvdintercept commented 1 month ago

To add to this, seeing same(/similar?) issue in our Github Action. We have also fixed the dotnet tool to version 7.1.1 pending a fix in 8.0

Snippet from our CI workflow Note we using sonarcloud.io as scanner.

env:
  SOLUTION_NAME: <omitted>.sln

jobs:
  building-project:
    runs-on: ubuntu-latest
    environment: test
    steps:
      - uses: actions/checkout@v3   

      - name: Setup .NET Core SDK
        uses: actions/setup-dotnet@v2.1.0
        with: 
              dotnet-version: '8.0.x'

      - name: Set up JDK 17
        uses: actions/setup-java@v2
        with:
          distribution: 'adopt'
          java-version: 17     

      - name: Restore project
        run: dotnet restore $SOLUTION_NAME

      - name: Begin Sonar scan
        run: |
          dotnet tool install --global dotnet-sonarscanner #by omitting version we install 8.0.0
          dotnet sonarscanner begin /o:<omitted> /k:<omitted> /n:<omitted> /d:sonar.login=${{ secrets.SONAR_TOKEN }} /d:sonar.host.url=https://sonarcloud.io /d:sonar.cs.vscoveragexml.reportsPaths=coverage.xml

      - name: Build project
        run: dotnet build $SOLUTION_NAME --configuration Release --no-restore

      - name: Run unit tests
        run: dotnet test $SOLUTION_NAME --logger trx --results-directory "test-results"

      - name: Run code coverage
        run: |
          dotnet tool install --global dotnet-coverage
          dotnet-coverage collect 'dotnet test' -f xml -o 'coverage.xml'

      - name: End Sonar scan
        run: dotnet sonarscanner end /d:sonar.login=${{ secrets.SONAR_TOKEN }}

The CI fails on the last step End sonar scan

Task output

Run dotnet sonarscanner end /d:sonar.login=***
  dotnet sonarscanner end /d:sonar.login=***
  shell: /usr/bin/bash -e {0}
  env:
    SOLUTION_NAME: <omitted>.sln
    DOTNET_ROOT: /home/runner/.dotnet
    JAVA_HOME: /opt/hostedtoolcache/Java_Adopt_jdk/17.0.12-7/x64
SonarScanner for MSBuild 8.0
Using the .NET Core version of the Scanner for MSBuild
Post-processing started.
13:57:04.425  13:57:04.425  WARNING: Multi-Language analysis is enabled. If this was not intended, please set "/d:sonar.scanner.scanAll=false" in the begin step.
Unhandled exception. System.UnauthorizedAccessException: Access to the path '/lost+found' is denied.
 ---> System.IO.IOException: Permission denied
   --- End of inner exception stack trace ---
   at System.IO.Enumeration.FileSystemEnumerator`1.CreateDirectoryHandle(String path, Boolean ignoreNotFound)
   at System.IO.Enumeration.FileSystemEnumerator`1.Init()
   at System.IO.Enumeration.FileSystemEnumerable`1..ctor(String directory, FindTransform transform, EnumerationOptions options, Boolean isNormalized)
   at System.IO.Enumeration.FileSystemEnumerableFactory.FileInfos(String directory, String expression, EnumerationOptions options, Boolean isNormalized)
   at System.IO.DirectoryInfo.InternalEnumerateInfos(String path, String searchPattern, SearchTarget searchTarget, EnumerationOptions options)
   at System.IO.DirectoryInfo.EnumerateFiles(String searchPattern, SearchOption searchOption)
   at SonarScanner.MSBuild.Common.DirectoryWrapper.EnumerateFiles(DirectoryInfo path, String searchPattern, SearchOption searchOption)
   at SonarScanner.MSBuild.Shim.AdditionalFilesService.<>c__DisplayClass9_0.<GetAllFiles>b__1(DirectoryInfo x)
   at System.Linq.Enumerable.SelectManySingleSelectorIterator`2.MoveNext()
   at System.Linq.Enumerable.WhereEnumerableIterator`1.ToArray()
   at SonarScanner.MSBuild.Shim.AdditionalFilesService.GetAllFiles(IEnumerable`1 extensions, DirectoryInfo projectBaseDir)
   at SonarScanner.MSBuild.Shim.AdditionalFilesService.AdditionalFiles(AnalysisConfig analysisConfig, DirectoryInfo projectBaseDir)
   at SonarScanner.MSBuild.Shim.PropertiesFileGenerator.PutFilesToRightModuleOrRoot(IEnumerable`1 projects, DirectoryInfo baseDirectory)
   at SonarScanner.MSBuild.Shim.PropertiesFileGenerator.TryWriteProperties(PropertiesWriter writer, IEnumerable`1& allProjects)
   at SonarScanner.MSBuild.Shim.PropertiesFileGenerator.GenerateFile()
   at SonarScanner.MSBuild.PostProcessor.PostProcessor.GenerateAndValidatePropertiesFile(AnalysisConfig config)
   at SonarScanner.MSBuild.PostProcessor.PostProcessor.Execute(String[] args, AnalysisConfig config, IBuildSettings settings)
   at SonarScanner.MSBuild.BootstrapperClass.PostProcess()
   at SonarScanner.MSBuild.BootstrapperClass.Execute()
   at SonarScanner.MSBuild.Program.Execute(String[] args, ILogger logger)
   at SonarScanner.MSBuild.Program.Execute(String[] args)
   at SonarScanner.MSBuild.Program.Main(String[] args)
   at SonarScanner.MSBuild.Program.<Main>(String[] args)
Error: Process completed with exit code 134.
costin-zaharia-sonarsource commented 1 month ago

We have also two community threads related to path issues that might be related:

costin-zaharia-sonarsource commented 1 month ago

@wynandmurray and @rvdintercept could you please attach to the issue the logs in verbose mode? You can enable verbose mode by providing /d:sonar.verbose=true parameter to the scanner begin step.

While doing this, please don't forget to trim the logs of any private information.

rvdintercept commented 1 month ago

With our CI running in verbose mode I see the following lines preceding the exception:

08:17:55.78  The supplied Code Analysis ErrorLog file is a valid json file and does not need to be fixed: /home/runner/work/<omitted>/<omitted>/.sonarqube/out/21/Issues.json
08:17:55.826  Using longest common projects path as a base directory: '/'.
Unhandled exception. System.UnauthorizedAccessException: Access to the path '/lost+found' is denied.
costin-zaharia-sonarsource commented 1 month ago

I see that your CI is running on an Ubuntu image and the root path detection determined that the base path directory is /.

In v8 we modified the root path detection to work in the following way:

Unless overridden by the user (sonar.projectBaseDir is provided by the user, TF_BUILD_SOURCESDIRECTORY env, or BUILD_SOURCESDIRECTORY env), the sonar.projectBaseDir property is set to the current directory (the folder from where the scanner was executed). If the user executes the scanner from a folder that does not contain the source code (the analyzed projects are not found in the current directory or in a nested folder), there is a fallback mechanism in place that looks for .csproj files linked in the .sln and tries to compute the common root directory.

Since the problem in your case seems to be the root path detection, could you please try manually setting the /d:sonar.projectBaseDir property? It should point to a folder that contains all the analyzed projects.

Additionally, to better understand why the automatic detection does not work, could you please share:

rvdintercept commented 1 month ago

Can confirm the scanner is not failing when I specify the projectBaseDir as /d:sonar.projectBaseDir=/home/runner/work/<repository>

In our workflow, from what I can see we run all of the commands from the same working directory, which follows the format /home/runner/work/<repository>/<repository>

The project structure within repository is that we have a src folder with each project in a subfolder, such that we end up with the following paths on our github runner:

/home/runner/work/<repository>/<repository>/src/projecta/projecta.csproj
/home/runner/work/<repository>/<repository>/src/projectb/projectb.csproj
/home/runner/work/<repository>/<repository>/tests/testprojecta/testprojecta.csproj
/home/runner/work/<repository>/<repository>/solution.sln