diff --git a/.github/scripts/test_update_dependency_changes.py b/.github/scripts/test_update_dependency_changes.py
index fde9720590..2afaeec6a3 100644
--- a/.github/scripts/test_update_dependency_changes.py
+++ b/.github/scripts/test_update_dependency_changes.py
@@ -14,7 +14,7 @@ import sys
import os
sys.path.insert(0, os.path.dirname(__file__))
-from update_dependency_changes import merge_changes, render_section, normalize_version, extract_preamble
+from update_dependency_changes import merge_changes, render_section, normalize_version, extract_preamble, bump_patch_if_released
def test_update_then_revert():
@@ -438,6 +438,55 @@ def test_normalize_version_stable():
print("✓ Passed: stable versions unchanged\n")
+def test_bump_patch_no_tag():
+ """Test: version tag does not exist, should return as-is."""
+ print("Test 23: bump_patch_if_released - no tag exists")
+ tag_exists = lambda t: False
+ assert bump_patch_if_released("10.3.0", tag_exists) == "10.3.0"
+ assert bump_patch_if_released("10.2.0", tag_exists) == "10.2.0"
+ print("✓ Passed: version unchanged when tag does not exist\n")
+
+
+def test_bump_patch_tag_exists():
+ """Test: version tag exists, should bump patch."""
+ print("Test 24: bump_patch_if_released - tag exists")
+ existing_tags = {"10.3.0"}
+ tag_exists = lambda t: t in existing_tags
+ assert bump_patch_if_released("10.3.0", tag_exists) == "10.3.1", \
+ f"Expected '10.3.1', got: {bump_patch_if_released('10.3.0', tag_exists)}"
+ print("✓ Passed: version bumped to 10.3.1\n")
+
+
+def test_bump_patch_multiple_tags():
+ """Test: multiple consecutive tags exist, should bump past all."""
+ print("Test 25: bump_patch_if_released - multiple tags exist")
+ existing_tags = {"10.3.0", "10.3.1", "10.3.2"}
+ tag_exists = lambda t: t in existing_tags
+ assert bump_patch_if_released("10.3.0", tag_exists) == "10.3.3", \
+ f"Expected '10.3.3', got: {bump_patch_if_released('10.3.0', tag_exists)}"
+ print("✓ Passed: version bumped past all existing tags\n")
+
+
+def test_bump_patch_prerelease_skipped():
+ """Test: pre-release versions should not be bumped."""
+ print("Test 26: bump_patch_if_released - pre-release skipped")
+ tag_exists = lambda t: True # all tags "exist"
+ assert bump_patch_if_released("10.3.0-rc.1", tag_exists) == "10.3.0-rc.1"
+ assert bump_patch_if_released("10.3.0-rc.2", tag_exists) == "10.3.0-rc.2"
+ assert bump_patch_if_released("10.3.0-preview", tag_exists) == "10.3.0-preview"
+ print("✓ Passed: pre-release versions not bumped\n")
+
+
+def test_bump_patch_non_zero_patch():
+ """Test: version with non-zero patch, tag exists, should bump."""
+ print("Test 27: bump_patch_if_released - non-zero patch version")
+ existing_tags = {"10.3.1"}
+ tag_exists = lambda t: t in existing_tags
+ assert bump_patch_if_released("10.3.1", tag_exists) == "10.3.2", \
+ f"Expected '10.3.2', got: {bump_patch_if_released('10.3.1', tag_exists)}"
+ print("✓ Passed: non-zero patch correctly bumped\n")
+
+
def run_all_tests():
"""Run all test cases."""
print("=" * 70)
@@ -466,9 +515,14 @@ def run_all_tests():
test_normalize_version_preview()
test_normalize_version_rc()
test_normalize_version_stable()
+ test_bump_patch_no_tag()
+ test_bump_patch_tag_exists()
+ test_bump_patch_multiple_tags()
+ test_bump_patch_prerelease_skipped()
+ test_bump_patch_non_zero_patch()
print("=" * 70)
- print("All 22 tests passed! ✓")
+ print("All 27 tests passed! ✓")
print("=" * 70)
print("\nTest coverage summary:")
print(" ✓ Basic scenarios (update, add, remove)")
@@ -478,6 +532,7 @@ def run_all_tests():
print(" ✓ Document format validation")
print(" ✓ Preamble extraction (SEO block, no preamble, no heading)")
print(" ✓ Version normalization (preview -> rc.1)")
+ print(" ✓ Patch version bump when tag already released")
print("=" * 70)
diff --git a/.github/scripts/update_dependency_changes.py b/.github/scripts/update_dependency_changes.py
index f4f2c3db6c..02ab623ff8 100644
--- a/.github/scripts/update_dependency_changes.py
+++ b/.github/scripts/update_dependency_changes.py
@@ -25,6 +25,46 @@ def normalize_version(version):
return version
+def check_tag_exists(tag):
+ """Check if a git tag exists."""
+ result = subprocess.run(
+ ["git", "tag", "-l", tag],
+ capture_output=True,
+ text=True,
+ )
+ return result.returncode == 0 and tag in result.stdout.strip().split("\n")
+
+
+def bump_patch_if_released(version, tag_exists_fn=None):
+ """If the version tag already exists, bump the patch version.
+
+ Only applies to stable versions (no pre-release suffix like -rc.N).
+ """
+ if tag_exists_fn is None:
+ tag_exists_fn = check_tag_exists
+
+ # Only bump stable versions (no pre-release suffix)
+ if "-" in version:
+ return version
+
+ parts = version.split(".")
+ if len(parts) != 3:
+ return version
+
+ major, minor = parts[0], parts[1]
+ try:
+ patch = int(parts[2])
+ except ValueError:
+ return version
+
+ current = version
+ while tag_exists_fn(current):
+ patch += 1
+ current = f"{major}.{minor}.{patch}"
+
+ return current
+
+
def get_version():
"""Read the current version from common.props."""
try:
@@ -296,6 +336,9 @@ def main():
print("Could not read version from common.props.")
sys.exit(1)
+ version = bump_patch_if_released(version)
+ print(f"Resolved version: {version}")
+
diff = get_diff(base_ref)
if not diff:
print("No diff found for Directory.Packages.props.")
diff --git a/.github/workflows/nuget-packages-version-change-detector.yml b/.github/workflows/nuget-packages-version-change-detector.yml
index 45dba3332b..4ed7fad8de 100644
--- a/.github/workflows/nuget-packages-version-change-detector.yml
+++ b/.github/workflows/nuget-packages-version-change-detector.yml
@@ -44,8 +44,10 @@ jobs:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 1
- - name: Fetch base branch
- run: git fetch origin ${{ github.event.pull_request.base.ref }}:refs/remotes/origin/${{ github.event.pull_request.base.ref }} --depth=1
+ - name: Fetch base branch and tags
+ run: |
+ git fetch origin ${{ github.event.pull_request.base.ref }}:refs/remotes/origin/${{ github.event.pull_request.base.ref }} --depth=1
+ git fetch --tags origin
- uses: actions/setup-python@v5
with:
diff --git a/Directory.Packages.props b/Directory.Packages.props
index df4d704afc..4b5cfa90f1 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -174,7 +174,7 @@
-
+
diff --git a/docs/en/package-version-changes.md b/docs/en/package-version-changes.md
index 86a4a0cfc9..5b4722e976 100644
--- a/docs/en/package-version-changes.md
+++ b/docs/en/package-version-changes.md
@@ -7,6 +7,12 @@
# Package Version Changes
+## 10.3.1
+
+| Package | Old Version | New Version | PR |
+|---------|-------------|-------------|-----|
+| System.Security.Cryptography.Xml | 10.0.2 | 10.0.6 | #25279 |
+
## 10.3.0-rc.1
| Package | Old Version | New Version | PR |