pipeline { agent none options { ansiColor('xterm'); timestamps() } environment { DOTNET_CLI_TELEMETRY_OPTOUT = '1' DOTNET_SKIP_FIRST_TIME_EXPERIENCE = '1' SONAR_PROJECT_KEY = 'as400api-dotnet' SONAR_PROJECT_NAME = 'AS400_API_DOTNET (.NET 9)' } stages { stage('Checkout') { agent any steps { checkout scm } } stage('Install prerequisites') { agent any steps { sh ''' set -e install_deps() { if command -v apt-get >/dev/null 2>&1; then export DEBIAN_FRONTEND=noninteractive apt-get update -y # ICU + common .NET native deps + helpers apt-get install -y --no-install-recommends \ libicu libkrb5-3 zlib1g libstdc++6 ca-certificates curl unzip # Fallback in case the meta "libicu" name doesn't exist on this distro apt-get install -y --no-install-recommends libicu-dev || true elif command -v dnf >/dev/null 2>&1; then dnf install -y libicu krb5-libs zlib libstdc++ ca-certificates curl unzip elif command -v yum >/dev/null 2>&1; then yum install -y libicu krb5-libs zlib libstdc++ ca-certificates curl unzip elif command -v apk >/dev/null 2>&1; then apk add --no-cache icu-libs krb5-libs zlib libstdc++ ca-certificates curl unzip else echo "Unsupported package manager. Please install ICU manually." exit 1 fi } install_deps ''' } } // Bootstrap .NET SDK if dotnet is missing stage('Bootstrap .NET SDK') { agent any steps { sh ''' set -e 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" dotnet --info ''' } } stage('SCA (NuGet vulnerabilities + OWASP)') { agent any steps { sh ''' set -e export PATH="$HOME/.dotnet:$PATH" echo "=== NuGet vulnerability audit ===" dotnet restore dotnet list package --vulnerable || true echo "=== OWASP Dependency-Check (no Docker) ===" mkdir -p depcheck DC_VER=latest # Grab the release (platform-independent zip) curl -Ls -o depcheck.zip \ https://github.com/jeremylong/DependencyCheck/releases/${DC_VER}/download/dependency-check-${DC_VER}-release.zip || \ curl -Ls -o depcheck.zip \ https://github.com/jeremylong/DependencyCheck/releases/latest/download/dependency-check-release.zip rm -rf dependency-check && mkdir dependency-check unzip -q depcheck.zip -d dependency-check DC_BIN=$(echo dependency-check/dependency-check*/bin/dependency-check.sh) bash "$DC_BIN" \ --format "HTML,XML" \ --project "AS400_API_DOTNET" \ --scan "." \ --out "depcheck" \ --noupdate || true echo "SCA reports generated in depcheck/" ''' } post { always { archiveArtifacts artifacts: 'depcheck/**', allowEmptyArchive: true } } } stage('SAST + Coverage (SonarQube + Tests)') { agent any steps { withSonarQubeEnv('SonarQube') { sh ''' set -e export PATH="$HOME/.dotnet:$PATH" # run tests with coverage (coverlet integrated) dotnet test /p:CollectCoverage=true /p:CoverletOutput=coverage/ \ /p:CoverletOutputFormat=cobertura # prepare coverage report location mkdir -p coverage-report # many test templates already emit Cobertura; adjust path if needed cp **/coverage.cobertura.xml coverage-report/Cobertura.xml || true # Sonar scan (assuming global dotnet-sonarscanner or use local tool) if ! command -v dotnet-sonarscanner >/dev/null 2>&1; then dotnet tool install --global dotnet-sonarscanner export PATH="$PATH:$HOME/.dotnet/tools" fi dotnet-sonarscanner begin \ /k:"${SONAR_PROJECT_KEY}" \ /n:"${SONAR_PROJECT_NAME}" \ /d:sonar.cs.opencover.reportsPaths="coverage-report/Cobertura.xml" dotnet build -c Release dotnet-sonarscanner end ''' } } post { always { publishCoverage adapters: [coberturaAdapter('coverage-report/Cobertura.xml')], sourceFileResolver: sourceFiles('STORE_LAST_BUILD') junit '**/TestResults/**/*.trx' archiveArtifacts artifacts: 'coverage-report/**', allowEmptyArchive: true } } } stage('Build Artifact') { agent any steps { sh ''' set -e export PATH="$HOME/.dotnet:$PATH" dotnet publish -c Release -o out ls -la out ''' archiveArtifacts artifacts: 'out/**', fingerprint: true } } } }