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