--- mailer.py.orig Sun Jan 4 17:08:13 2004 +++ mailer.py Sun Jan 4 17:05:20 2004 @@ -56,11 +56,20 @@ param_list.sort() groups[group, tuple(param_list)] = params - output = determine_output(cfg, repos, changelist) + output = Commit(cfg, determine_output(cfg, repos), repos, changelist) output.generate(groups, pool) -def determine_output(cfg, repos, changelist): +def main_revprop(pool, config_fname, repos_dir, rev, author, propname): + repos = Repository(repos_dir, rev, pool) + + cfg = Config(config_fname, repos) + + output = RevProp(cfg, determine_output(cfg, repos), repos, author, propname) + output.generate(pool) + + +def determine_output(cfg, repos): if cfg.is_set('general.mail_command'): cls = PipeOutput elif cfg.is_set('general.smtp_hostname'): @@ -68,90 +77,23 @@ else: cls = StandardOutput - return cls(cfg, repos, changelist) + return cls(cfg, repos) class MailedOutput: - def __init__(self, cfg, repos, changelist): + def __init__(self, cfg, repos): self.cfg = cfg self.repos = repos - self.changelist = changelist - - # figure out the changed directories - dirs = { } - for path, change in changelist: - if change.item_kind == svn.core.svn_node_dir: - dirs[path] = None - else: - idx = string.rfind(path, '/') - if idx == -1: - dirs[''] = None - else: - dirs[path[:idx]] = None - - dirlist = dirs.keys() - # figure out the common portion of all the dirs. note that there is - # no "common" if only a single dir was changed, or the root was changed. - if len(dirs) == 1 or dirs.has_key(''): - commondir = '' - else: - common = string.split(dirlist.pop(), '/') - for d in dirlist: - parts = string.split(d, '/') - for i in range(len(common)): - if i == len(parts) or common[i] != parts[i]: - del common[i:] - break - commondir = string.join(common, '/') - if commondir: - # strip the common portion from each directory - l = len(commondir) + 1 - dirlist = [ ] - for d in dirs.keys(): - if d == commondir: - dirlist.append('.') - else: - dirlist.append(d[l:]) - else: - # nothing in common, so reset the list of directories - dirlist = dirs.keys() - - # compose the basic subject line. later, we can prefix it. - dirlist.sort() - dirlist = string.join(dirlist) - if commondir: - self.subject = 'r%d - in %s: %s' % (repos.rev, commondir, dirlist) - else: - self.subject = 'r%d - %s' % (repos.rev, dirlist) - - def generate(self, groups, pool): - "Generate email for the various groups and option-params." - - ### these groups need to be further compressed. if the headers and - ### body are the same across groups, then we can have multiple To: - ### addresses. SMTPOutput holds the entire message body in memory, - ### so if the body doesn't change, then it can be sent N times - ### rather than rebuilding it each time. - - subpool = svn.util.svn_pool_create(pool) - - for (group, param_tuple), params in groups.items(): - self.start(group, params) - - # generate the content for this group and set of params - generate_content(self, self.cfg, self.repos, self.changelist, - group, params, subpool) - - self.finish() - svn.util.svn_pool_clear(subpool) - - svn.util.svn_pool_destroy(subpool) - - def start(self, group, params): + def start(self, group, params, override_author = None): self.to_addr = self.cfg.get('to_addr', group, params) - self.from_addr = self.cfg.get('from_addr', group, params) or \ - self.repos.author or 'no_author' + author = self.cfg.get('from_addr', group, params) + if not author: + if override_author: + author = override_author + else: + author = self.repos.author or 'no_author' + self.from_addr = author self.reply_to = self.cfg.get('reply_to', group, params) def mail_headers(self, group, params): @@ -174,18 +116,15 @@ class SMTPOutput(MailedOutput): "Deliver a mail message to an MTA using SMTP." - def __init__(self, cfg, repos, changelist): - MailedOutput.__init__(self, cfg, repos, changelist) - - def start(self, group, params): - MailedOutput.start(self, group, params) + def start(self, group, params, **args): + MailedOutput.start(self, group, params, **args) self.buffer = cStringIO.StringIO() self.write = self.buffer.write self.write(self.mail_headers(group, params)) - def run_diff(self, cmd): + def run(self, cmd): # we're holding everything in memory, so we may as well read the # entire diff into memory and stash that into the buffer pipe_ob = popen2.Popen3(cmd) @@ -206,22 +145,19 @@ class StandardOutput: "Print the commit message to stdout." - def __init__(self, cfg, repos, changelist): + def __init__(self, cfg, repos): self.cfg = cfg self.repos = repos - self.changelist = changelist self.write = sys.stdout.write - def generate(self, groups, pool): - "Generate the output; the groups are ignored." + def start(self, group, params, **args): + pass - # use the default group and no parameters - ### is that right? - generate_content(self, self.cfg, self.repos, self.changelist, - None, { }, pool) + def finish(self): + pass - def run_diff(self, cmd): + def run(self, cmd): # flush our output to keep the parent/child output in sync sys.stdout.flush() sys.stderr.flush() @@ -244,8 +180,8 @@ class PipeOutput(MailedOutput): "Deliver a mail message to an MDA via a pipe." - def __init__(self, cfg, repos, changelist): - MailedOutput.__init__(self, cfg, repos, changelist) + def __init__(self, cfg, repos): + MailedOutput.__init__(self, cfg, repos) # figure out the command for delivery self.cmd = string.split(cfg.general.mail_command) @@ -253,8 +189,8 @@ # we want a descriptor to /dev/null for hooking up to the diffs' stdin self.null = os.open('/dev/null', os.O_RDONLY) - def start(self, group, params): - MailedOutput.start(self, group, params) + def start(self, group, params, **args): + MailedOutput.start(self, group, params, **args) ### gotta fix this. this is pretty specific to sendmail and qmail's ### mailwrapper program. should be able to use option param substitution @@ -270,7 +206,7 @@ # start writing out the mail message self.write(self.mail_headers(group, params)) - def run_diff(self, cmd): + def run(self, cmd): # flush the buffers that write to the mailer. we're about to fork, and # we don't want data sitting in both copies of the buffer. we also # want to ensure the parts are delivered to the mailer in the right order. @@ -311,6 +247,110 @@ self.pipe.wait() +class Commit: + def __init__(self, cfg, output, repos, changelist): + self.cfg = cfg + self.output = output + self.repos = repos + self.changelist = changelist + + # figure out the changed directories + dirs = { } + for path, change in changelist: + if change.item_kind == svn.core.svn_node_dir: + dirs[path] = None + else: + idx = string.rfind(path, '/') + if idx == -1: + dirs[''] = None + else: + dirs[path[:idx]] = None + + dirlist = dirs.keys() + + # figure out the common portion of all the dirs. note that there is + # no "common" if only a single dir was changed, or the root was changed. + if len(dirs) == 1 or dirs.has_key(''): + commondir = '' + else: + common = string.split(dirlist.pop(), '/') + for d in dirlist: + parts = string.split(d, '/') + for i in range(len(common)): + if i == len(parts) or common[i] != parts[i]: + del common[i:] + break + commondir = string.join(common, '/') + if commondir: + # strip the common portion from each directory + l = len(commondir) + 1 + dirlist = [ ] + for d in dirs.keys(): + if d == commondir: + dirlist.append('.') + else: + dirlist.append(d[l:]) + else: + # nothing in common, so reset the list of directories + dirlist = dirs.keys() + + # compose the basic subject line. later, we can prefix it. + dirlist.sort() + dirlist = string.join(dirlist) + if commondir: + self.output.subject = 'r%d - in %s: %s' % (repos.rev, commondir, dirlist) + else: + self.output.subject = 'r%d - %s' % (repos.rev, dirlist) + + def generate(self, groups, pool): + "Generate email for the various groups and option-params." + + ### these groups need to be further compressed. if the headers and + ### body are the same across groups, then we can have multiple To: + ### addresses. SMTPOutput holds the entire message body in memory, + ### so if the body doesn't change, then it can be sent N times + ### rather than rebuilding it each time. + + subpool = svn.util.svn_pool_create(pool) + + for (group, param_tuple), params in groups.items(): + self.output.start(group, params) + + # generate the content for this group and set of params + generate_content(self.output, self.cfg, self.repos, self.changelist, + group, params, subpool) + + self.output.finish() + svn.util.svn_pool_clear(subpool) + + svn.util.svn_pool_destroy(subpool) + + +class RevProp: + def __init__(self, cfg, output, repos, author, propname): + self.cfg = cfg + self.output = output + self.repos = repos + self.author = author + self.propname = propname + + self.output.subject = 'propchange - r%d - %s' % (repos.rev, propname) + + + def generate(self, pool): + self.output.start([], [], override_author = author) + + self.output.write('Author: %s\nRevision: %s\nProperty Name: %s\n\n' + % (self.author, self.repos.rev, self.propname)) + + propvalue = self.repos.get_rev_prop(self.propname) + + self.output.write('New Property Value:\n') + self.output.write(propvalue) + + self.output.finish() + + def generate_content(output, cfg, repos, changelist, group, params, pool): svndate = repos.get_rev_prop(svn.util.SVN_PROP_REVISION_DATE) @@ -450,7 +490,7 @@ src_fname, dst_fname = diff.get_files() - output.run_diff(cfg.get_diff_cmd({ + output.run(cfg.get_diff_cmd({ 'label_from' : label1, 'label_to' : label2, 'from' : src_fname, @@ -625,14 +665,36 @@ if __name__ == '__main__': - if len(sys.argv) < 3 or len(sys.argv) > 4: - sys.stderr.write('USAGE: %s REPOS-DIR REVISION [CONFIG-FILE]\n' - % sys.argv[0]) + def usage(): + sys.stderr.write( +'''USAGE: %s REPOS-DIR REVISION [CONFIG-FILE] + %s --revprop REPOS-DIR REVISION AUTHOR PROPNAME [CONFIG-FILE] +''' + % (sys.argv[0], sys.argv[0])) sys.exit(1) + if len(sys.argv) < 1: + usage() + + if sys.argv[1] == '--revprop': + if len(sys.argv) < 6 or len(sys.argv) > 7: + usage() + revprop = True + sys.argv.pop(0) + else: + if len(sys.argv) < 3 or len(sys.argv) > 4: + usage() + revprop = False + repos_dir = sys.argv[1] revision = int(sys.argv[2]) + if revprop: + author = sys.argv[3] + sys.argv.pop(3) + propname = sys.argv[3] + sys.argv.pop(3) + if len(sys.argv) == 3: # default to REPOS-DIR/conf/mailer.conf config_fname = os.path.join(repos_dir, 'conf', 'mailer.conf') @@ -647,7 +709,10 @@ raise MissingConfig(config_fname) ### run some validation on these params - svn.util.run_app(main, config_fname, repos_dir, revision) + if revprop: + svn.util.run_app(main_revprop, config_fname, repos_dir, revision, author, propname) + else: + svn.util.run_app(main, config_fname, repos_dir, revision) # ------------------------------------------------------------------------