| Index: third_party/buildbot_7_12/buildbot/changes/mail.py
|
| diff --git a/third_party/buildbot_7_12/buildbot/changes/mail.py b/third_party/buildbot_7_12/buildbot/changes/mail.py
|
| deleted file mode 100644
|
| index e430ddad14fc8f4b3280f8564edffacbdc48e6d9..0000000000000000000000000000000000000000
|
| --- a/third_party/buildbot_7_12/buildbot/changes/mail.py
|
| +++ /dev/null
|
| @@ -1,595 +0,0 @@
|
| -# -*- test-case-name: buildbot.test.test_mailparse -*-
|
| -
|
| -"""
|
| -Parse various kinds of 'CVS notify' email.
|
| -"""
|
| -import os, re
|
| -import time, calendar
|
| -from email import message_from_file
|
| -from email.Utils import parseaddr
|
| -from email.Iterators import body_line_iterator
|
| -
|
| -from zope.interface import implements
|
| -from twisted.python import log
|
| -from buildbot import util
|
| -from buildbot.interfaces import IChangeSource
|
| -from buildbot.changes import changes
|
| -from buildbot.changes.maildir import MaildirService
|
| -
|
| -class MaildirSource(MaildirService, util.ComparableMixin):
|
| - """This source will watch a maildir that is subscribed to a FreshCVS
|
| - change-announcement mailing list.
|
| - """
|
| - implements(IChangeSource)
|
| -
|
| - compare_attrs = ["basedir", "pollinterval", "prefix"]
|
| - name = None
|
| -
|
| - def __init__(self, maildir, prefix=None):
|
| - MaildirService.__init__(self, maildir)
|
| - self.prefix = prefix
|
| - if prefix and not prefix.endswith("/"):
|
| - log.msg("%s: you probably want your prefix=('%s') to end with "
|
| - "a slash")
|
| -
|
| - def describe(self):
|
| - return "%s mailing list in maildir %s" % (self.name, self.basedir)
|
| -
|
| - def messageReceived(self, filename):
|
| - path = os.path.join(self.basedir, "new", filename)
|
| - change = self.parse_file(open(path, "r"), self.prefix)
|
| - if change:
|
| - self.parent.addChange(change)
|
| - os.rename(os.path.join(self.basedir, "new", filename),
|
| - os.path.join(self.basedir, "cur", filename))
|
| -
|
| - def parse_file(self, fd, prefix=None):
|
| - m = message_from_file(fd)
|
| - return self.parse(m, prefix)
|
| -
|
| -class FCMaildirSource(MaildirSource):
|
| - name = "FreshCVS"
|
| -
|
| - def parse(self, m, prefix=None):
|
| - """Parse mail sent by FreshCVS"""
|
| -
|
| - # FreshCVS sets From: to "user CVS <user>", but the <> part may be
|
| - # modified by the MTA (to include a local domain)
|
| - name, addr = parseaddr(m["from"])
|
| - if not name:
|
| - return None # no From means this message isn't from FreshCVS
|
| - cvs = name.find(" CVS")
|
| - if cvs == -1:
|
| - return None # this message isn't from FreshCVS
|
| - who = name[:cvs]
|
| -
|
| - # we take the time of receipt as the time of checkin. Not correct,
|
| - # but it avoids the out-of-order-changes issue. See the comment in
|
| - # parseSyncmail about using the 'Date:' header
|
| - when = util.now()
|
| -
|
| - files = []
|
| - comments = ""
|
| - isdir = 0
|
| - lines = list(body_line_iterator(m))
|
| - while lines:
|
| - line = lines.pop(0)
|
| - if line == "Modified files:\n":
|
| - break
|
| - while lines:
|
| - line = lines.pop(0)
|
| - if line == "\n":
|
| - break
|
| - line = line.rstrip("\n")
|
| - linebits = line.split(None, 1)
|
| - file = linebits[0]
|
| - if prefix:
|
| - # insist that the file start with the prefix: FreshCVS sends
|
| - # changes we don't care about too
|
| - if file.startswith(prefix):
|
| - file = file[len(prefix):]
|
| - else:
|
| - continue
|
| - if len(linebits) == 1:
|
| - isdir = 1
|
| - elif linebits[1] == "0 0":
|
| - isdir = 1
|
| - files.append(file)
|
| - while lines:
|
| - line = lines.pop(0)
|
| - if line == "Log message:\n":
|
| - break
|
| - # message is terminated by "ViewCVS links:" or "Index:..." (patch)
|
| - while lines:
|
| - line = lines.pop(0)
|
| - if line == "ViewCVS links:\n":
|
| - break
|
| - if line.find("Index: ") == 0:
|
| - break
|
| - comments += line
|
| - comments = comments.rstrip() + "\n"
|
| -
|
| - if not files:
|
| - return None
|
| -
|
| - change = changes.Change(who, files, comments, isdir, when=when)
|
| -
|
| - return change
|
| -
|
| -class SyncmailMaildirSource(MaildirSource):
|
| - name = "Syncmail"
|
| -
|
| - def parse(self, m, prefix=None):
|
| - """Parse messages sent by the 'syncmail' program, as suggested by the
|
| - sourceforge.net CVS Admin documentation. Syncmail is maintained at
|
| - syncmail.sf.net .
|
| - """
|
| - # pretty much the same as freshcvs mail, not surprising since CVS is
|
| - # the one creating most of the text
|
| -
|
| - # The mail is sent from the person doing the checkin. Assume that the
|
| - # local username is enough to identify them (this assumes a one-server
|
| - # cvs-over-rsh environment rather than the server-dirs-shared-over-NFS
|
| - # model)
|
| - name, addr = parseaddr(m["from"])
|
| - if not addr:
|
| - return None # no From means this message isn't from FreshCVS
|
| - at = addr.find("@")
|
| - if at == -1:
|
| - who = addr # might still be useful
|
| - else:
|
| - who = addr[:at]
|
| -
|
| - # we take the time of receipt as the time of checkin. Not correct (it
|
| - # depends upon the email latency), but it avoids the
|
| - # out-of-order-changes issue. Also syncmail doesn't give us anything
|
| - # better to work with, unless you count pulling the v1-vs-v2
|
| - # timestamp out of the diffs, which would be ugly. TODO: Pulling the
|
| - # 'Date:' header from the mail is a possibility, and
|
| - # email.Utils.parsedate_tz may be useful. It should be configurable,
|
| - # however, because there are a lot of broken clocks out there.
|
| - when = util.now()
|
| -
|
| - subject = m["subject"]
|
| - # syncmail puts the repository-relative directory in the subject:
|
| - # mprefix + "%(dir)s %(file)s,%(oldversion)s,%(newversion)s", where
|
| - # 'mprefix' is something that could be added by a mailing list
|
| - # manager.
|
| - # this is the only reasonable way to determine the directory name
|
| - space = subject.find(" ")
|
| - if space != -1:
|
| - directory = subject[:space]
|
| - else:
|
| - directory = subject
|
| -
|
| - files = []
|
| - comments = ""
|
| - isdir = 0
|
| - branch = None
|
| -
|
| - lines = list(body_line_iterator(m))
|
| - while lines:
|
| - line = lines.pop(0)
|
| -
|
| - if (line == "Modified Files:\n" or
|
| - line == "Added Files:\n" or
|
| - line == "Removed Files:\n"):
|
| - break
|
| -
|
| - while lines:
|
| - line = lines.pop(0)
|
| - if line == "\n":
|
| - break
|
| - if line == "Log Message:\n":
|
| - lines.insert(0, line)
|
| - break
|
| - line = line.lstrip()
|
| - line = line.rstrip()
|
| - # note: syncmail will send one email per directory involved in a
|
| - # commit, with multiple files if they were in the same directory.
|
| - # Unlike freshCVS, it makes no attempt to collect all related
|
| - # commits into a single message.
|
| -
|
| - # note: syncmail will report a Tag underneath the ... Files: line
|
| - # e.g.: Tag: BRANCH-DEVEL
|
| -
|
| - if line.startswith('Tag:'):
|
| - branch = line.split(' ')[-1].rstrip()
|
| - continue
|
| -
|
| - thesefiles = line.split(" ")
|
| - for f in thesefiles:
|
| - f = directory + "/" + f
|
| - if prefix:
|
| - # insist that the file start with the prefix: we may get
|
| - # changes we don't care about too
|
| - if f.startswith(prefix):
|
| - f = f[len(prefix):]
|
| - else:
|
| - continue
|
| - break
|
| - # TODO: figure out how new directories are described, set
|
| - # .isdir
|
| - files.append(f)
|
| -
|
| - if not files:
|
| - return None
|
| -
|
| - while lines:
|
| - line = lines.pop(0)
|
| - if line == "Log Message:\n":
|
| - break
|
| - # message is terminated by "Index:..." (patch) or "--- NEW FILE.."
|
| - # or "--- filename DELETED ---". Sigh.
|
| - while lines:
|
| - line = lines.pop(0)
|
| - if line.find("Index: ") == 0:
|
| - break
|
| - if re.search(r"^--- NEW FILE", line):
|
| - break
|
| - if re.search(r" DELETED ---$", line):
|
| - break
|
| - comments += line
|
| - comments = comments.rstrip() + "\n"
|
| -
|
| - change = changes.Change(who, files, comments, isdir, when=when,
|
| - branch=branch)
|
| -
|
| - return change
|
| -
|
| -# Bonsai mail parser by Stephen Davis.
|
| -#
|
| -# This handles changes for CVS repositories that are watched by Bonsai
|
| -# (http://www.mozilla.org/bonsai.html)
|
| -
|
| -# A Bonsai-formatted email message looks like:
|
| -#
|
| -# C|1071099907|stephend|/cvs|Sources/Scripts/buildbot|bonsai.py|1.2|||18|7
|
| -# A|1071099907|stephend|/cvs|Sources/Scripts/buildbot|master.cfg|1.1|||18|7
|
| -# R|1071099907|stephend|/cvs|Sources/Scripts/buildbot|BuildMaster.py|||
|
| -# LOGCOMMENT
|
| -# Updated bonsai parser and switched master config to buildbot-0.4.1 style.
|
| -#
|
| -# :ENDLOGCOMMENT
|
| -#
|
| -# In the first example line, stephend is the user, /cvs the repository,
|
| -# buildbot the directory, bonsai.py the file, 1.2 the revision, no sticky
|
| -# and branch, 18 lines added and 7 removed. All of these fields might not be
|
| -# present (during "removes" for example).
|
| -#
|
| -# There may be multiple "control" lines or even none (imports, directory
|
| -# additions) but there is one email per directory. We only care about actual
|
| -# changes since it is presumed directory additions don't actually affect the
|
| -# build. At least one file should need to change (the makefile, say) to
|
| -# actually make a new directory part of the build process. That's my story
|
| -# and I'm sticking to it.
|
| -
|
| -class BonsaiMaildirSource(MaildirSource):
|
| - name = "Bonsai"
|
| -
|
| - def parse(self, m, prefix=None):
|
| - """Parse mail sent by the Bonsai cvs loginfo script."""
|
| -
|
| - # we don't care who the email came from b/c the cvs user is in the
|
| - # msg text
|
| -
|
| - who = "unknown"
|
| - timestamp = None
|
| - files = []
|
| - lines = list(body_line_iterator(m))
|
| -
|
| - # read the control lines (what/who/where/file/etc.)
|
| - while lines:
|
| - line = lines.pop(0)
|
| - if line == "LOGCOMMENT\n":
|
| - break;
|
| - line = line.rstrip("\n")
|
| -
|
| - # we'd like to do the following but it won't work if the number of
|
| - # items doesn't match so...
|
| - # what, timestamp, user, repo, module, file = line.split( '|' )
|
| - items = line.split('|')
|
| - if len(items) < 6:
|
| - # not a valid line, assume this isn't a bonsai message
|
| - return None
|
| -
|
| - try:
|
| - # just grab the bottom-most timestamp, they're probably all the
|
| - # same. TODO: I'm assuming this is relative to the epoch, but
|
| - # this needs testing.
|
| - timestamp = int(items[1])
|
| - except ValueError:
|
| - pass
|
| -
|
| - user = items[2]
|
| - if user:
|
| - who = user
|
| -
|
| - module = items[4]
|
| - file = items[5]
|
| - if module and file:
|
| - path = "%s/%s" % (module, file)
|
| - files.append(path)
|
| - sticky = items[7]
|
| - branch = items[8]
|
| -
|
| - # if no files changed, return nothing
|
| - if not files:
|
| - return None
|
| -
|
| - # read the comments
|
| - comments = ""
|
| - while lines:
|
| - line = lines.pop(0)
|
| - if line == ":ENDLOGCOMMENT\n":
|
| - break
|
| - comments += line
|
| - comments = comments.rstrip() + "\n"
|
| -
|
| - # return buildbot Change object
|
| - return changes.Change(who, files, comments, when=timestamp,
|
| - branch=branch)
|
| -
|
| -# svn "commit-email.pl" handler. The format is very similar to freshcvs mail;
|
| -# here's a sample:
|
| -
|
| -# From: username [at] apache.org [slightly obfuscated to avoid spam here]
|
| -# To: commits [at] spamassassin.apache.org
|
| -# Subject: svn commit: r105955 - in spamassassin/trunk: . lib/Mail
|
| -# ...
|
| -#
|
| -# Author: username
|
| -# Date: Sat Nov 20 00:17:49 2004 [note: TZ = local tz on server!]
|
| -# New Revision: 105955
|
| -#
|
| -# Modified: [also Removed: and Added:]
|
| -# [filename]
|
| -# ...
|
| -# Log:
|
| -# [log message]
|
| -# ...
|
| -#
|
| -#
|
| -# Modified: spamassassin/trunk/lib/Mail/SpamAssassin.pm
|
| -# [unified diff]
|
| -#
|
| -# [end of mail]
|
| -
|
| -class SVNCommitEmailMaildirSource(MaildirSource):
|
| - name = "SVN commit-email.pl"
|
| -
|
| - def parse(self, m, prefix=None):
|
| - """Parse messages sent by the svn 'commit-email.pl' trigger.
|
| - """
|
| -
|
| - # The mail is sent from the person doing the checkin. Assume that the
|
| - # local username is enough to identify them (this assumes a one-server
|
| - # cvs-over-rsh environment rather than the server-dirs-shared-over-NFS
|
| - # model)
|
| - name, addr = parseaddr(m["from"])
|
| - if not addr:
|
| - return None # no From means this message isn't from FreshCVS
|
| - at = addr.find("@")
|
| - if at == -1:
|
| - who = addr # might still be useful
|
| - else:
|
| - who = addr[:at]
|
| -
|
| - # we take the time of receipt as the time of checkin. Not correct (it
|
| - # depends upon the email latency), but it avoids the
|
| - # out-of-order-changes issue. Also syncmail doesn't give us anything
|
| - # better to work with, unless you count pulling the v1-vs-v2
|
| - # timestamp out of the diffs, which would be ugly. TODO: Pulling the
|
| - # 'Date:' header from the mail is a possibility, and
|
| - # email.Utils.parsedate_tz may be useful. It should be configurable,
|
| - # however, because there are a lot of broken clocks out there.
|
| - when = util.now()
|
| -
|
| - files = []
|
| - comments = ""
|
| - isdir = 0
|
| - lines = list(body_line_iterator(m))
|
| - rev = None
|
| - while lines:
|
| - line = lines.pop(0)
|
| -
|
| - # "Author: jmason"
|
| - match = re.search(r"^Author: (\S+)", line)
|
| - if match:
|
| - who = match.group(1)
|
| -
|
| - # "New Revision: 105955"
|
| - match = re.search(r"^New Revision: (\d+)", line)
|
| - if match:
|
| - rev = match.group(1)
|
| -
|
| - # possible TODO: use "Date: ..." data here instead of time of
|
| - # commit message receipt, above. however, this timestamp is
|
| - # specified *without* a timezone, in the server's local TZ, so to
|
| - # be accurate buildbot would need a config setting to specify the
|
| - # source server's expected TZ setting! messy.
|
| -
|
| - # this stanza ends with the "Log:"
|
| - if (line == "Log:\n"):
|
| - break
|
| -
|
| - # commit message is terminated by the file-listing section
|
| - while lines:
|
| - line = lines.pop(0)
|
| - if (line == "Modified:\n" or
|
| - line == "Added:\n" or
|
| - line == "Removed:\n"):
|
| - break
|
| - comments += line
|
| - comments = comments.rstrip() + "\n"
|
| -
|
| - while lines:
|
| - line = lines.pop(0)
|
| - if line == "\n":
|
| - break
|
| - if line.find("Modified:\n") == 0:
|
| - continue # ignore this line
|
| - if line.find("Added:\n") == 0:
|
| - continue # ignore this line
|
| - if line.find("Removed:\n") == 0:
|
| - continue # ignore this line
|
| - line = line.strip()
|
| -
|
| - thesefiles = line.split(" ")
|
| - for f in thesefiles:
|
| - if prefix:
|
| - # insist that the file start with the prefix: we may get
|
| - # changes we don't care about too
|
| - if f.startswith(prefix):
|
| - f = f[len(prefix):]
|
| - else:
|
| - log.msg("ignored file from svn commit: prefix '%s' "
|
| - "does not match filename '%s'" % (prefix, f))
|
| - continue
|
| -
|
| - # TODO: figure out how new directories are described, set
|
| - # .isdir
|
| - files.append(f)
|
| -
|
| - if not files:
|
| - log.msg("no matching files found, ignoring commit")
|
| - return None
|
| -
|
| - return changes.Change(who, files, comments, when=when, revision=rev)
|
| -
|
| -# bzr Launchpad branch subscription mails. Sample mail:
|
| -#
|
| -# From: noreply@launchpad.net
|
| -# Subject: [Branch ~knielsen/maria/tmp-buildbot-test] Rev 2701: test add file
|
| -# To: Joe <joe@acme.com>
|
| -# ...
|
| -#
|
| -# ------------------------------------------------------------
|
| -# revno: 2701
|
| -# committer: Joe <joe@acme.com>
|
| -# branch nick: tmpbb
|
| -# timestamp: Fri 2009-05-15 10:35:43 +0200
|
| -# message:
|
| -# test add file
|
| -# added:
|
| -# test-add-file
|
| -#
|
| -#
|
| -# --
|
| -#
|
| -# https://code.launchpad.net/~knielsen/maria/tmp-buildbot-test
|
| -#
|
| -# You are subscribed to branch lp:~knielsen/maria/tmp-buildbot-test.
|
| -# To unsubscribe from this branch go to https://code.launchpad.net/~knielsen/maria/tmp-buildbot-test/+edit-subscription.
|
| -#
|
| -# [end of mail]
|
| -
|
| -class BzrLaunchpadEmailMaildirSource(MaildirSource):
|
| - name = "Launchpad"
|
| -
|
| - compare_attrs = MaildirSource.compare_attrs + ["branchMap", "defaultBranch"]
|
| -
|
| - def __init__(self, maildir, prefix=None, branchMap=None, defaultBranch=None, **kwargs):
|
| - self.branchMap = branchMap
|
| - self.defaultBranch = defaultBranch
|
| - MaildirSource.__init__(self, maildir, prefix, **kwargs)
|
| -
|
| - def parse(self, m, prefix=None):
|
| - """Parse branch notification messages sent by Launchpad.
|
| - """
|
| -
|
| - subject = m["subject"]
|
| - match = re.search(r"^\s*\[Branch\s+([^]]+)\]", subject)
|
| - if match:
|
| - repository = match.group(1)
|
| - else:
|
| - repository = None
|
| -
|
| - # Put these into a dictionary, otherwise we cannot assign them
|
| - # from nested function definitions.
|
| - d = { 'files': [], 'comments': "" }
|
| - gobbler = None
|
| - rev = None
|
| - who = None
|
| - when = util.now()
|
| - def gobble_comment(s):
|
| - d['comments'] += s + "\n"
|
| - def gobble_removed(s):
|
| - d['files'].append('%s REMOVED' % s)
|
| - def gobble_added(s):
|
| - d['files'].append('%s ADDED' % s)
|
| - def gobble_modified(s):
|
| - d['files'].append('%s MODIFIED' % s)
|
| - def gobble_renamed(s):
|
| - match = re.search(r"^(.+) => (.+)$", s)
|
| - if match:
|
| - d['files'].append('%s RENAMED %s' % (match.group(1), match.group(2)))
|
| - else:
|
| - d['files'].append('%s RENAMED' % s)
|
| -
|
| - lines = list(body_line_iterator(m, True))
|
| - rev = None
|
| - while lines:
|
| - line = lines.pop(0)
|
| -
|
| - # revno: 101
|
| - match = re.search(r"^revno: ([0-9.]+)", line)
|
| - if match:
|
| - rev = match.group(1)
|
| -
|
| - # committer: Joe <joe@acme.com>
|
| - match = re.search(r"^committer: (.*)$", line)
|
| - if match:
|
| - who = match.group(1)
|
| -
|
| - # timestamp: Fri 2009-05-15 10:35:43 +0200
|
| - # datetime.strptime() is supposed to support %z for time zone, but
|
| - # it does not seem to work. So handle the time zone manually.
|
| - match = re.search(r"^timestamp: [a-zA-Z]{3} (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) ([-+])(\d{2})(\d{2})$", line)
|
| - if match:
|
| - datestr = match.group(1)
|
| - tz_sign = match.group(2)
|
| - tz_hours = match.group(3)
|
| - tz_minutes = match.group(4)
|
| - when = parseLaunchpadDate(datestr, tz_sign, tz_hours, tz_minutes)
|
| -
|
| - if re.search(r"^message:\s*$", line):
|
| - gobbler = gobble_comment
|
| - elif re.search(r"^removed:\s*$", line):
|
| - gobbler = gobble_removed
|
| - elif re.search(r"^added:\s*$", line):
|
| - gobbler = gobble_added
|
| - elif re.search(r"^renamed:\s*$", line):
|
| - gobbler = gobble_renamed
|
| - elif re.search(r"^modified:\s*$", line):
|
| - gobbler = gobble_modified
|
| - elif re.search(r"^ ", line) and gobbler:
|
| - gobbler(line[2:-1]) # Use :-1 to gobble trailing newline
|
| -
|
| - # Determine the name of the branch.
|
| - branch = None
|
| - if self.branchMap and repository:
|
| - if self.branchMap.has_key(repository):
|
| - branch = self.branchMap[repository]
|
| - elif self.branchMap.has_key('lp:' + repository):
|
| - branch = self.branchMap['lp:' + repository]
|
| - if not branch:
|
| - if self.defaultBranch:
|
| - branch = self.defaultBranch
|
| - else:
|
| - if repository:
|
| - branch = 'lp:' + repository
|
| - else:
|
| - branch = None
|
| -
|
| - #log.msg("parse(): rev=%s who=%s files=%s comments='%s' when=%s branch=%s" % (rev, who, d['files'], d['comments'], time.asctime(time.localtime(when)), branch))
|
| - if rev and who:
|
| - return changes.Change(who, d['files'], d['comments'],
|
| - when=when, revision=rev, branch=branch)
|
| - else:
|
| - return None
|
| -
|
| -def parseLaunchpadDate(datestr, tz_sign, tz_hours, tz_minutes):
|
| - time_no_tz = calendar.timegm(time.strptime(datestr, "%Y-%m-%d %H:%M:%S"))
|
| - tz_delta = 60 * 60 * int(tz_sign + tz_hours) + 60 * int(tz_minutes)
|
| - return time_no_tz - tz_delta
|
|
|