AS400_API_DOTNET/Jenkinsfile

245 lines
9.9 KiB
Plaintext
Raw Normal View History

2025-10-17 17:19:55 +07:00
pipeline {
2025-10-28 13:07:28 +07:00
agent any
2025-10-21 00:08:03 +07:00
2025-10-27 21:48:23 +07:00
options {
ansiColor('xterm')
timestamps()
buildDiscarder(logRotator(numToKeepStr: '15'))
disableConcurrentBuilds()
skipDefaultCheckout(true)
}
2025-10-17 17:19:55 +07:00
2025-10-27 21:48:23 +07:00
parameters {
2025-10-28 00:27:55 +07:00
string(name: 'GIT_URL', defaultValue: 'file:///repos/AS400API.git', description: 'Git repository URL to clone')
2025-10-27 22:47:50 +07:00
string(name: 'GIT_BRANCH', defaultValue: 'main', description: 'Branch or ref to build')
2025-10-28 00:49:59 +07:00
// string(name: 'AGENT_LABEL', defaultValue: '', description: 'Optional Jenkins agent label with Docker CLI access (leave blank for default node)')
2025-10-27 21:48:23 +07:00
}
2025-10-21 00:08:03 +07:00
2025-10-27 21:48:23 +07:00
environment {
DOTNET_CLI_TELEMETRY_OPTOUT = '1'
DOTNET_SKIP_FIRST_TIME_EXPERIENCE = '1'
DOTNET_NOLOGO = '1'
2025-10-27 22:47:50 +07:00
BUILD_CONFIGURATION = 'Release'
TEST_RESULTS_DIR = 'TestResults'
COVERAGE_DIR = 'TestResults/Coverage'
COVERAGE_FILE = 'TestResults/Coverage/coverage.opencover.xml'
2025-10-28 15:42:29 +07:00
COVERAGE_HTML_DIR = 'TestResults/Coverage/html'
2025-10-28 13:54:54 +07:00
// Path ติดตั้ง dotnet ชั่วคราวใน pipeline
DOTNET_ROOT = "${WORKSPACE}/.dotnet"
PATH = "${DOTNET_ROOT}:${PATH}"
2025-10-27 22:47:50 +07:00
PUBLISH_DIR = 'publish/Release'
2025-10-27 21:48:23 +07:00
SONAR_PROJECT_KEY = 'AS400API'
2025-10-27 22:47:50 +07:00
SONAR_PROJECT_NAME = 'AS400API'
2025-10-27 21:48:23 +07:00
}
2025-10-21 00:08:03 +07:00
2025-10-27 21:48:23 +07:00
stages {
stage('Checkout') {
steps {
2025-10-27 22:47:50 +07:00
// Fetch the requested branch from the configured Git URL.
2025-10-27 21:48:23 +07:00
script {
2025-10-27 22:47:50 +07:00
if (!params.GIT_URL?.trim()) {
error 'GIT_URL parameter must not be empty.'
2025-10-27 21:48:23 +07:00
}
2025-10-27 22:47:50 +07:00
String branchSpec = params.GIT_BRANCH?.trim() ?: 'main'
if (!branchSpec.startsWith('*/')) {
branchSpec = "*/${branchSpec}"
2025-10-27 21:48:23 +07:00
}
checkout([
$class : 'GitSCM',
2025-10-27 22:47:50 +07:00
branches : [[name: branchSpec]],
2025-10-27 21:48:23 +07:00
doGenerateSubmoduleConfigurations: false,
extensions : [[$class: 'CloneOption', depth: 0, noTags: false, shallow: false]],
2025-10-27 22:47:50 +07:00
userRemoteConfigs : [[url: params.GIT_URL.trim()]]
2025-10-27 21:48:23 +07:00
])
}
}
}
2025-10-21 00:08:03 +07:00
2025-10-27 22:47:50 +07:00
stage('.NET SDK Info') {
2025-10-27 21:48:23 +07:00
steps {
2025-10-27 22:47:50 +07:00
// Sanity check the SDK and install required global tools (Java for Sonar + dotnet-sonarscanner).
sh '''#!/bin/bash -e
2025-10-27 21:48:23 +07:00
apt-get update
2025-10-28 13:18:40 +07:00
apt-get install -y --no-install-recommends openjdk-21-jre-headless
2025-10-17 17:19:55 +07:00
2025-10-28 13:57:03 +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}"
2025-10-28 14:03:00 +07:00
else
echo "Installing fallback ICU development package..."
apt-get install -y --no-install-recommends libicu-dev
2025-10-28 13:57:03 +07:00
fi
fi
2025-10-28 14:03:00 +07:00
rm -rf /var/lib/apt/lists/*
2025-10-28 13:57:03 +07:00
2025-10-28 13:07:28 +07:00
# Install .NET SDK locally for the build user
mkdir -p "${WORKSPACE}/.dotnet"
curl -fsSL https://dot.net/v1/dotnet-install.sh -o dotnet-install.sh
bash dotnet-install.sh --channel 9.0 --install-dir "${WORKSPACE}/.dotnet"
2025-10-27 22:47:50 +07:00
echo '### dotnet --info'
2025-10-27 21:48:23 +07:00
dotnet --info
2025-10-17 17:19:55 +07:00
2025-10-28 15:42:29 +07:00
echo '### Install/Update dotnet tooling'
2025-10-27 22:47:50 +07:00
dotnet tool install --global dotnet-sonarscanner || dotnet tool update --global dotnet-sonarscanner
2025-10-28 15:42:29 +07:00
dotnet tool install --global dotnet-reportgenerator-globaltool || dotnet tool update --global dotnet-reportgenerator-globaltool
2025-10-27 21:48:23 +07:00
'''
2025-10-27 22:47:50 +07:00
script {
2025-10-28 18:33:31 +07:00
String dotnetToolsPath = "${env.HOME ?: '/var/jenkins_home'}/.dotnet/tools"
if (!env.PATH.contains(dotnetToolsPath)) {
env.PATH = "${env.PATH}:${dotnetToolsPath}"
2025-10-27 23:01:50 +07:00
}
2025-10-27 22:47:50 +07:00
}
2025-10-27 21:48:23 +07:00
}
}
2025-10-25 22:25:21 +07:00
2025-10-27 21:48:23 +07:00
stage('Restore') {
steps {
2025-10-27 22:47:50 +07:00
// Restore solution dependencies.
sh '''#!/bin/bash -e
2025-10-27 21:48:23 +07:00
dotnet restore AS400API.sln
'''
}
}
2025-10-25 22:25:21 +07:00
2025-10-27 22:47:50 +07:00
stage('Build') {
2025-10-27 21:48:23 +07:00
steps {
2025-10-27 22:47:50 +07:00
// Build the solution in Release mode without failing on warnings.
sh '''#!/bin/bash -e
dotnet build AS400API.sln --configuration "${BUILD_CONFIGURATION}" --no-restore
'''
}
}
2025-10-20 12:01:53 +07:00
2025-10-27 22:47:50 +07:00
stage('Test & Coverage') {
steps {
// Execute tests once for fast feedback and capture TRX + OpenCover reports.
sh '''#!/bin/bash -e
rm -rf "${TEST_RESULTS_DIR}"
mkdir -p "${COVERAGE_DIR}"
2025-10-28 15:42:29 +07:00
mkdir -p "${COVERAGE_HTML_DIR}"
2025-10-27 22:47:50 +07:00
dotnet test AS400API.sln --configuration "${BUILD_CONFIGURATION}" --no-build \
/p:CollectCoverage=true \
/p:CoverletOutputFormat=opencover \
2025-10-28 16:21:26 +07:00
/p:CoverletOutput="${WORKSPACE}/${COVERAGE_DIR}/" \
2025-10-28 15:37:14 +07:00
--results-directory "${TEST_RESULTS_DIR}" \
2025-10-27 22:47:50 +07:00
--logger "trx;LogFileName=test-results.trx" \
--logger "junit;LogFileName=test-results.xml"
2025-10-28 15:42:29 +07:00
2025-10-28 15:49:52 +07:00
"${HOME}/.dotnet/tools/reportgenerator" \
2025-10-28 15:42:29 +07:00
-reports:"${COVERAGE_FILE}" \
-targetdir:"${COVERAGE_HTML_DIR}" \
-reporttypes:"HtmlInline_AzurePipelines;Cobertura"
2025-10-27 22:47:50 +07:00
'''
junit testResults: "${TEST_RESULTS_DIR}/**/*.xml", allowEmptyResults: false
2025-10-28 15:42:29 +07:00
publishHTML(target: [
allowMissing : false,
keepAll : true,
reportDir : "${COVERAGE_HTML_DIR}",
reportFiles : 'index.html',
reportName : 'Code Coverage'
])
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-27 22:47:50 +07:00
stage('SonarQube: Begin') {
2025-10-27 21:48:23 +07:00
steps {
2025-10-27 22:47:50 +07:00
// Kick off Sonar analysis with project metadata and coverage location.
2025-10-27 21:48:23 +07:00
script {
2025-10-27 22:47:50 +07:00
env.SONAR_PROJECT_VERSION = env.BUILD_NUMBER ?: '0.0.0'
}
2025-10-28 18:28:53 +07:00
withSonarQubeEnv('SonarQube') {
2025-10-27 22:47:50 +07:00
sh '''#!/bin/bash -e
2025-10-28 20:11:04 +07:00
# Ensure scanner is available and PATH includes global tools
dotnet tool update --global dotnet-sonarscanner
2025-10-28 20:35:30 +07:00
export PATH="$PATH:${HOME}/.dotnet/tools"
2025-10-28 20:11:04 +07:00
2025-10-28 20:25:54 +07:00
# dotnet sonarscanner begin \
# /k:"${SONAR_PROJECT_KEY}" \
# /n:"${SONAR_PROJECT_NAME}" \
# /v:"${SONAR_PROJECT_VERSION}" \
# /d:sonar.host.url="${SONAR_HOST_URL}" \
# /d:sonar.login="${SONAR_AUTH_TOKEN}" \
# /d:sonar.cs.opencover.reportsPaths="${COVERAGE_FILE}"
2025-10-28 20:11:04 +07:00
2025-10-27 22:47:50 +07:00
dotnet sonarscanner begin \
2025-10-28 20:11:04 +07:00
/k:AS400API \
2025-10-28 20:49:41 +07:00
/d:sonar.cs.opencover.reportsPaths="${COVERAGE_FILE}" \
/d:sonar.exclusions="TestResults/Coverage/html/**"
2025-10-28 20:11:04 +07:00
2025-10-27 22:47:50 +07:00
'''
2025-10-27 21:48:23 +07:00
}
}
2025-10-21 12:12:52 +07:00
}
2025-10-25 21:52:18 +07:00
2025-10-27 22:47:50 +07:00
stage('Rebuild for Sonar') {
2025-10-27 21:48:23 +07:00
steps {
2025-10-27 22:47:50 +07:00
// Run a rebuild under the Sonar scanner context so analysis picks up compilation data.
sh '''#!/bin/bash -e
dotnet build AS400API.sln --configuration "${BUILD_CONFIGURATION}" --no-restore --no-incremental
2025-10-27 21:48:23 +07:00
'''
}
2025-10-17 17:19:55 +07:00
}
2025-10-27 22:47:50 +07:00
stage('SonarQube: End') {
2025-10-27 21:48:23 +07:00
steps {
2025-10-27 22:47:50 +07:00
// Close the Sonar analysis and push data to the server.
2025-10-28 18:28:53 +07:00
withSonarQubeEnv('SonarQube') {
2025-10-27 22:47:50 +07:00
sh '''#!/bin/bash -e
2025-10-28 20:35:30 +07:00
export PATH="$PATH:${HOME}/.dotnet/tools"
2025-10-28 20:29:01 +07:00
dotnet sonarscanner end
2025-10-27 22:47:50 +07:00
'''
2025-10-27 21:48:23 +07:00
}
}
2025-10-21 00:08:03 +07:00
}
2025-10-21 12:12:52 +07:00
2025-10-27 22:47:50 +07:00
stage('Quality Gate') {
2025-10-27 21:48:23 +07:00
steps {
2025-10-27 22:47:50 +07:00
// Wait for the Quality Gate result and fail fast on non-green outcomes.
timeout(time: 15, unit: 'MINUTES') {
2025-10-27 22:59:05 +07:00
script {
def qualityGate = waitForQualityGate()
if (qualityGate.status != 'OK') {
error "SonarQube Quality Gate failed: ${qualityGate.status}"
}
2025-10-27 22:47:50 +07:00
}
}
2025-10-27 21:48:23 +07:00
}
}
2025-10-27 22:47:50 +07:00
stage('Archive') {
2025-10-27 21:48:23 +07:00
steps {
2025-10-27 22:47:50 +07:00
// Publish Release-ready binaries and test artifacts for downstream consumption.
sh '''#!/bin/bash -e
rm -rf "${PUBLISH_DIR}"
dotnet publish AS400API.sln --configuration "${BUILD_CONFIGURATION}" --no-restore --output "${PUBLISH_DIR}"
2025-10-27 21:48:23 +07:00
'''
2025-10-27 22:47:50 +07:00
archiveArtifacts artifacts: "${PUBLISH_DIR}/**", fingerprint: true
archiveArtifacts artifacts: "${TEST_RESULTS_DIR}/**/*.trx", allowEmptyArchive: true
archiveArtifacts artifacts: "${COVERAGE_FILE}", allowEmptyArchive: true
2025-10-28 15:42:29 +07:00
archiveArtifacts artifacts: "${COVERAGE_HTML_DIR}/**", allowEmptyArchive: true
2025-10-27 21:48:23 +07:00
}
2025-10-21 12:12:52 +07:00
}
}
2025-10-21 00:08:03 +07:00
2025-10-27 21:48:23 +07:00
post {
always {
2025-10-27 23:25:17 +07:00
script {
try {
deleteDir()
} catch (org.jenkinsci.plugins.workflow.steps.MissingContextVariableException ignore) {
echo 'Workspace cleanup skipped because no workspace was provisioned.'
}
}
2025-10-27 21:48:23 +07:00
}
2025-10-17 17:19:55 +07:00
}
2025-10-27 21:48:23 +07:00
}