|
1 | 1 | from pathlib import Path |
| 2 | +from typing import Literal |
2 | 3 |
|
3 | 4 | from changelogger.conf import settings |
4 | 5 | from changelogger.conf.models import VersionedFile |
5 | | -from changelogger.exceptions import RollbackException, UpgradeException |
6 | | -from changelogger.models.domain_models import ChangelogUpdate, ReleaseNotes |
| 6 | +from changelogger.exceptions import ( |
| 7 | + CommandException, |
| 8 | + RollbackException, |
| 9 | + UpgradeException, |
| 10 | +) |
| 11 | +from changelogger.models.domain_models import ( |
| 12 | + ChangelogUpdate, |
| 13 | + ReleaseNotes, |
| 14 | + VersionInfo, |
| 15 | +) |
7 | 16 | from changelogger.templating import update_with_jinja |
8 | 17 | from changelogger.utils import cached_compile |
9 | 18 |
|
| 19 | +CHANGELOG_PARTITION_RELEASE_NOTES = "RELEASE NOTES" |
| 20 | +CHANGELOG_PARTITION_LINKS = "LINKS" |
10 | 21 |
|
11 | | -def get_all_links() -> dict[str, str]: |
12 | | - lines = settings.CHANGELOG_PATH.read_text().split("\n") |
| 22 | + |
| 23 | +def _get_changelog_parition(partition: str) -> str: |
| 24 | + changelog_content = settings.CHANGELOG_PATH.read_text() |
| 25 | + |
| 26 | + start_partition = f"<!-- BEGIN {partition} -->" |
| 27 | + end_partition = f"<!-- END {partition} -->" |
| 28 | + partition_re = cached_compile( |
| 29 | + rf"{start_partition}([\s\S]*){end_partition}" |
| 30 | + ) |
| 31 | + |
| 32 | + match = partition_re.search(changelog_content) |
| 33 | + if not match: |
| 34 | + raise CommandException( |
| 35 | + f"Expected partition for `{partition}`; None found." |
| 36 | + ) |
| 37 | + return match[1] |
| 38 | + |
| 39 | + |
| 40 | +def _get_release_notes_parition() -> str: |
| 41 | + return _get_changelog_parition(CHANGELOG_PARTITION_RELEASE_NOTES) |
| 42 | + |
| 43 | + |
| 44 | +def _get_links_parition() -> str: |
| 45 | + return _get_changelog_parition(CHANGELOG_PARTITION_LINKS) |
| 46 | + |
| 47 | + |
| 48 | +def get_all_links() -> dict[VersionInfo | str, str]: |
| 49 | + lines = _get_links_parition().split("\n") |
13 | 50 |
|
14 | 51 | links = {} |
15 | 52 | for line in lines: |
16 | 53 | match = cached_compile( |
17 | | - r"\[([\d.]+|Unreleased)]: (.*)", |
| 54 | + r"\[(.*)]: (.*)", |
18 | 55 | ).search( |
19 | 56 | line, |
20 | 57 | ) |
21 | 58 |
|
22 | 59 | if not match: |
23 | 60 | continue |
24 | 61 |
|
25 | | - links[match[1]] = match[2] |
| 62 | + version_str = match[1] |
| 63 | + link = match[2] |
26 | 64 |
|
27 | | - return links |
| 65 | + if version_str == "Unreleased": |
| 66 | + links[version_str] = link |
| 67 | + continue |
| 68 | + |
| 69 | + match = VersionInfo._REGEX.fullmatch(version_str) |
| 70 | + if not match: |
| 71 | + continue |
| 72 | + |
| 73 | + links[VersionInfo.parse(version_str)] = link |
28 | 74 |
|
| 75 | + return links |
29 | 76 |
|
30 | | -def get_all_versions() -> list[str]: |
31 | | - lines = settings.CHANGELOG_PATH.read_text().split("\n") |
32 | 77 |
|
| 78 | +def get_all_versions() -> list[VersionInfo]: |
| 79 | + lines = _get_release_notes_parition().split("\n") |
33 | 80 | versions = [] |
34 | 81 | for line in lines: |
35 | 82 | match = cached_compile( |
36 | | - r"### \[([\d.]+)]", |
| 83 | + r"### \[(.*)]", |
37 | 84 | ).search( |
38 | 85 | line, |
39 | 86 | ) |
40 | 87 |
|
41 | 88 | if not match: |
42 | 89 | continue |
43 | 90 |
|
44 | | - versions.append(match[1]) |
| 91 | + version_str = match[1] |
| 92 | + |
| 93 | + match = VersionInfo._REGEX.fullmatch(version_str) |
| 94 | + if not match: |
| 95 | + continue |
| 96 | + |
| 97 | + versions.append(VersionInfo.parse(version_str)) |
45 | 98 | return versions |
46 | 99 |
|
47 | 100 |
|
48 | | -def get_sorted_versions() -> list[str]: |
49 | | - versions = get_all_versions() |
50 | | - sorted_versions = sorted( |
51 | | - (tuple(map(int, version.split("."))) for version in versions) |
52 | | - ) |
53 | | - return [".".join(map(str, v)) for v in sorted_versions] |
| 101 | +def get_sorted_versions() -> list[VersionInfo]: |
| 102 | + return sorted(get_all_versions()) |
54 | 103 |
|
55 | 104 |
|
56 | | -def get_latest_version() -> str: |
| 105 | +def get_latest_version() -> VersionInfo: |
57 | 106 | versions = get_sorted_versions() |
58 | 107 | if not versions: |
59 | 108 | raise UpgradeException(f"This changelog has no versions currently.") |
60 | 109 | return versions[-1] |
61 | 110 |
|
62 | 111 |
|
63 | | -def get_release_notes(version: str, prev_version: str) -> ReleaseNotes: |
64 | | - version = version.replace(".", r"\.") |
| 112 | +def get_release_notes( |
| 113 | + new_version: VersionInfo | Literal["Unreleased"], |
| 114 | + old_version: VersionInfo | None, |
| 115 | +) -> ReleaseNotes: |
| 116 | + new_version_pattern = str(new_version).replace(".", r"\.") |
65 | 117 |
|
66 | | - content = settings.CHANGELOG_PATH.read_text() |
| 118 | + content = _get_release_notes_parition() |
67 | 119 |
|
| 120 | + pattern = rf"### \[{new_version_pattern}\]( - \d+-\d+-\d+)?([\s\S]*)" |
| 121 | + if old_version: |
| 122 | + old_version_pattern = str(old_version).replace(".", r"\.") |
| 123 | + pattern += rf"### \[{old_version_pattern}\]" |
68 | 124 | match = cached_compile( |
69 | | - rf"### \[{version}\]( - \d+-\d+-\d+)?([\s\S]*)### \[{prev_version}\]", |
| 125 | + pattern, |
70 | 126 | ).search( |
71 | 127 | content, |
72 | 128 | ) |
|
0 commit comments