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
81 changes: 64 additions & 17 deletions .github/scripts/bump-nuget.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
#!/usr/bin/env python3
"""
Simplified package bumping for Codebelt service updates (Option B).
Package bumping for Codebelt service updates.

Only updates packages published by the triggering source repo.
Updates packages published by the triggering source repo to the specified version.
Additionally fetches the latest stable version from NuGet for all other Codebelt-related
packages and updates them as well.
Does NOT update Microsoft.Extensions.*, BenchmarkDotNet, or other third-party packages.
Does NOT parse TFM conditions - only bumps Codebelt/Cuemon/Savvyio packages to the triggering version.

Usage:
TRIGGER_SOURCE=cuemon TRIGGER_VERSION=10.3.0 python3 bump-nuget.py

Behavior:
- If TRIGGER_SOURCE is "cuemon" and TRIGGER_VERSION is "10.3.0":
- Cuemon.Core: 10.2.1 → 10.3.0
- Cuemon.Extensions.IO: 10.2.1 → 10.3.0
- Cuemon.Core: 10.2.1 → 10.3.0 (triggered source, set to given version)
- Cuemon.Extensions.IO: 10.2.1 → 10.3.0 (triggered source, set to given version)
- Codebelt.Extensions.BenchmarkDotNet.*: 1.2.3 → <latest from NuGet> (other Codebelt)
- Microsoft.Extensions.Hosting: 9.0.13 → UNCHANGED (not a Codebelt package)
- BenchmarkDotNet: 0.15.8 → UNCHANGED (not a Codebelt package)
"""

import json
import re
import os
import sys
from typing import Dict, List
import urllib.request
from typing import Dict, List, Optional

TRIGGER_SOURCE = os.environ.get("TRIGGER_SOURCE", "")
TRIGGER_VERSION = os.environ.get("TRIGGER_VERSION", "")
Expand All @@ -31,21 +35,24 @@
"xunit": ["Codebelt.Extensions.Xunit"],
"benchmarkdotnet": ["Codebelt.Extensions.BenchmarkDotNet"],
"bootstrapper": ["Codebelt.Bootstrapper"],
"carter": ["Codebelt.Extensions.Carter"],
"newtonsoft-json": [
"Codebelt.Extensions.Newtonsoft.Json",
"Codebelt.Extensions.AspNetCore.Newtonsoft.Json",
"Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft",
],
"aws-signature-v4": ["Codebelt.Extensions.AspNetCore.Authentication.AwsSignature"],
"unitify": ["Codebelt.Unitify"],
"yamldotnet": [
"Codebelt.Extensions.YamlDotNet",
"Codebelt.Extensions.AspNetCore.Text.Yaml",
"Codebelt.Extensions.AspNetCore.Mvc.Formatters.Text.Yaml",
],
"globalization": ["Codebelt.Extensions.Globalization"],
"asp-versioning": ["Codebelt.Extensions.Asp.Versioning"],
"swashbuckle-aspnetcore": ["Codebelt.Extensions.Swashbuckle"],
"savvyio": ["Savvyio."],
"shared-kernel": [],
"shared-kernel": ["Codebelt.SharedKernel"],
}


Expand All @@ -57,6 +64,38 @@ def is_triggered_package(package_name: str) -> bool:
return any(package_name.startswith(prefix) for prefix in prefixes)


def is_codebelt_package(package_name: str) -> bool:
"""Check if package belongs to any Codebelt repo (regardless of trigger source)."""
for repo_prefixes in SOURCE_PACKAGE_MAP.values():
if any(package_name.startswith(prefix) for prefix in repo_prefixes if prefix):
return True
return False


_nuget_version_cache: Dict[str, Optional[str]] = {}


def get_latest_nuget_version(package_name: str) -> Optional[str]:
"""Fetch the latest stable version of a package from NuGet."""
if package_name in _nuget_version_cache:
return _nuget_version_cache[package_name]

url = f"https://api.nuget.org/v3-flatcontainer/{package_name.lower()}/index.json"
try:
with urllib.request.urlopen(url, timeout=15) as response:
data = json.loads(response.read())
versions = data.get("versions", [])
# Stable versions have no hyphen (no pre-release suffix)
stable = [v for v in versions if "-" not in v]
result = stable[-1] if stable else (versions[-1] if versions else None)
except Exception as exc:
print(f" Warning: Could not fetch latest version for {package_name}: {exc}")
result = None

_nuget_version_cache[package_name] = result
return result


def main():
if not TRIGGER_SOURCE or not TRIGGER_VERSION:
print(
Expand All @@ -70,7 +109,7 @@ def main():
target_version = TRIGGER_VERSION.lstrip("v")

print(f"Trigger: {TRIGGER_SOURCE} @ {target_version}")
print(f"Only updating packages from: {TRIGGER_SOURCE}")
print(f"Triggered packages set to {target_version}; other Codebelt packages fetched from NuGet.")
print()

try:
Expand All @@ -87,16 +126,24 @@ def replace_version(m: re.Match) -> str:
pkg = m.group(1)
current = m.group(2)

if not is_triggered_package(pkg):
skipped_third_party.append(f" {pkg} (skipped - not from {TRIGGER_SOURCE})")
if is_triggered_package(pkg):
if target_version != current:
changes.append(f" {pkg}: {current} → {target_version}")
return m.group(0).replace(
f'Version="{current}"', f'Version="{target_version}"'
)
return m.group(0)

if target_version != current:
changes.append(f" {pkg}: {current} → {target_version}")
return m.group(0).replace(
f'Version="{current}"', f'Version="{target_version}"'
)
if is_codebelt_package(pkg):
latest = get_latest_nuget_version(pkg)
if latest and latest != current:
changes.append(f" {pkg}: {current} → {latest} (latest from NuGet)")
return m.group(0).replace(
f'Version="{current}"', f'Version="{latest}"'
)
return m.group(0)

skipped_third_party.append(f" {pkg} (skipped - not a Codebelt package)")
return m.group(0)

# Match PackageVersion elements (handles multiline)
Expand All @@ -111,10 +158,10 @@ def replace_version(m: re.Match) -> str:

# Show results
if changes:
print(f"Updated {len(changes)} package(s) from {TRIGGER_SOURCE}:")
print(f"Updated {len(changes)} package(s):")
print("\n".join(changes))
else:
print(f"No packages from {TRIGGER_SOURCE} needed updating.")
print("No Codebelt packages needed updating.")

if skipped_third_party:
print()
Expand Down
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<PackageVersion Include="Codebelt.Bootstrapper.Console" Version="5.0.3" />
<PackageVersion Include="Codebelt.Extensions.Xunit.App" Version="11.0.6" />
<PackageVersion Include="Cuemon.Core" Version="10.3.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageVersion Include="MinVer" Version="7.0.0" />
<PackageVersion Include="coverlet.collector" Version="8.0.0" />
<PackageVersion Include="coverlet.msbuild" Version="8.0.0" />
Expand Down
Loading