如何於Jenkins Pipeline專案整合sonarQube


繼上一篇文章:如何建立 Jenkins Pipeline 專案 ,於 Jenkins Pipeline 專案內,加入 sonarQube 程式碼分析

關於 sonarQube 的部分,請參考:SonarQube 程式碼分析工具 - 2022

安裝 SonarQube 服務

透過 docker-compose 安裝服務
REF:sonarQube-docker-compose.yml

version: "3"

services:
  sonarqube:
    image: sonarqube
    expose:
      - 9000
    ports:
      - "127.0.0.1:9000:9000"
    networks:
      - sonarnet
    environment:
      - SONARQUBE_JDBC_URL=jdbc:postgresql://db:5432/sonar
      - SONARQUBE_JDBC_USERNAME=sonar
      - SONARQUBE_JDBC_PASSWORD=sonar
    volumes:
      - sonarqube_conf:/opt/sonarqube/conf
      - sonarqube_data:/opt/sonarqube/data
      - sonarqube_extensions:/opt/sonarqube/extensions
      - sonarqube_bundled-plugins:/opt/sonarqube/lib/bundled-plugins

  db:
    image: postgres
    networks:
      - sonarnet
    environment:
      - POSTGRES_USER=sonar
      - POSTGRES_PASSWORD=sonar
    volumes:
      - postgresql:/var/lib/postgresql
      - postgresql_data:/var/lib/postgresql/data

networks:
  sonarnet:

volumes:
  sonarqube_conf:
  sonarqube_data:
  sonarqube_extensions:
  sonarqube_bundled-plugins:
  postgresql:
  postgresql_data:

執行即可,sonarQube 網站將會在http://127.0.0.1:9000,帳密預設皆為admin

新建 sonarqube 專案

於網站右上角點選新增專案的按鈕,並指定好專案名稱

這邊需要產生一個 token 讓後續步驟使用,指定一個名稱即可,token 內容會被產生出來

這個密碼只會出現一次,但是如果之後忘記了,可以在個人的帳號底下管理 token

這邊因為我的範例項目是 c-sharp,所以當然就選 c-sharp,其他語言的話,點選也會有指示

下面的步驟就照著做

這個頁面下載合適的版本 解壓縮後將目錄加入系統環境變數 Path 之內,省的每次都要打完整路徑執行

畫面的指令碼有三段,其實就是做三件事情

  1. 準備蒐集資訊,這裡需要告訴 scanner 要分析的專案名稱、還有剛剛的 token,另外也跟 sacnner 說,我們所建立的 sonarqube 網站在哪裡
  2. 透過 msbuild 重建專案,讓 scanner 蒐集資訊
  3. 分析剛剛所蒐集到的資訊並傳送給 sonarqube

因為我在截圖的時候沒有把 token 複製下來,所以下面的指令是我產生新的 token


## 啟動SonarScanner
SonarScanner.MSBuild.exe begin /k:"taskproject" /d:sonar.host.url="http://127.0.0.1:9000" /d:sonar.login="9ef26bd5d79f1893a0bfe91d572a04a04b12908a" /d:sonar.cs.dotcover.reportsPaths="report.html"

## Msbuild
MSBuild.exe /t:Rebuild

## 結束分析
SonarScanner.MSBuild.exe end /d:sonar.login="9ef26bd5d79f1893a0bfe91d572a04a04b12908a"

執行完上述步驟,應該可以在網站上面看到分析結果

但是,測試覆蓋率的地方是不是有點問題?怎麼都是 0 呢?

讓 SonarQube 正確顯示單元測試覆蓋率

依照先前的步驟,應該可以看到一些指標數據,但是在單元測試的覆蓋率應該都是看不到的,因為 sonarQube 還必須要經過其他的方式取得單元測試的數據才能正確顯示,更詳細一點的文件可以參考官方文章C# Plugin,這兩篇文章的內容大致上就是說明了一下官方建議的做法,有興趣可以研究一下

官方文件的重點有

  1. 需要用工具產生報告,工具可以選擇 dotCover, NCover, OpenCover, PartCover 其中一個
  2. 每一種工具所支援的報告格式不太一樣,使用前須詳閱說明書,以 dotCover 為例,需要給 html 格式報告,並透過sonar.cs.dotcover.reportsPaths參數指定

因為 sonar 支援的三種工具,我已經有購買了 dotCover,所以當然首選使用它作為覆蓋率的工具,在使用上需要注意的是,從官方下載記得要選Command Line Tools,因為已經有授權,所以我也不是很清楚沒有授權的話會發生甚麼事情,但應該也可以用OpenCover代替

查閱了 dotCover 的文件,產生單一單元測試專案的報告可透過指令,當然指令也支援設定檔(xml)

可透過dotCover.exe help analyse cover.xml 指令產生一個範例設定檔,將正確設定填入即可

將設定檔設定如下

這樣子就可以直接透過指令進行分析、並產生報告

如果想要用指令列參數替代上面這個設定檔,可改寫成下列型式

dotCover cover /TargetExecutable="C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe" /TargetArguments="TaskProjectTests.dll" /TargetWorkingDir="TaskProjectTests\bin\debug" /Output="report.html" /ReportType="HTML"

產生完畢報告之後,可以透過瀏覽器觀看

但是分析居然連測試專案都一起顯示了,我希望能夠聚焦在我的 lib,而不要顯示測試專案的數據

這當然也是可以透過排除的方式來設定;而如果有多個專案要合併測試結果,則需要為每一個測試專案先產生報告的快照,再將這些快照合併,最終將合併的結果轉換為 Html 格式的報告才可以用

更多細節就請參考文件Coverage Analysis from the Command Line

將專案根目錄下的coverage.xml加入 Filter 區段的設置如下

<?xml version="1.0" encoding="utf-8"?>
<CoverageParams>
  <TargetExecutable>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe</TargetExecutable>
  <TargetArguments>TaskProjectTests.dll</TargetArguments>
  <TargetWorkingDir>TaskProjectTests\bin\debug</TargetWorkingDir>
  <Output>report.html</Output>
  <ReportType>HTML</ReportType>
  <InheritConsole>True</InheritConsole>
  <AnalyzeTargetArguments>True</AnalyzeTargetArguments>
  <Filters>
    <ExcludeFilters>
      <FilterEntry>
        <ModuleMask>TaskProjectTests</ModuleMask>
        <ClassMask>*</ClassMask>
        <FunctionMask>*</FunctionMask>
      </FilterEntry>
    </ExcludeFilters>
  </Filters>
</CoverageParams>

這次就只有 lib 的數據了

將報告結果加入 sonarqube 顯示

依照官方的說法,我們必須將 dotCover 的 HTML 格式報告,透過參數指定讓 sonarScanner 取得,加入參數/d:sonar.cs.dotcover.reportsPaths="report.html" 之後的語法如下


## 啟動SonarScanner
SonarScanner.MSBuild.exe begin /k:"taskproject" /d:sonar.host.url="http://127.0.0.1:9000" /d:sonar.login="9ef26bd5d79f1893a0bfe91d572a04a04b12908a" /d:sonar.cs.dotcover.reportsPaths="report.html"

## Msbuild
MSBuild.exe /t:Rebuild

## 結束分析
SonarScanner.MSBuild.exe end /d:sonar.login="9ef26bd5d79f1893a0bfe91d572a04a04b12908a"

測試覆蓋率已經能正確顯示了

實際範例

下面這個是我實際的執行指令,專案是.netCore 2.1

# 產生測試報告
dotCover cover --TargetExecutable="C:\\Program Files\\dotnet\\dotnet.exe" --TargetWorkingDir="myproject.Tests" --TargetArguments="test \\"myproject.Tests.csproj\\"" --Filters=-:myproject.Tests --output=AppCoverageReport.html --reportType=HTML

利用dotnet tool install --global dotnet-sonarscanner的指令安裝 dotnet 外掛

dotnet sonarscanner begin /d:sonar.host.url="http://127.0.0.1:9090" /k:"myproject" /d:sonar.login="2c244539263ac8b5c4b4414b2b8c190a8ca873d9" /d:sonar.cs.dotcover.reportsPaths="AppCoverageReport.html"

dotnet build project.sln

dotnet sonarscanner end /d:sonar.login="2c244539263ac8b5c4b4414b2b8c190a8ca873d9"

於 Pipeline 專案當中設定

手動執行成功後,將其透過 jenkins 的 pipeline syntax 的幫助,我們可以將需要執行的指令透過 groovy 語法寫出來

     stage('build + SonarQube') {
        steps {
           bat label: '', script: 'D:\\art\\programs\\sonar-scanner-msbuild-4.7.1.2311-net46\\SonarScanner.MSBuild.exe begin /k:"taskproject" /d:sonar.host.url="http://127.0.0.1:9000" /d:sonar.login="9ef26bd5d79f1893a0bfe91d572a04a04b12908a" /d:sonar.cs.dotcover.reportsPaths="report.html"'

           bat label: '', script: 'MSBuild.exe /t:Rebuild'

           bat label: '', script: 'D:\\art\\programs\\sonar-scanner-msbuild-4.7.1.2311-net46\\SonarScanner.MSBuild.exe end /d:sonar.login="9ef26bd5d79f1893a0bfe91d572a04a04b12908a"'
        }
     }

而整個 pipeline 需要執行的動作分別是

  1. 取得原始檔案
  2. nuget 還原套件
  3. 先建置第一次產生測試專案的 dll 給 dotCover 產生報告用
  4. 呼叫 dotCover 產生報告
  5. 啟用 sonarScanner 準備蒐集資訊,同時給予測試報告
  6. 專案重新建置
  7. 關閉 sonarScanner,分析資訊

所以整體的jenkinsFile設定如下

pipeline {
    agent any

    stages {
        stage('git') {
            steps {
                git credentialsId: 'e3b7e18a-ea0f-48be-8d8f-a1d214c3c351', url: 'https://github.com/partypeopleland/TaskProject'
            }
        }

        stage('nuget') {
            steps {
                bat label: '', script: 'nuget restore TaskProject.sln'
            }
        }

        stage('build for testDLL') {
            steps {
                bat label: '', script: 'msbuild /p:Configuration=Debug'
            }
        }

        stage('analyse + unittest') {
            steps {
                bat label: '', script: '"D:\\art\\programs\\JetBrains.dotCover.CommandLineTools.2019.3.1\\dotCover.exe" analyse coverage.xml'
            }
        }

        stage('build + SonarQube') {
            steps {
                bat label: '', script: 'D:\\art\\programs\\sonar-scanner-msbuild-4.7.1.2311-net46\\SonarScanner.MSBuild.exe begin /k:"taskproject" /d:sonar.host.url="http://127.0.0.1:9000" /d:sonar.login="9ef26bd5d79f1893a0bfe91d572a04a04b12908a" /d:sonar.cs.dotcover.reportsPaths="report.html"'
                bat label: '', script: 'MSBuild.exe /t:Rebuild'
                bat label: '', script: 'D:\\art\\programs\\sonar-scanner-msbuild-4.7.1.2311-net46\\SonarScanner.MSBuild.exe end /d:sonar.login="9ef26bd5d79f1893a0bfe91d572a04a04b12908a"'
            }
        }
    }
 }

Jenkins Console 亂碼

參考保哥的文章Jenkins on Windows 心得分享 (03):有效避免記錄檔或訊息出現亂碼的方法

  1. 將 Java 的預設字集修改為 UTF-8 編碼:SETX /M JAVA_TOOL_OPTIONS -Dfile.encoding=UTF8
  2. 將自訂的「執行 Windows 批次指令」的第一行都加上以下命令:chcp 65001
    stage('sonar end') {
        steps {
            bat label: '', script: '''
            chcp 65001
            SonarScanner.MSBuild.exe end /d:sonar.login="32aafa7ac56a55dae90d0891487e7af98506ed33"
            '''
        }
    }