You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

269 lines
8.5 KiB
Groovy

#!groovy
/**
* Checkouts and builds notes for released components.
*
* Manuele Simi (ISTI-CNR)
*/
def agent_root_folder = '/var/lib/jenkins/.m2'
// load the build report's content
def text
if (params.report) {
println "Using local report"
text = params.report
} else {
String reportURL = "https://code-repo.d4science.org/gCubeCI/gCubeReleases/raw/branch/master/closed/${gCube_release_version}/build_commits.${report_number}.csv"
println "Pulling the report from Git at ${reportURL}"
//load the report from the URL
text = reportURL.toURL().getText()
}
// parse the report and extract the data
def components = parseBuildCommits(text)
assert 0 < components.size(): "No component found in build_commits.${report_number}.csv"
for (component in components) {
println "$component"
}
pipeline {
agent {
label 'CD'
}
environment {
AGENT_ROOT_FOLDER = "${agent_root_folder}"
GCUBE_RELEASE_NUMBER = "${params.gCube_release_version}"
PIPELINE_BUILD_NUMBER = "${env.BUILD_NUMBER}"
REPORT_NUMBER = "${params.report_number}"
RELEASE_NOTES = "${agent_root_folder}/release_notes.${report_number}.md"
}
parameters {
string(name: 'gCube_release_version',
defaultValue: 'x.y.z',
description: 'The number of the gCube release. Sample values: 4.14, 4.15, etc.')
string(name: 'report_number',
defaultValue: '',
description: 'The build report number on Git to pull the notes.')
string(name: 'report',
defaultValue: '',
description: 'The build report. Leave blank to pull the build report from Git.')
booleanParam(name: 'use_report_commits',
defaultValue: true,
description: 'Use the commits on the report or the latest is on master?')
}
stages {
stage('initialize reports') {
steps {
sh '''
date=`date`
echo "# Release Notes for gCube ${gCube_release_version}" > $RELEASE_NOTES
echo "" >> $RELEASE_NOTES
echo "---" >> $RELEASE_NOTES
'''
}
}
stage('notes') {
steps {
script {
for (int i = 0; i < components.size(); i++) {
stage(components[i]['name']) {
appendHeading(components[i]['name'], components[i]['version'],components[i]['gitRepo'], components[i]['commitID'])
createNotes(components[i]['name'], components[i]['gitRepo'],
params.use_report_commits? components[i]['commitID']:null, components[i]['version'], gCube_release_version)
}
}
appendFooter()
}
}
}
}
// post-build actions
post {
always {
script {
sh '''
cp $RELEASE_NOTES ./release_notes.${PIPELINE_BUILD_NUMBER}.md
cat ./release_notes.${PIPELINE_BUILD_NUMBER}.md
'''
}
}
success {
echo 'The release notes pipeline worked!'
emailext to: 'jenkinsbuilds@d4science.org',
subject: "[Jenkins build D4S] build ${currentBuild.fullDisplayName} worked",
body: "Build time: ${currentBuild.durationString}. See ${env.BUILD_URL}"
emailext attachmentsPattern: "**/release_notes.${PIPELINE_BUILD_NUMBER}.md",
to: 'jenkinsreleases@d4science.org',
subject: "Release notes for gCube ${GCUBE_RELEASE_NUMBER} (build #${PIPELINE_BUILD_NUMBER})",
body: "Release notes extracted from the CHANGELOG.md(s) of the released components. See ${env.BUILD_URL}"
}
failure {
echo 'The release notes pipeline has failed'
emailext attachLog: true,
to: 'jenkinsbuilds@d4science.org',
subject: "[Jenkins build D4S] build ${currentBuild.fullDisplayName} failed",
body: "Something is wrong with ${env.BUILD_URL}"
}
}
}
/**
* Clones the repository, tags and creates notes
* NOTE: 'credentialsId' be manually configured in Jenkins to access all the repos
*/
def createNotes(repo_name, repo_url, commit, version, gCube_release_version) {
echo "Checkout SHA from reference $commit"
sh(script: "rm -r ${repo_name} || true", returnStdout: true)?.trim()
checkout([
$class : 'GitSCM',
branches : [[name: commit? commit : '*/master']],
doGenerateSubmoduleConfigurations: false,
extensions : [
[$class: 'RelativeTargetDirectory', relativeTargetDir: repo_name],
[$class: 'CloneOption', noTags: false, reference: '']
],
submoduleCfg : [],
userRemoteConfigs : [
[credentialsId: '88b54962-1c0e-49cb-8155-22276860f346', url: repo_url] //git.gcube credentials on jenkins
]
])
def log_content = get_changelog(repo_name,repo_url)
appendNotes(extract(repo_url,log_content, version, gCube_release_version,repo_name))
}
String get_last_commit(repo_name) {
String msg;
dir(repo_name) {
msg = sh(script: 'git rev-parse HEAD', returnStdout: true)?.trim()
}
return msg;
}
/**
* Reads the CHANGELOG.md for the given repository.
*
* @param repo_name
* @return the full content of CHANGELOG.md
*/
String get_changelog(repo_name,repo_url) {
String text;
dir(repo_name) {
text = sh(script: 'cat CHANGELOG.md || echo Missing CHANGELOG.md', returnStdout: true)?.trim()
}
return text;
}
/**
* Extracts the notes from the changelog's content.
*
* @param log_content
* @param gCube_release_version
* @param tag
* @params component
* @return the notes for then current release
*/
String extract(repo_url, log_content, tag, gCube_release_version, component) {
println "Changelong content: ${log_content}"
if (log_content.startsWith('Missing CHANGELOG.md'))
return log_content + " at ${repo_url}"
def section = ''
def matcher = ("${log_content}" =~ /(?is)\[v$tag\](.*?)##\s+\[v.*?\]/)
if (!matcher.find()) {
def eof_matcher = ("${log_content}" =~ /(?is)\[v$tag\](.*$?)/)
if (!eof_matcher.find()) {
return "Tags ${tag} / ${gCube_release_version} not found in CHANGELOG.md at ${repo_url}"
} else {
assert eof_matcher[0][1]: "Missing release notes for ${component}"
section = eof_matcher[0][1]
}
} else {
assert matcher[0][1]: "Missing release notes for ${component}"
section = matcher[0][1]
}
return section.replace("[r$gCube_release_version]", '')
}
/**
* Appends the header to the release notes file.
*/
def appendHeader() {
sh("""
echo "---" >> $RELEASE_NOTES
""")
}
/**
* Appends the footer to the release notes file.
*/
def appendFooter() {
def now = new Date()
sh("""
echo "" >> $RELEASE_NOTES
echo "---" >> $RELEASE_NOTES
echo "*generated by the gCube-ReleaseNotes pipeline from report ${REPORT_NUMBER}*" >> $RELEASE_NOTES
echo "" >> $RELEASE_NOTES
echo "*last update $now*" >> $RELEASE_NOTES
""")
}
/**
* Appends the new notes to the release notes file.
*
* @param repo_notes the notes for the repository to append
*/
def appendNotes(repo_notes) {
sh("""
echo "${repo_notes}" >> $RELEASE_NOTES
""")
}
/**
* Appends heading to the release notes file.
*
* @param name the name of the repository
* @param version the version of the component
*/
def appendHeading(name, version, gitRepo, commitID) {
sh("""
echo "### ${name} ${version}" >> $RELEASE_NOTES
echo "Tagged commit: _[${commitID}](${gitRepo}/commit/${commitID})_" >> $RELEASE_NOTES
echo "" >> $RELEASE_NOTES
""")
}
//a non CPS method is necessary for the usage of splitEachLine()
@NonCPS
def parseBuildCommits(def text) {
def components = []
"${text}".splitEachLine(',') { columns ->
if (columns[0].startsWith('#') || columns[0].startsWith('GroupID'))
return
components.add([
name : columns[1],
version : columns[2],
gitRepo : columns[3],
commitID: columns[4]
]
)
}
return components
}