blob: 0d443d68a9c27b70399e8db3295fedcb2b592a5b [file] [log] [blame]
Dmitry Burmistrov4dc07192025-09-01 14:19:16 +04001/**
2 * This method creates config Map object and calls upload(Map) method
3 *
4 * @param server a server ID - file name at resources/../servers
5 * @param spec a JSON representation of upload spec
6 * @return a List object that contains upload results
7 */
8List upload(String server, String spec) {
9 return upload(server: server, spec: spec)
10}
11
12
13/**
14 * This method converts JSON representation of spec into Map object and calls
15 * upload(String, Map)
16 *
17 * @param config a Map object with params `[ server: String, spec: String or Map ]`
18 * @return a List object that contains upload results
19 */
20List upload(Map config) {
21 if (config.spec?.getClass() in [java.util.LinkedHashMap, net.sf.json.JSONObject]) {
22 return upload(config.server, config.spec)
23 }
24 return upload(config.server, parseJSON(config.spec))
25}
26
27
28/**
29 * This method uploads files into artifactory instance
30 *
31 * Input spec example:
32 * [ files: [[
33 * pattern: "**",
34 * target: "my-repository/path/to/upload",
35 * props: "myPropKey1=myPropValue1;myPropKey2=myPropValue2", // optional
36 * ],]
37 *
38 * Result example:
39 * [[
40 * localPath: "local/path/to/file.name",
41 * remotePath: "/path/to/upload/local/path/to/file.name",
42 * repo: "my-repository",
43 * size: 12345,
44 * uri: ... ,
45 * checksums: [
46 * md5: ... ,
47 * sha1: ... ,
48 * sha256: ... "
49 * ],
50 * ],]
51 *
52 * @param server a server ID - server name at resources/../servers
53 * @param spec a Map object with upload spec
54 * @return a List object that contains upload results
55 */
56List upload(String server, Map spec) {
57 List result = []
58
59 Map artConfig = parseJSON(loadResource("artifactory/servers/${server}.json"))
60 String uploadScript = loadResource("artifactory/scripts/upload.sh")
61
62 List artCredentials = [usernamePassword(
63 credentialsId: artConfig.credentialsId,
64 usernameVariable: 'ARTIFACTORY_USERNAME',
65 passwordVariable: 'ARTIFACTORY_PASSWORD')]
66
67 List files = createFileListBySpec(spec)
68
69 retry(artConfig.get('connectionRetry', 1)) {
70 withCredentials(artCredentials) {
71 files.each{ file ->
72 String scriptRawResult
73 List envList = [
74 "ARTIFACTORY_URL=${artConfig.artifactoryUrl}",
75 "ARTIFACTORY_TARGET=${file.target}",
76 "ARTIFACTORY_PROPS=${file.props}",
77 "FILE_TO_UPLOAD=${file.name}"
78 ]
79 withEnv(envList) {
80 scriptRawResult = sh \
81 script: uploadScript,
82 returnStdout: true
83 }
84
85 Map scriptResult = parseScriptResult(scriptRawResult)
86 Map uploadResult = parseJSON(scriptResult.stdout)
87 ['created', 'createdBy', 'downloadUri', 'mimeType', 'originalChecksums'].each {
88 uploadResult.remove(it)
89 }
90 uploadResult.localPath = file.name
91 uploadResult.put('remotePath', uploadResult.remove('path'));
92 result << uploadResult
93 }
94 }
95 }
96 return result
97}
98
99
100/**
101 * This method looks up for local files to upload according to the spec
102 *
103 * @param spec a Map object with upload spec
104 * @return a List object that contains found local files to upload
105 */
106List createFileListBySpec(Map spec) {
107 List result = []
108
109 Map jenkinsProps = [
110 "build.name": env.JOB_NAME,
111 "build.number": env.BUILD_NUMBER,
112 "build.timestamp": currentBuild.startTimeInMillis
113 ]
114
115 spec.files?.each{ specItem ->
116 Map targetProps = specItem.props?.split(';')?.findAll{ it.contains('=') }?.collectEntries{
117 List parts = it.split('=', 2)
118 return [(parts[0]): parts[1]]
119 } ?: [:]
120
121 String props = (jenkinsProps + targetProps)
122 .collect{ "${it.key}=${it.value}" }
123 .join(';')
124
125 if (!(specItem.pattern && specItem.target)) {
126 error "ArtifactoryUploader: Malformed upload spec:\n${spec}"
127 }
128
129 List targetFiles = []
130 try {
Dmitry Burmistrovb24458e2025-09-04 11:40:36 +0400131 targetFiles = findFiles(glob: specItem.pattern.replaceFirst("^${env.WORKSPACE}/?", ""))
Dmitry Burmistrov4dc07192025-09-01 14:19:16 +0400132 .findAll{ !it.directory }
133 .collect{ it.path }
134 } catch (Exception e) {
135 error "ArtifactoryUploader: Unable to find files by pattern: ${specItem.pattern}"
136 }
137
138 targetFiles.each{ file ->
139 result << [ name: file, target: specItem.target, props: props ]
140 }
141 }
142 return result
143}
144
145
146/**
147 * This method parses uploading results
148 *
149 * @param scriptRawResult a JSON representation of uploading results
150 * @return a Map object that contains uploading results
151 */
152Map parseScriptResult(String scriptRawResult) {
153 Map result = parseJSON scriptRawResult
154
155 if (result.exit_code == 0 &&
156 result.response_code &&
157 result.response_code in 200..299 &&
158 result.stdout) { return result }
159
160 String errorMessage = "ArtifactoryUploader: Upload failed"
161 if (result.stdout) {
162 errorMessage += "\nStdout: ${result.stdout}"
163 }
164 if (result.stderr) {
165 errorMessage += "\nStderr: ${result.stderr}"
166 }
167 if (result.response_code) {
168 errorMessage += "\nResponse code: ${result.response_code}"
169 }
170 error errorMessage
171}
172
173
174/**
175 * This method loads resourcse file from the library
176 *
177 * @param path path to the resource file
178 * @return content of the resource file
179 */
180String loadResource(String path) {
181 try {
182 return libraryResource(path)
183 } catch (Exception e) {
184 error "ArtifactoryUploader: Unable to load resource: ${path}"
185 }
186}
187
188
189/**
190 * This method converts a JSON representation into an object
191 *
192 * @param text a JSON content
193 * @return a Map or List object
194 */
195Map parseJSON(String text) {
196 def json = new groovy.json.JsonSlurper()
197 Map result
198 try {
199 // result = readJSON text: text
200 result = json.parseText(text)
201 } catch (Exception e) {
202 json = null
203 error "ArtifactoryUploader: Unable to parse JSON:\n${text}"
204 }
205 json = null
206 return result
207}