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