From 6ef1543dcd9c8de9b314b956aab57c9bafc1a906 Mon Sep 17 00:00:00 2001 From: Enno Gelhaus Date: Fri, 3 Oct 2025 15:56:16 +0200 Subject: [PATCH] Refactor Jenkins pipeline to remove global environment variable for Sonar Scanner and streamline installation process within the analysis stage --- Jenkins/Build.Jenkinsfile | 71 +++++++++++++---------------- Jenkins/BuildPR.Jenkinsfile | 89 ++++++++++++++++++------------------- 2 files changed, 76 insertions(+), 84 deletions(-) diff --git a/Jenkins/Build.Jenkinsfile b/Jenkins/Build.Jenkinsfile index ebc3c0cf..886ade0a 100644 --- a/Jenkins/Build.Jenkinsfile +++ b/Jenkins/Build.Jenkinsfile @@ -3,10 +3,7 @@ pipeline { // Defines the execution environment. Using 'agent any' to ensure an agent is available. agent any - // Global environment variable to store the path to the manually installed scanner. - environment { - SONAR_SCANNER_PATH = '' - } + // Global environment block removed to prevent Groovy scoping issues with manual path calculation. stages { // Stage 1: Checkout the code (Relies on the initial SCM checkout done by Jenkins) @@ -16,7 +13,7 @@ pipeline { } } - // Stage 2: Setup Node.js v20, install pnpm, and MANUALLY install Sonar Scanner + // Stage 2: Setup Node.js v20 and install pnpm stage('Setup Environment and Tools') { steps { sh ''' @@ -33,34 +30,6 @@ pipeline { npm install -g pnpm@8 echo "pnpm version: \$(pnpm -v)" ''' - - // --- MANUALLY INSTALL THE SONAR SCANNER CLI (FIXED GROOVY SCOPE) --- - script { - sh """ - echo "Manually downloading and installing Sonar Scanner CLI..." - - # Download the stable scanner CLI package - curl -sS -o sonar-scanner.zip \ - "https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.7.0.2747.zip" - - unzip -q sonar-scanner.zip -d . - - # Find the extracted directory name - def scannerDir = \$(find . -maxdepth 1 -type d -name "sonar-scanner*" | head -n 1) - - echo "Scanner extracted to: \${scannerDir}" - - # Set the global environment variable (SONAR_SCANNER_PATH) - // This allows us to run the scanner by full path in the next stage - echo "SONAR_SCANNER_PATH=\${scannerDir}/bin" > .scanner_path.env - """ - // Load the environment variable set by the shell script - // This is the correct way to pass shell variables back to Groovy/Jenkins environment - def scannerPath = readProperties file: '.scanner_path.env' - env.SONAR_SCANNER_PATH = "${env.WORKSPACE}/${scannerPath.SONAR_SCANNER_PATH}" - - echo "Global Sonar Path set to: ${env.SONAR_SCANNER_PATH}" - } } } @@ -72,7 +41,7 @@ pipeline { } } - // Stage 4: Run SonarQube analysis using the plugin's variables and the manual path. + // Stage 4: Run SonarQube analysis: Install scanner, get version, and execute. stage('SonarQube Analysis') { steps { script { @@ -80,15 +49,39 @@ pipeline { def commitShaShort = sh(returnStdout: true, script: 'git rev-parse --short=8 HEAD').trim() echo "Commit SHA (short) is: ${commitShaShort}" - // 2. Use withSonarQubeEnv to set up the secure variables (HOST and TOKEN) - // The 'SonarQube-Server' name is used as per your previous log. + // --- 2. MANUALLY INSTALL THE SONAR SCANNER CLI LOCALLY IN THIS STAGE --- + sh """ + echo "Manually downloading and installing Sonar Scanner CLI..." + + # Download the stable scanner CLI package + curl -sS -o sonar-scanner.zip \ + "https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.7.0.2747.zip" + + # Added -o flag to force overwrite and prevent interactive prompt failure + unzip -o -q sonar-scanner.zip -d . + """ + + // 3. Find the extracted directory name and capture the full absolute bin path in Groovy + // This is defined locally and used directly, avoiding environment variable issues. + def scannerBinPath = sh( + returnStdout: true, + script: ''' + SCANNER_DIR=$(find . -maxdepth 1 -type d -name "sonar-scanner*" | head -n 1) + # Get the full absolute path to the executable file + echo \$(pwd)/\${SCANNER_DIR}/bin/sonar-scanner + ''' + ).trim() + + echo "Scanner executable path captured: ${scannerBinPath}" + + // 4. Use withSonarQubeEnv to set up the secure variables (HOST and TOKEN) withSonarQubeEnv(installationName: 'SonarQube-Server') { - // 3. Execute the scanner using the manually determined full path. - // We rely on the sonar-project.properties file for the project key. + // 5. Execute the scanner using the Groovy variable directly. sh """ echo "Starting SonarQube Analysis for project version: ${commitShaShort}" - \${SONAR_SCANNER_PATH}/sonar-scanner \\ + # Execute the full, absolute path captured in the Groovy variable. + '${scannerBinPath}' \\ -Dsonar.projectVersion=${commitShaShort} \\ -Dsonar.sources=. diff --git a/Jenkins/BuildPR.Jenkinsfile b/Jenkins/BuildPR.Jenkinsfile index 3840b726..49237fc8 100644 --- a/Jenkins/BuildPR.Jenkinsfile +++ b/Jenkins/BuildPR.Jenkinsfile @@ -1,54 +1,38 @@ // Declarative Pipeline for building Node.js application and running SonarQube analysis for a Pull Request. pipeline { - // Defines the execution environment. Replace 'linux-agent' with your specific agent label. + // Defines the execution environment. Using 'agent any' to ensure an agent is available. agent any - - // Environment variables that hold PR details (set automatically by Jenkins SCM plugins like Git/GitHub Branch Source) + + // Environment variables that hold PR details, provided by Jenkins Multibranch setup. environment { - // These variables are provided by Jenkins (e.g., in a Multibranch Pipeline setup) - // CHANGE_ID corresponds to ${{ github.event.pull_request.number }} - // CHANGE_BRANCH corresponds to ${{ github.event.pull_request.head.ref }} - // CHANGE_TARGET corresponds to ${{ github.event.pull_request.base.ref }} + // These variables are typically provided by Jenkins (e.g., in a Multibranch Pipeline setup) PR_KEY = env.CHANGE_ID PR_BRANCH = env.CHANGE_BRANCH PR_BASE = env.CHANGE_TARGET } stages { - // Stage 1: Checkout the code with full history (fetch-depth: 0) + // Stage 1: Checkout the code (Relies on the initial SCM checkout done by Jenkins) stage('Source Checkout') { steps { - script { - // This performs a deep clone (fetch-depth: 0) - // NOTE: You must replace 'YOUR_GIT_CREDENTIALS_ID' with the actual Jenkins credential ID - // that has access to your repository. - checkout([ - $class: 'GitSCM', - branches: [[name: 'HEAD']], - extensions: [ - [$class: 'WipeWorkspace'], - [$class: 'CleanBeforeCheckout'], - [$class: 'CloneOption', depth: 0, noTags: false, reference: '', shallow: false] - ], - userRemoteConfigs: [ - [url: env.GIT_URL ?: ''] - ] - ]) - } + echo "Workspace already populated by the initial SCM checkout. Proceeding." } } - // Stage 2: Setup Node.js v20 and install pnpm - stage('Setup Environment') { + // Stage 2: Setup Node.js v20, install pnpm, and install required tools (curl, unzip) + stage('Setup Environment and Tools') { steps { sh ''' - # Install Node.js v20 (closest matching the specified version '20.17.0') + echo "Ensuring required utilities and Node.js are installed..." + sudo apt-get update + sudo apt-get install -y curl unzip nodejs + + # 1. Install Node.js v20 (closest matching the specified version '20.17.0') curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - sudo apt-get install -y nodejs - echo "Node.js version: \$(node -v)" - # Install pnpm globally (version 8) + # 2. Install pnpm globally (version 8) npm install -g pnpm@8 echo "pnpm version: \$(pnpm -v)" ''' @@ -63,7 +47,7 @@ pipeline { } } - // Stage 4: Retrieve secrets from Vault and run SonarQube PR analysis + // Stage 4: Run SonarQube PR analysis: Install scanner locally, get version, and execute. stage('SonarQube Pull Request Analysis') { steps { script { @@ -71,26 +55,41 @@ pipeline { def commitShaShort = sh(returnStdout: true, script: 'git rev-parse --short=8 HEAD').trim() echo "Commit SHA (short) is: ${commitShaShort}" - // 2. Retrieve secrets from HashiCorp Vault using the dedicated plugin binding. - withCredentials([ - // Requires the Jenkins HashiCorp Vault Plugin - [$class: 'VaultSecretCredentialsBinding', - vaultSecrets: [ - [$class: 'VaultSecret', secretPath: 'postiz/data/ci/sonar', secretKey: 'SONAR_TOKEN', envVar: 'SONAR_TOKEN'], - [$class: 'VaultSecret', secretPath: 'postiz/data/ci/sonar', secretKey: 'SONAR_HOST_URL', envVar: 'SONAR_HOST_URL'] - ]] - ]) { - // 3. Execute sonar-scanner CLI with Pull Request parameters + // --- 2. MANUALLY INSTALL THE SONAR SCANNER CLI LOCALLY --- + sh """ + echo "Manually downloading and installing Sonar Scanner CLI..." + curl -sS -o sonar-scanner.zip \ + "https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.7.0.2747.zip" + unzip -o -q sonar-scanner.zip -d . + """ + + // 3. Find the extracted directory name and capture the full absolute executable path. + def scannerBinPath = sh( + returnStdout: true, + script: ''' + SCANNER_DIR=$(find . -maxdepth 1 -type d -name "sonar-scanner*" | head -n 1) + # Get the full absolute path to the executable file + echo \$(pwd)/\${SCANNER_DIR}/bin/sonar-scanner + ''' + ).trim() + + echo "Scanner executable path captured: ${scannerBinPath}" + + // 4. Use withSonarQubeEnv to set up the secure variables (HOST and TOKEN) + withSonarQubeEnv(installationName: 'SonarQube-Server') { + // 5. Execute the scanner using the Groovy variable directly with PR parameters. sh """ echo "Starting SonarQube Pull Request Analysis for PR #${PR_KEY}" - sonar-scanner \\ + + '${scannerBinPath}' \\ -Dsonar.projectVersion=${commitShaShort} \\ - -Dsonar.token=\${SONAR_TOKEN} \\ - -Dsonar.host.url=\${SONAR_HOST_URL} \\ + -Dsonar.sources=. \\ -Dsonar.pullrequest.key=${PR_KEY} \\ -Dsonar.pullrequest.branch=${PR_BRANCH} \\ -Dsonar.pullrequest.base=${PR_BASE} - # Add other analysis properties here if needed (e.g., -Dsonar.projectKey=...) + + # SONAR_HOST_URL and SONAR_TOKEN are automatically passed as environment variables + # by the withSonarQubeEnv block. """ } }