From a925a4e420763306586e2adfa39e48c400886706 Mon Sep 17 00:00:00 2001 From: drewcassidy Date: Thu, 6 May 2021 23:09:34 -0700 Subject: [PATCH] walk back last change slightly preamble now contains the title, to allow for representing Jekyll front matter or any other information above the title --- CHANGELOG.md | 27 ++++++++++------ docs/handbook/changelog_files.md | 10 ++---- tests/common.py | 4 +-- tests/test_changelog.py | 10 ++---- yaclog/changelog.py | 55 +++++++++++++++----------------- 5 files changed, 50 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8e6ff1..18976ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,13 @@ All notable changes to this project will be documented in this file ### Changed +- API changes: + - `header` attribute renamed to `preamble` to avoid confusion. - improved version header parsing to be more robust and handle multi-word version names. - improved version number incrementing in `release`. - can now handle other text surrounding a pep440-compliant version number, which will not be modified - - can now handle pre-releases correctly. The version to increment is the most recent version in the log with a valid pep440 version number in it. Release increment and prerelease increments can be mixed, allowing e.g: `yaclog release -mr` to create a release candidate with in incremented minor version number. -- `header` attribute on the changelog class has been split into `title` and `preamble` + - can now handle pre-releases correctly. The version to increment is the most recent version in the log with a valid pep440 version number in it. + - Release increment and prerelease increments can be mixed, allowing e.g: `yaclog release -mr` to create a release candidate with in incremented minor version number. ### Removed @@ -20,6 +22,7 @@ All notable changes to this project will be documented in this file - Terminal output has color to distinguish version names/headers, sections, and git information + ## 0.3.3 - 2021-04-27 ### Added @@ -34,6 +37,7 @@ All notable changes to this project will be documented in this file - `release` now resets lesser version values when incrementing - `release` now works with logs that have only unreleased changes + ## 0.3.2 - 2021-04-24 ### Added @@ -49,17 +53,19 @@ All notable changes to this project will be documented in this file - `release` and `entry` commands now work using empty changelogs. + ## 0.3.1 - 2021-04-24 ### Added - `yaclog` tool for manipulating changelogs from the command line - - `init` command to make a new changelog - - `format` command to reformat the changelog - - `show` command to show changes from the changelog - - `entry` command for manipulating entries in the changelog - - `tag` command for manipulating tags in the changelog - - `release` command for creating releases + - `init` command to make a new changelog + - `format` command to reformat the changelog + - `show` command to show changes from the changelog + - `entry` command for manipulating entries in the changelog + - `tag` command for manipulating tags in the changelog + - `release` command for creating releases + ## 0.2.0 - 2021-04-19 @@ -71,8 +77,9 @@ All notable changes to this project will be documented in this file - Updated package metadata - Rewrote parser to use a 2-step method that is more flexible. - - Parser can now handle code blocks. - - Parser can now handle setext-style headers and H2s not conforming to the schema. + - Parser can now handle code blocks. + - Parser can now handle setext-style headers and H2s not conforming to the schema. + ## 0.1.0 - 2021-04-16 diff --git a/docs/handbook/changelog_files.md b/docs/handbook/changelog_files.md index 7011d8b..41b95e6 100644 --- a/docs/handbook/changelog_files.md +++ b/docs/handbook/changelog_files.md @@ -2,13 +2,9 @@ Yaclog works on markdown changelog files, using a machine-readable format based on what is proposed by [Keep a Changelog](https://keepachangelog.com). Changelog files can be created using the {command}`yaclog init` command. -## Title - -The title is the first H1 in the file giving its title, usually `# Changlog`. - ## Preamble -The preamble is the text at the top of the file before any version information. It can contain an explanation of the file's purpose, as well as any general machine-readable information you may want to include for use with other tools. Yaclog does not provide any ways to manipulate the front matter from the command line due to its open-ended nature. +The preamble is the text at the top of the file before any version information. It can contain the title, an explanation of the file's purpose, as well as any general machine-readable information you may want to include for use with other tools. Yaclog does not provide any ways to manipulate the front matter from the command line due to its open-ended nature. ## Versions @@ -17,9 +13,11 @@ Version information begins with a header, which is an H2 containing the version' ```markdown ## 1.0.0 ``` + ```markdown ## 3.2.0 "Columbia" - 1981-07-20 ``` + ```markdown ## Version 8.0.0rc1 1988-11-15 [PRERELEASE] ``` @@ -54,7 +52,6 @@ Yanked due to issues with oxygen tanks, currently investigating - Replaced Ken Mattingly - Stirred oxygen tanks - ## 0.12.0 "Intrepid" - 1969-11-14 ### Added @@ -71,7 +68,6 @@ Yanked due to issues with oxygen tanks, currently investigating - Lightning strike during launch: No effect on performance - ## 0.11.0 "Eagle" - 1969-07-20 Initial stable release diff --git a/tests/common.py b/tests/common.py index 088b9e6..7eb0c9a 100644 --- a/tests/common.py +++ b/tests/common.py @@ -46,8 +46,8 @@ log_segments = [ log_text = '\n\n'.join(log_segments) log = yaclog.Changelog() -log.title = 'Changelog' -log.preamble = ['This changelog is for testing the parser, and has many things in it that might trip it up.'] +log.preamble = '# Changelog\n\n' \ + 'This changelog is for testing the parser, and has many things in it that might trip it up.' log.links = {'id': 'http://www.koalastothemax.com'} log.versions = [yaclog.changelog.VersionEntry(), yaclog.changelog.VersionEntry(), yaclog.changelog.VersionEntry()] diff --git a/tests/test_changelog.py b/tests/test_changelog.py index c203df0..d83c1d6 100644 --- a/tests/test_changelog.py +++ b/tests/test_changelog.py @@ -22,10 +22,6 @@ class TestParser(unittest.TestCase): """Test the log's path""" self.assertEqual(self.path, self.log.path) - def test_title(self): - """Test the title at the top of the file""" - self.assertEqual(log.title, self.log.title) - def test_preamble(self): """Test the preamble at the top of the file""" self.assertEqual(log.preamble, self.log.preamble) @@ -56,11 +52,11 @@ class TestWriter(unittest.TestCase): log.write(cls.path) with open(cls.path) as fd: cls.log_text = fd.read() - cls.log_segments = [line for line in cls.log_text.split('\n\n') if line] + cls.log_segments = [line.lstrip('\n') for line in cls.log_text.split('\n\n') if line] - def test_header(self): + def test_preamble(self): """Test the header information at the top of the file""" - self.assertEqual(log_segments[1], self.log_segments[1]) + self.assertEqual(log_segments[0:2], self.log_segments[0:2]) def test_links(self): """Test the links at the end of the file""" diff --git a/yaclog/changelog.py b/yaclog/changelog.py index 4f60308..42e6986 100644 --- a/yaclog/changelog.py +++ b/yaclog/changelog.py @@ -1,6 +1,6 @@ """ Contains the `Changelog` class that represents a parsed changelog file that can be read from and written to -disk as markdown, as well as the `VersionEntry` class that represents a single version within that changelog +disk as markdown, as well as the `VersionEntry` class that represents a single version within that changelog. """ # yaclog: yet another changelog tool @@ -33,7 +33,10 @@ import yaclog.version class VersionEntry: - """Holds a single version entry in a :py:class:`Changelog`""" + """ + A serialized representation of a single version entry in a `Changelog`, + containing the changes made since the previous version + """ _header_regex = re.compile( # THE LANGUAGE OF THE GODS r"##\s+(?P.*?)(?:\s+-)?(?:\s+(?P\d{4}-\d{2}-\d{2}))?(?P(?:\s+\[[^]]*?])*)\s*$") @@ -215,32 +218,31 @@ class VersionEntry: class Changelog: - """A changelog made up of a header, several versions, and a link table""" + """ + A serialized representation of a Markdown changelog made up of a preamble, multiple versions, and a link table. + """ def __init__(self, path=None, - title: str = 'Changelog', - preamble: str = "All notable changes to this project will be documented in this file"): + preamble: str = "Changelog\n\nAll notable changes to this project will be documented in this file"): """ Contents will be automatically read from disk if the file exists - :param path: The changelog's path on disk - :param str title: The changelog title to use if the file does not exist. + :param path: The changelog's path on disk. :param str preamble: The changelog preamble to use if the file does not exist. """ self.path = os.path.abspath(path) if path else None """The path of the changelog's file on disk""" - self.title: str = title - """The title of the changelog""" - - self.preamble: List[str] = preamble - """Any text at the top of the changelog before any version information as a list of paragraphs""" + self.preamble: str = preamble + """Any text at the top of the changelog before any version information, including the title. + It can contain the title, an explanation of the file's purpose, as well as any general machine-readable + information for use with other tools.""" self.versions: List[VersionEntry] = [] - """A list of versions in the changelog""" + """A list of versions in the changelog, with the most recent version first""" self.links: Dict[str, str] = {} - """Link IDs at the end of the changelog""" + """Link definitions at the end of the changelog, as a dictionary of ``{id: url}``""" if path and os.path.exists(path): self.read() @@ -250,7 +252,7 @@ class Changelog: Read a markdown changelog file from disk. The object's contents will be overwritten by the file contents if reading is successful. - :param path: The changelog's path on disk. By default, :py:attr:`~Changelog.path` is used. + :param path: The changelog's path on disk. By default, :py:attr:`~Changelog.path` is used """ if not path: @@ -263,8 +265,7 @@ class Changelog: section = '' versions = [] - title = None - preamble = [] + preamble_segments = [] for token in tokens: text = '\n'.join(token.lines) @@ -276,11 +277,8 @@ class Changelog: elif len(versions) == 0: # we haven't encountered any version headers yet, - # so its best to just add this line to the preamble or title - if token.kind == 'h1' and not title: - title = text.strip('#').strip() - else: - preamble.append(text) + # so its best to just add this line to the preamble + preamble_segments.append(text) elif token.kind == 'h3': # start of a version section @@ -306,14 +304,13 @@ class Changelog: # id-matched link version.link = links[version.link_id] - self.title = title - self.preamble = preamble + self.preamble = markdown.join(preamble_segments) self.versions = versions self.links = links def write(self, path=None) -> None: """ - Write a markdown changelog to a file. + Write a changelog to a Markdown file. :param path: The changelog's path on disk. By default, :py:attr:`~Changelog.path` is used. """ @@ -324,10 +321,8 @@ class Changelog: segments = [] - if self.title: - segments.append(f'# {self.title}') if self.preamble: - segments += self.preamble + segments.append(self.preamble) v_links = {**self.links} @@ -335,7 +330,7 @@ class Changelog: if version.link: v_links[version.name.lower()] = version.link - segments.append(version.text()) + segments.append(version.text() + '\n') segments += [f'[{link_id}]: {link}' for link_id, link in v_links.items()] @@ -386,7 +381,7 @@ class Changelog: def get_version(self, name: Optional[str] = None) -> VersionEntry: """ - Get a version from the changelog + Get a version from the changelog by name :param name: The name of the version to get, or `None` to return the most recent :return: The first version with the selected name