172 lines
5.7 KiB
Python
Executable file
172 lines
5.7 KiB
Python
Executable file
#!/usr/bin/python
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# darcs_send_changes.py
|
|
# ---------------------
|
|
#
|
|
# Copyright (C) 2007 Jeremie Dimino <jeremie@dimino.org>
|
|
#
|
|
# 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 <serveur> spécifie le serveur smtp à utiliser
|
|
-r, --repo <chemin> spécifie l'emplacement du dépôt
|
|
-t, --template <fichier> 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)
|