diff options
Diffstat (limited to 'release-notes/rtems-release-notes')
-rwxr-xr-x | release-notes/rtems-release-notes | 167 |
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) |