Skip to content

Commit 3602389

Browse files
committed
Refactor Changelog modifications so clearer order of operations and have testing
1 parent f608f7f commit 3602389

File tree

7 files changed

+239
-173
lines changed

7 files changed

+239
-173
lines changed

exasol/toolbox/nox/_release.py

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,8 @@
1313
_version,
1414
)
1515
from exasol.toolbox.nox.plugin import NoxTasks
16-
from exasol.toolbox.release import (
17-
extract_release_notes,
18-
new_changelog,
19-
new_changes,
20-
new_unreleased,
21-
)
2216
from exasol.toolbox.util.git import Git
17+
from exasol.toolbox.util.release.changelog import Changelogs
2318
from exasol.toolbox.util.version import (
2419
ReleaseTypes,
2520
Version,
@@ -68,22 +63,6 @@ def _update_project_version(session: Session, version: Version) -> Version:
6863
return version
6964

7065

71-
def _update_changelog(version: Version) -> tuple[Path, Path, Path]:
72-
unreleased = Path(PROJECT_CONFIG.root) / "doc" / "changes" / "unreleased.md"
73-
changelog = Path(PROJECT_CONFIG.root) / "doc" / "changes" / f"changes_{version}.md"
74-
changes = Path(PROJECT_CONFIG.root) / "doc" / "changes" / f"changelog.md"
75-
76-
changelog_content = extract_release_notes(unreleased)
77-
changelog.write_text(new_changelog(version, changelog_content))
78-
79-
unreleased.write_text(new_unreleased())
80-
81-
changes_content = new_changes(changes, version)
82-
changes.write_text(changes_content)
83-
84-
return changelog, changes, unreleased
85-
86-
8766
def _add_files_to_index(session: Session, files: list[Path]) -> None:
8867
for file in files:
8968
session.run("git", "add", f"{file}")
@@ -138,7 +117,12 @@ def prepare_release(session: Session) -> None:
138117
Git.create_and_switch_to_branch(f"release/prepare-{new_version}")
139118

140119
_ = _update_project_version(session, new_version)
141-
changelog, changes, unreleased = _update_changelog(new_version)
120+
121+
changelogs = Changelogs(
122+
changes_path=PROJECT_CONFIG.doc / "changes", version=new_version
123+
)
124+
changelogs.update_changelogs_for_release()
125+
changed_files = changelogs.get_changed_files()
142126

143127
pm = NoxTasks.plugin_manager(PROJECT_CONFIG)
144128
pm.hook.prepare_release_update_version(
@@ -148,18 +132,15 @@ def prepare_release(session: Session) -> None:
148132
if args.no_add:
149133
return
150134

151-
files = [
152-
changelog,
153-
unreleased,
154-
changes,
135+
changed_files += [
155136
PROJECT_CONFIG.root / "pyproject.toml",
156137
PROJECT_CONFIG.version_file,
157138
]
158139
results = pm.hook.prepare_release_add_files(session=session, config=PROJECT_CONFIG)
159-
files += [f for plugin_response in results for f in plugin_response]
140+
changed_files += [f for plugin_response in results for f in plugin_response]
160141
_add_files_to_index(
161142
session,
162-
files,
143+
changed_files,
163144
)
164145
session.run("git", "commit", "-m", f"Prepare release {new_version}")
165146

exasol/toolbox/release/__init__.py

Lines changed: 0 additions & 85 deletions
This file was deleted.

exasol/toolbox/util/git.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import subprocess
1+
import subprocess # nosec
22
from functools import wraps
33
from pathlib import Path
44

@@ -9,7 +9,7 @@ def wrapper(*args, **kwargs) -> str:
99
command_list = func(*args, **kwargs).split()
1010
output = subprocess.run(
1111
command_list, capture_output=True, text=True, check=True
12-
)
12+
) # nosec
1313
return output.stdout.strip()
1414

1515
return wrapper

exasol/toolbox/util/release/__init__.py

Whitespace-only changes.
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
from __future__ import annotations
2+
3+
from datetime import datetime
4+
from inspect import cleandoc
5+
from pathlib import Path
6+
7+
from exasol.toolbox.util.version import Version
8+
9+
UNRELEASED_TEXT = "# Unreleased\n"
10+
11+
12+
class Changelogs:
13+
def __init__(self, changes_path: Path, version: Version) -> None:
14+
self.version = version
15+
self.unreleased_md: Path = changes_path / "unreleased.md"
16+
self.versioned_changelog_md: Path = changes_path / f"changes_{version}.md"
17+
self.changes_md: Path = changes_path / "changes.md"
18+
19+
def _create_new_unreleased(self):
20+
"""
21+
Write a new unreleased changelog file.
22+
"""
23+
self.unreleased_md.write_text(UNRELEASED_TEXT)
24+
25+
def _create_versioned_changelog(self, content: str) -> None:
26+
"""
27+
Create a changelog entry for a specific version.
28+
29+
Args:
30+
content: The content of the changelog entry.
31+
32+
"""
33+
template = cleandoc(
34+
f"""
35+
# {self.version} - {datetime.today().strftime("%Y-%m-%d")}
36+
37+
{content}
38+
"""
39+
)
40+
self.versioned_changelog_md.write_text(template)
41+
42+
def _extract_unreleased_notes(self) -> str:
43+
"""
44+
Extract release notes from `unreleased.md`.
45+
"""
46+
with self.unreleased_md.open(mode="r", encoding="utf-8") as f:
47+
# skip header when reading in file, as contains # Unreleased
48+
lines = f.readlines()[1:]
49+
unreleased_content = cleandoc("".join(lines))
50+
unreleased_content += "\n"
51+
return unreleased_content
52+
53+
def _update_changelog_table_of_contents(self) -> None:
54+
"""
55+
Read in existing `changelog.md` and append to appropriate sections
56+
before writing out to again.
57+
"""
58+
updated_content = []
59+
with self.changes_md.open(mode="r", encoding="utf-8") as f:
60+
for line in f:
61+
updated_content.append(line)
62+
if line.startswith("* [unreleased]"):
63+
updated_content.append(
64+
f"* [{self.version}](changes_{self.version}.md)\n"
65+
)
66+
if line.startswith("unreleased"):
67+
updated_content.append(f"changes_{self.version}\n")
68+
updated_content_str = "".join(updated_content)
69+
70+
self.changes_md.write_text(updated_content_str)
71+
72+
def get_changed_files(self) -> list[Path]:
73+
return [self.unreleased_md, self.versioned_changelog_md, self.changes_md]
74+
75+
def update_changelogs_for_release(self) -> None:
76+
"""
77+
Rotates the changelogs as is needed for a release.
78+
79+
1. Moves the contents of the `unreleased.md` to the `changes_<version>.md`
80+
2. Create a new `unreleased.md`
81+
3. Updates the table of contents in the `change.md` with the new `changes_<version>.md`
82+
"""
83+
84+
# create versioned changelog
85+
unreleased_content = self._extract_unreleased_notes()
86+
self._create_versioned_changelog(unreleased_content)
87+
88+
# update other changelogs now that versioned changelog exists
89+
self._create_new_unreleased()
90+
self._update_changelog_table_of_contents()

test/unit/release_test.py

Lines changed: 14 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@
1212
ReleaseError,
1313
_trigger_release,
1414
)
15-
from exasol.toolbox.release import (
16-
extract_release_notes,
17-
new_changelog,
15+
from exasol.toolbox.util.release.changelog import (
16+
_new_changelog,
1817
)
1918
from exasol.toolbox.util.version import Version
2019

@@ -26,72 +25,30 @@
2625
Version(0, 1, 0),
2726
cleandoc(
2827
"""
29-
## Added
30-
* Some great feature
28+
## Added
29+
* Some great feature
3130
32-
## Refactored
33-
* Replaced xyz
34-
"""
31+
## Refactored
32+
* Replaced xyz
33+
"""
3534
),
3635
datetime(2024, 2, 7),
3736
cleandoc(
3837
"""
39-
# 0.1.0 - 2024-02-07
38+
# 0.1.0 - 2024-02-07
4039
41-
## Added
42-
* Some great feature
40+
## Added
41+
* Some great feature
4342
44-
## Refactored
45-
* Replaced xyz
46-
"""
43+
## Refactored
44+
* Replaced xyz
45+
"""
4746
),
4847
),
4948
],
5049
)
5150
def test_changelog(version, content, date, expected):
52-
actual = new_changelog(version, content, date)
53-
assert expected == actual
54-
55-
56-
@pytest.fixture
57-
def unreleased_md(tmp_path):
58-
file = tmp_path / "unreleased.md"
59-
file.write_text(
60-
cleandoc(
61-
"""
62-
# Unreleased
63-
64-
## ✨ Added
65-
* Added Awesome feature
66-
67-
## 🔧 Changed
68-
* Some behaviour
69-
70-
## 🐞 Fixed
71-
* Fixed nasty bug
72-
"""
73-
)
74-
)
75-
yield file
76-
77-
78-
def test_extract_release_notes(unreleased_md):
79-
expected = (
80-
cleandoc(
81-
"""
82-
## ✨ Added
83-
* Added Awesome feature
84-
85-
## 🔧 Changed
86-
* Some behaviour
87-
88-
## 🐞 Fixed
89-
* Fixed nasty bug
90-
"""
91-
)
92-
+ "\n"
93-
)
94-
actual = extract_release_notes(unreleased_md)
51+
actual = _new_changelog(version, content, date)
9552
assert expected == actual
9653

9754

0 commit comments

Comments
 (0)