Search code examples
unity-game-enginejenkinssonarqubesonarqube-scansonarqube-msbuild-runner

Analyzing non-Visual Studio, Unity C# code with MSBuild/SonarQube via Jenkins


I inherited the responsibility for a SonarQube Linux server which was running version 5.1 and analyzing dozens of Unity C# projects. After upgrading it to SQ 6.1 I discovered that support for analyzing C# projects on non-Windows systems was dropped in version 5.5, so I downgraded it to version 5.4 for the present time.

I have subsequently set up a Windows system as a Jenkins node and SonarQube server. However, the projects I am hoping to evaluate with SonarQube are (mostly) developed on and (always) built on macOS in Unity and therefore lack MSbuild solution files. Is it at all possible for me to use SonarQube within these constraints, short of adding Visual Studio-specific code to the projects? Is there some kind of dummy solution file that I could add to the projects that would allow them to be analyzed without the expectation of a working binary being produced (along the lines of parsing/tokenizing without compiling/linking)? Thanks.


Solution

  • Here is the procedure I have developed for analyzing Unity projects in SonarQube via Jenkins.

    1. A user-facing Jenkins job with the following settings and options, among others specific to my environment.

      1. General—This project is parameterized
        1. String parameter PROJECTKEY
        2. String parameter PROJECTNAME
        3. String parameter PROJECTVERSION
      2. General—Restrict where this project can be run set to the Jenkins build node name you used for your SonarQube server.
      3. Source Code Management
        1. Repository URL set to the repository you want to analyze.
        2. Branches to build set to the main branch, i.e. refs/heads/master.
        3. Additional Behaviors—Recursively update submodules checked as appropriate for your repository.
      4. Build Triggers
        1. Poll SCM checked, but no schedule specified.
      5. Build Environment
        1. Run buildstep before SCM runs—Inject environment variables set to HOME=C:\Users\Jenkins (or as appropriate for your system). This environment variable makes it possible for git to find the correct ssh credentials to use with the Git server. You'll need to put your git private key in %HOME%\.ssh; that is documented elsewhere and left as an exercise to the reader.
        2. Use build environment from another project—Template Project pointing to a second Jenkins job. Mine is called SonarQube_Scanner (see below).
      6. Build
        1. Use builders from another project—Template Project pointing to the same Jenkins job SonarQube_Scanner.
      7. Post-build Actions
        1. Archive the artifacts—Files to archive set to log.zip.
    2. A non-user-facing Jenkins job (SonarQube_Scanner) with the following settings and options.

      1. General—Disable this project checked.
      2. General—Restrict where this project can be run checked and set to your SonarQube server.
      3. Source Code Management set to None.
      4. Build Environment—Inject environment variables to the build process checked.

        1. Properties Content contains

          `LANG=en_US.UTF-8`
          
      5. Build

        1. Execute Windows batch command contains

          set FILENAME=%WORKSPACE%\client\Assets\Editor\SQMenuItems.cs
          REM Yes, this syntax works and is sooo much more readable
           > %FILENAME% ECHO using UnityEditor;
          >> %FILENAME% ECHO public static class SQMenuItems {
          >> %FILENAME% ECHO   static void AssetsOpenCSharpProject() {
          >> %FILENAME% ECHO #if UNITY_5
          >> %FILENAME% ECHO     EditorApplication.ExecuteMenuItem("Assets/Open C# Project");
          >> %FILENAME% ECHO #else
          >> %FILENAME% ECHO     EditorApplication.ExecuteMenuItem("Assets/Sync MonoDevelop Project");
          >> %FILENAME% ECHO #endif
          >> %FILENAME% ECHO     EditorApplication.Exit(0);
          >> %FILENAME% ECHO   }
          >> %FILENAME% ECHO }
          "C:\Program Files\Unity_5.5.2\Editor\Unity.exe" -quit -batchmode -nographics -logFile "%WORKSPACE%\log.txt" -buildTarget android -projectPath %WORKSPACE%\client -executeMethod SQMenuItems.AssetsOpenCSharpProject
          "C:\Program Files\7-zip\7z.exe" -tzip a log.zip log.txt
          
          • SQMenuItems.cs is what triggers Unity to create the .sln and .*proj files that MSBuild needs. The .sln and .*proj files are typically in the .gitignore file and therefore not in the repository.
        2. SonarQube Scanner for MSBuild - Begin Analysis

          1. SonarQube Installation set to the Jenkins build node name you used for your SonarQube server.
          2. Project key set to ${PROJECTKEY}.
          3. Project name set to ${PROJECTNAME}.
          4. Project version set to ${PROJECTVERSION}.
          5. Additional arguments

            /d:sonar.exclusions=**\\SQMenuItems.cs

        3. Execute Windows batch command

          "C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe" /maxcpucount /nr:false /nologo /target:rebuild /verbosity:quiet client\client.sln
          
          • You may need to change client\client.sln above to match your environment. I haven't yet tried replacing that with an environment variable defined in the user-facing Jenkins job.
        4. SonarQube Scanner for MSBuild - End Analysis
    3. DONE
    4. GOTCHAS
      1. If the job fails because Unity fails because it can't find the projectPath, it may have forgotten its license key. Remote desktop to your SonarQube server and launch the Unity GUI. If it pops up asking for your license key, re-enter the key, then quit. Subsequent jobs should succeed.
      2. Things that make it very difficult to pass information to SonarQube in a dynamic way.
        1. Windows environment variables defined in 2.5.1 “Execute Windows batch command” are not preserved through to 2.5.2 “SonarQube Scanner for MSBuild - Begin Analysis”.
        2. The SonarQube.Analysis.xml file specifically prohibits assigning the properties sonar.projectName, sonar.projectKey, and sonar.projectVersion.
    5. I've probably overlooked something; please don't hesitate to inquire.