Статьи

Имитация перехвата электронной почты Subversion Commit на стороне клиента

Если вы используете Subversion, вы, вероятно, знаете, что можете включить хук фиксации электронной почты на сервере. Затем вы можете настроить SVN для отправки писем при каждом коммите. Вы также можете (я думаю, не пробовал этого) настроить его, установив свойства hook: commit- email в папке интересов. Здесь, на моей новой работе, мы также используем Subversion для контроля версий. Однако сервер Subversion не настроен на отправку сообщений о коммитах. Я пошел поговорить с нашим администратором SVN, и он сказал мне, что наш сервер SVN слишком стар, поэтому мы не получим это в ближайшее время.

Ладно … хорошо, это не проблема, я подумал, что могу смоделировать ту же функцию на стороне клиента с помощью небольшого количества сценариев. Основная идея заключается в следующем: выгрузить проект с svn на локальный диск, иметь скрипт для выполнения сравнения с хранилищем каждые 5 минут, а затем сценарий для отправки электронной почты, если есть непустой diff.

Я немного погуглил, но не смог найти таких сценариев, поэтому я решил написать один сам с отличным сценарием
. Сценарий работает следующим образом:

1. Проверьте проект, к которому вы хотите добавить ловушку, затем каждые 5 минут выполняйте команду ‘svn log -r BASE: HEAD’ для этого проекта, чтобы получить список ревизий и журналов, вывод svn log выглядит примерно так (с опцией —incremental)

  
------------------------------------------------------------------------
r183 | zl25-drexel | 2008-06-18 22:44:32 -0400 (Wed, 18 Jun 2008) | 1 line

that's good enough for now
------------------------------------------------------------------------
r184 | zl25-drexel | 2008-06-18 22:57:06 -0400 (Wed, 18 Jun 2008) | 1 line

test
------------------------------------------------------------------------
r185 | zl25-drexel | 2008-06-18 22:59:11 -0400 (Wed, 18 Jun 2008) | 1 line

2. Выполните анализ этих журналов, чтобы получить набор номеров ревизий (а также автора, дату и т. Д.). Затем для каждой ревизии выполните ‘svn diff -r $ rev1: $ rev2’ для каждой смежной ревизии. Например, если ревизиями были r182, r183 и r184, то сценарий должен различать r182: r183 и r183: r184. Для каждого сравнения отправьте электронное письмо, содержащее результаты сравнения.

3. После того, как все различия выполнены, выполните ‘svn up -r $ lastrev’, чтобы привести вашу локальную копию к последней ревизии, с которой вы проверяли (примечание: не обновляйте ее до HEAD, потому что во время скрипт рассылает электронные письма).

Вуаля! с помощью описанных выше 3 простых шагов вы получите точно такую ​​же функциональность, как если бы на сервере была включена перехват фиксации электронной почты.

Вот скрипт для вашего удовольствия от просмотра

#!/usr/bin/env groovy
import java.text.SimpleDateFormat
import javax.mail.internet.InternetAddress
import javax.mail.Message
import javax.mail.internet.MimeMessage
import javax.mail.Transport
import javax.mail.Session
import groovy.text.SimpleTemplateEngine

def chill = 5 //min
def emailconfig = [protocol:'smtps',
host : 'smtp.gmail.com',
port : 465, //must be int
user : 'XXX', password : 'XXX']
def projects = [
'projectName':/C:\Projects\project1/,
'projectName2':/C:\Project\project2/
]



def subject="[<%=rev%>] [SVN:<%=name%>] [Author:<%=author%>]"
def body = """<%=name%> revision <%=rev%> report
Author: <%=author%>
Date: <%=date%>

Log Message:
--------------------------------------------------
<%=log%>
--------------------------------------------------

<%=fulldiff%>
"""
def engine = new SimpleTemplateEngine()
def BODY = engine.createTemplate(body)
def SUBJECT = engine.createTemplate(subject)
def ps = new PrintStream(
new BufferedOutputStream(new FileOutputStream('svndiff.log')))
def printLog(def msg, def ps){
def df = new SimpleDateFormat()
println "[${df.format(new Date())}] $msg"
ps.println "[${df.format(new Date())}] $msg"
}

def parseLog(def log){
try{
def lines = []
new StringReader(log).eachLine{ line ->
lines << line
}
if(lines.size < 2) return;
def ret = [:]
def items = lines[1].split(/[|]/)
ret['rev'] = items[0].trim().substring(1)
ret['author'] = items[1]
ret['date'] = items[2]
ret['log'] = log
return ret
}catch (Exception e) {
e.printStackTrace()
return null
}
}

def getBASErev(def path){
def base = 'BASE'
"svn info -r BASE $path".execute().in.eachLine{
def m = it=~/Revision:\s+(\d+)/
if(m.matches()){ base = m.group(1) }
}
return base
}

while(true){
try{
projects.each{name, path ->
printLog("processing $name", ps)
def revisions =
"svn log -r BASE:HEAD --limit 30 --incremental $path".execute().text.split('-'*72)
def BASErev = getBASErev(path)
def lastrev = null
def logs = [['rev':'BASE']]
revisions.each{rev ->
def parsed = parseLog(rev)
if (parsed != null)logs << parsed
}
if(logs.size == 1){
printLog('already up to date, nothing to process', ps); return
}
for(int i =0; i< logs.size -1; i++){
def rev1 = logs[i], rev2 = logs[i+1]
if(rev2['rev'] == BASErev){ continue;} //no need to diff BASE

def diff = "svn diff -r ${rev1['rev']}:${rev2['rev']} $path".execute().text
def s = SUBJECT.make([rev: rev2['rev'], name: name, author: rev2['author'] ]).toString()
def b = BODY.make([name: name, rev: rev2['rev'],
author:rev2['author'], date: rev2['date'],
log: logs[i+1]['log'], fulldiff: diff]).toString()
printLog( "sending $s ...", ps)
sendEmail(s, b, emailconfig)
lastrev = rev2['rev']
}
// bring the local to the last rev we had diffed
if(lastrev != null){
printLog("svn up -r $lastrev $path", ps) ;
"svn up -r $lastrev $path".execute() }
}
}catch(Exception e){
e.printStackTrace()
}
printLog('\nChilling out for 5 min ...\n', ps)
//chill out for 5 min
sleep chill*60*1000
}

def sendEmail(def subject, def body, def config){
Properties props = new Properties()
props.put("mail.transport.protocol", config.protocol);
props.put("mail.smtps.host", config.host);
props.put("mail.smtps.auth", "true");

Session session = Session.getDefaultInstance(props)
Transport transport = session.getTransport()

MimeMessage message = new MimeMessage(session)
message.setSubject(subject)
message.setContent(body, 'text/plain')
message.addRecipient(Message.RecipientType.TO,
new InternetAddress('[email protected]'))

transport.connect(config.host, config.port, config.user, config.password)

transport.sendMessage(message,
message.getRecipients(Message.RecipientType.TO));
transport.close();

}


A few things should be noted. First of all, it uses java mail, which is not part of the JDK. So make sure you have it
available in groovy's classpath (just throw the jars in $GROOVY_HOME/lib). I am using gmail's smtp server just for illustration
purposes. It's probably not a good idea to have your company's svn commits messages all go thru gmail, it might get you into troubles. You
should use your company's internal smtp server for internal projects.

This script works as long as you have read accesses to a repository. So if you are not a developer of projects say apache commons but still
want to track its revisions thru commit emails, you can hook this script to their repository and receive emails for every commit!

There are many features in Groovy (this language makes me so happy 🙂 ) which make scripting extremely easy. The above script showcased quite a
few of them. Imagine if you need to write it in Java how much codes you need to write. I don't think I will want to write this in Java.
 
Here's a sample email that was produced by the above script:
myproject revision 173 report

Author:  zl25-drexel

Date:  2008-06-17 20:26:38 -0400 (Tue, 17 Jun 2008)



Log Message:

------------------------------

r173 | зл25-дрексел | 2008-06-17 20:26:38 -0400 (вт, 17 июня 2008 г.) | 1 линия

тест тест тест

——————————
————— ——

Индекс: C: / cygwin / home / jliang /
testdiff / jgeocoder / jgeocoder /
difftest.txt

========================= =====
==============================
=======

— C: / cygwin / home / jliang /
testdiff / jgeocoder / jgeocoder /
difftest.txt (версия 172)

+++ C: / cygwin / home / jliang /
testdiff / jgeocoder / jgeocoder /
difftest.txt (версия 173)

@@ -2,4 +2 , 4 @@ еще

 одна строка

— еще одна строка

\ Нет новой строки в конце файла

+ еще один тест

\ Нет новой строки в конце файла

Индекс: C: / cygwin / home / jliang /
testdiff / jgeocoder / jgeocoder /
src / main / java / net / sourceforge /
jgeocoder / test / JaysTest.java

===== =========================
========================= =====
=======

— C: / cygwin / home / jliang /
testdiff / jgeocoder / jgeocoder /
src / main / java / net / sourceforge /
jgeocoder / test / JaysTest.java (редакция 172 )

+++ C: / cygwin / home / jliang /
testdiff / jgeocoder / jgeocoder /
src / main / java / net / sourceforge /
jgeocoder / test / JaysTest.java (редакция 173)

@@ -10,7 +10,11 @@

 import net.sourceforge.jgeocoder.
tiger.H2DbDataSourceFactory;

 импорт net.sourceforge.jgeocoder.
tiger.JGeocoder;

 импорт net.sourceforge.jgeocoder.
tiger.JGeocoderConfig;



+ / **

+ * бла-бла

+ * @author jliang

+ *

+ * /

 открытый класс JaysTest {

  открытый статический void main (String [] args) {

    JGeocoderConfig config = new JGeocoderConfig ();