This commit is contained in:
Anupong Hompan 2025-10-21 12:12:52 +07:00
parent 7ade39e07f
commit 23eb4b7b4a

91
Jenkinsfile vendored
View File

@ -14,12 +14,12 @@ pipeline {
DOTNET_ROOT = "${WORKSPACE}/.dotnet" DOTNET_ROOT = "${WORKSPACE}/.dotnet"
PATH = "${DOTNET_ROOT}:${PATH}" PATH = "${DOTNET_ROOT}:${PATH}"
// Dependency-Check cache (แมพกับ volume/โฟลเดอร์บน Jenkins) // Dependency-Check cache
DC_DATA = "${JENKINS_HOME}/.dc-cache" DC_DATA = "${JENKINS_HOME}/.dc-cache"
// ถ้าจะใช้ SonarQube ให้ตั้งค่าตามระบบจริง // SonarQube
SONARQUBE_INSTANCE = 'SonarQube' SONARQUBE_INSTANCE = 'SonarQube'
SONAR_PROJECT_KEY = 'AS400API' SONAR_PROJECT_KEY = 'AS400API'
} }
stages { stages {
@ -50,7 +50,6 @@ pipeline {
echo "Installing ICU package: ${PKG}" echo "Installing ICU package: ${PKG}"
apt-get install -y --no-install-recommends "${PKG}" apt-get install -y --no-install-recommends "${PKG}"
else else
# Fallback: dev package also provides the libs (heavier)
echo "Falling back to libicu-dev..." echo "Falling back to libicu-dev..."
apt-get install -y --no-install-recommends libicu-dev apt-get install -y --no-install-recommends libicu-dev
fi fi
@ -88,11 +87,7 @@ pipeline {
unzip -oq depcheck.zip -d dependency-check unzip -oq depcheck.zip -d dependency-check
DC_BIN="dependency-check/dependency-check/bin/dependency-check.sh" DC_BIN="dependency-check/dependency-check/bin/dependency-check.sh"
# อัปเดตฐานข้อมูลครั้งแรก (และทุกครั้งที่ cache ว่าง)
bash "$DC_BIN" --data "${DC_DATA}" --updateonly || true bash "$DC_BIN" --data "${DC_DATA}" --updateonly || true
# สแกนจริง (เร็ว เพราะใช้ cache)
bash "$DC_BIN" -f HTML -f XML \ bash "$DC_BIN" -f HTML -f XML \
--project "AS400API" \ --project "AS400API" \
--scan . \ --scan . \
@ -104,7 +99,6 @@ pipeline {
post { post {
always { always {
archiveArtifacts artifacts: 'depcheck/**', allowEmptyArchive: true archiveArtifacts artifacts: 'depcheck/**', allowEmptyArchive: true
// ถ้ามี HTML Publisher plugin จะโชว์รายงานสวยขึ้น
script { script {
try { try {
publishHTML(target: [ publishHTML(target: [
@ -116,50 +110,39 @@ pipeline {
allowMissing: true allowMissing: true
]) ])
} catch (Throwable e) { } catch (Throwable e) {
echo "Skipping HTML report publish (plugin unavailable?): ${e.getClass().getSimpleName()}" echo "Skipping HTML report publish: ${e.getClass().getSimpleName()}"
} }
} }
} }
} }
} }
stage('SAST') { stage('SAST with SonarQube') {
steps { steps {
script { withSonarQubeEnv("${SONARQUBE_INSTANCE}") {
if (env.SONARQUBE_INSTANCE?.trim()) { sh '''
withSonarQubeEnv(env.SONARQUBE_INSTANCE) { set -euo pipefail
sh """ echo "=== SAST with SonarQube ==="
set -euo pipefail
echo "=== SAST with SonarQube (${env.SONARQUBE_INSTANCE}) ===" dotnet tool update --global dotnet-sonarscanner
dotnet tool update --global dotnet-sonarscanner || dotnet tool install --global dotnet-sonarscanner export PATH="$PATH:/root/.dotnet/tools"
export PATH="$HOME/.dotnet/tools:${PATH}"
dotnet clean -c Release dotnet sonarscanner begin \
/k:"'"${SONAR_PROJECT_KEY}"'" \
/d:sonar.host.url="$SONAR_HOST_URL" \
/d:sonar.login="$SONAR_AUTH_TOKEN" \
/d:sonar.exclusions="**/bin/**,**/obj/**" \
/d:sonar.test.exclusions="**/*.Tests/**"
dotnet sonarscanner begin \ dotnet clean -c Release
/k:"${env.SONAR_PROJECT_KEY}" \ # สำคัญ: ปิด warnings-as-errors
/d:sonar.host.url="\$SONAR_HOST_URL" \ dotnet build -c Release /warnaserror- -p:TreatWarningsAsErrors=false
/d:sonar.login="\$SONAR_AUTH_TOKEN" '''
}
dotnet build -c Release \ }
-p:TreatWarningsAsErrors=false \ post {
-warnaserror always {
sh 'dotnet sonarscanner end /d:sonar.login="$SONAR_AUTH_TOKEN" || true'
dotnet sonarscanner end /d:sonar.login="\$SONAR_AUTH_TOKEN"
"""
}
} else {
sh '''
set -euo pipefail
echo "=== SAST with Roslyn analyzers (no Sonar) ==="
dotnet clean -c Release
dotnet build -c Release \
-p:EnableNETAnalyzers=true \
-p:TreatWarningsAsErrors=true \
-warnaserror
'''
}
} }
} }
} }
@ -168,10 +151,7 @@ pipeline {
steps { steps {
sh ''' sh '''
set -euo pipefail set -euo pipefail
# รันเทส + สร้างผลลัพธ์ JUnit XML + Coverage (Cobertura)
dotnet build -c Debug dotnet build -c Debug
dotnet test \ dotnet test \
--logger "junit;LogFileName=test-results.xml" \ --logger "junit;LogFileName=test-results.xml" \
--results-directory "TestResults" \ --results-directory "TestResults" \
@ -179,7 +159,6 @@ pipeline {
/p:CoverletOutput=coverage/ \ /p:CoverletOutput=coverage/ \
/p:CoverletOutputFormat=cobertura /p:CoverletOutputFormat=cobertura
# รวม coverage ไว้ที่เดียวเพื่อ publish/เก็บ artifacts
mkdir -p coverage-report mkdir -p coverage-report
COB=$(find . -type f -name "coverage.cobertura.xml" | head -n1 || true) COB=$(find . -type f -name "coverage.cobertura.xml" | head -n1 || true)
if [ -n "$COB" ]; then if [ -n "$COB" ]; then
@ -189,14 +168,8 @@ pipeline {
} }
post { post {
always { always {
// รายงานผลเทส (JUnit)
junit allowEmptyResults: false, testResults: '**/TestResults/**/*.xml' junit allowEmptyResults: false, testResults: '**/TestResults/**/*.xml'
// เก็บไฟล์ coverage
archiveArtifacts artifacts: 'coverage-report/**', allowEmptyArchive: true archiveArtifacts artifacts: 'coverage-report/**', allowEmptyArchive: true
// (ทางเลือก) ถ้าติดตั้ง Coverage plugin
// publishCoverage adapters: [coberturaAdapter('coverage-report/Cobertura.xml')], sourceFileResolver: sourceFiles('STORE_LAST_BUILD')
} }
} }
} }
@ -214,11 +187,19 @@ pipeline {
} }
} }
} }
}
stage('Quality Gate') {
steps {
timeout(time: 30, unit: 'MINUTES') {
waitForQualityGate abortPipeline: true
}
}
}
} // end stages
post { post {
always { always {
echo "Pipeline finished (status: ${currentBuild.currentResult})" echo "Pipeline finished (status: ${currentBuild.currentResult})"
} }
} }
} } // end pipeline