# We use argparse rather than getopt because it allows us to setup a parser as # a separate object and then we can test input combination with an external # python script that uses the unittest module. import argparse import logging import os import sys # handle_exit is a context manager which guarantees that the project temporary # directories are cleaned up when there are signals. from linaropy.handle_exit import handle_exit from datetime import datetime from linaropy.series import Series from linaropy.rn.gccclone import GCCClone from linaropy.git.clone import Clone from linaropy.rn.linaroseries import LinaroSeries from linaropy.rn.linaroseries import linaroSeriesFromBranchname from linaropy.rn.linaroseries import linaro_series_from_tag from linaropy.proj import Proj from linaropy.git.gitrepo import cd from linaropy.rn.template import RNTemplate from linaropy.rn.rnseries import RNSeries from linaropy.rn.rngen import rngen from linaropy.rninput import yninput from linaropy.rninput import finput from jinja2.exceptions import TemplateSyntaxError import traceback rnProj = [] # Cleanup the temporary project directory if necessary. def rncleanup(): if not rnProj: # This will be the case if the script is run via the test driver. print "No cleanup needed" else: print "Cleaning up Proj dir %s if possible." % rnProj[0].projdir rnProj[0].cleanup() def generate(track, to_date, to_series, gccsource, persist): # Delay creating the Proj directory until now so that the parser (and # parser validation functions) can be tested in the unittests without # invoking the internals of this driver. rnProj.append(Proj(prefix='rn', persist=persist)) print "proj dir is: %s with persist=%s" % (rnProj[0].projdir, str(persist)) # This will raise an exception if gccsource is not a git repository. gccclone = GCCClone(rnProj[0], clonedir=gccsource) # use gccsource to figure out the GCC Base Version and the FSF Version # from the git commit history. print 'gccbaseversion is ' + gccclone.get_base_version() print 'fsf revision is ' + gccclone.get_fsf_revision() if gccclone.tag_exists(track): logging.info("%s is a tag. Creating Series from tag.") track_series = linaro_series_from_tag(track) else: logging.info("%s is a branch? Creating Series from branchname.") track_series = linaroSeriesFromBranchname(track) try: next_series = track_series.toNext(to_series) except TypeError: print( "Next series '%s' from '%s' in an invalid progression" % (LinaroSeries.series[to_series], track_series.shorttype())) print("If this is a release series try tracking the release-candidate tag instead of the release branch.") sys.exit(2) if to_date != next_series.date: raise RuntimeError( 'The date passed to this driver does not equal the date computed by LinaroSeries.toNext()') rnclone = Clone( rnProj[0], remote=u'ssh://git@git.linaro.org/toolchain/release-notes.git') next_rn = RNSeries( rnProj[0], rnrepo=rnclone.clonedir(), track_series=track_series, next_series=next_series) if next_rn.update_templ_readmes(): print "Please verify that your changes have been committed on the template branch:" next_rn.rn_template.log(1) ans = True history = finput( 'Please enter the location of the changelog csv file: ', "6.1-2016.06.csv") next_rn.update_series_readmes() while ans == True: # Generate the temporary output files to the projdir. with cd(rnProj[0].projdir): try: rngen(next_rn, gccclone, history) except TemplateSyntaxError: traceback.print_exc(file=sys.stdout) print "Please correct the template and try again." print "Please direct your browser to the rendered .html files in" print "%s and make sure that they look correct." % rnProj[0].projdir ans = next_rn.update_series_readmes() # Verify that the GCC Source is located where it says it is. class VerifyGCCSourceAction(argparse.Action): def __init__(self, option_strings, dest, nargs=None, **kwargs): if nargs is not None: raise ValueError("nargs not allowed") super(VerifyGCCSourceAction, self).__init__( option_strings, dest, **kwargs) def __call__(self, parser, namespace, values, option_string=None): print('%r %r %r' % (namespace, values, option_string)) setattr(namespace, self.dest, values) print "gccsource: " + values # We simply want to test that the directory exists. We'll prove that # it is a git directory in a later step. if not os.path.isdir(values): sys.exit(2) def str_to_datetime(datestr): # strptime will throw an exception if the input date is not a string or is # not parsable. if len(datestr) < 10: inputdate = datetime.strptime(datestr, "%Y.%m") else: inputdate = datetime.strptime(datestr, "%Y.%m.%d") # Force the 'day' to 15 to unify comparisons. return inputdate.replace(day=15) def create_parser(): parser = argparse.ArgumentParser( prog="rn.py", description='''Generate release notes.''' ) # Positionals are required by default. parser.add_argument( 'track', help='branchname or tag name of series to track.') parser.add_argument('-g', '--gccsource', dest='gccsource', required=True, action=VerifyGCCSourceAction, help='location of the gcc source') parser.add_argument('-d', '--date', dest='to_date', required=True, help='the next series date in "YYYY.MM" form.', type=str_to_datetime) parser.add_argument('-n', '--nopersist', dest='persist', default=True, action='store_false', help='The proj dir will not persist once this program has executed.') # At least one of the following arguments are required. group = parser.add_mutually_exclusive_group(required=True) group.add_argument('-c', '--candidate', dest='to_series', action='store_const', const=LinaroSeries.series.index("candidate")) group.add_argument('-r', '--release', dest='to_series', action='store_const', const=LinaroSeries.series.index("release")) group.add_argument('-s', '--snapshot', dest='to_series', action='store_const', const=LinaroSeries.series.index("snapshot")) return parser def main(): parser = create_parser() args = parser.parse_args() generate(args.track, args.to_date, args.to_series, args.gccsource, args.persist) if __name__ == '__main__': logging.basicConfig(level="INFO") with handle_exit(rncleanup): main()