16
16
# along with this program; if not, write to the Free Software
17
17
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
"""Generate doc/en/release-notes/index.txt from the per-series NEWS files.
21
NEWS files are kept in doc/en/release-notes/, one file per series, e.g.
22
doc/en/release-notes/bzr-2.3.txt
25
# XXX: add test_source test that latest doc/en/release-notes/bzr-*.txt has the
26
# NEWS file-id (so that merges of new work will tend to always land new NEWS
27
# entries in the latest series).
21
33
from optparse import OptionParser
24
def split_into_topics(lines, out_file, out_dir):
25
"""Split a large NEWS file into topics, one per release.
27
Releases are detected by matching headings that look like
28
release names. Topics are created with matching names
29
replacing spaces with dashes.
42
.. contents:: List of Releases
47
preamble_sphinx = """\
59
def natural_sort_key(file_name):
60
"""Split 'aaa-N.MMbbb' into ('aaa-', N, '.' MM, 'bbb')
62
e.g. 1.10b1 will sort as greater than 1.2::
64
>>> natural_sort_key('bzr-1.10b1.txt') > natural_sort_key('bzr-1.2.txt')
32
for index, line in enumerate(lines):
33
maybe_new_topic = line[:4] in ('bzr ', 'bzr-',)
34
if maybe_new_topic and lines[index + 1].startswith('####'):
35
release = line.strip()
36
if topic_file is None:
38
out_file.write(".. toctree::\n :maxdepth: 1\n\n")
40
# close the current topic
42
topic_file = open_topic_file(out_file, out_dir, release)
44
topic_file.write(line)
46
# FIXME: the 'content' directive is used for rst2html (and
47
# conflicts with the 'toctree' we insert), we should get rid of
48
# that once we fully switch to sphinx -- vila 20100505
49
if (line.startswith('.. contents::')
50
or line.startswith(' :depth:')):
52
# Still in the header - dump content straight to output
54
if topic_file is not None:
55
# Close the last topic_file opened
59
def open_topic_file(out_file, out_dir, release):
60
topic_name = release.replace(' ', '-')
61
out_file.write(" %s <%s>\n" % (release, topic_name,))
62
topic_path = os.path.join(out_dir, "%s.txt" % (topic_name,))
63
result = open(topic_path, 'w')
64
result.write("%s\n" % (release,))
67
file_name = os.path.basename(file_name)
68
parts = re.findall(r'(?:[0-9]+|[^0-9]+)', file_name)
71
if re.match('^[0-9]+$', part) is not None:
77
def output_news_file_sphinx(out_file, news_file_name):
78
news_file_name = os.path.basename(news_file_name)
79
if not news_file_name.endswith('.txt'):
81
'NEWS file %s does not have .txt extension.'
83
doc_name = news_file_name[:-4]
84
link_text = doc_name.replace('-', ' ')
85
out_file.write(' %s <%s>\n' % (link_text, doc_name))
88
def output_news_file_plain(out_file, news_file_name):
89
f = open(news_file_name, 'rb')
94
title = os.path.basename(news_file_name)[len('bzr-'):-len('.txt')]
96
if line == '####################\n':
97
line = '#' * len(title) + '\n'
98
elif line == 'Bazaar Release Notes\n':
100
elif line == '.. toctree::\n':
102
elif line == ' :maxdepth: 1\n':
105
out_file.write('\n\n')
70
parser = OptionParser(usage="%prog SOURCE DESTINATION")
110
parser = OptionParser(usage="%prog OUTPUT_FILE NEWS_FILE [NEWS_FILE ...]")
71
111
(options, args) = parser.parse_args(argv)
73
113
parser.print_help()
76
116
# Open the files and do the work
78
outfile_name = args[1]
79
outdir = os.path.dirname(outfile_name)
80
infile = open(infile_name, 'r')
82
lines = infile.readlines()
85
outfile = open(outfile_name, 'w')
87
split_into_topics(lines, outfile, outdir)
117
out_file_name = args[0]
118
news_file_names = sorted(args[1:], key=natural_sort_key, reverse=True)
120
if os.path.basename(out_file_name) == 'index.txt':
121
preamble = preamble_sphinx
122
output_news_file = output_news_file_sphinx
124
preamble = preamble_plain
125
output_news_file = output_news_file_plain
127
out_file = open(out_file_name, 'w')
129
out_file.write(preamble)
130
for news_file_name in news_file_names:
131
output_news_file(out_file, news_file_name)
92
136
if __name__ == '__main__':