summaryrefslogtreecommitdiff
path: root/tools/generate_release_notes.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/generate_release_notes.py')
-rw-r--r--tools/generate_release_notes.py137
1 files changed, 137 insertions, 0 deletions
diff --git a/tools/generate_release_notes.py b/tools/generate_release_notes.py
new file mode 100644
index 0000000..934dd0b
--- /dev/null
+++ b/tools/generate_release_notes.py
@@ -0,0 +1,137 @@
+#!/usr/bin/python
+
+# Copyright 2009-2010 Canonical Ltd.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+"""Generate doc/en/release-notes/index.txt from the per-series NEWS files.
+
+NEWS files are kept in doc/en/release-notes/, one file per series, e.g.
+doc/en/release-notes/bzr-2.3.txt
+"""
+
+# XXX: add test_source test that latest doc/en/release-notes/bzr-*.txt has the
+# NEWS file-id (so that merges of new work will tend to always land new NEWS
+# entries in the latest series).
+
+
+import os.path
+import re
+import sys
+from optparse import OptionParser
+
+
+preamble_plain = """\
+####################
+Bazaar Release Notes
+####################
+
+
+.. contents:: List of Releases
+ :depth: 2
+
+"""
+
+preamble_sphinx = """\
+####################
+Bazaar Release Notes
+####################
+
+
+.. toctree::
+ :maxdepth: 2
+
+"""
+
+
+def natural_sort_key(file_name):
+ """Split 'aaa-N.MMbbb' into ('aaa-', N, '.' MM, 'bbb')
+
+ e.g. 1.10b1 will sort as greater than 1.2::
+
+ >>> natural_sort_key('bzr-1.10b1.txt') > natural_sort_key('bzr-1.2.txt')
+ True
+ """
+ file_name = os.path.basename(file_name)
+ parts = re.findall(r'(?:[0-9]+|[^0-9]+)', file_name)
+ result = []
+ for part in parts:
+ if re.match('^[0-9]+$', part) is not None:
+ part = int(part)
+ result.append(part)
+ return tuple(result)
+
+
+def output_news_file_sphinx(out_file, news_file_name):
+ news_file_name = os.path.basename(news_file_name)
+ if not news_file_name.endswith('.txt'):
+ raise AssertionError(
+ 'NEWS file %s does not have .txt extension.'
+ % (news_file_name,))
+ doc_name = news_file_name[:-4]
+ link_text = doc_name.replace('-', ' ')
+ out_file.write(' %s <%s>\n' % (link_text, doc_name))
+
+
+def output_news_file_plain(out_file, news_file_name):
+ f = open(news_file_name, 'rb')
+ try:
+ lines = f.readlines()
+ finally:
+ f.close()
+ title = os.path.basename(news_file_name)[len('bzr-'):-len('.txt')]
+ for line in lines:
+ if line == '####################\n':
+ line = '#' * len(title) + '\n'
+ elif line == 'Bazaar Release Notes\n':
+ line = title + '\n'
+ elif line == '.. toctree::\n':
+ continue
+ elif line == ' :maxdepth: 1\n':
+ continue
+ out_file.write(line)
+ out_file.write('\n\n')
+
+
+def main(argv):
+ # Check usage
+ parser = OptionParser(usage="%prog OUTPUT_FILE NEWS_FILE [NEWS_FILE ...]")
+ (options, args) = parser.parse_args(argv)
+ if len(args) < 2:
+ parser.print_help()
+ sys.exit(1)
+
+ # Open the files and do the work
+ out_file_name = args[0]
+ news_file_names = sorted(args[1:], key=natural_sort_key, reverse=True)
+
+ if os.path.basename(out_file_name) == 'index.txt':
+ preamble = preamble_sphinx
+ output_news_file = output_news_file_sphinx
+ else:
+ preamble = preamble_plain
+ output_news_file = output_news_file_plain
+
+ out_file = open(out_file_name, 'w')
+ try:
+ out_file.write(preamble)
+ for news_file_name in news_file_names:
+ output_news_file(out_file, news_file_name)
+ finally:
+ out_file.close()
+
+
+if __name__ == '__main__':
+ main(sys.argv[1:])