1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 from twisted.python import log
19 from twisted.internet import defer
20
21 from buildbot.process import buildstep
22 from buildbot.steps.source.base import Source
23 from buildbot.interfaces import BuildSlaveTooOldError
24 from buildbot.config import ConfigErrors
25 from buildbot.status.results import SUCCESS
28 """ Class for Mercurial with all the smarts """
29 name = "hg"
30
31 renderables = [ "repourl" ]
32 possible_modes = ('incremental', 'full')
33 possible_methods = (None, 'clean', 'fresh', 'clobber')
34 possible_branchTypes = ('inrepo', 'dirname')
35
36 - def __init__(self, repourl=None, mode='incremental',
37 method=None, defaultBranch=None, branchType='dirname',
38 clobberOnBranchChange=True, **kwargs):
39
40 """
41 @type repourl: string
42 @param repourl: the URL which points at the Mercurial repository.
43 if 'dirname' branches are enabled, this is the base URL
44 to which a branch name will be appended. It should
45 probably end in a slash.
46
47 @param defaultBranch: if branches are enabled, this is the branch
48 to use if the Build does not specify one
49 explicitly.
50 For 'dirname' branches, It will simply be
51 appended to C{repourl} and the result handed to
52 the 'hg update' command.
53 For 'inrepo' branches, this specifies the named
54 revision to which the tree will update after a
55 clone.
56
57 @param branchType: either 'dirname' or 'inrepo' depending on whether
58 the branch name should be appended to the C{repourl}
59 or the branch is a mercurial named branch and can be
60 found within the C{repourl}
61
62 @param clobberOnBranchChange: boolean, defaults to True. If set and
63 using inrepos branches, clobber the tree
64 at each branch change. Otherwise, just
65 update to the branch.
66 """
67
68 self.repourl = repourl
69 self.defaultBranch = self.branch = defaultBranch
70 self.branchType = branchType
71 self.method = method
72 self.clobberOnBranchChange = clobberOnBranchChange
73 self.mode = mode
74 Source.__init__(self, **kwargs)
75
76 errors = []
77 if self.mode not in self.possible_modes:
78 errors.append("mode %s is not one of %s" %
79 (self.mode, self.possible_modes))
80 if self.method not in self.possible_methods:
81 errors.append("method %s is not one of %s" %
82 (self.method, self.possible_methods))
83 if self.branchType not in self.possible_branchTypes:
84 errors.append("branchType %s is not one of %s" %
85 (self.branchType, self.possible_branchTypes))
86
87 if repourl is None:
88 errors.append("you must provide a repourl")
89
90 if errors:
91 raise ConfigErrors(errors)
92
93 - def startVC(self, branch, revision, patch):
94 self.revision = revision
95 self.method = self._getMethod()
96 self.stdio_log = self.addLog("stdio")
97 d = self.checkHg()
98 def checkInstall(hgInstalled):
99 if not hgInstalled:
100 raise BuildSlaveTooOldError("Mercurial is not installed on slave")
101 return 0
102 d.addCallback(checkInstall)
103
104 if self.branchType == 'dirname':
105 self.repourl = self.repourl + (branch or '')
106 self.branch = self.defaultBranch
107 self.update_branch = branch
108 elif self.branchType == 'inrepo':
109 self.update_branch = (branch or 'default')
110
111 if self.mode == 'full':
112 d.addCallback(lambda _: self.full())
113 elif self.mode == 'incremental':
114 d.addCallback(lambda _: self.incremental())
115
116 if patch:
117 d.addCallback(self.patch, patch)
118
119 d.addCallback(self.parseGotRevision)
120 d.addCallback(self.finish)
121 d.addErrback(self.failed)
122
123 @defer.inlineCallbacks
125 if self.method == 'clobber':
126 yield self.clobber(None)
127 return
128
129 updatable = yield self._sourcedirIsUpdatable()
130 if not updatable:
131 yield self._dovccmd(['clone', self.repourl, '.'])
132 elif self.method == 'clean':
133 yield self.clean(None)
134 elif self.method == 'fresh':
135 yield self.fresh(None)
136 else:
137 raise ValueError("Unknown method, check your configuration")
138
140 if self.method is not None:
141 raise ValueError(self.method)
142
143 d = self._sourcedirIsUpdatable()
144 def _cmd(updatable):
145 if updatable:
146 command = ['pull', self.repourl]
147 else:
148 command = ['clone', self.repourl, '.', '--noupdate']
149 return command
150
151 d.addCallback(_cmd)
152 d.addCallback(self._dovccmd)
153 d.addCallback(self._checkBranchChange)
154 return d
155
157 command = ['--config', 'extensions.purge=', 'purge']
158 d = self._dovccmd(command)
159 d.addCallback(self._pullUpdate)
160 return d
161
163 cmd = buildstep.RemoteCommand('rmdir', {'dir': self.workdir,
164 'logEnviron':self.logEnviron})
165 cmd.useLog(self.stdio_log, False)
166 d = self.runCommand(cmd)
167 d.addCallback(lambda _: self._dovccmd(['clone', '--noupdate'
168 , self.repourl, "."]))
169 d.addCallback(self._update)
170 return d
171
173 command = ['--config', 'extensions.purge=', 'purge', '--all']
174 d = self._dovccmd(command)
175 d.addCallback(self._pullUpdate)
176 return d
177
183 d.addCallback(_gotResults)
184 d.addCallbacks(self.finished, self.checkDisconnect)
185 return d
186
188 d = self._dovccmd(['parents', '--template', '{node}\\n'], collectStdout=True)
189 def _setrev(stdout):
190 revision = stdout.strip()
191 if len(revision) != 40:
192 raise ValueError("Incorrect revision id")
193 log.msg("Got Mercurial revision %s" % (revision, ))
194 self.updateSourceProperty('got_revision', revision)
195 return 0
196 d.addCallback(_setrev)
197 return d
198
199 @defer.inlineCallbacks
201 current_branch = yield self._getCurrentBranch()
202 msg = "Working dir is on in-repo branch '%s' and build needs '%s'." % \
203 (current_branch, self.update_branch)
204 if current_branch != self.update_branch and self.clobberOnBranchChange:
205 msg += ' Clobbering.'
206 log.msg(msg)
207 yield self.clobber(None)
208 return
209 msg += ' Updating.'
210 log.msg(msg)
211 yield self._removeAddedFilesAndUpdate(None)
212
214 command = ['pull' , self.repourl]
215 if self.revision:
216 command.extend(['--rev', self.revision])
217 d = self._dovccmd(command)
218 d.addCallback(self._checkBranchChange)
219 return d
220
221 - def _dovccmd(self, command, collectStdout=False, initialStdin=None, decodeRC={0:SUCCESS}):
222 if not command:
223 raise ValueError("No command specified")
224 cmd = buildstep.RemoteShellCommand(self.workdir, ['hg', '--verbose'] + command,
225 env=self.env,
226 logEnviron=self.logEnviron,
227 timeout=self.timeout,
228 collectStdout=collectStdout,
229 initialStdin=initialStdin,
230 decodeRC=decodeRC)
231 cmd.useLog(self.stdio_log, False)
232 log.msg("Starting mercurial command : hg %s" % (" ".join(command), ))
233 d = self.runCommand(cmd)
234 def evaluateCommand(cmd):
235 if cmd.didFail():
236 log.msg("Source step failed while running command %s" % cmd)
237 raise buildstep.BuildStepFailed()
238 if collectStdout:
239 return cmd.stdout
240 else:
241 return cmd.rc
242 d.addCallback(lambda _: evaluateCommand(cmd))
243 return d
244
246 if not changes:
247 return None
248
249
250
251
252 if len(changes) > 1:
253 log.msg("Mercurial.computeSourceRevision: warning: "
254 "there are %d changes here, assuming the last one is "
255 "the most recent" % len(changes))
256 return changes[-1].revision
257
258 - def patch(self, _, patch):
259 d = self._dovccmd(['import', '--no-commit', '-p', str(patch[0]), '-'],
260 initialStdin=patch[1])
261 return d
262
264 if self.branchType == 'dirname':
265 return defer.succeed(self.branch)
266 else:
267 d = self._dovccmd(['identify', '--branch'], collectStdout=True)
268 def _getbranch(stdout):
269 return stdout.strip()
270 d.addCallback(_getbranch).addErrback
271 return d
272
274 if self.method is not None and self.mode != 'incremental':
275 return self.method
276 elif self.mode == 'incremental':
277 return None
278 elif self.method is None and self.mode == 'full':
279 return 'fresh'
280
290 d.addCallback(_fail)
291 return d
292
314 d.addCallback(parseAndRemove)
315 d.addCallback(self._update)
316 return d
317
318 @defer.inlineCallbacks
329
331 command = ['update', '--clean']
332 if self.revision:
333 command += ['--rev', self.revision]
334 elif self.branchType == 'inrepo':
335 command += ['--rev', self.update_branch]
336 d = self._dovccmd(command)
337 return d
338
340 d = self._dovccmd(['--version'])
341 def check(res):
342 if res == 0:
343 return True
344 return False
345 d.addCallback(check)
346 return d
347