#! /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)