Index: third_party/buildbot_7_12/buildbot/changes/monotone.py |
diff --git a/third_party/buildbot_7_12/buildbot/changes/monotone.py b/third_party/buildbot_7_12/buildbot/changes/monotone.py |
deleted file mode 100644 |
index 302c1c5654a80bf0d4c6fa8bd5f03a831ec657e4..0000000000000000000000000000000000000000 |
--- a/third_party/buildbot_7_12/buildbot/changes/monotone.py |
+++ /dev/null |
@@ -1,305 +0,0 @@ |
- |
-import tempfile |
-import os |
-from cStringIO import StringIO |
- |
-from twisted.python import log |
-from twisted.application import service |
-from twisted.internet import defer, protocol, error, reactor |
-from twisted.internet.task import LoopingCall |
- |
-from buildbot import util |
-from buildbot.interfaces import IChangeSource |
-from buildbot.changes.changes import Change |
- |
-class _MTProtocol(protocol.ProcessProtocol): |
- |
- def __init__(self, deferred, cmdline): |
- self.cmdline = cmdline |
- self.deferred = deferred |
- self.s = StringIO() |
- |
- def errReceived(self, text): |
- log.msg("stderr: %s" % text) |
- |
- def outReceived(self, text): |
- log.msg("stdout: %s" % text) |
- self.s.write(text) |
- |
- def processEnded(self, reason): |
- log.msg("Command %r exited with value %s" % (self.cmdline, reason)) |
- if isinstance(reason.value, error.ProcessDone): |
- self.deferred.callback(self.s.getvalue()) |
- else: |
- self.deferred.errback(reason) |
- |
-class Monotone: |
- """All methods of this class return a Deferred.""" |
- |
- def __init__(self, bin, db): |
- self.bin = bin |
- self.db = db |
- |
- def _run_monotone(self, args): |
- d = defer.Deferred() |
- cmdline = (self.bin, "--db=" + self.db) + tuple(args) |
- p = _MTProtocol(d, cmdline) |
- log.msg("Running command: %r" % (cmdline,)) |
- log.msg("wd: %s" % os.getcwd()) |
- reactor.spawnProcess(p, self.bin, cmdline) |
- return d |
- |
- def _process_revision_list(self, output): |
- if output: |
- return output.strip().split("\n") |
- else: |
- return [] |
- |
- def get_interface_version(self): |
- d = self._run_monotone(["automate", "interface_version"]) |
- d.addCallback(self._process_interface_version) |
- return d |
- |
- def _process_interface_version(self, output): |
- return tuple(map(int, output.strip().split("."))) |
- |
- def db_init(self): |
- return self._run_monotone(["db", "init"]) |
- |
- def db_migrate(self): |
- return self._run_monotone(["db", "migrate"]) |
- |
- def pull(self, server, pattern): |
- return self._run_monotone(["pull", server, pattern]) |
- |
- def get_revision(self, rid): |
- return self._run_monotone(["cat", "revision", rid]) |
- |
- def get_heads(self, branch, rcfile=""): |
- cmd = ["automate", "heads", branch] |
- if rcfile: |
- cmd += ["--rcfile=" + rcfile] |
- d = self._run_monotone(cmd) |
- d.addCallback(self._process_revision_list) |
- return d |
- |
- def erase_ancestors(self, revs): |
- d = self._run_monotone(["automate", "erase_ancestors"] + revs) |
- d.addCallback(self._process_revision_list) |
- return d |
- |
- def ancestry_difference(self, new_rev, old_revs): |
- d = self._run_monotone(["automate", "ancestry_difference", new_rev] |
- + old_revs) |
- d.addCallback(self._process_revision_list) |
- return d |
- |
- def descendents(self, rev): |
- d = self._run_monotone(["automate", "descendents", rev]) |
- d.addCallback(self._process_revision_list) |
- return d |
- |
- def log(self, rev, depth=None): |
- if depth is not None: |
- depth_arg = ["--last=%i" % (depth,)] |
- else: |
- depth_arg = [] |
- return self._run_monotone(["log", "-r", rev] + depth_arg) |
- |
- |
-class MonotoneSource(service.Service, util.ComparableMixin): |
- """This source will poll a monotone server for changes and submit them to |
- the change master. |
- |
- @param server_addr: monotone server specification (host:portno) |
- |
- @param branch: monotone branch to watch |
- |
- @param trusted_keys: list of keys whose code you trust |
- |
- @param db_path: path to monotone database to pull into |
- |
- @param pollinterval: interval in seconds between polls, defaults to 10 minutes |
- @param monotone_exec: path to monotone executable, defaults to "monotone" |
- """ |
- |
- __implements__ = IChangeSource, service.Service.__implements__ |
- compare_attrs = ["server_addr", "trusted_keys", "db_path", |
- "pollinterval", "branch", "monotone_exec"] |
- |
- parent = None # filled in when we're added |
- done_revisions = [] |
- last_revision = None |
- loop = None |
- d = None |
- tmpfile = None |
- monotone = None |
- volatile = ["loop", "d", "tmpfile", "monotone"] |
- |
- def __init__(self, server_addr, branch, trusted_keys, db_path, |
- pollinterval=60 * 10, monotone_exec="monotone"): |
- self.server_addr = server_addr |
- self.branch = branch |
- self.trusted_keys = trusted_keys |
- self.db_path = db_path |
- self.pollinterval = pollinterval |
- self.monotone_exec = monotone_exec |
- self.monotone = Monotone(self.monotone_exec, self.db_path) |
- |
- def startService(self): |
- self.loop = LoopingCall(self.start_poll) |
- self.loop.start(self.pollinterval) |
- service.Service.startService(self) |
- |
- def stopService(self): |
- self.loop.stop() |
- return service.Service.stopService(self) |
- |
- def describe(self): |
- return "monotone_source %s %s" % (self.server_addr, |
- self.branch) |
- |
- def start_poll(self): |
- if self.d is not None: |
- log.msg("last poll still in progress, skipping next poll") |
- return |
- log.msg("starting poll") |
- self.d = self._maybe_init_db() |
- self.d.addCallback(self._do_netsync) |
- self.d.addCallback(self._get_changes) |
- self.d.addErrback(self._handle_error) |
- |
- def _handle_error(self, failure): |
- log.err(failure) |
- self.d = None |
- |
- def _maybe_init_db(self): |
- if not os.path.exists(self.db_path): |
- log.msg("init'ing db") |
- return self.monotone.db_init() |
- else: |
- log.msg("db already exists, migrating") |
- return self.monotone.db_migrate() |
- |
- def _do_netsync(self, output): |
- return self.monotone.pull(self.server_addr, self.branch) |
- |
- def _get_changes(self, output): |
- d = self._get_new_head() |
- d.addCallback(self._process_new_head) |
- return d |
- |
- def _get_new_head(self): |
- # This function returns a deferred that resolves to a good pick of new |
- # head (or None if there is no good new head.) |
- |
- # First need to get all new heads... |
- rcfile = """function get_revision_cert_trust(signers, id, name, val) |
- local trusted_signers = { %s } |
- local ts_table = {} |
- for k, v in pairs(trusted_signers) do ts_table[v] = 1 end |
- for k, v in pairs(signers) do |
- if ts_table[v] then |
- return true |
- end |
- end |
- return false |
- end |
- """ |
- trusted_list = ", ".join(['"' + key + '"' for key in self.trusted_keys]) |
- # mktemp is unsafe, but mkstemp is not 2.2 compatible. |
- tmpfile_name = tempfile.mktemp() |
- f = open(tmpfile_name, "w") |
- f.write(rcfile % trusted_list) |
- f.close() |
- d = self.monotone.get_heads(self.branch, tmpfile_name) |
- d.addCallback(self._find_new_head, tmpfile_name) |
- return d |
- |
- def _find_new_head(self, new_heads, tmpfile_name): |
- os.unlink(tmpfile_name) |
- # Now get the old head's descendents... |
- if self.last_revision is not None: |
- d = self.monotone.descendents(self.last_revision) |
- else: |
- d = defer.succeed(new_heads) |
- d.addCallback(self._pick_new_head, new_heads) |
- return d |
- |
- def _pick_new_head(self, old_head_descendents, new_heads): |
- for r in new_heads: |
- if r in old_head_descendents: |
- return r |
- return None |
- |
- def _process_new_head(self, new_head): |
- if new_head is None: |
- log.msg("No new head") |
- self.d = None |
- return None |
- # Okay, we have a new head; we need to get all the revisions since |
- # then and create change objects for them. |
- # Step 1: simplify set of processed revisions. |
- d = self._simplify_revisions() |
- # Step 2: get the list of new revisions |
- d.addCallback(self._get_new_revisions, new_head) |
- # Step 3: add a change for each |
- d.addCallback(self._add_changes_for_revisions) |
- # Step 4: all done |
- d.addCallback(self._finish_changes, new_head) |
- return d |
- |
- def _simplify_revisions(self): |
- d = self.monotone.erase_ancestors(self.done_revisions) |
- d.addCallback(self._reset_done_revisions) |
- return d |
- |
- def _reset_done_revisions(self, new_done_revisions): |
- self.done_revisions = new_done_revisions |
- return None |
- |
- def _get_new_revisions(self, blah, new_head): |
- if self.done_revisions: |
- return self.monotone.ancestry_difference(new_head, |
- self.done_revisions) |
- else: |
- # Don't force feed the builder with every change since the |
- # beginning of time when it's first started up. |
- return defer.succeed([new_head]) |
- |
- def _add_changes_for_revisions(self, revs): |
- d = defer.succeed(None) |
- for rid in revs: |
- d.addCallback(self._add_change_for_revision, rid) |
- return d |
- |
- def _add_change_for_revision(self, blah, rid): |
- d = self.monotone.log(rid, 1) |
- d.addCallback(self._add_change_from_log, rid) |
- return d |
- |
- def _add_change_from_log(self, log, rid): |
- d = self.monotone.get_revision(rid) |
- d.addCallback(self._add_change_from_log_and_revision, log, rid) |
- return d |
- |
- def _add_change_from_log_and_revision(self, revision, log, rid): |
- # Stupid way to pull out everything inside quotes (which currently |
- # uniquely identifies filenames inside a changeset). |
- pieces = revision.split('"') |
- files = [] |
- for i in range(len(pieces)): |
- if (i % 2) == 1: |
- files.append(pieces[i]) |
- # Also pull out author key and date |
- author = "unknown author" |
- pieces = log.split('\n') |
- for p in pieces: |
- if p.startswith("Author:"): |
- author = p.split()[1] |
- self.parent.addChange(Change(author, files, log, revision=rid)) |
- |
- def _finish_changes(self, blah, new_head): |
- self.done_revisions.append(new_head) |
- self.last_revision = new_head |
- self.d = None |