feat(release): publish changelog-driven notes
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
"""Release-note changelog extraction tests (DC-6, DC-7)."""
|
||||
import importlib.util
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
EXTRACTOR_PATH = REPO_ROOT / "scripts" / "extract_changelog.py"
|
||||
RELEASE_REQUEST_PATH = REPO_ROOT / "scripts" / "release_request.py"
|
||||
CHANGELOG_PATH = REPO_ROOT / "CHANGELOG.md"
|
||||
|
||||
|
||||
def _extractor_module():
|
||||
spec = importlib.util.spec_from_file_location("extract_changelog", EXTRACTOR_PATH)
|
||||
assert spec and spec.loader
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[spec.name] = module
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def test_extracts_the_requested_version_section_verbatim():
|
||||
extractor = _extractor_module()
|
||||
changelog = """# Changelog
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.4.0] - 2026-09-10
|
||||
|
||||
### Added
|
||||
|
||||
- Show a release summary to consumers.
|
||||
|
||||
## [1.3.0] - 2026-09-01
|
||||
|
||||
### Fixed
|
||||
|
||||
- Preserve the observation history during upgrades.
|
||||
"""
|
||||
expected = """## [1.4.0] - 2026-09-10
|
||||
|
||||
### Added
|
||||
|
||||
- Show a release summary to consumers.
|
||||
|
||||
"""
|
||||
|
||||
assert extractor.extract_version_section(changelog, "1.4.0") == expected
|
||||
|
||||
|
||||
def test_checked_in_changelog_keeps_unreleased_first_and_categories_limited():
|
||||
lines = CHANGELOG_PATH.read_text(encoding="utf-8").splitlines()
|
||||
unreleased = lines.index("## [Unreleased]")
|
||||
version_headings = [
|
||||
index for index, line in enumerate(lines)
|
||||
if line.startswith("## [") and line != "## [Unreleased]"
|
||||
]
|
||||
first_version = version_headings[0] if version_headings else len(lines)
|
||||
categories = [
|
||||
line.removeprefix("### ")
|
||||
for line in lines[unreleased + 1:first_version]
|
||||
if line.startswith("### ")
|
||||
]
|
||||
|
||||
assert unreleased < first_version
|
||||
assert set(categories) <= {"Added", "Changed", "Fixed"}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("changelog", "expected_error"),
|
||||
[
|
||||
("# Changelog\n\n## [Unreleased]\n", "missing"),
|
||||
(
|
||||
"# Changelog\n\n## [Unreleased]\n\n## [1.4.0] - 2026-09-10\n",
|
||||
"empty",
|
||||
),
|
||||
(
|
||||
"# Changelog\n\n## [Unreleased]\n\n## [1.4.0] - 2026-02-30\n\n- Add a note.\n",
|
||||
"malformed release date",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_fails_closed_for_missing_empty_or_malformed_sections(
|
||||
changelog, expected_error
|
||||
):
|
||||
extractor = _extractor_module()
|
||||
|
||||
with pytest.raises(extractor.ChangelogError, match=expected_error):
|
||||
extractor.extract_version_section(changelog, "1.4.0")
|
||||
|
||||
|
||||
def test_command_emits_a_workflow_error_and_nonzero_status(tmp_path):
|
||||
changelog = tmp_path / "CHANGELOG.md"
|
||||
changelog.write_text("# Changelog\n\n## [Unreleased]\n", encoding="utf-8")
|
||||
|
||||
result = subprocess.run(
|
||||
[sys.executable, str(EXTRACTOR_PATH), str(changelog), "1.4.0"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
assert result.returncode != 0
|
||||
assert result.stderr.startswith("::error::")
|
||||
assert "missing" in result.stderr
|
||||
|
||||
|
||||
def test_assembles_a_release_body_without_changing_the_section():
|
||||
extractor = _extractor_module()
|
||||
section = "## [1.4.0] - 2026-09-10\n\n### Added\n\n- Show a release summary.\n"
|
||||
footer = "## Install\n\nUse the package channel.\n"
|
||||
|
||||
assert extractor.assemble_release_body(section, footer) == (
|
||||
section + "\n" + footer
|
||||
)
|
||||
|
||||
|
||||
def test_command_can_write_the_complete_release_body(tmp_path):
|
||||
changelog = tmp_path / "CHANGELOG.md"
|
||||
changelog.write_text(
|
||||
"# Changelog\n\n## [Unreleased]\n\n## [1.4.0] - 2026-09-10\n\n"
|
||||
"### Added\n\n- Show a release summary.\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
footer = tmp_path / "footer.md"
|
||||
footer.write_text("## Install\n\nUse the package channel.\n", encoding="utf-8")
|
||||
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(EXTRACTOR_PATH),
|
||||
str(changelog),
|
||||
"1.4.0",
|
||||
"--footer",
|
||||
str(footer),
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
assert result.returncode == 0
|
||||
assert result.stdout == (
|
||||
"## [1.4.0] - 2026-09-10\n\n### Added\n\n- Show a release summary.\n\n"
|
||||
"## Install\n\nUse the package channel.\n"
|
||||
)
|
||||
|
||||
|
||||
def test_release_request_command_reports_create_or_patch_decisions(tmp_path):
|
||||
body = tmp_path / "release-body.md"
|
||||
body.write_text("## [1.4.0] - 2026-09-10\n", encoding="utf-8")
|
||||
|
||||
create = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(RELEASE_REQUEST_PATH),
|
||||
"--version",
|
||||
"1.4.0",
|
||||
"--body-file",
|
||||
str(body),
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
existing = tmp_path / "existing-release.json"
|
||||
existing.write_text('{"id": 17, "assets": []}', encoding="utf-8")
|
||||
patch = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(RELEASE_REQUEST_PATH),
|
||||
"--version",
|
||||
"1.4.0",
|
||||
"--body-file",
|
||||
str(body),
|
||||
"--existing-release",
|
||||
str(existing),
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
assert create.returncode == patch.returncode == 0
|
||||
assert json.loads(create.stdout) == {
|
||||
"method": "POST",
|
||||
"path": "/releases",
|
||||
"payload": {
|
||||
"tag_name": "v1.4.0",
|
||||
"name": "v1.4.0",
|
||||
"body": "## [1.4.0] - 2026-09-10\n",
|
||||
},
|
||||
}
|
||||
assert json.loads(patch.stdout) == {
|
||||
"method": "PATCH",
|
||||
"path": "/releases/17",
|
||||
"payload": {"body": "## [1.4.0] - 2026-09-10\n"},
|
||||
}
|
||||
Reference in New Issue
Block a user