def test_add_entries(tmp_path): os.chdir(tmp_path) runner = CliRunner() runner.invoke(main.main, ["init"]) result = runner.invoke(main.main, ["added", "a changelog entry string"]) parsed = keepachangelog.to_dict("CHANGELOG.md", show_unreleased=True) assert result.exit_code == 0 assert parsed == { "Unreleased": { "version": "Unreleased", "release_date": None, "added": ["- a changelog entry string"], } } result = runner.invoke( main.main, ["added", "an", "unquoted", "changelog", "entry", "string"]) parsed = keepachangelog.to_dict("CHANGELOG.md", show_unreleased=True) assert result.exit_code == 0 assert parsed == { "Unreleased": { "version": "Unreleased", "release_date": None, "added": [ "- a changelog entry string", "- an unquoted changelog entry string", ], } } result = runner.invoke( main.main, ["changed", "an", "unquoted", "changelog", "entry", "string"]) parsed = keepachangelog.to_dict("CHANGELOG.md", show_unreleased=True) assert result.exit_code == 0 assert parsed == { "Unreleased": { "version": "Unreleased", "release_date": None, "changed": ["- an unquoted changelog entry string"], "added": [ "- a changelog entry string", "- an unquoted changelog entry string", ], } }
def test_changelog_with_versions_and_all_categories_as_file_reader(changelog): with io.StringIO(open(changelog).read()) as file_reader: assert keepachangelog.to_dict(file_reader) == changelog_as_dict # Assert that file reader is not closed file_reader.seek(0) assert keepachangelog.to_dict(file_reader) == changelog_as_dict
def add_entry(self, section_name: str, changelog_line: typing.Union[str, tuple]) -> None: _changelog_line = "- " + " ".join(changelog_line) current = keepachangelog.to_dict(self.filename, show_unreleased=True) unreleased = current.get("Unreleased") if not unreleased: unreleased = { "Unreleased": { "version": "Unreleased", "release_date": None } } new = {} new.update(unreleased) new.update(current) current = new unreleased = current["Unreleased"] section = unreleased.setdefault(section_name, []) section.append(_changelog_line) self.write(current=current)
def test_release(tmp_path): os.chdir(tmp_path) runner = CliRunner() result = runner.invoke(main.main, ["init"]) assert result.exit_code == 0 result = runner.invoke(main.main, ["release"]) assert result.exit_code == 1 result = runner.invoke(main.main, ["added", "a changelog entry string"]) result = runner.invoke(main.main, ["release"]) assert result.exit_code == 0 # test changelog entries order creation result = runner.invoke(main.main, ["added", "a changelog entry string"]) parsed = keepachangelog.to_dict("CHANGELOG.md", show_unreleased=True) assert list(parsed.keys()) == ["Unreleased", "0.0.1"] assert parsed == { "Unreleased": { "added": ["- a changelog entry string"], "release_date": None, "version": "Unreleased", }, "0.0.1": { "added": ["- a changelog entry string"], "release_date": datetime.now().isoformat().split("T")[0], "version": "0.0.1", }, }
def test_changelog_with_versions_and_no_deprecated(changelog): assert keepachangelog.to_dict(changelog) == { "1.1.0": { "changed": [ "- Enhancement 1 (1.1.0)", "- sub enhancement 1", "- sub enhancement 2", "- Enhancement 2 (1.1.0)", ], "release_date": "2018-05-31", "version": "1.1.0", }, "1.0.1": { "fixed": [ "- Bug fix 1 (1.0.1)", "- sub bug 1", "- sub bug 2", "- Bug fix 2 (1.0.1)", ], "release_date": "2018-05-31", "version": "1.0.1", }, "1.0.0": { "release_date": "2017-04-10", "version": "1.0.0" }, }
def release(self, mode: str) -> None: current = keepachangelog.to_dict(self.filename, show_unreleased=True) if "Unreleased" not in current: print("nothing to bump!") import sys sys.exit(1) try: last = [version for version in current if version != "Unreleased"][0] except IndexError: last = "0.0.0" version = semver.parse_version_info(last) if mode == "major": last = str(version.bump_major()) elif mode == "minor": last = str(version.bump_minor()) elif mode == "patch": last = str(version.bump_patch()) else: last = mode entries = current.pop("Unreleased") changelog = {last: entries} changelog[last]["release_date"] = datetime.now().isoformat().split( "T")[0] changelog.update(current) self.write(current=changelog)
def test_changelog_without_category(changelog): assert keepachangelog.to_dict(changelog) == { "1.2.0": { "uncategorized": ["Release note 1.", "Release note 2."], "fixed": ["Bug fix 1", "sub bug 1", "sub bug 2", "Bug fix 2"], "metadata": { "release_date": "2018-06-01", "version": "1.2.0", "semantic_version": { "buildmetadata": None, "major": 1, "minor": 2, "patch": 0, "prerelease": None, }, }, }, "1.1.0": { "uncategorized": [ "Enhancement 1 (1.1.0)", "sub enhancement 1", "sub enhancement 2", "Enhancement 2 (1.1.0)", ], "metadata": { "release_date": "2018-05-31", "version": "1.1.0", "semantic_version": { "buildmetadata": None, "major": 1, "minor": 1, "patch": 0, "prerelease": None, }, }, }, "1.0.1": { "uncategorized": [ "Bug fix 1 (1.0.1)", "sub bug 1", "sub bug 2", "Bug fix 2 (1.0.1)", ], "metadata": { "release_date": "2018-05-31", "version": "1.0.1", "semantic_version": { "buildmetadata": None, "major": 1, "minor": 0, "patch": 1, "prerelease": None, }, }, }, }
def test_changelog_with_unreleased_versions_and_all_categories(changelog): assert keepachangelog.to_dict(changelog, show_unreleased=True) == { "master": { "release_date": None, "version": "master" }, "1.2.0": { "added": [ "Enhancement 1", "sub enhancement 1", "sub enhancement 2", "Enhancement 2", ], "changed": ["Release note 1.", "Release note 2."], "deprecated": ["Deprecated feature 1", "Future removal 2"], "fixed": ["Bug fix 1", "sub bug 1", "sub bug 2", "Bug fix 2"], "release_date": "august 28, 2019", "removed": ["Deprecated feature 2", "Future removal 1"], "security": ["Known issue 1", "Known issue 2"], "version": "1.2.0", }, "1.1.0": { "changed": [ "Enhancement 1 (1.1.0)", "sub enhancement 1", "sub enhancement 2", "Enhancement 2 (1.1.0)", ], "release_date": "may 03, 2018", "version": "1.1.0", }, "1.0.1": { "fixed": [ "Bug fix 1 (1.0.1)", "sub bug 1", "sub bug 2", "Bug fix 2 (1.0.1)", ], "release_date": "may 01, 2018", "version": "1.0.1", }, "1.0.0": { "deprecated": ["Known issue 1 (1.0.0)", "Known issue 2 (1.0.0)"], "release_date": "2017-01-01", "version": "1.0.0", }, }
def test_changelog_with_versions_and_all_categories(changelog): assert keepachangelog.to_dict(changelog, show_unreleased=True) == { "Unreleased": { "version": "Unreleased", "release_date": None, "changed": ["- Release note 1.", "- Release note 2."], "added": [ "- Enhancement 1", "- sub enhancement 1", "- sub enhancement 2", "- Enhancement 2", ], "fixed": ["- Bug fix 1", "- sub bug 1", "- sub bug 2", "- Bug fix 2"], "security": ["- Known issue 1", "- Known issue 2"], "deprecated": ["- Deprecated feature 1", "- Future removal 2"], "removed": ["- Deprecated feature 2", "- Future removal 1"], }, "1.1.0": { "version": "1.1.0", "release_date": "2018-05-31", "changed": [ "- Enhancement 1 (1.1.0)", "- sub enhancement 1", "- sub enhancement 2", "- Enhancement 2 (1.1.0)", ], }, "1.0.1": { "version": "1.0.1", "release_date": "2018-05-31", "fixed": [ "- Bug fix 1 (1.0.1)", "- sub bug 1", "- sub bug 2", "- Bug fix 2 (1.0.1)", ], }, "1.0.0": { "version": "1.0.0", "release_date": "2017-04-10", "deprecated": ["- Known issue 1 (1.0.0)", "- Known issue 2 (1.0.0)"], }, }
def test_changelog_with_versions_and_no_removed(changelog): assert keepachangelog.to_dict(changelog) == { "1.2.0": { "added": [ "Enhancement 1", "sub enhancement 1", "sub enhancement 2", "Enhancement 2", ], "changed": ["Release note 1.", "Release note 2."], "deprecated": ["Deprecated feature 1", "Future removal 2"], "fixed": ["Bug fix 1", "sub bug 1", "sub bug 2", "Bug fix 2"], "release_date": "2018-06-01", "security": ["Known issue 1", "Known issue 2"], "version": "1.2.0", }, "1.1.0": { "changed": [ "Enhancement 1 (1.1.0)", "sub enhancement 1", "sub enhancement 2", "Enhancement 2 (1.1.0)", ], "release_date": "2018-05-31", "version": "1.1.0", }, "1.0.1": { "fixed": [ "Bug fix 1 (1.0.1)", "sub bug 1", "sub bug 2", "Bug fix 2 (1.0.1)", ], "release_date": "2018-05-31", "version": "1.0.1", }, "1.0.0": { "deprecated": ["Known issue 1 (1.0.0)", "Known issue 2 (1.0.0)"], "release_date": "2017-04-10", "version": "1.0.0", }, }
def test_release_specfic(tmp_path): os.chdir(tmp_path) runner = CliRunner() runner.invoke(main.main, ["init"]) runner.invoke(main.main, ["added", "a changelog entry string"]) result = runner.invoke(main.main, ["release", "2.0.0"]) assert result.exit_code == 0, result.stdout parsed = keepachangelog.to_dict("CHANGELOG.md", show_unreleased=True) assert parsed == { "2.0.0": { "added": ["- a changelog entry string"], "release_date": datetime.now().isoformat().split("T")[0], "version": "2.0.0", } }
def test_changelog_with_empty_version(changelog): assert keepachangelog.to_dict(changelog) == { "": { "changed": ["Release note 1.", "Release note 2."], "deprecated": ["Deprecated feature 1", "Future removal 2"], "fixed": ["Bug fix 1", "sub bug 1", "sub bug 2", "Bug fix 2"], "removed": ["Deprecated feature 2", "Future removal 1"], "security": ["Known issue 1", "Known issue 2"], "metadata": { "release_date": "2018-06-01", "version": "", "semantic_version": { "buildmetadata": None, "major": 0, "minor": 0, "patch": 0, "prerelease": None, }, }, }, }
def write(self, *, current: dict = None, config: Configuration = None) -> None: if not config: config = self.get_config(init=True) config.driver = "KAC" if not current: current = keepachangelog.to_dict(self.filename, show_unreleased=True) ctx = dict(header=DEFAULT_HEADER, current=current) if config.git_provider: git_provider = Provider(current, config) ctx["git_provider"] = git_provider if config: ctx["config"] = config.marshal() with open(self.filename, "w") as outfile: outfile.write(TEMPLATE.render(ctx) + "\n")
"P": "patch", "G": "git push", "C": "cancel" } parser = argparse.ArgumentParser() parser.add_argument("--run", action="store_true") args = parser.parse_args() def parse_version(version): return version.split(".") data = keepachangelog.to_dict("CHANGELOG.md").keys() versions_list = list(data) versions_list.sort() latest_changelog_version = versions_list[-1] if not args.run: print("Running in DRY RUN mode") print("Current", package["name"], "package version:", package["version"], "\n") while (True): option = input( "Publish [m]ajor, m[i]nor, [p]atch, just push to [g]it, [C]ancel? " ).upper() if option == "" or option == "C":
def test_changelog_with_versions_and_no_changed(changelog): assert keepachangelog.to_dict(changelog) == { "1.2.0": { "added": [ "Enhancement 1", "sub enhancement 1", "sub enhancement 2", "Enhancement 2", ], "deprecated": ["Deprecated feature 1", "Future removal 2"], "fixed": ["Bug fix 1", "sub bug 1", "sub bug 2", "Bug fix 2"], "removed": ["Deprecated feature 2", "Future removal 1"], "security": ["Known issue 1", "Known issue 2"], "metadata": { "release_date": "2018-06-01", "version": "1.2.0", "semantic_version": { "buildmetadata": None, "major": 1, "minor": 2, "patch": 0, "prerelease": None, }, }, }, "1.1.0": { "metadata": { "release_date": "2018-05-31", "version": "1.1.0", "semantic_version": { "buildmetadata": None, "major": 1, "minor": 1, "patch": 0, "prerelease": None, }, }, }, "1.0.1": { "fixed": [ "Bug fix 1 (1.0.1)", "sub bug 1", "sub bug 2", "Bug fix 2 (1.0.1)", ], "metadata": { "release_date": "2018-05-31", "version": "1.0.1", "semantic_version": { "buildmetadata": None, "major": 1, "minor": 0, "patch": 1, "prerelease": None, }, }, }, "1.0.0": { "deprecated": ["Known issue 1 (1.0.0)", "Known issue 2 (1.0.0)"], "metadata": { "release_date": "2017-04-10", "version": "1.0.0", "semantic_version": { "buildmetadata": None, "major": 1, "minor": 0, "patch": 0, "prerelease": None, }, }, }, }
import sys import keepachangelog CATEGORIES = ['added', 'changed', 'deprecated', 'removed', 'fixed', 'security'] version = sys.argv[1] changes = keepachangelog.to_dict("CHANGELOG.md")[version] print('## Changelog') for category in CATEGORIES: entries = changes.get(category, []) if entries: print(f'### {category.capitalize()}') for entry in entries: print(f'- {entry}')
if section_name in changes: value = "\n".join(["‣ {}".format(change) for change in changes[section_name]]) if len(value) > MAX_FIELD_LEN: value = value[: MAX_FIELD_LEN - 3] + "..." embed.add_embed_field(name=field_title, value=value, inline=False) if len(argv) < 3: print("Usage: {} [webhook url] [version]".format(argv[0])) exit(1) webhook_url = argv[1] version = argv[2] changelog = keepachangelog.to_dict("./CHANGELOG.md") if version not in changelog: print("Version {} not in changelog!".format(version)) exit(1) changes = changelog[version] title = "🎹🎶 **{} v{} - {}**".format( GITHUB_REPO, changes["version"], changes["release_date"] ) release_url = "https://github.com/{}/{}/releases/tag/v{}".format( GITHUB_USER, GITHUB_REPO, version )
def test_changelog_with_versions_and_all_categories(changelog): assert keepachangelog.to_dict(changelog, show_unreleased=True) == { "unreleased": { "changed": ["Release note 1.", "Release note 2."], "added": [ "Enhancement 1", "sub enhancement 1", "sub enhancement 2", "Enhancement 2", ], "fixed": ["Bug fix 1", "sub bug 1", "sub bug 2", "Bug fix 2"], "security": ["Known issue 1", "Known issue 2"], "uncategorized": ["Release note 0."], "deprecated": ["Deprecated feature 1", "Future removal 2"], "removed": ["Deprecated feature 2", "Future removal 1"], "metadata": { "version": "unreleased", "release_date": None, "url": "https://github.test_url/test_project/compare/v1.1.0...HEAD", }, }, "1.1.0": { "changed": [ "Enhancement 1 (1.1.0)", "sub *enhancement 1*", "sub enhancement 2", "Enhancement 2 (1.1.0)", ], "metadata": { "version": "1.1.0", "semantic_version": { "buildmetadata": None, "major": 1, "minor": 1, "patch": 0, "prerelease": None, }, "release_date": "2018-05-31", "url": "https://github.test_url/test_project/compare/v1.0.2...v1.1.0", }, }, "1.0.2": { "metadata": { "url": "https://github.test_url/test_project/compare/v1.0.1...v1.0.2", "version": "1.0.2", }, }, "1.0.1": { "fixed": [ "Bug fix 1 (1.0.1)", "sub bug 1", "sub bug 2", "Bug fix 2 (1.0.1)", ], "metadata": { "version": "1.0.1", "semantic_version": { "buildmetadata": None, "major": 1, "minor": 0, "patch": 1, "prerelease": None, }, "release_date": "2018-05-31", "url": "https://github.test_url/test_project/compare/v1.0.0...v1.0.1", }, }, "1.0.0": { "deprecated": ["Known issue 1 (1.0.0)", "Known issue 2 (1.0.0)"], "metadata": { "version": "1.0.0", "semantic_version": { "buildmetadata": None, "major": 1, "minor": 0, "patch": 0, "prerelease": None, }, "release_date": "2017-04-10", "url": "https://github.test_url/test_project/releases/tag/v1.0.0", }, }, "0.0.1": { "added": ["First release"], "metadata": { "release_date": "2017-01-01", "semantic_version": { "buildmetadata": None, "major": 0, "minor": 0, "patch": 1, "prerelease": None, }, "version": "0.0.1", }, }, }
def test_changelog_from_dict(changelog): releases = keepachangelog.to_dict(changelog, show_unreleased=True) assert (keepachangelog.from_dict(releases) == """# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] * Release note 0. ### Changed - Release note 1. - Release note 2. ### Added - Enhancement 1 - sub enhancement 1 - sub enhancement 2 - Enhancement 2 ### Fixed - Bug fix 1 - sub bug 1 - sub bug 2 - Bug fix 2 ### Security - Known issue 1 - Known issue 2 ### Deprecated - Deprecated feature 1 - Future removal 2 ### Removed - Deprecated feature 2 - Future removal 1 ## [1.1.0] - 2018-05-31 ### Changed - Enhancement 1 (1.1.0) - sub *enhancement 1* - sub enhancement 2 - Enhancement 2 (1.1.0) ## [1.0.1] - 2018-05-31 ### Fixed - Bug fix 1 (1.0.1) - sub bug 1 - sub bug 2 - Bug fix 2 (1.0.1) ## [1.0.0] - 2017-04-10 ### Deprecated - Known issue 1 (1.0.0) - Known issue 2 (1.0.0) ## [0.0.1] - 2017-01-01 ### Added - First release ## [1.0.2] [Unreleased]: https://github.test_url/test_project/compare/v1.1.0...HEAD [1.1.0]: https://github.test_url/test_project/compare/v1.0.2...v1.1.0 [1.0.1]: https://github.test_url/test_project/compare/v1.0.0...v1.0.1 [1.0.0]: https://github.test_url/test_project/releases/tag/v1.0.0 [1.0.2]: https://github.test_url/test_project/compare/v1.0.1...v1.0.2 """)
def test_changelog_without_versions(changelog): assert keepachangelog.to_dict(changelog) == {}
def test_changelog_with_versions_and_all_categories(changelog): assert keepachangelog.to_dict(changelog) == { "1.2.0": { "added": [ "Enhancement 1", "sub enhancement 1", "sub enhancement 2", "Enhancement 2", ], "changed": ["Release note 1.", "Release note 2."], "deprecated": ["Deprecated feature 1", "Future removal 2"], "fixed": ["Bug fix 1", "sub bug 1", "sub bug 2", "Bug fix 2"], "removed": ["Deprecated feature 2", "Future removal 1"], "security": ["Known issue 1", "Known issue 2"], "metadata": { "release_date": "august 28, 2019", "version": "1.2.0", "semantic_version": { "buildmetadata": None, "major": 1, "minor": 2, "patch": 0, "prerelease": None, }, }, }, "1.1.0": { "changed": [ "Enhancement 1 (1.1.0)", "sub enhancement 1", "sub enhancement 2", "Enhancement 2 (1.1.0)", ], "metadata": { "release_date": "may 03, 2018", "version": "1.1.0", "semantic_version": { "buildmetadata": None, "major": 1, "minor": 1, "patch": 0, "prerelease": None, }, }, }, "1.0.1": { "fixed": [ "Bug fix 1 (1.0.1)", "sub bug 1", "sub bug 2", "Bug fix 2 (1.0.1)", ], "metadata": { "release_date": "may 01, 2018", "version": "1.0.1", "semantic_version": { "buildmetadata": None, "major": 1, "minor": 0, "patch": 1, "prerelease": None, }, }, }, "1.0.0": { "deprecated": ["Known issue 1 (1.0.0)", "Known issue 2 (1.0.0)"], "metadata": { "release_date": "2017-01-01", "version": "1.0.0", "semantic_version": { "buildmetadata": None, "major": 1, "minor": 0, "patch": 0, "prerelease": None, }, }, }, }
def test_changelog_with_versions_and_all_categories(changelog): assert keepachangelog.to_dict(changelog) == changelog_as_dict
def test_changelog_not_found(): with pytest.raises(FileNotFoundError): keepachangelog.to_dict("do not exists")