diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..6664312 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,130 @@ +pipeline { + agent none + + environment { + // Optional: tweak for speed + DOTNET_CLI_TELEMETRY_OPTOUT = '1' + DOTNET_SKIP_FIRST_TIME_EXPERIENCE = '1' + + // Sonar: set host via withSonarQubeEnv; token via credentials below + SONAR_PROJECT_KEY = 'AS400-API' + SONAR_PROJECT_NAME = 'AS/400 API (.NET9)' + } + + stages { + stage('Checkout') { + agent any + steps { checkout scm } + } + + stage('SCA (Dependency Audit)') { + // Run inside official .NET 9 SDK image + agent { + docker { image 'mcr.microsoft.com/dotnet/sdk:9.0'; reuseNode true } + } + steps { + sh ''' + dotnet --info + # 1) Built-in vulnerability audit (NuGet advisory DB) + dotnet restore + # dotnet list package --vulnerable exits 0 even when vulnerabilities found; use Jenkins Warnings NG to surface, or grep to fail on high + dotnet list package --vulnerable || true + + # 2) (Optional) OWASP Dependency-Check via Docker + # Scans csproj/packages.lock.json for known CVEs + mkdir -p depcheck + docker run --rm \ + -v "$PWD":/src \ + -v "$PWD/depcheck":/report \ + owasp/dependency-check:latest \ + --scan /src \ + --format "HTML" \ + --out /report || true + echo "Dependency-Check report at depcheck/dependency-check-report.html" + ''' + // You can archive the HTML report for viewing in Jenkins + archiveArtifacts artifacts: 'depcheck/**', allowEmptyArchive: true + } + } + + stage('SAST + Coverage (SonarQube + Tests)') { + agent { + docker { + // Use SDK image; we’ll install scanner + reportgenerator inside + image 'mcr.microsoft.com/dotnet/sdk:9.0' + reuseNode true + } + } + environment { + SONAR_TOKEN = credentials('sonar-token') + } + steps { + withSonarQubeEnv('SonarQubeServer') { + sh ''' + # Install dotnet tools we need + dotnet tool install --global dotnet-sonarscanner --version 7.* + dotnet tool install --global dotnet-reportgenerator-globaltool + export PATH="$PATH:/root/.dotnet/tools" + + # Begin Sonar analysis (SAST + quality gates) + dotnet sonarscanner begin \ + /k:"$SONAR_PROJECT_KEY" \ + /n:"$SONAR_PROJECT_NAME" \ + /d:sonar.host.url="$SONAR_HOST_URL" \ + /d:sonar.login="$SONAR_TOKEN" \ + /d:sonar.cs.opencover.reportsPaths="**/TestResults/**/coverage.opencover.xml" \ + /d:sonar.coverage.exclusions="**/*.cshtml,**/Migrations/**" + + # Build (needed for Sonar to analyze) + dotnet restore + dotnet build -c Release --no-restore + + # Test + Coverage (OpenCover format for Sonar) + # This uses coverlet.msbuild (works without editing csproj) + # Generates: .//TestResults//coverage.opencover.xml + dotnet test -c Release --no-build \ + /p:CollectCoverage=true \ + /p:CoverletOutputFormat=opencover \ + /p:CoverletOutput=./TestResults/coverage + + # End Sonar (uploads results to server) + dotnet sonarscanner end /d:sonar.login="$SONAR_TOKEN" + + # Optional: create a single Cobertura report for Jenkins UI + reportgenerator \ + -reports:**/TestResults/**/coverage.opencover.xml \ + -targetdir:coverage-report \ + -reporttypes:Cobertura + ''' + } + } + post { + always { + // Publish coverage into Jenkins (Cobertura) + 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 { + docker { image 'mcr.microsoft.com/dotnet/sdk:9.0'; reuseNode true } + } + steps { + sh ''' + dotnet publish -c Release -o out + ls -la out + ''' + archiveArtifacts artifacts: 'out/**', fingerprint: true + } + } + } + + options { + ansiColor('xterm') + timestamps() + } +} \ No newline at end of file