453 lines
14 KiB
Plaintext
453 lines
14 KiB
Plaintext
|
/*
|
||
|
* This is a generic jenkinsfile to build Asterisk and optionally
|
||
|
* perform one or more of the following:
|
||
|
* * Publish the API docs to the wiki
|
||
|
* * Run the Unit tests
|
||
|
* * Run Testsuite Tests
|
||
|
*
|
||
|
* This job can be triggered manually from Jenkins or be triggered
|
||
|
* automatically on a schedule based on a cron string.
|
||
|
*
|
||
|
* To use this jenkinsfile, create a new "Multi-Branch Pipeline" job
|
||
|
* in Jenkins. For easier configuration, the job name should contain
|
||
|
* only letters, numbers, or the "-", "_" and "." special characters.
|
||
|
* Use the "by Jenkinsfile" "Build Configuration" mode and specify
|
||
|
* the path to this jenkinsfile.
|
||
|
*
|
||
|
* When you save this job definition, Jenkins will scan the git
|
||
|
* repository and find any branches with this Jenkinsfile and then try
|
||
|
* run the job. It's expected that the jobs will fail because you
|
||
|
* haven't create the config file yet.
|
||
|
*
|
||
|
* The job is configured from a Jenkins managed config file named
|
||
|
* "jobConfig". These files are created using the "Config Files"
|
||
|
* option of the base job and are unique to a job so you can create
|
||
|
* multiple jobs based on this Jenkinsfile without conflicts.
|
||
|
*
|
||
|
* Create the file as a "Json file" remembering to change the ID
|
||
|
* from the auto-generated UUID to "jobConfig".
|
||
|
*
|
||
|
* Example contents:
|
||
|
* {
|
||
|
* cronString: 'H H(0-4) * * *',
|
||
|
* jobTimeout: {
|
||
|
* timeout: 2,
|
||
|
* units: 'HOURS',
|
||
|
* },
|
||
|
* jobCleanup: {
|
||
|
* keepBuilds: 5,
|
||
|
* artifactKeepBuilds: 2
|
||
|
* },
|
||
|
* throttleCategories: [
|
||
|
* 'default'
|
||
|
* ],
|
||
|
* docker: [
|
||
|
* images: [
|
||
|
* 'asterisk/jenkins-agent-centos7'
|
||
|
* ]
|
||
|
* ],
|
||
|
* buildAsterisk: [
|
||
|
* build: true,
|
||
|
* env: [
|
||
|
* REF_DEBUG: true
|
||
|
* ]
|
||
|
* ],
|
||
|
* unitTests: [
|
||
|
* run: true,
|
||
|
* testCommand: 'test execute all'
|
||
|
* ]
|
||
|
* }
|
||
|
*
|
||
|
* NOTE: The JSON file can actually reference variables from the
|
||
|
* environment using string interpolation. For example, if you
|
||
|
* need to substitute the current branch in a value for some reason,
|
||
|
* you could use:
|
||
|
* mybranch: "${BRANCH}"
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* All jobConfig parameters have defaults BUT if left that way,
|
||
|
* only an Asterisk build will be done.
|
||
|
*
|
||
|
* NOTE: Groovy syntax uses brackets "[]" for both arrays and
|
||
|
* maps/dictionaries where JSON uses brackets "[]" for arrays but
|
||
|
* braces "{}" for maps/dictionaries. Your jobConfig file is JSON
|
||
|
* but the defaults below are Groovy.
|
||
|
*/
|
||
|
def jobConfig = [
|
||
|
/* Must match a label assigned to agents. */
|
||
|
agentLabel: 'swdev-docker',
|
||
|
/*
|
||
|
* https://jenkins.io/doc/book/pipeline/syntax/#cron-syntax
|
||
|
* If empty, job will not be scheduled and must be triggered manually.
|
||
|
*/
|
||
|
cronString: '',
|
||
|
/*
|
||
|
* An array of strings that name categories defined in Jenkins
|
||
|
* Global Settings under "Throttle Concurrent Builds". If you
|
||
|
* specify one or more categories, they MUST have been defined
|
||
|
* or the job will fail.
|
||
|
*/
|
||
|
throttleCategories: [
|
||
|
],
|
||
|
jobTimeout: [
|
||
|
/* How long should the job be allowed to run? */
|
||
|
timeout: 120,
|
||
|
/* Common valid units are "MINUTES", "HOURS", "DAYS". */
|
||
|
units: 'MINUTES'
|
||
|
],
|
||
|
jobCleanup: [
|
||
|
/* The total number of past jobs to keep. */
|
||
|
keepBuilds: 14,
|
||
|
/* But only this number will have their artifacts saved. */
|
||
|
artifactKeepBuilds: 7,
|
||
|
/* Clean up the workspace on the agent when the job completes. */
|
||
|
cleanupWorkspace: true
|
||
|
],
|
||
|
docker: [
|
||
|
/* The host and port of our Docker image registry. */
|
||
|
registry: 'swdev-docker0:5000',
|
||
|
/*
|
||
|
* An array of images that can be used for this job.
|
||
|
* One will be chosen from the list at random.
|
||
|
*/
|
||
|
images: [
|
||
|
'asterisk/jenkins-agent-centos7'
|
||
|
],
|
||
|
],
|
||
|
buildAsterisk: [
|
||
|
/* Build Asterisk */
|
||
|
build: true,
|
||
|
/* Additional envuronment variables to pass to buildAsterisk.sh */
|
||
|
env: [
|
||
|
]
|
||
|
],
|
||
|
unitTests: [
|
||
|
/* Run the Asterisk Unit Tests. */
|
||
|
run: false,
|
||
|
/* The Asterisk CLI command to run the tests. */
|
||
|
testCommand: 'test execute all'
|
||
|
],
|
||
|
wikiDocs: [
|
||
|
/* Build and publish the wiki documentation? */
|
||
|
publish: false,
|
||
|
/* The URL to the "publish-docs" repository */
|
||
|
gitURL: "https://gerrit.asterisk.org/publish-docs",
|
||
|
/*
|
||
|
* Only for branches that match the regex.
|
||
|
* I.E. Only the base branches excluding master.
|
||
|
*/
|
||
|
branchRegex: '^([0-9]+)$'
|
||
|
],
|
||
|
testsuite: [
|
||
|
/* Run the Testsuite? */
|
||
|
run: false,
|
||
|
/* The URL to the "testsuite" repository */
|
||
|
gitURL: "https://gerrit.asterisk.org/testsuite",
|
||
|
/*
|
||
|
* The name of the testsuite config file.
|
||
|
* See the "Testsuite" stage below for more info.
|
||
|
*/
|
||
|
configFile: 'testsuiteConfig',
|
||
|
]
|
||
|
]
|
||
|
|
||
|
/*
|
||
|
* The easiest way to process the above defaults is to merge the
|
||
|
* values from the jobConfig file over the defaults map. Groovy
|
||
|
* provides a standard way to do this but it's not a deep operation
|
||
|
* so we provide our own deep merge function.
|
||
|
*/
|
||
|
Map merge(Map onto, Map... overrides) {
|
||
|
if (!overrides)
|
||
|
return onto
|
||
|
else if (overrides.length == 1) {
|
||
|
overrides[0]?.each { k, v ->
|
||
|
if (v instanceof Map && onto[k] instanceof Map)
|
||
|
merge((Map) onto[k], (Map) v)
|
||
|
else
|
||
|
onto[k] = v
|
||
|
}
|
||
|
return onto
|
||
|
}
|
||
|
return overrides.inject(onto, { acc, override -> merge(acc, override ?: [:]) })
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* The job setup steps such as reading the config file and merging the
|
||
|
* defaults can be done on the "built-in" node before we send the job off
|
||
|
* to an agent.
|
||
|
*/
|
||
|
node('built-in') {
|
||
|
def tempJobConfig
|
||
|
configFileProvider([configFile(fileId: 'jobConfig',
|
||
|
replaceTokens: true, variable: 'JOB_CONFIG_FILE')]) {
|
||
|
echo "Retrieved jobConfig file from ${env.JOB_CONFIG_FILE}"
|
||
|
tempJobConfig = readJSON file: env.JOB_CONFIG_FILE
|
||
|
}
|
||
|
script {
|
||
|
merge(jobConfig, tempJobConfig)
|
||
|
echo jobConfig.toString()
|
||
|
causeClasses = currentBuild.getBuildCauses()
|
||
|
causeClass = causeClasses[0]
|
||
|
echo "Build Cause: ${causeClass.toString()}"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pipeline {
|
||
|
triggers {
|
||
|
/* If jobConfig.cronString is empty (the default), the trigger will be ignored */
|
||
|
cron jobConfig.cronString
|
||
|
}
|
||
|
|
||
|
options {
|
||
|
throttle(jobConfig.throttleCategories)
|
||
|
timeout(time: jobConfig.jobTimeout.timeout, unit: jobConfig.jobTimeout.units)
|
||
|
buildDiscarder(
|
||
|
logRotator(numToKeepStr: "${jobConfig.jobCleanup.keepBuilds}",
|
||
|
artifactNumToKeepStr: "${jobConfig.jobCleanup.artifactKeepBuilds}"))
|
||
|
}
|
||
|
|
||
|
agent {
|
||
|
label jobConfig.agentLabel
|
||
|
}
|
||
|
|
||
|
stages {
|
||
|
stage ("Setup") {
|
||
|
when {
|
||
|
/*
|
||
|
* When you make changes to the base job or a new branch is discovered
|
||
|
* Jenkins tries to run it the job. We probably don't want this to happen
|
||
|
* so if "BranchIndexing" was teh cause, don't run any of the steps.
|
||
|
*/
|
||
|
not {
|
||
|
triggeredBy 'BranchIndexingCause'
|
||
|
}
|
||
|
}
|
||
|
|
||
|
steps { script {
|
||
|
createSummary(icon: "/plugin/workflow-job/images/48x48/pipelinejob.png", text: "Docker Host: ${NODE_NAME}")
|
||
|
sh "sudo chown -R jenkins:users ."
|
||
|
sh "printenv -0 | sort -z | tr '\\0' '\\n'"
|
||
|
sh "sudo tests/CI/setupJenkinsEnvironment.sh"
|
||
|
|
||
|
/* Find a docker image, setup parameters and pull image */
|
||
|
def r = currentBuild.startTimeInMillis % jobConfig.docker.images.size()
|
||
|
def ri = jobConfig.docker.images[(int)r]
|
||
|
echo "Docker Image: ${ri}"
|
||
|
def randomImage = jobConfig.docker.registry + "/" + ri
|
||
|
echo "Docker Path: ${randomImage}"
|
||
|
dockerOptions = "--privileged --ulimit core=0 --ulimit nofile=10240 " +
|
||
|
" --tmpfs /tmp:exec,size=1G -v /srv/jenkins:/srv/jenkins:rw -v /srv/cache:/srv/cache:rw " +
|
||
|
" --entrypoint=''"
|
||
|
buildTag = env.BUILD_TAG.replaceAll(/[^a-zA-Z0-9_.-]/, '-')
|
||
|
dockerImage = docker.image(randomImage)
|
||
|
dockerImage.pull()
|
||
|
}}
|
||
|
}
|
||
|
|
||
|
stage ("Build") {
|
||
|
when {
|
||
|
expression { jobConfig.buildAsterisk.build }
|
||
|
not {
|
||
|
triggeredBy 'BranchIndexingCause'
|
||
|
}
|
||
|
}
|
||
|
steps { script {
|
||
|
dockerImage.inside(dockerOptions + " --name ${buildTag}-build") {
|
||
|
echo 'Building..'
|
||
|
|
||
|
withEnv(jobConfig.buildAsterisk.env) {
|
||
|
sh "./tests/CI/buildAsterisk.sh --branch-name=${BRANCH_NAME} --output-dir=tests/CI/output/Build --cache-dir=/srv/cache"
|
||
|
}
|
||
|
|
||
|
archiveArtifacts allowEmptyArchive: true, defaultExcludes: false, fingerprint: false,
|
||
|
artifacts: "tests/CI/output/Build/*"
|
||
|
}
|
||
|
}}
|
||
|
}
|
||
|
|
||
|
stage ("WikiDocs") {
|
||
|
when {
|
||
|
expression { jobConfig.wikiDocs.publish }
|
||
|
not {
|
||
|
triggeredBy 'BranchIndexingCause'
|
||
|
}
|
||
|
}
|
||
|
steps { script {
|
||
|
dockerImage.inside(dockerOptions + " --name ${buildTag}-wikidocs") {
|
||
|
sh "sudo ./tests/CI/installAsterisk.sh --branch-name=${BRANCH_NAME} --user-group=jenkins:users"
|
||
|
|
||
|
checkout scm: [$class: 'GitSCM',
|
||
|
branches: [[name: "master"]],
|
||
|
extensions: [
|
||
|
[$class: 'RelativeTargetDirectory', relativeTargetDir: "tests/CI/output/publish-docs"],
|
||
|
[$class: 'CloneOption',
|
||
|
noTags: true,
|
||
|
honorRefspec: true,
|
||
|
shallow: false
|
||
|
],
|
||
|
],
|
||
|
userRemoteConfigs: [[url: jobConfig.wikiDocs.gitURL]]
|
||
|
]
|
||
|
sh "./tests/CI/publishAsteriskDocs.sh --user-group=jenkins:users --branch-name=${BRANCH_NAME} --wiki-doc-branch-regex=\"${jobConfig.wikiDocs.branchRegex}\""
|
||
|
}
|
||
|
}}
|
||
|
}
|
||
|
|
||
|
stage ("UnitTests") {
|
||
|
when {
|
||
|
expression { jobConfig.unitTests.run }
|
||
|
not {
|
||
|
triggeredBy 'BranchIndexingCause'
|
||
|
}
|
||
|
}
|
||
|
steps { script {
|
||
|
dockerImage.inside(dockerOptions + " --name ${buildTag}-unittests") {
|
||
|
def outputdir = "tests/CI/output/UnitTests"
|
||
|
def outputfile = "${outputdir}/unittests-results.xml"
|
||
|
|
||
|
sh "sudo ./tests/CI/installAsterisk.sh --uninstall-all --branch-name=${BRANCH_NAME} --user-group=jenkins:users"
|
||
|
sh "tests/CI/runUnittests.sh --user-group=jenkins:users --output-dir='${outputdir}' --output-xml='${outputfile}' --unittest-command='${jobConfig.unitTests.testCommand}'"
|
||
|
|
||
|
archiveArtifacts allowEmptyArchive: true, defaultExcludes: false, fingerprint: true,
|
||
|
artifacts: "${outputdir}/**"
|
||
|
junit testResults: outputfile,
|
||
|
healthScaleFactor: 1.0,
|
||
|
keepLongStdio: true
|
||
|
}
|
||
|
}}
|
||
|
}
|
||
|
|
||
|
/* Testsuite Tests
|
||
|
*
|
||
|
* When jobConfig.testsuite.run is true, load the JSON file specified by
|
||
|
* jobConfig.testsuite.configFile (default "testsuiteConfig") and spin off a
|
||
|
* separate docker container for each testGroup contained therein that also
|
||
|
* has its "enabled" property set to true.
|
||
|
*
|
||
|
* If a testGroup has a customTests child, the specified custom tests repo
|
||
|
* will be cloned into "<groupDir>/tests/custom" and can be referenced as
|
||
|
* any other testsuite test.
|
||
|
*
|
||
|
* Example testsuiteConfig file:
|
||
|
*
|
||
|
* {
|
||
|
* testGroups: [
|
||
|
* {
|
||
|
* name: "ari1-mwi",
|
||
|
* enabled: false,
|
||
|
* dir: "tests/CI/output/ari1",
|
||
|
* runTestsuiteOptions: "--test-timeout=180",
|
||
|
* testcmd: "--test-regex=tests/rest_api --test-regex=tests/channels/pjsip/.*mwi"
|
||
|
* },
|
||
|
* {
|
||
|
* name: "custom1",
|
||
|
* enabled: false,
|
||
|
* dir: "tests/CI/output/custom1",
|
||
|
* runTestsuiteOptions: "--test-timeout=180",
|
||
|
* testcmd: "--test-regex=tests/custom/tests/stress",
|
||
|
* customTests: {
|
||
|
* branch: "master",
|
||
|
* gitURL: "http://somehost/private-tests"
|
||
|
* }
|
||
|
* }
|
||
|
* ]
|
||
|
* }
|
||
|
*
|
||
|
*/
|
||
|
stage("Testsuite") {
|
||
|
when {
|
||
|
expression { jobConfig.testsuite.run }
|
||
|
}
|
||
|
steps { script {
|
||
|
testConfig = [
|
||
|
testGroups: [],
|
||
|
]
|
||
|
def tempTestConfig
|
||
|
configFileProvider([configFile(fileId: jobConfig.testsuite.configFile, variable: 'TESTSUITE_CONFIG_FILE')]) {
|
||
|
echo "Retrieved test config file from ${env.TESTSUITE_CONFIG_FILE}"
|
||
|
tempTestConfig = readJSON file: env.TESTSUITE_CONFIG_FILE
|
||
|
}
|
||
|
merge(testConfig, tempTestConfig)
|
||
|
|
||
|
tasks = [ : ]
|
||
|
|
||
|
testConfig.testGroups.each {
|
||
|
def testGroup = it
|
||
|
tasks[testGroup.name] = {
|
||
|
dockerImage.inside("${dockerOptions} --name ${buildTag}-${testGroup.name}") {
|
||
|
|
||
|
lock("${JOB_NAME}.${NODE_NAME}.installer") {
|
||
|
sh "sudo ./tests/CI/installAsterisk.sh --uninstall-all --branch-name=${BRANCH_NAME} --user-group=jenkins:users"
|
||
|
}
|
||
|
|
||
|
sh "sudo rm -rf ${testGroup.dir} || : "
|
||
|
|
||
|
checkout scm: [$class: 'GitSCM',
|
||
|
branches: [[name: "${BRANCH_NAME}"]],
|
||
|
extensions: [
|
||
|
[$class: 'RelativeTargetDirectory', relativeTargetDir: testGroup.dir],
|
||
|
[$class: 'CloneOption',
|
||
|
noTags: true,
|
||
|
depth: 100,
|
||
|
honorRefspec: true,
|
||
|
shallow: true
|
||
|
],
|
||
|
],
|
||
|
userRemoteConfigs: [[url: jobConfig.testsuite.gitURL]]
|
||
|
]
|
||
|
echo "Test Custom Config: ${testGroup.customTests.toString()}"
|
||
|
|
||
|
if (testGroup.customTests && testGroup.customTests?.branch && testGroup.customTests?.gitURL) {
|
||
|
checkout scm: [$class: 'GitSCM',
|
||
|
branches: [[name: testGroup.customTests.branch]],
|
||
|
extensions: [
|
||
|
[$class: 'RelativeTargetDirectory', relativeTargetDir: "${testGroup.dir}/tests/custom"],
|
||
|
[$class: 'CloneOption',
|
||
|
noTags: true,
|
||
|
depth: 100,
|
||
|
honorRefspec: true,
|
||
|
shallow: true
|
||
|
],
|
||
|
],
|
||
|
userRemoteConfigs: [[url: testGroup.customTests.gitURL]]
|
||
|
]
|
||
|
}
|
||
|
sh "sudo tests/CI/runTestsuite.sh ${testGroup.runTestsuiteOptions} --testsuite-dir='${testGroup.dir}' --testsuite-command='${testGroup.testcmd}'"
|
||
|
|
||
|
echo "Group result d: ${currentBuild.currentResult}"
|
||
|
|
||
|
archiveArtifacts allowEmptyArchive: true, defaultExcludes: false, fingerprint: true,
|
||
|
artifacts: "${testGroup.dir}/asterisk-test-suite-report.xml, ${testGroup.dir}/logs/**, ${testGroup.dir}/core*.txt"
|
||
|
|
||
|
junit testResults: "${testGroup.dir}/asterisk-test-suite-report.xml",
|
||
|
healthScaleFactor: 1.0,
|
||
|
keepLongStdio: true
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
parallel tasks
|
||
|
}}
|
||
|
}
|
||
|
}
|
||
|
post {
|
||
|
cleanup {
|
||
|
script {
|
||
|
if (jobConfig.jobCleanup.cleanupWorkspace) {
|
||
|
cleanWs deleteDirs: true, notFailBuild: false
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
success {
|
||
|
echo "Reporting ${currentBuild.currentResult} Passed"
|
||
|
}
|
||
|
failure {
|
||
|
echo "Reporting ${currentBuild.currentResult}: Failed: Fatal Error"
|
||
|
}
|
||
|
unstable {
|
||
|
echo "Reporting ${currentBuild.currentResult}: Failed: Tests Failed"
|
||
|
}
|
||
|
}
|
||
|
}
|