AS400_API_DOTNET/Jenkinsfile

221 lines
7.7 KiB
Plaintext
Raw Normal View History

2025-10-17 17:19:55 +07:00
pipeline {
agent none
2025-10-17 17:52:49 +07:00
options { ansiColor('xterm'); timestamps() }
2025-10-17 17:19:55 +07:00
environment {
DOTNET_CLI_TELEMETRY_OPTOUT = '1'
DOTNET_SKIP_FIRST_TIME_EXPERIENCE = '1'
2025-10-20 12:01:53 +07:00
SONAR_PROJECT_KEY = 'as400api-dotnet'
2025-10-17 17:52:49 +07:00
SONAR_PROJECT_NAME = 'AS400_API_DOTNET (.NET 9)'
2025-10-17 17:19:55 +07:00
}
stages {
stage('Checkout') {
agent any
steps { checkout scm }
}
2025-10-20 12:15:23 +07:00
stage('Install prerequisites') {
agent any
steps {
sh '''
2025-10-20 12:21:18 +07:00
set -euo pipefail
2025-10-20 12:15:23 +07:00
install_deps() {
if command -v apt-get >/dev/null 2>&1; then
export DEBIAN_FRONTEND=noninteractive
2025-10-20 12:21:18 +07:00
apt-get update
# Common native deps for .NET + tools
2025-10-20 12:15:23 +07:00
apt-get install -y --no-install-recommends \
2025-10-20 12:21:18 +07:00
libkrb5-3 zlib1g libstdc++6 ca-certificates curl unzip jq
# Java (for OWASP Dependency-Check)
# prefer 17 headless; fall back to default-jre-headless
(apt-get install -y --no-install-recommends openjdk-17-jre-headless) || \
(apt-get install -y --no-install-recommends default-jre-headless) || true
# ---- ICU on Debian/Ubuntu ----
# 1) try meta/dev names (older releases)
apt-get install -y --no-install-recommends libicu || true
2025-10-20 12:15:23 +07:00
apt-get install -y --no-install-recommends libicu-dev || true
2025-10-20 12:21:18 +07:00
# 2) if still missing, detect versioned package (e.g., libicu74 on trixie)
if ! ldconfig -p 2>/dev/null | grep -qi 'libicu'; then
PKG="$(apt-cache search -n '^libicu[0-9]+$' | awk '{print $1}' | sort -V | tail -n1 || true)"
if [ -n "${PKG:-}" ]; then
echo "Installing detected ICU package: $PKG"
apt-get install -y --no-install-recommends "$PKG"
fi
fi
# 3) final sanity
if ! ldconfig -p 2>/dev/null | grep -qi 'libicu'; then
echo "WARN: ICU not found after install attempts (will rely on invariant mode if needed)"
fi
2025-10-20 12:15:23 +07:00
elif command -v dnf >/dev/null 2>&1; then
2025-10-20 12:21:18 +07:00
dnf install -y libicu krb5-libs zlib libstdc++ ca-certificates curl unzip java-17-openjdk-headless jq || true
2025-10-20 12:15:23 +07:00
elif command -v yum >/dev/null 2>&1; then
2025-10-20 12:21:18 +07:00
yum install -y libicu krb5-libs zlib libstdc++ ca-certificates curl unzip java-17-openjdk-headless jq || true
2025-10-20 12:15:23 +07:00
elif command -v apk >/dev/null 2>&1; then
2025-10-20 12:21:18 +07:00
apk add --no-cache icu-libs krb5-libs zlib libstdc++ ca-certificates curl unzip openjdk17-jre-headless jq || true
2025-10-20 12:15:23 +07:00
else
2025-10-20 12:21:18 +07:00
echo "Unsupported package manager. Please install ICU + Java manually."
2025-10-20 12:15:23 +07:00
exit 1
fi
}
install_deps
'''
}
}
2025-10-20 12:01:53 +07:00
// Bootstrap .NET SDK if dotnet is missing
stage('Bootstrap .NET SDK') {
agent any
2025-10-17 17:19:55 +07:00
steps {
sh '''
2025-10-17 17:52:49 +07:00
set -e
2025-10-20 12:01:53 +07:00
if ! command -v dotnet >/dev/null 2>&1; then
echo "Installing .NET SDK locally for this build..."
curl -fsSL https://dot.net/v1/dotnet-install.sh -o dotnet-install.sh
bash dotnet-install.sh --channel 9.0 --install-dir "$HOME/.dotnet"
fi
export PATH="$HOME/.dotnet:$PATH"
2025-10-20 12:21:18 +07:00
# If ICU still missing (e.g., no libicu*.so found), enable invariant mode as a fallback
if ! ldconfig -p 2>/dev/null | grep -qi 'libicu'; then
export DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
echo "NOTE: Running .NET in globalization invariant mode (ICU not found)"
fi
2025-10-17 17:19:55 +07:00
dotnet --info
2025-10-20 12:01:53 +07:00
'''
}
}
stage('SCA (NuGet vulnerabilities + OWASP)') {
agent any
steps {
sh '''
2025-10-20 12:32:04 +07:00
set -euo pipefail
2025-10-20 12:01:53 +07:00
export PATH="$HOME/.dotnet:$PATH"
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-20 12:01:53 +07:00
echo "=== OWASP Dependency-Check (no Docker) ==="
2025-10-20 12:32:04 +07:00
rm -f depcheck.zip || true
rm -rf dependency-check || true
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-20 12:38:29 +07:00
# Resolve the correct asset URL (ends with -release.zip)
2025-10-20 12:32:04 +07:00
echo "Resolving Dependency-Check latest asset URL from GitHub API..."
ASSET_URL="$(curl -fsSL "$API" \
2025-10-20 12:38:29 +07:00
| jq -r '.assets[]?.browser_download_url | select(test("release\\\\.zip$"))' \
2025-10-20 12:32:04 +07:00
| head -n1 || true)"
2025-10-20 12:38:29 +07:00
# Fallback from tag_name if assets listing is throttled
2025-10-20 12:32:04 +07:00
if [ -z "${ASSET_URL:-}" ]; then
TAG="$(curl -fsSL "$API" | jq -r '.tag_name' || true)"
if [ -n "${TAG:-}" ]; then
VER="${TAG#v}"
ASSET_URL="https://github.com/jeremylong/DependencyCheck/releases/download/${TAG}/dependency-check-${VER}-release.zip"
fi
fi
if [ -z "${ASSET_URL:-}" ]; then
echo "ERROR: Could not resolve Dependency-Check release asset URL."
exit 9
fi
echo "Downloading: $ASSET_URL"
curl -fL --retry 3 --retry-all-errors -o depcheck.zip "$ASSET_URL"
2025-10-20 12:38:29 +07:00
# Validate and extract
2025-10-20 12:32:04 +07:00
unzip -tq depcheck.zip || { echo "Downloaded file is not a valid ZIP"; exit 9; }
mkdir -p dependency-check
2025-10-20 12:01:53 +07:00
unzip -q depcheck.zip -d dependency-check
2025-10-20 12:32:04 +07:00
DC_BIN="$(echo dependency-check/dependency-check*/bin/dependency-check.sh)"
if [ ! -x "$DC_BIN" ]; then
echo "ERROR: dependency-check.sh not found under extracted folder"
ls -la dependency-check || true
exit 9
fi
2025-10-20 12:38:29 +07:00
# Generate HTML and XML reports (note: use multiple -f flags)
2025-10-20 12:01:53 +07:00
bash "$DC_BIN" \
2025-10-20 12:38:29 +07:00
-f HTML -f XML \
2025-10-20 12:01:53 +07:00
--project "AS400_API_DOTNET" \
--scan "." \
--out "depcheck" \
--noupdate || true
2025-10-17 17:52:49 +07:00
echo "SCA reports generated in depcheck/"
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-17 17:19:55 +07:00
}
}
stage('SAST + Coverage (SonarQube + Tests)') {
2025-10-20 12:01:53 +07:00
agent any
2025-10-17 17:19:55 +07:00
steps {
2025-10-20 12:38:29 +07:00
sh '''
set -e
export PATH="$HOME/.dotnet:$PATH"
2025-10-17 17:19:55 +07:00
2025-10-20 12:38:29 +07:00
# run tests with coverage (cobertura) + produce TRX results for JUnit
dotnet test \
--logger "trx;LogFileName=test_results.trx" \
/p:CollectCoverage=true \
/p:CoverletOutput=coverage/ \
/p:CoverletOutputFormat=cobertura
mkdir -p coverage-report
# copy the cobertura file (adjust path if your solution layout differs)
COBERTURA_FILE=$(find . -type f -name "coverage.cobertura.xml" | head -n1 || true)
[ -n "$COBERTURA_FILE" ] && cp "$COBERTURA_FILE" coverage-report/Cobertura.xml || true
# If SonarQube is configured, run scanner; otherwise skip gracefully.
if [ -n "${SONARQUBE_ENV_NAME:-}" ]; then
echo "SonarQube env variable detected: $SONARQUBE_ENV_NAME"
else
echo "SonarQube not configured; skipping Sonar scan."
exit 0
fi
'''
2025-10-17 17:19:55 +07:00
}
post {
always {
2025-10-20 12:38:29 +07:00
// Publish TRX results (built-in)
2025-10-17 17:19:55 +07:00
junit '**/TestResults/**/*.trx'
2025-10-20 12:38:29 +07:00
// Archive coverage XML so you can inspect it
2025-10-17 17:19:55 +07:00
archiveArtifacts artifacts: 'coverage-report/**', allowEmptyArchive: true
}
}
}
stage('Build Artifact') {
2025-10-20 12:01:53 +07:00
agent any
2025-10-17 17:19:55 +07:00
steps {
sh '''
2025-10-17 17:52:49 +07:00
set -e
2025-10-20 12:01:53 +07:00
export PATH="$HOME/.dotnet:$PATH"
2025-10-17 17:19:55 +07:00
dotnet publish -c Release -o out
ls -la out
'''
archiveArtifacts artifacts: 'out/**', fingerprint: true
}
}
}
}