We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
#4268 breaks Buildbot on Git step workers with different filesystem layout than the master
Resolving `abspath` in 33682e89057349fed6b72ca7613944b2687633f9 on the Buildbot master does not work in scenarios that the Buildbot worker is using a different working directory than the master.

[Master WORKDIR: /var/lib/buildbot](https://github.com/buildbot/buildbot/blob/master/master/Dockerfile#L93)
[Worker WORKDIR: /buildbot](https://github.com/buildbot/buildbot/blob/master/worker/Dockerfile#L51)

This was rather tricky to track down and I'm going to revert this commit locally and look at fixing it in a subsequent PR.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `master/buildbot/util/git.py`
Content:
```
1 # This file is part of Buildbot.  Buildbot is free software: you can
2 # redistribute it and/or modify it under the terms of the GNU General Public
3 # License as published by the Free Software Foundation, version 2.
4 #
5 # This program is distributed in the hope that it will be useful, but WITHOUT
6 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
7 # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
8 # details.
9 #
10 # You should have received a copy of the GNU General Public License along with
11 # this program; if not, write to the Free Software Foundation, Inc., 51
12 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
13 #
14 # Copyright Buildbot Team Members
15 
16 from __future__ import absolute_import
17 from __future__ import print_function
18 from future.utils import iteritems
19 
20 from distutils.version import LooseVersion
21 
22 from twisted.internet import defer
23 from twisted.python import log
24 
25 from buildbot import config as bbconfig
26 from buildbot.process import buildstep
27 from buildbot.process import remotecommand
28 from buildbot.process.properties import Properties
29 
30 RC_SUCCESS = 0
31 
32 
33 def getSshCommand(keyPath, knownHostsPath):
34     command = ['ssh']
35     if keyPath is not None:
36         command += ['-i', '"{0}"'.format(keyPath)]
37     if knownHostsPath is not None:
38         command += ['-o', '"UserKnownHostsFile={0}"'.format(knownHostsPath)]
39     return ' '.join(command)
40 
41 
42 class GitMixin(object):
43 
44     def setupGit(self):
45         self.gitInstalled = False
46         self.supportsBranch = False
47         self.supportsSubmoduleForce = False
48         self.supportsSubmoduleCheckout = False
49         self.supportsSshPrivateKeyAsEnvOption = False
50         self.supportsSshPrivateKeyAsConfigOption = False
51 
52     def parseGitFeatures(self, version_stdout):
53 
54         if 'git' not in version_stdout:
55             return
56 
57         try:
58             version = version_stdout.strip().split(' ')[2]
59         except IndexError:
60             return
61 
62         self.gitInstalled = True
63         if LooseVersion(version) >= LooseVersion("1.6.5"):
64             self.supportsBranch = True
65         if LooseVersion(version) >= LooseVersion("1.7.6"):
66             self.supportsSubmoduleForce = True
67         if LooseVersion(version) >= LooseVersion("1.7.8"):
68             self.supportsSubmoduleCheckout = True
69         if LooseVersion(version) >= LooseVersion("2.3.0"):
70             self.supportsSshPrivateKeyAsEnvOption = True
71         if LooseVersion(version) >= LooseVersion("2.10.0"):
72             self.supportsSshPrivateKeyAsConfigOption = True
73 
74     def adjustCommandParamsForSshPrivateKey(self, command, env,
75                                             keyPath, sshWrapperPath=None,
76                                             knownHostsPath=None):
77         ssh_command = getSshCommand(keyPath, knownHostsPath)
78 
79         if self.supportsSshPrivateKeyAsConfigOption:
80             command.append('-c')
81             command.append('core.sshCommand={0}'.format(ssh_command))
82         elif self.supportsSshPrivateKeyAsEnvOption:
83             env['GIT_SSH_COMMAND'] = ssh_command
84         else:
85             if sshWrapperPath is None:
86                 raise Exception('Only SSH wrapper script is supported but path '
87                                 'not given')
88             env['GIT_SSH'] = sshWrapperPath
89 
90 
91 def getSshWrapperScriptContents(keyPath, knownHostsPath=None):
92     ssh_command = getSshCommand(keyPath, knownHostsPath)
93 
94     # note that this works on windows if using git with MINGW embedded.
95     return '#!/bin/sh\n{0} "$@"\n'.format(ssh_command)
96 
97 
98 def getSshKnownHostsContents(hostKey):
99     host_name = '*'
100     return '{0} {1}'.format(host_name, hostKey)
101 
102 
103 class GitStepMixin(GitMixin):
104 
105     def setupGitStep(self):
106         self.didDownloadSshPrivateKey = False
107         self.setupGit()
108 
109         if self.sshHostKey is not None and self.sshPrivateKey is None:
110             bbconfig.error('Git: sshPrivateKey must be provided in order '
111                            'use sshHostKey')
112             self.sshPrivateKey = None
113 
114         if not self.repourl:
115             bbconfig.error("Git: must provide repourl.")
116 
117     def _isSshPrivateKeyNeededForGitCommand(self, command):
118         if not command or self.sshPrivateKey is None:
119             return False
120 
121         gitCommandsThatNeedSshKey = [
122             'clone', 'submodule', 'fetch', 'push'
123         ]
124         if command[0] in gitCommandsThatNeedSshKey:
125             return True
126         return False
127 
128     def _getSshDataPath(self):
129         # we can't use the workdir for temporary ssh-related files, because
130         # it's needed when cloning repositories and git does not like the
131         # destination directory being non-empty. We have to use separate
132         # temporary directory for that data to ensure the confidentiality of it.
133         # So instead of
134         # '{path}/{to}/{workdir}/.buildbot-ssh-key' we put the key at
135         # '{path}/{to}/.{workdir}.buildbot/ssh-key'.
136 
137         # basename and dirname interpret the last element being empty for paths
138         # ending with a slash
139         path_module = self.build.path_module
140 
141         workdir = self._getSshDataWorkDir().rstrip('/\\')
142         parent_path = path_module.dirname(workdir)
143 
144         basename = '.{0}.buildbot'.format(path_module.basename(workdir))
145         return path_module.join(parent_path, basename)
146 
147     def _getSshPrivateKeyPath(self):
148         return self.build.path_module.join(self._getSshDataPath(), 'ssh-key')
149 
150     def _getSshHostKeyPath(self):
151         return self.build.path_module.join(self._getSshDataPath(), 'ssh-known-hosts')
152 
153     def _getSshWrapperScriptPath(self):
154         return self.build.path_module.join(self._getSshDataPath(), 'ssh-wrapper.sh')
155 
156     def _getSshWrapperScript(self):
157         rel_key_path = self.build.path_module.relpath(
158                 self._getSshPrivateKeyPath(), self._getSshDataWorkDir())
159 
160         return getSshWrapperScriptContents(rel_key_path)
161 
162     def _adjustCommandParamsForSshPrivateKey(self, full_command, full_env):
163 
164         rel_key_path = self.build.path_module.relpath(
165                 self._getSshPrivateKeyPath(), self.workdir)
166         rel_ssh_wrapper_path = self.build.path_module.relpath(
167                 self._getSshWrapperScriptPath(), self.workdir)
168         rel_host_key_path = None
169         if self.sshHostKey is not None:
170             rel_host_key_path = self.build.path_module.relpath(
171                     self._getSshHostKeyPath(), self.workdir)
172 
173         self.adjustCommandParamsForSshPrivateKey(full_command, full_env,
174                                                  rel_key_path,
175                                                  rel_ssh_wrapper_path,
176                                                  rel_host_key_path)
177 
178     @defer.inlineCallbacks
179     def _dovccmd(self, command, abandonOnFailure=True, collectStdout=False, initialStdin=None):
180         full_command = ['git']
181         full_env = self.env.copy() if self.env else {}
182 
183         if self.config is not None:
184             for name, value in iteritems(self.config):
185                 full_command.append('-c')
186                 full_command.append('%s=%s' % (name, value))
187 
188         if self._isSshPrivateKeyNeededForGitCommand(command):
189             self._adjustCommandParamsForSshPrivateKey(full_command, full_env)
190 
191         full_command.extend(command)
192 
193         # check for the interruptSignal flag
194         sigtermTime = None
195         interruptSignal = None
196 
197         # If possible prefer to send a SIGTERM to git before we send a SIGKILL.
198         # If we send a SIGKILL, git is prone to leaving around stale lockfiles.
199         # By priming it with a SIGTERM first we can ensure that it has a chance to shut-down gracefully
200         # before getting terminated
201         if not self.workerVersionIsOlderThan("shell", "2.16"):
202             # git should shut-down quickly on SIGTERM.  If it doesn't don't let it
203             # stick around for too long because this is on top of any timeout
204             # we have hit.
205             sigtermTime = 1
206         else:
207             # Since sigtermTime is unavailable try to just use SIGTERM by itself instead of
208             # killing.  This should be safe.
209             if self.workerVersionIsOlderThan("shell", "2.15"):
210                 log.msg(
211                     "NOTE: worker does not allow master to specify "
212                     "interruptSignal. This may leave a stale lockfile around "
213                     "if the command is interrupted/times out\n")
214             else:
215                 interruptSignal = 'TERM'
216 
217         cmd = remotecommand.RemoteShellCommand(self.workdir,
218                                                full_command,
219                                                env=full_env,
220                                                logEnviron=self.logEnviron,
221                                                timeout=self.timeout,
222                                                sigtermTime=sigtermTime,
223                                                interruptSignal=interruptSignal,
224                                                collectStdout=collectStdout,
225                                                initialStdin=initialStdin)
226         cmd.useLog(self.stdio_log, False)
227         yield self.runCommand(cmd)
228 
229         if abandonOnFailure and cmd.didFail():
230             log.msg("Source step failed while running command %s" % cmd)
231             raise buildstep.BuildStepFailed()
232         if collectStdout:
233             defer.returnValue(cmd.stdout)
234             return
235         defer.returnValue(cmd.rc)
236 
237     @defer.inlineCallbacks
238     def checkBranchSupport(self):
239         stdout = yield self._dovccmd(['--version'], collectStdout=True)
240 
241         self.parseGitFeatures(stdout)
242 
243         defer.returnValue(self.gitInstalled)
244 
245     @defer.inlineCallbacks
246     def _downloadSshPrivateKeyIfNeeded(self):
247         if self.sshPrivateKey is None:
248             defer.returnValue(RC_SUCCESS)
249 
250         p = Properties()
251         p.master = self.master
252         private_key = yield p.render(self.sshPrivateKey)
253         host_key = yield p.render(self.sshHostKey)
254 
255         # not using self.workdir because it may be changed depending on step
256         # options
257         workdir = self._getSshDataWorkDir()
258 
259         rel_key_path = self.build.path_module.relpath(
260                 self._getSshPrivateKeyPath(), workdir)
261         rel_host_key_path = self.build.path_module.relpath(
262                 self._getSshHostKeyPath(), workdir)
263         rel_wrapper_script_path = self.build.path_module.relpath(
264                 self._getSshWrapperScriptPath(), workdir)
265 
266         yield self.runMkdir(self._getSshDataPath())
267 
268         if not self.supportsSshPrivateKeyAsEnvOption:
269             yield self.downloadFileContentToWorker(rel_wrapper_script_path,
270                                                    self._getSshWrapperScript(),
271                                                    workdir=workdir, mode=0o700)
272 
273         yield self.downloadFileContentToWorker(rel_key_path, private_key,
274                                                workdir=workdir, mode=0o400)
275 
276         if self.sshHostKey is not None:
277             known_hosts_contents = getSshKnownHostsContents(host_key)
278             yield self.downloadFileContentToWorker(rel_host_key_path,
279                                                    known_hosts_contents,
280                                                    workdir=workdir, mode=0o400)
281 
282         self.didDownloadSshPrivateKey = True
283         defer.returnValue(RC_SUCCESS)
284 
285     @defer.inlineCallbacks
286     def _removeSshPrivateKeyIfNeeded(self):
287         if not self.didDownloadSshPrivateKey:
288             defer.returnValue(RC_SUCCESS)
289 
290         yield self.runRmdir(self._getSshDataPath())
291         defer.returnValue(RC_SUCCESS)
292
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/master/buildbot/util/git.py b/master/buildbot/util/git.py
--- a/master/buildbot/util/git.py
+++ b/master/buildbot/util/git.py
@@ -139,7 +139,11 @@
         path_module = self.build.path_module
 
         workdir = self._getSshDataWorkDir().rstrip('/\\')
-        parent_path = path_module.dirname(workdir)
+        if path_module.isabs(workdir):
+            parent_path = path_module.dirname(workdir)
+        else:
+            parent_path = path_module.join(self.worker.worker_basedir,
+                                           path_module.dirname(workdir))
 
         basename = '.{0}.buildbot'.format(path_module.basename(workdir))
         return path_module.join(parent_path, basename)
@@ -154,26 +158,19 @@
         return self.build.path_module.join(self._getSshDataPath(), 'ssh-wrapper.sh')
 
     def _getSshWrapperScript(self):
-        rel_key_path = self.build.path_module.relpath(
-                self._getSshPrivateKeyPath(), self._getSshDataWorkDir())
-
-        return getSshWrapperScriptContents(rel_key_path)
+        return getSshWrapperScriptContents(self._getSshPrivateKeyPath)
 
     def _adjustCommandParamsForSshPrivateKey(self, full_command, full_env):
 
-        rel_key_path = self.build.path_module.relpath(
-                self._getSshPrivateKeyPath(), self.workdir)
-        rel_ssh_wrapper_path = self.build.path_module.relpath(
-                self._getSshWrapperScriptPath(), self.workdir)
-        rel_host_key_path = None
+        key_path = self._getSshPrivateKeyPath()
+        ssh_wrapper_path = self._getSshWrapperScriptPath()
+        host_key_path = None
         if self.sshHostKey is not None:
-            rel_host_key_path = self.build.path_module.relpath(
-                    self._getSshHostKeyPath(), self.workdir)
+            host_key_path = self._getSshHostKeyPath()
 
         self.adjustCommandParamsForSshPrivateKey(full_command, full_env,
-                                                 rel_key_path,
-                                                 rel_ssh_wrapper_path,
-                                                 rel_host_key_path)
+                                                 key_path, ssh_wrapper_path,
+                                                 host_key_path)
 
     @defer.inlineCallbacks
     def _dovccmd(self, command, abandonOnFailure=True, collectStdout=False, initialStdin=None):
@@ -256,26 +253,20 @@
         # options
         workdir = self._getSshDataWorkDir()
 
-        rel_key_path = self.build.path_module.relpath(
-                self._getSshPrivateKeyPath(), workdir)
-        rel_host_key_path = self.build.path_module.relpath(
-                self._getSshHostKeyPath(), workdir)
-        rel_wrapper_script_path = self.build.path_module.relpath(
-                self._getSshWrapperScriptPath(), workdir)
-
         yield self.runMkdir(self._getSshDataPath())
 
         if not self.supportsSshPrivateKeyAsEnvOption:
-            yield self.downloadFileContentToWorker(rel_wrapper_script_path,
+            yield self.downloadFileContentToWorker(self._getSshWrapperScriptPath(),
                                                    self._getSshWrapperScript(),
                                                    workdir=workdir, mode=0o700)
 
-        yield self.downloadFileContentToWorker(rel_key_path, private_key,
+        yield self.downloadFileContentToWorker(self._getSshPrivateKeyPath(),
+                                               private_key,
                                                workdir=workdir, mode=0o400)
 
         if self.sshHostKey is not None:
             known_hosts_contents = getSshKnownHostsContents(host_key)
-            yield self.downloadFileContentToWorker(rel_host_key_path,
+            yield self.downloadFileContentToWorker(self._getSshHostKeyPath(),
                                                    known_hosts_contents,
                                                    workdir=workdir, mode=0o400)

{"golden_diff": "diff --git a/master/buildbot/util/git.py b/master/buildbot/util/git.py\n--- a/master/buildbot/util/git.py\n+++ b/master/buildbot/util/git.py\n@@ -139,7 +139,11 @@\n         path_module = self.build.path_module\n \n         workdir = self._getSshDataWorkDir().rstrip('/\\\\')\n-        parent_path = path_module.dirname(workdir)\n+        if path_module.isabs(workdir):\n+            parent_path = path_module.dirname(workdir)\n+        else:\n+            parent_path = path_module.join(self.worker.worker_basedir,\n+                                           path_module.dirname(workdir))\n \n         basename = '.{0}.buildbot'.format(path_module.basename(workdir))\n         return path_module.join(parent_path, basename)\n@@ -154,26 +158,19 @@\n         return self.build.path_module.join(self._getSshDataPath(), 'ssh-wrapper.sh')\n \n     def _getSshWrapperScript(self):\n-        rel_key_path = self.build.path_module.relpath(\n-                self._getSshPrivateKeyPath(), self._getSshDataWorkDir())\n-\n-        return getSshWrapperScriptContents(rel_key_path)\n+        return getSshWrapperScriptContents(self._getSshPrivateKeyPath)\n \n     def _adjustCommandParamsForSshPrivateKey(self, full_command, full_env):\n \n-        rel_key_path = self.build.path_module.relpath(\n-                self._getSshPrivateKeyPath(), self.workdir)\n-        rel_ssh_wrapper_path = self.build.path_module.relpath(\n-                self._getSshWrapperScriptPath(), self.workdir)\n-        rel_host_key_path = None\n+        key_path = self._getSshPrivateKeyPath()\n+        ssh_wrapper_path = self._getSshWrapperScriptPath()\n+        host_key_path = None\n         if self.sshHostKey is not None:\n-            rel_host_key_path = self.build.path_module.relpath(\n-                    self._getSshHostKeyPath(), self.workdir)\n+            host_key_path = self._getSshHostKeyPath()\n \n         self.adjustCommandParamsForSshPrivateKey(full_command, full_env,\n-                                                 rel_key_path,\n-                                                 rel_ssh_wrapper_path,\n-                                                 rel_host_key_path)\n+                                                 key_path, ssh_wrapper_path,\n+                                                 host_key_path)\n \n     @defer.inlineCallbacks\n     def _dovccmd(self, command, abandonOnFailure=True, collectStdout=False, initialStdin=None):\n@@ -256,26 +253,20 @@\n         # options\n         workdir = self._getSshDataWorkDir()\n \n-        rel_key_path = self.build.path_module.relpath(\n-                self._getSshPrivateKeyPath(), workdir)\n-        rel_host_key_path = self.build.path_module.relpath(\n-                self._getSshHostKeyPath(), workdir)\n-        rel_wrapper_script_path = self.build.path_module.relpath(\n-                self._getSshWrapperScriptPath(), workdir)\n-\n         yield self.runMkdir(self._getSshDataPath())\n \n         if not self.supportsSshPrivateKeyAsEnvOption:\n-            yield self.downloadFileContentToWorker(rel_wrapper_script_path,\n+            yield self.downloadFileContentToWorker(self._getSshWrapperScriptPath(),\n                                                    self._getSshWrapperScript(),\n                                                    workdir=workdir, mode=0o700)\n \n-        yield self.downloadFileContentToWorker(rel_key_path, private_key,\n+        yield self.downloadFileContentToWorker(self._getSshPrivateKeyPath(),\n+                                               private_key,\n                                                workdir=workdir, mode=0o400)\n \n         if self.sshHostKey is not None:\n             known_hosts_contents = getSshKnownHostsContents(host_key)\n-            yield self.downloadFileContentToWorker(rel_host_key_path,\n+            yield self.downloadFileContentToWorker(self._getSshHostKeyPath(),\n                                                    known_hosts_contents,\n                                                    workdir=workdir, mode=0o400)\n", "issue": "#4268 breaks Buildbot on Git step workers with different filesystem layout than the master\nResolving `abspath` in 33682e89057349fed6b72ca7613944b2687633f9 on the Buildbot master does not work in scenarios that the Buildbot worker is using a different working directory than the master.\r\n\r\n[Master WORKDIR: /var/lib/buildbot](https://github.com/buildbot/buildbot/blob/master/master/Dockerfile#L93)\r\n[Worker WORKDIR: /buildbot](https://github.com/buildbot/buildbot/blob/master/worker/Dockerfile#L51)\r\n\r\nThis was rather tricky to track down and I'm going to revert this commit locally and look at fixing it in a subsequent PR.\n", "before_files": [{"content": "# This file is part of Buildbot.  Buildbot is free software: you can\n# redistribute it and/or modify it under the terms of the GNU General Public\n# License as published by the Free Software Foundation, version 2.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program; if not, write to the Free Software Foundation, Inc., 51\n# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n#\n# Copyright Buildbot Team Members\n\nfrom __future__ import absolute_import\nfrom __future__ import print_function\nfrom future.utils import iteritems\n\nfrom distutils.version import LooseVersion\n\nfrom twisted.internet import defer\nfrom twisted.python import log\n\nfrom buildbot import config as bbconfig\nfrom buildbot.process import buildstep\nfrom buildbot.process import remotecommand\nfrom buildbot.process.properties import Properties\n\nRC_SUCCESS = 0\n\n\ndef getSshCommand(keyPath, knownHostsPath):\n    command = ['ssh']\n    if keyPath is not None:\n        command += ['-i', '\"{0}\"'.format(keyPath)]\n    if knownHostsPath is not None:\n        command += ['-o', '\"UserKnownHostsFile={0}\"'.format(knownHostsPath)]\n    return ' '.join(command)\n\n\nclass GitMixin(object):\n\n    def setupGit(self):\n        self.gitInstalled = False\n        self.supportsBranch = False\n        self.supportsSubmoduleForce = False\n        self.supportsSubmoduleCheckout = False\n        self.supportsSshPrivateKeyAsEnvOption = False\n        self.supportsSshPrivateKeyAsConfigOption = False\n\n    def parseGitFeatures(self, version_stdout):\n\n        if 'git' not in version_stdout:\n            return\n\n        try:\n            version = version_stdout.strip().split(' ')[2]\n        except IndexError:\n            return\n\n        self.gitInstalled = True\n        if LooseVersion(version) >= LooseVersion(\"1.6.5\"):\n            self.supportsBranch = True\n        if LooseVersion(version) >= LooseVersion(\"1.7.6\"):\n            self.supportsSubmoduleForce = True\n        if LooseVersion(version) >= LooseVersion(\"1.7.8\"):\n            self.supportsSubmoduleCheckout = True\n        if LooseVersion(version) >= LooseVersion(\"2.3.0\"):\n            self.supportsSshPrivateKeyAsEnvOption = True\n        if LooseVersion(version) >= LooseVersion(\"2.10.0\"):\n            self.supportsSshPrivateKeyAsConfigOption = True\n\n    def adjustCommandParamsForSshPrivateKey(self, command, env,\n                                            keyPath, sshWrapperPath=None,\n                                            knownHostsPath=None):\n        ssh_command = getSshCommand(keyPath, knownHostsPath)\n\n        if self.supportsSshPrivateKeyAsConfigOption:\n            command.append('-c')\n            command.append('core.sshCommand={0}'.format(ssh_command))\n        elif self.supportsSshPrivateKeyAsEnvOption:\n            env['GIT_SSH_COMMAND'] = ssh_command\n        else:\n            if sshWrapperPath is None:\n                raise Exception('Only SSH wrapper script is supported but path '\n                                'not given')\n            env['GIT_SSH'] = sshWrapperPath\n\n\ndef getSshWrapperScriptContents(keyPath, knownHostsPath=None):\n    ssh_command = getSshCommand(keyPath, knownHostsPath)\n\n    # note that this works on windows if using git with MINGW embedded.\n    return '#!/bin/sh\\n{0} \"$@\"\\n'.format(ssh_command)\n\n\ndef getSshKnownHostsContents(hostKey):\n    host_name = '*'\n    return '{0} {1}'.format(host_name, hostKey)\n\n\nclass GitStepMixin(GitMixin):\n\n    def setupGitStep(self):\n        self.didDownloadSshPrivateKey = False\n        self.setupGit()\n\n        if self.sshHostKey is not None and self.sshPrivateKey is None:\n            bbconfig.error('Git: sshPrivateKey must be provided in order '\n                           'use sshHostKey')\n            self.sshPrivateKey = None\n\n        if not self.repourl:\n            bbconfig.error(\"Git: must provide repourl.\")\n\n    def _isSshPrivateKeyNeededForGitCommand(self, command):\n        if not command or self.sshPrivateKey is None:\n            return False\n\n        gitCommandsThatNeedSshKey = [\n            'clone', 'submodule', 'fetch', 'push'\n        ]\n        if command[0] in gitCommandsThatNeedSshKey:\n            return True\n        return False\n\n    def _getSshDataPath(self):\n        # we can't use the workdir for temporary ssh-related files, because\n        # it's needed when cloning repositories and git does not like the\n        # destination directory being non-empty. We have to use separate\n        # temporary directory for that data to ensure the confidentiality of it.\n        # So instead of\n        # '{path}/{to}/{workdir}/.buildbot-ssh-key' we put the key at\n        # '{path}/{to}/.{workdir}.buildbot/ssh-key'.\n\n        # basename and dirname interpret the last element being empty for paths\n        # ending with a slash\n        path_module = self.build.path_module\n\n        workdir = self._getSshDataWorkDir().rstrip('/\\\\')\n        parent_path = path_module.dirname(workdir)\n\n        basename = '.{0}.buildbot'.format(path_module.basename(workdir))\n        return path_module.join(parent_path, basename)\n\n    def _getSshPrivateKeyPath(self):\n        return self.build.path_module.join(self._getSshDataPath(), 'ssh-key')\n\n    def _getSshHostKeyPath(self):\n        return self.build.path_module.join(self._getSshDataPath(), 'ssh-known-hosts')\n\n    def _getSshWrapperScriptPath(self):\n        return self.build.path_module.join(self._getSshDataPath(), 'ssh-wrapper.sh')\n\n    def _getSshWrapperScript(self):\n        rel_key_path = self.build.path_module.relpath(\n                self._getSshPrivateKeyPath(), self._getSshDataWorkDir())\n\n        return getSshWrapperScriptContents(rel_key_path)\n\n    def _adjustCommandParamsForSshPrivateKey(self, full_command, full_env):\n\n        rel_key_path = self.build.path_module.relpath(\n                self._getSshPrivateKeyPath(), self.workdir)\n        rel_ssh_wrapper_path = self.build.path_module.relpath(\n                self._getSshWrapperScriptPath(), self.workdir)\n        rel_host_key_path = None\n        if self.sshHostKey is not None:\n            rel_host_key_path = self.build.path_module.relpath(\n                    self._getSshHostKeyPath(), self.workdir)\n\n        self.adjustCommandParamsForSshPrivateKey(full_command, full_env,\n                                                 rel_key_path,\n                                                 rel_ssh_wrapper_path,\n                                                 rel_host_key_path)\n\n    @defer.inlineCallbacks\n    def _dovccmd(self, command, abandonOnFailure=True, collectStdout=False, initialStdin=None):\n        full_command = ['git']\n        full_env = self.env.copy() if self.env else {}\n\n        if self.config is not None:\n            for name, value in iteritems(self.config):\n                full_command.append('-c')\n                full_command.append('%s=%s' % (name, value))\n\n        if self._isSshPrivateKeyNeededForGitCommand(command):\n            self._adjustCommandParamsForSshPrivateKey(full_command, full_env)\n\n        full_command.extend(command)\n\n        # check for the interruptSignal flag\n        sigtermTime = None\n        interruptSignal = None\n\n        # If possible prefer to send a SIGTERM to git before we send a SIGKILL.\n        # If we send a SIGKILL, git is prone to leaving around stale lockfiles.\n        # By priming it with a SIGTERM first we can ensure that it has a chance to shut-down gracefully\n        # before getting terminated\n        if not self.workerVersionIsOlderThan(\"shell\", \"2.16\"):\n            # git should shut-down quickly on SIGTERM.  If it doesn't don't let it\n            # stick around for too long because this is on top of any timeout\n            # we have hit.\n            sigtermTime = 1\n        else:\n            # Since sigtermTime is unavailable try to just use SIGTERM by itself instead of\n            # killing.  This should be safe.\n            if self.workerVersionIsOlderThan(\"shell\", \"2.15\"):\n                log.msg(\n                    \"NOTE: worker does not allow master to specify \"\n                    \"interruptSignal. This may leave a stale lockfile around \"\n                    \"if the command is interrupted/times out\\n\")\n            else:\n                interruptSignal = 'TERM'\n\n        cmd = remotecommand.RemoteShellCommand(self.workdir,\n                                               full_command,\n                                               env=full_env,\n                                               logEnviron=self.logEnviron,\n                                               timeout=self.timeout,\n                                               sigtermTime=sigtermTime,\n                                               interruptSignal=interruptSignal,\n                                               collectStdout=collectStdout,\n                                               initialStdin=initialStdin)\n        cmd.useLog(self.stdio_log, False)\n        yield self.runCommand(cmd)\n\n        if abandonOnFailure and cmd.didFail():\n            log.msg(\"Source step failed while running command %s\" % cmd)\n            raise buildstep.BuildStepFailed()\n        if collectStdout:\n            defer.returnValue(cmd.stdout)\n            return\n        defer.returnValue(cmd.rc)\n\n    @defer.inlineCallbacks\n    def checkBranchSupport(self):\n        stdout = yield self._dovccmd(['--version'], collectStdout=True)\n\n        self.parseGitFeatures(stdout)\n\n        defer.returnValue(self.gitInstalled)\n\n    @defer.inlineCallbacks\n    def _downloadSshPrivateKeyIfNeeded(self):\n        if self.sshPrivateKey is None:\n            defer.returnValue(RC_SUCCESS)\n\n        p = Properties()\n        p.master = self.master\n        private_key = yield p.render(self.sshPrivateKey)\n        host_key = yield p.render(self.sshHostKey)\n\n        # not using self.workdir because it may be changed depending on step\n        # options\n        workdir = self._getSshDataWorkDir()\n\n        rel_key_path = self.build.path_module.relpath(\n                self._getSshPrivateKeyPath(), workdir)\n        rel_host_key_path = self.build.path_module.relpath(\n                self._getSshHostKeyPath(), workdir)\n        rel_wrapper_script_path = self.build.path_module.relpath(\n                self._getSshWrapperScriptPath(), workdir)\n\n        yield self.runMkdir(self._getSshDataPath())\n\n        if not self.supportsSshPrivateKeyAsEnvOption:\n            yield self.downloadFileContentToWorker(rel_wrapper_script_path,\n                                                   self._getSshWrapperScript(),\n                                                   workdir=workdir, mode=0o700)\n\n        yield self.downloadFileContentToWorker(rel_key_path, private_key,\n                                               workdir=workdir, mode=0o400)\n\n        if self.sshHostKey is not None:\n            known_hosts_contents = getSshKnownHostsContents(host_key)\n            yield self.downloadFileContentToWorker(rel_host_key_path,\n                                                   known_hosts_contents,\n                                                   workdir=workdir, mode=0o400)\n\n        self.didDownloadSshPrivateKey = True\n        defer.returnValue(RC_SUCCESS)\n\n    @defer.inlineCallbacks\n    def _removeSshPrivateKeyIfNeeded(self):\n        if not self.didDownloadSshPrivateKey:\n            defer.returnValue(RC_SUCCESS)\n\n        yield self.runRmdir(self._getSshDataPath())\n        defer.returnValue(RC_SUCCESS)\n", "path": "master/buildbot/util/git.py"}], "after_files": [{"content": "# This file is part of Buildbot.  Buildbot is free software: you can\n# redistribute it and/or modify it under the terms of the GNU General Public\n# License as published by the Free Software Foundation, version 2.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program; if not, write to the Free Software Foundation, Inc., 51\n# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n#\n# Copyright Buildbot Team Members\n\nfrom __future__ import absolute_import\nfrom __future__ import print_function\nfrom future.utils import iteritems\n\nfrom distutils.version import LooseVersion\n\nfrom twisted.internet import defer\nfrom twisted.python import log\n\nfrom buildbot import config as bbconfig\nfrom buildbot.process import buildstep\nfrom buildbot.process import remotecommand\nfrom buildbot.process.properties import Properties\n\nRC_SUCCESS = 0\n\n\ndef getSshCommand(keyPath, knownHostsPath):\n    command = ['ssh']\n    if keyPath is not None:\n        command += ['-i', '\"{0}\"'.format(keyPath)]\n    if knownHostsPath is not None:\n        command += ['-o', '\"UserKnownHostsFile={0}\"'.format(knownHostsPath)]\n    return ' '.join(command)\n\n\nclass GitMixin(object):\n\n    def setupGit(self):\n        self.gitInstalled = False\n        self.supportsBranch = False\n        self.supportsSubmoduleForce = False\n        self.supportsSubmoduleCheckout = False\n        self.supportsSshPrivateKeyAsEnvOption = False\n        self.supportsSshPrivateKeyAsConfigOption = False\n\n    def parseGitFeatures(self, version_stdout):\n\n        if 'git' not in version_stdout:\n            return\n\n        try:\n            version = version_stdout.strip().split(' ')[2]\n        except IndexError:\n            return\n\n        self.gitInstalled = True\n        if LooseVersion(version) >= LooseVersion(\"1.6.5\"):\n            self.supportsBranch = True\n        if LooseVersion(version) >= LooseVersion(\"1.7.6\"):\n            self.supportsSubmoduleForce = True\n        if LooseVersion(version) >= LooseVersion(\"1.7.8\"):\n            self.supportsSubmoduleCheckout = True\n        if LooseVersion(version) >= LooseVersion(\"2.3.0\"):\n            self.supportsSshPrivateKeyAsEnvOption = True\n        if LooseVersion(version) >= LooseVersion(\"2.10.0\"):\n            self.supportsSshPrivateKeyAsConfigOption = True\n\n    def adjustCommandParamsForSshPrivateKey(self, command, env,\n                                            keyPath, sshWrapperPath=None,\n                                            knownHostsPath=None):\n        ssh_command = getSshCommand(keyPath, knownHostsPath)\n\n        if self.supportsSshPrivateKeyAsConfigOption:\n            command.append('-c')\n            command.append('core.sshCommand={0}'.format(ssh_command))\n        elif self.supportsSshPrivateKeyAsEnvOption:\n            env['GIT_SSH_COMMAND'] = ssh_command\n        else:\n            if sshWrapperPath is None:\n                raise Exception('Only SSH wrapper script is supported but path '\n                                'not given')\n            env['GIT_SSH'] = sshWrapperPath\n\n\ndef getSshWrapperScriptContents(keyPath, knownHostsPath=None):\n    ssh_command = getSshCommand(keyPath, knownHostsPath)\n\n    # note that this works on windows if using git with MINGW embedded.\n    return '#!/bin/sh\\n{0} \"$@\"\\n'.format(ssh_command)\n\n\ndef getSshKnownHostsContents(hostKey):\n    host_name = '*'\n    return '{0} {1}'.format(host_name, hostKey)\n\n\nclass GitStepMixin(GitMixin):\n\n    def setupGitStep(self):\n        self.didDownloadSshPrivateKey = False\n        self.setupGit()\n\n        if self.sshHostKey is not None and self.sshPrivateKey is None:\n            bbconfig.error('Git: sshPrivateKey must be provided in order '\n                           'use sshHostKey')\n            self.sshPrivateKey = None\n\n        if not self.repourl:\n            bbconfig.error(\"Git: must provide repourl.\")\n\n    def _isSshPrivateKeyNeededForGitCommand(self, command):\n        if not command or self.sshPrivateKey is None:\n            return False\n\n        gitCommandsThatNeedSshKey = [\n            'clone', 'submodule', 'fetch', 'push'\n        ]\n        if command[0] in gitCommandsThatNeedSshKey:\n            return True\n        return False\n\n    def _getSshDataPath(self):\n        # we can't use the workdir for temporary ssh-related files, because\n        # it's needed when cloning repositories and git does not like the\n        # destination directory being non-empty. We have to use separate\n        # temporary directory for that data to ensure the confidentiality of it.\n        # So instead of\n        # '{path}/{to}/{workdir}/.buildbot-ssh-key' we put the key at\n        # '{path}/{to}/.{workdir}.buildbot/ssh-key'.\n\n        # basename and dirname interpret the last element being empty for paths\n        # ending with a slash\n        path_module = self.build.path_module\n\n        workdir = self._getSshDataWorkDir().rstrip('/\\\\')\n        if path_module.isabs(workdir):\n            parent_path = path_module.dirname(workdir)\n        else:\n            parent_path = path_module.join(self.worker.worker_basedir,\n                                           path_module.dirname(workdir))\n\n        basename = '.{0}.buildbot'.format(path_module.basename(workdir))\n        return path_module.join(parent_path, basename)\n\n    def _getSshPrivateKeyPath(self):\n        return self.build.path_module.join(self._getSshDataPath(), 'ssh-key')\n\n    def _getSshHostKeyPath(self):\n        return self.build.path_module.join(self._getSshDataPath(), 'ssh-known-hosts')\n\n    def _getSshWrapperScriptPath(self):\n        return self.build.path_module.join(self._getSshDataPath(), 'ssh-wrapper.sh')\n\n    def _getSshWrapperScript(self):\n        return getSshWrapperScriptContents(self._getSshPrivateKeyPath)\n\n    def _adjustCommandParamsForSshPrivateKey(self, full_command, full_env):\n\n        key_path = self._getSshPrivateKeyPath()\n        ssh_wrapper_path = self._getSshWrapperScriptPath()\n        host_key_path = None\n        if self.sshHostKey is not None:\n            host_key_path = self._getSshHostKeyPath()\n\n        self.adjustCommandParamsForSshPrivateKey(full_command, full_env,\n                                                 key_path, ssh_wrapper_path,\n                                                 host_key_path)\n\n    @defer.inlineCallbacks\n    def _dovccmd(self, command, abandonOnFailure=True, collectStdout=False, initialStdin=None):\n        full_command = ['git']\n        full_env = self.env.copy() if self.env else {}\n\n        if self.config is not None:\n            for name, value in iteritems(self.config):\n                full_command.append('-c')\n                full_command.append('%s=%s' % (name, value))\n\n        if self._isSshPrivateKeyNeededForGitCommand(command):\n            self._adjustCommandParamsForSshPrivateKey(full_command, full_env)\n\n        full_command.extend(command)\n\n        # check for the interruptSignal flag\n        sigtermTime = None\n        interruptSignal = None\n\n        # If possible prefer to send a SIGTERM to git before we send a SIGKILL.\n        # If we send a SIGKILL, git is prone to leaving around stale lockfiles.\n        # By priming it with a SIGTERM first we can ensure that it has a chance to shut-down gracefully\n        # before getting terminated\n        if not self.workerVersionIsOlderThan(\"shell\", \"2.16\"):\n            # git should shut-down quickly on SIGTERM.  If it doesn't don't let it\n            # stick around for too long because this is on top of any timeout\n            # we have hit.\n            sigtermTime = 1\n        else:\n            # Since sigtermTime is unavailable try to just use SIGTERM by itself instead of\n            # killing.  This should be safe.\n            if self.workerVersionIsOlderThan(\"shell\", \"2.15\"):\n                log.msg(\n                    \"NOTE: worker does not allow master to specify \"\n                    \"interruptSignal. This may leave a stale lockfile around \"\n                    \"if the command is interrupted/times out\\n\")\n            else:\n                interruptSignal = 'TERM'\n\n        cmd = remotecommand.RemoteShellCommand(self.workdir,\n                                               full_command,\n                                               env=full_env,\n                                               logEnviron=self.logEnviron,\n                                               timeout=self.timeout,\n                                               sigtermTime=sigtermTime,\n                                               interruptSignal=interruptSignal,\n                                               collectStdout=collectStdout,\n                                               initialStdin=initialStdin)\n        cmd.useLog(self.stdio_log, False)\n        yield self.runCommand(cmd)\n\n        if abandonOnFailure and cmd.didFail():\n            log.msg(\"Source step failed while running command %s\" % cmd)\n            raise buildstep.BuildStepFailed()\n        if collectStdout:\n            defer.returnValue(cmd.stdout)\n            return\n        defer.returnValue(cmd.rc)\n\n    @defer.inlineCallbacks\n    def checkBranchSupport(self):\n        stdout = yield self._dovccmd(['--version'], collectStdout=True)\n\n        self.parseGitFeatures(stdout)\n\n        defer.returnValue(self.gitInstalled)\n\n    @defer.inlineCallbacks\n    def _downloadSshPrivateKeyIfNeeded(self):\n        if self.sshPrivateKey is None:\n            defer.returnValue(RC_SUCCESS)\n\n        p = Properties()\n        p.master = self.master\n        private_key = yield p.render(self.sshPrivateKey)\n        host_key = yield p.render(self.sshHostKey)\n\n        # not using self.workdir because it may be changed depending on step\n        # options\n        workdir = self._getSshDataWorkDir()\n\n        yield self.runMkdir(self._getSshDataPath())\n\n        if not self.supportsSshPrivateKeyAsEnvOption:\n            yield self.downloadFileContentToWorker(self._getSshWrapperScriptPath(),\n                                                   self._getSshWrapperScript(),\n                                                   workdir=workdir, mode=0o700)\n\n        yield self.downloadFileContentToWorker(self._getSshPrivateKeyPath(),\n                                               private_key,\n                                               workdir=workdir, mode=0o400)\n\n        if self.sshHostKey is not None:\n            known_hosts_contents = getSshKnownHostsContents(host_key)\n            yield self.downloadFileContentToWorker(self._getSshHostKeyPath(),\n                                                   known_hosts_contents,\n                                                   workdir=workdir, mode=0o400)\n\n        self.didDownloadSshPrivateKey = True\n        defer.returnValue(RC_SUCCESS)\n\n    @defer.inlineCallbacks\n    def _removeSshPrivateKeyIfNeeded(self):\n        if not self.didDownloadSshPrivateKey:\n            defer.returnValue(RC_SUCCESS)\n\n        yield self.runRmdir(self._getSshDataPath())\n        defer.returnValue(RC_SUCCESS)\n", "path": "master/buildbot/util/git.py"}]}