lbry-desktop/build/changelog.py
2017-03-09 13:33:06 -05:00

129 lines
3.5 KiB
Python

import argparse
import datetime
import re
import sys
CHANGELOG_START_RE = re.compile(r'^\#\# \[Unreleased\]')
CHANGELOG_END_RE = re.compile(r'^\#\# \[.*\] - \d{4}-\d{2}-\d{2}')
# if we come across a section header between two release section headers
# then we probably have an improperly formatted changelog
CHANGELOG_ERROR_RE = re.compile(r'^\#\# ')
SECTION_RE = re.compile(r'^\#\#\# (.*)$')
EMPTY_RE = re.compile(r'^\w*\*\w*$')
ENTRY_RE = re.compile(r'\* (.*)')
VALID_SECTIONS = ['Added', 'Changed', 'Deprecated', 'Removed', 'Fixed', 'Security']
# allocate some entries to cut-down on merge conflicts
TEMPLATE = """### Added
*
*
*
### Changed
*
*
*
### Fixed
*
*
*
"""
def main():
parser = argparse.ArgumentParser()
parser.add_argument('changelog')
parser.add_argument('version')
args = parser.parse_args()
bump(changelog, version)
def bump(changelog, version):
with open(changelog) as fp:
lines = fp.readlines()
start = []
unreleased = []
rest = []
unreleased_start_found = False
unreleased_end_found = False
for line in lines:
if not unreleased_start_found:
start.append(line)
if CHANGELOG_START_RE.search(line):
unreleased_start_found = True
continue
if unreleased_end_found:
rest.append(line)
continue
if CHANGELOG_END_RE.search(line):
rest.append(line)
unreleased_end_found = True
continue
if CHANGELOG_ERROR_RE.search(line):
raise Exception(
'Failed to parse {}: {}'.format(changelog, 'unexpected section header found'))
unreleased.append(line)
today = datetime.datetime.today()
header = '## [{}] - {}\n'.format(version, today.strftime('%Y-%m-%d'))
released = normalize(unreleased)
if not released:
# If we don't have anything in the Unreleased section, then leave the
# changelog as it is and return None
return
changelog_data = (
''.join(start) +
TEMPLATE +
header +
'\n'.join(released) + '\n\n'
+ ''.join(rest)
)
with open(changelog, 'w') as fp:
fp.write(changelog_data)
return '\n'.join(released) + '\n\n'
def normalize(lines):
"""Parse a changelog entry and output a normalized form"""
sections = {}
current_section_name = None
current_section_contents = []
for line in lines:
line = line.strip()
if not line or EMPTY_RE.match(line):
continue
match = SECTION_RE.match(line)
if match:
if current_section_contents:
sections[current_section_name] = current_section_contents
current_section_contents = []
current_section_name = match.group(1)
if current_section_name not in VALID_SECTIONS:
raise ValueError("Section '{}' is not valid".format(current_section_name))
continue
match = ENTRY_RE.match(line)
if match:
current_section_contents.append(match.group(1))
continue
raise Exception('Something is wrong with line: {}'.format(line))
if current_section_contents:
sections[current_section_name] = current_section_contents
output = []
for section in VALID_SECTIONS:
if section not in sections:
continue
output.append('### {}'.format(section))
for entry in sections[section]:
output.append(' * {}'.format(entry))
return output
if __name__ == '__main__':
sys.exit(main())