Mailing List Archive

SVN commit-integration
H'lo folks.

So I wrote the Subversion-commit-integration script that I mentioned
previousely and wanted to show it to ya'll for thoughts. Mostly I want to
know if I did anything incorrect in your database. :)

It allows you to put commands into your commit messages like: "fixes
#2, #3" or "closes #4 and #12", or, "closes #4 and references #7". I wanted
it to be as 'easy' as possible to use, so put in as many alternate spellings
into the regex as I could think of.

I started to put in the code to allow it to 'closes #4 as
(resolution)', but then figured that didn't make sense-- it seems to me that
any resolution other then 'fixed' wouldn't be triggered by someone commiting
code into the repository.

Thoughts? Any ideas for other needs of such a script? I've attached
it (as a .txt).

Thankee.

--Stephen
-------------- next part --------------
#!/usr/bin/env python

# svn-trac-hook

# ----------------------------------------------------------------------------
# Copyright (c) 2004 Stephen Hansen
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
# ----------------------------------------------------------------------------

# This Subversion post-commit hook script is meant to interface to the
# Trac (http://www.edgewall.com/products/trac/) issue tracking/wiki/etc
# system.
#
# It should be called from the 'post-commit' script in Subversion, such as
# via:
#
# svn-trac-hook "$(svnlook author -r $REV $REPOS)" $REV "$(svnlook log -r $REV $REPOS)"
#
# It searches commit messages for text in the form of:
# command #1
# command #1, #2
# command #1 & #2
# command #1 and #2
#
# You can have more then one command in a message. The following commands
# are supported. There is more then one spelling for each command, to make
# this as user-friendly as possible.
#
# closes, fixes
# The specified issue numbers are closed with the contents of this
# commit message being added to it.
# references, refs, addresses, re
# The specified issue numbers are left in their current status, but
# the contents of this commit message are added to their notes.
#
# A fairly complicated example of what you can do is with a commit message
# of:
#
# Changed blah and foo to do this or that. Fixes #10 and #12, and refs #12.
#
# This will close #10 and #12, and add a note to #12.

import re
import os
import sys
import time

import sqlite

_TRAC_DB = '/usr/share/trac/trac.db'

commandPattern = re.compile(r'(?P<action>[A-Za-z]*).?(?P<ticket>#[0-9]+(?:(?:[, &]*|[ ]?and[ ]?)#[0-9]+)*)(?: ?as (?P<arg>[A-Za-z]*))?')
ticketPattern = re.compile(r'#([0-9]*)')

class CommitHook:
_supported_cmds = { "closes": '_cmdClose',
"fixes": '_cmdClose',
"references": '_cmdRefs',
"refs": '_cmdRefs',
"re": '_cmdRefs',
}
def __init__(self, author, rev, msg):
self.author = author
self.rev = rev
self.msg = "(In SVN Revision %s) %s" % (rev, msg)
self.now = int(time.time())
self.con = sqlite.connect(_TRAC_DB, autocommit=0)

cmdGroups = commandPattern.findall(msg)
for cmd, tkts, arg in cmdGroups:
try:
getattr(self, CommandHook._supported_cmds[cmd.lower()])(ticketPattern.findall(tkts))
except:
pass
self.con.commit()

def _cmdClose(self, tickets):
cur = self.con.cursor()
for tkt in tickets:
cur.execute("SELECT status FROM ticket WHERE id=%d", int(tkt))
row = cur.fetchone()
oldStatus = row[0]
cur.execute("UPDATE ticket SET status='closed', resolution='fixed', changetime=%s WHERE id=%s", self.now, int(tkt))
cur.execute("INSERT INTO ticket_change (ticket, time, author, field, oldvalue, newvalue) VALUES (%s, %s, %s, %s, %s, %s)",
int(tkt), self.now, self.author, 'comment', '', self.msg)
cur.execute("INSERT INTO ticket_change (ticket, time, author, field, oldvalue, newvalue) VALUES (%s, %s, %s, %s, %s, %s)",
int(tkt), self.now, self.author, 'status', oldStatus, 'closed')
cur.execute("INSERT INTO ticket_change (ticket, time, author, field, oldvalue, newvalue) VALUES (%s, %s, %s, %s, %s, %s)",
int(tkt), self.now, self.author, 'resolution', '', 'fixed')

def _cmdRefs(self, tickets):
cur = self.con.cursor()
now = int(time.time())
for tkt in tickets:
cur.execute("UPDATE ticket SET changetime=%s WHERE id=%s", self.now, int(tkt))
cur.execute("INSERT INTO ticket_change (ticket, time, author, field, oldvalue, newvalue) VALUES (%s, %s, %s, %s, %s, %s)",
int(tkt), self.now, self.author, 'comment', '', self.msg)

if __name__ == "__main__":
if len(sys.argv) != 4:
sys.exit(1)

CommitHook( sys.argv[1], sys.argv[2], sys.argv[3] )
SVN commit-integration [ In reply to ]
On Sun, Mar 07, 2004 at 05:12:44PM -0800, Stephen Hansen wrote:
> H'lo folks.
>
> So I wrote the Subversion-commit-integration script that I mentioned
> previousely and wanted to show it to ya'll for thoughts. Mostly I want to
> know if I did anything incorrect in your database. :)
>

Cool, this is exactly what we had in mind. With this kind of script it will
be easy for users to configure the exact behavior without having to learn
the trac database schema.

I have two comments about the code:
1. The database filename should probably be a command line argument.
2. It looks like the script will try to commit the transaction even if the
_cmdFOO function throws an exception.

> It allows you to put commands into your commit messages like: "fixes
> #2, #3" or "closes #4 and #12", or, "closes #4 and references #7". I wanted
> it to be as 'easy' as possible to use, so put in as many alternate spellings
> into the regex as I could think of.
>

I think that will work just fine. But some users might want to be able to write
"closes #7" without actually closing the ticket and only want the script to
parse commands inside parenthesis or something like this:

"(closes #7)" or "[closes #7]".

So you could perhaps include a regexp (commented out by default) that
requires parenthesis around the commands.

> I started to put in the code to allow it to 'closes #4 as
> (resolution)', but then figured that didn't make sense-- it seems to me that
> any resolution other then 'fixed' wouldn't be triggered by someone commiting
> code into the repository.
>
exactly, I also think "closed" is the only resolution the script need to
care about.

Good work, this script should probably be added to the contrib directory if
it's ok with you. (of course with the commit message "closes #96" :)

/ Jonas
--
Jonas Borgstr?m | Edgewall Software
jonas@edgewall.com | Professional GNU/Linux & Open Source Consulting.
| http://www.edgewall.com/
SVN commit-integration [ In reply to ]
> > So I wrote the Subversion-commit-integration script
> that I mentioned
> > previousely and wanted to show it to ya'll for thoughts.
> Mostly I want
> > to know if I did anything incorrect in your database. :)
> >
>
> Cool, this is exactly what we had in mind. With this kind of
> script it will be easy for users to configure the exact
> behavior without having to learn the trac database schema.
>
> I have two comments about the code:
> 1. The database filename should probably be a command line argument.

My first thought was to use a SetEnv directive in the Subversion
section of the apache configuration, but during debugging and doing a print
os.environ, it didn't seem to be there by the time the script ran. But yeah,
having it be a global is sub-optimal. I'll fiddle.

> 2. It looks like the script will try to commit the
> transaction even if the
> _cmdFOO function throws an exception.

That shouldn't matter unless sqlite operates significantly different
then PostgreSQL-- if any errors occur in the transaction, a .commit doesn't
do anything. In fact, in postgresql (or mxODBC for that matter) it'll raise
an exception. And since this is a post-commit script, how we exit the script
is irrelevent. I thought of error-checking but I don't actually think
there's any benefit: if it fails it fails all the way, and there's nothing
we can do. I may be wrong in regards to sqlite-- I'll test it out.

> > It allows you to put commands into your commit messages
> like: "fixes
> > #2, #3" or "closes #4 and #12", or, "closes #4 and
> references #7". I
> > wanted it to be as 'easy' as possible to use, so put in as many
> > alternate spellings into the regex as I could think of.
> >
>
> I think that will work just fine. But some users might want
> to be able to write "closes #7" without actually closing the
> ticket and only want the script to parse commands inside
> parenthesis or something like this:
>
> "(closes #7)" or "[closes #7]".
>
> So you could perhaps include a regexp (commented out by
> default) that requires parenthesis around the commands.

Good idea. Might make it a command-line option.

> > I started to put in the code to allow it to 'closes #4 as
> > (resolution)', but then figured that didn't make sense-- it
> seems to
> > me that any resolution other then 'fixed' wouldn't be triggered by
> > someone commiting code into the repository.
> >
> exactly, I also think "closed" is the only resolution the
> script need to care about.
>
> Good work, this script should probably be added to the
> contrib directory if it's ok with you. (of course with the
> commit message "closes #96" :)

I don't mind at all. I'll fiddle some more and send another to the
list, and if it meets approval you can feel free to add it.

--Stephen
SVN commit-integration [ In reply to ]
[.AOn Mon, Mar 08, 2004 at 02:42:29AM -0800, Stephen Hansen wrote:
> > 2. It looks like the script will try to commit the
> > transaction even if the
> > _cmdFOO function throws an exception.
>
> That shouldn't matter unless sqlite operates significantly different
> then PostgreSQL-- if any errors occur in the transaction, a .commit doesn't
> do anything. In fact, in postgresql (or mxODBC for that matter) it'll raise
> an exception. And since this is a post-commit script, how we exit the script
> is irrelevent. I thought of error-checking but I don't actually think
> there's any benefit: if it fails it fails all the way, and there's nothing
> we can do. I may be wrong in regards to sqlite-- I'll test it out.
>
Perhaps if it's a sqlite error that might happen. But if a non-sqlite error
(IndexError, KeyboardInterrupt, etc) occurs during the transaction. Then
the "except: pass" statement in __init__ will void that exception and commit the
half done transaction anyway. This problem (albeit unlikely) can be avoided by
moving the commit statement into the _cmd-functions and add a rollback
statement to the exception handler.

> > Good work, this script should probably be added to the
> > contrib directory if it's ok with you. (of course with the
> > commit message "closes #96" :)
>
> I don't mind at all. I'll fiddle some more and send another to the
> list, and if it meets approval you can feel free to add it.
>
One other thing, this line:

self.msg = "(In SVN Revision %s) %s" % (rev, msg)

Should perhaps be:

self.msg = "(In [%s]) %s" % (rev, msg)

To get a nice wiki hyperlink to the changeset.

/ Jonas
--
Jonas Borgstr?m | Edgewall Software
jonas@edgewall.com | Professional GNU/Linux & Open Source Consulting.
| http://www.edgewall.com/