summaryrefslogtreecommitdiffstats
path: root/release-notes/rtems-release-notes
diff options
context:
space:
mode:
Diffstat (limited to 'release-notes/rtems-release-notes')
-rwxr-xr-xrelease-notes/rtems-release-notes167
1 files changed, 167 insertions, 0 deletions
diff --git a/release-notes/rtems-release-notes b/release-notes/rtems-release-notes
new file mode 100755
index 0000000..1cbf3d0
--- /dev/null
+++ b/release-notes/rtems-release-notes
@@ -0,0 +1,167 @@
+#! /usr/bin/env python
+#
+# RTEMS Tools Project (http://www.rtems.org/)
+# Copyright 2022 Chris Johns (chris@contemporary.software)
+# All rights reserved.
+#
+# This file is part of the RTEMS Tools package in 'rtems-tools'.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+import argparse
+import sys
+
+import trac
+import tickets
+import reports
+
+
+def get_notes(notes_file):
+ return [l[:-1] for l in open(notes_file, 'r').readlines()] if notes_file is not None else None
+
+
+def milestone_to_major_minor(release):
+ rtems_major, rtems_minor = release.split('.', 1)
+ try:
+ major = int(rtems_major)
+ rm = ''
+ for c in rtems_minor:
+ if c.isdigit():
+ rm += c
+ else:
+ break
+ try:
+ minor = int(rm)
+ except:
+ raise RuntimeError('invalid release: ' + milestone)
+ except:
+ raise RuntimeError('invalid release: ' + milestone)
+ return major, minor
+
+
+def milestone_from_major_minor(major, minor):
+ return '%d.%d' % (major, minor)
+
+
+def milestones(release, reverse=False):
+ major, minor = milestone_to_major_minor(release)
+ ms = [milestone_from_major_minor(major, m) for m in range(1, minor + 1)]
+ if reverse:
+ ms.reverse()
+ return ms
+
+
+def collect_tickets(release, cache, force):
+ '''
+ Collect the tickets for the release and all previous release milestones
+
+ A release is major.minor[-.*] from minor back to 1.
+ '''
+ ticks = {}
+ for milestone in milestones(release):
+ print(
+ f"Fetching and processing tickets for release {release} milestone {milestone}."
+ )
+ tcache = trac.cache(milestone, cache, force)
+ ticks[milestone] = tickets.tickets(release=release, milestone=milestone)
+ ticks[milestone].load(cache=tcache)
+ return ticks
+
+
+def generate(ticks, release, notes_file):
+ rtems_major, rtems_minor = milestone_to_major_minor(release)
+ notes = {}
+ for milestone in milestones(release):
+ notes[milestone] = get_notes(notes_file % (milestone))
+ gen = reports.generator(release)
+ gen.generator.gen_heading('Table of Content', reports.heading_base)
+ for milestone in milestones(release, reverse=True):
+ print(
+ f"Formatting tickets for release {release} milestone {milestone}."
+ )
+ t = ticks[milestone]
+ gen.set_milestone(milestone)
+ gen.gen_toc(notes[milestone])
+ for milestone in milestones(release, reverse=True):
+ t = ticks[milestone]
+ gen.generator.gen_page_break()
+ gen.generator.gen_line_break()
+ gen.set_milestone(milestone)
+ gen.gen_start(notes[milestone])
+ gen.gen_overall_progress(t.tickets['overall_progress'])
+ gen.gen_tickets_summary(t.tickets['tickets'])
+ gen.gen_tickets_stats_by_category(t.tickets['by_category'])
+ gen.gen_individual_tickets_info(t.tickets['tickets'])
+ return gen.generator.content
+
+
+if __name__ == '__main__':
+
+ args = argparse.ArgumentParser()
+
+ args.add_argument('-r',
+ '--release',
+ required=True,
+ dest='release',
+ help='The release to report',
+ type=str,
+ default=None)
+ args.add_argument('-f',
+ '--force',
+ dest='force',
+ help='Force downloading of tickets',
+ action='store_true')
+ args.add_argument('-c',
+ '--cache',
+ dest='cache',
+ help='Cache file base name of ticket data, one per milestone',
+ type=str,
+ default=None)
+ args.add_argument('-o',
+ '--output',
+ required=True,
+ dest='output',
+ help='Output file',
+ type=str,
+ default=None)
+ args.add_argument('-N',
+ '--notes',
+ dest='notes',
+ help='Top-level, manually-written release notes',
+ default=None)
+
+ opts = args.parse_args()
+
+ if opts.cache is not None:
+ cache = opts.cache
+ else:
+ cache = '.rng-cache'
+
+ ticks = collect_tickets(release=opts.release, cache=cache, force=opts.force)
+ contents = generate(ticks, opts.release, opts.notes)
+
+ print('Writing ' + opts.output)
+
+ with open(opts.output, 'w') as f:
+ f.write(contents)