2018-10-30 17:49:53 +02:00
import static com . sap . piper . Prerequisites . checkScript
2018-10-17 12:05:11 +02:00
import com.sap.piper.ConfigurationHelper
import com.sap.piper.Utils
import groovy.text.SimpleTemplateEngine
import groovy.transform.Field
2018-11-29 10:54:05 +02:00
@Field String STEP_NAME = getClass ( ) . getName ( )
2018-10-17 12:05:11 +02:00
@Field Set GENERAL_CONFIG_KEYS = [ 'gitSshKeyCredentialsId' ]
@Field Set STEP_CONFIG_KEYS = [
'projectName' ,
'buildResult' ,
'gitUrl' ,
'gitCommitId' ,
'gitSshKeyCredentialsId' ,
'wrapInNode' ,
'notifyCulprits' ,
'notificationAttachment' ,
'notificationRecipients' ,
'numLogLinesInBody'
]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
void call ( Map parameters = [ : ] ) {
handlePipelineStepErrors ( stepName: STEP_NAME , stepParameters: parameters , allowBuildFailure: true ) {
2018-10-31 09:40:12 +02:00
def script = checkScript ( this , parameters ) ? : this
2018-10-17 12:05:11 +02:00
// load default & individual configuration
2018-10-18 11:02:09 +02:00
Map config = ConfigurationHelper . newInstance ( this )
. loadStepDefaults ( )
2018-10-17 12:05:11 +02:00
. mixinGeneralConfig ( script . commonPipelineEnvironment , GENERAL_CONFIG_KEYS )
. mixinStepConfig ( script . commonPipelineEnvironment , STEP_CONFIG_KEYS )
. mixinStageConfig ( script . commonPipelineEnvironment , parameters . stageName ? : env . STAGE_NAME , STEP_CONFIG_KEYS )
. mixin (
projectName: script . currentBuild . fullProjectName ,
displayName: script . currentBuild . displayName ,
buildResult: script . currentBuild . result ,
gitUrl: script . commonPipelineEnvironment . getGitSshUrl ( ) ,
gitCommitId: script . commonPipelineEnvironment . getGitCommitId ( )
)
. mixin ( parameters , PARAMETER_KEYS )
. use ( )
new Utils ( ) . pushToSWA ( [ step: STEP_NAME ] , config )
//this takes care that terminated builds due to milestone-locking do not cause an error
if ( script . commonPipelineEnvironment . getBuildResult ( ) = = 'ABORTED' ) return
def subject = "${config.buildResult}: Build ${config.projectName} ${config.displayName}"
def log = ''
def mailTemplate
if ( config . buildResult = = 'UNSTABLE' | | config . buildResult = = 'FAILURE' ) {
mailTemplate = 'com.sap.piper/templates/mailFailure.html'
log = script . currentBuild . rawBuild . getLog ( config . numLogLinesInBody ) . join ( '\n' )
} else if ( hasRecovered ( config . buildResult , script . currentBuild ) ) {
mailTemplate = 'com.sap.piper/templates/mailRecover.html'
subject + = ' is back to normal'
}
if ( mailTemplate ) {
def mailContent = SimpleTemplateEngine . newInstance ( ) . createTemplate ( libraryResource ( mailTemplate ) ) . make ( [ env: env , log: log ] ) . toString ( )
def recipientList = ''
if ( config . notifyCulprits ) {
if ( ! config . gitUrl ) {
echo "[${STEP_NAME}] no gitUrl available, -> exiting without sending mails"
return
}
recipientList + = getCulpritCommitters ( config , script . currentBuild )
}
if ( config . notificationRecipients )
recipientList + = " ${config.notificationRecipients}"
emailext (
mimeType: 'text/html' ,
subject: subject . trim ( ) ,
body: mailContent ,
to: recipientList . trim ( ) ,
recipientProviders: [ requestor ( ) ] ,
attachLog: config . notificationAttachment
)
}
}
}
def getNumberOfCommits ( buildList ) {
def numCommits = 0
if ( buildList ! = null )
for ( actBuild in buildList ) {
def changeLogSets = actBuild . getChangeSets ( )
if ( changeLogSets ! = null )
for ( changeLogSet in changeLogSets )
for ( change in changeLogSet )
numCommits + +
}
return numCommits
}
def getCulpritCommitters ( config , currentBuild ) {
def recipients
def buildList = [ ]
def build = currentBuild
if ( build ! = null ) {
// At least add the current build
buildList . add ( build )
// Now collect FAILED or ABORTED ones
build = build . getPreviousBuild ( )
while ( build ! = null ) {
if ( build . getResult ( ) ! = 'SUCCESS' ) {
buildList . add ( build )
} else {
break
}
build = build . getPreviousBuild ( )
}
}
def numberOfCommits = getNumberOfCommits ( buildList )
if ( config . wrapInNode ) {
node ( ) {
try {
recipients = getCulprits ( config , env . BRANCH_NAME , numberOfCommits )
} finally {
deleteDir ( )
}
}
} else {
try {
recipients = getCulprits ( config , env . BRANCH_NAME , numberOfCommits )
} finally {
deleteDir ( )
}
}
echo "[${STEP_NAME}] last ${numberOfCommits} commits revealed following responsibles ${recipients}"
return recipients
}
def getCulprits ( config , branch , numberOfCommits ) {
2018-11-20 16:56:00 +02:00
try {
if ( branch ? . startsWith ( 'PR-' ) ) {
//special GitHub Pull Request handling
2018-10-17 12:05:11 +02:00
deleteDir ( )
sshagent (
credentials: [ config . gitSshKeyCredentialsId ] ,
ignoreMissing: true
) {
2018-11-20 16:56:00 +02:00
def pullRequestID = branch . replaceAll ( 'PR-' , '' )
def localBranchName = "pr" + pullRequestID ;
sh "" " git init
git fetch $ { config . gitUrl } pull /${pullRequestID}/ head: $ { localBranchName } > /dev/ null 2 > & 1
git checkout - f $ { localBranchName } > /dev/ null 2 > & 1
"" "
2018-10-17 12:05:11 +02:00
}
} else {
2018-11-20 16:56:00 +02:00
//standard git/GitHub handling
if ( config . gitCommitId ) {
deleteDir ( )
sshagent (
credentials: [ config . gitSshKeyCredentialsId ] ,
ignoreMissing: true
) {
sh "" " git clone $ { config . gitUrl } .
git checkout $ { config . gitCommitId } > /dev/ null 2 > & 1 "" "
}
} else {
def retCode = sh ( returnStatus: true , script: 'git log > /dev/null 2>&1' )
if ( retCode ! = 0 ) {
echo "[${STEP_NAME}] No git context available to retrieve culprits"
return ''
}
2018-10-17 12:05:11 +02:00
}
}
2018-11-20 16:56:00 +02:00
def recipients = sh ( returnStdout: true , script: "git log -${numberOfCommits} --pretty=format:'%ae %ce'" )
return getDistinctRecipients ( recipients )
} catch ( err ) {
echo "[${STEP_NAME}] Culprit retrieval from git failed with '${err.getMessage()}'. Please make sure to configure gitSshKeyCredentialsId. So far, only fixed list of recipients is used."
return ''
}
2018-10-17 12:05:11 +02:00
}
def getDistinctRecipients ( recipients ) {
def result
def recipientAddresses = recipients . split ( )
def knownAddresses = new HashSet < String > ( )
if ( recipientAddresses ! = null ) {
for ( address in recipientAddresses ) {
address = address . trim ( )
if ( address
& & address . contains ( "@" )
& & ! address . startsWith ( "noreply" )
& & ! knownAddresses . contains ( address ) ) {
knownAddresses . add ( address )
}
}
result = knownAddresses . join ( " " )
}
return result
}
def hasRecovered ( buildResult , currentBuild ) {
return buildResult = = 'SUCCESS' & & currentBuild . getPreviousBuild ( ) ? . result ! = 'SUCCESS'
}