1
2 import tempfile
3 import os
4 from cStringIO import StringIO
5
6 from twisted.python import log
7 from twisted.application import service
8 from twisted.internet import defer, protocol, error, reactor
9 from twisted.internet.task import LoopingCall
10
11 from buildbot import util
12 from buildbot.interfaces import IChangeSource
13 from buildbot.changes.changes import Change
14
16
18 self.cmdline = cmdline
19 self.deferred = deferred
20 self.s = StringIO()
21
24
28
30 log.msg("Command %r exited with value %s" % (self.cmdline, reason))
31 if isinstance(reason.value, error.ProcessDone):
32 self.deferred.callback(self.s.getvalue())
33 else:
34 self.deferred.errback(reason)
35
37 """All methods of this class return a Deferred."""
38
40 self.bin = bin
41 self.db = db
42
44 d = defer.Deferred()
45 cmdline = (self.bin, "--db=" + self.db) + tuple(args)
46 p = _MTProtocol(d, cmdline)
47 log.msg("Running command: %r" % (cmdline,))
48 log.msg("wd: %s" % os.getcwd())
49 reactor.spawnProcess(p, self.bin, cmdline)
50 return d
51
53 if output:
54 return output.strip().split("\n")
55 else:
56 return []
57
59 d = self._run_monotone(["automate", "interface_version"])
60 d.addCallback(self._process_interface_version)
61 return d
62
64 return tuple(map(int, output.strip().split(".")))
65
67 return self._run_monotone(["db", "init"])
68
70 return self._run_monotone(["db", "migrate"])
71
72 - def pull(self, server, pattern):
73 return self._run_monotone(["pull", server, pattern])
74
76 return self._run_monotone(["cat", "revision", rid])
77
79 cmd = ["automate", "heads", branch]
80 if rcfile:
81 cmd += ["--rcfile=" + rcfile]
82 d = self._run_monotone(cmd)
83 d.addCallback(self._process_revision_list)
84 return d
85
87 d = self._run_monotone(["automate", "erase_ancestors"] + revs)
88 d.addCallback(self._process_revision_list)
89 return d
90
92 d = self._run_monotone(["automate", "ancestry_difference", new_rev]
93 + old_revs)
94 d.addCallback(self._process_revision_list)
95 return d
96
98 d = self._run_monotone(["automate", "descendents", rev])
99 d.addCallback(self._process_revision_list)
100 return d
101
102 - def log(self, rev, depth=None):
103 if depth is not None:
104 depth_arg = ["--last=%i" % (depth,)]
105 else:
106 depth_arg = []
107 return self._run_monotone(["log", "-r", rev] + depth_arg)
108
109
111 """This source will poll a monotone server for changes and submit them to
112 the change master.
113
114 @param server_addr: monotone server specification (host:portno)
115
116 @param branch: monotone branch to watch
117
118 @param trusted_keys: list of keys whose code you trust
119
120 @param db_path: path to monotone database to pull into
121
122 @param pollinterval: interval in seconds between polls, defaults to 10 minutes
123 @param monotone_exec: path to monotone executable, defaults to "monotone"
124 """
125
126 __implements__ = IChangeSource, service.Service.__implements__
127 compare_attrs = ["server_addr", "trusted_keys", "db_path",
128 "pollinterval", "branch", "monotone_exec"]
129
130 parent = None
131 done_revisions = []
132 last_revision = None
133 loop = None
134 d = None
135 tmpfile = None
136 monotone = None
137 volatile = ["loop", "d", "tmpfile", "monotone"]
138
139 - def __init__(self, server_addr, branch, trusted_keys, db_path,
140 pollinterval=60 * 10, monotone_exec="monotone"):
141 self.server_addr = server_addr
142 self.branch = branch
143 self.trusted_keys = trusted_keys
144 self.db_path = db_path
145 self.pollinterval = pollinterval
146 self.monotone_exec = monotone_exec
147 self.monotone = Monotone(self.monotone_exec, self.db_path)
148
153
157
159 return "monotone_source %s %s" % (self.server_addr,
160 self.branch)
161
163 if self.d is not None:
164 log.msg("last poll still in progress, skipping next poll")
165 return
166 log.msg("starting poll")
167 self.d = self._maybe_init_db()
168 self.d.addCallback(self._do_netsync)
169 self.d.addCallback(self._get_changes)
170 self.d.addErrback(self._handle_error)
171
175
183
186
188 d = self._get_new_head()
189 d.addCallback(self._process_new_head)
190 return d
191
193
194
195
196
197 rcfile = """function get_revision_cert_trust(signers, id, name, val)
198 local trusted_signers = { %s }
199 local ts_table = {}
200 for k, v in pairs(trusted_signers) do ts_table[v] = 1 end
201 for k, v in pairs(signers) do
202 if ts_table[v] then
203 return true
204 end
205 end
206 return false
207 end
208 """
209 trusted_list = ", ".join(['"' + key + '"' for key in self.trusted_keys])
210
211 tmpfile_name = tempfile.mktemp()
212 f = open(tmpfile_name, "w")
213 f.write(rcfile % trusted_list)
214 f.close()
215 d = self.monotone.get_heads(self.branch, tmpfile_name)
216 d.addCallback(self._find_new_head, tmpfile_name)
217 return d
218
228
230 for r in new_heads:
231 if r in old_head_descendents:
232 return r
233 return None
234
236 if new_head is None:
237 log.msg("No new head")
238 self.d = None
239 return None
240
241
242
243 d = self._simplify_revisions()
244
245 d.addCallback(self._get_new_revisions, new_head)
246
247 d.addCallback(self._add_changes_for_revisions)
248
249 d.addCallback(self._finish_changes, new_head)
250 return d
251
256
260
269
271 d = defer.succeed(None)
272 for rid in revs:
273 d.addCallback(self._add_change_for_revision, rid)
274 return d
275
277 d = self.monotone.log(rid, 1)
278 d.addCallback(self._add_change_from_log, rid)
279 return d
280
285
287
288
289 pieces = revision.split('"')
290 files = []
291 for i in range(len(pieces)):
292 if (i % 2) == 1:
293 files.append(pieces[i])
294
295 author = "unknown author"
296 pieces = log.split('\n')
297 for p in pieces:
298 if p.startswith("Author:"):
299 author = p.split()[1]
300 self.parent.addChange(Change(author, files, log, revision=rid))
301
306