Produced by Araxis Merge on 10/12/2018 4:04:42 PM Eastern Daylight Time. See www.araxis.com for information about Merge. This report uses XHTML and CSS2, and is best viewed with a modern standards-compliant browser. For optimum results when printing this report, use landscape orientation and enable printing of background images and colours in your browser.
| # | Location | File | Last Modified |
|---|---|---|---|
| 1 | Vetlink 2.0.zip\Vetlink 2.0\vetlink-services.zip\vetlink-services | Jenkinsfile | Fri Aug 24 18:25:23 2018 UTC |
| 2 | Vetlink 2.0.zip\Vetlink 2.0\vetlink-services.zip\vetlink-services | Jenkinsfile | Fri Oct 12 13:16:41 2018 UTC |
| Description | Between Files 1 and 2 |
|
|---|---|---|
| Text Blocks | Lines | |
| Unchanged | 2 | 824 |
| Changed | 1 | 2 |
| Inserted | 0 | 0 |
| Removed | 0 | 0 |
| Whitespace | |
|---|---|
| Character case | Differences in character case are significant |
| Line endings | Differences in line endings (CR and LF characters) are ignored |
| CR/LF characters | Not shown in the comparison detail |
No regular expressions were active.
| 1 | properties ([ | |
| 2 | paramete rs([ | |
| 3 | boolea nParam(nam e: 'CLEAN' , defaultV alue: true , descript ion: 'clea ns workspa ce on star t up'), | |
| 4 | choice (name: 'FA IL_WAIT', choices: " 0\n1\n5\n1 0\n15\n20" , descript ion: 'On f ailure, am ount of ti me to wait before fa iling and terminatin g the job. This allo ws for tro ubleshooti ng in the job enviro nment.') | |
| 5 | ]) | |
| 6 | ]) | |
| 7 | ||
| 8 | /********* ********** ********** ******* | |
| 9 | * Applica tion-speci fic values | |
| 10 | ********* ********** ********** *******/ | |
| 11 | def appChe ckoutDir = "vetlink- services" | |
| 12 | def enviro nmentFileL ist = [] | |
| 13 | ||
| 14 | ||
| 15 | def APIGAT EWAY_ADDR = 'localho st:8089' | |
| 16 | def STASH_ URL = env. STASH_URL ?: "https: // DNS . URL " | |
| 17 | def buildU serID | |
| 18 | ||
| 19 | node('buil d-docker-l arge'){ | |
| 20 | de f repos = [:] | |
| 21 | de f executio nError | |
| 22 | ||
| 23 | /* ********* | |
| 24 | * These wil l be used throughout the build process a s the appl ication wo rkspace an d the stac k workspac e. | |
| 25 | * Both wil l be set t o the dire ctory into which the ir respect ive source code is p ulled/chec ked out | |
| 26 | * *********/ | |
| 27 | de f app_work space = ap pCheckoutD ir | |
| 28 | ||
| 29 | bu ildUserID = sh ( | |
| 30 | scri pt: "id -u ", | |
| 31 | retu rnStdout: true | |
| 32 | ). trim() | |
| 33 | ||
| 34 | st age('initi alization' ) { | |
| 35 | sh " docker sto p \$(docke r ps -a -q ) || true ; docker r m \$(docke r ps -a -q ) || true" | |
| 36 | noti fySlack() | |
| 37 | } | |
| 38 | ||
| 39 | st age('clean workspace ') { | |
| 40 | if ( params.CLE AN) { | |
| 41 | clea nWs notFai lBuild: tr ue | |
| 42 | } | |
| 43 | } | |
| 44 | ||
| 45 | st age('check out'){ | |
| 46 | //Ca n't obtain this dire ctory name from next genConfig. yml becaus e that fil e isn't ye t checked out | |
| 47 | dir( appCheckou tDir){ | |
| 48 | checko ut scm | |
| 49 | repos = readYaml file: 'ne xtgenConfi g.yml' | |
| 50 | } | |
| 51 | /*** ******* | |
| 52 | * I ntent is t o use exis ting artif acts in Je nkins to s peed the b uild proce ss; howeve r, | |
| 53 | * acceptance tests exi st within the indivi dual repos . Perform shallow ch eckouts of all | |
| 54 | * source rep os | |
| 55 | *** *******/ | |
| 56 | para llel repos .collectEn tries { ke y, value - > | |
| 57 | value. collectEnt ries { sub key, subva lue -> | |
| 58 | ["$subke y-checkout " : source Checkout(s ubvalue)] | |
| 59 | } | |
| 60 | } | |
| 61 | ||
| 62 | app_ workspace = pwd() + "/" + repo s["core"][ "applicati on"]["chec koutDir"] | |
| 63 | ||
| 64 | } | |
| 65 | st age('regis try login' ){ | |
| 66 | with Credential s([[$class : 'Usernam ePasswordM ultiBindin g', creden tialsId: ' innovation s_gitlab', passwordV ariable: ' DTR_PASS', usernameV ariable: ' DTR_USER'] ]){ | |
| 67 | sh "dock er login h ttps://mob ileapps.va ftl.us:925 0 -u \"${e nv.DTR_USE R}\" -p \" ${env.DTR_ PASS}\"" | |
| 68 | } | |
| 69 | sh " aws ecr ge t-login -- region us- east-1 --n o-include- email | ba sh" | |
| 70 | } | |
| 71 | st age('prep' ){ | |
| 72 | para llel 'perm issions':{ | |
| 73 | def ch mods = "" | |
| 74 | repos. each { key , value -> | |
| 75 | value.ea ch { subke y, subvalu e -> | |
| 76 | def dirPer m = dirPer missions(s ubvalue) | |
| 77 | chmods += dirPerm ? dirPerm + " && " : " " | |
| 78 | } | |
| 79 | } | |
| 80 | chmods = chmods. endsWith(" && ") ? ch mods[0..-4 ] : chmods | |
| 81 | sh chm ods | |
| 82 | } | |
| 83 | ||
| 84 | } | |
| 85 | ||
| 86 | tr y { | |
| 87 | /* E nvironment settings that will be used th roughout t he build/r un/test pr ocess */ | |
| 88 | with Env(["NEXT GEN_APP_HO ME=${app_w orkspace}" , "COMPOSE _HTTP_TIME OUT=480"]) { | |
| 89 | ||
| 90 | /* Wor karound - performing actions t hat use do cker-compo se before running ru n-all.sh * / | |
| 91 | stage( 'create do cker netwo rk'){ | |
| 92 | sh "dock er network inspect a pigateway- network || docker ne twork crea te apigate way-networ k" | |
| 93 | } | |
| 94 | ||
| 95 | /* The builder w ill be use d in subse quent step s, and thu s should b e pulled " up front" */ | |
| 96 | stage( 'pull buil der'){ | |
| 97 | dir(app_ workspace) { | |
| 98 | sh "./grad lew pullBu ildImages" | |
| 99 | } | |
| 100 | } | |
| 101 | ||
| 102 | stage( 'pull and build depe ndencies') { | |
| 103 | def buil dSteps = r epos["serv ices"].col lectEntrie s { subkey , subvalue -> | |
| 104 | ["$subkey- build" : a pplication Build(subk ey, subval ue, buildU serID, rep os["core"] ["applicat ion"]["che ckoutDir"] )] | |
| 105 | } | |
| 106 | ||
| 107 | /* Don't perform m ore than m axParallel build act ions simul taneously: | |
| 108 | * Trans form: [k1: v1,k2:v2,k 3:v3,k4:v4 ,k5:v5] | |
| 109 | * Into: [[k1:v1,k 2:v2,k3:v3 ],[k4:v4,k 5:v5]] | |
| 110 | */ | |
| 111 | def maxP arallel = 3 | |
| 112 | def buil dSubSteps = [] | |
| 113 | /* Initi alize buil dSubSteps as a colle ction of m aps */ | |
| 114 | (buildSt eps.size() /maxParall el).setSca le(0, BigD ecimal.ROU ND_CEILING ).times { buildSubSt eps[it] = [:] } | |
| 115 | /* Itera te over th e map, put ting the v alues into the build Substeps[f loor(idx/m axParallel )] */ | |
| 116 | buildSte ps.eachWit hIndex { k ey, val, i dx -> | |
| 117 | buildSubSt eps[((idx / maxParal lel).setSc ale(0, Big Decimal.RO UND_FLOOR) ).intValue ()][key] = val | |
| 118 | } | |
| 119 | parallel 'nextgen image pull ':{ | |
| 120 | dir(app_wo rkspace){ | |
| 121 | sh "./gradle w pullStac k" | |
| 122 | } | |
| 123 | }, 'dev image pull ':{ | |
| 124 | dir(app_wo rkspace){ | |
| 125 | sh "./gradle w pullDev" | |
| 126 | } | |
| 127 | }, 'buil d dependen cies':{ | |
| 128 | buildSubSt eps.each { subSteps -> | |
| 129 | pa rallel sub Steps | |
| 130 | } | |
| 131 | } | |
| 132 | } | |
| 133 | ||
| 134 | stage( 'build app lication') { | |
| 135 | script a pplication Build("app lication", repos["co re"]["appl ication"], buildUser ID, repos[ "core"]["a pplication "]["checko utDir"]) | |
| 136 | } | |
| 137 | ||
| 138 | stage( 'build dev images'){ | |
| 139 | def imag eBuildStep s = repos[ "services" ].collectE ntries { s ubkey, sub value -> | |
| 140 | ["$subkey- image-buil d" : appli cationImag eBuild(sub key, subva lue, build UserID, re pos["core" ]["applica tion"]["ch eckoutDir" ])] | |
| 141 | } | |
| 142 | imageBui ldSteps += ["applica tion-image -build" : applicatio nBuild("ap plication" , repos["c ore"]["app lication"] , buildUse rID, repos ["core"][" applicatio n"]["check outDir"])] | |
| 143 | ||
| 144 | def maxP arallel = 3 | |
| 145 | def imag eBuildSubS teps = [] | |
| 146 | /* Initi alize buil dSubSteps as a colle ction of m aps */ | |
| 147 | (imageBu ildSteps.s ize()/maxP arallel).s etScale(0, BigDecima l.ROUND_CE ILING).tim es { image BuildSubSt eps[it] = [:] } | |
| 148 | /* Itera te over th e map, put ting the v alues into the image BuildSubSt eps[floor( idx/maxPar allel)] */ | |
| 149 | imageBui ldSteps.ea chWithInde x { key, v al, idx -> | |
| 150 | imageBuild SubSteps[( (idx / max Parallel). setScale(0 , BigDecim al.ROUND_F LOOR)).int Value()][k ey] = val | |
| 151 | } | |
| 152 | imageBui ldSubSteps .each { su bSteps -> | |
| 153 | parallel s ubSteps | |
| 154 | } | |
| 155 | } | |
| 156 | ||
| 157 | stage( 'start nex tgen-stack '){ | |
| 158 | sh "dock er network prune -f" | |
| 159 | dir (app _workspace ) { | |
| 160 | sh "./grad lew runSta ck" | |
| 161 | } | |
| 162 | } | |
| 163 | ||
| 164 | stage( 'start app lication') { | |
| 165 | dir (app _workspace ) { | |
| 166 | sh "./grad lew runDev " | |
| 167 | } | |
| 168 | } | |
| 169 | ||
| 170 | stage( 'verify st ack up') { | |
| 171 | sh "dock er ps -a - -format 't able {{.Im age}}\t{{. Names}}\t{ {.Status}} ' ; docker images -- format 'ta ble {{.Rep ository}}\ t{{.Tag}}\ t{{.Digest }}'" | |
| 172 | ||
| 173 | def firs tRun = tru e | |
| 174 | ||
| 175 | retry(10 ) { | |
| 176 | if(!firstR un) { | |
| 177 | sl eep time: 1, unit: ' MINUTES' | |
| 178 | di r(repos["c ore"]["sta ck"]["chec koutDir"]) { | |
| 179 | sh " docker-com pose resta rt registr ator" | |
| 180 | } | |
| 181 | } | |
| 182 | firstRun = false | |
| 183 | ||
| 184 | verifyUrl( "http://${ APIGATEWAY _ADDR}/use rs/v1/sess ion", 200) | |
| 185 | } | |
| 186 | } | |
| 187 | stage( 'integrati on test'){ | |
| 188 | sh "dock er images --format ' table {{.R epository} }\t{{.Tag} }\t{{.Dige st}}'" | |
| 189 | script c omponentIn tegration( "applicati on", repos ["core"][" applicatio n"], build UserID) | |
| 190 | } | |
| 191 | } | |
| 192 | } | |
| 193 | ca tch(err) { | |
| 194 | curr entBuild.r esult = 'F AILURE' | |
| 195 | echo "EXECUTIO N ERROR: $ {err}" | |
| 196 | exec utionError = err | |
| 197 | slee p time: pa rams.FAIL_ WAIT.toInt eger(), un it: 'MINUT ES' | |
| 198 | ||
| 199 | } | |
| 200 | fi nally{ | |
| 201 | stag e('stop') { | |
| 202 | sh "do cker ps -a " | |
| 203 | sh "mk dir -p log s && for c ontainer i n \$(docke r ps -a -q ) ; do doc ker logs \ $container &> logs/\ $(docker p s -af \"ID =\$contain er\" --for mat \"{{.N ames}}\"). log ; done " | |
| 204 | sh "do cker stop \$(docker ps -a -q) && docker rm \$(dock er ps -a - q)" // Sto p all cont ainers | |
| 205 | } | |
| 206 | stag e('archive '){ | |
| 207 | archiv eArtifacts 'logs/*.l og,**/scre enshots/*. png' | |
| 208 | junit allowEmpty Results: t rue, testR esults: '* */intTest/ *.xml' | |
| 209 | } | |
| 210 | if ( executionE rror) { | |
| 211 | error "${executi onError}" | |
| 212 | } | |
| 213 | } | |
| 214 | } | |
| 215 | ||
| 216 | /********* ********** ********** ********** ********** ********** ********** ********** ********** *** | |
| 217 | * SUPPORT M ETHODS | |
| 218 | * | |
| 219 | * The fol lowing met hods are u sed by the pipeline to perform repetitiv e operatio ns or | |
| 220 | * return closures (anonymous code bloc ks) that r epresent c ommon buil d steps po pulated | |
| 221 | * with v alues from the nextg enConfig y aml | |
| 222 | ********* ********** ********** ********** ********** ********** ********** ********** ********** ***/ | |
| 223 | ||
| 224 | /********* * | |
| 225 | * Name: s ourceCheck out | |
| 226 | * Descrip tion: Retu rns a clos ure repres enting a J enkinsfile checkout step for a git repos itory; | |
| 227 | * allows variable s etting of: | |
| 228 | * - git bran ch (`branc h`) | |
| 229 | * - submodul es (`hasSu bmodules` - If `true `, disable Submodules will be s et to `fal se`) | |
| 230 | * - reposito ry project /repo (`re po`) | |
| 231 | * Paramet ers: repo: A <Map> r epresentin g a reposi tory const ruct | |
| 232 | * Return: A closure represent ing a Jenk insfile ch eckout ste p (or a de scriptive no-op) | |
| 233 | ********* */ | |
| 234 | def source Checkout(r epo){ | |
| 235 | /* If there is a repo for the en try, perfo rm checkou t */ | |
| 236 | if (repo.rep o){ | |
| 237 | retu rn { | |
| 238 | checko ut( | |
| 239 | changelo g: false, | |
| 240 | poll: fa lse, | |
| 241 | scm: [ | |
| 242 | $class: 'G itSCM', | |
| 243 | branches: [ | |
| 244 | [ name: "*/$ {repo.bran ch}" ] | |
| 245 | ], | |
| 246 | doGenerate SubmoduleC onfigurati ons: false , | |
| 247 | extensions : [ | |
| 248 | [ $class: 'S ubmoduleOp tion', dis ableSubmod ules: !rep o.hasSubmo dules, par entCredent ials: true , recursiv eSubmodule s: false, reference: '', track ingSubmodu les: false ], | |
| 249 | [ $class: 'C heckoutOpt ion', time out: 20 ], | |
| 250 | [ $class: 'C loneOption ', noTags: false, re ference: ' ', shallow : true ], | |
| 251 | [ $class: 'R elativeTar getDirecto ry', relat iveTargetD ir: repo.c heckoutDir ] | |
| 252 | ], submodu leCfg: [], | |
| 253 | userRemote Configs: [ | |
| 254 | [ credential sId: 'STAS H_USER', u rl: "${STA SH_URL}/sc m/${repo.r epo}.git" ] | |
| 255 | ] | |
| 256 | ] | |
| 257 | ) | |
| 258 | } | |
| 259 | } | |
| 260 | /* Otherwise , return a no-op */ | |
| 261 | el se{ | |
| 262 | retu rn { | |
| 263 | echo " No repo sp ecified" | |
| 264 | } | |
| 265 | } | |
| 266 | } | |
| 267 | ||
| 268 | /********* * | |
| 269 | * Name: d irPermissi ons | |
| 270 | * Descrip tion: Gene rate strin gs represe nting shel l chmod co mmands | |
| 271 | * Paramet ers: repo: A <Map> r epresentin g a reposi tory const ruct | |
| 272 | * Output: A string representi ng a shell chmod com mand | |
| 273 | ********* */ | |
| 274 | def dirPer missions(r epo){ | |
| 275 | re turn repo. dirPermiss ion ? "sud o chmod -R ${repo.di rPermissio n} ${repo. checkoutDi r}" : "" | |
| 276 | } | |
| 277 | ||
| 278 | /********* * | |
| 279 | * Name: v erifyUrl | |
| 280 | * Descrip tion: Uses `curl` to verify if a URL res ponse code is as exp ected | |
| 281 | * Paramet ers: url: A <String > URL to t est | |
| 282 | * expected Response: A <String> or <Integ er> HTTP r esponse co de | |
| 283 | * Output: None (exe cutes a Je nkinsfile build step that may succeed or fail) | |
| 284 | ********* */ | |
| 285 | def verify Url(url, e xpectedRes ponse) { | |
| 286 | sh "test \$( curl -sL - w '%{http_ code}' -o /dev/null '${url}') = '${expec tedRespons e}'" | |
| 287 | } | |
| 288 | ||
| 289 | /********* * | |
| 290 | * Name: c omponentIn tegration | |
| 291 | * Descrip tion: Retu rns a clos ure repres enting the integrati on test st ep(s) for a componen t (applica tion, appl et, etc) | |
| 292 | * Paramet ers: name : A <Strin g> name of the compo nent | |
| 293 | * componen t: A <Map> represent ing a comp onent cons truct | |
| 294 | * buildUse rID (optio nal): A <S tring> use r ID | |
| 295 | * baseDir (optional) : The dire ctory in w hich to in itiate the build, if not compo nent.check outDir | |
| 296 | * Output: A closure represent ing Jenkin sfile test step(s) | |
| 297 | ********* */ | |
| 298 | def compon entIntegra tion(name, component , buildUse rID, baseD ir=null){ | |
| 299 | /* Default n o-op build action */ | |
| 300 | de f testClos ure = { ec ho "No tes ts to run for ${name }" } | |
| 301 | /* If there is an arti fact speci fied, buil d step sho uld copy a nd explode that arti fact */ | |
| 302 | if (component .integrati on) { | |
| 303 | test Closure = { | |
| 304 | if(!bu ildUserID) { | |
| 305 | buildUse rID = sh ( | |
| 306 | script: "i d -u", | |
| 307 | returnStdo ut: true | |
| 308 | ).trim() | |
| 309 | } | |
| 310 | testDi r = (baseD ir ?: comp onent.chec koutDir ?: ".") + "/ " + compon ent.integr ation.test Dir ?: "." | |
| 311 | dir(te stDir){ | |
| 312 | sh "./gr adlew ${co mponent.in tegration. testTask ? : "intTest "} ${compo nent.integ ration.tes tFlags ?: ""}" | |
| 313 | } | |
| 314 | } | |
| 315 | } | |
| 316 | re turn testC losure | |
| 317 | } | |
| 318 | ||
| 319 | ||
| 320 | /********* * | |
| 321 | * Name: a pplication Build | |
| 322 | * Descrip tion: Retu rns a clos ure repres enting the build ste p(s) for a n applicat ion; | |
| 323 | * will be skipped i f the yaml entry set s skipCIBu ild=true | |
| 324 | * Paramet ers: name : A <Strin g> name of the conta iner | |
| 325 | * repo: A <Map> repr esenting a repositor y construc t | |
| 326 | * buildUse rID (optio nal): A <S tring> use r ID | |
| 327 | * baseDir: The direc tory in wh ich to ini tiate the build, if not repo.c heckoutDir | |
| 328 | * Output: A closure represent ing Jenkin sfile buil d step(s) | |
| 329 | ********* */ | |
| 330 | def applic ationBuild (name, rep o, buildUs erID, base Dir=null){ | |
| 331 | de f closure = { echo " Skipping b uild" } | |
| 332 | if (repo.skip CIBuild){ | |
| 333 | clos ure = { ec ho "Skippi ng build p er `skipCI Build` fla g" } | |
| 334 | } | |
| 335 | el se if(!rep o.buildCom mand){ | |
| 336 | clos ure = { ec ho "Skippi ng build; no buildCo mmand defi ned"} | |
| 337 | } | |
| 338 | el se { | |
| 339 | clos ure = { | |
| 340 | if(!bu ildUserID) { | |
| 341 | buildUse rID = sh ( | |
| 342 | script: "i d -u", | |
| 343 | returnStdo ut: true | |
| 344 | ).trim() | |
| 345 | } | |
| 346 | buildD ir = baseD ir ?: repo .checkoutD ir ?: "." | |
| 347 | dir(bu ildDir){ | |
| 348 | sh "COMP OSE_HTTP_T IMEOUT=300 docker-co mpose -f n extgen/doc ker-compos e.build.ym l run --na me ${name} -build --r m -u ${bui ldUserID} builder /b in/bash -c \"cd ${bu ildDir} && ./gradlew --no-daem on --proje ct-cache-d ir=/tmp/pr oject_cach e ${name}B uild -Pski pCheckout= true --sta cktrace\"" | |
| 349 | } | |
| 350 | } | |
| 351 | } | |
| 352 | re turn closu re | |
| 353 | } | |
| 354 | ||
| 355 | /********* * | |
| 356 | * Name: a pplication ImageBuild | |
| 357 | * Descrip tion: Retu rns a clos ure repres enting the docker im age build step(s) fo r an appli cation; | |
| 358 | * will be skipped i f the yaml entry set s skipCIBu ild=true | |
| 359 | * Paramet ers: name : A <Strin g> name of the conta iner | |
| 360 | * repo: A <Map> repr esenting a repositor y construc t | |
| 361 | * buildUse rID (optio nal): A <S tring> use r ID | |
| 362 | * baseDir: The direc tory in wh ich to ini tiate the build, if not repo.c heckoutDir | |
| 363 | * Output: A closure represent ing Jenkin sfile buil d step(s) | |
| 364 | ********* */ | |
| 365 | def applic ationImage Build(name , repo, bu ildUserID, baseDir=n ull){ | |
| 366 | de f closure = { echo " Skipping b uild" } | |
| 367 | if (repo.skip CIBuild){ | |
| 368 | clos ure = { ec ho "Skippi ng image b uild per ` skipCIBuil d` flag" } | |
| 369 | } | |
| 370 | el se if(!rep o.buildCom mand){ | |
| 371 | clos ure = { ec ho "Skippi ng image b uild; no b uildComman d defined" } | |
| 372 | } | |
| 373 | el se { | |
| 374 | clos ure ={ | |
| 375 | if(!bu ildUserID) { | |
| 376 | buildUse rID = sh ( | |
| 377 | script: "i d -u", | |
| 378 | returnStdo ut: true | |
| 379 | ).trim() | |
| 380 | } | |
| 381 | buildD ir = baseD ir ?: repo .checkoutD ir ?: "." | |
| 382 | dir(bu ildDir){ | |
| 383 | sh "./gr adlew ${na me}ImageBu ild --stac ktrace\"" | |
| 384 | } | |
| 385 | } | |
| 386 | } | |
| 387 | re turn closu re | |
| 388 | } | |
| 389 | ||
| 390 | ||
| 391 | def notify Slack(Stri ng buildSt atus = 'ST ARTED') { | |
| 392 | // Bui ld status of null me ans succes s. | |
| 393 | buildS tatus = bu ildStatus ?: 'SUCCES S' | |
| 394 | ||
| 395 | def co lor | |
| 396 | ||
| 397 | if (bu ildStatus == 'STARTE D') { | |
| 398 | co lor = '#D4 DADF' | |
| 399 | } else if (build Status == 'SUCCESS') { | |
| 400 | co lor = '#BD FFC3' | |
| 401 | } else if (build Status == 'UNSTABLE' ) { | |
| 402 | co lor = '#FF FE89' | |
| 403 | } else { | |
| 404 | co lor = '#FF 9FA1' | |
| 405 | } | |
| 406 | ||
| 407 | def ms g = "${bui ldStatus}: `${env.JO B_NAME}` # ${env.BUIL D_NUMBER}: \n${env.BU ILD_URL}" | |
| 408 | ||
| 409 | slackS end(color: color, me ssage: msg ) | |
| 410 | } | |
| 411 | ||
| 412 | ||
| 413 |
Araxis Merge (but not the data content of this report) is Copyright © 1993-2016 Araxis Ltd (www.araxis.com). All rights reserved.