blob: 1e24aeb8e92cdf19aca61397148bcd8d34617b28 [file] [log] [blame]
Sergey Kolekonovba203982016-12-21 18:32:17 +04001package com.mirantis.mk
2
3/**
4 *
5 * Git functions
6 *
7 */
8
9/**
10 * Checkout single git repository
11 *
12 * @param path Directory to checkout repository to
13 * @param url Source Git repository URL
14 * @param branch Source Git repository branch
15 * @param credentialsId Credentials ID to use for source Git
Jakub Josef7dccebe2017-03-06 18:08:32 +010016 * @param poll Enable git polling (default true)
17 * @param timeout Set checkout timeout (default 10)
Jakub Josef61f29e62017-03-08 16:42:06 +010018 * @param depth Git depth param (default 0 means no depth)
Alexandr Lovtsove818e102019-07-29 14:45:01 +030019 * @param reference Git reference param to checkout (default empyt, i.e. no reference)
Sergey Kolekonovba203982016-12-21 18:32:17 +040020 */
Alexandr Lovtsove818e102019-07-29 14:45:01 +030021def checkoutGitRepository(path, url, branch, credentialsId = null, poll = true, timeout = 10, depth = 0, reference = ''){
Alexandr Lovtsov73786142019-09-02 17:45:12 +030022 def branch_name = reference ? 'FETCH_HEAD' : "*/${branch}"
Sergey Kolekonovba203982016-12-21 18:32:17 +040023 dir(path) {
Jakub Josef6fa8cb12017-03-06 18:20:08 +010024 checkout(
25 changelog:true,
26 poll: poll,
27 scm: [
28 $class: 'GitSCM',
Alexandr Lovtsov73786142019-09-02 17:45:12 +030029 branches: [[name: branch_name]],
Jakub Josef6fa8cb12017-03-06 18:20:08 +010030 doGenerateSubmoduleConfigurations: false,
31 extensions: [
Jakub Josef61f29e62017-03-08 16:42:06 +010032 [$class: 'CheckoutOption', timeout: timeout],
Alexandr Lovtsov73786142019-09-02 17:45:12 +030033 [$class: 'CloneOption', depth: depth, noTags: false, shallow: depth > 0, timeout: timeout]],
Jakub Josef6fa8cb12017-03-06 18:20:08 +010034 submoduleCfg: [],
Alexandr Lovtsov73786142019-09-02 17:45:12 +030035 userRemoteConfigs: [[url: url, credentialsId: credentialsId, refspec: reference]]]
Jakub Josef6fa8cb12017-03-06 18:20:08 +010036 )
Sergey Kolekonovba203982016-12-21 18:32:17 +040037 }
38}
39
40/**
41 * Parse HEAD of current directory and return commit hash
42 */
43def getGitCommit() {
44 git_commit = sh (
45 script: 'git rev-parse HEAD',
46 returnStdout: true
47 ).trim()
48 return git_commit
49}
50
51/**
Ales Komarekfb7cbcb2017-02-24 14:02:03 +010052 * Change actual working branch of repo
53 *
54 * @param path Path to the git repository
55 * @param branch Branch desired to switch to
56 */
57def changeGitBranch(path, branch) {
58 dir(path) {
59 git_cmd = sh (
Leontii Istominb4f4ae12018-02-27 20:25:43 +010060 script: "git checkout ${branch}",
Ales Komarekfb7cbcb2017-02-24 14:02:03 +010061 returnStdout: true
62 ).trim()
63 }
64 return git_cmd
65}
66
67/**
Ales Komarekc3a8b972017-03-24 13:57:25 +010068 * Get remote URL
69 *
70 * @param name Name of remote (default any)
71 * @param type Type (fetch or push, default fetch)
72 */
73def getGitRemote(name = '', type = 'fetch') {
74 gitRemote = sh (
75 script: "git remote -v | grep '${name}' | grep ${type} | awk '{print \$2}' | head -1",
76 returnStdout: true
77 ).trim()
78 return gitRemote
79}
80
81/**
Alexandr Lovtsovd1540612020-05-07 14:10:37 +030082 * Get commit message for given commit reference
83 */
84def getGitCommitMessage(String path, String commitRef = 'HEAD') {
85 dir(path) {
86 commitMsg = sh (
87 script: "git log --format=%B -n 1 ${commitRef}",
88 returnStdout: true
89 ).trim()
90 }
91 return commitMsg
92}
93
94/**
Ales Komarekc3a8b972017-03-24 13:57:25 +010095 * Create new working branch for repo
96 *
97 * @param path Path to the git repository
98 * @param branch Branch desired to switch to
99 */
100def createGitBranch(path, branch) {
101 def git_cmd
102 dir(path) {
103 git_cmd = sh (
104 script: "git checkout -b ${branch}",
105 returnStdout: true
106 ).trim()
107 }
108 return git_cmd
109}
110
111/**
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100112 * Commit changes to the git repo
113 *
114 * @param path Path to the git repository
115 * @param message A commit message
Denis Egorenkof4c45512019-03-04 15:53:36 +0400116 * @param global Use global config
Oleksii Grudeva64e5b22019-06-11 11:21:02 +0300117 * @param amend Whether to use "--amend" in commit command
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100118 */
Oleksii Grudeva64e5b22019-06-11 11:21:02 +0300119def commitGitChanges(path, message, gitEmail='jenkins@localhost', gitName='jenkins-slave', global=false, amend=false) {
Ales Komarekc3a8b972017-03-24 13:57:25 +0100120 def git_cmd
Oleksii Grudeva64e5b22019-06-11 11:21:02 +0300121 def gitOpts
Denis Egorenkof4c45512019-03-04 15:53:36 +0400122 def global_arg = ''
123 if (global) {
124 global_arg = '--global'
125 }
Oleksii Grudeva64e5b22019-06-11 11:21:02 +0300126 if (amend) {
127 gitOpts = '--amend'
128 } else {
129 gitOpts = ''
130 }
Alexandr Lovtsov2fb93482020-06-16 14:39:43 +0300131 def gitEnv = [
132 "GIT_AUTHOR_NAME=${gitName}",
133 "GIT_AUTHOR_EMAIL=${gitEmail}",
134 "GIT_COMMITTER_NAME=${gitName}",
135 "GIT_COMMITTER_EMAIL=${gitEmail}",
136 ]
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100137 dir(path) {
Denis Egorenkof4c45512019-03-04 15:53:36 +0400138 sh "git config ${global_arg} user.email '${gitEmail}'"
139 sh "git config ${global_arg} user.name '${gitName}'"
Tomáš Kukráldf7bebc2017-03-27 15:12:43 +0200140
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100141 sh(
142 script: 'git add -A',
143 returnStdout: true
144 ).trim()
Alexandr Lovtsov2fb93482020-06-16 14:39:43 +0300145 withEnv(gitEnv) {
146 git_cmd = sh(
147 script: "git commit ${gitOpts} -m '${message}'",
148 returnStdout: true
149 ).trim()
150 }
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100151 }
152 return git_cmd
153}
154
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100155/**
156 * Push git changes to remote repo
157 *
Ales Komarekc3a8b972017-03-24 13:57:25 +0100158 * @param path Path to the local git repository
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100159 * @param branch Branch on the remote git repository
160 * @param remote Name of the remote repository
Ales Komarekc3a8b972017-03-24 13:57:25 +0100161 * @param credentialsId Credentials with write permissions
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100162 */
Ales Komarekc3a8b972017-03-24 13:57:25 +0100163def pushGitChanges(path, branch = 'master', remote = 'origin', credentialsId = null) {
164 def ssh = new com.mirantis.mk.Ssh()
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100165 dir(path) {
Ales Komarekc3a8b972017-03-24 13:57:25 +0100166 if (credentialsId == null) {
167 sh script: "git push ${remote} ${branch}"
168 }
169 else {
170 ssh.prepareSshAgentKey(credentialsId)
171 ssh.runSshAgentCommand("git push ${remote} ${branch}")
172 }
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100173 }
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100174}
175
Ales Komarekc3a8b972017-03-24 13:57:25 +0100176
Sergey Kolekonovba203982016-12-21 18:32:17 +0400177/**
Filip Pytloun49d66302017-03-06 10:26:22 +0100178 * Mirror git repository, merge target changes (downstream) on top of source
179 * (upstream) and push target or both if pushSource is true
180 *
181 * @param sourceUrl Source git repository
182 * @param targetUrl Target git repository
Ivan Berezovskiycf269442019-07-18 16:15:26 +0400183 * @param credentialsId Credentials id to use for accessing target repositories
Filip Pytloun49d66302017-03-06 10:26:22 +0100184 * @param branches List or comma-separated string of branches to sync
185 * @param followTags Mirror tags
186 * @param pushSource Push back into source branch, resulting in 2-way sync
187 * @param pushSourceTags Push target tags into source or skip pushing tags
188 * @param gitEmail Email for creation of merge commits
189 * @param gitName Name for creation of merge commits
Sergey Kolekonovba203982016-12-21 18:32:17 +0400190 */
Ivan Berezovskiycf269442019-07-18 16:15:26 +0400191def mirrorGit(sourceUrl, targetUrl, credentialsId, branches, followTags = false, pushSource = false, pushSourceTags = false, gitEmail = 'jenkins@localhost', gitName = 'Jenkins', sourceRemote = 'origin') {
Jakub Josef668dc2b2017-06-19 16:55:26 +0200192 def common = new com.mirantis.mk.Common()
193 def ssh = new com.mirantis.mk.Ssh()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400194 if (branches instanceof String) {
195 branches = branches.tokenize(',')
196 }
Ivan Berezovskiycf269442019-07-18 16:15:26 +0400197 // If both source and target repos are secured and accessible via http/https,
198 // we need to switch GIT_ASKPASS value when running git commands
199 def sourceAskPass
200 def targetAskPass
Sergey Kolekonovba203982016-12-21 18:32:17 +0400201
Ivan Berezovskiycf269442019-07-18 16:15:26 +0400202 def sshCreds = common.getCredentialsById(credentialsId, 'sshKey') // True if found
203 if (sshCreds) {
204 ssh.prepareSshAgentKey(credentialsId)
205 ssh.ensureKnownHosts(targetUrl)
206 sh "git config user.name '${gitName}'"
207 } else {
208 withCredentials([[$class : 'UsernamePasswordMultiBinding',
209 credentialsId : credentialsId,
210 passwordVariable: 'GIT_PASSWORD',
211 usernameVariable: 'GIT_USERNAME']]) {
212 sh """
213 set +x
214 git config --global credential.${targetUrl}.username \${GIT_USERNAME}
215 echo "echo \${GIT_PASSWORD}" > ${WORKSPACE}/${credentialsId}_askpass.sh
216 chmod +x ${WORKSPACE}/${credentialsId}_askpass.sh
217 git config user.name \${GIT_USERNAME}
218 """
219 sourceAskPass = env.GIT_ASKPASS ?: ''
220 targetAskPass = "${WORKSPACE}/${credentialsId}_askpass.sh"
221 }
222 }
Filip Pytloun49d66302017-03-06 10:26:22 +0100223 sh "git config user.email '${gitEmail}'"
Filip Pytloun49d66302017-03-06 10:26:22 +0100224
Jakub Josef1caa7ae2017-08-21 16:39:00 +0200225 def remoteExistence = sh(script: "git remote -v | grep ${TARGET_URL} | grep target", returnStatus: true)
Ivan Berezovskiycf269442019-07-18 16:15:26 +0400226 if(remoteExistence == 0) {
227 // silently try to remove target
228 sh(script: "git remote remove target", returnStatus: true)
Jakub Josef1caa7ae2017-08-21 16:39:00 +0200229 }
Ivan Berezovskiycf269442019-07-18 16:15:26 +0400230 sh("git remote add target ${TARGET_URL}")
231 if (sshCreds) {
232 ssh.agentSh "git remote update --prune"
233 } else {
234 env.GIT_ASKPASS = sourceAskPass
235 sh "git remote update ${sourceRemote} --prune"
236 env.GIT_ASKPASS = targetAskPass
237 sh "git remote update target --prune"
238 }
Filip Pytloun49d66302017-03-06 10:26:22 +0100239
Sergey Kolekonovba203982016-12-21 18:32:17 +0400240 for (i=0; i < branches.size; i++) {
241 branch = branches[i]
Jakub Josef668dc2b2017-06-19 16:55:26 +0200242 sh "git branch | grep ${branch} || git checkout -b ${branch}"
243 def resetResult = sh(script: "git checkout ${branch} && git reset --hard origin/${branch}", returnStatus: true)
244 if(resetResult != 0){
245 common.warningMsg("Cannot reset to origin/${branch} for perform git mirror, trying to reset from target/${branch}")
246 resetResult = sh(script: "git checkout ${branch} && git reset --hard target/${branch}", returnStatus: true)
247 if(resetResult != 0){
248 throw new Exception("Cannot reset even to target/${branch}, git mirroring failed!")
249 }
250 }
Sergey Kolekonovba203982016-12-21 18:32:17 +0400251
Sergey Kolekonovba203982016-12-21 18:32:17 +0400252 sh "git ls-tree target/${branch} && git merge --no-edit --ff target/${branch} || echo 'Target repository is empty, skipping merge'"
253 followTagsArg = followTags ? "--follow-tags" : ""
Ivan Berezovskiycf269442019-07-18 16:15:26 +0400254 if (sshCreds) {
255 ssh.agentSh "git push ${followTagsArg} target HEAD:${branch}"
256 } else {
257 sh "git push ${followTagsArg} target HEAD:${branch}"
258 }
Filip Pytloun49d66302017-03-06 10:26:22 +0100259
260 if (pushSource == true) {
261 followTagsArg = followTags && pushSourceTags ? "--follow-tags" : ""
Ivan Berezovskiycf269442019-07-18 16:15:26 +0400262 if (sshCreds) {
263 ssh.agentSh "git push ${followTagsArg} origin HEAD:${branch}"
264 } else {
265 sh "git push ${followTagsArg} origin HEAD:${branch}"
266 }
Filip Pytloun49d66302017-03-06 10:26:22 +0100267 }
268 }
Filip Pytloun49d66302017-03-06 10:26:22 +0100269 if (followTags == true) {
Ivan Berezovskiycf269442019-07-18 16:15:26 +0400270 if (sshCreds) {
271 ssh.agentSh "git push -f target --tags"
272 } else {
273 sh "git push -f target --tags"
274 }
Filip Pytloun49d66302017-03-06 10:26:22 +0100275
276 if (pushSourceTags == true) {
Ivan Berezovskiycf269442019-07-18 16:15:26 +0400277 if (sshCreds) {
278 ssh.agentSh "git push -f origin --tags"
279 } else {
280 sh "git push -f origin --tags"
281 }
Filip Pytloun49d66302017-03-06 10:26:22 +0100282 }
Sergey Kolekonovba203982016-12-21 18:32:17 +0400283 }
Jakub Josefecf8b452017-04-20 13:34:29 +0200284 sh "git remote rm target"
Ivan Berezovskiycf269442019-07-18 16:15:26 +0400285 if (!sshCreds) {
286 sh "set +x; rm -f ${targetAskPass}"
287 sh "git config --global --unset credential.${targetUrl}.username"
288 }
Sergey Kolekonovba203982016-12-21 18:32:17 +0400289}
Martin Polreich765f7ba2019-03-12 16:39:25 +0100290
291
292/**
293 * Return all branches for the defined git repository that match the matcher.
294 *
295 * @param repoUrl URL of git repository
296 * @param branchMatcher matcher to filter out the branches (If '' or '*', returns all branches without filtering)
297 * @return branchesList list of branches
298 */
299
300def getBranchesForGitRepo(repoUrl, branchMatcher = ''){
301
302 if (branchMatcher.equals("*")) {
303 branchMatcher = ''
304 }
305 branchesList = sh (
306 script: "git ls-remote --heads ${repoUrl} | cut -f2 | grep -e '${branchMatcher}' | sed 's/refs\\/heads\\///g'",
307 returnStdout: true
308 ).trim()
309 return branchesList.tokenize('\n')
Oleksii Grudeva64e5b22019-06-11 11:21:02 +0300310}
311
Mykyta Karpin82437932019-06-06 14:08:18 +0300312/**
313 * Method for preparing a tag to be SemVer 2 compatible, and can handle next cases:
314 * - length of tag splitted by dots is more than 3
315 * - first part of splitted tag starts not from digit
316 * - length of tag is lower than 3
317 *
318 * @param tag String which contains a git tag from repository
319 * @return HashMap HashMap in the form: ['version': 'x.x.x', 'extra': 'x.x.x'], extra
320 * is added only if size of original tag splitted by dots is more than 3
321 */
322
323def prepareTag(tag){
324 def parts = tag.tokenize('.')
325 def res = [:]
326 // Handle case with tags like v1.1.1
327 parts[0] = parts[0].replaceFirst("[^\\d.]", '')
328 // handle different sizes of tags - 1.1.1.1 or 1.1.1.1rc1
329 if (parts.size() > 3){
330 res['extra'] = parts[3..-1].join('.')
331 } else if (parts.size() < 3){
332 (parts.size()..2).each {
333 parts[it] = '0'
334 }
335 }
336 res['version'] = "${parts[0]}.${parts[1]}.${parts[2]}"
337 return res
338}
339
340/**
341 * Method for incrementing SemVer 2 compatible version
342 *
343 * @param version String which contains main part of SemVer2 version - '2.1.0'
344 * @return string String conaining version with Patch part of version incremented by 1
345 */
346
347def incrementVersion(version){
348 def parts = checkVersion(version)
349 return "${parts[0]}.${parts[1]}.${parts[2].toInteger() + 1}"
350}
351
352/**
353 * Method for checking whether version is compatible with Sem Ver 2
354 *
355 * @param version String which contains main part of SemVer2 version - '2.1.0'
356 * @return list With 3 strings as result of splitting version by dots
357 */
358
359def checkVersion(version) {
360 def parts = version.tokenize('.')
361 if (parts.size() != 3 || !(parts[0] ==~ /^\d+/)) {
362 error "Bad version ${version}"
363 }
364 return parts
365}
366
367/**
368 * Method for constructing SemVer2 compatible version from tag in Git repository:
369 * - if current commit matches the last tag, last tag will be returned as version
370 * - if no tag found assuming no release was done, version will be 0.0.1 with pre release metadata
371 * - if tag found - patch part of version will be incremented and pre-release metadata will be added
372 *
373 *
374 * @param repoDir String which contains path to directory with git repository
375 * @param allowNonSemVer2 Bool whether to allow working with tags which aren't compatible
376 * with Sem Ver 2 (not in form X.Y.Z). if set to true tag will be
377* converted to Sem Ver 2 version e.g tag 1.1.1.1rc1 -> version 1.1.1-1rc1
378 * @return version String
379 */
380def getVersion(repoDir, allowNonSemVer2 = false) {
381 def common = new com.mirantis.mk.Common()
382 dir(repoDir){
383 def cmd = common.shCmdStatus('git describe --tags --first-parent --abbrev=0')
384 def tag_data = [:]
385 def last_tag = cmd['stdout'].trim()
386 def commits_since_tag
387 if (cmd['status'] != 0){
388 if (cmd['stderr'].contains('fatal: No names found, cannot describe anything')){
389 common.warningMsg('No parent tag found, using initial version 0.0.0')
390 tag_data['version'] = '0.0.0'
391 commits_since_tag = sh(script: 'git rev-list --count HEAD', returnStdout: true).trim()
392 } else {
393 error("Something went wrong, cannot find git information ${cmd['stderr']}")
394 }
395 } else {
396 tag_data['version'] = last_tag
397 commits_since_tag = sh(script: "git rev-list --count ${last_tag}..HEAD", returnStdout: true).trim()
398 }
399 try {
400 checkVersion(tag_data['version'])
401 } catch (Exception e) {
402 if (allowNonSemVer2){
403 common.errorMsg(
404 """Git tag isn't compatible with SemVer2, but allowNonSemVer2 is set.
405 Trying to convert git tag to Sem Ver 2 compatible version
406 ${e.message}""")
407 tag_data = prepareTag(tag_data['version'])
408 } else {
409 error("Git tag isn't compatible with SemVer2\n${e.message}")
410 }
411 }
412 // If current commit is exact match to the first parent tag than return it
413 def pre_release_meta = []
414 if (tag_data.get('extra')){
415 pre_release_meta.add(tag_data['extra'])
416 }
417 if (common.shCmdStatus('git describe --tags --first-parent --exact-match')['status'] == 0){
418 if (pre_release_meta){
419 return "${tag_data['version']}-${pre_release_meta[0]}"
420 } else {
421 return tag_data['version']
422 }
423 }
424 // If we away from last tag for some number of commits - add additional metadata and increment version
425 pre_release_meta.add(commits_since_tag)
426 def next_version = incrementVersion(tag_data['version'])
427 def commit_sha = sh(script: 'git rev-parse --short=7 HEAD', returnStdout: true).trim()
428 return "${next_version}-${pre_release_meta.join('.')}-${commit_sha}"
429 }
430}
Mykyta Karpinf5b6c162019-08-08 14:28:15 +0300431
432
433/**
434 * Method for uploading a change request
435 *
436 * @param repo String which contains path to directory with git repository
437 * @param credentialsId Credentials id to use for accessing target repositories
438 * @param commit Id of commit which should be uploaded
439 * @param branch Name of the branch for uploading
440 * @param topic Topic of the change
441 *
442 */
443def pushForReview(repo, credentialsId, commit, branch, topic='', remote='origin') {
444 def common = new com.mirantis.mk.Common()
445 def ssh = new com.mirantis.mk.Ssh()
446 common.infoMsg("Uploading commit ${commit} to ${branch} for review...")
447
448 def pushArg = "${commit}:refs/for/${branch}"
449 def process = [:]
450 if (topic){
451 pushArg += '%topic=' + topic
452 }
453 dir(repo){
454 ssh.prepareSshAgentKey(credentialsId)
455 ssh.runSshAgentCommand("git push ${remote} ${pushArg}")
456 }
457}
458
459/**
460 * Generates a commit message with predefined or auto generate change id. If change
461 * id isn't provided, changeIdSeed and current sha of git head will be used in
462 * generation of commit change id.
463 *
464 * @param repo String which contains path to directory with git repository
465 * @param message Commit message main part
466 * @param changeId User defined change-id usually sha1 hash
467 * @param changeIdSeed Custom part of change id which can be added during change id generation
468 *
469 *
470 * @return commitMessage Multiline String with generated commit message
471 */
472def genCommitMessage(repo, message, changeId = '', changeIdSeed = ''){
473 def git = new com.mirantis.mk.Git()
474 def common = new com.mirantis.mk.Common()
475 def commitMessage
476 def id = changeId
477 def seed = changeIdSeed
478 if (!id) {
479 if (!seed){
480 seed = common.generateRandomHashString(32)
481 }
482 def head_sha
483 dir(repo){
484 head_sha = git.getGitCommit()
485 }
486 id = 'I' + sh(script: 'echo -n ' + seed + head_sha + ' | sha1sum | awk \'{print $1}\'', returnStdout: true)
487 }
488 commitMessage =
489 """${message}
490
491 |Change-Id: ${id}
492 """.stripMargin()
493
494 return commitMessage
495}
Alexandr Lovtsov4fdafd92019-09-09 17:52:16 +0200496
497/**
498 * Update (or create if cannot find) gerrit change request
499 *
500 * @param params Map of parameters to customize commit
501 * - gerritAuth A map containing information about Gerrit. Should include HOST, PORT and USER
502 * - credentialsId Jenkins credentials id for gerrit
503 * - repo Local directory with repository
504 * - comment Commit comment
505 * - change_id_seed Custom part of change id which can be added during change id generation
506 * - branch Name of the branch for uploading
507 * - topic Topic of the change
508 * - project Gerrit project to search in for gerrit change request
509 * - status Change request's status to search for
510 * - changeAuthorEmail Author's email of the change
511 * - changeAuthorName Author's name of the change
Mykyta Karpincfbbbd82021-11-03 16:58:07 +0000512 * - forceUpdate Whether to update change if no diff between local state and remote
Mykyta Karpin28695382022-04-22 16:16:44 +0300513 * - gerritPatch Maps with patch information (result of gerrit.findGerritChange)
514 * - amend Do amend current patch
515 */
Alexandr Lovtsov4fdafd92019-09-09 17:52:16 +0200516def updateChangeRequest(Map params) {
517 def gerrit = new com.mirantis.mk.Gerrit()
Mykyta Karpincfbbbd82021-11-03 16:58:07 +0000518 def common = new com.mirantis.mk.Common()
Alexandr Lovtsov4fdafd92019-09-09 17:52:16 +0200519
520 def commitMessage
Alexandr Lovtsov4fdafd92019-09-09 17:52:16 +0200521 def creds = params['credentialsId']
522 def repo = params['repo']
523 def comment = params['comment']
524 def change_id_seed = params.get('change_id_seed', JOB_NAME)
525 def branch = params['branch']
526 def topic = params['topic']
Alexandr Lovtsov4fdafd92019-09-09 17:52:16 +0200527 def changeAuthorEmail = params['changeAuthorEmail']
528 def changeAuthorName = params['changeAuthorName']
Mykyta Karpincfbbbd82021-11-03 16:58:07 +0000529 def forceUpdate = params.get('forceUpdate', true)
Mykyta Karpin28695382022-04-22 16:16:44 +0300530 def amend = params.get('amend', false)
531 def jsonChange = params.get('gerritPatch', [:])
Maxim Rasskazov427435f2020-09-16 15:38:56 +0400532 def changeId = params.get('changeId', '')
Alexandr Lovtsov4fdafd92019-09-09 17:52:16 +0200533 def commit
Mykyta Karpin28695382022-04-22 16:16:44 +0300534
535 if (!jsonChange) {
536 def auth = params['gerritAuth']
537 def status = params.get('status', 'open')
538 def project = params['project']
539 def changeParams = ['owner': auth['USER'], 'status': status, 'project': project, 'branch': branch, 'topic': topic]
540 def gerritChange = gerrit.findGerritChange(creds, auth, changeParams, '--current-patch-set')
541 if (gerritChange) {
542 jsonChange = readJSON text: gerritChange
543 }
544 }
545 if (jsonChange) {
Alexandr Lovtsov4fdafd92019-09-09 17:52:16 +0200546 changeId = jsonChange['id']
Mykyta Karpincfbbbd82021-11-03 16:58:07 +0000547 if(!forceUpdate){
548 def ref = jsonChange['currentPatchSet']['ref']
549 def res
550 dir(repo){
551 sshagent (credentials: [creds]){
552 res = common.shCmdStatus("git fetch origin ${ref} && git diff --quiet --exit-code FETCH_HEAD")["status"]
553 }
554 }
555 if (res == 0){
556 common.infoMsg("Current patch set ${ref} is up to date, no need to update")
557 return
558 }
559 }
Alexandr Lovtsov4fdafd92019-09-09 17:52:16 +0200560 }
561 commitMessage = genCommitMessage(repo, comment, changeId, change_id_seed)
Mykyta Karpin28695382022-04-22 16:16:44 +0300562 commitGitChanges(repo, commitMessage, changeAuthorEmail, changeAuthorName, false, amend)
Alexandr Lovtsov4fdafd92019-09-09 17:52:16 +0200563 dir(repo){
564 commit = getGitCommit()
565 }
566 pushForReview(repo, creds, commit, branch, topic)
567}
Mykyta Karpin28695382022-04-22 16:16:44 +0300568
569/**
570 * Create new working branch for repo from patch if it exists
571 *
572 * @param params Map of parameters to customize commit
573 * - gerritAuth A map containing information about Gerrit. Should include HOST, PORT and USER
574 * - credentialsId Jenkins credentials id for gerrit
575 * - repo Local directory with repository
576 * - branch Name of the branch for uploading
577 * - topic Topic of the change
578 * - project Gerrit project to search in for gerrit change request
579 * - status Change request's status to search for
580 */
581def createGitBranchFromRef(Map params) {
582 def gerrit = new com.mirantis.mk.Gerrit()
583 def common = new com.mirantis.mk.Common()
584
585 def auth = params['gerritAuth']
586 def creds = params['credentialsId']
587 def repo = params['repo']
588 def branch = params['branch']
589 def topic = params['topic']
590 def project = params['project']
591 def status = params.get('status', 'open')
592 def localBranch = "branch_${topic}"
593 def jsonChange = [:]
594
595 def changeParams = ['owner': auth['USER'], 'status': status, 'project': project, 'branch': branch, 'topic': topic]
596 def gerritChange = gerrit.findGerritChange(creds, auth, changeParams, '--current-patch-set')
597 if (gerritChange) {
598 jsonChange = readJSON text: gerritChange
599 def ref = jsonChange['currentPatchSet']['ref']
600 changeId = jsonChange['id']
601 dir(repo){
602 sshagent (credentials: [creds]){
603 common.shCmdStatus("git fetch origin ${ref} && git checkout -b ${localBranch} FETCH_HEAD")
604 }
605 }
606 }
607 else {
608 createGitBranch(repo, localBranch)
609 }
610 return jsonChange
611}