import com.sap.piper.JsonUtils import static com.sap.piper.Prerequisites.checkScript import com.sap.piper.GenerateDocumentation import com.sap.piper.Utils import com.sap.piper.ConfigurationHelper import groovy.transform.Field @Field String STEP_NAME = getClass().getName() @Field Set GENERAL_CONFIG_KEYS = [ /** Allows to overwrite the GitHub API url.*/ 'githubApiUrl', /** * Allows to overwrite the GitHub token credentials id. * @possibleValues Jenkins credential id */ 'githubTokenCredentialsId', /** Allows to overwrite the GitHub url.*/ 'githubServerUrl' ] @Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS.plus([ /** * If it is set to `true`, a list of all closed issues and merged pull-requests since the last release will added below the `releaseBodyHeader`. * @possibleValues `true`, `false` */ 'addClosedIssues', /** * If you set `addDeltaToLastRelease` to `true`, a link will be added to the relese information that brings up all commits since the last release. * @possibleValues `true`, `false` */ 'addDeltaToLastRelease', /** Allows to pass additional filter criteria for retrieving closed issues since the last release. Additional criteria could be for example specific `label`, or `filter` according to [GitHub API documentation](https://developer.github.com/v3/issues/).*/ 'customFilterExtension', /** Allows to exclude issues with dedicated labels. Usage is like `excludeLabels: ['label1', 'label2']`.*/ 'excludeLabels', /** Allows to overwrite the GitHub organitation.*/ 'githubOrg', /** Allows to overwrite the GitHub repository.*/ 'githubRepo', /** Allows to specify the content which will appear for the release.*/ 'releaseBodyHeader', /** Defines the version number which will be written as tag as well as release name.*/ 'version' ]) @Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS /** * This step creates a tag in your GitHub repository together with a release. * * The release can be filled with text plus additional information like: * * * Closed pull request since last release * * Closed issues since last release * * link to delta information showing all commits since last release * * The result looks like * * ![Example release](../images/githubRelease.png) */ @GenerateDocumentation void call(Map parameters = [:]) { handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) { def script = checkScript(this, parameters) ?: this // load default & individual configuration Map config = ConfigurationHelper.newInstance(this) .loadStepDefaults() .mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS) .mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS) .mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS) .mixin(parameters, PARAMETER_KEYS) .addIfEmpty('githubOrg', script.commonPipelineEnvironment.getGithubOrg()) .addIfEmpty('githubRepo', script.commonPipelineEnvironment.getGithubRepo()) .addIfEmpty('version', script.commonPipelineEnvironment.getArtifactVersion()) .withMandatoryProperty('githubOrg') .withMandatoryProperty('githubRepo') .withMandatoryProperty('githubTokenCredentialsId') .withMandatoryProperty('version') .use() new Utils().pushToSWA([step: STEP_NAME], config) withCredentials([string(credentialsId: config.githubTokenCredentialsId, variable: 'TOKEN')]) { def releaseBody = config.releaseBodyHeader?"${config.releaseBodyHeader}
":'' def content = getLastRelease(config, TOKEN) if (config.addClosedIssues) releaseBody += addClosedIssue(config, TOKEN, content.published_at) if (config.addDeltaToLastRelease) releaseBody += addDeltaToLastRelease(config, content.tag_name) postNewRelease(config, TOKEN, releaseBody) } } } Map getLastRelease(config, TOKEN){ def result = [:] def response = httpRequest url: "${config.githubApiUrl}/repos/${config.githubOrg}/${config.githubRepo}/releases/latest?access_token=${TOKEN}", validResponseCodes: '100:500' if (response.status == 200) { result = readJSON text: response.content } else { echo "[${STEP_NAME}] This is the first release - no previous releases available" config.addDeltaToLastRelease = false } return result } String addClosedIssue(config, TOKEN, publishedAt){ if (config.customFilterExtension) { config.customFilterExtension = "&${config.customFilterExtension}" } def publishedAtFilter = publishedAt ? "&since=${publishedAt}": '' def response = httpRequest "${config.githubApiUrl}/repos/${config.githubOrg}/${config.githubRepo}/issues?access_token=${TOKEN}&per_page=100&state=closed&direction=asc${publishedAtFilter}${config.customFilterExtension}" def result = '' content = readJSON text: response.content //list closed pull-requests result += '
**List of closed pull-requests since last release**
' for (def item : content) { if (item.pull_request && !isExcluded(item, config.excludeLabels)) { result += "[#${item.number}](${item.html_url}): ${item.title}
" } } //list closed issues result += '
**List of closed issues since last release**
' for (def item : content) { if (!item.pull_request && !isExcluded(item, config.excludeLabels)) { result += "[#${item.number}](${item.html_url}): ${item.title}
" } } return result } String addDeltaToLastRelease(config, latestTag){ def result = '' //add delta link to previous release result += '
**Changes**
' result += "[${latestTag}...${config.version}](${config.githubServerUrl}/${config.githubOrg}/${config.githubRepo}/compare/${latestTag}...${config.version})
" return result } void postNewRelease(config, TOKEN, releaseBody){ Map messageBody = [ tag_name: "${config.version}", target_commitish: 'master', name: "${config.version}", body: releaseBody, draft: false, prerelease: false ] def data = new JsonUtils().groovyObjectToJsonString(messageBody) try { httpRequest httpMode: 'POST', requestBody: data, url: "${config.githubApiUrl}/repos/${config.githubOrg}/${config.githubRepo}/releases?access_token=${TOKEN}" } catch (e) { echo """[${STEP_NAME}] Error occured when writing release information --------------------------------------------------------------------- Request body was: --------------------------------------------------------------------- ${data} ---------------------------------------------------------------------""" throw e } } boolean isExcluded(item, excludeLabels){ def result = false excludeLabels.each {labelName -> item.labels.each { label -> if (label.name == labelName) { result = true } } } return result }