---
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權限,所以如果發生編譯錯誤,只知道發生錯誤,但無法得知具體編譯錯誤發生甚麼錯誤,在哪個檔案的哪一行。

因為我之前有導入Jenkins運用MsBuild編譯C++服務器程式上版的經驗,所以後來我主動提出來協助解決這一塊。
最後完成結果如下,可以清楚知道是哪一個檔案的哪一行發生甚麼錯誤

## 本文
# 流程圖:取得編譯錯誤資訊

```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的依靠下完成這段簡單但又不單純的錯誤訊息擷取。