Skip to content

Commit c3061d8

Browse files
committed
Corrections for changelog handling.
1 parent a2172dc commit c3061d8

File tree

6 files changed

+84
-48
lines changed

6 files changed

+84
-48
lines changed

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# makeapp changelog
22

33

4-
# Unreleased
5-
* !! Big rewrite. Now uses up-to-date technologies.
4+
### Unreleased
5+
* !! Big rewrite. Now uses up-to-date conventions and technologies.
66
* ++ Added virtual environment creation on project rollout.
77
* ++ CLI. Added 'venv reset' command.
88
* ++ CLI. Descriptions passed to 'change' command all go into a commit messages.

src/makeapp/app_templates/__default__/CHANGELOG.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,4 @@
22

33

44
### Unreleased
5-
65
* ++ Basic functionality.

src/makeapp/apptools.py

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ def version_bump(self, increment: str = 'patch') -> tuple[int, ...]:
164164
class ChangelogData(DataContainer):
165165
"""Information gathered from changelog file."""
166166

167-
PREFIXES = '!+-*'
167+
change_markers = '!+-*'
168168
"""Line prefixes denoting change nature.
169169
170170
! Important change/improvement/fix
@@ -173,36 +173,42 @@ class ChangelogData(DataContainer):
173173
* Minor change/improvement/fix
174174
175175
"""
176+
change_marker_default = '*'
176177

177-
FILENAME_CHANGELOG = 'CHANGELOG.md'
178-
UNRELEASED_STR = 'Unreleased'
178+
filename = 'CHANGELOG.md'
179+
marker_unreleased = 'Unreleased'
180+
181+
_prefix_version = '### '
182+
_prefix_change = '* '
183+
_offset_change = 1
179184

180185
@classmethod
181186
def get(cls) -> 'ChangelogData':
182187
"""Gathers information from a changelog."""
183188

184-
filepath = cls.FILENAME_CHANGELOG
189+
filepath = Path(cls.filename)
185190

186-
LOG.debug(f'Getting changelog from `{os.path.basename(filepath)}` ...')
191+
LOG.debug(f'Getting changelog from: {filepath.name} ...')
187192

188-
if not os.path.isfile(filepath):
193+
if not filepath.is_file():
189194
raise ProjectorExeption('Changelog file not found.')
190195

191196
changelog = FileHelper.read_file(filepath)
192197

193198
if not changelog[0].startswith('# '):
194199
raise ProjectorExeption('Unexpected changelog file format.')
195200

196-
unreleased_str = cls.UNRELEASED_STR
201+
unreleased_str = cls.marker_unreleased
202+
prefix_version = cls._prefix_version
197203
version_line_idx = None
198204

199-
for supposed_line_idx in (3, 4, 5):
205+
for supposed_line_idx in (2, 3, 4):
200206
line = changelog[supposed_line_idx].lstrip('# ')
201207
unreleased_entry_exists = line == unreleased_str
202208

203209
if unreleased_entry_exists or line.startswith('v'):
204210
version_line_idx = supposed_line_idx
205-
LOG.info(f'Current version from changelog: `{line}`')
211+
LOG.info(f'Current version from changelog: {line}.')
206212
break
207213

208214
if version_line_idx is None:
@@ -211,15 +217,10 @@ def get(cls) -> 'ChangelogData':
211217
if not unreleased_entry_exists:
212218
# Add `Unreleased` entry.
213219
changelog[version_line_idx:version_line_idx] = [
214-
f'## {unreleased_str}',
215-
'', ''
220+
f'{prefix_version}{unreleased_str}',
221+
''
216222
]
217223

218-
# Normalize.
219-
if version_line_idx == 3:
220-
changelog.insert(3, '')
221-
version_line_idx = 4
222-
223224
result = ChangelogData(
224225
file_helper=FileHelper(
225226
filepath=filepath,
@@ -233,22 +234,22 @@ def get(cls) -> 'ChangelogData':
233234
def deduce_version_increment(self) -> str:
234235
"""Deduces version increment chunk from a changelog.
235236
236-
Changelog bullets:
237+
Changelog markers:
237238
* changed/fixed
238239
+ new/feature
239240
- removed
240241
241242
Deduction rules:
242243
By default `patch` chunk is incremented.
243-
If any + entries `minor` is incremented.
244+
If any `+` entries, `minor` is incremented.
244245
245246
Returns: major, minor, patch
246247
247248
"""
248249
supposed_chunk = 'patch'
249250

250251
for change in self.get_changes():
251-
if change.startswith('* ++'):
252+
if change.startswith(f'{self._prefix_change}++'):
252253
supposed_chunk = 'minor'
253254
break
254255
return supposed_chunk
@@ -261,14 +262,14 @@ def version_bump(self, new_version: tuple[int, ...]) -> str:
261262
:param new_version:
262263
263264
"""
264-
version_str = f"v{'.'.join(map(str, new_version))}"
265-
version_with_date = f"{version_str} [{datetime.now().strftime('%Y-%m-%d')}]"
265+
version_num = f"v{'.'.join(map(str, new_version))}"
266+
version_with_date = f"{self._prefix_version}{version_num} [{datetime.now().strftime('%Y-%m-%d')}]"
266267

267268
replace = self.file_helper.line_replace
268269

269270
replace(version_with_date)
270271

271-
return version_str
272+
return version_num
272273

273274
def add_change(self, description: str):
274275
"""Adds change into changelog.
@@ -279,23 +280,23 @@ def add_change(self, description: str):
279280
if not description:
280281
return
281282

282-
bullet = description[0]
283-
prefixes = self.PREFIXES
283+
marker = description[0]
284+
markers = self.change_markers
284285

285-
if bullet not in prefixes:
286-
bullet = '*'
286+
if marker not in markers:
287+
marker = self.change_marker_default
287288

288-
description = description.lstrip(f' {prefixes}')
289-
description = f'* {bullet * 2} {description}'
289+
description = description.lstrip(f' {markers}')
290+
description = f'{self._prefix_change}{marker * 2} {description}'
290291

291-
self.file_helper.insert(description, offset=2)
292+
self.file_helper.insert(description, offset=self._offset_change)
292293

293294
def get_changes(self) -> list[str]:
294295
"""Returns a list of new version changes from a changelog."""
295296

296297
changes = []
297298

298-
for line in self.file_helper.iter_after(offset=2):
299+
for line in self.file_helper.iter_after(offset=self._offset_change):
299300

300301
if not line.strip():
301302
break
@@ -310,9 +311,9 @@ def get_version_summary(self) -> str:
310311
return '\n'.join(self.get_changes()).strip()
311312

312313
def sort_version_changes(self):
313-
"""Sorts changes of latest version inplace."""
314+
"""Sorts changes of the latest version inplace."""
314315

315-
priorities = {prefix: priority for priority, prefix in enumerate(self.PREFIXES)}
316+
priorities = {prefix: priority for priority, prefix in enumerate(self.change_markers)}
316317
priority_default = 3
317318

318319
def sorter(line):
@@ -464,7 +465,7 @@ def add_change(self, descriptions: list[str] | tuple[str, ...] | str, *, stage_m
464465

465466
changelog.write()
466467

467-
commit_message = f'{changelog.FILENAME_CHANGELOG} updated'
468+
commit_message = f'{changelog.filename} updated'
468469

469470
files_to_stage = [changelog.filepath]
470471

@@ -473,7 +474,7 @@ def add_change(self, descriptions: list[str] | tuple[str, ...] | str, *, stage_m
473474

474475
# Set a description as a commit message.
475476
commit_message = '\n'.join(
476-
description.strip(changelog.PREFIXES)
477+
description.strip(changelog.change_markers)
477478
for description in descriptions
478479
)
479480

src/makeapp/helpers/files.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
class FileHelper:
1010
"""Encapsulates file related functions."""
1111

12-
def __init__(self, filepath, line_idx, contents):
12+
def __init__(self, filepath: Path | str, line_idx, contents):
1313
self.filepath = filepath
1414
self.line_idx = line_idx
1515
self.contents = contents

src/makeapp/helpers/vcs.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ def add(self, filename: list[str] | str | list[Path] | Path = None):
9898
"""
9999
filename = filename or []
100100
if isinstance(filename, list):
101-
filename = ' '.join(filename)
101+
filename = ' '.join(map(str, filename))
102102

103103
filename = f'{filename}'.strip() or ''
104104

@@ -172,7 +172,7 @@ def add_remote(self, address: str, *, alias: str = 'origin'):
172172

173173
self.run_command(f'remote add {alias} {address}')
174174

175-
def add(self, filename: str = None):
175+
def add(self, filename: Path | str = None):
176176
"""Adds a file into a changelist.
177177
178178
:param filename: If not provided all files in working tree are added.

tests/test_apptools.py

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from textwrap import dedent
22

3+
from makeapp.apptools import Project, ChangelogData
34
from makeapp.helpers.vcs import VcsHelper
4-
from makeapp.apptools import Project
55

66

77
def test_git(in_tmp_path, get_appmaker, assert_content, monkeypatch):
@@ -27,13 +27,7 @@ def test_git(in_tmp_path, get_appmaker, assert_content, monkeypatch):
2727
version, summary = project.get_release_info()
2828

2929
assert version == 'v0.1.0'
30-
assert summary == (
31-
'* ++ add.\n'
32-
'* ++ Basic functionality.\n'
33-
'* ** change.\n'
34-
'* ** fix1.\n'
35-
'* ++ Basic functionality.'
36-
)
30+
assert summary == '* !! warn.\n* ++ add.\n* ++ Basic functionality.\n* ** change.\n* ** fix1.'
3731

3832
project.release(version, summary)
3933

@@ -72,3 +66,45 @@ def test_venv(in_tmp_path, get_appmaker, assert_content):
7266
assert_content(in_tmp_path / '.venv/pyvenv.cfg', [
7367
'version_info ='
7468
])
69+
70+
71+
def test_changelog(in_tmp_path):
72+
73+
fchangelog = (in_tmp_path / ChangelogData.filename)
74+
fchangelog.write_text(dedent(""" # {{ app_name }} changelog
75+
76+
### Unreleased
77+
* ++ Basic functionality.
78+
79+
"""))
80+
81+
def load():
82+
data_ = ChangelogData.get()
83+
contents_ = data_.file_helper.contents
84+
return data_, contents_
85+
86+
data, contents = load()
87+
assert len(contents) == 5
88+
89+
assert data.deduce_version_increment() == 'minor'
90+
data.sort_version_changes()
91+
92+
data.add_change('Some fix')
93+
assert len(contents) == 6
94+
95+
assert data.get_version_summary() == '* ** Some fix\n* ++ Basic functionality.'
96+
97+
assert data.version_bump((1, 2, 3)) == 'v1.2.3'
98+
assert '1.2.3' in contents[2]
99+
data.write()
100+
101+
# another loop
102+
data, contents = load()
103+
assert len(contents) == 7 # Unreleased added
104+
assert data.deduce_version_increment() == 'patch'
105+
106+
data.add_change('+ Some feature')
107+
assert len(contents) == 8
108+
assert data.deduce_version_increment() == 'minor'
109+
110+
assert data.get_version_summary() == '* ++ Some feature'

0 commit comments

Comments
 (0)