AS400_API_DOTNET/Jenkinsfile

228 lines
7.0 KiB
Plaintext
Raw Normal View History

2025-10-17 17:19:55 +07:00
pipeline {
2025-10-21 00:08:03 +07:00
agent any
options {
ansiColor('xterm')
timestamps()
}
2025-10-17 17:19:55 +07:00
environment {
2025-10-21 00:08:03 +07:00
// ถ้าใช้ local bare repo
GIT_URL = 'file:///repos/AS400API.git'
// Path ติดตั้ง dotnet ชั่วคราวใน pipeline
DOTNET_ROOT = "${WORKSPACE}/.dotnet"
PATH = "${DOTNET_ROOT}:${PATH}"
2025-10-21 12:12:52 +07:00
// Dependency-Check cache
2025-10-21 00:08:03 +07:00
DC_DATA = "${JENKINS_HOME}/.dc-cache"
2025-10-21 12:12:52 +07:00
// SonarQube
2025-10-21 08:35:51 +07:00
SONARQUBE_INSTANCE = 'SonarQube'
2025-10-21 12:12:52 +07:00
SONAR_PROJECT_KEY = 'AS400API'
2025-10-17 17:19:55 +07:00
}
stages {
2025-10-25 22:25:21 +07:00
2025-10-25 22:26:12 +07:00
2025-10-25 22:25:21 +07:00
2025-10-21 00:08:03 +07:00
stage('Checkout') {
2025-10-20 12:15:23 +07:00
steps {
2025-10-21 00:08:03 +07:00
checkout([$class: 'GitSCM',
branches: [[name: '*/main']],
userRemoteConfigs: [[url: "${GIT_URL}"]]
])
2025-10-20 12:15:23 +07:00
}
}
2025-10-25 22:47:24 +07:00
2025-10-25 22:26:12 +07:00
2025-10-25 22:25:21 +07:00
2025-10-21 00:08:03 +07:00
stage('Install prerequisites') {
2025-10-17 17:19:55 +07:00
steps {
sh '''
2025-10-21 00:08:03 +07:00
set -euo pipefail
if command -v apt-get >/dev/null 2>&1; then
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y --no-install-recommends \
ca-certificates curl unzip jq git openjdk-21-jre-headless
2025-10-21 00:30:32 +07:00
# --- Install ICU runtime (Debian uses versioned package names) ---
if ! ldconfig -p | grep -qi libicu; then
PKG="$(apt-cache search '^libicu[0-9]+$' | awk '{print $1}' | head -n1 || true)"
if [ -n "$PKG" ]; then
echo "Installing ICU package: ${PKG}"
apt-get install -y --no-install-recommends "${PKG}"
else
echo "Falling back to libicu-dev..."
apt-get install -y --no-install-recommends libicu-dev
fi
fi
2025-10-20 12:21:18 +07:00
fi
2025-10-21 00:30:32 +07:00
# Install .NET SDK locally for the build user
mkdir -p "${WORKSPACE}/.dotnet"
2025-10-21 00:08:03 +07:00
curl -fsSL https://dot.net/v1/dotnet-install.sh -o dotnet-install.sh
2025-10-21 00:30:32 +07:00
bash dotnet-install.sh --channel 9.0 --install-dir "${WORKSPACE}/.dotnet"
2025-10-24 22:04:07 +07:00
bash dotnet-install.sh --channel 8.0 --install-dir "${WORKSPACE}/.dotnet"
2025-10-21 00:08:03 +07:00
2025-10-21 00:30:32 +07:00
export PATH="${WORKSPACE}/.dotnet:${PATH}"
dotnet --info
2025-10-20 12:01:53 +07:00
'''
}
}
2025-10-21 00:08:03 +07:00
stage('SCA (NuGet + OWASP)') {
2025-10-20 12:01:53 +07:00
steps {
sh '''
2025-10-20 12:32:04 +07:00
set -euo pipefail
2025-10-17 17:52:49 +07:00
echo "=== NuGet vulnerability audit ==="
2025-10-20 12:01:53 +07:00
dotnet restore
2025-10-17 17:19:55 +07:00
dotnet list package --vulnerable || true
2025-10-21 00:08:03 +07:00
echo "=== OWASP Dependency-Check ==="
2025-10-21 08:03:24 +07:00
rm -rf depcheck dependency-check
2025-10-17 17:19:55 +07:00
mkdir -p depcheck
2025-10-20 12:32:04 +07:00
API="https://api.github.com/repos/jeremylong/DependencyCheck/releases/latest"
2025-10-21 00:08:03 +07:00
echo "Resolving latest Dependency-Check..."
ASSET_URL=$(curl -fsSL "$API" | jq -r '.assets[]?.browser_download_url | select(test("release\\\\.zip$"))' | head -n1)
2025-10-20 12:32:04 +07:00
echo "Downloading: $ASSET_URL"
curl -fL --retry 3 --retry-all-errors -o depcheck.zip "$ASSET_URL"
2025-10-21 08:03:24 +07:00
unzip -oq depcheck.zip -d dependency-check
2025-10-20 12:32:04 +07:00
2025-10-21 00:08:03 +07:00
DC_BIN="dependency-check/dependency-check/bin/dependency-check.sh"
bash "$DC_BIN" --data "${DC_DATA}" --updateonly || true
bash "$DC_BIN" -f HTML -f XML \
--project "AS400API" \
--scan . \
--out depcheck \
--data "${DC_DATA}" \
2025-10-24 22:04:07 +07:00
--noupdate || true \
--disableAssembly
2025-10-17 17:19:55 +07:00
'''
2025-10-17 17:52:49 +07:00
}
post {
always {
archiveArtifacts artifacts: 'depcheck/**', allowEmptyArchive: true
2025-10-21 08:03:24 +07:00
script {
try {
publishHTML(target: [
reportName: 'OWASP Dependency-Check',
reportDir: 'depcheck',
reportFiles: 'dependency-check-report.html',
keepAll: true,
alwaysLinkToLastBuild: true,
allowMissing: true
])
} catch (Throwable e) {
2025-10-21 12:12:52 +07:00
echo "Skipping HTML report publish: ${e.getClass().getSimpleName()}"
2025-10-21 08:03:24 +07:00
}
}
2025-10-17 17:52:49 +07:00
}
2025-10-17 17:19:55 +07:00
}
}
2025-10-21 12:12:52 +07:00
stage('SAST with SonarQube') {
2025-10-17 17:19:55 +07:00
steps {
2025-10-21 15:27:54 +07:00
withSonarQubeEnv('SonarQube') {
2025-10-21 12:12:52 +07:00
sh '''
set -euo pipefail
echo "=== SAST with SonarQube ==="
2025-10-21 15:27:54 +07:00
# Ensure scanner is available and PATH includes global tools
2025-10-21 12:12:52 +07:00
dotnet tool update --global dotnet-sonarscanner
export PATH="$PATH:/root/.dotnet/tools"
2025-10-25 21:52:18 +07:00
# Optional: show Java & reachability
java -version || true
curl -sf "$SONAR_HOST_URL/api/system/health" || { echo "Cannot reach SonarQube at $SONAR_HOST_URL"; exit 1; }
# BEGIN (use injected URL/token; add verbose for diagnostics)
2025-10-21 12:12:52 +07:00
dotnet sonarscanner begin \
2025-10-21 15:27:54 +07:00
/k:AS400API \
2025-10-25 21:52:18 +07:00
/d:sonar.verbose=true \
2025-10-21 15:27:54 +07:00
/d:sonar.exclusions=**/bin/**,**/obj/** \
/d:sonar.test.exclusions=**/*.Tests/** \
/d:sonar.cs.opencover.reportsPaths=**/coverage.opencover.xml
2025-10-25 21:52:18 +07:00
# BUILD & TEST (produce OpenCover report)
2025-10-21 15:27:54 +07:00
dotnet restore
dotnet build -c Release
dotnet test AS400API.Tests/AS400API.Tests.csproj -c Release \
/p:CollectCoverage=true \
/p:CoverletOutput=./TestResults/coverage/ \
2025-10-25 21:52:18 +07:00
/p:CoverletOutputFormat=opencover
2025-10-21 15:27:54 +07:00
2025-10-25 21:52:18 +07:00
# END (no login flag; uses env from withSonarQubeEnv)
dotnet sonarscanner end /d:sonar.verbose=true
2025-10-21 12:12:52 +07:00
'''
}
2025-10-25 21:52:18 +07:00
// Optionally wait for the Quality Gate (only if 'end' succeeds)
2025-10-21 15:27:54 +07:00
// waitForQualityGate abortPipeline: true
2025-10-21 00:08:03 +07:00
}
}
stage('Test + Coverage') {
steps {
sh '''
set -euo pipefail
2025-10-21 08:16:01 +07:00
dotnet build -c Debug
2025-10-20 12:38:29 +07:00
dotnet test \
2025-10-21 00:08:03 +07:00
--logger "junit;LogFileName=test-results.xml" \
2025-10-21 08:09:21 +07:00
--results-directory "TestResults" \
2025-10-20 12:38:29 +07:00
/p:CollectCoverage=true \
/p:CoverletOutput=coverage/ \
/p:CoverletOutputFormat=cobertura
mkdir -p coverage-report
2025-10-21 00:08:03 +07:00
COB=$(find . -type f -name "coverage.cobertura.xml" | head -n1 || true)
if [ -n "$COB" ]; then
cp "$COB" coverage-report/Cobertura.xml
2025-10-20 12:38:29 +07:00
fi
'''
2025-10-17 17:19:55 +07:00
}
post {
always {
2025-10-21 00:08:03 +07:00
junit allowEmptyResults: false, testResults: '**/TestResults/**/*.xml'
2025-10-17 17:19:55 +07:00
archiveArtifacts artifacts: 'coverage-report/**', allowEmptyArchive: true
}
}
}
2025-10-21 00:08:03 +07:00
stage('Build') {
2025-10-17 17:19:55 +07:00
steps {
sh '''
2025-10-21 00:08:03 +07:00
set -euo pipefail
2025-10-21 12:53:09 +07:00
# build only the app project
dotnet restore AS400API.csproj
dotnet build AS400API.csproj -c Release -warnaserror:false -p:TreatWarningsAsErrors=false
# publish the app project (not the solution)
dotnet publish AS400API.csproj -c Release -o out --no-build
2025-10-17 17:19:55 +07:00
'''
}
2025-10-21 00:08:03 +07:00
post {
success {
archiveArtifacts artifacts: 'out/**', allowEmptyArchive: false
}
}
}
2025-10-21 12:12:52 +07:00
stage('Quality Gate') {
steps {
timeout(time: 30, unit: 'MINUTES') {
waitForQualityGate abortPipeline: true
}
}
}
} // end stages
2025-10-21 00:08:03 +07:00
post {
always {
echo "Pipeline finished (status: ${currentBuild.currentResult})"
2025-10-17 17:19:55 +07:00
}
}
2025-10-21 12:12:52 +07:00
} // end pipeline