import argparse import os.path from collections import defaultdict from reno import config from reno import loader BEFORE_2024_2_0_NOTE = """Before 2024.2.0 all the OpenStack-Helm charts were versioned independently. Here we provide all the release notes for the chart for all versions before 2024.2.0. """ def _indent_for_list(text, prefix=' '): lines = text.splitlines() return '\n'.join([lines[0]] + [ prefix + l for l in lines[1:] ]) def chart_reports(loader, config, versions_to_include, title=None, charts=None): reports = defaultdict(list) file_contents = {} for version in versions_to_include: for filename, sha in loader[version]: body = loader.parse_note_file(filename, sha) file_contents[filename] = body for chart in charts: if title: reports[chart].append(f"# {title}") reports[chart].append('') for version in versions_to_include: if '-' in version: version_title = config.unreleased_version_title or version else: version_title = version reports[chart].append(f"## {version_title}") reports[chart].append('') if version == "2024.2.0": reports[chart].append(BEFORE_2024_2_0_NOTE) if config.add_release_date: reports[chart].append('Release Date: ' + loader.get_version_date(version)) reports[chart].append('') notefiles = loader[version] # Prepare not named section # 1. Get all files named *.yaml # and get section from all these files # 2. Get all files named common*.yaml and get # section from all these files is_content = False for fn, sha in notefiles: if os.path.basename(fn).startswith(chart) or \ os.path.basename(fn).startswith("common"): notes = file_contents[fn].get(chart, []) for n in notes: is_content = True reports[chart].append(f"- {_indent_for_list(n)}") # Add new line after unnamed section if it is not empty if is_content: reports[chart].append("") # Prepare named sections # 1. Get all files named *.yaml # and get all sections from all these files except # 2. Get all files named common*.yaml # and get all sections from all these files except for section in config.sections: is_content = False # Skip chart specific sections if section.name not in ["features", "isseus", "upgrade", "api", "security", "fixes"]: continue for fn, sha in notefiles: if os.path.basename(fn).startswith(chart) or \ os.path.basename(fn).startswith("common"): notes = file_contents[fn].get(section.name, []) if notes and not is_content: reports[chart].append(f"### {section.title}") reports[chart].append("") if notes: is_content = True for n in notes: reports[chart].append(f"- {_indent_for_list(n)}") # Add new line after the section if it is not empty if is_content: reports[chart].append("") report = reports[chart] reports[chart] = '\n'.join(report) return reports def main(): parser = argparse.ArgumentParser() parser.add_argument("--charts", nargs="+", default=[], help="Charts to generate release notes for") args = parser.parse_args() conf = config.Config(".", "releasenotes") with loader.Loader(conf) as ldr: versions = ldr.versions reports = chart_reports( ldr, conf, versions, title="Release notes", charts=args.charts, ) for chart in reports: with open(f"{chart}/CHANGELOG.md", "w") as f: f.write(reports[chart]) return if __name__ == "__main__": main()