土曜日, 6月 07, 2008

TracAPIを使ったSubversionコミットメール

昨日のやつをもう少し精査してみました。 trac-post-commit-hook.pyを少しいじって(いじる場所はちょっと前に書いてます。) とりあえず、作ったのは
  • trac.svn.notification.py
  • postcommit_notify_email.cs
の2つです。 .csはClearSilverのテンプレートだそうだ。 ソースはこんな感じ まずはtrac.svn.notification.py
# -*- coding: utf-8 -*-

# Copyright (C) 2003-2006 Edgewall Software
# Copyright (C) 2003-2005 Daniel Lundin 
# Copyright (C) 2005-2006 Emmanuel Blot 
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://trac.edgewall.org/wiki/TracLicense.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://trac.edgewall.org/log/.
#
# Author:gara
#

from trac import __version__
from trac.config import *
from trac.core import *
from trac.notification import NotifyEmail
from trac.util.datefmt import format_datetime
from trac.util.text import CRLF, wrap
from trac.versioncontrol.api import NoSuchChangeset
from trac.versioncontrol.diff import unified_diff
from trac.mimeview.api import is_binary
import md5

_kindmap = {'dir':u"ディレクトリ", 'file':u"ファイル"}
_actionmap = {'add': u"追加", 'copy': u"コピー",
            'delete': u"削除", 'edit': u"更新",
            'move': u"移動"}

logfile = "postcommit_notification.log"
LOG = True

if LOG:
  f = open (logfile, "w")
  f.write("Begin Log\n")
  f.close()
  def log (s, *params):
      f = open (logfile, "a")
      f.write(s % params)
      f.write("\n")
      f.close()
else:
  def log (s, *params):
      pass

class PostCommitNotificationSystem(Component):

  always_notify_owner = BoolOption('notification', 'always_notify_owner',
                                   'false',
      u"""チケットの担当者に常に通知メールを送信するかを設定します (''0.9 以降'') 。""")

  always_notify_reporter = BoolOption('notification', 'always_notify_reporter',
                                      'false',
      u"""''報告者'' フィールドにあるアドレスに常に通知メールを
      送信するかを設定します。""")

  always_notify_updater = BoolOption('notification', 'always_notify_updater',
                                     'true',
      u"""チケットの属性の変更者に常に通知メールを
      送信するかを設定します。""")


class PostCommitNotifyEmail(NotifyEmail):
  """Subversionのコミット情報をメール送信"""

  template_name = "postcommit_notify_email.cs"
  from_email = 'trac+ticket@localhost'
  COLS = 75

  def __init__(self, env):
      self.env = env
      self.repos = self.env.get_repository()
      self.repos.sync()
      NotifyEmail.__init__(self, env)
      self.prev_cc = []

  def notify(self, rev):
      self.rev = rev
      try:
          chgset = self.repos.get_changeset(rev)
      except NoSuchChangeset:
          return # キャッシュされているチェンジセットの値からはみ出ている
      self.chgset = chgset
      self.message = wrap(self.chgset.message,
                                       self.COLS, initial_indent=' ',
                                       subsequent_indent=' ', linesep=CRLF)
      self.reporter = ''
      self.owner = ''
      subject = self.format_subj()
      link = self.env.abs_href.changeset(self.rev)
      self.link = link
      self.hdf.set_unescaped('email.subject', subject)
      self.hdf.set_unescaped('email.commit.author', self.chgset.author)
      self.hdf.set_unescaped('email.commit.date', format_datetime(self.chgset.date))
      self.hdf.set_unescaped('email.commit.diff', self.unified_diff())
      self.hdf.set_unescaped('email.commit.message', self.message)
      self.hdf.set_unescaped('email.commit.link', self.link)
    
      NotifyEmail.notify(self, self.rev, subject)

  def get_recipients(self, rev):
      notify_reporter = self.config.getbool('notification',
                                            'always_notify_reporter')
      notify_owner = self.config.getbool('notification',
                                         'always_notify_owner')
      notify_updater = self.config.getbool('notification',
                                           'always_notify_updater')

      ccrecipients = self.prev_cc
      torecipients = []

      return (torecipients, ccrecipients)

  def format_subj(self):
      prefix = self.config.get('notification', 'smtp_subject_prefix')
      if prefix == '__default__':
          prefix = '[%s]' % self.config.get('project', 'name')
      if prefix:
          return '%s rev:%s' % (prefix, self.rev)
      else:
          return 'rev:%s' % (self.rev)

  def get_message_id(self, rcpt, modtime=0):
      """Generate a predictable, but sufficiently unique message ID."""
      s = '%s.%08d.%d.%s' % (self.config.get('project', 'url'),
                             int(self.rev), self.chgset.date,
                             rcpt.encode('ascii', 'ignore'))
      dig = md5.new(s).hexdigest()
      host = self.from_email[self.from_email.find('@') + 1:]
      msgid = '<%03d.%s@%s>' % (len(s), dig, host)
      return msgid

  def send(self, torcpts, ccrcpts):
      dest = self.reporter or 'anonymous'
      hdrs = {}
      hdrs['X-svn-changeset-ID'] = str(self.rev)
      msgid = self.get_message_id(dest)
      hdrs['In-Reply-To'] = msgid
      hdrs['References'] = msgid
      NotifyEmail.send(self, torcpts, ccrcpts, hdrs)

  def unified_diff(self):
      txt = CRLF
      try:
          chgset = self.repos.get_changeset(self.rev)
      except NoSuchChangeset:
           return # out of scope changesets are not cached
      for set in chgset.get_changes():
          lst = list(set)
          path = list(set)[0]
          kind = list(set)[1]
          change = list(set)[2]
          base_path = list(set)[3]
          base_rev = list(set)[4]
          if kind != u"file":
              continue
          if change == u"add":
              continue
          elif change == u"delete":
              continue
          elif change == u"move":
              continue
          elif change == u"copy":
              continue
          new_node = self.repos.get_node(path, chgset.rev)
          old_node = self.repos.get_node(base_path, base_rev)
          txt += CRLF
          txt += u"diff: " + path + CRLF
          line = "%s %s %s" % (_actionmap.get(change), _kindmap.get(kind), path)
          txt += wrap(line, self.COLS, linesep=CRLF) + CRLF
          txt += wrap(u"Unified Diff--------------------------------------------------------------", self.COLS, linesep=CRLF) + CRLF
          new_content = new_node.get_content().read()
          old_content = old_node.get_content().read()
          if is_binary(new_content) or is_binary(old_content):
              txt += u"(バイナリファイルが異なっています)" + CRLF
              continue

          txt += wrap(u"--- %s (リビジョン %s)" % (base_path, base_rev), self.COLS, linesep=CRLF) + CRLF
          txt += wrap(u"+++ %s (リビジョン %s)" % (path, chgset.rev), self.COLS, linesep=CRLF) + CRLF
          context = 3
          options = ['-U%d' % -1]
          options.append('-B')
          options.append('-i')
          options.append('-b')
          for option in options:
              if option.startswith('-U'):
                  context = int(option[2:])
                  break
          for line in unified_diff(old_content.splitlines(),
                                new_content.splitlines(), context,
                                    ignore_blank_lines='-B' in options,
                                    ignore_case='-i' in options,
                                    ignore_space_changes='-b' in options):
              txt += wrap(line, self.COLS, linesep=CRLF) + CRLF

      return txt
そんでもってpostcommit_notify_email.cs
更新 (更新者: <?cs var:email.commit.author ?>):
更新日時: <?cs var:email.commit.date ?>

コメント:
<?cs var:email.commit.message ?>
<?cs var:email.commit.diff ?>
--
Commit URL: <<?cs var:email.commit.link ?>>
<?cs var:project.name ?> <<?cs var:project.url ?>>
<?cs var:project.descr ?>
こんなんでも、誰かの参考になれば。。。 いいなw

0 件のコメント:

failed to read qemu headerのときのメモ

かなり久々。。。 忘れないようにここに書きこんでおく。 ちょっとした手違いで libvirtでイメージを起動しようとすると failed to read qemu header なんておっしゃられて起動しない。。。 vmwareserverを使って...