diff --git a/README.md b/README.md index 190bf6f..8b99e90 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,15 @@ Create version info files based on the latest changelog entry. - [Available default template files](#available-default-template-files) - [C header file](#c-header-file) - [Python package file](#python-package-file) + - [JSON output](#json-output) + - [Console](#console) + - [File](#file) - [Advanced](#advanced) - [Custom regular expressions](#custom-regular-expressions) - [Custom template file](#custom-template-file) - [Additional version info content](#additional-version-info-content) +- [Contributing](#contributing) + - [Unittests](#unittests) - [Credits](#credits) @@ -105,6 +110,36 @@ __version__ = '.'.join(__version_info__) ``` +#### JSON output + +The additional, optional argument `--pretty` will output the JSON data with an +indentation of 4 in order to provide the data in an easy to read format. + +##### Console + +```bash +changelog2version \ + --changelog_file changelog.md \ + --print \ + --debug +``` + +```json +{"info": {"version": "0.6.0"}, "releases": {"0.6.0": [{"upload_time": "2022-10-26"}], "0.5.0": [{"upload_time": "2022-10-20"}], "0.4.0": [{"upload_time": "2022-08-07"}], "0.3.0": [{"upload_time": "2022-08-05"}], "0.2.0": [{"upload_time": "2022-08-03"}], "0.1.1": [{"upload_time": "2022-07-31"}], "0.1.0": [{"upload_time": "2022-07-31"}]}} +``` + +##### File + +```bash +changelog2version \ + --changelog_file changelog.md \ + --output changelog.json \ + --pretty \ + --debug +``` + +See [example JSON file][ref-example-json-file] + ## Advanced ### Custom regular expressions @@ -202,6 +237,33 @@ __version__ = '.'.join(__version_info__) + '-rc1234' ``` +## Contributing + +### Unittests + +Run the unittests locally with the following command after installing this +package in a virtual environment or by using `tox` to create one on each run. + +```bash +# install the package with all its development dependencies +pip install .[dev] + +# run all tests +nose2 --config tests/unittest.cfg + +# run only one specific tests +nose2 tests.test_extract_version.TestExtractVersion.test_version_line_regex +``` + +Generate the coverage files with + +```bash +python create_report_dirs.py +coverage html +``` + +The coverage report is placed at `reports/coverage/html/index.html` + ## Credits Based on the [PyPa sample project][ref-pypa-sample]. Also a big thank you to @@ -211,6 +273,7 @@ documentation and [regex example][ref-semver-regex-example] [ref-package-version-file]: src/changelog2version/version.py [ref-templates-folder]: src/changelog2version/templates +[ref-example-json-file]: examples/changelog.json [ref-py-core-metadata-spec]: https://packaging.python.org/specifications/core-metadat [ref-pep440]: https://peps.python.org/pep-0440/ [ref-pypa-sample]: https://github.com/pypa/sampleproject diff --git a/changelog.md b/changelog.md index be88102..ad86492 100644 --- a/changelog.md +++ b/changelog.md @@ -17,6 +17,17 @@ r"^\#\# \[\d{1,}[.]\d{1,}[.]\d{1,}\] \- \d{4}\-\d{2}-\d{2}$" --> ## Released +## [0.6.0] - 2022-10-26 +### Added +- Changelog can be parsed as JSON file with a similar format as the pypi + package JSON info available at `https://pypi.org/pypi/PACKAGENAME/json`, see + [#16][ref-issue-16] + The new option `--output` writes the data in JSON valid format to a file specified file, `--print` flag sends the data in JSON valid format to + `sys.stdout`. In both cases `--pretty` flag can be set to output the data with an indentation of 4. + +### Changed +- `--version_file` argument is optional in order to allow a JSON output only + ## [0.5.0] - 2022-10-20 ### Added - Support additional version info file content in python version files by @@ -128,8 +139,9 @@ r"^\#\# \[\d{1,}[.]\d{1,}[.]\d{1,}\] \- \d{4}\-\d{2}-\d{2}$" - Data folder after fork -[Unreleased]: https://github.com/brainelectronics/changelog2version/compare/0.5.0...develop +[Unreleased]: https://github.com/brainelectronics/changelog2version/compare/0.6.0...develop +[0.6.0]: https://github.com/brainelectronics/changelog2version/tree/0.6.0 [0.5.0]: https://github.com/brainelectronics/changelog2version/tree/0.5.0 [0.4.0]: https://github.com/brainelectronics/changelog2version/tree/0.4.0 [0.3.0]: https://github.com/brainelectronics/changelog2version/tree/0.3.0 @@ -137,6 +149,7 @@ r"^\#\# \[\d{1,}[.]\d{1,}[.]\d{1,}\] \- \d{4}\-\d{2}-\d{2}$" [0.1.1]: https://github.com/brainelectronics/changelog2version/tree/0.1.1 [0.1.0]: https://github.com/brainelectronics/changelog2version/tree/0.1.0 +[ref-issue-16]: https://github.com/brainelectronics/changelog2version/issues/16 [ref-issue-5]: https://github.com/brainelectronics/changelog2version/issues/5 [ref-issue-8]: https://github.com/brainelectronics/changelog2version/issues/8 [ref-issue-11]: https://github.com/brainelectronics/changelog2version/issues/11 diff --git a/examples/changelog.json b/examples/changelog.json new file mode 100644 index 0000000..00f6074 --- /dev/null +++ b/examples/changelog.json @@ -0,0 +1,42 @@ +{ + "info": { + "version": "0.6.0" + }, + "releases": { + "0.6.0": [ + { + "upload_time": "2022-10-26" + } + ], + "0.5.0": [ + { + "upload_time": "2022-10-20" + } + ], + "0.4.0": [ + { + "upload_time": "2022-08-07" + } + ], + "0.3.0": [ + { + "upload_time": "2022-08-05" + } + ], + "0.2.0": [ + { + "upload_time": "2022-08-03" + } + ], + "0.1.1": [ + { + "upload_time": "2022-07-31" + } + ], + "0.1.0": [ + { + "upload_time": "2022-07-31" + } + ] + } +} \ No newline at end of file diff --git a/src/changelog2version/extract_version.py b/src/changelog2version/extract_version.py index 9032ea7..f53a6e3 100644 --- a/src/changelog2version/extract_version.py +++ b/src/changelog2version/extract_version.py @@ -11,7 +11,7 @@ import re from semver import VersionInfo from sys import stdout -from typing import Optional +from typing import List, Optional class ExtractVersionError(Exception): @@ -61,6 +61,13 @@ def __init__(self, logger: Optional[logging.Logger] = None): r"(?P\d{2,}:\d{2,}:\d{2,}?))?" # time as HH:MM:SS ) + self._date_line_regex = ( + r".*" # anything + r"(?P\d{4}\-\d{2}-\d{2})" # datetime as YYYY-MM-DD + r"(([T ]{1})" # seperation between date and time by "T" or space + r"(?P\d{2,}:\d{2,}:\d{2,}?))?" # time as HH:MM:SS + ) + @property def version_line_regex(self) -> str: """ @@ -109,6 +116,30 @@ def semver_line_regex(self, value: str) -> None: except re.error: raise ExtractVersionError("Invalid regex pattern") + @property + def date_line_regex(self) -> str: + """ + Get regex to extract the date part from the complete version line + + :returns: Regex to get the date part + :rtype: str + """ + return self._date_line_regex + + @date_line_regex.setter + def date_line_regex(self, value: str) -> None: + """ + Set regex to extract the date part from the complete version line + + :param value: Regex to get the date part + :type value: str + """ + try: + re.compile(value) + self._date_line_regex = value + except re.error: + raise ExtractVersionError("Invalid regex pattern") + @property def semver_data(self) -> VersionInfo: """ @@ -173,17 +204,69 @@ def parse_changelog(self, changelog_file: Path) -> str: """ release_version_line = "" + release_version_lines = self.parse_changelog_completely( + changelog_file=changelog_file, + first_line_only=True) + + if len(release_version_lines) >= 1: + release_version_line = release_version_lines[0] + + return release_version_line + + def parse_changelog_completely(self, + changelog_file: Path, + first_line_only: bool = False) -> List[str]: + """ + Parse the changelog for all matching version lines + + :param changelog_file: The path to the changelog file + :type changelog_file: Path + :param first_line_only: Flag to break after first match found + :type first_line_only: bool + + :returns: List of extracted semantic version strings + :rtype: List[str] + """ + release_version_lines = [] + with open(changelog_file, "r") as f: for line in f: match = re.search(self.version_line_regex, line) if match: - release_version_line = match.group() - break + release_version_lines.append(match.group()) + if first_line_only: + break - self._logger.debug("First matching release version line: '{}'". - format(release_version_line)) + self._logger.debug("Matching release version lines: '{}'". + format(release_version_lines)) - return release_version_line + return release_version_lines + + def parse_semver_line_date(self, release_version_line: str) -> str: + """ + Parse a version line for a valid ISO8601 datetime + + Examples of a valid ISO8601 datetime lines: + - "## [0.2.0] - 2022-05-19" + - "## [107.3.18] - 1900-01-01 12:34:56" + - "## [1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay] - 2012-01-02" # noqa + + :param release_version_line: The release version line + :type release_version_line: str + + :returns: ISO8601 datetime string, e.g. "1970-01-01" + :rtype: str + """ + date_string = "1970-01-01" + + match = re.search(self.date_line_regex, release_version_line) + if match: + if len(match.groups()) >= 4 and match.group(2): + date_string = match.group(1) + match.group(2) + else: + date_string = match.group(1) + + return date_string def parse_semver_line(self, release_version_line: str) -> str: """ diff --git a/src/changelog2version/update_version.py b/src/changelog2version/update_version.py index 7f80c52..9052078 100644 --- a/src/changelog2version/update_version.py +++ b/src/changelog2version/update_version.py @@ -91,7 +91,7 @@ def parse_arguments() -> argparse.Namespace: parser.add_argument('--version_file', dest='version_file', - required=True, + required=False, help='Path to rendered file') parser.add_argument('--version_file_type', @@ -134,6 +134,22 @@ def parse_arguments() -> argparse.Namespace: help='Regex to extract semver part of from a version ' 'line') + parser.add_argument('--output', + dest='dump_to_file', + required=False, + help='Dump parsed changelog as JSON file to file') + + parser.add_argument('--print', + dest='print_result', + required=False, + action='store_true', + help='Print parsed changelog as JSON to stdout') + + parser.add_argument('--pretty', + dest='pretty_output', + action='store_true', + help='Print JSON data at stdout in readable format') + parsed_args = parser.parse_args() return parsed_args @@ -154,16 +170,21 @@ def main(): # changelog_file = Path(args.changelog_file).resolve() changelog_file = args.changelog_file - version_file = Path(args.version_file).resolve() + version_file = None template_file = args.template_file version_file_type = args.version_file_type additional_template_data = args.additional_template_data additional_version_info = args.additional_version_info version_line_regex = args.version_line_regex semver_line_regex = args.semver_line_regex + dump_to_file = args.dump_to_file + print_result = args.print_result + pretty_output = args.pretty_output - logger.debug("Using changelog file '{}' to update version file '{}'". - format(changelog_file, version_file)) + if args.version_file: + version_file = Path(args.version_file).resolve() + logger.debug("Using changelog file '{}' to update version file '{}'". + format(changelog_file, version_file)) version_extractor = ExtractVersion(logger=logger) @@ -177,8 +198,21 @@ def main(): "changelog file: {}".format(version_line_regex)) version_extractor.version_line_regex = version_line_regex - version_line = version_extractor.parse_changelog(changelog_file) - semver_string = version_extractor.parse_semver_line(version_line) + version_line = version_extractor.parse_changelog( + changelog_file=changelog_file) + version_lines = version_extractor.parse_changelog_completely( + changelog_file=changelog_file) + + release_infos = {} + for line in version_lines: + this_semver_string = version_extractor.parse_semver_line( + release_version_line=line) + this_date_string = version_extractor.parse_semver_line_date( + release_version_line=line) + release_infos[this_semver_string] = [{"upload_time": this_date_string}] + + semver_string = version_extractor.parse_semver_line( + release_version_line=version_line) file_renderer = RenderVersionFile() semver_data = version_extractor.semver_data @@ -212,10 +246,29 @@ def main(): "a template from this list: {}". format(template_file_map.keys())) - rendered_content = file_renderer.render_file( - template=template_file, - file_path=version_file, - content=version_file_content) + if version_file: + rendered_content = file_renderer.render_file( + template=template_file, + file_path=version_file, + content=version_file_content) + + changelog_data = { + 'info': {'version': semver_string}, + 'releases': release_infos + } + + if print_result: + if pretty_output: + stdout.write(json.dumps(changelog_data, indent=4)) + else: + stdout.write(json.dumps(changelog_data)) + + if dump_to_file: + with open(dump_to_file, 'w') as file: + if pretty_output: + file.write(json.dumps(changelog_data, indent=4)) + else: + file.write(json.dumps(changelog_data)) if __name__ == '__main__': diff --git a/tests/data/valid/changelog_with_date.md b/tests/data/valid/changelog_with_date.md index 8ab5034..13f56e3 100644 --- a/tests/data/valid/changelog_with_date.md +++ b/tests/data/valid/changelog_with_date.md @@ -17,9 +17,14 @@ r"^\#\# \[\d{1,}[.]\d{1,}[.]\d{1,}\] \- \d{4}\-\d{2}-\d{2}" --> ## Released -## [1.2.3] - 2022-07-31 +## [1.3.0] - 2022-10-26 ### Added - Something fixed +## [1.2.3] - 2022-07-31 +### Fixed +- Something fixed + +[1.3.0]: https://github.com/brainelectronics/changelog2version/tree/1.3.0 [1.2.3]: https://github.com/brainelectronics/changelog2version/tree/1.2.3 diff --git a/tests/data/valid/changelog_with_date_and_time.md b/tests/data/valid/changelog_with_date_and_time.md index 16f9e30..c9f1c50 100644 --- a/tests/data/valid/changelog_with_date_and_time.md +++ b/tests/data/valid/changelog_with_date_and_time.md @@ -17,9 +17,14 @@ r"^\#\# \[\d{1,}[.]\d{1,}[.]\d{1,}\] \- \d{4}\-\d{2}-\d{2}" --> ## Released -## [93.10.1] - 2022-07-31 12:34:56 +## [94.0.0] - 2022-10-26 23:59:01 ### Added - Something fixed +## [93.10.1] - 2022-07-31 12:34:56 +### Fixed +- Something fixed + +[94.0.0]: https://github.com/brainelectronics/changelog2version/tree/94.0.0 [93.10.1]: https://github.com/brainelectronics/changelog2version/tree/93.10.1 diff --git a/tests/test_extract_version.py b/tests/test_extract_version.py index 2e97998..abef1e2 100644 --- a/tests/test_extract_version.py +++ b/tests/test_extract_version.py @@ -6,6 +6,7 @@ from nose2.tools import params from pathlib import Path from sys import stdout +from typing import List import unittest from unittest.mock import patch, mock_open @@ -60,15 +61,24 @@ def test_semver_line_regex(self) -> None: self.assertEqual("Invalid regex pattern", str(context.exception)) + def test_date_line_regex(self) -> None: + """Test property date_line_regex""" + self.ev.date_line_regex = "gray|grey" + + with self.assertRaises(ExtractVersionError) as context: + self.ev.date_line_regex = "[" + + self.assertEqual("Invalid regex pattern", str(context.exception)) + @unittest.skip("Not yet implemented") def test__create_logger(self): """Test logger creation""" pass @params( - ("changelog_with_date.md", "## [1.2.3] - 2022-07-31"), + ("changelog_with_date.md", "## [1.3.0] - 2022-10-26"), ("changelog_with_date_and_time.md", - "## [93.10.1] - 2022-07-31 12:34:56"), + "## [94.0.0] - 2022-10-26 23:59:01"), ) def test_parse_changelog_file(self, file_name: str, @@ -83,6 +93,34 @@ def test_parse_changelog_file(self, self.assertIsInstance(result, str) self.assertEqual(expectation, result) + @params( + ( + "changelog_with_date.md", + ["## [1.3.0] - 2022-10-26", "## [1.2.3] - 2022-07-31"], + ), + ( + "changelog_with_date_and_time.md", + [ + "## [94.0.0] - 2022-10-26 23:59:01", + "## [93.10.1] - 2022-07-31 12:34:56" + ] + ), + ) + def test_parse_changelog_completely_file(self, + file_name: str, + expectation: List[str]) -> None: + """Test parse_changelog""" + changelog = self._here / 'data' / 'valid' / file_name + + result = self.ev.parse_changelog_completely(changelog_file=changelog) + self.test_logger.debug('Extracted "{}" from "{}"'. + format(result, changelog.name)) + + self.assertIsInstance(result, list) + self.assertEqual(len(result), len(expectation)) + self.assertTrue(all(isinstance(ele, str) for ele in result)) + self.assertEqual(expectation, result) + @params( # valid semver release version lines ("## [1.2.3] - 2012-01-02", "## [1.2.3] - 2012-01-02"), @@ -156,6 +194,311 @@ def test_parse_changelog_line(self, line: str, expectation: str) -> None: self.assertIsInstance(result, str) self.assertEqual(expectation, result) + @params( + # test strings taken from https://regex101.com/r/Ly7O1x/3/ and adopted + # valid semver release version lines without timestamp + ("## [1.2.3] - 2012-01-02", "2012-01-02"), + ("## [10.20.30] - 2012-01-02", "2012-01-02"), + ("## [1.1.2-prerelease+meta] - 2012-01-02", "2012-01-02"), + ("## [1.1.2+meta] - 2012-01-02", "2012-01-02"), + ("## [1.1.2+meta-valid] - 2012-01-02", "2012-01-02"), + ("## [1.0.0-alpha] - 2012-01-02", "2012-01-02"), + ("## [1.0.0-beta] - 2012-01-02", "2012-01-02"), + ("## [1.0.0-alpha.beta] - 2012-01-02", "2012-01-02"), + ("## [1.0.0-alpha.beta.1] - 2012-01-02", "2012-01-02"), + ("## [1.0.0-alpha.1] - 2012-01-02", "2012-01-02"), + ("## [1.0.0-alpha0.valid] - 2012-01-02", "2012-01-02"), + ("## [1.0.0-alpha.0valid] - 2012-01-02", "2012-01-02"), + ( + "## [1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay] - " + "2012-01-02", + "2012-01-02" + ), + ("## [1.0.0-rc.1+build.1] - 2012-01-02", "2012-01-02"), + ("## [2.0.0-rc.1+build.123] - 2012-01-02", "2012-01-02"), + ("## [1.2.3-beta] - 2012-01-02", "2012-01-02"), + ("## [10.2.3-DEV-SNAPSHOT] - 2012-01-02", "2012-01-02"), + ("## [1.2.3-SNAPSHOT-123] - 2012-01-02", "2012-01-02"), + ("## [1.0.0] - 2012-01-02", "2012-01-02"), + ("## [2.0.0] - 2012-01-02", "2012-01-02"), + ("## [1.1.7] - 2012-01-02", "2012-01-02"), + ("## [2.0.0+build.1848] - 2012-01-02", "2012-01-02"), + ("## [2.0.1-alpha.1227] - 2012-01-02", "2012-01-02"), + ("## [1.0.0-alpha+beta] - 2012-01-02", "2012-01-02"), + ( + "## [1.2.3----RC-SNAPSHOT.12.9.1--.12+788] - 2012-01-02", + "2012-01-02" + ), + ( + "## [1.2.3----R-S.12.9.1--.12+meta] - 2012-01-02", + "2012-01-02" + ), + ( + "## [1.2.3----RC-SNAPSHOT.12.9.1--.12] - 2012-01-02", + "2012-01-02" + ), + ( + "## [1.0.0+0.build.1-rc.10000aaa-kk-0.1] - 2012-01-02", + "2012-01-02" + ), + ( + "## [99999999999999999999999.999999999999999999.99999999999999999]" + " - 2012-01-02", + "2012-01-02" + ), + ("## [1.0.0-0A.is.legal] - 2012-01-02", "2012-01-02"), + + # valid release version lines with timestamp + # timestamp shall be seperated from date by + # - "T" to follow the ISO8601 norm + # - a single space + # timestamp seperated from date by a single "T" + ("## [1.2.3] - 2012-01-02T12:34:56", "2012-01-02T12:34:56"), + ("## [10.20.30] - 2012-01-02T12:34:56", "2012-01-02T12:34:56"), + ( + "## [1.1.2-prerelease+meta] - 2012-01-02T12:34:56", + "2012-01-02T12:34:56" + ), + ("## [1.1.2+meta] - 2012-01-02T12:34:56", "2012-01-02T12:34:56"), + ("## [1.1.2+meta-valid] - 2012-01-02T12:34:56", "2012-01-02T12:34:56"), + ("## [1.0.0-alpha] - 2012-01-02T12:34:56", "2012-01-02T12:34:56"), + ("## [1.0.0-beta] - 2012-01-02T12:34:56", "2012-01-02T12:34:56"), + ("## [1.0.0-alpha.beta] - 2012-01-02T12:34:56", "2012-01-02T12:34:56"), + ( + "## [1.0.0-alpha.beta.1] - 2012-01-02T12:34:56", + "2012-01-02T12:34:56" + ), + ("## [1.0.0-alpha.1] - 2012-01-02T12:34:56", "2012-01-02T12:34:56"), + ( + "## [1.0.0-alpha0.valid] - 2012-01-02T12:34:56", + "2012-01-02T12:34:56" + ), + ( + "## [1.0.0-alpha.0valid] - 2012-01-02T12:34:56", + "2012-01-02T12:34:56" + ), + ( + "## [1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay] - " + "2012-01-02T12:34:56", + "2012-01-02T12:34:56" + ), + ( + "## [1.0.0-rc.1+build.1] - 2012-01-02T12:34:56", + "2012-01-02T12:34:56" + ), + ( + "## [2.0.0-rc.1+build.123] - 2012-01-02T12:34:56", + "2012-01-02T12:34:56" + ), + ("## [1.2.3-beta] - 2012-01-02T12:34:56", "2012-01-02T12:34:56"), + ( + "## [10.2.3-DEV-SNAPSHOT] - 2012-01-02T12:34:56", + "2012-01-02T12:34:56" + ), + ( + "## [1.2.3-SNAPSHOT-123] - 2012-01-02T12:34:56", + "2012-01-02T12:34:56" + ), + ("## [1.0.0] - 2012-01-02T12:34:56", "2012-01-02T12:34:56"), + ("## [2.0.0] - 2012-01-02T12:34:56", "2012-01-02T12:34:56"), + ("## [1.1.7] - 2012-01-02T12:34:56", "2012-01-02T12:34:56"), + ("## [2.0.0+build.1848] - 2012-01-02T12:34:56", "2012-01-02T12:34:56"), + ("## [2.0.1-alpha.1227] - 2012-01-02T12:34:56", "2012-01-02T12:34:56"), + ("## [1.0.0-alpha+beta] - 2012-01-02T12:34:56", "2012-01-02T12:34:56"), + ( + "## [1.2.3----RC-SNAPSHOT.12.9.1--.12+788] - 2012-01-02T12:34:56", + "2012-01-02T12:34:56" + ), + ( + "## [1.2.3----R-S.12.9.1--.12+meta] - 2012-01-02T12:34:56", + "2012-01-02T12:34:56" + ), + ( + "## [1.2.3----RC-SNAPSHOT.12.9.1--.12] - 2012-01-02T12:34:56", + "2012-01-02T12:34:56" + ), + ( + "## [1.0.0+0.build.1-rc.10000aaa-kk-0.1] - 2012-01-02T12:34:56", + "2012-01-02T12:34:56" + ), + ( + "## [99999999999999999999999.999999999999999999.99999999999999999]" + " - 2012-01-02T12:34:56", + "2012-01-02T12:34:56" + ), + ( + "## [1.0.0-0A.is.legal] - 2012-01-02T12:34:56", + "2012-01-02T12:34:56" + ), + + # timestamp seperated from date by a single space + ("## [1.2.3] - 2012-01-02 12:34:56", "2012-01-02 12:34:56"), + ("## [10.20.30] - 2012-01-02 12:34:56", "2012-01-02 12:34:56"), + ( + "## [1.1.2-prerelease+meta] - 2012-01-02 12:34:56", + "2012-01-02 12:34:56" + ), + ("## [1.1.2+meta] - 2012-01-02 12:34:56", "2012-01-02 12:34:56"), + ("## [1.1.2+meta-valid] - 2012-01-02 12:34:56", "2012-01-02 12:34:56"), + ("## [1.0.0-alpha] - 2012-01-02 12:34:56", "2012-01-02 12:34:56"), + ("## [1.0.0-beta] - 2012-01-02 12:34:56", "2012-01-02 12:34:56"), + ("## [1.0.0-alpha.beta] - 2012-01-02 12:34:56", "2012-01-02 12:34:56"), + ( + "## [1.0.0-alpha.beta.1] - 2012-01-02 12:34:56", + "2012-01-02 12:34:56" + ), + ("## [1.0.0-alpha.1] - 2012-01-02 12:34:56", "2012-01-02 12:34:56"), + ( + "## [1.0.0-alpha0.valid] - 2012-01-02 12:34:56", + "2012-01-02 12:34:56" + ), + ( + "## [1.0.0-alpha.0valid] - 2012-01-02 12:34:56", + "2012-01-02 12:34:56" + ), + ( + "## [1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay] - " + "2012-01-02 12:34:56", + "2012-01-02 12:34:56" + ), + ( + "## [1.0.0-rc.1+build.1] - 2012-01-02 12:34:56", + "2012-01-02 12:34:56" + ), + ( + "## [2.0.0-rc.1+build.123] - 2012-01-02 12:34:56", + "2012-01-02 12:34:56" + ), + ("## [1.2.3-beta] - 2012-01-02 12:34:56", "2012-01-02 12:34:56"), + ( + "## [10.2.3-DEV-SNAPSHOT] - 2012-01-02 12:34:56", + "2012-01-02 12:34:56" + ), + ( + "## [1.2.3-SNAPSHOT-123] - 2012-01-02 12:34:56", + "2012-01-02 12:34:56" + ), + ("## [1.0.0] - 2012-01-02 12:34:56", "2012-01-02 12:34:56"), + ("## [2.0.0] - 2012-01-02 12:34:56", "2012-01-02 12:34:56"), + ("## [1.1.7] - 2012-01-02 12:34:56", "2012-01-02 12:34:56"), + ("## [2.0.0+build.1848] - 2012-01-02 12:34:56", "2012-01-02 12:34:56"), + ("## [2.0.1-alpha.1227] - 2012-01-02 12:34:56", "2012-01-02 12:34:56"), + ("## [1.0.0-alpha+beta] - 2012-01-02 12:34:56", "2012-01-02 12:34:56"), + ( + "## [1.2.3----RC-SNAPSHOT.12.9.1--.12+788] - 2012-01-02 12:34:56", + "2012-01-02 12:34:56" + ), + ( + "## [1.2.3----R-S.12.9.1--.12+meta] - 2012-01-02 12:34:56", + "2012-01-02 12:34:56" + ), + ( + "## [1.2.3----RC-SNAPSHOT.12.9.1--.12] - 2012-01-02 12:34:56", + "2012-01-02 12:34:56" + ), + ( + "## [1.0.0+0.build.1-rc.10000aaa-kk-0.1] - 2012-01-02 12:34:56", + "2012-01-02 12:34:56" + ), + ( + "## [99999999999999999999999.999999999999999999.99999999999999999]" + " - 2012-01-02 12:34:56", + "2012-01-02 12:34:56" + ), + ( + "## [1.0.0-0A.is.legal] - 2012-01-02 12:34:56", + "2012-01-02 12:34:56" + ), + + # invalid release version lines + ("## [1] - 1970-01-01", "1970-01-01"), + ("## [1.2] - 1970-01-01", "1970-01-01"), + ("## [1.2.3-0123] - 1970-01-01", "1970-01-01"), + ("## [1.2.3-0123.0123] - 1970-01-01", "1970-01-01"), + ("## [1.1.2+.123] - 1970-01-01", "1970-01-01"), + ("## [+invalid] - 1970-01-01", "1970-01-01"), + ("## [-invalid] - 1970-01-01", "1970-01-01"), + ("## [-invalid+invalid] - 1970-01-01", "1970-01-01"), + ("## [-invalid.01] - 1970-01-01", "1970-01-01"), + ("## [alpha] - 1970-01-01", "1970-01-01"), + ("## [alpha.beta] - 1970-01-01", "1970-01-01"), + ("## [alpha.beta.1] - 1970-01-01", "1970-01-01"), + ("## [alpha.1] - 1970-01-01", "1970-01-01"), + ("## [alpha+beta] - 1970-01-01", "1970-01-01"), + ("## [alpha_beta] - 1970-01-01", "1970-01-01"), + ("## [alpha.] - 1970-01-01", "1970-01-01"), + ("## [alpha..] - 1970-01-01", "1970-01-01"), + ("## [beta] - 1970-01-01", "1970-01-01"), + ("## [1.0.0-alpha_beta] - 1970-01-01", "1970-01-01"), + ("## [-alpha.] - 1970-01-01", "1970-01-01"), + ("## [1.0.0-alpha..] - 1970-01-01", "1970-01-01"), + ("## [1.0.0-alpha..1] - 1970-01-01", "1970-01-01"), + ("## [1.0.0-alpha...1] - 1970-01-01", "1970-01-01"), + ("## [1.0.0-alpha....1] - 1970-01-01", "1970-01-01"), + ("## [1.0.0-alpha.....1] - 1970-01-01", "1970-01-01"), + ("## [1.0.0-alpha......1] - 1970-01-01", "1970-01-01"), + ("## [1.0.0-alpha.......1] - 1970-01-01", "1970-01-01"), + ("## [01.1.1] - 1970-01-01", "1970-01-01"), + ("## [1.01.1] - 1970-01-01", "1970-01-01"), + ("## [1.1.01] - 1970-01-01", "1970-01-01"), + ("## [1.2] - 1970-01-01", "1970-01-01"), + ("## [1.2.3.DEV] - 1970-01-01", "1970-01-01"), + ("## [1.2-SNAPSHOT] - 1970-01-01", "1970-01-01"), + ( + "## [1.2.31.2.3----RC-SNAPSHOT.12.09.1--..12+788] - 1970-01-01", + "1970-01-01" + ), + ("## [1.2-RC-SNAPSHOT] - 1970-01-01", "1970-01-01"), + ("## [-1.0.3-gamma+b7718] - 1970-01-01", "1970-01-01"), + ("## [+justmeta] - 1970-01-01", "1970-01-01"), + ("## [9.8.7+meta+meta] - 1970-01-01", "1970-01-01"), + ("## [9.8.7-whatever+meta+meta] - 1970-01-01", "1970-01-01"), + ( + "## [99999999999999999999999.999999999999999999.99999999999999999" + "----RC-SNAPSHOT.12.09.1--------------------------------..12] - " + "1970-01-01", + "1970-01-01" + ), + + # valid version line, but invalid changelog version lines + ( + # missing "## " at the beginning + "[1.2.0] - 1900-01-01", "1900-01-01" + ), + ( + # year does not follow YYYY format + "## [1.2.3] - 19-01-01", "1970-01-01" + ), + ( + # month does not follow MM format + "## [1.3.0] - 1900-1-01", "1970-01-01"), + ( + # day does not follow DD format + "## [1.4.7] - 1900-01-1", "1970-01-01"), + ( + # month and day does not follow MM-DD form + "## [1.5.8] - 1900-1-1", "1970-01-01"), + ( + # year, month and day does not follow YYYY-MM-DD format + "## [1.6.0] - 19-1-1", "1970-01-01" + ), + # there will be a problem on the first of January 10.000 :) + ) + def test_parse_semver_line_date(self, line: str, expectation: str) -> None: + """ + Test parse semver line + + :param line: The parse semver line + :type line: str + :param expectation: Expected, parsed result + :type expectation: str + """ + result = self.ev.parse_semver_line_date(release_version_line=line) + self.test_logger.debug('Extracted "{}" from "{}"'.format(result, line)) + + self.assertIsInstance(result, str) + self.assertEqual(expectation, result) + @params( # test strings taken from https://regex101.com/r/Ly7O1x/3/ and adopted # valid semver release version lines without timestamp