xref: /aosp_15_r20/external/aws-sdk-java-v2/scripts/changelog/writer.py (revision 8a52c7834d808308836a99fc2a6e0ed8db339086)
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