Browse Source

Merge from 5.3 to 6.0:

[7015] Updated browser support list in Release Notes. For #2538.
[7028] Merge from branches/release/5.3.0 to versions/5.3 (multiple changesets concerning build)
[7039] Updated tutorial PDF, also added the header logo element.

svn changeset:7166/svn branch:6.0
tags/6.7.0.beta1
Henri Sara 15 years ago
parent
commit
b77e0f7dfb
5 changed files with 841 additions and 15 deletions
  1. 30
    14
      WebContent/release-notes.html
  2. 1
    1
      build/VERSION.properties
  3. 682
    0
      build/bin/mergetool.py
  4. 109
    0
      build/bin/svnlog-to-rn.py
  5. 19
    0
      build/bin/svnlog-to-rn.xsl

+ 30
- 14
WebContent/release-notes.html View File

@@ -331,7 +331,7 @@ for your platform.</p>
</ul>

<p>IT Mill Toolkit supports Java Servlet API 2.3 and later versions and should work with
any Java application servers that conform to the standard. It supports the following
any Java application server that conforms to the standard. It supports the following
application servers:</p>

<ul>
@@ -348,30 +348,46 @@ it:</p>

<ul>
<li>Mozilla Firefox releases 2, and 3 (see notice above about
Gecco 1.7).</li>
<li>Internet Explorer releases 6, 7, and 8.</li>
Gecko 1.7).</li>
<li>Internet Explorer releases 6, 7, and 8<sup>&#8224;</sup>.</li>
<li>Safari 3</li>
<li>Opera 9.6</li>
<li>Opera 9.6<sup>&#8225;</sup></li>
</ul>

<p>There may be differences between the exact versions of the supported browsers that may
cause incompatibility with applications made with IT Mill Toolkit. Support for Opera
has currently some limitations, such as the front page of the Sampler demo.</p>
<p style="font-size: 80%;">&#8224; Internet Explorer 8 RC1 may not be usable because of a
serious bug in the RC1. <a href="http://dev.itmill.com/ticket/2578">#2578</a><br/>

&#8225; The current support for Opera has some limitations, such as the front page of
the Sampler demo. <a href="http://dev.itmill.com/ticket/2652">#2652</a></p>

<p>The support for browsers follows the support by GWT. The browsers are supported on both
Windows and Mac, if available. Firefox is supported also on Linux (Opera 10a1 works also
on Linux though also suffers from <a
href="http://dev.itmill.com/ticket/2652">#2652</a>). There may be differences between the
exact versions of the supported browsers that may cause incompatibility with applications
made with IT Mill Toolkit.</p>

<p>The following browsers are not supported but have been found to
work to a large degree:</p>

<ul>
<li>Safari 2</li>
<li>Safari 2, and 4 beta</li>
<li>Firefox 1.5</li>
<li>iPhone</li>
<li>Epiphany, Galeon, and other Gecco-based browsers (see notice
about Gecco 1.7 above).</li>
<li>Google Chrome 1.0.x (available only for Windows)</li>
<li>iPhone (firmware 2.2)</li>
<li>Midori (0.1.2)</li>
<li>Epiphany (2.22.3), Galeon, and other Gecko-based browsers (see the notice
regarding Gecko 1.7 above). Also WebKit-based Epiphany (2.22.3) works.</li>
<li>Konqueror 4.2 (3.5.x does not work)</li>
<li>Nokia Internet Tablet N800 and N810 (ITOS 2008, Opera-based browser).</li>
</ul>

<p>Nokia E-series phones with at least 128MB of memory have been known to work with older
versions, but not with IT Mill Toolkit 5.2 and later. Konqueror, the default browser in
many Linux distributions, is known to not work.</p>
<p>The reported versions are those that have been tested, though other versions may work
as well.</p>

<p>Nokia E-series phones, such as E90, have been known to work with older versions, but
not with IT Mill Toolkit 5.2 and later. Links, Lynx, and other text-based browsers do not
work.</p>

<!-- h2>ChangeLog Between IT Mill Toolkit 5.1.2 and 5.2.0</h2 --></div>
<!-- /getting-started -->

+ 1
- 1
build/VERSION.properties View File

@@ -1 +1 @@
version=5.3.0-rc13
version=5.3.0

+ 682
- 0
build/bin/mergetool.py View File

@@ -0,0 +1,682 @@
#!/usr/bin/python

import sys,re,os,string,urllib,httplib

################################################################################
# Configuration
################################################################################

# Determine repository root
pin = os.popen("svn info|grep 'Repository Root'|sed -e 's/^.\+: //'", "r")
REPOSITORY = pin.read().rstrip()+"/"
pin.close()

print "Repository: %s" % (REPOSITORY)

################################################################################
# Parse command-line arguments
################################################################################
def help(exitvalue = 0):
print "Usage: batchmerge [options] <command>\n"
print "Options:"
print "\t-m\tOnly merge. (For 'single' command.)"
print "\t-c\tOnly commit. (For 'single' command.)"
print "\t-html\tHTML output. (For 'log' command.)"
print "\t-author\tHTML output. (Include author in HTML log.)"
print "\t-milestone <ms>\tList tickets in milestone (For 'log' command.)"
print "\nCommands:"
print "massmerge <cfg> <src> <trg> [<from>] "
print " - Merges changesets listed in the configuration"
print " file. The file is in svn log (text) format."
print " You can comment out unwanted changesets."
print " Merge is stopped on conflict."
print " If you give the optional <from> parameter,"
print " merge is started from the changeset number. "
print "single <chg#> <target> - Merges a single changeset. If -m is given,"
print " only merge is done. If -c is given, only commit is done."
print "revert - Reverts all changes made to repository except"
print " changes in this program and the merge files."
print "log <cfg> <src> <trg> - Prints a ChangeLog as it will appear in the"
print " commit log. If -html option is given,"
print " the log is printed in a HTML format"
print " suitable for the Release Notes."
print "commit <cfg> <src> <trg> - Commits all changes, except changes to this"
print " program and merge files. The commit log"
print " comment includes list of all changesets"
print " listed in the configuration. The <target>"
print " is the branch name, e.g., \"5.2\".\n"
print "Common parameters:"
print " <cfg> - Configuration file (svn text log format)."
print " <src> - Source branch relative to repository URI."
print " <trg> - Target branch relative to repository URI."
print "You must run the command in the root directory of the branch."
print "The program file contains some basic configuration parameters."
sys.exit(exitvalue)

################################################################################
# Globals
################################################################################
tickets = {}

################################################################################
# Utility Functions
################################################################################
def command(cmd, dryrun=0):
print cmd
if not dryrun:
if os.system(cmd):
print "Command failed, exiting."
sys.exit(1)
else:
print "Dry run - not executing."

def listChangedFiles():
# Get Svn status
pin = os.popen("svn st", "r")
lines = pin.readlines()
pin.close()

changed = []
for line in lines:
# Remove trailing newline
line = line[:-1]

# Extract the file state and name
(filestate, filename) = re.split(r'[ \+]+', line)

# Ignore files in build directory
if (filename.startswith("build/merge/") \
or filename.startswith("build/bin/mergetool.py") \
or filename.startswith("build/testing")) \
and filestate == "M":
continue

# File is changed if it is not local
if filestate != "?":
changed.append(filename)

return changed

# Retrieves ticket summary string with HTTP
# Returns: (summary, milestone)
def fetchSummary(ticketno):
params = urllib.urlencode({'format': 'tab'})
conn = httplib.HTTPConnection("dev.itmill.com")
conn.request("GET", "/ticket/%d?%s" % (ticketno, params) )
response = conn.getresponse()
data = response.read()
conn.close()

lines = data.split("\n")
data = reduce(lambda x,y: x+"\n"+y, lines[1:])
#cols = lines[1].split("\t")

cols = data.split("\t")

return (cols[1],cols[8])

# Adds summary to ticket number, unless the context already has it
# Returns: (summary, milestone)
def addSummary(m):
ticketnum = int(m.group(1))
context = m.group(2)
if re.match(" *\(", context):
# The context already has ticket summary
return "#%d%s" % (ticketnum, context)

(summary,milestone) = fetchSummary(ticketnum)

# Remove possible " quotation from the summary
if summary.startswith('"'):
summary = summary.strip('"')
summary = summary.replace('""', '"')

# Add summaries to further ticket numbers recursively
context = re.sub(r'#([0-9]+)(.*)', addSummary, context)

return "#%s (<i>%s</i>) %s" % (ticketnum, summary, context)

################################################################################
# Change
################################################################################
class Ticket:
def __init__(self, id, summary=None, milestone=None):
self.id = id
self.summary = summary
self.milestone = milestone

def fetchData(self):
(summary, milestone) = fetchSummary(self.id)
self.summary = summary
self.milestone = milestone

################################################################################
# Change
################################################################################
class Change:
def __init__(self, id, undo=0, author=""):
self.id = id
self.author = author
self.comment = ""
self.undo = undo
self.tickets = []

def addCommentLine(self, line):
self.comment += line

def merge(self, trunkURI, dryrun=0):
drycmd = ""
if dryrun:
drycmd = "--dry-run"

# Handle negative merge
mergesign = ""
if self.undo:
mergesign = "-"

# Build the merge command
cmd = "svn merge --non-interactive %s -c %s%d %s" % (drycmd, mergesign, self.id, trunkURI)
print cmd
# Run the merge command
pin = os.popen(cmd, "r")
lines = pin.readlines()
pin.close()

# Parse the lines for conflicts
conflicts = 0
for line in lines:
print line[:-1]

# Check for conflict
if line.startswith("C"):
conflicts += 1

# Check for skipped file
elif line.startswith("Skipped"):
conflicts += 1

filename = line[8:-1]

# Simply exit if there was any problem
if conflicts > 0:
print "Problems detected. Exiting."
sys.exit(1)

def fetchComment(self, trunkURI):
cmd = "svn log -r %d %s" % (self.id, trunkURI)
# Run the log command
pin = os.popen(cmd, "r")
lines = pin.readlines()
pin.close()

STATE_START = 0
STATE_COMMENT = 1
comment = None
state = STATE_START
for line in lines:
if state == STATE_START:
if line == "\n":
state = STATE_COMMENT
elif state == STATE_COMMENT:
if line.startswith("-----------------"):
self.comment = comment
return comment
elif comment:
comment += "\n" + line.rstrip("\n")
else:
comment = line.rstrip("\n")

self.comment = comment
return comment

def commit(self):
# Write the log comment to a temporary file
logtmpname = "/tmp/merge-single-%d.log" % (os.getpid())
fout = open(logtmpname, "w")
fout.write(self.comment)
fout.close()

# Get listo
files = listChangedFiles()
if len(files) == 0:
print "Error: Will not do empty commit."
sys.exit(1)

# Write the list of files to be committed to a temporary file
changedfiles = ("\n".join(files)) + "\n"
targettmpname = "/tmp/merge-targets-%d.txt" % (os.getpid())
fout = open(targettmpname, "w")
fout.write(changedfiles)
fout.close()
print changedfiles,

command("svn commit --file %s --targets %s" % (logtmpname, targettmpname))

command("rm %s %s" % (logtmpname, targettmpname))


def getNumber(self):
return self.id

def getComment(self):
return self.comment

def getUndo(self):
return self.undo

def getAuthor(self):
return self.author

def isForMilestone(self, milestone):
return self.author

def addSummary(self, m, target_milestone=None):
ticketnum = int(m.group(1))
context = m.group(2)
if re.match(" *\(", context):
# The context already has ticket summary
return "#%d%s" % (ticketnum, context)

# Check for cached ticket
if tickets.has_key(ticketnum):
summary = tickets[ticketnum].summary
ticket_milestone = tickets[ticketnum].milestone
else:
# Fetch ticket from server and add to cache
(summary,ticket_milestone) = fetchSummary(ticketnum)
tickets[ticketnum] = Ticket(ticketnum,summary,ticket_milestone)

self.tickets.append(ticketnum);

# Remove possible " quotation from the summary
if summary.startswith('"'):
summary = summary.strip('"')
summary = summary.replace('""', '"')

ticketnum = "#%s" % (ticketnum)
# Emphasize tickets matching the target milestone
if target_milestone:
if ticket_milestone.find(target_milestone) != -1:
ticketnum = "<b>%s</b>" % (ticketnum)

# Add summaries to further ticket numbers recursively
context = re.sub(r'#([0-9]+)(.*)', lambda m: self.addSummary(m, target_milestone=target_milestone), context)

return "%s (<i>%s</i>) %s" % (ticketnum, summary, context)

def registerTicket(self, m, ticketNumbers):
ticketNumbers[int(m.group(1))] = 1
return ""

# Returns a list of ticket numbers referenced by this change
def listTickets(self):
ticketNumbers = {}
re.sub(r'#([0-9]+)', lambda m: self.registerTicket(m,ticketNumbers=ticketNumbers), self.comment)
return ticketNumbers.keys()

################################################################################
# Read configuration file
################################################################################
class Configuration:
def __init__(self, cfgfilename, startfrom=0):
self.changes = []
self.readConfig(cfgfilename, startfrom)

def readConfig(self, cfgfilename, startfrom=0):
fin = open(cfgfilename, "r")
content = fin.readlines()
fin.close()

# Parse configuration
currentChange = None
skipChange = 0
for line in content:
m_changestart = re.match(r'(-?)r([0-9]+)', line)
m_endofchange = re.match(r'------+', line)
m_emptyline = re.match(r'^$', line)
if m_changestart:
# Parse negative merge
undo = 0
if m_changestart.group(1) == "-":
undo = 1

# Get changeset number
id = int(m_changestart.group(2))
# Skip the target if its number is too small
if startfrom != 0 and id < startfrom:
skipChange = 1

# Get the author
author = re.sub(r'\@.+', '', line.split("|")[1].strip())

currentChange = Change(id, undo=undo, author=author)
elif m_endofchange:
# Register changeset, unless it is marked
# for skipping.
if currentChange:
if skipChange:
skipChange = 0
else:
self.changes.append(currentChange)
currentChange = None
elif m_emptyline:
pass
else:
if currentChange:
currentChange.addCommentLine(line)

def massMerge(self, trunkURI, dryrun=0):
for change in self.changes:
change.merge(trunkURI, dryrun=dryrun)

def createLogComment(self):
# Create a log comment that lists all merged changesets with
# comments
logcomment = "Merge from %s to %s:\n" % (sourcebranch, targetbranch)
for change in self.changes:
changeno = change.getNumber()
changecomment = change.getComment().rstrip("\n")
if change.getUndo():
logcomment += "Reverted [%d] merge: %s\n" % (changeno, changecomment)
else:
logcomment += "Merged [%d]: %s\n" % (changeno, changecomment)
return logcomment

def logHtml(self, author=0, milestone=None):
# In author inclusion mode, include some styles to make a printout look better
if author:
print "<head>\n<style type=\"text/css\">\n"+ \
"tr {\n vertical-align: top;\n}\ntd {\n font-size: 8pt;\n}\n</style>\n</head>\n"
# Print header
print "<table id=\"changelog-table\">"
authorcolumnheader = ""
if author:
authorcolumnheader = "<td>Author</td>"
print " <tr><td>#</td><td>Changeset Comment</td>%s</tr>" % (authorcolumnheader)

# Print body: the changes
for change in self.changes:
changeno = change.getNumber()
changecomment = change.getComment().rstrip("\n")
changeref = "[%d]" % (changeno)

# Handle merge undo markup
if change.getUndo():
changeref = "<font class=\"changeset-merge-undone\">%s</font>" % (changeref)
changecomment = "<font class=\"changeset-merge-undone\">%s</font>" % (changecomment)
changecomment = "Reverted a change: "+changecomment

authorcolumn = ""
if author:
authorcolumn = "<td>%s</td>" % (change.getAuthor())

# Add ticket summary after ticket number, if missing
# TODO: this handles only one
changecomment = re.sub(r'#([0-9]+)(.*)', lambda m: change.addSummary(m, target_milestone=milestone), changecomment, 100)
# item = re.sub(r'#([0-9]+)(.*)', '#\\1 (SUMMARY) \\2', item)
# Change ticket numbers to references to tickets
changecomment = re.sub(r'#([0-9]+)', '#<a href="http://dev.itmill.com/ticket/\\1">\\1</a>', changecomment)
# Change changeset numbers to references to changesets
changecomment = re.sub(r'\[([0-9]+)\]', '[<a href="http://dev.itmill.com/changeset/\\1">\\1</a>]', changecomment)
changeref = re.sub(r'\[([0-9]+)\]', '[<a href="http://dev.itmill.com/changeset/\\1">\\1</a>]', changeref)

# See if any of the tickets have milestone under work.
if milestone:
for ticketnum in change.tickets:
ticket = tickets[ticketnum]
if ticket.milestone.find(milestone) != -1:
changeref = "<b>%s</b>" % (changeref)

# Make basic HTML formatting
item = " <tr><td>%s:</td><td>%s</td>%s</tr>" % (changeref, changecomment, authorcolumn)

print item
sys.stdout.flush()
print "</table>"

# Prints a commit log to standard output
def log(self, sourcebranch, targetbranch, html=0, author=0, milestone=None):
if html:
self.logHtml(author=author,milestone=milestone)
return
sys.stdout.write(self.createLogComment())

def commit(self, sourcebranch, targetbranch, dryrun=0):
logcomment = self.createLogComment()

# Write the log comment to a temporary file
logtmpname = "/tmp/massmerge-pid-%d.log" % (os.getpid())
fout = open(logtmpname, "w")
fout.write(logcomment)
fout.close()

# Write the list of files to be committed to a temporary file
changedfiles = "\n".join(listChangedFiles())
targettmpname = "/tmp/massmerge-targets-%d.txt" % (os.getpid())
fout = open(targettmpname, "w")
fout.write(changedfiles)
fout.close()

if dryrun:
print "Log:"
os.system("cat %s" % (logtmpname))
print "\nChanged Files:"
os.system("cat %s" % (targettmpname))
print "\n"

command("svn commit --file %s --targets %s" % (logtmpname, targettmpname), dryrun=dryrun)

command("rm %s %s" % (logtmpname, targettmpname))

def listTickets(self):
fixed = {}
for change in self.changes:
changeno = change.getNumber()
changeTickets = change.listTickets()
if len(changeTickets)>0 and change.comment.lower().find("fix") != -1:
for ticket in changeTickets:
fixed[ticket] = 1

fixedlist = fixed.keys()
fixedlist.sort()
# print "Fixed:", fixedlist

print "<ul>"
for ticketNum in fixedlist:
if not tickets.has_key(ticketNum):
ticket = Ticket(ticketNum)
ticket.fetchData()
tickets[ticketNum] = ticket
ticket = tickets[ticketNum]
# print "%d: %s" % (ticket.id, ticket.summary)
print " <li><a href=\"http://dev.itmill.com/ticket/%d\">#%d</a>: %s</li>" % (ticket.id, ticket.id, ticket.summary)
sys.stdout.flush()

print "</ul>"

################################################################################
# Commands
################################################################################

# Command: revert
def commandRevert():
# Get Svn status
pin = os.popen("svn st", "r")
lines = pin.readlines()
pin.close()

reverted = []
removed = []
for line in lines:
# Remove trailing newline
line = line[:-1]

# Extract the file state and name
(filestate, filename) = re.split(r'[ \+]+', line)

# Ignore files in build directory
if (filename.startswith("build/merge/") \
or filename.startswith("build/bin/")) \
and filestate == "M":
continue

# Added files are simply deleted
if filestate != "?":
reverted.append(filename)

# Added files have to be removed in addition to reverting
if filestate == "A":
removed.append(filename)

# Remove conflict choises
elif filestate == "?" and \
(filename.find(".merge-left.r") != -1 or \
filename.find(".merge-right.r") != -1):
removed.append(filename)

# Revert files marked for reverting
donework = 0
if len(reverted) > 0:
files = " ".join(reverted)
command("svn revert -R %s" % (files))
donework = 1

# Remove files marked for deletion
if len(removed) > 0:
files = " ".join(removed)
command("rm %s" % (files))
donework = 1

if not donework:
print "Nothing to do."

# Command: massmerge
def commandMassmerge(cfgfilename, sourceuri, startfrom, dryrun=0):
cfg = Configuration(cfgfilename, startfrom=startfrom)
cfg.massMerge(sourceuri, dryrun=dryrun)

# Command: single
def commandSingle(trunkuri, changeset, sourcebranch, targetbranch, onlymerge=0, onlycommit=0):
change = Change(changeset)
print "Found changeset with log comment:\n "+change.fetchComment(trunkuri) + "\n"

change.merge(trunkuri, dryrun=onlycommit)
if onlycommit:
print "Got file list."
else:
print "Merge successful."

# Change the comment
change.comment = "Merged [%d] from %s to %s branch: %s" % (change.id, sourcebranch, branchname, change.comment)
print "\nLog comment: \"%s\"" % (change.comment)
if not onlymerge:
print "Committing."
change.commit()

# Command: commit
def commandCommit(cfgfilename, sourcebranch, targetbranch, dryrun=0):
cfg = Configuration(cfgfilename)
cfg.commit(sourcebranch, targetbranch, dryrun=dryrun)

# Command: log
def commandLog(cfgfilename, sourcebranch, targetbranch, html=0, author=0, milestone=None):
cfg = Configuration(cfgfilename)
cfg.log(sourcebranch, targetbranch, html=html, author=author, milestone=milestone)

# Command: tickets
def commandTickets(cfgfilename):
cfg = Configuration(cfgfilename)
cfg.listTickets()

################################################################################
# Main Program
################################################################################

# Handle switches
dryrun = 0
html = 0
html_author = 0
html_milestone = None
onlymerge = 0
onlycommit = 0
while len(sys.argv)>1 and sys.argv[1][0] == '-':
if sys.argv[1] == "-d":
dryrun = 1
del sys.argv[1:2]

elif sys.argv[1] == "-html":
html = 1
del sys.argv[1:2]

elif sys.argv[1] == "-author":
html_author = 1
del sys.argv[1:2]

elif sys.argv[1] == "-milestone":
html_milestone = sys.argv[2]
del sys.argv[1:3]

elif sys.argv[1] == "-m":
onlymerge = 1
del sys.argv[1:2]

elif sys.argv[1] == "-c":
onlycommit = 1
del sys.argv[1:2]

else:
print "Invalid option '%s'." % (sys.argv[1])
sys.exit(1)

if len(sys.argv) < 2:
help(1)

# Handle commands
if sys.argv[1] == "revert":
commandRevert()

elif (len(sys.argv) == 4 or len(sys.argv)==5) and sys.argv[1] == "massmerge":
cfgfilename = sys.argv[2]
sourcebranch = sys.argv[3]
startfrom = None
if len(sys.argv)>4:
startfrom = int(sys.argv[4])
commandMassmerge(cfgfilename, sourceuri=REPOSITORY+sourcebranch, startfrom=startfrom, dryrun=dryrun)

elif len(sys.argv) == 5 and sys.argv[1] == "single":
changeset = int(sys.argv[2])
sourcebranch = sys.argv[3]
targetbranch = sys.argv[4]
commandSingle(REPOSITORY+sourcebranch, changeset, targetbranch, onlymerge=onlymerge, onlycommit=onlycommit)

elif len(sys.argv) == 5 and sys.argv[1] == "commit":
cfgfilename = sys.argv[2]
sourcebranch = sys.argv[3]
targetbranch = sys.argv[4]
commandCommit(cfgfilename, sourcebranch, targetbranch, dryrun=dryrun)

elif len(sys.argv) == 5 and sys.argv[1] == "log":
cfgfilename = sys.argv[2]
sourcebranch = sys.argv[3]
targetbranch = sys.argv[4]
commandLog(cfgfilename, sourcebranch, targetbranch, html=html, author=html_author, milestone=html_milestone)

elif len(sys.argv) == 3 and sys.argv[1] == "tickets":
cfgfilename = sys.argv[2]
commandTickets(cfgfilename)

else:
help(1)

+ 109
- 0
build/bin/svnlog-to-rn.py View File

@@ -0,0 +1,109 @@
#!/usr/bin/python
################################################################################
# SVN Log to ChangeLog generator for Release Notes
#
# Generates list of changes in HTML for ChangeLog
# from SVN Log in XML format. You typically generate the log with
# a command such as:
# svn log -v -r 1234:HEAD > svnlog-1234:HEAD.log.xml
# The command must be executed in the root directory of Toolkit project,
# either in the trunk or in the proper branch. The converter is then
# used as follows:
# ./build/bin/svnlog-to-rn.py svnlog-1234:HEAD.log.xml
#
# The ChangeLog generator will strip away any merges that begin with
# "Merged [...] from trunk to x.x branch."
#
# The generator will handle the following markup:
# - Changeset tags such as [1234] to links to dev.itmill.com/changeset/1234
# - Ticket references such as #1234 to links to dev.itmill.com/ticket/1234
# - If ticket reference does not have explanation in parentheses,
# the script will fetch the summary of the ticket from Trac and
# add it in parentheses after the reference, such as:
# "fixes #1234 (A nasty bug I found)".
#
# Requirements:
# - Xalan
################################################################################

import sys,re,os,httplib,urllib

################################################################################
# Convert XML to XHTML
# - The transformation includes various relevent information
# and does basic formatting
################################################################################

# Determine path to XSLT file
pathToScript = sys.argv[0]
sloc = pathToScript.rfind("/")
pathToScript = pathToScript[:sloc]

if len(sys.argv) != 2:
print "Usage: svnlog-to-rn.py <logfile.xml>"
print "Read the svnlog-to-rn.py header for more info."
sys.exit(1)

# Open Xalan
filename = sys.argv[1]
fin = open(filename, "r")
(pout,pin) = os.popen2("xalan -xsl %s/svnlog-to-rn.xsl" % (pathToScript))

# Preprocessing before feeding to XSLT Processor
lines = fin.readlines()
out = ""
for line in lines:
if line.find("action") != -1:
line = line.replace(r'>[^<]+/', '')
#print line,
pout.write(line)
pout.close()

################################################################################
# Helper functions for postprocessing
################################################################################

# Retrieves summary string with HTTP
def fetchSummary(ticketno):
params = urllib.urlencode({'format': 'tab'})
conn = httplib.HTTPConnection("dev.itmill.com")
conn.request("GET", "/ticket/%d?%s" % (ticketno, params) )
response = conn.getresponse()
data = response.read()
conn.close()

lines = data.split("\n")
cols = lines[1].split("\t")
return cols[1]

# Adds summary to ticket number, unless the context already has it
def addSummary(m):
ticketnum = int(m.group(1))
context = m.group(2)
if re.match(" *\(", context):
# The context already has ticket summary
return "#%d%s" % (ticketnum, context)

summary = fetchSummary(ticketnum)

return "#%s (<i>%s</i>) %s" % (ticketnum, summary, context)

################################################################################
# Postprocessing for XSLT output
################################################################################

lines = pin.readlines()
for line in lines:
# Add ticket summary after ticket number, if missing
line = re.sub(r'#([0-9]+)(.*)', addSummary, line)
# Change ticket numbers to references to tickets
line = re.sub(r'#([0-9]+)', '#<a href="http://dev.itmill.com/ticket/\\1">\\1</a>', line)
# Change changeset numbers to references to changesets
#line = re.sub(r'\[([0-9]+)\]', '[<a href="http://dev.itmill.com/changeset/\\1">\\1</a>]', line)

# Remove prefix about merging
line = re.sub(r'Merged.+from trunk to [0-9]+.[0-9]+ branch: ', '', line)
print line,

+ 19
- 0
build/bin/svnlog-to-rn.xsl View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:output method="html"/>

<xsl:template match="logentry">
<li>[<xsl:element name="a"><xsl:attribute name="href">http://dev.itmill.com/changeset/<xsl:value-of select="@revision"/></xsl:attribute><xsl:value-of select="@revision"/></xsl:element>]: <xsl:value-of select="msg"/></li>
</xsl:template>

<xsl:template match="/">
<html>
<body bgcolor="#FFFFFF">
<xsl:apply-templates/>
</body>
</html>
</xsl:template>

</xsl:stylesheet>

Loading…
Cancel
Save