1*8a52c783SCole Faustfrom changelog.git import stage_file 2*8a52c783SCole Faustfrom changelog.util import load_all_released_changes, load_unreleased_changes, version_cmp 3*8a52c783SCole Faustfrom functools import cmp_to_key 4*8a52c783SCole Faustfrom operator import attrgetter 5*8a52c783SCole Faust 6*8a52c783SCole Faustclass ChangelogWriter(object): 7*8a52c783SCole Faust """ 8*8a52c783SCole Faust Writes ReleaseChanges objects to a file using the following format: 9*8a52c783SCole Faust 10*8a52c783SCole Faust # __VERSION__ __YYYY-MM-DD__ 11*8a52c783SCole Faust ## __Category__ 12*8a52c783SCole Faust - ### __Features__ 13*8a52c783SCole Faust - ... 14*8a52c783SCole Faust - ### __Bugfixes__ 15*8a52c783SCole Faust - ... 16*8a52c783SCole Faust - ### __Deprecations__ 17*8a52c783SCole Faust - ... 18*8a52c783SCole Faust """ 19*8a52c783SCole Faust 20*8a52c783SCole Faust def __init__(self, output_file): 21*8a52c783SCole Faust self.output_file = output_file 22*8a52c783SCole Faust 23*8a52c783SCole Faust def write_changes(self, changes): 24*8a52c783SCole Faust self.process_changes(changes) 25*8a52c783SCole Faust self.write_header() 26*8a52c783SCole Faust for s in self.get_sorted_categories(): 27*8a52c783SCole Faust self.write_category_header(s) 28*8a52c783SCole Faust self.write_items_for_category(s, self.features, "Features") 29*8a52c783SCole Faust self.write_items_for_category(s, self.bugfixes, "Bugfixes") 30*8a52c783SCole Faust self.write_items_for_category(s, self.deprecations, "Deprecations") 31*8a52c783SCole Faust self.write_items_for_category(s, self.removals, "Removals") 32*8a52c783SCole Faust self.write_items_for_category(s, self.documentations, "Documentations") 33*8a52c783SCole Faust self.write_contributors() 34*8a52c783SCole Faust 35*8a52c783SCole Faust def write_contributors(self): 36*8a52c783SCole Faust contributors = set() 37*8a52c783SCole Faust for e in self.current_changes.entries: 38*8a52c783SCole Faust if e.contributor: 39*8a52c783SCole Faust contributors.add(self.contributor_with_link(e.contributor)) 40*8a52c783SCole Faust 41*8a52c783SCole Faust if contributors: 42*8a52c783SCole Faust self.output_file.write("## __Contributors__\n") 43*8a52c783SCole Faust contributors_string = ', '.join(contributors) 44*8a52c783SCole Faust self.output_file.write("Special thanks to the following contributors to this release: \n") 45*8a52c783SCole Faust self.output_file.write("\n" + contributors_string + "\n") 46*8a52c783SCole Faust 47*8a52c783SCole Faust def contributor_with_link(self, contributor): 48*8a52c783SCole Faust return "[@" + contributor + "]" + "(" + \ 49*8a52c783SCole Faust "https://github.com/" + contributor + ")" 50*8a52c783SCole Faust 51*8a52c783SCole Faust def process_changes(self, changes): 52*8a52c783SCole Faust self.current_changes = changes 53*8a52c783SCole Faust self.reset_maps() 54*8a52c783SCole Faust self.group_entries() 55*8a52c783SCole Faust 56*8a52c783SCole Faust def reset_maps(self): 57*8a52c783SCole Faust self.features = {} 58*8a52c783SCole Faust self.bugfixes = {} 59*8a52c783SCole Faust self.deprecations = {} 60*8a52c783SCole Faust self.removals = {} 61*8a52c783SCole Faust self.documentations = {} 62*8a52c783SCole Faust self.categories = set() 63*8a52c783SCole Faust 64*8a52c783SCole Faust def group_entries(self): 65*8a52c783SCole Faust for e in self.current_changes.entries: 66*8a52c783SCole Faust m = self.get_map_for_type(e.type) 67*8a52c783SCole Faust m.setdefault(e.category, []).append(e) 68*8a52c783SCole Faust self.categories.add(e.category) 69*8a52c783SCole Faust 70*8a52c783SCole Faust def get_sorted_categories(self): 71*8a52c783SCole Faust return sorted(list(self.categories)) 72*8a52c783SCole Faust 73*8a52c783SCole Faust def is_service_category(self,s): 74*8a52c783SCole Faust return s.lower() not in NON_SERVICE_CATEGORIES 75*8a52c783SCole Faust 76*8a52c783SCole Faust def write_header(self): 77*8a52c783SCole Faust version_string = self.current_changes.version 78*8a52c783SCole Faust if version_string is None: 79*8a52c783SCole Faust version_string = "@AWS_JAVA_SDK_VERSION@" 80*8a52c783SCole Faust self.write("# __%s__ __%s__\n" % (version_string, self.current_changes.date)) 81*8a52c783SCole Faust 82*8a52c783SCole Faust def write_category_header(self, c): 83*8a52c783SCole Faust self.output_file.write("## __%s__\n" % c) 84*8a52c783SCole Faust 85*8a52c783SCole Faust def write_items_for_category(self, category, map, header): 86*8a52c783SCole Faust entries = map.get(category, []) 87*8a52c783SCole Faust items = sorted(entries, key=attrgetter('description')) 88*8a52c783SCole Faust self.write_entries_with_header(header, items) 89*8a52c783SCole Faust 90*8a52c783SCole Faust def write_entries_with_header(self, header, entries): 91*8a52c783SCole Faust if not len(entries) > 0: 92*8a52c783SCole Faust return 93*8a52c783SCole Faust self.write(" - ### %s\n" % header) 94*8a52c783SCole Faust for e in entries: 95*8a52c783SCole Faust self.write_entry(e) 96*8a52c783SCole Faust self.write('\n') 97*8a52c783SCole Faust 98*8a52c783SCole Faust def write_entry(self,e): 99*8a52c783SCole Faust description = e.description 100*8a52c783SCole Faust entry_lines = description.splitlines(True) 101*8a52c783SCole Faust self.write(" - %s" % entry_lines[0]) 102*8a52c783SCole Faust for l in entry_lines[1:]: 103*8a52c783SCole Faust if len(l.strip()) == 0: 104*8a52c783SCole Faust self.write("\n") 105*8a52c783SCole Faust else: 106*8a52c783SCole Faust self.write(" %s" % l) 107*8a52c783SCole Faust self.write('\n') 108*8a52c783SCole Faust if e.contributor: 109*8a52c783SCole Faust self.write(" - ") 110*8a52c783SCole Faust self.write("Contributed by: " + self.contributor_with_link(e.contributor)) 111*8a52c783SCole Faust self.write('\n') 112*8a52c783SCole Faust 113*8a52c783SCole Faust def get_map_for_type(self, t): 114*8a52c783SCole Faust if t == 'feature': 115*8a52c783SCole Faust return self.features 116*8a52c783SCole Faust elif t == 'bugfix': 117*8a52c783SCole Faust return self.bugfixes 118*8a52c783SCole Faust elif t == 'deprecation': 119*8a52c783SCole Faust return self.deprecations 120*8a52c783SCole Faust elif t == 'removal': 121*8a52c783SCole Faust return self.removals 122*8a52c783SCole Faust elif t == 'documentation': 123*8a52c783SCole Faust return self.documentations 124*8a52c783SCole Faust else: 125*8a52c783SCole Faust raise Exception("Unknown entry type %s!" % t) 126*8a52c783SCole Faust 127*8a52c783SCole Faust def write(self, s): 128*8a52c783SCole Faust self.output_file.write(s) 129*8a52c783SCole Faust 130*8a52c783SCole Faustdef write_archived_changelogs_message(changelog_file): 131*8a52c783SCole Faust message = " #### _Looking for changelogs for older versions? You can find them in the [changelogs](./changelogs) directory._\n" 132*8a52c783SCole Faust changelog_file.write(message) 133*8a52c783SCole Faust 134*8a52c783SCole Faustdef write_changelog(): 135*8a52c783SCole Faust unreleased = load_unreleased_changes(".changes/next-release") 136*8a52c783SCole Faust released = load_all_released_changes(".changes") 137*8a52c783SCole Faust released = sorted(released, key=lambda c: [c.version.major, c.version.minor, c.version.patch, c.version.prerelease_version_number()], reverse=True) 138*8a52c783SCole Faust 139*8a52c783SCole Faust if unreleased is not None: 140*8a52c783SCole Faust all_changes = [unreleased] + released 141*8a52c783SCole Faust else: 142*8a52c783SCole Faust all_changes = released 143*8a52c783SCole Faust 144*8a52c783SCole Faust if len(all_changes) == 0: 145*8a52c783SCole Faust return 146*8a52c783SCole Faust 147*8a52c783SCole Faust with open('CHANGELOG.md', 'w') as cl: 148*8a52c783SCole Faust write_archived_changelogs_message(cl) 149*8a52c783SCole Faust writer = ChangelogWriter(cl) 150*8a52c783SCole Faust for changes in all_changes: 151*8a52c783SCole Faust writer.write_changes(changes) 152*8a52c783SCole Faust 153*8a52c783SCole Faust stage_file('CHANGELOG.md') 154