aboutsummaryrefslogtreecommitdiff
path: root/vars/utils.groovy
diff options
context:
space:
mode:
Diffstat (limited to 'vars/utils.groovy')
-rw-r--r--vars/utils.groovy153
1 files changed, 145 insertions, 8 deletions
diff --git a/vars/utils.groovy b/vars/utils.groovy
index 1dd384c..e6a29f2 100644
--- a/vars/utils.groovy
+++ b/vars/utils.groovy
@@ -1,21 +1,35 @@
// Jenkinsfile support utilities
import BuildConfig.BuildConfig
+import JobConfig
import groovy.io.FileType
import groovy.json.JsonOutput
import org.apache.commons.lang3.SerializationUtils
import org.apache.commons.io.FilenameUtils
+@Grab(group='org.kohsuke', module='github-api', version='1.93')
+import org.kohsuke.github.GitHub
+
+
+@NonCPS
+def github(reponame, username, password, subject, message) {
+ def github = GitHub.connectUsingPassword("${username}", "${password}")
+ def repo = github.getRepository(reponame)
+ def ibuilder = repo.createIssue(subject)
+ ibuilder.body(message)
+ ibuilder.create()
+}
// Clone the source repository and examine the most recent commit message.
// If a '[ci skip]' or '[skip ci]' directive is present, immediately
// terminate the job with a success code.
// If no skip directive is found, or skip_disable is true, stash all the
// source files for efficient retrieval by subsequent nodes.
-//def scm_checkout(skip_disable=false) {
def scm_checkout(args = ['skip_disable':false]) {
+ def JobConfig jc = new JobConfig()
skip_job = 0
node("on-master") {
stage("Setup") {
+
checkout(scm)
println("args['skip_disable'] = ${args['skip_disable']}")
if (args['skip_disable'] == false) {
@@ -107,6 +121,102 @@ def install_conda(version, install_dir) {
return true
}
+
+//
+// Testing summary notifier
+//
+def test_summary_notify(single_issue) {
+ // Unstash all test reports produced by all possible agents.
+ // Iterate over all unique files to compose the testing summary.
+ def confname = ''
+ def report_hdr = ''
+ def short_hdr = ''
+ def raw_totals = ''
+ def totals = [:]
+ def message = "Regression Testing (RT) Summary:\n\n"
+ def subject = ''
+ def send_notification = false
+ def stashcount = 0
+ println("Retrieving stashed test report files...")
+ while(true) {
+ try {
+ unstash "${stashcount}.name"
+ unstash "${stashcount}.report"
+ } catch(Exception) {
+ println("All test report stashes retrieved.")
+ break
+ }
+ confname = readFile "${stashcount}.name"
+ println("confname: ${confname}")
+
+ report_hdr = sh(script:"grep 'testsuite errors' *.xml",
+ returnStdout: true)
+ short_hdr = report_hdr.findAll(/(?<=testsuite ).*/)[0]
+ short_hdr = short_hdr.split('><testcase')[0]
+
+ raw_totals = short_hdr.split()
+ totals = [:]
+
+ for (total in raw_totals) {
+ expr = total.split('=')
+ expr[1] = expr[1].replace('"', '')
+ totals[expr[0]] = expr[1]
+ try {
+ totals[expr[0]] = expr[1].toInteger()
+ } catch(Exception NumberFormatException) {
+ continue
+ }
+ }
+
+ // Check for errors or failures
+ if (totals['errors'] != 0 || totals['failures'] != 0) {
+ send_notification = true
+ message = "${message}Configuration: ${confname}\n\n" +
+ "| Total tests | ${totals['tests']} |\n" +
+ "|----|----|\n" +
+ "| Errors | ${totals['errors']} |\n" +
+ "| Failures | ${totals['failures']} |\n" +
+ "| Skipped | ${totals['skips']} |\n\n"
+ }
+ stashcount++
+
+ } //end while(true) over stashes//
+
+ // If there were any test errors or failures, send the summary to github.
+ if (send_notification) {
+ // Match digits between '/' chars at end of BUILD_URL (build number).
+ def pattern = ~/\/\d+\/$/
+ def report_url = env.BUILD_URL.replaceAll(pattern, '/test_results_analyzer/')
+ message = "${message}Report: ${report_url}"
+ subject = "[AUTO] Regression testing summary"
+
+ def regpat = ~/https:\/\/github.com\//
+ def reponame = scm.userRemoteConfigs[0].url.replaceAll(regpat, '')
+ regpat = ~/\.git$/
+ reponame = reponame.replaceAll(regpat, '')
+
+ println("Test failures and/or errors occurred.\n" +
+ "Posting summary to Github.\n" +
+ " ${reponame} Issue subject: ${subject}")
+ if (single_issue) {
+ withCredentials([usernamePassword(credentialsId:'github_st-automaton-01',
+ usernameVariable: 'USERNAME',
+ passwordVariable: 'PASSWORD')]) {
+ // Locally bound vars here to keep Jenkins happy.
+ def username = USERNAME
+ def password = PASSWORD
+ github(reponame, username, password, subject, message)
+ }
+ } else {
+ println("Posting all RT summaries in separate issues is not yet implemented.")
+ // TODO: Determine if the reserved issue and/or comment text already exists.
+ // If so, post message as a comment on that issue.
+ // If not, post a new issue with message text.
+ }
+ } //endif (send_notification)
+}
+
+
// Execute build/test task(s) based on passed-in configuration(s).
// Each task is defined by a BuildConfig object.
// A list of such objects is iterated over to process all configurations.
@@ -115,17 +225,30 @@ def install_conda(version, install_dir) {
// (optional) concurrent - boolean, whether or not to run all build
// configurations in parallel. The default is
// true when no value is provided.
+//
+// Optionally accept a jobConfig object as part of the incoming list.
+// Test for type of list object and parse attributes accordingly.
def run(configs, concurrent = true) {
+ // Split off any JobConfig object, leaving the config objects.
+ def ljobconfig = null
+ def lconfigs = []
+ configs.eachWithIndex { config, index ->
+ if (config.getClass() == JobConfig) {
+ ljobconfig = config
+ configs.remove(configs.indexOf(config))
+ return // effectively a 'continue' from within a closure.
+ }
+ }
+
def tasks = [:]
configs.eachWithIndex { config, index ->
+
def BuildConfig myconfig = new BuildConfig() // MUST be inside eachWith loop.
myconfig = SerializationUtils.clone(config)
def config_name = ""
config_name = config.name
- println("config_name: ${config_name}")
-
// Test for GStrings (double quoted). These perform string interpolation
// immediately and may not what the user intends to do when defining
// environment variables to use in the build. Disallow them here.
@@ -238,11 +361,7 @@ def run(configs, concurrent = true) {
}
withEnv(runtime) {
stage("Build (${myconfig.name})") {
- println('About to unstash')
- sh "pwd"
- sh "ls -al"
unstash "source_tree"
- println('Unstash complete')
for (cmd in myconfig.build_cmds) {
sh(script: cmd)
}
@@ -329,16 +448,26 @@ def run(configs, concurrent = true) {
[$class: 'FailedThreshold', unstableThreshold: "${myconfig.failedUnstableThresh}"],
[$class: 'FailedThreshold', failureThreshold: "${myconfig.failedFailureThresh}"]],
tools: [[$class: 'JUnitType', pattern: '*.xml']]])
+
} else {
println("No .xml files found in workspace. Test report ingestion skipped.")
}
+ writeFile file: "${index}.name", text: config_name, encoding: "UTF-8"
+ def stashname = "${index}.name"
+ // TODO: Define results file name centrally and reference here.
+ if (fileExists('results.xml')) {
+ stash includes: '*.name', name: stashname, useDefaultExcludes: false
+ stashname = "${index}.report"
+ stash includes: '*.xml', name: stashname, useDefaultExcludes: false
+ }
+
} // end test test_cmd finally clause
} // end stage test_cmd
} // end withEnv
} // end node
} //end tasks
- } //end for
+ } //end closure configs.eachWithIndex
if (concurrent == true) {
stage("Matrix") {
@@ -356,6 +485,14 @@ def run(configs, concurrent = true) {
iter++
}
}
+
+ node("on-master") {
+ stage("Post-build") {
+ if (ljobconfig.post_rt_summary) {
+ test_summary_notify(ljobconfig.all_posts_in_same_issue)
+ }
+ } //end stage
+ } //end node
}