1+ from collections import Counter
2+ from typing import Callable
3+
14import typer
25from rich import print
6+ from rich .progress import Progress
37
48from changelogger import changelog , templating
5- from changelogger .app .commands .notes import _unreleased_notes
69from changelogger .conf import settings
710from changelogger .conf .models import VersionedFile
811from changelogger .exceptions import ValidationException
@@ -17,24 +20,97 @@ def check(
1720 "--fail" ,
1821 help = "Exit with a status of 2 if any versioned files are invalid." ,
1922 ),
23+ files : list [str ] = typer .Option (
24+ [],
25+ "--file" ,
26+ help = "Only check the specified file(s)." ,
27+ ),
2028) -> None :
2129 """Checks the versioned files for any unparsable sections which do not match
2230 the Changelogger configuration and reports them.
2331 """
32+ versioned_files = (
33+ settings .VERSIONED_FILES
34+ if not (files_set := set (files ))
35+ else [
36+ file
37+ for file in settings .VERSIONED_FILES
38+ if str (file .rel_path ) in files_set
39+ ]
40+ )
2441 try :
25- _check_changelog ()
26- _check_versioned_files ()
42+ _check_versioned_files (versioned_files )
2743 except ValidationException as e :
2844 print (f"[bold red]Error:[/bold red] { str (e )} " )
2945 if sys_exit :
3046 raise typer .Exit (code = 2 )
3147 else :
3248 print (
33- ":white_heavy_check_mark: [bold green]All versioned files are valid![/bold green]"
49+ ":white_heavy_check_mark: [bold green]Versioned files are valid![/bold green]"
3450 )
3551
3652
37- def _check_changelog () -> None :
53+ def _check_versioned_files (versioned_files : list [VersionedFile ]) -> None :
54+ """For each of the user-specified and default versioned files, check
55+ that a search for the pattern over the files content results in a find.
56+ """
57+
58+ # Contrive a fake update so we can check if the pattern would have been
59+ # found.
60+ old_version = changelog .get_latest_version ()
61+ update = ChangelogUpdate (
62+ new_version = old_version .bump_minor (),
63+ old_version = old_version ,
64+ release_notes = changelog .get_release_notes ("Unreleased" , old_version ),
65+ )
66+
67+ counts = Counter (file .rel_path for file in versioned_files )
68+ with Progress () as progress :
69+ if settings .CHANGELOG_PATH in counts :
70+ counts [settings .CHANGELOG_PATH ] += 6
71+
72+ tasks = {
73+ path : progress .add_task (
74+ f'Checking [green]"{ path } "[/green]' ,
75+ total = total ,
76+ )
77+ for path , total in counts .items ()
78+ }
79+
80+ for file in versioned_files :
81+ _check_versioned_file (file , update )
82+ progress .advance (tasks [file .rel_path ])
83+
84+ if any (
85+ file .rel_path == settings .CHANGELOG_PATH
86+ for file in versioned_files
87+ ):
88+ advancer = lambda : progress .advance (
89+ tasks [settings .CHANGELOG_PATH ],
90+ )
91+ _check_changelog (advancer )
92+
93+
94+ def _check_versioned_file (
95+ file : VersionedFile , update : ChangelogUpdate
96+ ) -> None :
97+ """Renders the versioned files pattern with an update and confirms
98+ there's a match.
99+ """
100+
101+ pattern = templating .render_pattern (file , update )
102+ content = file .rel_path .read_text ()
103+ if cached_compile (pattern ).search (content ):
104+ return
105+
106+ raise ValidationException (
107+ f"Could not find the pattern `[bright_blue]{ file .pattern } [/bright_blue]` "
108+ f'in "{ file .rel_path } ".\n \n '
109+ f"Rendered pattern used when searching: `[bright_blue]{ pattern } [/bright_blue]`."
110+ )
111+
112+
113+ def _check_changelog (advance : Callable ) -> None :
38114 """Validates the specified versioned files are parsable and updatable."""
39115 # Validate there's at least 1 version
40116 # Point of Failure 0
@@ -43,6 +119,7 @@ def _check_changelog() -> None:
43119 raise ValidationException (
44120 "Expected there to be at least 1 version; None found."
45121 )
122+ advance ()
46123
47124 # Validate all release notes are parseable for all versions
48125 # Point of Failure 1
@@ -56,6 +133,7 @@ def _check_changelog() -> None:
56133 raise ValidationException (
57134 f"Failed to validate notes for version { new_version } : { str (e )} ."
58135 )
136+ advance ()
59137
60138 # Validate there are links in the expected format for all versions
61139 # Point of Failure 2
@@ -73,59 +151,26 @@ def _check_changelog() -> None:
73151 raise ValidationException (
74152 f"Link is incorrect for version { version } "
75153 )
154+ advance ()
76155
77156 link = all_links .get ("Unreleased" )
78157 # Point of Failure 4
79158 if not link :
80159 raise ValidationException (
81160 "Could not find the link for unreleased changes."
82161 )
162+ advance ()
83163
84164 # Point of Failure 5
85165 if f"{ all_versions [0 ]} ...HEAD" not in link :
86166 raise ValidationException (
87167 "Link is incorrect for the unreleased changes."
88168 )
169+ advance ()
89170
90171 # Point of Failure 6
91172 if sorted_versions [0 ] not in all_links :
92173 raise ValidationException (
93174 f"Could not find the link for version { sorted_versions [0 ]} "
94175 )
95-
96-
97- def _check_versioned_files () -> None :
98- """For each of the user-specified and default versioned files, check
99- that a search for the pattern over the files content results in a find.
100- """
101-
102- # Contrive a fake update so we can check if the pattern would have been
103- # found.
104- old_version = changelog .get_latest_version ()
105- update = ChangelogUpdate (
106- new_version = old_version .bump_minor (),
107- old_version = old_version ,
108- release_notes = _unreleased_notes (),
109- )
110-
111- for version_file in settings .VERSIONED_FILES :
112- _check_versioned_file (version_file , update )
113-
114-
115- def _check_versioned_file (
116- file : VersionedFile , update : ChangelogUpdate
117- ) -> None :
118- """Renders the versioned files pattern with an update and confirms
119- there's a match.
120- """
121-
122- pattern = templating .render_pattern (file , update )
123- content = file .rel_path .read_text ()
124- if cached_compile (pattern ).search (content ):
125- return
126-
127- raise ValidationException (
128- f"Could not find the pattern `[bright_blue]{ file .pattern } [/bright_blue]` "
129- f'in the versioned file "{ file .rel_path } ".\n \n '
130- f"Rendered pattern used when searching: [bright_blue]{ pattern } [/bright_blue]."
131- )
176+ advance ()
0 commit comments