Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions eng/tools/azure-sdk-tools/packaging_tools/sdk_update_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,43 @@ def edit_version_file(content: list[str]):
def edit_changelog_file(content: list[str]):
nonlocal unchanged
version_line = f"## {version} ({release_date})\n"
first_version_index = -1
for i in range(0, len(content)):
if re.match(r"^## \d+\.\d+\.\d+(b\d+)?", content[i]):
content[i] = version_line
unchanged = False
first_version_index = i
_LOGGER.info(f"Updated version line in CHANGELOG.md to: {version_line.strip()}")
break

# Remove duplicate version section: if a later section has the same version,
# delete it (from its header up to the next version header or end of file).
# This handles re-generation where the same changelog content is inserted again.
if first_version_index >= 0:
# Capture the version token from each version header (e.g., "1.2.3" or "1.2.3b1").
version_pattern = re.compile(r"^## (?P<ver>\d+\.\d+\.\d+(b\d+)?)")
duplicate_start = -1
for j in range(first_version_index + 1, len(content)):
match = version_pattern.match(content[j])
if match:
# Compare only the version token, ignoring the release date or other suffixes.
if match.group("ver") == version:
duplicate_start = j
break # stop at the next version header regardless

if duplicate_start >= 0:
# Find end of the duplicate section (next version header or EOF)
duplicate_end = len(content)
for k in range(duplicate_start + 1, len(content)):
if version_pattern.match(content[k]):
duplicate_end = k
break
_LOGGER.info(
f"Removing duplicate version section for {version} "
f"(lines {duplicate_start + 1}-{duplicate_end})"
)
del content[duplicate_start:duplicate_end]

modify_file(str(changelog_path), edit_changelog_file)
if unchanged:
_LOGGER.warning(f"No version line found in {changelog_path} to update.")
Expand Down
92 changes: 92 additions & 0 deletions eng/tools/azure-sdk-tools/tests/test_sdk_update_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,95 @@ def mock_log_failed_message(message: str, enable_log_error: bool):
assert (
log_level is None
), "Expected no error log for invalid changelog content in ARM SDK when version is explicitly provided"


def test_duplicate_version_section_removed(temp_package):
"""When re-generating an SDK, the changelog may end up with duplicate version sections.
The update_version_main should detect and remove the duplicate. (GitHub issue #12425)"""
pkg = temp_package
(pkg / "_version.py").write_text('VERSION = "1.1.0b2"\n')

# Simulate re-generation: changelog has a new section (with placeholder version)
# followed by an existing section with the same content and version.
changelog_content = (
"# Release History\n"
"\n"
"## 0.0.0 (UnReleased)\n"
"\n"
"### Features Added\n"
"\n"
" - Model `Client` added parameter `setting` in method `__init__`\n"
"\n"
"### Breaking Changes\n"
"\n"
" - Model `StorageProperties` deleted property `iops`\n"
"\n"
"## 1.1.0b2 (2025-10-08)\n"
"\n"
"### Features Added\n"
"\n"
" - Model `Client` added parameter `setting` in method `__init__`\n"
"\n"
"### Breaking Changes\n"
"\n"
" - Model `StorageProperties` deleted property `iops`\n"
"\n"
"## 1.1.0b1 (2025-09-01)\n"
"\n"
"### Other Changes\n"
"\n"
" - Initial version\n"
)
(pkg / "CHANGELOG.md").write_text(changelog_content)

package_result = {
"version": "1.1.0b2",
"tagIsStable": False,
"targetReleaseDate": "2025-10-08",
"changelog": {"content": "### Features Added\n\n - Model `Client` added parameter `setting`"},
}
update_version_main(pkg, package_result=package_result)

result = (pkg / "CHANGELOG.md").read_text()
# The duplicate '## 1.1.0b2' section should be removed
assert result.count("## 1.1.0b2") == 1, f"Expected exactly one '## 1.1.0b2' section, got:\n{result}"
# The older version section should still be present
assert "## 1.1.0b1 (2025-09-01)" in result
assert " - Initial version" in result


def test_no_duplicate_version_left_intact(temp_package):
"""When there is no duplicate, the changelog should remain unchanged aside from the version line update."""
pkg = temp_package
(pkg / "_version.py").write_text('VERSION = "1.0.0b1"\n')

changelog_content = (
"# Release History\n"
"\n"
"## 0.0.0 (UnReleased)\n"
"\n"
"### Features Added\n"
"\n"
" - New feature\n"
"\n"
"## 1.0.0b1 (2025-09-01)\n"
"\n"
"### Other Changes\n"
"\n"
" - Initial version\n"
)
(pkg / "CHANGELOG.md").write_text(changelog_content)

package_result = {
"version": "1.0.0b1",
"tagIsStable": False,
"targetReleaseDate": "2025-10-08",
"changelog": {"content": "### Features Added\n\n - New feature"},
}
update_version_main(pkg, package_result=package_result)

result = (pkg / "CHANGELOG.md").read_text()
# Version should be updated to b2
assert "## 1.0.0b2" in result
# Old b1 section should still exist (it's a different version, not a duplicate)
assert "## 1.0.0b1 (2025-09-01)" in result
Loading