blob: 51caed900108e48d1eb06ae8df9beef473ab0447 [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)
Sergey Kolekonovba203982016-12-21 18:32:17 +040019 */
Jakub Josef61f29e62017-03-08 16:42:06 +010020def checkoutGitRepository(path, url, branch, credentialsId = null, poll = true, timeout = 10, depth = 0){
Sergey Kolekonovba203982016-12-21 18:32:17 +040021 dir(path) {
Jakub Josef6fa8cb12017-03-06 18:20:08 +010022 checkout(
23 changelog:true,
24 poll: poll,
25 scm: [
26 $class: 'GitSCM',
27 branches: [[name: "*/${branch}"]],
28 doGenerateSubmoduleConfigurations: false,
29 extensions: [
Jakub Josef61f29e62017-03-08 16:42:06 +010030 [$class: 'CheckoutOption', timeout: timeout],
Jakub Josef1589f9a2017-03-08 17:41:21 +010031 [$class: 'CloneOption', depth: depth, noTags: false, reference: '', shallow: depth > 0, timeout: timeout]],
Jakub Josef6fa8cb12017-03-06 18:20:08 +010032 submoduleCfg: [],
33 userRemoteConfigs: [[url: url, credentialsId: credentialsId]]]
34 )
Sergey Kolekonovba203982016-12-21 18:32:17 +040035 sh(returnStdout: true, script: 'git rev-parse HEAD').trim()
36 }
37}
38
39/**
40 * Parse HEAD of current directory and return commit hash
41 */
42def getGitCommit() {
43 git_commit = sh (
44 script: 'git rev-parse HEAD',
45 returnStdout: true
46 ).trim()
47 return git_commit
48}
49
50/**
Ales Komarekfb7cbcb2017-02-24 14:02:03 +010051 * Change actual working branch of repo
52 *
53 * @param path Path to the git repository
54 * @param branch Branch desired to switch to
55 */
56def changeGitBranch(path, branch) {
57 dir(path) {
58 git_cmd = sh (
Leontii Istominb4f4ae12018-02-27 20:25:43 +010059 script: "git checkout ${branch}",
Ales Komarekfb7cbcb2017-02-24 14:02:03 +010060 returnStdout: true
61 ).trim()
62 }
63 return git_cmd
64}
65
66/**
Ales Komarekc3a8b972017-03-24 13:57:25 +010067 * Get remote URL
68 *
69 * @param name Name of remote (default any)
70 * @param type Type (fetch or push, default fetch)
71 */
72def getGitRemote(name = '', type = 'fetch') {
73 gitRemote = sh (
74 script: "git remote -v | grep '${name}' | grep ${type} | awk '{print \$2}' | head -1",
75 returnStdout: true
76 ).trim()
77 return gitRemote
78}
79
80/**
81 * Create new working branch for repo
82 *
83 * @param path Path to the git repository
84 * @param branch Branch desired to switch to
85 */
86def createGitBranch(path, branch) {
87 def git_cmd
88 dir(path) {
89 git_cmd = sh (
90 script: "git checkout -b ${branch}",
91 returnStdout: true
92 ).trim()
93 }
94 return git_cmd
95}
96
97/**
Ales Komarekfb7cbcb2017-02-24 14:02:03 +010098 * Commit changes to the git repo
99 *
100 * @param path Path to the git repository
101 * @param message A commit message
102 */
Tomáš Kukrál15f94c32017-07-19 17:57:04 +0200103def commitGitChanges(path, message, gitEmail='jenkins@localhost', gitName='jenkins-slave') {
Ales Komarekc3a8b972017-03-24 13:57:25 +0100104 def git_cmd
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100105 dir(path) {
Tomáš Kukrál15f94c32017-07-19 17:57:04 +0200106 sh "git config --global user.email '${gitEmail}'"
107 sh "git config --global user.name '${gitName}'"
Tomáš Kukráldf7bebc2017-03-27 15:12:43 +0200108
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100109 sh(
110 script: 'git add -A',
111 returnStdout: true
112 ).trim()
113 git_cmd = sh(
114 script: "git commit -m '${message}'",
115 returnStdout: true
116 ).trim()
117 }
118 return git_cmd
119}
120
121
122/**
123 * Push git changes to remote repo
124 *
Ales Komarekc3a8b972017-03-24 13:57:25 +0100125 * @param path Path to the local git repository
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100126 * @param branch Branch on the remote git repository
127 * @param remote Name of the remote repository
Ales Komarekc3a8b972017-03-24 13:57:25 +0100128 * @param credentialsId Credentials with write permissions
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100129 */
Ales Komarekc3a8b972017-03-24 13:57:25 +0100130def pushGitChanges(path, branch = 'master', remote = 'origin', credentialsId = null) {
131 def ssh = new com.mirantis.mk.Ssh()
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100132 dir(path) {
Ales Komarekc3a8b972017-03-24 13:57:25 +0100133 if (credentialsId == null) {
134 sh script: "git push ${remote} ${branch}"
135 }
136 else {
137 ssh.prepareSshAgentKey(credentialsId)
138 ssh.runSshAgentCommand("git push ${remote} ${branch}")
139 }
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100140 }
Ales Komarekfb7cbcb2017-02-24 14:02:03 +0100141}
142
Ales Komarekc3a8b972017-03-24 13:57:25 +0100143
Sergey Kolekonovba203982016-12-21 18:32:17 +0400144/**
Filip Pytloun49d66302017-03-06 10:26:22 +0100145 * Mirror git repository, merge target changes (downstream) on top of source
146 * (upstream) and push target or both if pushSource is true
147 *
148 * @param sourceUrl Source git repository
149 * @param targetUrl Target git repository
Ivan Berezovskiy82d05452019-07-18 16:15:26 +0400150 * @param credentialsId Credentials id to use for accessing target repositories
Filip Pytloun49d66302017-03-06 10:26:22 +0100151 * @param branches List or comma-separated string of branches to sync
152 * @param followTags Mirror tags
153 * @param pushSource Push back into source branch, resulting in 2-way sync
154 * @param pushSourceTags Push target tags into source or skip pushing tags
155 * @param gitEmail Email for creation of merge commits
156 * @param gitName Name for creation of merge commits
Sergey Kolekonovba203982016-12-21 18:32:17 +0400157 */
Ivan Berezovskiy82d05452019-07-18 16:15:26 +0400158def 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 +0200159 def common = new com.mirantis.mk.Common()
160 def ssh = new com.mirantis.mk.Ssh()
Sergey Kolekonovba203982016-12-21 18:32:17 +0400161 if (branches instanceof String) {
162 branches = branches.tokenize(',')
163 }
Ivan Berezovskiy82d05452019-07-18 16:15:26 +0400164 // If both source and target repos are secured and accessible via http/https,
165 // we need to switch GIT_ASKPASS value when running git commands
166 def sourceAskPass
167 def targetAskPass
Sergey Kolekonovba203982016-12-21 18:32:17 +0400168
Ivan Berezovskiy82d05452019-07-18 16:15:26 +0400169 def sshCreds = common.getCredentialsById(credentialsId, 'sshKey') // True if found
170 if (sshCreds) {
171 ssh.prepareSshAgentKey(credentialsId)
172 ssh.ensureKnownHosts(targetUrl)
173 sh "git config user.name '${gitName}'"
174 } else {
175 withCredentials([[$class : 'UsernamePasswordMultiBinding',
176 credentialsId : credentialsId,
177 passwordVariable: 'GIT_PASSWORD',
178 usernameVariable: 'GIT_USERNAME']]) {
179 sh """
180 set +x
181 git config --global credential.${targetUrl}.username \${GIT_USERNAME}
182 echo "echo \${GIT_PASSWORD}" > ${WORKSPACE}/${credentialsId}_askpass.sh
183 chmod +x ${WORKSPACE}/${credentialsId}_askpass.sh
184 git config user.name \${GIT_USERNAME}
185 """
186 sourceAskPass = env.GIT_ASKPASS ?: ''
187 targetAskPass = "${WORKSPACE}/${credentialsId}_askpass.sh"
188 }
189 }
Filip Pytloun49d66302017-03-06 10:26:22 +0100190 sh "git config user.email '${gitEmail}'"
Filip Pytloun49d66302017-03-06 10:26:22 +0100191
Jakub Josef1caa7ae2017-08-21 16:39:00 +0200192 def remoteExistence = sh(script: "git remote -v | grep ${TARGET_URL} | grep target", returnStatus: true)
Ivan Berezovskiy82d05452019-07-18 16:15:26 +0400193 if(remoteExistence == 0) {
194 // silently try to remove target
195 sh(script: "git remote remove target", returnStatus: true)
Jakub Josef1caa7ae2017-08-21 16:39:00 +0200196 }
Ivan Berezovskiy82d05452019-07-18 16:15:26 +0400197 sh("git remote add target ${TARGET_URL}")
198 if (sshCreds) {
199 ssh.agentSh "git remote update --prune"
200 } else {
201 env.GIT_ASKPASS = sourceAskPass
202 sh "git remote update ${sourceRemote} --prune"
203 env.GIT_ASKPASS = targetAskPass
204 sh "git remote update target --prune"
205 }
Filip Pytloun49d66302017-03-06 10:26:22 +0100206
Sergey Kolekonovba203982016-12-21 18:32:17 +0400207 for (i=0; i < branches.size; i++) {
208 branch = branches[i]
Jakub Josef668dc2b2017-06-19 16:55:26 +0200209 sh "git branch | grep ${branch} || git checkout -b ${branch}"
210 def resetResult = sh(script: "git checkout ${branch} && git reset --hard origin/${branch}", returnStatus: true)
211 if(resetResult != 0){
212 common.warningMsg("Cannot reset to origin/${branch} for perform git mirror, trying to reset from target/${branch}")
213 resetResult = sh(script: "git checkout ${branch} && git reset --hard target/${branch}", returnStatus: true)
214 if(resetResult != 0){
215 throw new Exception("Cannot reset even to target/${branch}, git mirroring failed!")
216 }
217 }
Sergey Kolekonovba203982016-12-21 18:32:17 +0400218
Sergey Kolekonovba203982016-12-21 18:32:17 +0400219 sh "git ls-tree target/${branch} && git merge --no-edit --ff target/${branch} || echo 'Target repository is empty, skipping merge'"
220 followTagsArg = followTags ? "--follow-tags" : ""
Ivan Berezovskiy82d05452019-07-18 16:15:26 +0400221 if (sshCreds) {
222 ssh.agentSh "git push ${followTagsArg} target HEAD:${branch}"
223 } else {
224 sh "git push ${followTagsArg} target HEAD:${branch}"
225 }
Filip Pytloun49d66302017-03-06 10:26:22 +0100226
227 if (pushSource == true) {
228 followTagsArg = followTags && pushSourceTags ? "--follow-tags" : ""
Ivan Berezovskiy82d05452019-07-18 16:15:26 +0400229 if (sshCreds) {
230 ssh.agentSh "git push ${followTagsArg} origin HEAD:${branch}"
231 } else {
232 sh "git push ${followTagsArg} origin HEAD:${branch}"
233 }
Filip Pytloun49d66302017-03-06 10:26:22 +0100234 }
235 }
Filip Pytloun49d66302017-03-06 10:26:22 +0100236 if (followTags == true) {
Ivan Berezovskiy82d05452019-07-18 16:15:26 +0400237 if (sshCreds) {
238 ssh.agentSh "git push -f target --tags"
239 } else {
240 sh "git push -f target --tags"
241 }
Filip Pytloun49d66302017-03-06 10:26:22 +0100242
243 if (pushSourceTags == true) {
Ivan Berezovskiy82d05452019-07-18 16:15:26 +0400244 if (sshCreds) {
245 ssh.agentSh "git push -f origin --tags"
246 } else {
247 sh "git push -f origin --tags"
248 }
Filip Pytloun49d66302017-03-06 10:26:22 +0100249 }
Sergey Kolekonovba203982016-12-21 18:32:17 +0400250 }
Jakub Josefecf8b452017-04-20 13:34:29 +0200251 sh "git remote rm target"
Ivan Berezovskiy82d05452019-07-18 16:15:26 +0400252 if (!sshCreds) {
253 sh "set +x; rm -f ${targetAskPass}"
254 sh "git config --global --unset credential.${targetUrl}.username"
255 }
Sergey Kolekonovba203982016-12-21 18:32:17 +0400256}
Martin Polreichb4f31ea2019-03-12 16:39:25 +0100257
258
259/**
260 * Return all branches for the defined git repository that match the matcher.
261 *
262 * @param repoUrl URL of git repository
263 * @param branchMatcher matcher to filter out the branches (If '' or '*', returns all branches without filtering)
264 * @return branchesList list of branches
265 */
266
267def getBranchesForGitRepo(repoUrl, branchMatcher = ''){
268
269 if (branchMatcher.equals("*")) {
270 branchMatcher = ''
271 }
272 branchesList = sh (
273 script: "git ls-remote --heads ${repoUrl} | cut -f2 | grep -e '${branchMatcher}' | sed 's/refs\\/heads\\///g'",
274 returnStdout: true
275 ).trim()
276 return branchesList.tokenize('\n')
277}