blob: 667e43cbb1fa7d44ad9b492af3c060c750b3c91c [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)
Viktor Karpochevcd42bbc2023-03-03 14:51:58 +030020 * @param withWipeOut Enable workspace wipe before checkout
Sergey Kolekonovba203982016-12-21 18:32:17 +040021 */
Viktor Karpochevcd42bbc2023-03-03 14:51:58 +030022def checkoutGitRepository(path, url, branch, credentialsId = null, poll = true, timeout = 10, depth = 0, reference = '', withWipeOut = false){
Alexandr Lovtsov73786142019-09-02 17:45:12 +030023 def branch_name = reference ? 'FETCH_HEAD' : "*/${branch}"
Viktor Karpochevcd42bbc2023-03-03 14:51:58 +030024 def scmExtensions = [
25 [$class: 'CheckoutOption', timeout: timeout],
26 [$class: 'CloneOption', depth: depth, noTags: false, shallow: depth > 0, timeout: timeout]
27 ]
28
29 // wipe workspace before checkout
30 if (withWipeOut) {
31 scmExtensions.add([$class: 'WipeWorkspace'])
32 }
33
Sergey Kolekonovba203982016-12-21 18:32:17 +040034 dir(path) {
Jakub Josef6fa8cb12017-03-06 18:20:08 +010035 checkout(
36 changelog:true,
37 poll: poll,
38 scm: [
39 $class: 'GitSCM',
Alexandr Lovtsov73786142019-09-02 17:45:12 +030040 branches: [[name: branch_name]],
Jakub Josef6fa8cb12017-03-06 18:20:08 +010041 doGenerateSubmoduleConfigurations: false,
Viktor Karpochevcd42bbc2023-03-03 14:51:58 +030042 extensions: scmExtensions,
Jakub Josef6fa8cb12017-03-06 18:20:08 +010043 submoduleCfg: [],
Alexandr Lovtsov73786142019-09-02 17:45:12 +030044 userRemoteConfigs: [[url: url, credentialsId: credentialsId, refspec: reference]]]
Jakub Josef6fa8cb12017-03-06 18:20:08 +010045 )
Sergey Kolekonovba203982016-12-21 18:32:17 +040046 }
47}
48
49/**
50 * Parse HEAD of current directory and return commit hash
51 */
52def getGitCommit() {
53 git_commit = sh (
54 script: 'git rev-parse HEAD',
55 returnStdout: true
56 ).trim()
57 return git_commit
58}
59
60/**
Ales Komarekfb7cbcb2017-02-24 14:02:03 +010061 * Change actual working branch of repo
62 *
63 * @param path Path to the git repository
64 * @param branch Branch desired to switch to
65 */
66def changeGitBranch(path, branch) {
67 dir(path) {
68 git_cmd = sh (
Leontii Istominb4f4ae12018-02-27 20:25:43 +010069 script: "git checkout ${branch}",
Ales Komarekfb7cbcb2017-02-24 14:02:03 +010070 returnStdout: true
71 ).trim()
72 }
73 return git_cmd
74}
75
76/**
Ales Komarekc3a8b972017-03-24 13:57:25 +010077 * Get remote URL
78 *
79 * @param name Name of remote (default any)
80 * @param type Type (fetch or push, default fetch)
81 */
82def getGitRemote(name = '', type = 'fetch') {
83 gitRemote = sh (
84 script: "git remote -v | grep '${name}' | grep ${type} | awk '{print \$2}' | head -1",
85 returnStdout: true
86 ).trim()
87 return gitRemote
88}
89
90/**
Alexandr Lovtsovd1540612020-05-07 14:10:37 +030091 * Get commit message for given commit reference
92 */
93def getGitCommitMessage(String path, String commitRef = 'HEAD') {
94 dir(path) {
95 commitMsg = sh (
96 script: "git log --format=%B -n 1 ${commitRef}",
97 returnStdout: true
98 ).trim()
99 }
100 return commitMsg
101}
102
103/**
Ales Komarekc3a8b972017-03-24 13:57:25 +0100104 * Create new working branch for repo
105 *
106 * @param path Path to the git repository
107 * @param branch Branch desired to switch to
108 */
109def createGitBranch(path, branch) {
110 def git_cmd
111 dir(path) {
112 git_cmd = sh (
113 script: "git checkout -b ${branch}",
114 returnStdout: true
115 ).trim()
116 }
117 return git_cmd
118}
119
120/**
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100121 * Commit changes to the git repo
122 *
123 * @param path Path to the git repository
124 * @param message A commit message
Denis Egorenkof4c45512019-03-04 15:53:36 +0400125 * @param global Use global config
Oleksii Grudeva64e5b22019-06-11 11:21:02 +0300126 * @param amend Whether to use "--amend" in commit command
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100127 */
Oleksii Grudeva64e5b22019-06-11 11:21:02 +0300128def commitGitChanges(path, message, gitEmail='jenkins@localhost', gitName='jenkins-slave', global=false, amend=false) {
Ales Komarekc3a8b972017-03-24 13:57:25 +0100129 def git_cmd
Oleksii Grudeva64e5b22019-06-11 11:21:02 +0300130 def gitOpts
Denis Egorenkof4c45512019-03-04 15:53:36 +0400131 def global_arg = ''
132 if (global) {
133 global_arg = '--global'
134 }
Oleksii Grudeva64e5b22019-06-11 11:21:02 +0300135 if (amend) {
136 gitOpts = '--amend'
137 } else {
138 gitOpts = ''
139 }
Alexandr Lovtsov2fb93482020-06-16 14:39:43 +0300140 def gitEnv = [
Maxim Rasskazovbf095032023-11-13 22:38:58 +0400141 "GIT_AUTHOR_NAME='${gitName}'",
142 "GIT_AUTHOR_EMAIL='${gitEmail}'",
143 "GIT_COMMITTER_NAME='${gitName}'",
144 "GIT_COMMITTER_EMAIL='${gitEmail}'",
Alexandr Lovtsov2fb93482020-06-16 14:39:43 +0300145 ]
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100146 dir(path) {
Denis Egorenkof4c45512019-03-04 15:53:36 +0400147 sh "git config ${global_arg} user.email '${gitEmail}'"
148 sh "git config ${global_arg} user.name '${gitName}'"
Tomáš Kukráldf7bebc2017-03-27 15:12:43 +0200149
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100150 sh(
151 script: 'git add -A',
152 returnStdout: true
153 ).trim()
Alexandr Lovtsov2fb93482020-06-16 14:39:43 +0300154 withEnv(gitEnv) {
155 git_cmd = sh(
156 script: "git commit ${gitOpts} -m '${message}'",
157 returnStdout: true
158 ).trim()
159 }
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100160 }
161 return git_cmd
162}
163
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100164/**
165 * Push git changes to remote repo
166 *
Ales Komarekc3a8b972017-03-24 13:57:25 +0100167 * @param path Path to the local git repository
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100168 * @param branch Branch on the remote git repository
169 * @param remote Name of the remote repository
Ales Komarekc3a8b972017-03-24 13:57:25 +0100170 * @param credentialsId Credentials with write permissions
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100171 */
Ales Komarekc3a8b972017-03-24 13:57:25 +0100172def pushGitChanges(path, branch = 'master', remote = 'origin', credentialsId = null) {
173 def ssh = new com.mirantis.mk.Ssh()
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100174 dir(path) {
Ales Komarekc3a8b972017-03-24 13:57:25 +0100175 if (credentialsId == null) {
176 sh script: "git push ${remote} ${branch}"
177 }
178 else {
179 ssh.prepareSshAgentKey(credentialsId)
180 ssh.runSshAgentCommand("git push ${remote} ${branch}")
181 }
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100182 }
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100183}
184
Ales Komarekc3a8b972017-03-24 13:57:25 +0100185
Sergey Kolekonovba203982016-12-21 18:32:17 +0400186/**
Filip Pytloun49d66302017-03-06 10:26:22 +0100187 * Mirror git repository, merge target changes (downstream) on top of source
188 * (upstream) and push target or both if pushSource is true
189 *
190 * @param sourceUrl Source git repository
191 * @param targetUrl Target git repository
Ivan Berezovskiycf269442019-07-18 16:15:26 +0400192 * @param credentialsId Credentials id to use for accessing target repositories
Filip Pytloun49d66302017-03-06 10:26:22 +0100193 * @param branches List or comma-separated string of branches to sync
194 * @param followTags Mirror tags
195 * @param pushSource Push back into source branch, resulting in 2-way sync
196 * @param pushSourceTags Push target tags into source or skip pushing tags
197 * @param gitEmail Email for creation of merge commits
198 * @param gitName Name for creation of merge commits
Sergey Kolekonovba203982016-12-21 18:32:17 +0400199 */
Ivan Berezovskiycf269442019-07-18 16:15:26 +0400200def 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 +0200201 def common = new com.mirantis.mk.Common()
202 def ssh = new com.mirantis.mk.Ssh()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400203 if (branches instanceof String) {
204 branches = branches.tokenize(',')
205 }
Ivan Berezovskiycf269442019-07-18 16:15:26 +0400206 // If both source and target repos are secured and accessible via http/https,
207 // we need to switch GIT_ASKPASS value when running git commands
208 def sourceAskPass
209 def targetAskPass
Sergey Kolekonovba203982016-12-21 18:32:17 +0400210
Ivan Berezovskiycf269442019-07-18 16:15:26 +0400211 def sshCreds = common.getCredentialsById(credentialsId, 'sshKey') // True if found
212 if (sshCreds) {
213 ssh.prepareSshAgentKey(credentialsId)
214 ssh.ensureKnownHosts(targetUrl)
215 sh "git config user.name '${gitName}'"
216 } else {
217 withCredentials([[$class : 'UsernamePasswordMultiBinding',
218 credentialsId : credentialsId,
219 passwordVariable: 'GIT_PASSWORD',
220 usernameVariable: 'GIT_USERNAME']]) {
221 sh """
222 set +x
223 git config --global credential.${targetUrl}.username \${GIT_USERNAME}
224 echo "echo \${GIT_PASSWORD}" > ${WORKSPACE}/${credentialsId}_askpass.sh
225 chmod +x ${WORKSPACE}/${credentialsId}_askpass.sh
226 git config user.name \${GIT_USERNAME}
227 """
228 sourceAskPass = env.GIT_ASKPASS ?: ''
229 targetAskPass = "${WORKSPACE}/${credentialsId}_askpass.sh"
230 }
231 }
Filip Pytloun49d66302017-03-06 10:26:22 +0100232 sh "git config user.email '${gitEmail}'"
Filip Pytloun49d66302017-03-06 10:26:22 +0100233
Jakub Josef1caa7ae2017-08-21 16:39:00 +0200234 def remoteExistence = sh(script: "git remote -v | grep ${TARGET_URL} | grep target", returnStatus: true)
Ivan Berezovskiycf269442019-07-18 16:15:26 +0400235 if(remoteExistence == 0) {
236 // silently try to remove target
237 sh(script: "git remote remove target", returnStatus: true)
Jakub Josef1caa7ae2017-08-21 16:39:00 +0200238 }
Ivan Berezovskiycf269442019-07-18 16:15:26 +0400239 sh("git remote add target ${TARGET_URL}")
240 if (sshCreds) {
241 ssh.agentSh "git remote update --prune"
242 } else {
243 env.GIT_ASKPASS = sourceAskPass
244 sh "git remote update ${sourceRemote} --prune"
245 env.GIT_ASKPASS = targetAskPass
246 sh "git remote update target --prune"
247 }
Filip Pytloun49d66302017-03-06 10:26:22 +0100248
Sergey Kolekonovba203982016-12-21 18:32:17 +0400249 for (i=0; i < branches.size; i++) {
250 branch = branches[i]
Jakub Josef668dc2b2017-06-19 16:55:26 +0200251 sh "git branch | grep ${branch} || git checkout -b ${branch}"
252 def resetResult = sh(script: "git checkout ${branch} && git reset --hard origin/${branch}", returnStatus: true)
253 if(resetResult != 0){
254 common.warningMsg("Cannot reset to origin/${branch} for perform git mirror, trying to reset from target/${branch}")
255 resetResult = sh(script: "git checkout ${branch} && git reset --hard target/${branch}", returnStatus: true)
256 if(resetResult != 0){
257 throw new Exception("Cannot reset even to target/${branch}, git mirroring failed!")
258 }
259 }
Sergey Kolekonovba203982016-12-21 18:32:17 +0400260
Sergey Kolekonovba203982016-12-21 18:32:17 +0400261 sh "git ls-tree target/${branch} && git merge --no-edit --ff target/${branch} || echo 'Target repository is empty, skipping merge'"
262 followTagsArg = followTags ? "--follow-tags" : ""
Ivan Berezovskiycf269442019-07-18 16:15:26 +0400263 if (sshCreds) {
264 ssh.agentSh "git push ${followTagsArg} target HEAD:${branch}"
265 } else {
266 sh "git push ${followTagsArg} target HEAD:${branch}"
267 }
Filip Pytloun49d66302017-03-06 10:26:22 +0100268
269 if (pushSource == true) {
270 followTagsArg = followTags && pushSourceTags ? "--follow-tags" : ""
Ivan Berezovskiycf269442019-07-18 16:15:26 +0400271 if (sshCreds) {
272 ssh.agentSh "git push ${followTagsArg} origin HEAD:${branch}"
273 } else {
274 sh "git push ${followTagsArg} origin HEAD:${branch}"
275 }
Filip Pytloun49d66302017-03-06 10:26:22 +0100276 }
277 }
Filip Pytloun49d66302017-03-06 10:26:22 +0100278 if (followTags == true) {
Ivan Berezovskiycf269442019-07-18 16:15:26 +0400279 if (sshCreds) {
280 ssh.agentSh "git push -f target --tags"
281 } else {
282 sh "git push -f target --tags"
283 }
Filip Pytloun49d66302017-03-06 10:26:22 +0100284
285 if (pushSourceTags == true) {
Ivan Berezovskiycf269442019-07-18 16:15:26 +0400286 if (sshCreds) {
287 ssh.agentSh "git push -f origin --tags"
288 } else {
289 sh "git push -f origin --tags"
290 }
Filip Pytloun49d66302017-03-06 10:26:22 +0100291 }
Sergey Kolekonovba203982016-12-21 18:32:17 +0400292 }
Jakub Josefecf8b452017-04-20 13:34:29 +0200293 sh "git remote rm target"
Ivan Berezovskiycf269442019-07-18 16:15:26 +0400294 if (!sshCreds) {
295 sh "set +x; rm -f ${targetAskPass}"
296 sh "git config --global --unset credential.${targetUrl}.username"
297 }
Sergey Kolekonovba203982016-12-21 18:32:17 +0400298}
Martin Polreich765f7ba2019-03-12 16:39:25 +0100299
300
301/**
302 * Return all branches for the defined git repository that match the matcher.
303 *
304 * @param repoUrl URL of git repository
305 * @param branchMatcher matcher to filter out the branches (If '' or '*', returns all branches without filtering)
306 * @return branchesList list of branches
307 */
308
309def getBranchesForGitRepo(repoUrl, branchMatcher = ''){
310
311 if (branchMatcher.equals("*")) {
312 branchMatcher = ''
313 }
314 branchesList = sh (
315 script: "git ls-remote --heads ${repoUrl} | cut -f2 | grep -e '${branchMatcher}' | sed 's/refs\\/heads\\///g'",
316 returnStdout: true
317 ).trim()
318 return branchesList.tokenize('\n')
Oleksii Grudeva64e5b22019-06-11 11:21:02 +0300319}
320
Mykyta Karpin82437932019-06-06 14:08:18 +0300321/**
322 * Method for preparing a tag to be SemVer 2 compatible, and can handle next cases:
323 * - length of tag splitted by dots is more than 3
324 * - first part of splitted tag starts not from digit
325 * - length of tag is lower than 3
326 *
327 * @param tag String which contains a git tag from repository
328 * @return HashMap HashMap in the form: ['version': 'x.x.x', 'extra': 'x.x.x'], extra
329 * is added only if size of original tag splitted by dots is more than 3
330 */
331
332def prepareTag(tag){
333 def parts = tag.tokenize('.')
334 def res = [:]
335 // Handle case with tags like v1.1.1
336 parts[0] = parts[0].replaceFirst("[^\\d.]", '')
337 // handle different sizes of tags - 1.1.1.1 or 1.1.1.1rc1
338 if (parts.size() > 3){
339 res['extra'] = parts[3..-1].join('.')
340 } else if (parts.size() < 3){
341 (parts.size()..2).each {
342 parts[it] = '0'
343 }
344 }
345 res['version'] = "${parts[0]}.${parts[1]}.${parts[2]}"
346 return res
347}
348
349/**
350 * Method for incrementing SemVer 2 compatible version
351 *
352 * @param version String which contains main part of SemVer2 version - '2.1.0'
353 * @return string String conaining version with Patch part of version incremented by 1
354 */
355
356def incrementVersion(version){
357 def parts = checkVersion(version)
358 return "${parts[0]}.${parts[1]}.${parts[2].toInteger() + 1}"
359}
360
361/**
362 * Method for checking whether version is compatible with Sem Ver 2
363 *
364 * @param version String which contains main part of SemVer2 version - '2.1.0'
365 * @return list With 3 strings as result of splitting version by dots
366 */
367
368def checkVersion(version) {
369 def parts = version.tokenize('.')
370 if (parts.size() != 3 || !(parts[0] ==~ /^\d+/)) {
371 error "Bad version ${version}"
372 }
373 return parts
374}
375
376/**
377 * Method for constructing SemVer2 compatible version from tag in Git repository:
378 * - if current commit matches the last tag, last tag will be returned as version
379 * - if no tag found assuming no release was done, version will be 0.0.1 with pre release metadata
380 * - if tag found - patch part of version will be incremented and pre-release metadata will be added
381 *
382 *
383 * @param repoDir String which contains path to directory with git repository
384 * @param allowNonSemVer2 Bool whether to allow working with tags which aren't compatible
385 * with Sem Ver 2 (not in form X.Y.Z). if set to true tag will be
386* converted to Sem Ver 2 version e.g tag 1.1.1.1rc1 -> version 1.1.1-1rc1
387 * @return version String
388 */
389def getVersion(repoDir, allowNonSemVer2 = false) {
390 def common = new com.mirantis.mk.Common()
391 dir(repoDir){
392 def cmd = common.shCmdStatus('git describe --tags --first-parent --abbrev=0')
393 def tag_data = [:]
394 def last_tag = cmd['stdout'].trim()
395 def commits_since_tag
396 if (cmd['status'] != 0){
397 if (cmd['stderr'].contains('fatal: No names found, cannot describe anything')){
398 common.warningMsg('No parent tag found, using initial version 0.0.0')
399 tag_data['version'] = '0.0.0'
400 commits_since_tag = sh(script: 'git rev-list --count HEAD', returnStdout: true).trim()
401 } else {
402 error("Something went wrong, cannot find git information ${cmd['stderr']}")
403 }
404 } else {
405 tag_data['version'] = last_tag
406 commits_since_tag = sh(script: "git rev-list --count ${last_tag}..HEAD", returnStdout: true).trim()
407 }
408 try {
409 checkVersion(tag_data['version'])
410 } catch (Exception e) {
411 if (allowNonSemVer2){
412 common.errorMsg(
413 """Git tag isn't compatible with SemVer2, but allowNonSemVer2 is set.
414 Trying to convert git tag to Sem Ver 2 compatible version
415 ${e.message}""")
416 tag_data = prepareTag(tag_data['version'])
417 } else {
418 error("Git tag isn't compatible with SemVer2\n${e.message}")
419 }
420 }
421 // If current commit is exact match to the first parent tag than return it
422 def pre_release_meta = []
423 if (tag_data.get('extra')){
424 pre_release_meta.add(tag_data['extra'])
425 }
426 if (common.shCmdStatus('git describe --tags --first-parent --exact-match')['status'] == 0){
427 if (pre_release_meta){
428 return "${tag_data['version']}-${pre_release_meta[0]}"
429 } else {
430 return tag_data['version']
431 }
432 }
433 // If we away from last tag for some number of commits - add additional metadata and increment version
434 pre_release_meta.add(commits_since_tag)
435 def next_version = incrementVersion(tag_data['version'])
436 def commit_sha = sh(script: 'git rev-parse --short=7 HEAD', returnStdout: true).trim()
437 return "${next_version}-${pre_release_meta.join('.')}-${commit_sha}"
438 }
439}
Mykyta Karpinf5b6c162019-08-08 14:28:15 +0300440
441
442/**
443 * Method for uploading a change request
444 *
445 * @param repo String which contains path to directory with git repository
446 * @param credentialsId Credentials id to use for accessing target repositories
447 * @param commit Id of commit which should be uploaded
448 * @param branch Name of the branch for uploading
449 * @param topic Topic of the change
450 *
451 */
452def pushForReview(repo, credentialsId, commit, branch, topic='', remote='origin') {
453 def common = new com.mirantis.mk.Common()
454 def ssh = new com.mirantis.mk.Ssh()
455 common.infoMsg("Uploading commit ${commit} to ${branch} for review...")
456
457 def pushArg = "${commit}:refs/for/${branch}"
458 def process = [:]
459 if (topic){
460 pushArg += '%topic=' + topic
461 }
462 dir(repo){
463 ssh.prepareSshAgentKey(credentialsId)
464 ssh.runSshAgentCommand("git push ${remote} ${pushArg}")
465 }
466}
467
468/**
469 * Generates a commit message with predefined or auto generate change id. If change
470 * id isn't provided, changeIdSeed and current sha of git head will be used in
471 * generation of commit change id.
472 *
473 * @param repo String which contains path to directory with git repository
474 * @param message Commit message main part
475 * @param changeId User defined change-id usually sha1 hash
476 * @param changeIdSeed Custom part of change id which can be added during change id generation
477 *
478 *
479 * @return commitMessage Multiline String with generated commit message
480 */
481def genCommitMessage(repo, message, changeId = '', changeIdSeed = ''){
482 def git = new com.mirantis.mk.Git()
483 def common = new com.mirantis.mk.Common()
484 def commitMessage
485 def id = changeId
486 def seed = changeIdSeed
487 if (!id) {
488 if (!seed){
489 seed = common.generateRandomHashString(32)
490 }
491 def head_sha
492 dir(repo){
493 head_sha = git.getGitCommit()
494 }
495 id = 'I' + sh(script: 'echo -n ' + seed + head_sha + ' | sha1sum | awk \'{print $1}\'', returnStdout: true)
496 }
497 commitMessage =
498 """${message}
499
500 |Change-Id: ${id}
501 """.stripMargin()
502
503 return commitMessage
504}
Alexandr Lovtsov4fdafd92019-09-09 17:52:16 +0200505
506/**
507 * Update (or create if cannot find) gerrit change request
508 *
509 * @param params Map of parameters to customize commit
510 * - gerritAuth A map containing information about Gerrit. Should include HOST, PORT and USER
511 * - credentialsId Jenkins credentials id for gerrit
512 * - repo Local directory with repository
513 * - comment Commit comment
514 * - change_id_seed Custom part of change id which can be added during change id generation
515 * - branch Name of the branch for uploading
516 * - topic Topic of the change
517 * - project Gerrit project to search in for gerrit change request
518 * - status Change request's status to search for
519 * - changeAuthorEmail Author's email of the change
520 * - changeAuthorName Author's name of the change
Mykyta Karpincfbbbd82021-11-03 16:58:07 +0000521 * - forceUpdate Whether to update change if no diff between local state and remote
Mykyta Karpin28695382022-04-22 16:16:44 +0300522 * - gerritPatch Maps with patch information (result of gerrit.findGerritChange)
523 * - amend Do amend current patch
524 */
Alexandr Lovtsov4fdafd92019-09-09 17:52:16 +0200525def updateChangeRequest(Map params) {
526 def gerrit = new com.mirantis.mk.Gerrit()
Mykyta Karpincfbbbd82021-11-03 16:58:07 +0000527 def common = new com.mirantis.mk.Common()
Alexandr Lovtsov4fdafd92019-09-09 17:52:16 +0200528
529 def commitMessage
Alexandr Lovtsov4fdafd92019-09-09 17:52:16 +0200530 def creds = params['credentialsId']
531 def repo = params['repo']
532 def comment = params['comment']
533 def change_id_seed = params.get('change_id_seed', JOB_NAME)
534 def branch = params['branch']
535 def topic = params['topic']
Alexandr Lovtsov4fdafd92019-09-09 17:52:16 +0200536 def changeAuthorEmail = params['changeAuthorEmail']
537 def changeAuthorName = params['changeAuthorName']
Mykyta Karpincfbbbd82021-11-03 16:58:07 +0000538 def forceUpdate = params.get('forceUpdate', true)
Mykyta Karpin28695382022-04-22 16:16:44 +0300539 def amend = params.get('amend', false)
540 def jsonChange = params.get('gerritPatch', [:])
Maxim Rasskazov427435f2020-09-16 15:38:56 +0400541 def changeId = params.get('changeId', '')
Sergey Kolekonov02d70022023-03-28 14:22:36 +0600542 def remote = params.get('remote', 'origin')
Alexandr Lovtsov4fdafd92019-09-09 17:52:16 +0200543 def commit
Mykyta Karpin28695382022-04-22 16:16:44 +0300544
545 if (!jsonChange) {
546 def auth = params['gerritAuth']
547 def status = params.get('status', 'open')
548 def project = params['project']
549 def changeParams = ['owner': auth['USER'], 'status': status, 'project': project, 'branch': branch, 'topic': topic]
550 def gerritChange = gerrit.findGerritChange(creds, auth, changeParams, '--current-patch-set')
551 if (gerritChange) {
552 jsonChange = readJSON text: gerritChange
553 }
554 }
555 if (jsonChange) {
Alexandr Lovtsov4fdafd92019-09-09 17:52:16 +0200556 changeId = jsonChange['id']
Mykyta Karpincfbbbd82021-11-03 16:58:07 +0000557 if(!forceUpdate){
558 def ref = jsonChange['currentPatchSet']['ref']
559 def res
560 dir(repo){
561 sshagent (credentials: [creds]){
562 res = common.shCmdStatus("git fetch origin ${ref} && git diff --quiet --exit-code FETCH_HEAD")["status"]
563 }
564 }
565 if (res == 0){
566 common.infoMsg("Current patch set ${ref} is up to date, no need to update")
567 return
568 }
569 }
Alexandr Lovtsov4fdafd92019-09-09 17:52:16 +0200570 }
571 commitMessage = genCommitMessage(repo, comment, changeId, change_id_seed)
Mykyta Karpin28695382022-04-22 16:16:44 +0300572 commitGitChanges(repo, commitMessage, changeAuthorEmail, changeAuthorName, false, amend)
Alexandr Lovtsov4fdafd92019-09-09 17:52:16 +0200573 dir(repo){
574 commit = getGitCommit()
575 }
Sergey Kolekonov02d70022023-03-28 14:22:36 +0600576 pushForReview(repo, creds, commit, branch, topic, remote)
Alexandr Lovtsov4fdafd92019-09-09 17:52:16 +0200577}
Mykyta Karpin28695382022-04-22 16:16:44 +0300578
579/**
580 * Create new working branch for repo from patch if it exists
581 *
582 * @param params Map of parameters to customize commit
583 * - gerritAuth A map containing information about Gerrit. Should include HOST, PORT and USER
584 * - credentialsId Jenkins credentials id for gerrit
585 * - repo Local directory with repository
586 * - branch Name of the branch for uploading
587 * - topic Topic of the change
588 * - project Gerrit project to search in for gerrit change request
589 * - status Change request's status to search for
590 */
591def createGitBranchFromRef(Map params) {
592 def gerrit = new com.mirantis.mk.Gerrit()
593 def common = new com.mirantis.mk.Common()
594
595 def auth = params['gerritAuth']
596 def creds = params['credentialsId']
597 def repo = params['repo']
598 def branch = params['branch']
599 def topic = params['topic']
600 def project = params['project']
601 def status = params.get('status', 'open')
602 def localBranch = "branch_${topic}"
603 def jsonChange = [:]
604
605 def changeParams = ['owner': auth['USER'], 'status': status, 'project': project, 'branch': branch, 'topic': topic]
606 def gerritChange = gerrit.findGerritChange(creds, auth, changeParams, '--current-patch-set')
607 if (gerritChange) {
608 jsonChange = readJSON text: gerritChange
609 def ref = jsonChange['currentPatchSet']['ref']
610 changeId = jsonChange['id']
611 dir(repo){
612 sshagent (credentials: [creds]){
613 common.shCmdStatus("git fetch origin ${ref} && git checkout -b ${localBranch} FETCH_HEAD")
614 }
615 }
616 }
617 else {
618 createGitBranch(repo, localBranch)
619 }
620 return jsonChange
621}
Oleksandr Kononenkof133a0e2024-02-21 17:04:41 +0200622
623/**
624 * Return array with branches that contains specific tag
625 *
626 * @param path Path to the git repository
627 * @param tag search tag
628 */
629def getBranchesContainsTag(String path, String tag) {
630 List result = []
631 dir(path) {
632 def gitResult = sh (
633 script: "git branch --all --contains tags/${tag}",
634 returnStdout: true
635 )
636 for (line in gitResult.split('\n')) {
637 if (! line.contains("HEAD detached")) {
638 result.add(line.trim().replaceAll('remotes/origin/', ''))
639 }
640 }
641 }
642 return result
643}