diff --git a/Templates/JenkinsPipeline/JenkinsfileWithWaziDeploy b/Templates/JenkinsPipeline/JenkinsfileWithWaziDeploy index 7f77ad07..1279aa47 100644 --- a/Templates/JenkinsPipeline/JenkinsfileWithWaziDeploy +++ b/Templates/JenkinsPipeline/JenkinsfileWithWaziDeploy @@ -2,789 +2,868 @@ // // Description: A sample Jenkins Declarative Multibranch pipeline job that demonstrates how to // implement the IBM recommended Git flow branching model. -// https://ibm.github.io/z-devops-acceleration-program/docs/git-branching-model-for-mainframe-dev/ -// The pipeline uses Jenkins, IBM Dependency Based Build, SonarQube Scan and IBM Wazi Deploy -// and the Common Backend scripts +// https://ibm.github.io/z-devops-acceleration-program/docs/branching/git-branching-model-for-mainframe-dev +// The pipeline uses Jenkins, IBM Dependency Based Build, SonarQube Scan, IBM Wazi Deploy, +// and the Common Backend scripts. // // Refer to README.md for more information. -// -//import java.nio.file.Files; -//import java.nio.file.Paths; -// Jenkins -// +// ============================================================================ +// Jenkins Configuration +// ============================================================================ // Information required to identify Jenkins specific configuration values. // -// JenkinsAgent - Label of the Jenkins Agent where the pipeline -// will execute in the Jenkins environment. -def JenkinsAgent = "ztec-201-STC" // Jenkins Node/Label under z/OS UNIX System Services -def JenkinsX86Agent = "x86-builder" // Jenkins Node/Label to drive distributed actions, such as requisting SonarQube scan +// JenkinsAgent - Label of the Jenkins Agent where the pipeline will execute. +// JenkinsX86Agent - Label for distributed actions, such as requesting SonarQube scan. +def JenkinsAgent = "ZosAgent" // Jenkins Node/Label under z/OS UNIX System Services. +def JenkinsX86Agent = "x86-builder" // Jenkins Node/Label to drive distributed actions. // Debug Variables -def pipeverbose = true // Enabled Pipeline Debug Messages (true/false). +def verbose = false // Enable Pipeline Debug Messages (true/false) - Default to false for production. -// Lifecycle -// +// ============================================================================ +// Lifecycle Management +// ============================================================================ // Variables for lifecycle management // -// PipelineType - The type of pipeline relating to the development lifecycle. Set by the user via -// pipeline parameter. Potential values: -// preview -// build (default) -// release -// ReleaseType - Defining the information for the semantic versioning to compute release candidate tags and release names. -// TODO - Further enhancement to automatically compute release candidate tag -def PipelineType = "build" -def ReleaseType = "" - -// Application -// -// AppName - Name of the Application folder within the application repository. -// AppHLQ - Build destination High-Level Qualifier for any pipeline -// -def AppName = "MortgageApplication" -def AppHLQ = "JENKINS.PIPELINE" - -// Generic Build Solution (zAppBuild) -// -// Variables that pertain to the generic build solution zAppBuild and Common Backend Script dbbBuild.sh +// PipelineType - The type of pipeline relating to the development lifecycle. +// Set by the user via pipeline parameter. Potential values: +// - preview +// - build (default) +// - release +// ReleaseType - Defining the information for the semantic versioning to compute +// release candidate tags and release names. +def PipelineType = "build" +def ReleaseType = "" + +// ============================================================================ +// Application Configuration +// ============================================================================ +// AppName - Name of the Application folder within the application repository. +// AppHLQ - Build destination High-Level Qualifier for any pipeline. +def AppBranch = "" +def BuildDir = "" +def Branch = "" +def AppName = "MortgageApplication" +def AppHLQ = "JENKINS.PIPELINE" + +// ============================================================================ +// Generic Build Solution (zBuilder) +// ============================================================================ +// Variables that pertain to zBuilder and Common Backend Scripts. // -// zAppBuildVerbose - Verbose string passed to the DBB Build script. Set later on based on the -// value of pipeline verbose variable. -// DbbDoBuild - If true, perform the DBB Build. Computed in Branch Contol stage -def zAppBuildVerbose = "" -def DbbDoBuild = true - +// zBuilderVerbose - Verbose string passed to the DBB Build script. Set later on based on the +// value of pipeline verbose variable. +// DbbDoBuild - If true, perform the DBB Build. Computed in Branch Control stage. +// DbbCommunityRepo - Install location of the DBB community git repository. +// DbbPipelineScripts - Path to the DBB Common-Backend-Scripts directory. +def zBuilderVerbose = "" +def DbbDoBuild = true +def DbbCommunityRepo = "/u/user/dbb" +def DbbPipelineScripts = "${DbbCommunityRepo}/Templates/Common-Backend-Scripts" + +// ============================================================================ // Software Analysis with SonarQube +// ============================================================================ +// Variables that pertain to the software analysis stage. +// SonarQubeConnection - Name of the configured SonarQube Connection in Jenkins. +// SonarQubeDoScan - If true, perform the Sonar analysis. Computed in Branch Control stage. +def SonarQubeConnection = "zsonar1" // Configured SonarQube connection +def SonarQubeDoScan = true + +// ============================================================================ +// Jenkins Runtime Variables +// ============================================================================ +// Variables that pertain to Jenkins runtime information // -// Variables that pertain to the software analysis stage -// -// SonarQubeConnection - Name of the configured sonarQube Connection in Jenkins -// SonarQubeDoScan - If true, perform the Sonar analysis. Computed in Branch Contol stage -def SonarQubeConnection = "zsonar1" // Configured SonarQube connection -def SonarQubeDoScan = true - -// Jenkins -// -// Variables that pertain to Jenkins. -// -// PipelineName - Supplied by Jenkins JOB_NAME Environmental Variable. -// BuildNumber - Supplied by Jenkins BUILD_NUMBER Environemntal Variable. Value will be -// eight (8) characters in length, padded to the left with zeros (0). -def PipelineName = "" -def BuildNumber = "" +// PipelineName - Supplied by Jenkins JOB_NAME Environmental Variable. +// BuildNumber - Supplied by Jenkins BUILD_NUMBER Environmental Variable. +// Value will be eight (8) characters in length, padded to the left with zeros (0). +def PipelineName = "" +def BuildNumber = "" +// ============================================================================ // Branch Controls +// ============================================================================ +// Variables that pertain to Repository Branches // -// Variables that pertain to Repository Branches. -// -// BranchOkay - Process controls for Branches that have been defined. -def BranchOkay = false +// BranchOkay - Process controls for Branches that have been defined +def BranchOkay = false -// Wazi Deploy -// -// Vairables that pertain to the deployment stage -// -// WdDoPackaging - If true, perform the Packaging. -// WdDoDeployment - If true, perform the Deployment through Wazi Deploy. +// ============================================================================ +// Wazi Deploy Configuration +// ============================================================================ +// Variables that pertain to the deployment stage // -def WdDoPackaging = true -def Artifactory = "" - -def WdDoDeployment = false -def WdEnvFileIntegration = "EOLEB7-MortgageApplication-Integration.yaml" - -// Directories on the GitLab runner environment to persist Wazi Deploy Evidence files and to create an Wazi Deploy Index -def WdEvidencesRoot = "/var/work/wazi_deploy_evidences_jenkins/" -def WdEvidencesIndex = "/var/work/wazi_deploy_evidences_jenkins_index/" -def WdEvidencesDir = "" - -// Internal Constants and Variables -// Build stage -def BuildCmd = "" // DBB Build Command. -def Buildrc = 0 // DBB Build Command Return Code. -def BuildOutputList = "" // List of timestamped directories under Jenkins Build Directory. -def BuildOutputDir = "" // DBB Build Output Directory (Discovered). -def BuildFile = "" // Will contain the contents of the DBB buildFile.txt. -def BuildHasFiles = true // DBB buildFile.txt indicates changed files. - -// Packaging stage -def ComputeReleaseVersionCmd = "" // Compute Release Version Command. -def ComputeReleaseVersionrc = 0 // Compute Release Version Return Code. -def ReleaseVersion = "" // Next release version. -def PackageCmd = "" // Packaging Command. -def Packagerc = 0 // Packaging Command Return Code. -def PackageOutputDir = "" // Temporary Directory for Packaging -def PackageVersion = "" // Package Version when deploy through Wazi Deploy. -def WdManifest = "" // Wazi Deploy Manifest File - -// Deployment plan generation stage -def WdGenDeployPlanCmd = "" // Generate Deployment Plan Command. -def WdGenDeployPlanrc = 0 // Generate Deployment Plan Return Code. -def ArtifactoryURL = "" - -// Deployment stage -def WdDeployCmd = "" // Wazi Deploy Deploy Command. -def WDDeployrc = 0 // Wazi Deploy Return Code. -def DeployOutputDir = "" // Wazi Deploy Output Directory. -def DeployEvidencesDir = "" // Wazi Deploy Evidences Directory. -def WdEvidenceCmd = "" // Wazi Deploy Evidences Command. -def WdEvidenceRc = 0 // Wazi Deploy Evidences Return Code. - -def autoCancelled = false // Cancellation Flag. -def DefaultEncoding = "IBM-1047" // Default Encoding. -def Msg = "" // Message String. -def MsgHdr = "" // HTML Header String ID for Jenkins Build Log. -def ShCmd = "" // Shell command. -def ShCmdRc = 0 // Retrun code from shell command. -def StartDateTime = "" // Pipeline Start Date/Time. - -// Okay, Let's Begin +// WdDoPackaging - If true, perform the Packaging. +// WdDoDeployment - If true, perform the Deployment through Wazi Deploy. +// WdEnvFileIntegration - Environment file for integration deployment. +// WdEvidencesRoot - Root directory to persist Wazi Deploy Evidence files. +// WdEvidencesIndex - Directory to create Wazi Deploy Index. +// WdEvidencesDir - Working directory for evidences. +def WdDoPackaging = true +def WdDoDeployment = false +def WdEnvFileIntegration = "${DbbCommunityRepo}/WaziDeploy/zDeploy/environment-configuration/python/EOLEB7-Integration.yml" +def WdEvidencesRoot = "/var/work/wazi_deploy_evidences_jenkins/" +def WdEvidencesIndex = "/var/work/wazi_deploy_evidences_jenkins_index/" +def WdEvidencesDir = "" + +// ============================================================================ +// Internal Constants and Variables - Build Stage +// ============================================================================ +def BuildCmd = "" // DBB Build Command +def Buildrc = 0 // DBB Build Command Return Code +def BuildOutputList = "" // List of timestamped directories under Jenkins Build Directory. +def BuildOutputDir = "" // DBB Build Output Directory (Discovered) +def BuildFile = "" // Will contain the contents of the DBB buildFile.txt. +def BuildHasFiles = true // DBB buildFile.txt indicates changed files. + +// ============================================================================ +// Internal Constants and Variables - Packaging Stage +// ============================================================================ +def ComputeReleaseVersionCmd = "" // Compute Release Version Command +def ComputeReleaseVersionrc = 0 // Compute Release Version Return Code +def ReleaseVersion = "" // Next release version +def PackageCmd = "" // Packaging Command +def Packagerc = 0 // Packaging Command Return Code +def PackageOutputDir = "" // Temporary Directory for Packaging +def PackageTarPath = "" // Temporary tar created by Packaging +def PackageVersion = "" // Package Version when deploy through Wazi Deploy +def WdManifest = "" // Wazi Deploy Manifest File + +// ============================================================================ +// Internal Constants and Variables - Deployment Plan Generation Stage +// ============================================================================ +def WdGenDeployPlanCmd = "" // Generate Deployment Plan Command +def WdGenDeployPlanrc = 0 // Generate Deployment Plan Return Code +def ArtifactoryURL = "" + +// ============================================================================ +// Internal Constants and Variables - Deployment Stage +// ============================================================================ +def WdDeployCmd = "" // Wazi Deploy Deploy Command +def WdDeployrc = 0 // Wazi Deploy Return Code (Fixed typo from WDDeployrc) +def DeployOutputDir = "" // Wazi Deploy Output Directory +def DeployEvidencesDir = "" // Wazi Deploy Evidences Directory +def WdEvidenceCmd = "" // Wazi Deploy Evidences Command +def WdEvidenceRc = 0 // Wazi Deploy Evidences Return Code + +// ============================================================================ +// General Variables +// ============================================================================ +def autoCancelled = false // Cancellation Flag +def DefaultEncoding = "IBM-1047" // Default Encoding +def Msg = "" // Message String +def MsgHdr = "" // HTML Header String ID for Jenkins Build Log +def ShCmd = "" // Shell command +def ShCmdRc = 0 // Return code from shell command +def StartDateTime = "" // Pipeline Start Date/Time StartDateTime = new Date() -// -// Pipeline - Begin the Pipeline. -// +// ============================================================================ +// Branch Configuration Map +// ============================================================================ +def branchConfigs = [ + 'MAIN': [build: true, scan: false, package: true, deploy: false], + 'FEATURE': [build: true, scan: false, package: true, deploy: false], + 'HOTFIX': [build: true, scan: false, package: true, deploy: false], + 'RELEASE': [build: true, scan: false, package: true, deploy: false], + 'EPIC': [build: true, scan: false, package: true, deploy: false] +] + +// ============================================================================ +// Pipeline Definition +// ============================================================================ pipeline { - - agent { label JenkinsAgent } - - options { - checkoutToSubdirectory("${AppName}") // Added AppName for checkout dir of implizit checkout - disableConcurrentBuilds() // throttle builds - } - environment { - GIT_TRACE = 'false' // Trace git for testing true/false,1,2. - GIT_TRACE_SETUP = 'false' // really cool trace tools. - } - - stages { - - stage ('Pipeline Setup') { - steps { - script { - - // Required Input Parameters - properties([ - parameters([ - choice( - choices : ["build", "release", "preview"], - description: 'Please select the pipeline type', - defaultValue: 'build', - name: 'PipelineType' - ), - choice( - choices : ["major", "minor", "patch"], - description: 'Please provide the release type for automated computing of tags', - defaultValue: 'minor', - name: 'ReleaseType' - ), - choice( - choices : ["no", "yes"], - description: 'Enable pipeline logging', - defaultValue: 'no', - name: 'PipelineVerboseLogging' - ) - ]) - ]) - - println("${PipelineName}[INFO]: Start Date/Time = " + StartDateTime) - - // Assess user provided parameters - if (params.PipelineVerboseLogging.equals("yes")){ - zAppBuildVerbose = "-v" - pipeverbose = true - } else { - zAppBuildVerbose = "" - pipeverbose = false - } - - PipelineType = params.PipelineType - ReleaseType = params.ReleaseType - - Buildrc = 0 - - if (pipeverbose) { - println("${PipelineName}[DEBUG]: Format yyyyMMdd.hhmmss.mmm = " + StartDateTime.format("yyyyMMdd.hhmmss.mmm")) - println("${PipelineName}[DEBUG]: Format yyyyMMdd.HHmmss.mmm = " + StartDateTime.format("yyyyMMdd.HHmmss.mmm")) - sh "env" - } - - // Pick up the AppBranch from Jenkins. - AppBranch = "${env.BRANCH_NAME}" - - // Fetch the Pipeline Name and Build Number. Set up and format the DBB Working - // directory for the build results. - PipelineName = env.JOB_NAME.split("/") - PipelineName = PipelineName[0] - - BuildNumber = "${env.BUILD_NUMBER}" - BuildNumber = BuildNumber.padLeft(8,"0") // Make Build Directories easier to sort. - - //Build directory - BuildDir = "${WORKSPACE}" - - // Branch Control Setup - Branch = env.BRANCH_NAME.toUpperCase() - BranchOkay = false - PipelineTypeUpper = PipelineType.toUpperCase() - - // Configure the stages for the main branch - if ( (Branch.startsWith('MAIN') == true) ) { - - DbbDoBuild = true // Perform DBB Build. - SonarQubeDoScan = true // Perform Sonar scan. - - WdDoPackaging = true // Perform the Packaging. - if (PipelineType == "release") { // Perform the Deployment through Wazi Deploy for a release pipeline. - WdDoDeployment = true - DeployOutputDir = "${BuildDir}/deployPkgDir" - DeployEvidencesDir = "${DeployOutputDir}/deploy/evidences" - } - else { //Do not perform the deployment if this is a build pipeline. - WdDoDeployment = false - } - - BranchOkay = true - } - - // Configure the stages for feature and hotfix branches - if (Branch.startsWith('FEATURE') == true || Branch.startsWith('HOTFIX') == true) { - - DbbDoBuild = true // Perform DBB Build. - SonarQubeDoScan = true // Perform Sonar scan. - - WdDoPackaging = true // Perform the Packaging. - WdDoDeployment = false // Do not perform the Deployment through Wazi Deploy. Up to the developer to install it. - - BranchOkay = true - } - - // Configure the stages for RELEASE and EPIC branches - if (Branch.startsWith('RELEASE') == true || Branch.startsWith('EPIC') == true) { - - DbbDoBuild = true // Perform DBB Build. - SonarQubeDoScan = true // Perform Sonar scan. - - WdDoPackaging = true // Perform the Packaging. - WdDoDeployment = false // Do not perform the Deployment through Wazi Deploy. Up to the developer to install it. - - BranchOkay = true - } - - // Manage preview pipelines - if ( (PipelineTypeUpper.startsWith('PREVIEW') == true) ) { - DbbDoBuild = true - // Skip any subsequent steps - SonarQubeDoScan = false - WdDoPackaging = false - WdDoDeployment = false - } - - // If Branch Proceesing has not been defined, gracefully terminate the Pipeline. - if (BranchOkay == false) { - autoCancelled = true - DbbDoBuild = false - SonarQubeDoScan = false - WdDoPackaging = false - WdDoDeployment = false - } - - println("${PipelineName}[INFO]: AppBranch = ${AppBranch} .") - - } - } - } // End: stage ('Pipeline Setup') - - stage ('Parameters and Values') { - steps { - script { - println("**************************************************************") - println("* Parameters and Variable Values") - println("*") - println("* Lifecycle") - println("* PipelineType : ${PipelineType}") - println("* ReleaseType : ${ReleaseType}") - println("*") - println("* Jenkins") - println("* env.JOB_NAME : ${env.JOB_NAME}") - println("* env.GIT_URL : ${env.GIT_URL}") - println("* env.GIT_BRANCH : ${env.GIT_BRANCH}") - println("* env.BRANCH_NAME : ${env.BRANCH_NAME}") - println("* env.BUILD_NUMBER : ${env.BUILD_NUMBER}") - println("* env.CHANGE_URL : ${env.CHANGE_URL}") // If this is a PR build - println("* WORKSPACE : ${WORKSPACE}") - println("* BuildNumber : ${BuildNumber}") - println("* PipelineName : ${PipelineName}") - println("* JenkinsAgent : ${JenkinsAgent}") - println("*") - println("* Application") - println("* AppBranch : ${AppBranch}") - println("* AppName : ${AppName}") - println("* AppHLQ : ${AppHLQ}") - println("* BuildDir : ${BuildDir}") - println("*") - println("* Build/zAppBuild") - println("* zAppBuildVerbose : ${zAppBuildVerbose}") - println("* DbbDoBuild : ${DbbDoBuild}") - println("*") - println("* Anaylsis/SonarQube") - println("* SonarQubeConnection : ${SonarQubeConnection}") - println("* SonarQubeDoScan : ${SonarQubeDoScan}") - println("*") - println("* Packaging") - println("* WdDoPackaging : ${WdDoPackaging}") - println("*") - println("* Deployment/Wazi Deploy") - println("* WdDoDeployment : ${WdDoDeployment}") - println("* WdEnvFileIntegration : ${WdEnvFileIntegration}") - println("* DeployOutputDir : ${DeployOutputDir}") - println("* WdEvidenceRoot : ${WdEvidencesRoot}") - println("*") - println("* General") - println("* DefaultEncoding : ${DefaultEncoding}") - println("* autoCancelled : ${autoCancelled}") - println("*") - println("* Environmental") - println("* HOME : ${HOME}") - println("* CLASSPATH : ${CLASSPATH}") - println("* PATH : ${PATH}") - println("* JAVA_HOME : ${JAVA_HOME}") - println("* JENKINS_HOME : $JENKINS_HOME") - println("*") - println("**************************************************************") - } - } - } // End: stage ('Parameters and Values') - - - stage('Build') { - when { - expression { return ((DbbDoBuild == true) && (autoCancelled == false)) } - } - steps { - script { - println("${PipelineName}[INFO]: DBB Build Starting.") - MsgHdr = "Build Step:" - - Buildrc = 0 - - if (pipeverbose) { - sh "echo ${PipelineName}[DEBUG]: HOME = ${HOME}" - sh "echo ${PipelineName}[DEBUG]: DBB_HOME = ${DBB_HOME}" - sh "echo ${PipelineName}[DEBUG]: DBB_CONF = ${DBB_CONF}" - sh "echo ${PipelineName}[DEBUG]: CLASSPATH = ${CLASSPATH}" - } - - BuildCmd = "dbbBuild.sh -w ${BuildDir} -a ${AppName} -b ${AppBranch} -p ${PipelineType} -q ${AppHLQ} ${zAppBuildVerbose}" - println("${PipelineName}[INFO]: Build Command = ${BuildCmd}") - - Buildrc = sh(script: "${BuildCmd}", returnStatus: true) - - - if (pipeverbose) { - sh "echo ${PipelineName}[DEBUG]: HOME = ${HOME}" - sh "echo ${PipelineName}[DEBUG]: DBB_HOME = ${DBB_HOME}" - sh "echo ${PipelineName}[DEBUG]: DBB_CONF = ${DBB_CONF}" - sh "echo ${PipelineName}[DEBUG]: CLASSPATH = ${CLASSPATH}" - } - - BuildOutputDir = "${BuildDir}/logs" - - println("${PipelineName}[INFO]: Build Output Folder = ${BuildOutputDir}") - - if (pipeverbose) { - // Show the contents of the DBB Build Directory. - dir ("${BuildOutputDir}") { - sh "pwd ; ls -alT" - } - } - - // Check to see if the build has any files - if (Buildrc == 0) { - - dir ("${BuildOutputDir}") { - BuildFile = findFiles(glob: "buildList.txt") - } - - if (pipeverbose) { - println("${PipelineName}[DEBUG]: BuildFile = " + BuildFile) - println("${PipelineName}[DEBUG]: BuildFile.length = " + BuildFile.length) - println("${PipelineName}[DEBUG]: BuildFile[0].length = " + BuildFile[0].length) - } - - BuildHasFiles = (BuildFile.length > 0) && (BuildFile[0].length > 0) - - if (BuildHasFiles) { - Msg = "Build resulted in updated files." - println("${PipelineName}[INFO]: ${Msg}") - createSummary icon:"accept.svg", text: "${MsgHdr} ${Msg}" - } else { - Buildrc = 4 - Msg = "Build resulted in no updated files." - println("${PipelineName}[WARN]: ${Msg}") - createSummary icon:"warning.svg", text: "${MsgHdr} ${Msg}" - WdDoPackaging = false // Do not package if nothing was built. - WdDoDeployment = false // Do not deploy is nothing was built. - } - } else { - Msg = "Build resulted in a return code=${Buildrc}. Refer to Jenkins Console Output Log." - println("${PipelineName}[WARN]: ${Msg}") - createSummary icon:"error.svg", text: "${MsgHdr} ${Msg}" - WdDoPackaging = false // Do not package if nothing was built. - WdDoDeployment = false // Do not deploy is nothing was built. - - if (Buildrc == 4) { // Mark Build unstable - unstable "DBB Build Warning. No source changes detected." - } else { // Mark Stage as Error - SonarQubeDoScan = false // Do not scan the repo. - catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { - sh "echo 'DBB Build Error. Please check log.' && exit ${Buildrc}" - } - } - - } - } - } - post { - always { - // Pick up files created by the build. - dir ("${BuildOutputDir}") { - archiveArtifacts allowEmptyArchive: true, - artifacts: '*.log,*.json,*.html,*.txt', - excludes: '*clist', - onlyIfSuccessful: false - } - } - } - } // End: stage('Build') - - stage('SonarQube Analysis') { - // Application repository is automatically checked out - agent { label JenkinsX86Agent } - - when { - expression { return ((sonarQubeDoScan == true) && (autoCancelled == false)) } - } - - steps { - // Pull scanner configuration - dir ("${WORKSPACE}/${AppName}") { - withSonarQubeEnv(sonarQubeConnection) { - sh "sonar-scanner" - } - } - - } - } // End: stage('SonarQube Analysis') - - stage('Packaging') { - when { - expression { return ((WdDoPackaging == true) && (autoCancelled == false)) } - } - steps { - script { - println("${PipelineName}[INFO]: Packaging Starting.") - //println("${PipelineName}[DEBUG]: Pipeline Type = ${PipelineType}") - //println("${PipelineName}[DEBUG]: Release Type = ${ReleaseType}") - MsgHdr = "Packaging Step:" - - PackageOutputDir = "${BuildDir}/logs/tempPackageDir" - - // Compute the next release verion for a release pipeline. - if (PipelineType == "release") { - println("${PipelineName}[INFO]: Start computing the next release version.") - ComputeReleaseVersionCmd = "computeReleaseVersion.sh -w ${WORKSPACE} -a ${AppName} -b ${AppBranch} -r ${ReleaseType}" - - println("${PipelineName}[INFO]: Compute Release Version Command = ${ComputeReleaseVersionCmd}") - - def (outText, outRc) = runCommand(ComputeReleaseVersionCmd) - //println("${PipelineName}[DEBUG]: ******* runCommand DONE *******: ${outRc}") - - if (outRc == 0) { - //println("${PipelineName}[DEBUG]: Start Search: ${outText}") - def version = searchLogOutput("version: .*", outText) - if (version){ - //println("${PipelineName}[DEBUG]: ${version}") - ReleaseVersion = version.substring(9,version.length()) - println("${PipelineName}[INFO]: Compute Release Version successfully, Next Release = ${ReleaseVersion}") + agent { label JenkinsAgent } + + options { + checkoutToSubdirectory("${AppName}") // Added AppName for checkout dir of implicit checkout. + disableConcurrentBuilds() // Throttle builds. + } + environment { + GIT_TRACE = 'false' // Trace git for testing (true/false, 1, 2). + GIT_TRACE_SETUP = 'false' // Git trace tools. + } + + stages { + stage('Pipeline Setup') { + steps { + script { + // Required Input Parameters + properties([ + parameters([ + choice( + choices: ["build", "release", "preview"], + description: 'Pipeline type: build (CI), release (CD), or preview (quick validation)', + defaultValue: 'build', + name: 'PipelineType' + ), + choice( + choices : ["patch", "minor", "major"], + description: 'Release type for semantic versioning (major.minor.patch)', + defaultValue: 'minor', + name: 'ReleaseType' + ), + choice( + choices: ["no", "yes"], + description: 'Enable verbose pipeline logging for debugging', + defaultValue: 'no', + name: 'PipelineVerboseLogging' + ) + ]) + ]) + + println("${PipelineName}[INFO]: Start Date/Time = " + StartDateTime) + + // Validate and assess user provided parameters + if (params.PipelineVerboseLogging.equals("yes")){ + zBuilderVerbose = "-v" + verbose = true + } else { + zBuilderVerbose = "" + verbose = false + } + + PipelineType = params.PipelineType + ReleaseType = params.ReleaseType + + Buildrc = 0 + + if (verbose) { + println("${PipelineName}[DEBUG]: Format yyyyMMdd.hhmmss.mmm = " + StartDateTime.format("yyyyMMdd.hhmmss.mmm")) + println("${PipelineName}[DEBUG]: Format yyyyMMdd.HHmmss.mmm = " + StartDateTime.format("yyyyMMdd.HHmmss.mmm")) + sh "env | sort" + } + + // Pick up the AppBranch from Jenkins. + AppBranch = "${env.BRANCH_NAME}" + + // Fetch the Pipeline Name and Build Number. + // Set up and format the DBB working directory for the build results. + PipelineName = env.JOB_NAME.split("/") + PipelineName = PipelineName[0] + + BuildNumber = "${env.BUILD_NUMBER}" + BuildNumber = BuildNumber.padLeft(8, "0") // Make Build Directories easier to sort. + + // Build directory + BuildDir = "${WORKSPACE}" + + // Branch Control Setup + Branch = env.BRANCH_NAME.toUpperCase() + BranchOkay = false + def PipelineTypeUpper = PipelineType.toUpperCase() + + // Apply branch configuration using centralized map + def branchPrefix = branchConfigs.keySet().find { Branch.startsWith(it) } + + if (branchPrefix) { + def config = branchConfigs[branchPrefix] + DbbDoBuild = config.build + SonarQubeDoScan = config.scan + WdDoPackaging = config.package + WdDoDeployment = config.deploy + + // Special handling for MAIN branch release pipeline + if ((branchPrefix == 'MAIN' || branchPrefix == 'RELEASE') && PipelineType == "release") { + WdDoDeployment = true + DeployOutputDir = "${BuildDir}/deployPkgDir" + DeployEvidencesDir = "${DeployOutputDir}/deploy/evidences" + } + + BranchOkay = true + } + + // Manage preview pipelines - override configuration + if (PipelineTypeUpper.startsWith('PREVIEW')) { + DbbDoBuild = true + // Skip any subsequent steps for preview + SonarQubeDoScan = false + WdDoPackaging = false + WdDoDeployment = false + BranchOkay = true // Allow preview on any branch + } + + // If branch processing has not been defined, gracefully terminate the pipeline. + if (BranchOkay == false) { + autoCancelled = true + DbbDoBuild = false + SonarQubeDoScan = false + WdDoPackaging = false + WdDoDeployment = false + } + + println("${PipelineName}[INFO]: AppBranch = ${AppBranch}") + println("${PipelineName}[INFO]: Branch Configuration Applied: Build=${DbbDoBuild}, Scan=${SonarQubeDoScan}, Package=${WdDoPackaging}, Deploy=${WdDoDeployment}") + } + } + } // End: stage('Pipeline Setup') + + stage('Parameters and Values') { + steps { + script { + println("**************************************************************") + println("* Parameters and Variable Values") + println("*") + println("* Lifecycle") + println("* PipelineType : ${PipelineType}") + println("* ReleaseType : ${ReleaseType}") + println("*") + println("* Jenkins") + println("* env.JOB_NAME : ${env.JOB_NAME}") + println("* env.GIT_URL : ${env.GIT_URL}") + println("* env.GIT_BRANCH : ${env.GIT_BRANCH}") + println("* env.BRANCH_NAME : ${env.BRANCH_NAME}") + println("* env.BUILD_NUMBER : ${env.BUILD_NUMBER}") + println("* env.CHANGE_URL : ${env.CHANGE_URL}") // If this is a PR build + println("* WORKSPACE : ${WORKSPACE}") + println("* BuildNumber : ${BuildNumber}") + println("* PipelineName : ${PipelineName}") + println("* JenkinsAgent : ${JenkinsAgent}") + println("*") + println("* Application") + println("* AppBranch : ${AppBranch}") + println("* AppName : ${AppName}") + println("* AppHLQ : ${AppHLQ}") + println("* BuildDir : ${BuildDir}") + println("*") + println("* Build/zBuilder") + println("* zBuilderVerbose : ${zBuilderVerbose}") + println("* DbbDoBuild : ${DbbDoBuild}") + println("*") + println("* Analysis/SonarQube") + println("* SonarQubeConnection : ${SonarQubeConnection}") + println("* SonarQubeDoScan : ${SonarQubeDoScan}") + println("*") + println("* Packaging") + println("* WdDoPackaging : ${WdDoPackaging}") + println("*") + println("* Deployment/Wazi Deploy") + println("* WdDoDeployment : ${WdDoDeployment}") + println("* WdEnvFileIntegration : ${WdEnvFileIntegration}") + println("* DeployOutputDir : ${DeployOutputDir}") + println("* WdEvidenceRoot : ${WdEvidencesRoot}") + println("*") + println("* General") + println("* DefaultEncoding : ${DefaultEncoding}") + println("* autoCancelled : ${autoCancelled}") + println("*") + println("* Environmental") + println("* HOME : ${HOME}") + println("* CLASSPATH : ${CLASSPATH}") + println("* PATH : ${PATH}") + println("* JAVA_HOME : ${JAVA_HOME}") + println("* JENKINS_HOME : ${JENKINS_HOME}") + println("*") + println("**************************************************************") + } + } + } // End: stage('Parameters and Values') + + stage('Build') { + when { + expression { return ((DbbDoBuild == true) && (autoCancelled == false)) } + } + steps { + script { + println("${PipelineName}[INFO]: DBB Build Starting.") + MsgHdr = "Build Step:" + + Buildrc = 0 + + if (verbose) { + sh "echo ${PipelineName}[DEBUG]: HOME = ${HOME}" + sh "echo ${PipelineName}[DEBUG]: DBB_HOME = ${DBB_HOME}" + sh "echo ${PipelineName}[DEBUG]: DBB_CONF = ${DBB_CONF}" + sh "echo ${PipelineName}[DEBUG]: CLASSPATH = ${CLASSPATH}" + } + BuildCmd = "${DbbPipelineScripts}/zBuilder.sh -w ${WORKSPACE} -a ${AppName} -b ${AppBranch} -p ${PipelineType} -q ${AppHLQ} ${zBuilderVerbose}" + println("${PipelineName}[INFO]: Build Command = ${BuildCmd}") + + dir("${WORKSPACE}/${AppName}") { + Buildrc = sh(script: "${BuildCmd}", returnStatus: true) + } + + if (verbose) { + sh "echo ${PipelineName}[DEBUG]: Build Return Code = ${Buildrc}" + } + BuildOutputDir = "${WORKSPACE}/${AppName}/logs" + println("${PipelineName}[INFO]: Build Output Folder = ${BuildOutputDir}") + + if (verbose) { + // Show the contents of the DBB Build Directory + dir("${BuildOutputDir}") { + sh "pwd ; ls -alT" + } + } + + // Check to see if the build has any files + if (Buildrc == 0) { + dir("${BuildOutputDir}") { + BuildFile = findFiles(glob: "buildList.txt") + } + + if (verbose) { + println("${PipelineName}[DEBUG]: BuildFile = " + BuildFile) + println("${PipelineName}[DEBUG]: BuildFile.length = " + BuildFile.length) + if (BuildFile.length > 0) { + println("${PipelineName}[DEBUG]: BuildFile[0].length = " + BuildFile[0].length) + } + } + + BuildHasFiles = (BuildFile.length > 0) && (BuildFile[0].length > 0) + + if (BuildHasFiles) { + Msg = "Build resulted in updated files." + println("${PipelineName}[INFO]: ${Msg}") + if (dslMethodExists('addSummary')) { + addSummary icon: "symbol-build plugin-ionicons-api", text: "${MsgHdr} ${Msg}", style: "color: var(--success-color)" + } + } else { + Buildrc = 4 + Msg = "Build resulted in no updated files." + println("${PipelineName}[WARN]: ${Msg}") + if (dslMethodExists('addSummary')) { + addSummary icon: "symbol-warning-outline plugin-ionicons-api plugin-ionicons-api", text: "${MsgHdr} ${Msg}" + } + if (dslMethodExists('addWarningBadge')) { + addWarningBadge icon: "symbol-warning-outline plugin-ionicons-api plugin-ionicons-api", text: "${MsgHdr} ${Msg}" + } + WdDoPackaging = false // Do not package if nothing was built. + WdDoDeployment = false // Do not deploy is nothing was built. + } + } else { + Msg = "Build resulted in a return code=${Buildrc}. Refer to Jenkins Console Output Log." + println("${PipelineName}[ERROR]: ${Msg}") + if (dslMethodExists('addSummary')) { + addSummary icon: "symbol-alert-circle-outline plugin-ionicons-api", text: "${MsgHdr} ${Msg}", style: "color: var(--error-color)" + } + if (dslMethodExists('addErrorBadge')) { + addErrorBadge icon: "symbol-error", text: "${MsgHdr} ${Msg}", style: "color: var(--error-color)" + } + WdDoPackaging = false // Do not package if nothing was built. + WdDoDeployment = false // Do not deploy is nothing was built. + + if (Buildrc == 4) { // Mark Build unstable + unstable "DBB Build Warning. No source changes detected." + } else { // Mark Stage as Error + SonarQubeDoScan = false // Do not scan the repo. + catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { + sh "echo 'DBB Build Error. Please check log.' && exit ${Buildrc}" + } + } + } + } + } + post { + always { + // Pick up files created by the build. + dir("${BuildOutputDir}") { + archiveArtifacts allowEmptyArchive: true, + artifacts: '*.log,*.json,*.html,*.txt', + excludes: '*clist', + onlyIfSuccessful: false + } + } + } + } // End: stage('Build') + + stage('SonarQube Analysis') { + // Application repository is automatically checked out. + agent { label JenkinsX86Agent } + + when { + expression { return ((SonarQubeDoScan == true) && (autoCancelled == false)) } + } + + steps { + script { + println("${PipelineName}[INFO]: Starting SonarQube Analysis.") + + // Pull scanner configuration + dir("${WORKSPACE}/${AppName}") { + withSonarQubeEnv(SonarQubeConnection) { + sh "sonar-scanner" + } + } + } + } + } // End: stage('SonarQube Analysis') + + stage('Packaging') { + when { + expression { return ((WdDoPackaging == true) && (autoCancelled == false)) } + } + steps { + script { + println("${PipelineName}[INFO]: Packaging Starting.") + if (verbose) { + println("${PipelineName}[DEBUG]: Pipeline Type = ${PipelineType}") + println("${PipelineName}[DEBUG]: Release Type = ${ReleaseType}")println("${PipelineName}[DEBUG]: Release Type = ${ReleaseType}") + } + + MsgHdr = "Packaging Step:" + + PackageOutputDir = "${WORKSPACE}/logs/tempPackageDir" + + // Compute the next release version for a release pipeline. + if (PipelineType == "release") { + println("${PipelineName}[INFO]: Start computing the next release version.") + ComputeReleaseVersionCmd = "${DbbPipelineScripts}/computeReleaseVersion.sh -w ${WORKSPACE} -a ${AppName} -b ${AppBranch} -r ${ReleaseType}" + + println("${PipelineName}[INFO]: Compute Release Version Command = ${ComputeReleaseVersionCmd}") + + def (outText, outRc) = runCommand(ComputeReleaseVersionCmd) + + if (outRc == 0) { + def version = searchLogOutput("version: .*", outText) + if (version) { + if (verbose) { + println("${PipelineName}[DEBUG]: ${version}") + } + ReleaseVersion = version.substring(9, version.indexOf(". rc=")) + println("${PipelineName}[INFO]: Compute Release Version successfully, Next Release = ${ReleaseVersion}") } else { Msg = "Computing release version failed. No version specified. Refer to Jenkins Console Output Log." - println("${PipelineName}[WARN]: ${Msg}") - createSummary icon:"error.svg", text: "${MsgHdr} ${Msg}" - autoCancelled == true + println("${PipelineName}[ERROR]: ${Msg}") + if (dslMethodExists('addSummary')) { + addSummary icon: "symbol-alert-circle-outline plugin-ionicons-api", text: "${MsgHdr} ${Msg}", style: "color: var(--error-color)" + } + if (dslMethodExists('addErrorBadge')) { + addErrorBadge icon: "symbol-error", text: "${MsgHdr} ${Msg}", style: "color: var(--error-color)" + } + autoCancelled = true + } + } else { + Msg = "Computing release version failed. Exit code: ${outRc}. Refer to Jenkins Console Output Log." + println("${PipelineName}[ERROR]: ${Msg}") + if (dslMethodExists('addSummary')) { + addSummary icon: "symbol-alert-circle-outline plugin-ionicons-api", text: "${MsgHdr} ${Msg}", style: "color: var(--error-color)" + } + if (dslMethodExists('addErrorBadge')) { + addErrorBadge icon: "symbol-error", text: "${MsgHdr} ${Msg}", style: "color: var(--error-color)" + } + autoCancelled = true + } + if (verbose) { + println("${PipelineName}[DEBUG]: Do not compute the next release version for non-release pipeline.") + } + autoCancelled = false + } + + // Start packaging when compute the next release version successfully or not a release pipeline. + if (autoCancelled == false) { + // The packaging script expects the BuildOutputDir to be in the workspace directory. + sh "rm -rf ${WORKSPACE}/logs" + sh "mv ${BuildOutputDir} ${WORKSPACE}" + + // Start packageBuildOutputs.sh command + if (ReleaseVersion == "") { + println("${PipelineName}[INFO]: Start packaging for a build pipeline.") + PackageCmd = "${DbbPipelineScripts}/packageBuildOutputs.sh -w ${WORKSPACE} -a ${AppName} -b ${AppBranch} -p ${PipelineType} -i ${BuildNumber}" + } else { + println("${PipelineName}[INFO]: Start packaging for a release pipeline. Release version: ${ReleaseVersion}") + PackageCmd = "${DbbPipelineScripts}/packageBuildOutputs.sh -w ${WORKSPACE} -a ${AppName} -b ${AppBranch} -p ${PipelineType} -r ${ReleaseVersion} -i ${BuildNumber}" + } + + println("${PipelineName}[INFO]: Package Command = ${PackageCmd}") + + Packagerc = sh(script: "${PackageCmd}", returnStatus: true) + if (Packagerc == 0) { + println("${PipelineName}[INFO]: Packaging job passed.") + autoCancelled = false + + // Find the package tar file to use as input for Wazi Deploy stages. + dir("${WORKSPACE}/logs") { + def tarFiles = findFiles(glob: "build-*.tar") + if (tarFiles.length > 0) { + PackageTarPath = tarFiles[0].path + println("${PipelineName}[INFO]: Found package tar file: ${PackageTarPath}") + } else { + Msg = "No package tar file found in ${WORKSPACE}/logs/" + println("${PipelineName}[ERROR]: ${Msg}") + autoCancelled = true + catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { + sh "echo '${Msg}' && exit 1" + } + } + } + } else { + Msg = "Packaging job failed. Exit code: ${Packagerc}. Refer to Jenkins Console Output Log." + println("${PipelineName}[ERROR]: ${Msg}") + if (dslMethodExists('addSummary')) { + addSummary icon: "symbol-alert-circle-outline plugin-ionicons-api", text: "${MsgHdr} ${Msg}", style: "color: var(--error-color)" } - } - else { - Msg = "Computing release version failed. Exit code: ${ComputeReleaseVersionrc}. Refer to Jenkins Console Output Log." - println("${PipelineName}[WARN]: ${Msg}") - createSummary icon:"error.svg", text: "${MsgHdr} ${Msg}" - autoCancelled == true - } - //println("${PipelineName}[DEBUG]: Do not compute the next release version for non-release pipeline.") - autoCancelled == false - } - - // Start packaging when compute the next release version successfully or not a release pipeline - if (autoCancelled == false) { - // Start packageBuildOutputs.sh command - if (ReleaseVersion == "") { - println("${PipelineName}[INFO]: Start packaging for a build pipeline.") - PackageCmd = "packageBuildOutputs.sh -w ${WORKSPACE} -a ${AppName} -b ${AppBranch} -p ${PipelineType} -i ${BuildNumber}" - } - else { - println("${PipelineName}[INFO]: Start packaging for a release pipeline. Release version: ${ReleaseVersion}") - PackageCmd = "packageBuildOutputs.sh -w ${WORKSPACE} -a ${AppName} -b ${AppBranch} -p ${PipelineType} -r ${ReleaseVersion} -i ${BuildNumber}" - } - - println("${PipelineName}[INFO]: Package Command = ${PackageCmd}") - - Packagerc = sh(script: "${PackageCmd}" , returnStatus: true) - if (Packagerc == 0) { - println("${PipelineName}[INFO]: Packaging job passed.") - autoCancelled == false - } - else { - Msg = "Packaging job failed. Exit code: ${Packagerc}. Refer to Jenkins Console Output Log." - println("${PipelineName}[WARN]: ${Msg}") - createSummary icon:"error.svg", text: "${MsgHdr} ${Msg}" - autoCancelled == true - } - } - - if (autoCancelled == true) { // Mark Stage as Error - catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { - sh "echo 'Packaging jab failed. Please check console output log.' && exit 8" - } - } - } - } - post { - always { - // Pick up files created by the packaging. - dir ("${PackageOutputDir}") { - archiveArtifacts allowEmptyArchive: true, - artifacts: '*.log,*.json,*.html,*.txt', - excludes: '*clist', - onlyIfSuccessful: false - } - } - } - } - - stage ('Generate Deployment Plan') { - when { - expression { return ((WdDoDeployment == true) && (autoCancelled == false)) } - } - steps { - script { - - WdGenDeployPlanrc = 0 - MsgHdr = "Generate Deployment Plan Step:" - - // Generage deployment plan - println("${PipelineName}[INFO] Start generating the deployment plan for a release pipeline. Release version: ${ReleaseVersion}") - WdGenDeployPlanCmd = "wazideploy-generate.sh -w ${WORKSPACE} -a ${AppName} -b ${AppBranch} -P ${PipelineType} -R ${ReleaseVersion} -I ${BuildNumber}" - - WdGenDeployPlanrc = sh(script: "${WdGenDeployPlanCmd}" , returnStatus: true) - - if (WdGenDeployPlanrc == 0) { - println("${PipelineName}[INFO]: Generate the deployment plan job passed.") - autoCancelled == false - } - else { - Msg = "Generate the deployment plan job failed. Exit code: ${WdGenDeployPlanrc}. Refer to Jenkins Console Output Log." - println("${PipelineName}[WARN]: ${Msg}") - createSummary icon:"error.svg", text: "${MsgHdr} ${Msg}" - autoCancelled == true - catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { - sh "echo 'Packaging jab failed. Please check console output log.' && exit ${WdGenDeployPlanrc}" - } - } - - DeployOutputDir = "${WORKSPACE}/deployPkgDir" - //println("${PipelineName}[DEBUG]: DeployOutputDir = ${DeployOutputDir}") - - } - } - post { - always { - // Pick up files created by the packaging. - dir ("${DeployOutputDir}") { - archiveArtifacts allowEmptyArchive: true, - artifacts: '*.html,*.yaml', - excludes: '*clist', - onlyIfSuccessful: false - } - } - } - } // End: stage('Generate Deployment Plan') - - stage ('Deployment-INT') { - when { - expression { return ((WdDoDeployment == true) && (autoCancelled == false)) } - } - steps { - script { - - WdDeployCmd = 0 - MsgHdr = "Deploy Integration Step:" - - WdDeployCmd = "wazideploy-deploy.sh -w ${WORKSPACE} -e ${WdEnvFileIntegration} -l deploy/evidences/evidence.yaml" - - WdDeployrc = sh(script: "${WdDeployCmd}" , returnStatus: true) - - if (WdDeployrc == 0) { - println("${PipelineName}[INFO]: Deploy to integration job passed") - - WdEvidenceCmd = "wazideploy-evidence.sh -w ${WORKSPACE} -l deploy/evidences/evidence.yaml -o deploy/deployment-report.html" - WdEvidenceRc = sh(script: "${WdEvidenceCmd}" , returnStatus: true) - //println("${PipelineName}[DEBUG]: WdEvidenceRc: ${WdEvidenceRc}") - - // Generate deployment evidence report - if (WdEvidenceRc == 0) { - println("${PipelineName}[INFO]: Generated deployment evidence report") - } - else { - println("${PipelineName}[ERROR]: Failed to generate deployment evidence report") - } - - DeployEvidencesDir = "${WORKSPACE}/deployPkgDir/deploy/evidences" - WdEvidencesDir = "${WdEvidencesRoot}/${AppName}/integration" - //println("${PipelineName}[DEBUG]: DeployEvidencesDir = ${DeployEvidencesDir}") - - //println("${PipelineName}[DEBUG]: WdEvidencesDir = ${WdEvidencesDir}") - - // Create evidence directory - ShCmd = "mkdir -p ${WdEvidencesDir} && mkdir -p ${WdEvidencesIndex}" - ShCmdRc = sh(script: "${ShCmd}" , returnStatus: true) - //println("${PipelineName}[DEBUG]: ShCmdRc: ${ShCmdRc}") - - //Copy evidence file - if (ShCmdRc == 0) { - println("${PipelineName}[INFO]: Created evidence directory successfully: ${WdEvidencesDir}") - println("${PipelineName}[INFO]: Created evidence index directory successfully: ${WdEvidencesIndex}") - ShCmd = "cp ${DeployEvidencesDir}/evidence.yaml ${WdEvidencesDir}/evidence-${BuildNumber}.yaml" - ShCmdRc = sh(script: "${ShCmd}" , returnStatus: true) - //println("${PipelineName}[DEBUG]: ShCmdRc: ${ShCmdRc}") - } - else { - println("${PipelineName}[INFO]: Create evidence directories failed") - } - - // Refresh index of all applications - if (ShCmdRc == 0) { - println("${PipelineName}[INFO]: Persisted deployment evidence file successfully at ${WdEvidencesDir}") - println("${PipelineName}[INFO]: Refresh index of all applications at ${WdEvidencesIndex}") - WdEvidenceCmd = "wazideploy-evidence --index ${WdEvidencesIndex} --dataFolder ${WdEvidencesRoot} i" - WdEvidenceRc = sh(script: "${WdEvidenceCmd}" , returnStatus: true) - } - - // Error handling - if (WdEvidenceRc == 0) { - println("${PipelineName}[INFO]: Update Wazi Deploy index completed.") - } - else { - println("${PipelineName}[WARNING]: Update Wazi Deploy index failed.") - } - - } - else { - Msg = "Deploy to integration job failed. Exit code: ${WdDeployrc}. Refer to Jenkins Console Output Log." - println("${PipelineName}[WARN]: ${Msg}") - createSummary icon:"error.svg", text: "${MsgHdr} ${Msg}" - catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { - sh "echo 'Packaging jab failed. Please check console output log.' && exit ${WdDeployrc}" - } - } - } - } - post { - always { - // Pick up files created by the deployment. - dir ("${DeployEvidencesDir}") { - archiveArtifacts allowEmptyArchive: true, - artifacts: '*.yml,*.yaml', - excludes: '*clist', - onlyIfSuccessful: false - } - dir ("${WORKSPACE}/deployPkgDir/deploy") { - archiveArtifacts allowEmptyArchive: true, - artifacts: '*.html', - excludes: '*clist', - onlyIfSuccessful: false - } - } - } - } // End: stage('Deployment-INT') - - stage ('Workspace Cleanup') { - when { - expression { return (autoCancelled == false) } - } - steps { - script { - if (pipeverbose) { - println("${PipelineName}[DEBUG]: Final Cleanup before deletes.") - - dir("${WORKSPACE}") { - sh "pwd ; ls -al" - } - } - - if (pipeverbose) { - println("${PipelineName}[DEBUG]: Final Cleanup after deletes.") - - dir("${WORKSPACE}") { - sh "pwd ; ls -al" - } - } - } - } - post { - // Clean after build - always { - cleanWs(cleanWhenNotBuilt: false, - deleteDirs: true, - disableDeferredWipeout: true, - notFailBuild: true) - } - } - - } // End: stage ('Final Cleanup') - } // End: stages + if (dslMethodExists('addErrorBadge')) { + addErrorBadge icon: "symbol-error", text: "${MsgHdr} ${Msg}", style: "color: var(--error-color)" + } + autoCancelled = true + } + } + + if (autoCancelled == true) { // Mark Stage as Error + catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { + sh "echo 'Packaging job failed. Please check console output log.' && exit 8" + } + } + } + } + post { + always { + // Pick up files created by the packaging + dir("${PackageOutputDir}") { + archiveArtifacts allowEmptyArchive: true, + artifacts: '*.log,*.json,*.html,*.txt', + excludes: '*clist', + onlyIfSuccessful: false + } + } + } + } // End: stage('Packaging') + + stage('Generate Deployment Plan') { + when { + expression { return ((WdDoDeployment == true) && (autoCancelled == false)) } + } + steps { + script { + WdGenDeployPlanrc = 0 + MsgHdr = "Generate Deployment Plan Step:" + + // Generate deployment plan + println("${PipelineName}[INFO]: Start generating the deployment plan for a release pipeline. Release version: ${ReleaseVersion}") + if (autoCancelled == false) { + WdGenDeployPlanCmd = "${DbbPipelineScripts}/wazideploy-generate.sh -w ${WORKSPACE} -a ${AppName} -P ${PipelineType} -R ${ReleaseVersion} -I ${BuildNumber} -i ${PackageTarPath}" + + dir("${WORKSPACE}") { + WdGenDeployPlanrc = sh(script: "${WdGenDeployPlanCmd}", returnStatus: true) + } + + if (WdGenDeployPlanrc == 0) { + println("${PipelineName}[INFO]: Generate the deployment plan job passed.") + autoCancelled = false + } else { + Msg = "Generate the deployment plan job failed. Exit code: ${WdGenDeployPlanrc}. Refer to Jenkins Console Output Log." + println("${PipelineName}[ERROR]: ${Msg}") + if (dslMethodExists('addSummary')) { + addSummary icon: "symbol-alert-circle-outline plugin-ionicons-api", text: "${MsgHdr} ${Msg}", style: "color: var(--error-color)" + } + if (dslMethodExists('addErrorBadge')) { + addErrorBadge icon: "symbol-error", text: "${MsgHdr} ${Msg}", style: "color: var(--error-color)" + } + autoCancelled = true + catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { + sh "echo 'Generate deployment plan job failed. Please check console output log.' && exit ${WdGenDeployPlanrc}" + } + } + + DeployOutputDir = "${WORKSPACE}/deployPkgDir" + if (verbose) { + println("${PipelineName}[DEBUG]: DeployOutputDir = ${DeployOutputDir}") + } + } + } + post { + always { + // Pick up files created by the packaging + dir("${DeployOutputDir}") { + archiveArtifacts allowEmptyArchive: true, + artifacts: '*.html,*.yaml', + excludes: '*clist', + onlyIfSuccessful: false + } + } + } + } // End: stage('Generate Deployment Plan') + + stage('Deployment-INT') { + when { + expression { return ((WdDoDeployment == true) && (autoCancelled == false)) } + } + steps { + script { + WdDeployCmd = "" + MsgHdr = "Deploy Integration Step:" + + WdDeployCmd = "${DbbPipelineScripts}/wazideploy-deploy.sh -w ${WORKSPACE} -e ${WdEnvFileIntegration} -i ${PackageTarPath} -l deploy/evidences/evidence.yaml" + if (verbose) { + WdDeployCmd = WdDeployCmd+" -d" + } + WdDeployrc = sh(script: "${WdDeployCmd}", returnStatus: true) + + if (WdDeployrc == 0) { + println("${PipelineName}[INFO]: Deploy to integration job passed") + + WdEvidenceCmd = "${DbbPipelineScripts}/wazideploy-evidence.sh -w ${WORKSPACE} -l deploy/evidences/evidence.yaml -o deploy/deployment-report.html" + WdEvidenceRc = sh(script: "${WdEvidenceCmd}", returnStatus: true) + if (verbose) { + println("${PipelineName}[DEBUG]: WdEvidenceRc: ${WdEvidenceRc}") + } + + // Generate deployment evidence report + if (WdEvidenceRc == 0) { + println("${PipelineName}[INFO]: Generated deployment evidence report") + } else { + println("${PipelineName}[ERROR]: Failed to generate deployment evidence report") + } + + DeployEvidencesDir = "${WORKSPACE}/deployPkgDir/deploy/evidences" + WdEvidencesDir = "${WdEvidencesRoot}/${AppName}/integration" + if (verbose) { + println("${PipelineName}[DEBUG]: DeployEvidencesDir = ${DeployEvidencesDir}") + println("${PipelineName}[DEBUG]: WdEvidencesDir = ${WdEvidencesDir}") + } + + // Create evidence directory + ShCmd = "mkdir -p ${WdEvidencesDir} && mkdir -p ${WdEvidencesIndex}" + ShCmdRc = sh(script: "${ShCmd}", returnStatus: true) + + // Copy evidence file + if (ShCmdRc == 0) { + println("${PipelineName}[INFO]: Created evidence directory successfully: ${WdEvidencesDir}") + println("${PipelineName}[INFO]: Created evidence index directory successfully: ${WdEvidencesIndex}") + ShCmd = "cp ${DeployEvidencesDir}/evidence.yaml ${WdEvidencesDir}/evidence-${BuildNumber}.yaml" + ShCmdRc = sh(script: "${ShCmd}", returnStatus: true) + } else { + println("${PipelineName}[ERROR]: Create evidence directories failed") + } + + // Refresh index of all applications + if (ShCmdRc == 0) { + println("${PipelineName}[INFO]: Persisted deployment evidence file successfully at ${WdEvidencesDir}") + println("${PipelineName}[INFO]: Refresh index of all applications at ${WdEvidencesIndex}") + WdEvidenceCmd = "wazideploy-evidence --index ${WdEvidencesIndex} --dataFolder ${WdEvidencesRoot} i" + WdEvidenceRc = sh(script: "${WdEvidenceCmd}", returnStatus: true) + } + + // Error handling + if (WdEvidenceRc == 0) { + println("${PipelineName}[INFO]: Update Wazi Deploy index completed.") + } else { + println("${PipelineName}[WARNING]: Update Wazi Deploy index failed.") + } + } else { + Msg = "Deploy to integration job failed. Exit code: ${WdDeployrc}. Refer to Jenkins Console Output Log." + println("${PipelineName}[ERROR]: ${Msg}") + if (dslMethodExists('addSummary')) { + addSummary icon: "symbol-alert-circle-outline plugin-ionicons-api", text: "${MsgHdr} ${Msg}", style: "color: var(--error-color)" + } + if (dslMethodExists('addErrorBadge')) { + addErrorBadge icon: "symbol-error", text: "${MsgHdr} ${Msg}", style: "color: var(--error-color)" + } + catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') { + sh "echo 'Deployment job failed. Please check console output log.' && exit ${WdDeployrc}" + } + } + } + } + post { + always { + // Pick up files created by the deployment. + dir("${DeployEvidencesDir}") { + archiveArtifacts allowEmptyArchive: true, + artifacts: '*.yml,*.yaml', + excludes: '*clist', + onlyIfSuccessful: false + } + dir("${WORKSPACE}/deployPkgDir/deploy") { + archiveArtifacts allowEmptyArchive: true, + artifacts: '*.html', + excludes: '*clist', + onlyIfSuccessful: false + } + } + } + } // End: stage('Deployment-INT') + + stage('Workspace Cleanup') { + when { + expression { return (autoCancelled == false) } + } + steps { + script { + println("${PipelineName}[INFO]: Performing workspace cleanup.") + + if (verbose) { + dir("${WORKSPACE}") { + sh "pwd ; ls -al" + } + } + } + } + post { + // Clean after build + always { + cleanWs(cleanWhenNotBuilt: false, + deleteDirs: true, + disableDeferredWipeout: true, + notFailBuild: true) + } + } + } // End: stage('Workspace Cleanup') + } // End: stages } // End: pipeline +// ============================================================================ +// Helper Methods +// ============================================================================ + /** - * Methods that perform non-serializable actions + * Search for a pattern in log output + * @param regexPattern The regex pattern to search for + * @param logContent The log content to search in + * @return The matched result or null if not found */ - @NonCPS -def searchLogOutput(String regexPattern, String logContent){ - //println("[DEBUG]: Start searchLogOutput = ${regexPattern} and ${logContent}") - pattern = java.util.regex.Pattern.compile(regexPattern) - def pMatcher = pattern.matcher(logContent) - if (pMatcher.find()) { - def result = "${pMatcher.group()}" - println("Results == ${result}") - return result - } else { - println("[INFO]: Failed to search for ${regexPattern}") +def searchLogOutput(String regexPattern, String logContent) { + try { + def pattern = java.util.regex.Pattern.compile(regexPattern) + def pMatcher = pattern.matcher(logContent) + if (pMatcher.find()) { + def result = "${pMatcher.group()}" + println("Results == ${result}") + return result + } else { + println("[INFO]: Failed to search for ${regexPattern}") + return null + } + } catch (Exception e) { + println("[ERROR]: Exception in searchLogOutput: ${e.message}") return null - } + } } +/** + * Execute a shell command and capture output + * @param cmd The command to execute + * @return A list containing [stdout, returnCode] + */ def runCommand(String cmd) { - def stdout + def stdout = "" def rc = 0 - //println("[DEBUG]: Start runCommand: ${cmd}") - try { + try { stdout = sh(script: cmd, returnStdout: true) echo stdout - } catch (e) { - echo "${e}" - rc = "${e}".tokenize().last() + } catch (Exception e) { + echo "[ERROR]: Command execution failed: ${e.message}" + // More robust error code extraction + def errorMsg = e.toString() + if (errorMsg.contains('script returned exit code')) { + def matcher = errorMsg =~ /exit code (\d+)/ + if (matcher.find()) { + rc = matcher.group(1) as Integer + } else { + rc = 1 // Default error code + } + } else { + rc = 1 // Default error code + } } return [stdout, rc] -} \ No newline at end of file +} + +boolean dslMethodExists(String name) { + return this.getBinding().hasVariable(name) +}