Бывают случаи, когда я хочу быстро выполнить поиск в хранилище Subversion по автору, диапазону ревизий и / или сообщениям о фиксации. Кшиштоф Котович опубликовал в своем блоге сообщения журнала Grep Subversion с помощью svn-grep, который представляет svn-grep , скрипт bash, использующий инструментарий командной строки XML под названием xmlstarlet (xmlstarlet также доступен в Windows ). Сам по себе это довольно полезный скрипт, но он дал мне идею для скрипта на основе Groovy, который мог бы работать на нескольких (всех поддерживаемых JVM) платформах.
searchSvnLog.groovy
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
#!/usr/bin/env groovy//// searchSvnLog.groovy//def cli = new CliBuilder( usage: 'searchSvnLog.groovy -r <revision1> -p <revision2> -a <author> -s <stringInMessage>')import org.apache.commons.cli.Optioncli.with{ h(longOpt: 'help', 'Usage Information', required: false) r(longOpt: 'revision1', 'First SVN Revision', args: 1, required: false) p(longOpt: 'revision2', 'Last SVN Revision', args: 1, required: false) a(longOpt: 'author', 'Revision Author', args: 1, required: false) s(longOpt: 'search', 'Search String', args: 1, required: false) t(longOpt: 'target', 'SVN target directory/URL', args: 1, required: true)}def opt = cli.parse(args)if (!opt) returnif (opt.h) cli.usage()Integer revision1 = opt.r ? (opt.r as int) : nullInteger revision2 = opt.p ? (opt.p as int) : nullif (revision1 != null && revision2 != null && revision1 > revision2){ println "It makes no sense to search for revisions ${revision1} through ${revision2}." System.exit(-1)}String author = opt.a ? (opt.a as String) : nullString search = opt.s ? (opt.s as String) : nullString logTarget = opt.tString command = "svn log -r ${revision1 ?: 1} ${revision2 ?: 'HEAD'} ${logTarget} --xml"def proc = command.execute()StringBuilder standard = new StringBuilder()StringBuilder error = new StringBuilder()proc.waitForProcessOutput(standard, error)def returnedCode = proc.exitValue() if (returnedCode != 0){ println "ERROR: Returned code ${returnedCode}"}def xmlLogOutput = standard.toString()def log = new XmlSlurper().parseText(xmlLogOutput)def logEntries = new TreeMap<Integer, LogEntry>()log.logentry.each{ svnLogEntry -> Integer logRevision = Integer.valueOf(svnLogEntry.@revision as String) String message = svnLogEntry.msg as String String entryAuthor = svnLogEntry.author as String if ( (!revision1 || revision1 <= logRevision) && (!revision2 || revision2 >= logRevision) && (!author || author == entryAuthor) && (!search || message.toLowerCase().contains(search.toLowerCase())) ) { def logEntry = new LogEntry(logRevision, svnLogEntry.author as String, svnLogEntry.date as String, message) logEntries.put(logRevision, logEntry) }}logEntries.each{ logEntryRevisionId, logEntry -> println "${logEntryRevisionId} : ${logEntry.author}/${logEntry.date} : ${logEntry.message}"} |
Одна вещь, которая делает этот сценарий намного проще для написания, это возможность команды журнала Subversion записывать свои выходные данные в формате XML с флагом –xml . Хотя XML в последние годы подвергался серьезной критике, одной из вещей, которые мне понравились в его доступности, является широко распространенная поддержка инструментов для написания и чтения XML. Способность Subversion писать определенные типы вывода в XML является хорошим примером этого. Без XML скрипту потребовалось бы написать собственный код синтаксического анализа для анализа нестандартного вывода журнала SVN. Поскольку Subversion поддерживает запись в стандартный формат XML для своего вывода, любой инструмент, поддерживающий XML, может прочитать его. В этом случае я использовал невероятно простую возможность Groovy XML slurping (XML-синтаксический анализ).
В сценарии также используется расширенный класс Process (GDK) Groovy, как я кратко описал в своем недавнем посте «Великолепная простота написания сценариев с помощью Groovy» .
Встроенная в Groovy поддержка командной строки ( CliBuilder ) используется в сценарии для принятия параметров для сужения поиска (таких как применимые ревизии, авторы, которые зафиксировали, или строки для поиска комментариев фиксации). Одним обязательным параметром является «target», который может быть файлом, каталогом или URL-адресом.
Сценарий ссылается на класс Groovy с именем LogEntry, и список кода для этого класса показан далее.
LogEntry.groovy
|
1
2
3
4
5
6
7
8
|
@groovy.transform.Canonicalclass LogEntry{ int revision String author String date String message} |
Этот простой на вид класс LogEntry гораздо мощнее, чем может показаться на первый взгляд. Поскольку это Groovy, для четырех атрибутов автоматически доступны методы установки / получения. Благодаря аннотации @Canonical он также поддерживает методы конструктора, equals , hashCode и toString . Другими словами, этот класс, состоящий из менее десяти строк, имеет методы доступа и мутатора, а также методы общего класса, переопределенные для него соответствующим образом.
Вывод
Groovy предлагает множество функций, облегчающих написание сценариев. В этом посте я использовал пример «поиска» --xml Subversion с помощью команды log Subversion (и его опции --xml ), чтобы продемонстрировать некоторые из этих полезных функций сценариев Groovy (синтаксический анализ параметров командной строки, встроенная интеграция с операционной системой и простой XML разбор). Кроме того, были использованы некоторые из приятных синтаксических преимуществ Groovy (замыкания, динамическая типизация, заполнители значений GString).