scripts/gestion/darcs_send_changes.py
Nicolas Dandrimont 8b3d4f05e6 Patch en pièce jointe pour la notification Darcs
Cette modification permet d'envoyer le patch en pièce jointe (inline) au lieu de le mettre directement dans le corps du message

darcs-hash:20071214223740-ffbb2-96d9400a9935026956c93c1b35933d9a49b12d85.gz
2007-12-14 23:37:40 +01:00

229 lines
7.8 KiB
Python
Executable file

#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# darcs_send_changes.py
# ---------------------
#
# Copyright (C) 2007 Jeremie Dimino <jeremie@dimino.org>
# Copyright (C) 2007 Nicolas Dandrimont <Nicolas.Dandrimont@crans.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
try:
from lxml import etree
except:
sys.stderr.write("darcs_send_changes requiert le paquet python-lxml.\n")
sys.stderr.flush()
sys.exit(1)
sys.path.append("/usr/scripts/gestion")
from affich_tools import cprint, encoding
from unicode2ascii import unicode2ascii
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.Utils import formatdate
from email import Encoders
def to_utf8(str):
""" Decode un str ou un unicode vers un str en UTF-8. """
if isinstance(str, unicode):
return str.encode("UTF-8")
else:
try:
# Si c'est une chaine brute, on commend par essayer
# de la décoder comme une chaine en UTF-8
str.decode("UTF-8")
return str
except:
try:
return str.decode(encoding).encode("UTF-8")
except:
return str
def darcs(args):
""" Invoque darcs et renvoie sa sortie. """
return to_utf8(commands.getoutput("env DARCS_DONT_ESCAPE_8BIT=1 darcs " + args))
def get_patch_properties(hash):
""" Récupère les informations a propos d'un certain patch. """
prop = etree.XML(darcs("changes --match='hash %s' --xml-output" % hash))[0]
diff = darcs("diff --match='hash %s' --unified" % hash)
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 --match='hash %s' --summary" % hash) }
def get_patches_properties(from_hash):
""" Construit la liste des informations sur les patches à partir du patch from_hash. """
changelog = etree.XML(darcs("changes --from-match='hash %s' --reverse --xml-output" % from_hash))
props = []
for change in changelog[1:]:
# Ca peut parraitre inutile de refaire un darcs changes, mais c'est pour palier aux problemes
# d'encodages
props.append(get_patch_properties(change.attrib['hash']))
return props
CONF_PATH = "_darcs/third-party/darcs-send-changes"
LAST_SEEN_FILE = CONF_PATH + "/last-seen"
def send_changes(smtp, recipient, patch_props):
""" Formate et envoie un mail avec les modifications sur le dernier
patch appliqué au dépot.
recipient est une liste des destinataires du mail.
Les différents templates sont des chaînes de format python qui peuvent
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
"""
from_template = "%(author)s"
subject_template = "Darcs record (%(shortrepo)s): %(name)s"
message_template = "%(changes)s"
diff_template = "%(diff)s"
# On met toutes valeurs en string, en UTF-8
for key, val in patch_props.items():
patch_props[key] = to_utf8(val)
mail = MIMEMultipart()
# On met le titre en ascii sinon c'est atroce pour le filtrage
# automatique
subject = subject_template % patch_props
if subject:
subject = unicode2ascii(subject.decode("UTF-8"))
mail['Subject'] = subject_template % patch_props
mail['From'] = from_template % patch_props
mail['To'] = ", ".join(recipient)
mail['Date'] = formatdate(localtime=True)
mail['Mail-Followup-To'] = ", ".join(recipient)
mail['Mail-Reply-To'] = from_template % patch_props
mail['X-CVSinfo'] = "CRANS"
mail['X-DarcsInfo'] = "CRANS-%(shortrepo)s" % patch_props
texte = MIMEText(message_template % patch_props, "UTF-8")
texte.set_charset("UTF-8")
mail.attach(texte)
patch = MIMEText(diff_template % patch_props, "UTF-8")
patch.set_type('text/x-patch')
patch.set_charset("UTF-8")
patch.add_header('Content-Disposition', 'inline', filename='%(hash)s.diff' % patch_props)
mail.attach(patch)
if not mail['Content-Transfer-Encoding']:
mail['Content-Transfer-Encoding'] = '8bit'
mail.set_charset("UTF-8")
for to in recipient:
smtp.sendmail(patch_props['author'], to, mail.as_string())
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
-f, --from <hash> hash du premier patch de la série a envoyer
Si aucun destinataire 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
from_hash = None
try:
options, arg = getopt.getopt(sys.argv[1:], 'hs:r:f:', [ 'help', 'smtp=', 'repo=', 'from='])
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 [ '-f', '--from' ]:
from_hash = val
else:
__usage("option inconnue « %s »'" % opt)
recipient = arg
if len(recipient) == 0:
recipient = [ 'roots@crans.org' ]
if repo:
os.chdir(repo)
else:
while not os.path.exists('_darcs') and os.getcwd() != '/':
os.chdir('..')
if not os.path.exists('_darcs') and os.getcwd() == '/':
cprint("Pas de dépôt darcs trouvé")
sys.exit(1)
if not from_hash:
if os.path.exists(LAST_SEEN_FILE):
f = open(LAST_SEEN_FILE)
from_hash = f.read().strip()
f.close()
else:
from_hash = ""
patches = get_patches_properties(from_hash)
if len(patches) == 0:
sys.exit(0)
for patch in patches:
cprint("Envoi du patch %s a %s." % (patch['hash'], ", ".join(recipient)))
send_changes(smtplib.SMTP(smtp), recipient, patch)
if not os.path.exists(CONF_PATH):
p = ""
for comp in CONF_PATH.split('/'):
p = "%s/%s" % (p, comp)
if not os.path.exits(p):
os.mkdir(p)
f = open(LAST_SEEN_FILE, "w")
f.write(patches[-1]['hash'])
f.close()