From 605c0eb8ab796b2d994c9bcb84188660925e00d6 Mon Sep 17 00:00:00 2001 From: jobevers Date: Tue, 21 Feb 2017 22:29:23 -0600 Subject: [PATCH] try parsing changelog --- .gitignore | 3 +- changelog.py | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++ lbry | 2 +- lbry-web-ui | 2 +- lbryum | 2 +- release.py | 54 +++++++++------------- 6 files changed, 155 insertions(+), 36 deletions(-) create mode 100644 changelog.py diff --git a/.gitignore b/.gitignore index 9a9fe1a4d..9c697f60f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ dist lbrynet/build lbrynet/venv .#* -build_venv \ No newline at end of file +build_venv +*.pyc \ No newline at end of file diff --git a/changelog.py b/changelog.py new file mode 100644 index 000000000..f941c48c4 --- /dev/null +++ b/changelog.py @@ -0,0 +1,128 @@ +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(err.format(filename, '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(new_changelog) + 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()) diff --git a/lbry b/lbry index 301e4ae0f..056ee2d1c 160000 --- a/lbry +++ b/lbry @@ -1 +1 @@ -Subproject commit 301e4ae0fa91335d455b79bd65b08a866183bb38 +Subproject commit 056ee2d1cefd79bdc8d0d5eff2de61ca3fce89a1 diff --git a/lbry-web-ui b/lbry-web-ui index f64e3d935..4f66a3a6a 160000 --- a/lbry-web-ui +++ b/lbry-web-ui @@ -1 +1 @@ -Subproject commit f64e3d935650ff6fa172ec2192d32eaaf5100085 +Subproject commit 4f66a3a6a140e8ccf3602418e161dba2d132cb23 diff --git a/lbryum b/lbryum index 8834b60c7..aae29ae7c 160000 --- a/lbryum +++ b/lbryum @@ -1 +1 @@ -Subproject commit 8834b60c7cdd9c0c31b51be87e8bcf1b21dbdbed +Subproject commit aae29ae7cc1e3f919ce4065eac8e1f4eaf0f7f05 diff --git a/release.py b/release.py index a61f263b4..6768d5a48 100644 --- a/release.py +++ b/release.py @@ -14,12 +14,8 @@ import git import github import requests +import changelog -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'^\#\# ') NO_CHANGE = ('No change since the last release. This release is simply a placeholder' ' so that LBRY and LBRY App track the same version') @@ -54,7 +50,7 @@ def main(): github_repo = auth.get_repo('lbryio/lbry-app') names = ['lbry', 'lbry-web-ui', 'lbryum'] - repos = [Repo(name) for name in names] + repos = [Repo(name, get_part(args, name)) for name in names] # in order to see if we've had any change in the submodule, we need to checkout # our last release, see what commit we were on, and then compare that to @@ -71,6 +67,8 @@ def main(): # ensure that we have changelog entries for each part for repo in repos: + print repo.new_version() + sys.exit(1) if repo.has_changes(): entry = repo.get_changelog_entry().strip() if not entry: @@ -83,8 +81,7 @@ def main(): # them even if there aren't any changes, but lbryum should only be # bumped if it has changes continue - part = get_part(args, repo.name) - if not part: + if not repo.part: raise Exception('Cannot bump version for {}: no part specified'.format(repo.name)) repo.bumpversion(part) @@ -156,11 +153,13 @@ def get_part(args, name): class Repo(object): - def __init__(self, name): + def __init__(self, name, part): self.name = name + self.part = part self.directory = os.path.join(os.getcwd(), name) self.git_repo = git.Repo(self.directory) self.saved_commit = None + self._bumped = False def has_changes(self): return self.git_repo.commit() == self.saved_commit @@ -173,31 +172,22 @@ class Repo(object): def get_changelog_entry(self): filename = os.path.join(self.directory, 'CHANGELOG.md') - err = 'Had trouble parsing changelog {}: {}' - output = [] - start_found = False - with open(filename) as fp: - for line in fp: - if not start_found: - if CHANGELOG_START_RE.search(line): - start_found = True - continue - if CHANGELOG_END_RE.search(line): - return ''.join(output) - if CHANGELOG_ERROR_RE.search(line): - raise Exception(err.format(filename, 'unexpected section header found')) - output.append(line) - # if we get here there was no previous release section, which is a problem - if start_found: - # TODO: once the lbry-web-ui has a released entry, uncomment this error - # raise Exception(err.format(filename, 'Reached end of file')) - return ''.join(output) - else: - raise Exception(err.format(filename, 'Unreleased section not found')) + return changelog.bump(filename, self.new_version()) - def bumpversion(self, part): + def new_version(self): + if self._bumped: + raise Exception('Cannot calculate a new version on an already bumped repo') with pushd(self.directory): - subprocess.check_call(['bumpversion', part]) + output = subprocess.check_output( + ['bumpversion', '--dry-run', '--list', '--allow-dirty', self.part]) + return re.search('^new_version=(.*)$', output).group(1) + + def bumpversion(self): + if self._bumped: + raise Exception('Cowardly refusing to bump a repo twice') + with pushd(self.directory): + subprocess.check_call(['bumpversion', '--allow-dirty', self.part]) + self._bumped = True @property def git(self):