Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 1 | package com.mirantis.mcp |
| 2 | |
Sergey Kulanov | 91d8def | 2016-11-15 13:53:17 +0200 | [diff] [blame] | 3 | import org.jfrog.hudson.pipeline.types.ArtifactoryServer |
| 4 | import org.jfrog.hudson.pipeline.types.buildInfo.BuildInfo |
| 5 | |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 6 | /** |
| 7 | * Return string of mandatory build properties for binaries |
| 8 | * User can also add some custom properties. |
| 9 | * |
| 10 | * @param customProperties a Array of Strings that should be added to mandatory props |
| 11 | * in format ["prop1=value1", "prop2=value2"] |
| 12 | * */ |
| 13 | def getBinaryBuildProperties(ArrayList customProperties) { |
| 14 | def namespace = "com.mirantis." |
| 15 | def properties = [ |
Sergey Kulanov | c70f1c2 | 2016-11-16 13:05:20 +0200 | [diff] [blame] | 16 | "buildName=${env.JOB_NAME}", |
| 17 | "buildNumber=${env.BUILD_NUMBER}", |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 18 | "gerritProject=${env.GERRIT_PROJECT}", |
| 19 | "gerritChangeNumber=${env.GERRIT_CHANGE_NUMBER}", |
| 20 | "gerritPatchsetNumber=${env.GERRIT_PATCHSET_NUMBER}", |
| 21 | "gerritChangeId=${env.GERRIT_CHANGE_ID}", |
| 22 | "gerritPatchsetRevision=${env.GERRIT_PATCHSET_REVISION}" |
| 23 | ] |
| 24 | |
| 25 | if (customProperties) { |
| 26 | properties.addAll(customProperties) |
| 27 | } |
| 28 | |
| 29 | def common = new com.mirantis.mcp.Common() |
| 30 | |
| 31 | return common.constructString(properties, namespace, ";") |
| 32 | } |
| 33 | |
| 34 | /** |
Kirill Mashchenko | 1d225c2 | 2018-06-19 13:52:17 +0300 | [diff] [blame] | 35 | * Get URL to artifact(s) by properties |
| 36 | * Returns String(s) with URL to found artifact or null if nothing |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 37 | * |
| 38 | * @param artifactoryURL String, an URL to Artifactory |
| 39 | * @param properties LinkedHashMap, a Hash of properties (key-value) which |
| 40 | * which should determine artifact in Artifactory |
Kirill Mashchenko | 1d225c2 | 2018-06-19 13:52:17 +0300 | [diff] [blame] | 41 | * @param onlyLastItem Boolean, return only last URL if true(by default), |
| 42 | * else return list of all found artifact URLS |
| 43 | * |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 44 | */ |
Kirill Mashchenko | 1d225c2 | 2018-06-19 13:52:17 +0300 | [diff] [blame] | 45 | def uriByProperties(String artifactoryURL, LinkedHashMap properties, Boolean onlyLastItem=true) { |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 46 | def key, value |
| 47 | def properties_str = '' |
| 48 | for (int i = 0; i < properties.size(); i++) { |
| 49 | // avoid serialization errors |
Kirill Mashchenko | 56c8ff3 | 2018-06-28 03:01:34 +0300 | [diff] [blame] | 50 | key = properties.entrySet().toArray()[i].key.trim() |
| 51 | value = properties.entrySet().toArray()[i].value.trim() |
| 52 | properties_str += /${key}=${value}&/ |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 53 | } |
| 54 | def search_url = "${artifactoryURL}/api/search/prop?${properties_str}" |
| 55 | |
Kirill Mashchenko | 56c8ff3 | 2018-06-28 03:01:34 +0300 | [diff] [blame] | 56 | def result = sh(script: /curl -X GET '${search_url}'/, |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 57 | returnStdout: true).trim() |
| 58 | def content = new groovy.json.JsonSlurperClassic().parseText(result) |
| 59 | def uri = content.get("results") |
| 60 | if (uri) { |
Kirill Mashchenko | 1d225c2 | 2018-06-19 13:52:17 +0300 | [diff] [blame] | 61 | if (onlyLastItem) { |
| 62 | return uri.last().get("uri") |
| 63 | } else { |
| 64 | res = [] |
| 65 | uri.each {it -> |
| 66 | res.add(it.get("uri")) |
| 67 | } |
| 68 | return res |
| 69 | } |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 70 | } else { |
| 71 | return null |
| 72 | } |
| 73 | } |
| 74 | |
Kirill Mashchenko | 1d225c2 | 2018-06-19 13:52:17 +0300 | [diff] [blame] | 75 | |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 76 | /** |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 77 | * Set properties for artifact in Artifactory repo |
| 78 | * |
| 79 | * @param artifactUrl String, an URL to artifact in Artifactory repo |
| 80 | * @param properties LinkedHashMap, a Hash of properties (key-value) which |
| 81 | * should be assigned for choosen artifact |
| 82 | * @param recursive Boolean, if artifact_url is a directory, whether to set |
| 83 | * properties recursively or not |
| 84 | */ |
| 85 | def setProperties(String artifactUrl, LinkedHashMap properties, Boolean recursive = false) { |
| 86 | def properties_str = 'properties=' |
| 87 | def key, value |
| 88 | if (recursive) { |
| 89 | recursive = 'recursive=1' |
| 90 | } else { |
| 91 | recursive = 'recursive=0' |
| 92 | } |
Alexander Evseev | bd40ef9 | 2017-10-18 12:24:45 +0300 | [diff] [blame] | 93 | properties_str += properties.collect({"${it.key}=${it.value}"}).join(';') |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 94 | def url = "${artifactUrl}?${properties_str}&${recursive}" |
| 95 | withCredentials([ |
| 96 | [$class : 'UsernamePasswordMultiBinding', |
| 97 | credentialsId : 'artifactory', |
| 98 | passwordVariable: 'ARTIFACTORY_PASSWORD', |
| 99 | usernameVariable: 'ARTIFACTORY_LOGIN'] |
| 100 | ]) { |
| 101 | sh "bash -c \"curl -X PUT -u ${ARTIFACTORY_LOGIN}:${ARTIFACTORY_PASSWORD} \'${url}\'\"" |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | /** |
| 106 | * Get properties for specified artifact in Artifactory |
| 107 | * Returns LinkedHashMap of properties |
| 108 | * |
| 109 | * @param artifactUrl String, an URL to artifact in Artifactory repo |
| 110 | */ |
| 111 | def getPropertiesForArtifact(String artifactUrl) { |
| 112 | def url = "${artifactUrl}?properties" |
| 113 | def result |
| 114 | withCredentials([ |
| 115 | [$class : 'UsernamePasswordMultiBinding', |
| 116 | credentialsId : 'artifactory', |
| 117 | passwordVariable: 'ARTIFACTORY_PASSWORD', |
| 118 | usernameVariable: 'ARTIFACTORY_LOGIN'] |
| 119 | ]) { |
| 120 | result = sh(script: "bash -c \"curl -X GET -u ${ARTIFACTORY_LOGIN}:${ARTIFACTORY_PASSWORD} \'${url}\'\"", |
| 121 | returnStdout: true).trim() |
| 122 | } |
| 123 | def properties = new groovy.json.JsonSlurperClassic().parseText(result) |
| 124 | return properties.get("properties") |
| 125 | } |
| 126 | |
| 127 | /** |
Denis Egorenko | edd21dc | 2018-11-23 17:38:17 +0400 | [diff] [blame] | 128 | * Check if image with tag exist by provided path |
| 129 | * Returns true or false |
| 130 | * |
| 131 | * @param artifactoryURL String, an URL to Artifactory |
| 132 | * @param imageRepo String, path to image to check, includes repo path and image name |
| 133 | * @param tag String, tag to check |
| 134 | * @param artifactoryCreds String, artifactory creds to use. Optional, default is 'artifactory' |
| 135 | */ |
| 136 | def imageExists(String artifactoryURL, String imageRepo, String tag, String artifactoryCreds = 'artifactory') { |
| 137 | def url = artifactoryURL + '/v2/' + imageRepo + '/manifest/' + tag |
| 138 | def result |
| 139 | withCredentials([ |
| 140 | [$class : 'UsernamePasswordMultiBinding', |
| 141 | credentialsId : artifactoryCreds, |
| 142 | passwordVariable: 'ARTIFACTORY_PASSWORD', |
| 143 | usernameVariable: 'ARTIFACTORY_LOGIN'] |
| 144 | ]) { |
| 145 | result = sh(script: "bash -c \"curl -X GET -u ${ARTIFACTORY_LOGIN}:${ARTIFACTORY_PASSWORD} \'${url}\'\"", |
| 146 | returnStdout: true).trim() |
| 147 | } |
| 148 | def properties = new groovy.json.JsonSlurperClassic().parseText(result) |
| 149 | return properties.get("errors") ? false : true |
| 150 | } |
| 151 | |
| 152 | /** |
Denis Egorenko | 7c0abfe | 2017-02-14 15:42:02 +0400 | [diff] [blame] | 153 | * Find docker images by tag |
| 154 | * Returns Array of image' hashes with names as full path in @repo |
| 155 | * |
| 156 | * Example: |
| 157 | * |
| 158 | * [ { |
| 159 | * "path" : "mirantis/ccp/ci-cd/gerrit-manage/test" |
| 160 | * }, |
| 161 | * { |
| 162 | * "path" : "mirantis/ccp/ci-cd/gerrit/test" |
| 163 | * } |
| 164 | * ] |
| 165 | * |
| 166 | * @param artifactoryURL String, an URL to Artifactory |
| 167 | * @param repo String, a name of repo where should be executed search |
| 168 | * @param tag String, tag of searched image |
| 169 | */ |
| 170 | def getImagesByTag(String artifactoryURL, String repo, String tag) { |
| 171 | def url = "${artifactoryURL}/api/search/aql" |
| 172 | def result |
| 173 | writeFile file: "query", |
| 174 | text: """\ |
| 175 | items.find( |
| 176 | { |
| 177 | \"repo\": \"${repo}\", |
| 178 | \"@docker.manifest\": { \"\$match\" : \"${tag}*\" } |
| 179 | } |
| 180 | ). |
| 181 | include(\"path\") |
| 182 | """.stripIndent() |
| 183 | withCredentials([ |
| 184 | [$class: 'UsernamePasswordMultiBinding', |
| 185 | credentialsId: 'artifactory', |
| 186 | passwordVariable: 'ARTIFACTORY_PASSWORD', |
| 187 | usernameVariable: 'ARTIFACTORY_LOGIN'] |
| 188 | ]) { |
| 189 | result = sh(script: "bash -c \"curl -X POST -u ${ARTIFACTORY_LOGIN}:${ARTIFACTORY_PASSWORD} -d @query \'${url}\'\"", |
| 190 | returnStdout: true).trim() |
| 191 | } |
| 192 | def images = new groovy.json.JsonSlurperClassic().parseText(result) |
| 193 | return images.get("results") |
| 194 | } |
| 195 | |
| 196 | /** |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 197 | * Upload docker image to Artifactory |
| 198 | * |
Sergey Kulanov | 8cd6d22 | 2016-11-17 13:42:47 +0200 | [diff] [blame] | 199 | * @param server ArtifactoryServer, the instance of Artifactory server |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 200 | * @param registry String, the name of Docker registry |
| 201 | * @param image String, Docker image name |
| 202 | * @param version String, Docker image version |
| 203 | * @param repository String, The name of Artifactory Docker repository |
Sergey Kulanov | 8cd6d22 | 2016-11-17 13:42:47 +0200 | [diff] [blame] | 204 | * @param buildInfo BuildInfo, the instance of a build-info object which can be published, |
| 205 | * if defined, then we publish BuildInfo |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 206 | */ |
Sergey Kulanov | 8cd6d22 | 2016-11-17 13:42:47 +0200 | [diff] [blame] | 207 | def uploadImageToArtifactory (ArtifactoryServer server, String registry, String image, |
| 208 | String version, String repository, |
Dmitry Burmistrov | 6ee3952 | 2017-05-22 12:46:25 +0400 | [diff] [blame] | 209 | BuildInfo buildInfo = null, |
| 210 | LinkedHashMap properties = null) { |
Denis Egorenko | edba5a5 | 2016-11-15 19:55:56 +0300 | [diff] [blame] | 211 | // TODO Switch to Artifactoy image' pushing mechanism once we will |
| 212 | // prepare automatical way for enabling artifactory build-proxy |
| 213 | //def artDocker |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 214 | withCredentials([ |
| 215 | [$class: 'UsernamePasswordMultiBinding', |
| 216 | credentialsId: 'artifactory', |
| 217 | passwordVariable: 'ARTIFACTORY_PASSWORD', |
| 218 | usernameVariable: 'ARTIFACTORY_LOGIN'] |
| 219 | ]) { |
| 220 | sh ("docker login -u ${ARTIFACTORY_LOGIN} -p ${ARTIFACTORY_PASSWORD} ${registry}") |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 221 | //artDocker = Artifactory.docker("${env.ARTIFACTORY_LOGIN}", "${env.ARTIFACTORY_PASSWORD}") |
| 222 | } |
| 223 | |
Denis Egorenko | edba5a5 | 2016-11-15 19:55:56 +0300 | [diff] [blame] | 224 | sh ("docker push ${registry}/${image}:${version}") |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 225 | //artDocker.push("${registry}/${image}:${version}", "${repository}") |
Sergey Kulanov | 8cd6d22 | 2016-11-17 13:42:47 +0200 | [diff] [blame] | 226 | def image_url = server.getUrl() + "/api/storage/${repository}/${image}/${version}" |
Dmitry Burmistrov | 6ee3952 | 2017-05-22 12:46:25 +0400 | [diff] [blame] | 227 | if ( ! properties ) { |
| 228 | properties = [ |
Sergey Kulanov | c70f1c2 | 2016-11-16 13:05:20 +0200 | [diff] [blame] | 229 | 'com.mirantis.buildName':"${env.JOB_NAME}", |
| 230 | 'com.mirantis.buildNumber': "${env.BUILD_NUMBER}", |
| 231 | 'com.mirantis.gerritProject': "${env.GERRIT_PROJECT}", |
| 232 | 'com.mirantis.gerritChangeNumber': "${env.GERRIT_CHANGE_NUMBER}", |
| 233 | 'com.mirantis.gerritPatchsetNumber': "${env.GERRIT_PATCHSET_NUMBER}", |
| 234 | 'com.mirantis.gerritChangeId': "${env.GERRIT_CHANGE_ID}", |
| 235 | 'com.mirantis.gerritPatchsetRevision': "${env.GERRIT_PATCHSET_REVISION}", |
Sergey Kulanov | 4d3951c | 2016-11-24 13:58:15 +0200 | [diff] [blame] | 236 | 'com.mirantis.targetImg': "${image}", |
Sergey Kulanov | c70f1c2 | 2016-11-16 13:05:20 +0200 | [diff] [blame] | 237 | 'com.mirantis.targetTag': "${version}" |
Dmitry Burmistrov | 6ee3952 | 2017-05-22 12:46:25 +0400 | [diff] [blame] | 238 | ] |
| 239 | } |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 240 | |
| 241 | setProperties(image_url, properties) |
Sergey Kulanov | 8cd6d22 | 2016-11-17 13:42:47 +0200 | [diff] [blame] | 242 | |
| 243 | if ( buildInfo != null ) { |
| 244 | buildInfo.env.capture = true |
| 245 | buildInfo.env.filter.addInclude("*") |
| 246 | buildInfo.env.filter.addExclude("*PASSWORD*") |
| 247 | buildInfo.env.filter.addExclude("*password*") |
| 248 | buildInfo.env.collect() |
| 249 | server.publishBuildInfo(buildInfo) |
| 250 | } |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 251 | } |
| 252 | |
| 253 | /** |
| 254 | * Upload binaries to Artifactory |
| 255 | * |
| 256 | * @param server ArtifactoryServer, the instance of Artifactory server |
| 257 | * @param buildInfo BuildInfo, the instance of a build-info object which can be published |
| 258 | * @param uploadSpec String, a spec which is a JSON file that specifies which files should be |
| 259 | * uploaded or downloaded and the target path |
| 260 | * @param publishInfo Boolean, whether publish a build-info object to Artifactory |
| 261 | */ |
Sergey Kulanov | 91d8def | 2016-11-15 13:53:17 +0200 | [diff] [blame] | 262 | def uploadBinariesToArtifactory (ArtifactoryServer server, BuildInfo buildInfo, String uploadSpec, |
| 263 | Boolean publishInfo = false) { |
Jakub Josef | befcf6c | 2017-11-14 18:03:10 +0100 | [diff] [blame] | 264 | server.upload(uploadSpec, buildInfo) |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 265 | |
| 266 | if ( publishInfo ) { |
| 267 | buildInfo.env.capture = true |
| 268 | buildInfo.env.filter.addInclude("*") |
| 269 | buildInfo.env.filter.addExclude("*PASSWORD*") |
| 270 | buildInfo.env.filter.addExclude("*password*") |
| 271 | buildInfo.env.collect() |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 272 | server.publishBuildInfo(buildInfo) |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | /** |
| 277 | * Promote Docker image artifact to release repo |
| 278 | * |
| 279 | * @param artifactoryURL String, an URL to Artifactory |
| 280 | * @param artifactoryDevRepo String, the source dev repository name |
| 281 | * @param artifactoryProdRepo String, the target repository for the move or copy |
| 282 | * @param dockerRepo String, the docker repository name to promote |
| 283 | * @param artifactTag String, an image tag name to promote |
| 284 | * @param targetTag String, target tag to assign the image after promotion |
| 285 | * @param copy Boolean, an optional value to set whether to copy instead of move |
| 286 | * Default: false |
| 287 | */ |
| 288 | def promoteDockerArtifact(String artifactoryURL, String artifactoryDevRepo, |
| 289 | String artifactoryProdRepo, String dockerRepo, |
| 290 | String artifactTag, String targetTag, Boolean copy = false) { |
| 291 | def url = "${artifactoryURL}/api/docker/${artifactoryDevRepo}/v2/promote" |
Dmitry Burmistrov | 5deaa7d | 2017-05-30 17:12:54 +0400 | [diff] [blame] | 292 | String queryFile = UUID.randomUUID().toString() |
Dmitry Burmistrov | 97beb9b | 2017-05-29 17:21:34 +0400 | [diff] [blame] | 293 | writeFile file: queryFile, |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 294 | text: """{ |
| 295 | \"targetRepo\": \"${artifactoryProdRepo}\", |
| 296 | \"dockerRepository\": \"${dockerRepo}\", |
| 297 | \"tag\": \"${artifactTag}\", |
| 298 | \"targetTag\" : \"${targetTag}\", |
| 299 | \"copy\": \"${copy}\" |
| 300 | }""".stripIndent() |
Dmitry Burmistrov | 97beb9b | 2017-05-29 17:21:34 +0400 | [diff] [blame] | 301 | sh "cat ${queryFile}" |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 302 | withCredentials([ |
| 303 | [$class : 'UsernamePasswordMultiBinding', |
| 304 | credentialsId : 'artifactory', |
| 305 | passwordVariable: 'ARTIFACTORY_PASSWORD', |
| 306 | usernameVariable: 'ARTIFACTORY_LOGIN'] |
| 307 | ]) { |
Sergey Reshetnyak | f0775fb | 2018-06-28 14:54:01 +0400 | [diff] [blame] | 308 | sh "bash -c \"curl --fail -u ${ARTIFACTORY_LOGIN}:${ARTIFACTORY_PASSWORD} -H \"Content-Type:application/json\" -X POST -d @${queryFile} ${url}\"" |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 309 | } |
Dmitry Burmistrov | 97beb9b | 2017-05-29 17:21:34 +0400 | [diff] [blame] | 310 | sh "rm -v ${queryFile}" |
Ruslan Kamaldinov | 90d4e67 | 2016-11-11 18:31:00 +0300 | [diff] [blame] | 311 | } |
Denis Egorenko | 60f47c1 | 2019-03-11 20:54:13 +0400 | [diff] [blame^] | 312 | |
| 313 | /** |
| 314 | * Save job artifacts to Artifactory server if available. |
| 315 | * Returns link to Artifactory repo, where saved job artifacts. |
| 316 | * |
| 317 | * @param config LinkedHashMap which contains next parameters: |
| 318 | * @param artifactory String, Artifactory server id |
| 319 | * @param artifactoryRepo String, repo to save job artifacts |
| 320 | * @param buildProps ArrayList, additional props for saved artifacts. Optional, default: [] |
| 321 | * @param artifactory_not_found_fail Boolean, whether to fail if provided artifactory |
| 322 | * id is not found or just print warning message. Optional, default: false |
| 323 | */ |
| 324 | def uploadJobArtifactsToArtifactory(LinkedHashMap config) { |
| 325 | def common = new com.mirantis.mk.Common() |
| 326 | def artifactsDescription = '' |
| 327 | def artifactoryServer |
| 328 | try { |
| 329 | artifactoryServer = Artifactory.server(config.get('artifactory')) |
| 330 | } catch (Exception e) { |
| 331 | if (config.get('artifactory_not_found_fail', false)) { |
| 332 | throw e |
| 333 | } else { |
| 334 | common.warningMsg(e) |
| 335 | return "Artifactory server is not found. Can't save artifacts in Artifactory." |
| 336 | } |
| 337 | } |
| 338 | def artifactDir = 'cur_build_artifacts' |
| 339 | def user = '' |
| 340 | wrap([$class: 'BuildUser']) { |
| 341 | user = env.BUILD_USER_ID |
| 342 | } |
| 343 | dir(artifactDir) { |
| 344 | try { |
| 345 | unarchive(mapping: ['*' : '.']) |
| 346 | // Mandatory and additional properties |
| 347 | def properties = getBinaryBuildProperties(config.get('buildProps', []) << "buildUser=${user}") |
| 348 | |
| 349 | // Build Artifactory spec object |
| 350 | def uploadSpec = """{ |
| 351 | "files": |
| 352 | [ |
| 353 | { |
| 354 | "pattern": "*", |
| 355 | "target": "${config.get('artifactoryRepo')}/", |
| 356 | "props": "${properties}" |
| 357 | } |
| 358 | ] |
| 359 | }""" |
| 360 | |
| 361 | artifactoryServer.upload(uploadSpec, newBuildInfo()) |
| 362 | def linkUrl = "${artifactoryServer.getUrl()}/artifactory/${config.get('artifactoryRepo')}" |
| 363 | artifactsDescription = "Job artifacts uploaded to Artifactory: <a href=\"${linkUrl}\">${linkUrl}</a>" |
| 364 | } catch (Exception e) { |
| 365 | if (e =~ /no artifacts/) { |
| 366 | artifactsDescription = 'Build has no artifacts saved.' |
| 367 | } else { |
| 368 | throw e |
| 369 | } |
| 370 | } finally { |
| 371 | deleteDir() |
| 372 | } |
| 373 | } |
| 374 | return artifactsDescription |
| 375 | } |