--- title: 在Jenkins內如何將具體編譯.Net程式碼錯誤導入通知系統 tags: [Jenkins,PowerShell,DotNet,blog] --- {%hackmd @hackmd/blog-article-base-layout %} <style> .blog-date { margin-top: 60px; } .blog-date.no-margin { margin-top: 20px; } body .ui-toc, #ui-toc-affix { display: none !important; } body > #doc.markdown-body { padding-top: 20px; } #doc.markdown-body h2 { margin-top: 5px; } </style> :arrow_left: [回到部落格首頁](/@siugar/blog-tw) :arrow_right: <a target="_blank" href="/@siugar/ExtractBuildError">本文單篇連結</a> # 在Jenkins內如何將具體編譯.Net程式碼錯誤導入通知系統 ## 前言 因為專案的運營人員使用Jenkins製作正式區版本,但因為我們開發人員沒有正式區的Jenkins權限,所以如果發生編譯錯誤,只知道發生錯誤,但無法得知具體編譯錯誤發生甚麼錯誤,在哪個檔案的哪一行。 ![image](https://hackmd.io/_uploads/S1X-Nbn91x.png) 因為我之前有導入Jenkins運用MsBuild編譯C++服務器程式上版的經驗,所以後來我主動提出來協助解決這一塊。 最後完成結果如下,可以清楚知道是哪一個檔案的哪一行發生甚麼錯誤 ![image](https://hackmd.io/_uploads/rJyEE-3qke.png) ## 本文 # 流程圖:取得編譯錯誤資訊 ![image](https://hackmd.io/_uploads/Bkawrcchye.png) ```javascript node('windows-node') { ws('D:\\Dev_git\\your_project_path') { try { stage('Git') { deleteDir() checkout([$class: 'GitSCM', branches: [[name: 'origin/feature/your_project_git_path']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'a1a2...', url: 'your_project_git_url.git']] ]) } stage('BuildProject') { script { // 執行 dotnet publish 並將輸出寫入日誌文件 def status = powershell returnStatus: true, script: """ chcp 65001 \$projectPath = "D:\\Dev_git\\project_path\\your_project.csproj" \$logFile = "build_output.log" \$errorLogFile = "error.log" \$tempErrorLogFile = "temp_error.log" # 記錄當前目錄 \$currentDir = Get-Location # 使用 dotnet build 並將輸出同時寫入控制台和日誌文件 \$buildCommand = "dotnet build `"\$projectPath`" --configuration Release -v m" Invoke-Expression "\$buildCommand 2>&1 | Tee-Object -FilePath \$logFile" # 檢查構建結果 \$buildOutput = Get-Content -Path \$logFile -Raw if (\$buildOutput -match "error") { # 如果構建失敗,將包含 "error" 的行寫入臨時錯誤日誌文件 Select-String -Pattern "error" -Path \$logFile | ForEach-Object { \$_.Line } | Set-Content -Path \$tempErrorLogFile -Encoding utf8 # 使用 PowerShell 進行排序和去重操作,確保使用 UTF-8 編碼 Get-Content -Path \$tempErrorLogFile -Encoding utf8 | Sort-Object -Unique | Set-Content -Path \$errorLogFile -Encoding utf8 # 刪除臨時錯誤日誌文件 Remove-Item -Path \$tempErrorLogFile } else { # 如果構建成功,刪除錯誤日誌文件 if (Test-Path -Path \$errorLogFile) { Remove-Item -Path \$errorLogFile } } """ if (status != 0) { def logContent = readFile('error.log') echo "Build failed with status ${status}. Log content:\n${logContent}" error "Build failed with status ${status}" } } } stage('PublishProject') { bat returnStdout: true, script: '''c: chcp 65001 dotnet publish "D:\\Dev_git\\project_path\\your_project.csproj" -o D:\\Deployment\\output_path -c Release -r win-x64 --no-self-contained ''' } } catch (Exception e) { currentBuild.result = 'FAILURE' throw e } finally { stage('Post') { script { // 發送 Office 365 Connector 通知 if (currentBuild.result == 'FAILURE') { def errorMsg = "Unknow" if (fileExists('error.log')) { errorMsg = readFile(file: "error.log") echo errorMsg } sendNotification(currentBuild.result, errorMsg) } } } } } } def sendNotification(buildStatus, logContent) { def webhookUrl = 'https://your_webhook_url' def buildNumber = currentBuild.number def startedBy = currentBuild.getBuildCauses()[0].userName ?: 'Unknown' def failingSince = getFirstFailingBuildNumber(currentBuild) def repeatedFailure = currentBuild.previousBuild?.result == 'FAILURE' ? 'Yes' : 'No' def facts = [ [name: 'Failing since', template: "build #${failingSince}"], [name: 'Remarks', template: "Started by user ${startedBy}"] ] office365ConnectorSend webhookUrl: webhookUrl, color:"#FF0000", message: "Latest status of build #${buildNumber}<br> Build failed. See details below.<br> ${logContent}", status: "Failed", factDefinitions: facts } ``` ## 重點說明 ### 1. Pipleline Script vs Groovy #### Groovy Script - Groovy Script 是一種通用的 Groovy 程式碼,可以用於各種自動化任務,不限於 Jenkins Pipeline。 - 它可以獨立執行,也可以嵌入到 Java 應用程式中。 - 在 Jenkins 中,您可以在「Script Console」(腳本控制台)中執行任意 Groovy Script,用於管理 Jenkins 實例、操作節點、檢索資訊等 ```Groovy Jenkins.instance.nodes.each { println it.displayName } ``` #### Pipeline Script - 專門用於定義 Jenkins Pipeline 的執行流程 - 強調結構化和可讀性,通過 stages、steps 等關鍵字來組織程式碼 - 本身也是Groovy script, 只是為了jenkins pipeline有額外的定義以及方法 ``` pipeline { agent any stages { stage('Build') { steps { sh 'mvn clean install' } } stage('Test') { steps { sh 'mvn test' } } stage('Deploy') { steps { sh 'deploy.sh' } } } } ``` ### 2. Tee-Object 這是PowerShell的指令,用於同時輸出寫入變數或檔案,並且保持輸出到Console,類似Linux的tee。 指令: ``` command | Tee-Object -FilePath <檔案路徑> ``` 範例: ``` $projectPath = "your_project.csproj" $logFile = "$PSScriptRoot\build_output.log" # 使用 dotnet build 並將輸出同時寫入控制台和日誌文件 $buildCommand = "dotnet build `"$projectPath`" --configuration Release -v m" Invoke-Expression "$buildCommand 2>&1 | Tee-Object -FilePath $logFile" ``` ## 結論 這段介紹的核心重點在於怎麼取出編譯失敗的訊息,其他的部分包含Jenkins與Pipleline script,只要有接觸過都可以編寫出來。而至於怎麼編寫出那一段power shell script有部分是靠AI的輔助,因為一開始我是用batch script編寫,但其中有一個需求是需要可以將訊息寫入檔案的同時,standard output仍然要可以正常運作,否則在jenkins或是平時debug看訊息會變成沒有資訊可以依靠,後來AI給的方案中Power shell script的Tee-Object是比較單純的解法,所以後來都在AI的依靠下完成這段簡單但又不單純的錯誤訊息擷取。