#!/usr/bin/python # -*- coding: utf-8 -*- # # darcs_send_changes.py # --------------------- # # Copyright (C) 2007 Jeremie Dimino # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This file is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. """ Envoie un mail détaillant le dernier patch appliqué à un dépot. """ import commands, os, sys, email, re, cStringIO try: from lxml import etree except: sys.stderr.write("darcs_send_changes recquiert le paquet python-lxml.\n") sys.stderr.flush() sys.exit(1) sys.path.append("/usr/scripts/gestion") from affich_tools import cprint from unicode2ascii import unicode2ascii def darcs(args): """ Invoque darcs et renvoie sa sortie. """ return commands.getoutput("darcs " + args) def get_patch_properties(): """ Récupère les informations a propos du dernier patch. """ prop = etree.XML(darcs("changes --last 1 --xml-output"))[0] diff = darcs("diff --last 1 --unified") diff = diff[diff.find('\ndiff ')+1:] cwd = os.getcwd() return { 'author': prop.attrib['author'], 'repo': "%s:%s" % (commands.getoutput('hostname -s'), cwd), 'shortrepo': os.path.basename(cwd), 'date': prop.attrib['local_date'], 'hash': prop.attrib['hash'], 'name': prop.findtext('name'), 'comment': prop.findtext('comment'), 'diff': diff, 'changes': darcs('changes --last 1 --summary') } DEFAULT_TEMPLATE = """From: %(author)s To: %(recipient)s Subject: Darcs record (%(shortrepo)s): %(name)s %(changes)s %(diff)s """ TEMPLATE_FILE = "_darcs/third-party/darcs-send-changes/email-template" __darcs_escaped_re = re.compile('\\[_\\\\[0-9a-f][0-9a-f]_\\]') def darcs_unescape(str): """ Converti les séquences d'échappement de darcs en charactère unicode. """ start = 0 newstr = cStringIO.StringIO() s = __darcs_escaped_re.search(str, start) while s: newstr.write(str[start:s.start()]) newstr.write(chr(int(str[s.start()+3:s.start()+5], 16))) start = s.end() s = __darcs_escaped_re.search(str, start) newstr.write(str[start:]) return newstr.getvalue() def send_changes(smtp, recipient, patch_props, template=DEFAULT_TEMPLATE): """ Formatte et envoie un mail avec les modifications sur le dernier patch appliqué au dépot. recipient est une liste des destinataires du mail. template est une chaîne de format python qui peut contenir les variables suivantes: * author, date, hash, name, comment * repo: le dépot (hote + chemin) * shortrepo: basename(chemin du dépot) * changes: la sortie de darcs changes --summary * diff: la sortie de darcs diff --unified a partir du premier diff * recipient: les destinataires du mail si template est None, DEFAULT_TEMPLATE est utilisé. """ patch_props["recipient"] = ", ".join(recipient) rawmail = darcs_unescape((template or DEFAULT_TEMPLATE) % patch_props) mail = email.message_from_string(rawmail) if not mail['Content-Transfer-Encoding']: mail['Content-Transfer-Encoding'] = 'quoted-printable' if not mail.get_charset(): mail.set_charset("UTF-8") # On met le titre en ascii sinon c'est atroce pour le filtrage # automatique subject = unicode2ascii(mail['Subject'].decode("UTF-8")) mail.replace_header('Subject', subject) rawmail = mail.as_string() for to in recipient: smtp.sendmail(patch_props['author'], to, rawmail) def __usage(err=None): if err: cprint("%s\n" % err) cprint("""Usage: %(name)s [OPTIONS] destinataires pour en savoir plus faites « %(name)s --help » """ % { 'name': os.path.basename(sys.argv[0]) }) sys.exit(2) def __help(): cprint("""Usage: %(name)s [options] destinataires Les options disponibles sont: -h, --help affiche cette aide -s, --smtp spécifie le serveur smtp à utiliser -r, --repo spécifie l'emplacement du dépôt -t, --template fichier a utiliser comme template Si aucun destinataires n'est donné, roots@crans.org est utilisé. """ % { 'name': os.path.basename(sys.argv[0]) }) sys.exit(0) if __name__ == "__main__": import smtplib, getopt smtp = 'localhost' repo = None template = None try: options, arg = getopt.getopt(sys.argv[1:], 'hs:r:t:', [ 'help', 'smtp=', 'repo=', 'template=']) except getopt.error, msg: __usage(unicode(msg)) for opt, val in options: if opt in [ '-h', '--help' ]: __help() elif opt in [ '-s', '--smtp' ]: smtp = val elif opt in [ '-r', '--repo' ]: repo = val elif opt in [ '-t', '--template' ]: template = val else: __usage("option inconnue « %s »'" % opt) recipient = arg if len(recipient) == 0: recipient = [ 'roots@crans.org' ] if repo: os.chdir(repo) if not template and os.path.exists(TEMPLATE_FILE): template = TEMPLATE_FILE if template: f = open(template) template = f.read() f.close() cprint("Envoie du patch a " + ", ".join(recipient)) send_changes(smtplib.SMTP(smtp), recipient, get_patch_properties(), template)