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
2 changes: 1 addition & 1 deletion .docfx/Dockerfile.docfx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ARG NGINX_VERSION=1.31.2-alpine
ARG NGINX_VERSION=1.31-alpine

FROM --platform=$BUILDPLATFORM nginx:${NGINX_VERSION} AS base
RUN rm -rf /usr/share/nginx/html/*
Expand Down
94 changes: 71 additions & 23 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,22 +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": [],
"carter": ["Codebelt.Extensions.Carter"],
"shared-kernel": ["Codebelt.SharedKernel"],
}


Expand All @@ -58,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 @@ -71,11 +109,13 @@ 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:
with open("Directory.Packages.props", "r") as f:
with open("Directory.Packages.props", "r", encoding="utf-8") as f:
content = f.read()
except FileNotFoundError:
print("Error: Directory.Packages.props not found")
Expand All @@ -88,16 +128,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 @@ -110,12 +158,15 @@ def replace_version(m: re.Match) -> str:
)
new_content = pattern.sub(replace_version, content)

with open("Directory.Packages.props", "w", encoding="utf-8", newline="\n") as f:
f.write(new_content)

# 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 All @@ -124,10 +175,7 @@ def replace_version(m: re.Match) -> str:
if len(skipped_third_party) > 5:
print(f" ... and {len(skipped_third_party) - 5} more")

with open("Directory.Packages.props", "w") as f:
f.write(new_content)

return 0 if changes else 0 # Return 0 even if no changes (not an error)
return 0


if __name__ == "__main__":
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
Version: 1.0.6
Availability: .NET 10

# ALM
- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs)

Version: 1.0.5
Availability: .NET 10

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
Version: 1.0.6
Availability: .NET 10

# ALM
- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs)

Version: 1.0.5
Availability: .NET 10

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
Version: 1.0.6
Availability: .NET 10

# ALM
- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs)

Version: 1.0.5
Availability: .NET 10

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
Version: 1.0.6
Availability: .NET 10

# ALM
- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs)

Version: 1.0.5
Availability: .NET 10

Expand Down
6 changes: 6 additions & 0 deletions .nuget/Codebelt.Extensions.Carter/PackageReleaseNotes.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
Version: 1.0.6
Availability: .NET 10

# ALM
- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs)

Version: 1.0.5
Availability: .NET 10

Expand Down
24 changes: 23 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,28 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

For more details, please refer to `PackageReleaseNotes.txt` on a per assembly basis in the `.nuget` folder.

## [1.0.6] - 2026-07-24

This is a patch release that updates Cuemon and Codebelt shared dependencies to their latest stable versions, improves build configuration, enhances CI/CD automation, and modernizes code formatting.

### Changed

- Codebelt.Extensions.AspNetCore.Newtonsoft.Json upgraded to 10.1.6,
- Codebelt.Extensions.AspNetCore.Text.Yaml upgraded to 10.1.6,
- Codebelt.Extensions.Xunit.App upgraded to 11.1.2,
- Cuemon.Core upgraded to 10.5.5,
- Cuemon.Extensions.AspNetCore.Text.Json upgraded to 10.5.5,
- Cuemon.Extensions.AspNetCore.Xml upgraded to 10.5.5,
- Cuemon.Extensions.IO upgraded to 10.5.5,
- Microsoft.NET.Test.Sdk upgraded to 18.8.1,
- Enhanced the NuGet package bumping script with intelligent version management, NuGet API lookups with caching, and improved output messaging,
- Refreshed the DocFX site container's NGINX base image to 1.31-alpine to allow more flexibility in the specific NGINX release,
- Modernized XmlResponseNegotiator code formatting by adopting file-scoped namespace syntax.

### Fixed

- Corrected the AnalysisMode property name in Directory.Build.props to use the correct MSBuild property.

## [1.0.5] - 2026-07-01

This is a patch service update that refreshes shared package baselines, hardens the DocFX publishing pipeline with new per-type usage examples, and tightens CI deployment gating so skipped optional jobs no longer suppress package publishing.
Expand Down Expand Up @@ -69,7 +91,7 @@ This is the initial stable release of the `Codebelt.Extensions.Carter`, `Codebel
- `YamlResponseNegotiator` class in the Codebelt.Extensions.Carter.AspNetCore.Text.Yaml namespace that provides a YAML response negotiator for Carter, capable of serializing response models to YAML format using `YamlDotNet`,
- `XmlResponseNegotiator` class in the Codebelt.Extensions.Carter.AspNetCore.Xml namespace that provides an XML response negotiator for Carter, capable of serializing response models to XML format using `System.Xml.XmlWriter`.

[Unreleased]: https://github.com/codebeltnet/carter/compare/v1.0.5...HEAD
[1.0.6]: https://github.com/codebeltnet/carter/compare/v1.0.5...v1.0.6
[1.0.5]: https://github.com/codebeltnet/carter/compare/v1.0.4...v1.0.5
[1.0.4]: https://github.com/codebeltnet/carter/compare/v1.0.3...v1.0.4
[1.0.3]: https://github.com/codebeltnet/carter/compare/v1.0.2...v1.0.3
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
<RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
<RunAnalyzersDuringLiveAnalysis>false</RunAnalyzersDuringLiveAnalysis>
<SonarQubeExclude>true</SonarQubeExclude>
<AnalysisLevel>none</AnalysisLevel>
<AnalysisMode>none</AnalysisMode>
<NoWarn>NU1701,NU1902,NU1903</NoWarn>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<NuGetAudit>false</NuGetAudit>
Expand Down
16 changes: 8 additions & 8 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8" />
<PackageVersion Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.15.8" />
<PackageVersion Include="Carter" Version="10.0.0" />
<PackageVersion Include="Codebelt.Extensions.AspNetCore.Newtonsoft.Json" Version="10.1.5" />
<PackageVersion Include="Codebelt.Extensions.AspNetCore.Text.Yaml" Version="10.1.5" />
<PackageVersion Include="Codebelt.Extensions.Xunit.App" Version="11.1.1" />
<PackageVersion Include="Cuemon.Core" Version="10.5.4" />
<PackageVersion Include="Cuemon.Extensions.AspNetCore.Text.Json" Version="10.5.4" />
<PackageVersion Include="Cuemon.Extensions.AspNetCore.Xml" Version="10.5.4" />
<PackageVersion Include="Cuemon.Extensions.IO" Version="10.5.4" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.7.0" />
<PackageVersion Include="Codebelt.Extensions.AspNetCore.Newtonsoft.Json" Version="10.1.6" />
<PackageVersion Include="Codebelt.Extensions.AspNetCore.Text.Yaml" Version="10.1.6" />
<PackageVersion Include="Codebelt.Extensions.Xunit.App" Version="11.1.2" />
<PackageVersion Include="Cuemon.Core" Version="10.5.5" />
<PackageVersion Include="Cuemon.Extensions.AspNetCore.Text.Json" Version="10.5.5" />
<PackageVersion Include="Cuemon.Extensions.AspNetCore.Xml" Version="10.5.5" />
<PackageVersion Include="Cuemon.Extensions.IO" Version="10.5.5" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
<PackageVersion Include="MinVer" Version="7.0.0" />
<PackageVersion Include="coverlet.collector" Version="10.0.1" />
<PackageVersion Include="coverlet.msbuild" Version="10.0.1" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,34 @@
using System.Text;
using Cuemon.Runtime.Serialization.Formatters;

namespace Codebelt.Extensions.Carter.AspNetCore.Xml
namespace Codebelt.Extensions.Carter.AspNetCore.Xml;

/// <summary>
/// Provides an XML response negotiator for Carter, capable of serializing response models to XML format.
/// </summary>
/// <seealso cref="ConfigurableResponseNegotiator{TOptions}"/>
public class XmlResponseNegotiator : ConfigurableResponseNegotiator<XmlFormatterOptions>
{
/// <summary>
/// Provides an XML response negotiator for Carter, capable of serializing response models to XML format.
/// Initializes a new instance of the <see cref="XmlResponseNegotiator"/> class.
/// </summary>
/// <seealso cref="ConfigurableResponseNegotiator{TOptions}"/>
public class XmlResponseNegotiator : ConfigurableResponseNegotiator<XmlFormatterOptions>
/// <param name="options">The <see cref="XmlFormatterOptions"/> used to configure XML serialization and supported media types.</param>
public XmlResponseNegotiator(IOptions<XmlFormatterOptions> options) : base(options.Value)
{
/// <summary>
/// Initializes a new instance of the <see cref="XmlResponseNegotiator"/> class.
/// </summary>
/// <param name="options">The <see cref="XmlFormatterOptions"/> used to configure XML serialization and supported media types.</param>
public XmlResponseNegotiator(IOptions<XmlFormatterOptions> options) : base(options.Value)
{
}
}

/// <summary>
/// Returns the character encoding specified by the <see cref="XmlFormatterOptions"/> writer settings.
/// </summary>
/// <returns>The default <see cref="Encoding"/> for this negotiator.</returns>
protected override Encoding GetDefaultEncoding() => Options.Settings.Writer.Encoding;
/// <summary>
/// Returns the character encoding specified by the <see cref="XmlFormatterOptions"/> writer settings.
/// </summary>
/// <returns>The default <see cref="Encoding"/> for this negotiator.</returns>
protected override Encoding GetDefaultEncoding() => Options.Settings.Writer.Encoding;

/// <summary>
/// Returns a new <see cref="XmlFormatter"/> configured with the current <see cref="XmlFormatterOptions"/>.
/// </summary>
/// <returns>A <see cref="XmlFormatter"/> instance configured with the current <see cref="XmlFormatterOptions"/>.</returns>
public override StreamFormatter<XmlFormatterOptions> GetFormatter()
{
return new XmlFormatter(Options);
}
/// <summary>
/// Returns a new <see cref="XmlFormatter"/> configured with the current <see cref="XmlFormatterOptions"/>.
/// </summary>
/// <returns>A <see cref="XmlFormatter"/> instance configured with the current <see cref="XmlFormatterOptions"/>.</returns>
public override StreamFormatter<XmlFormatterOptions> GetFormatter()
{
return new XmlFormatter(Options);
}
}
Loading