From 8ae63ab5eb6b9deb708db7c7ac191e8d88879cb0 Mon Sep 17 00:00:00 2001 From: Tim Hess Date: Fri, 15 Aug 2025 13:00:00 -0500 Subject: [PATCH 1/6] update diff styles (no borders, show + in green, and - in red) --- src/Steeltoe.io/wwwroot/css/shared.css | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/Steeltoe.io/wwwroot/css/shared.css b/src/Steeltoe.io/wwwroot/css/shared.css index 991ae390..ec6d0fb7 100644 --- a/src/Steeltoe.io/wwwroot/css/shared.css +++ b/src/Steeltoe.io/wwwroot/css/shared.css @@ -83,6 +83,10 @@ footer a:hover { color: steelblue; } +.hljs-deletion { + color: red; +} + :not(a):not(pre) > code, body[data-yaml-mime=ManagedReference] article dl.parameters > dt > code, body[data-yaml-mime=ApiPage] article dl.parameters > dt > code { @@ -97,6 +101,14 @@ body[data-yaml-mime=ApiPage] article dl.parameters > dt > code { color: #006881 } + .hljs-deletion, .hljs-addition { + display: inline-block; + } + + .hljs-addition { + color: green; + } + :not(a):not(pre) > code { color: var(--bs-primary-text-emphasis); background-color: #e6e6e6; @@ -188,6 +200,15 @@ body[data-yaml-mime=ApiPage] article dl.parameters > dt > code { .hljs-built_in { color: #569cd6; } + + .hljs-deletion { + background-color: transparent; + } + + .hljs-addition { + background-color: transparent; + color: lightgreen; + } } } From d9aba6c72fd8fea114f7ea386879efb9a6065bc8 Mon Sep 17 00:00:00 2001 From: Bart Koelman <104792814+bart-vmware@users.noreply.github.com> Date: Thu, 14 Aug 2025 17:18:59 +0200 Subject: [PATCH 2/6] migration steps, excluding JWT/OAuth/OpenID Connect/Certificates --- docs/docs/v4/welcome/migrate-quick-steps.md | 1194 +++++++++++++++++++ docs/docs/v4/welcome/toc.yml | 2 + docs/docs/v4/welcome/whats-new.md | 8 +- src/Steeltoe.io/Program.cs | 2 +- 4 files changed, 1202 insertions(+), 4 deletions(-) create mode 100644 docs/docs/v4/welcome/migrate-quick-steps.md diff --git a/docs/docs/v4/welcome/migrate-quick-steps.md b/docs/docs/v4/welcome/migrate-quick-steps.md new file mode 100644 index 00000000..c7144354 --- /dev/null +++ b/docs/docs/v4/welcome/migrate-quick-steps.md @@ -0,0 +1,1194 @@ +# Migrating from Steeltoe 3 + +This topic provides quick steps to migrate existing applications to Steeltoe 4. +For non-trivial cases, see the related documentation topic and samples for v4. + +> [!TIP] +> For detailed information on what has changed, see [What's new in Steeltoe 4](./whats-new.md). + +## Bootstrap + +For additional information, see the updated [Bootstrap documentation](../bootstrap/index.md). + +Project file: + +```diff + + +- ++ + + +``` + +Program.cs: + +```diff +-using Steeltoe.Bootstrap.Autoconfig; ++using Steeltoe.Bootstrap.AutoConfiguration; + +var builder = WebApplication.CreateBuilder(args); +builder.AddSteeltoe(); +``` + +## CircuitBreaker + +CircuitBreaker (a .NET port of Netflix Hystrix) has been removed from Steeltoe in v4. +Use [Polly](https://github.com/App-vNext/Polly) instead. + +## Configuration + +For additional information, see the updated [Configuration documentation](../configuration/index.md) and +[Configuration samples](https://github.com/SteeltoeOSS/Samples/tree/main/Configuration). + +### Cloud Foundry + +Project file: + +```diff + + +- ++ + + +``` + +#### Load `VCAP_SERVICES`/`VCAP_APPLICATION` into `IConfiguration` + +Program.cs: + +```diff +-using Steeltoe.Extensions.Configuration.CloudFoundry; ++using Steeltoe.Configuration.CloudFoundry; + +var builder = WebApplication.CreateBuilder(args); +builder.AddCloudFoundryConfiguration(); + +Console.WriteLine($"Application name: {builder.Configuration["vcap:application:application_name"]}"); + +foreach (var section in builder.Configuration.GetRequiredSection("vcap:services").GetChildren()) +{ + var plans = string.Join(", ", section + .GetChildren() + .SelectMany(child => child.GetChildren()) + .Where(child => child.Key == "plan") + .Select(child => child.Value)); + Console.WriteLine($"Service: {section.Key} with plans: {plans}"); +} +``` + +#### Load `VCAP_SERVICES`/`VCAP_APPLICATION` into `OptionsMonitor` + +Program.cs: + +```diff +using Microsoft.Extensions.Options; +-using Steeltoe.Extensions.Configuration.CloudFoundry; ++using Steeltoe.Configuration.CloudFoundry; + +var builder = WebApplication.CreateBuilder(args); +builder.AddCloudFoundryConfiguration(); +-builder.Services.ConfigureCloudFoundryOptions(builder.Configuration); + +var app = builder.Build(); + +var appMonitor = app.Services.GetRequiredService>(); +Console.WriteLine($"Application name: {appMonitor.CurrentValue.ApplicationName}"); + +var servicesMonitor = app.Services.GetRequiredService>(); +foreach (var services in servicesMonitor.CurrentValue.Services) +{ + var plans = string.Join(", ", services.Value.Select(service => service.Plan)); + Console.WriteLine($"Service: {services.Key} with plans: {plans}"); +} +``` + +### Config Server + +Project file: + +```diff + + +- ++ + + +``` + +Program.cs: + +```diff +-using Steeltoe.Extensions.Configuration.ConfigServer; ++using Steeltoe.Configuration.ConfigServer; + +var builder = WebApplication.CreateBuilder(args); +builder.AddConfigServer(); +``` + +### Kubernetes + +Direct interaction with the Kubernetes API has been removed from Steeltoe in v4. + +### Placeholder + +Project file: + +```diff + + +- ++ + + +``` + +Program.cs: + +```diff +-using Steeltoe.Extensions.Configuration.Placeholder; ++using Steeltoe.Configuration.Placeholder; + +var builder = WebApplication.CreateBuilder(args); +-builder.AddPlaceholderResolver(); ++builder.Configuration.AddPlaceholderResolver(); +``` + +### Random Value + +Project file: + +```diff + + +- ++ + + +``` + +Program.cs: + +```diff +-using Steeltoe.Extensions.Configuration.RandomValue; ++using Steeltoe.Configuration.RandomValue; + +var builder = WebApplication.CreateBuilder(args); +builder.Configuration.AddRandomValueSource(); +``` + +### Spring Boot + +Project file: + +```diff + + +- ++ + + +``` + +Program.cs: + +```diff +-using Steeltoe.Extensions.Configuration.SpringBoot; ++using Steeltoe.Configuration.SpringBoot; + +var builder = WebApplication.CreateBuilder(args); +-builder.AddSpringBootConfiguration(); ++builder.Configuration.AddSpringBootFromCommandLine(args); ++builder.Configuration.AddSpringBootFromEnvironmentVariable(); +``` + +## Connectors + +For additional information, see the updated [Connectors documentation](../configuration/index.md) and +[Configuration samples](https://github.com/SteeltoeOSS/Samples/tree/main/Connectors). + +### MySQL using ADO.NET + +Project file: + +```diff + + + +- ++ + + +``` + +appsettings.json: + +```diff +{ +- "$schema": "https://steeltoe.io/schema/v3/schema.json", ++ "$schema": "https://steeltoe.io/schema/v4/schema.json", +- "MySql:Client:ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" ++ "Steeltoe:Client:MySql:Default:ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" +} +``` + +Program.cs: + +```diff +using MySql.Data.MySqlClient; +-using Steeltoe.Connector.MySql; ++using Steeltoe.Connectors; ++using Steeltoe.Connectors.MySql; + +var builder = WebApplication.CreateBuilder(args); +-builder.Services.AddMySqlConnection(builder.Configuration); ++builder.AddMySql(); + +var app = builder.Build(); + +-await using var scope = app.Services.CreateAsyncScope(); +-await using var connection = scope.ServiceProvider.GetRequiredService(); ++var factory = app.Services.GetRequiredService>(); ++var connector = factory.Get(); ++Console.WriteLine($"Using connection string: {connector.Options.ConnectionString}"); ++await using var connection = connector.GetConnection(); + +await connection.OpenAsync(); +await using var command = connection.CreateCommand(); +command.CommandText = "SELECT 1"; +var result = await command.ExecuteScalarAsync(); +Console.WriteLine($"Query returned: {result}"); +``` + +### MySQL using Entity Framework Core + +Project file: + +```diff + + + +- +- ++ + + +``` + +appsettings.json: + +```diff +{ +- "$schema": "https://steeltoe.io/schema/v3/schema.json", ++ "$schema": "https://steeltoe.io/schema/v4/schema.json", +- "MySql:Client:ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" ++ "Steeltoe:Client:MySql:Default:ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" +} +``` + +Program.cs: + +```diff +using Microsoft.EntityFrameworkCore; +-using Steeltoe.Connector.MySql; ++using Steeltoe.Connectors.MySql; +-using Steeltoe.Connector.MySql.EFCore; ++using Steeltoe.Connectors.EntityFrameworkCore.MySql; + +var builder = WebApplication.CreateBuilder(args); +-builder.Services.AddDbContext(options => options.UseMySql(builder.Configuration)); +-builder.Services.AddMySqlHealthContributor(builder.Configuration); ++builder.AddMySql(); ++builder.Services.AddDbContext((serviceProvider, options) => options.UseMySql(serviceProvider)); + +var app = builder.Build(); + +await using var scope = app.Services.CreateAsyncScope(); +await using var dbContext = scope.ServiceProvider.GetRequiredService(); +var rowCount = await dbContext.ExampleEntities.CountAsync(); +Console.WriteLine($"Found {rowCount} rows."); +``` + +### PostgreSQL using ADO.NET + +Project file: + +```diff + + + +- ++ + + +``` + +appsettings.json: + +```diff +{ +- "$schema": "https://steeltoe.io/schema/v3/schema.json", ++ "$schema": "https://steeltoe.io/schema/v4/schema.json", +- "Postgres:Client:ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" ++ "Steeltoe:Client:PostgreSQL:Default:ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" +} +``` + +Program.cs: + +```diff +using Npgsql; +-using Steeltoe.Connector.PostgreSql; ++using Steeltoe.Connectors; ++using Steeltoe.Connectors.PostgreSql; + +var builder = WebApplication.CreateBuilder(args); +-builder.Services.AddPostgresConnection(builder.Configuration); ++builder.AddPostgreSql(); + +var app = builder.Build(); + +-await using var scope = app.Services.CreateAsyncScope(); +-await using var connection = scope.ServiceProvider.GetRequiredService(); ++var factory = app.Services.GetRequiredService>(); ++var connector = factory.Get(); ++Console.WriteLine($"Using connection string: {connector.Options.ConnectionString}"); ++await using var connection = connector.GetConnection(); + +await connection.OpenAsync(); +await using var command = connection.CreateCommand(); +command.CommandText = "SELECT 1"; +var result = await command.ExecuteScalarAsync(); +Console.WriteLine($"Query returned: {result}"); +``` + +### PostgreSQL using Entity Framework Core + +Project file: + +```diff + + + +- +- ++ + + +``` + +appsettings.json: + +```diff +{ +- "$schema": "https://steeltoe.io/schema/v3/schema.json", ++ "$schema": "https://steeltoe.io/schema/v4/schema.json", +- "Postgres:Client:ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" ++ "Steeltoe:Client:PostgreSQL:Default:ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" +} +``` + +Program.cs: + +```diff +using Microsoft.EntityFrameworkCore; +-using Steeltoe.Connector.PostgreSql; ++using Steeltoe.Connectors.PostgreSql; +-using Steeltoe.Connector.PostgreSql.EFCore; ++using Steeltoe.Connectors.EntityFrameworkCore.PostgreSql; + +var builder = WebApplication.CreateBuilder(args); +-builder.Services.AddDbContext(options => options.UseNpgsql(builder.Configuration)); +-builder.Services.AddPostgresHealthContributor(builder.Configuration); ++builder.AddPostgreSql(); ++builder.Services.AddDbContext((serviceProvider, options) => options.UseNpgsql(serviceProvider)); + +var app = builder.Build(); + +await using var scope = app.Services.CreateAsyncScope(); +await using var dbContext = scope.ServiceProvider.GetRequiredService(); +var rowCount = await dbContext.ExampleEntities.CountAsync(); +Console.WriteLine($"Found {rowCount} rows."); +``` + +### RabbitMQ + +Project file: + +```diff + + + +- ++ + + +``` + +appsettings.json: + +```diff +{ +- "$schema": "https://steeltoe.io/schema/v3/schema.json", ++ "$schema": "https://steeltoe.io/schema/v4/schema.json", +- "RabbitMQ:Client:ConnectionString": "Server=localhost" ++ "Steeltoe:Client:RabbitMQ:Default:ConnectionString": "amqp://localhost:5672" +} +``` + +> [!TIP] +> See the RabbitMQ documentation [here](https://www.rabbitmq.com/docs/uri-spec) and [here](https://www.rabbitmq.com/docs/uri-query-parameters) for the `ConnectionString` URI format. + +Program.cs: + +```diff +using RabbitMQ.Client; +-using Steeltoe.Connector.RabbitMQ; ++using Steeltoe.Connectors; ++using Steeltoe.Connectors.RabbitMQ; +using System.Text; + +var builder = WebApplication.CreateBuilder(args); +-builder.Services.AddRabbitMQConnection(builder.Configuration, ServiceLifetime.Singleton); ++builder.AddRabbitMQ(); + +var app = builder.Build(); + +-var connectionFactory = app.Services.GetRequiredService(); +-var connection = await connectionFactory.CreateConnectionAsync(); // long-lived, do not dispose ++var factory = app.Services.GetRequiredService>(); ++var connector = factory.Get(); ++Console.WriteLine($"Using connection string: {connector.Options.ConnectionString}"); ++var connection = connector.GetConnection(); // long-lived, do not dispose +await using var channel = await connection.CreateChannelAsync(); +const string queueName = "example-queue-name"; +await channel.QueueDeclareAsync(queueName); + +byte[] messageToSend = "example-message"u8.ToArray(); +await channel.BasicPublishAsync(exchange: "", queueName, mandatory: true, new BasicProperties(), messageToSend); + +var result = await channel.BasicGetAsync(queueName, autoAck: true); +string messageReceived = result == null ? "(none)" : Encoding.UTF8.GetString(result.Body.ToArray()); +Console.WriteLine($"Received message: {messageReceived}"); +``` + +### Redis/Valkey + +Project file: + +```diff + + + +- ++ + + +``` + +appsettings.json: + +```diff +{ +- "$schema": "https://steeltoe.io/schema/v3/schema.json", ++ "$schema": "https://steeltoe.io/schema/v4/schema.json", +- "Redis:Client:ConnectionString": "localhost:6379" ++ "Steeltoe:Client:Redis:Default:ConnectionString": "localhost" +} +``` + +Program.cs: + +```diff +using Microsoft.Extensions.Caching.Distributed; +-using Steeltoe.Connector.Redis; ++using Steeltoe.Connectors; ++using Steeltoe.Connectors.Redis; + +var builder = WebApplication.CreateBuilder(args); +-builder.Services.AddDistributedRedisCache(builder.Configuration); ++builder.AddRedis(); + +var app = builder.Build(); + +-var cache = app.Services.GetRequiredService(); ++var factory = app.Services.GetRequiredService>(); ++var connector = factory.Get(); ++Console.WriteLine($"Using connection string: {connector.Options.ConnectionString}"); ++var cache = connector.GetConnection(); +await cache.SetAsync("example-key", "example-value"u8.ToArray()); +var value = await cache.GetStringAsync("example-key"); +Console.WriteLine($"Received value: {value}"); +``` + +## Discovery + +For additional information, see the updated [Discovery documentation](../discovery/index.md) and +[Discovery samples](https://github.com/SteeltoeOSS/Samples/tree/main/Discovery). + +### Eureka + +#### Register your service + +Project file: + +```diff + + +- ++ + + +``` + +appsettings.json: + +```diff +{ +- "$schema": "https://steeltoe.io/schema/v3/schema.json", ++ "$schema": "https://steeltoe.io/schema/v4/schema.json", + "Spring:Application:Name": "example-service", + "Eureka:Client:ShouldRegisterWithEureka": true, + "Eureka:Client:ShouldFetchRegistry": false +} +``` + +launchSettings.json: + +``` +{ + "profiles": { + "http": { + "commandName": "Project", + "applicationUrl": "http://+:5005" // bind to all host names and IP addressess + } + } +} +``` + +Program.cs: + +```diff +-using Steeltoe.Discovery.Client; ++using Steeltoe.Discovery.Eureka; + +var builder = WebApplication.CreateBuilder(args); +-builder.Services.AddDiscoveryClient(); ++builder.Services.AddEurekaDiscoveryClient(); + +var app = builder.Build(); + +app.MapGet("/ping", async httpContext => +{ + httpContext.Response.StatusCode = 200; + httpContext.Response.ContentType = "text/plain"; + await httpContext.Response.WriteAsync("pong"); +}); +``` + +#### Lookup other services + +Project file: + +```diff + + +- ++ ++ + + +``` + +appsettings.json: + +```diff +{ +- "$schema": "https://steeltoe.io/schema/v3/schema.json", ++ "$schema": "https://steeltoe.io/schema/v4/schema.json", + "Eureka:Client:ShouldRegisterWithEureka": false, + "Eureka:Client:ShouldFetchRegistry": true +} +``` + +Program.cs: + +```diff +-using Steeltoe.Common.Http.Discovery; +-using Steeltoe.Discovery.Client; ++using Steeltoe.Discovery.Eureka; ++using Steeltoe.Discovery.HttpClients; + +var builder = WebApplication.CreateBuilder(args); +-builder.Services.AddDiscoveryClient(); ++builder.Services.AddEurekaDiscoveryClient(); +builder.Services + .AddHttpClient(httpClient => httpClient.BaseAddress = new Uri("http://example-service/")) + .AddServiceDiscovery(); + +var app = builder.Build(); + +var pingClient = app.Services.GetRequiredService(); +string response = await pingClient.GetPingAsync(); +Console.WriteLine($"Response: {response}"); + +public class PingClient(HttpClient httpClient) +{ + public async Task GetPingAsync() + { + return await httpClient.GetStringAsync("ping"); + } +} +``` + +### Consul + +#### Register your service + +Project file: + +```diff + + +- ++ + + +``` + +appsettings.json: + +```diff +{ +- "$schema": "https://steeltoe.io/schema/v3/schema.json", ++ "$schema": "https://steeltoe.io/schema/v4/schema.json", + "Spring:Application:Name": "example-service", + "Consul:Discovery:Register": true +} +``` + +launchSettings.json: + +``` +{ + "profiles": { + "http": { + "commandName": "Project", + "applicationUrl": "http://+:5005" // bind to all host names and IP addressess + } + } +} +``` + +Program.cs: + +```diff +-using Steeltoe.Discovery.Client; ++using Steeltoe.Discovery.Consul; + +var builder = WebApplication.CreateBuilder(args); +-builder.Services.AddDiscoveryClient(); +builder.Services.AddConsulDiscoveryClient(); + +var app = builder.Build(); + +app.MapGet("/ping", async httpContext => +{ + httpContext.Response.StatusCode = 200; + httpContext.Response.ContentType = "text/plain"; + await httpContext.Response.WriteAsync("pong"); +}); +``` + +#### Lookup other services + +Project file: + +```diff + + +- ++ ++ + + +``` + +appsettings.json: + +```diff +{ +- "$schema": "https://steeltoe.io/schema/v3/schema.json", ++ "$schema": "https://steeltoe.io/schema/v4/schema.json", + "Consul:Discovery:Register": false +} +``` + +Program.cs: + +```diff +-using Steeltoe.Common.Http.Discovery; +-using Steeltoe.Discovery.Client; ++using Steeltoe.Discovery.Consul; ++using Steeltoe.Discovery.HttpClients; + +var builder = WebApplication.CreateBuilder(args); +-builder.Services.AddDiscoveryClient(); ++builder.Services.AddConsulDiscoveryClient(); +builder.Services + .AddHttpClient(httpClient => httpClient.BaseAddress = new Uri("http://example-service/")) + .AddServiceDiscovery(); + +var app = builder.Build(); + +var pingClient = app.Services.GetRequiredService(); +string response = await pingClient.GetPingAsync(); +Console.WriteLine($"Response: {response}"); + +public class PingClient(HttpClient httpClient) +{ + public async Task GetPingAsync() + { + return await httpClient.GetStringAsync("ping"); + } +} +``` + +## Integration + +Integration (lightweight messaging for Spring-based applications) has been removed from Steeltoe in v4. + +## Logging + +For additional information, see the updated [Logging documentation](../logging/index.md). + +### Dynamic Console + +Project file: + +```diff + + +- ++ + + +``` + +Program.cs: + +```diff +-using Steeltoe.Extensions.Logging; ++using Steeltoe.Logging; ++using Steeltoe.Logging.DynamicConsole; + +var builder = WebApplication.CreateBuilder(args); +builder.Logging.SetMinimumLevel(LogLevel.Debug); +builder.Configuration["Logging:LogLevel:Default"] = "Warning"; +builder.Logging.AddDynamicConsole(); + +var app = builder.Build(); + +var loggerFactory = app.Services.GetRequiredService(); +var exampleLogger = loggerFactory.CreateLogger("Example.Sub.Namespace"); + +exampleLogger.LogDebug("Example debug message (1) - hidden"); + +var dynamicLoggerProvider = app.Services.GetRequiredService(); +dynamicLoggerProvider.SetLogLevel("Example", LogLevel.Debug); + +exampleLogger.LogDebug("Example debug message (2)"); + +await Task.Delay(TimeSpan.FromMilliseconds(250)); // wait for logs to flush +``` + +## Management + +For additional information, see the updated [Management documentation](../management/index.md) and +[Management samples](https://github.com/SteeltoeOSS/Samples/tree/main/Management). + +### Endpoints + +Project file: + +```diff + + +- ++ + + +``` + +#### All actuators + +appsettings.json: + +```diff +{ +- "$schema": "https://steeltoe.io/schema/v3/schema.json", ++ "$schema": "https://steeltoe.io/schema/v4/schema.json", + "Management:Endpoints:Actuator:Exposure:Include": ["*"], ++ "Management:Endpoints:Health:ShowComponents": "Always", ++ "Management:Endpoints:Health:ShowDetails": "Always", ++ "Management:Endpoints:Health:Readiness:Enabled": true, ++ "Management:Endpoints:Health:Liveness:Enabled": true +} +``` + +Program.cs: + +```diff +-using Steeltoe.Management.Endpoint; ++using Steeltoe.Management.Endpoint.Actuators.All; + +var builder = WebApplication.CreateBuilder(args); +-builder.AddAllActuators(); ++builder.Services.AddAllActuators(); +``` + +#### Custom health contributor + +appsettings.json: + +```diff +{ +- "$schema": "https://steeltoe.io/schema/v3/schema.json", ++ "$schema": "https://steeltoe.io/schema/v4/schema.json", ++ "Management:Endpoints:Health:ShowComponents": "Always", ++ "Management:Endpoints:Health:ShowDetails": "Always", ++ "Management:Endpoints:Health:Readiness:Enabled": true, ++ "Management:Endpoints:Health:Liveness:Enabled": true +} +``` + +Program.cs: + +```diff +using Steeltoe.Common.HealthChecks; +-using Steeltoe.Management.Endpoint; ++using Steeltoe.Management.Endpoint.Actuators.Health; + +var builder = WebApplication.CreateBuilder(args); + +-builder.AddHealthActuator(); ++builder.Services.AddHealthActuator(); +-builder.Services.AddSingleton(); ++builder.Services.AddHealthContributor(); +-builder.Services.AddControllers(); + +var app = builder.Build(); +-app.MapControllers(); +app.Run(); + +public class WarningHealthContributor : IHealthContributor +{ + public string Id => "exampleContributor"; + +- public HealthCheckResult Health() ++ public async Task CheckHealthAsync(CancellationToken cancellationToken) + { ++ await Task.Yield(); + return new HealthCheckResult + { +- Status = HealthStatus.WARNING, ++ Status = HealthStatus.Warning, ++ Description = "Example health contributor reports warning.", + Details = + { +- ["status"] = HealthStatus.WARNING, +- ["description"] = "Example health contributor reports warning.", + ["currentTime"] = DateTime.UtcNow.ToString("O") + } + }; + } +} +``` + +#### Custom info contributor + +Program.cs: + +```diff +-using Steeltoe.Management.Endpoint; +-using Steeltoe.Management.Info; ++using Steeltoe.Management.Endpoint.Actuators.Info; + +var builder = WebApplication.CreateBuilder(args); +-builder.AddInfoActuator(); ++builder.Services.AddInfoActuator(); +-builder.Services.AddSingleton(); ++builder.Services.AddInfoContributor(); +-builder.Services.AddControllers(); + +var app = builder.Build(); +-app.MapControllers(); +app.Run(); + +public class ExampleInfoContributor : IInfoContributor +{ +- public void Contribute(IInfoBuilder builder) ++ public async Task ContributeAsync(InfoBuilder builder, CancellationToken cancellationToken) + { ++ await Task.Yield(); + builder.WithInfo(".NET version", Environment.Version); + } +} +``` + +#### Cloud hosting + +The `UseCloudHosting` extension method has been removed from Steeltoe in v4. Use one of the methods described at +[8 ways to set the URLs for an ASP.NET Core app](https://andrewlock.net/8-ways-to-set-the-urls-for-an-aspnetcore-app/) +to configure the port number(s) to listen on. + +Program.cs: + +```diff +-using Steeltoe.Common.Hosting; +-using Steeltoe.Management.Endpoint; +using Steeltoe.Management.Endpoint.Actuators.All; + +var builder = WebApplication.CreateBuilder(args); +-builder.UseCloudHosting(runLocalHttpPort: 8080, runLocalHttpsPort: 9090); ++builder.WebHost.UseUrls("http://+:8080", "https://+:9090"); +-builder.AddAllActuators(); +builder.Services.AddAllActuators(); +``` + +For deployment to Cloud Foundry, the `builder.WebHost.UseUrls` line should be omitted. + +- When using the dotnet_core_buildpack, the `PORT` environment variable is picked up automatically. +- When using the binary_buildpack, use the `PORT` environment variable in the `manifest.yml` file: + + ```yaml + --- + applications: + - name: example-app + stack: windows + buildpacks: + - binary_buildpack + command: cmd /c ./example-app --urls=http://0.0.0.0:%PORT% + ``` + +#### Spring Boot Admin + +appsettings.json: + +```diff +{ +- "$schema": "https://steeltoe.io/schema/v3/schema.json", ++ "$schema": "https://steeltoe.io/schema/v4/schema.json", + "Spring:Application:Name": "example-service", + "Management:Endpoints:Actuator:Exposure:Include": [ "*" ], + "Spring:Boot:Admin:Client:Url": "http://localhost:9099", +- "Spring:Boot:Admin:Client:BasePath": "http://host.docker.internal:5050" ++ "Spring:Boot:Admin:Client:BaseHost": "host.docker.internal" +} +``` + +Program.cs: + +```diff +-using Steeltoe.Management.Endpoint; ++using Steeltoe.Management.Endpoint.Actuators.All; ++using Steeltoe.Management.Endpoint.SpringBootAdminClient; + +var builder = WebApplication.CreateBuilder(args); +builder.WebHost.UseUrls("http://host.docker.internal:5050"); +-builder.AddAllActuators(); ++builder.Services.AddAllActuators(); +builder.Services.AddSpringBootAdminClient(); +``` + +### OpenTelemetry + +Using OpenTelemetry for collecting logs, metrics and distributed traces now works out of the box without requiring a Steeltoe NuGet package. +See the instructions [here](../tracing/index.md) to configure OpenTelemetry in your application. +See [here](../management/prometheus.md) to export metrics to Prometheus using Steeltoe v4. + +The sample [here](https://github.com/SteeltoeOSS/Samples/blob/main/Management/src/ActuatorWeb/README.md#viewing-metric-dashboards) demonstrates exporting to Prometheus and Grafana. + +### Kubernetes + +Direct interaction with the Kubernetes API has been removed from Steeltoe in v4. + +### Application tasks + +Project file: + +```diff + + +- ++ + + +``` + +After the following steps, run your app: + +```shell +dotnet run runtask=example-task +``` + +#### Using inline code + +Program.cs: + +```diff +-using Steeltoe.Management.TaskCore; ++using Steeltoe.Management.Tasks; + +var builder = WebApplication.CreateBuilder(args); +-builder.Services.AddTask("example-task", serviceProvider => ++builder.Services.AddTask("example-task", async (serviceProvider, cancellationToken) => +{ ++ await Task.Yield(); + var loggerFactory = serviceProvider.GetRequiredService(); + var logger = loggerFactory.CreateLogger("ExampleTaskLogger"); + logger.LogInformation("Example task executed."); +}); + +var app = builder.Build(); +-app.RunWithTasks(); ++await app.RunWithTasksAsync(CancellationToken.None); +``` + +#### Implementing `IApplicationTask` + +Program.cs: + +```diff +using Steeltoe.Common; +-using Steeltoe.Management.TaskCore; ++using Steeltoe.Management.Tasks; + +var builder = WebApplication.CreateBuilder(args); +-builder.Services.AddTask(); ++builder.Services.AddTask("example-task"); + +var app = builder.Build(); +-app.RunWithTasks(); ++await app.RunWithTasksAsync(CancellationToken.None); + +public class ExampleTask(ILogger logger) : IApplicationTask +{ +- public string Name => "example-task"; + +- public void Run() ++ public Task RunAsync(CancellationToken cancellationToken) + { + logger.LogInformation("Example task executed."); ++ return Task.CompletedTask; + } +} +``` + +## Messaging + +Template-based support for Spring messaging systems has been removed from Steeltoe in v4. + +## Stream + +Spring Cloud Stream support has been removed from Steeltoe in v4. + +## Security + +For additional information, see the updated [Security documentation](../security/index.md) and +[Discovery samples](https://github.com/SteeltoeOSS/Samples/tree/main/Security). + +### CredHub client + +The CredHub client has been removed from Steeltoe in v4. +Use [CredHub Service Broker](https://techdocs.broadcom.com/us/en/vmware-tanzu/platform-services/credhub-service-broker/services/credhub-sb/index.html) instead. + +### TODO: JWT/OAuth/OpenID Connect/Certificates... + +### DataProtection Key Store using Redis/Valkey + +```diff + + +- +- +- ++ + + +``` + +appsettings.json: + +```diff +{ +- "$schema": "https://steeltoe.io/schema/v3/schema.json", ++ "$schema": "https://steeltoe.io/schema/v4/schema.json", +- "Redis:Client:ConnectionString": "localhost:6379" ++ "Steeltoe:Client:Redis:Default:ConnectionString": "localhost" +} +``` + +Program.cs: + +```diff +using Microsoft.AspNetCore.DataProtection; +-using Steeltoe.Connector.Redis; ++using Steeltoe.Connectors.Redis; +-using Steeltoe.Security.DataProtection; ++using Steeltoe.Security.DataProtection.Redis; + +var builder = WebApplication.CreateBuilder(args); +-builder.Services.AddRedisConnectionMultiplexer(builder.Configuration); +-builder.Services.AddDistributedRedisCache(builder.Configuration); ++builder.AddRedis(); +builder.Services.AddDataProtection() + .PersistKeysToRedis() + .SetApplicationName("example-app"); +builder.Services.AddSession(); + +var app = builder.Build(); +app.UseSession(); + +app.MapPost("set-session", httpContext => +{ + httpContext.Session.SetString("example-key", $"example-value-{Guid.NewGuid()}"); + httpContext.Response.StatusCode = 204; + return Task.CompletedTask; +}); + +app.MapGet("get-session", async httpContext => +{ + var sessionValue = httpContext.Session.GetString("example-key"); + httpContext.Response.StatusCode = 200; + httpContext.Response.ContentType = "text/plain"; + await httpContext.Response.WriteAsync($"Session value: {sessionValue ?? "(none)"}"); +}); + +app.Run(); +``` + +# TODO: Remove temporary template below + +Project file: + +```diff + + +- ++ + + +``` + +Program.cs: + +```diff +-XXXXX ++XXXXX + +var builder = WebApplication.CreateBuilder(args); +-XXXXX ++XXXXX +``` diff --git a/docs/docs/v4/welcome/toc.yml b/docs/docs/v4/welcome/toc.yml index 54cb274e..a146cbc6 100644 --- a/docs/docs/v4/welcome/toc.yml +++ b/docs/docs/v4/welcome/toc.yml @@ -1,5 +1,7 @@ - href: whats-new.md name: What's new in Steeltoe 4 +- href: migrate-quick-steps.md + name: Migrating from Steeltoe 3 - href: prerequisites.md name: Prerequisites - href: common-steps.md diff --git a/docs/docs/v4/welcome/whats-new.md b/docs/docs/v4/welcome/whats-new.md index 23cb73f0..6517d576 100644 --- a/docs/docs/v4/welcome/whats-new.md +++ b/docs/docs/v4/welcome/whats-new.md @@ -620,8 +620,6 @@ For more information, see the updated [Configuration documentation](../configura For more information, see the updated [Connectors documentation](../configuration/index.md) and [Configuration samples](https://github.com/SteeltoeOSS/Samples/tree/main/Connectors). ---- - ## Discovery ### Behavior changes @@ -984,7 +982,11 @@ For more information, see the updated [Logging documentation](../logging/index.m - Actuators can be turned on/off or bound to different verbs at runtime using configuration - Simplified content negotiation; updated all actuators to support latest Spring media type - New actuator `/beans` that lists the contents of the .NET dependency container, including support for keyed services -- Update health checks and actuator to align with latest Spring; hide details by default; contributors can be turned on/off at runtime using configuration +- Update health checks and actuator to align with latest Spring + - Hide components/details by default + - Liveness/readiness contributors are turned off by default + - Contributors can be turned on/off at runtime using configuration + - Contributors must be singletons (you can inject `IHttpContextAccessor`, but `HttpContext` is unavailable from Discovery) - Support Windows network shares in disk space health contributor - Update `/mappings` actuator to include endpoints from Minimal APIs, Razor Pages, and Blazor, with richer metadata and improved compatibility with Spring - Heap dumps are enabled by default in Cloud Foundry on Linux; all dump types supported on Windows/Linux/macOS diff --git a/src/Steeltoe.io/Program.cs b/src/Steeltoe.io/Program.cs index f110845f..bed7d560 100644 --- a/src/Steeltoe.io/Program.cs +++ b/src/Steeltoe.io/Program.cs @@ -23,7 +23,7 @@ } var rewriteOptions = new RewriteOptions() - .AddRedirect("^docs/v3/obsolete", "docs/v4/welcome/whats-new.html", 301) + .AddRedirect("^docs/v3/obsolete", "docs/v4/welcome/migrate-quick-steps.html", 301) .AddRedirect("^circuit-breakers.*", "attic", 301) .AddRedirect("^steeltoe-circuitbreaker", "attic", 301) .AddRedirect("^event-driven", "attic", 301) From 10dc02f1fc2fe039b73947daf7b6911d214fc8bf Mon Sep 17 00:00:00 2001 From: Bart Koelman <104792814+bart-vmware@users.noreply.github.com> Date: Fri, 22 Aug 2025 12:49:28 +0200 Subject: [PATCH 3/6] Expand appsettings.json content, fix incorrect 3x settings for RabbitMQ, add callout, remove template --- docs/docs/v4/welcome/migrate-quick-steps.md | 255 +++++++++++++++----- 1 file changed, 195 insertions(+), 60 deletions(-) diff --git a/docs/docs/v4/welcome/migrate-quick-steps.md b/docs/docs/v4/welcome/migrate-quick-steps.md index c7144354..61279985 100644 --- a/docs/docs/v4/welcome/migrate-quick-steps.md +++ b/docs/docs/v4/welcome/migrate-quick-steps.md @@ -208,6 +208,10 @@ var builder = WebApplication.CreateBuilder(args); For additional information, see the updated [Connectors documentation](../configuration/index.md) and [Configuration samples](https://github.com/SteeltoeOSS/Samples/tree/main/Connectors). +> [!IMPORTANT] +> The configuration structure for Connectors has changed in Steeltoe 4. Always use the `ConnectionString` property instead of `Host`, `Port`, `Username`, `Password`, etc. +> Replace the key `Default` with the name of the service binding if you have multiple. + ### MySQL using ADO.NET Project file: @@ -228,8 +232,20 @@ appsettings.json: { - "$schema": "https://steeltoe.io/schema/v3/schema.json", + "$schema": "https://steeltoe.io/schema/v4/schema.json", -- "MySql:Client:ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" -+ "Steeltoe:Client:MySql:Default:ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" +- "MySql": { +- "Client": { +- "ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" +- } +- } ++ "Steeltoe": { ++ "Client": { ++ "MySql": { ++ "Default": { ++ "ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" ++ } ++ } ++ } ++ } } ``` @@ -282,8 +298,20 @@ appsettings.json: { - "$schema": "https://steeltoe.io/schema/v3/schema.json", + "$schema": "https://steeltoe.io/schema/v4/schema.json", -- "MySql:Client:ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" -+ "Steeltoe:Client:MySql:Default:ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" +- "MySql": { +- "Client": { +- "ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" +- } +- } ++ "Steeltoe": { ++ "Client": { ++ "MySql": { ++ "Default": { ++ "ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" ++ } ++ } ++ } ++ } } ``` @@ -330,8 +358,20 @@ appsettings.json: { - "$schema": "https://steeltoe.io/schema/v3/schema.json", + "$schema": "https://steeltoe.io/schema/v4/schema.json", -- "Postgres:Client:ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" -+ "Steeltoe:Client:PostgreSQL:Default:ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" +- "Postgres": { +- "Client": { +- "ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" +- } +- } ++ "Steeltoe": { ++ "Client": { ++ "PostgreSql": { ++ "Default": { ++ "ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" ++ } ++ } ++ } ++ } } ``` @@ -384,8 +424,20 @@ appsettings.json: { - "$schema": "https://steeltoe.io/schema/v3/schema.json", + "$schema": "https://steeltoe.io/schema/v4/schema.json", -- "Postgres:Client:ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" -+ "Steeltoe:Client:PostgreSQL:Default:ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" +- "Postgres": { +- "Client": { +- "ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" +- } +- } ++ "Steeltoe": { ++ "Client": { ++ "PostgreSql": { ++ "Default": { ++ "ConnectionString": "Server=localhost;Database=steeltoe;Uid=steeltoe;Pwd=steeltoe" ++ } ++ } ++ } ++ } } ``` @@ -432,8 +484,20 @@ appsettings.json: { - "$schema": "https://steeltoe.io/schema/v3/schema.json", + "$schema": "https://steeltoe.io/schema/v4/schema.json", -- "RabbitMQ:Client:ConnectionString": "Server=localhost" -+ "Steeltoe:Client:RabbitMQ:Default:ConnectionString": "amqp://localhost:5672" +- "Rabbitmq": { +- "Client": { +- "Uri": "amqp://guest:guest@127.0.0.1/" +- } +- } ++ "Steeltoe": { ++ "Client": { ++ "RabbitMQ": { ++ "Default": { ++ "ConnectionString": "amqp://localhost:5672" ++ } ++ } ++ } ++ } } ``` @@ -493,8 +557,20 @@ appsettings.json: { - "$schema": "https://steeltoe.io/schema/v3/schema.json", + "$schema": "https://steeltoe.io/schema/v4/schema.json", -- "Redis:Client:ConnectionString": "localhost:6379" -+ "Steeltoe:Client:Redis:Default:ConnectionString": "localhost" +- "Redis": { +- "Client": { +- "ConnectionString": "localhost:6379" +- } +- } ++ "Steeltoe": { ++ "Client": { ++ "Redis": { ++ "Default": { ++ "ConnectionString": "localhost" ++ } ++ } ++ } ++ } } ``` @@ -548,9 +624,17 @@ appsettings.json: { - "$schema": "https://steeltoe.io/schema/v3/schema.json", + "$schema": "https://steeltoe.io/schema/v4/schema.json", - "Spring:Application:Name": "example-service", - "Eureka:Client:ShouldRegisterWithEureka": true, - "Eureka:Client:ShouldFetchRegistry": false + "Spring": { + "Application": { + "Name": "example-service" + } + }, + "Eureka": { + "Client": { + "ShouldRegisterWithEureka": true, + "ShouldFetchRegistry": false + } + } } ``` @@ -607,8 +691,17 @@ appsettings.json: { - "$schema": "https://steeltoe.io/schema/v3/schema.json", + "$schema": "https://steeltoe.io/schema/v4/schema.json", - "Eureka:Client:ShouldRegisterWithEureka": false, - "Eureka:Client:ShouldFetchRegistry": true + "Spring": { + "Application": { + "Name": "example-service" + } + }, + "Eureka": { + "Client": { + "ShouldRegisterWithEureka": false, + "ShouldFetchRegistry": true + } + } } ``` @@ -663,8 +756,16 @@ appsettings.json: { - "$schema": "https://steeltoe.io/schema/v3/schema.json", + "$schema": "https://steeltoe.io/schema/v4/schema.json", - "Spring:Application:Name": "example-service", - "Consul:Discovery:Register": true + "Spring": { + "Application": { + "Name": "example-service" + } + }, + "Consul": { + "Discovery": { + "Register": true + } + } } ``` @@ -721,7 +822,11 @@ appsettings.json: { - "$schema": "https://steeltoe.io/schema/v3/schema.json", + "$schema": "https://steeltoe.io/schema/v4/schema.json", - "Consul:Discovery:Register": false + "Consul": { + "Discovery": { + "Register": false + } + } } ``` @@ -829,11 +934,25 @@ appsettings.json: { - "$schema": "https://steeltoe.io/schema/v3/schema.json", + "$schema": "https://steeltoe.io/schema/v4/schema.json", - "Management:Endpoints:Actuator:Exposure:Include": ["*"], -+ "Management:Endpoints:Health:ShowComponents": "Always", -+ "Management:Endpoints:Health:ShowDetails": "Always", -+ "Management:Endpoints:Health:Readiness:Enabled": true, -+ "Management:Endpoints:Health:Liveness:Enabled": true + "Management": { + "Endpoints": { + "Actuator": { + "Exposure": { + "Include": [ "*" ] + } ++ }, ++ "Health": { ++ "ShowComponents": "Always", ++ "ShowDetails": "Always", ++ "Readiness": { ++ "Enabled": true ++ }, ++ "Liveness": { ++ "Enabled": true ++ } + } + } + } } ``` @@ -856,10 +975,20 @@ appsettings.json: { - "$schema": "https://steeltoe.io/schema/v3/schema.json", + "$schema": "https://steeltoe.io/schema/v4/schema.json", -+ "Management:Endpoints:Health:ShowComponents": "Always", -+ "Management:Endpoints:Health:ShowDetails": "Always", -+ "Management:Endpoints:Health:Readiness:Enabled": true, -+ "Management:Endpoints:Health:Liveness:Enabled": true ++ "Management": { ++ "Endpoints": { ++ "Health": { ++ "ShowComponents": "Always", ++ "ShowDetails": "Always", ++ "Readiness": { ++ "Enabled": true ++ }, ++ "Liveness": { ++ "Enabled": true ++ } ++ } ++ } ++ } } ``` @@ -980,11 +1109,29 @@ appsettings.json: { - "$schema": "https://steeltoe.io/schema/v3/schema.json", + "$schema": "https://steeltoe.io/schema/v4/schema.json", - "Spring:Application:Name": "example-service", - "Management:Endpoints:Actuator:Exposure:Include": [ "*" ], - "Spring:Boot:Admin:Client:Url": "http://localhost:9099", -- "Spring:Boot:Admin:Client:BasePath": "http://host.docker.internal:5050" -+ "Spring:Boot:Admin:Client:BaseHost": "host.docker.internal" + "Spring": { + "Application": { + "Name": "example-service" + }, + "Boot": { + "Admin": { + "Client": { + "Url": "http://localhost:9099", +- "BasePath": "http://host.docker.internal:5050" ++ "BaseHost": "host.docker.internal" + } + } + } + }, + "Management": { + "Endpoints": { + "Actuator": { + "Exposure": { + "Include": [ "*" ] + } + } + } + } } ``` @@ -1125,8 +1272,20 @@ appsettings.json: { - "$schema": "https://steeltoe.io/schema/v3/schema.json", + "$schema": "https://steeltoe.io/schema/v4/schema.json", -- "Redis:Client:ConnectionString": "localhost:6379" -+ "Steeltoe:Client:Redis:Default:ConnectionString": "localhost" +- "Redis": { +- "Client": { +- "ConnectionString": "localhost:6379" +- } +- } ++ "Steeltoe": { ++ "Client": { ++ "Redis": { ++ "Default": { ++ "ConnectionString": "localhost" ++ } ++ } ++ } ++ } } ``` @@ -1168,27 +1327,3 @@ app.MapGet("get-session", async httpContext => app.Run(); ``` - -# TODO: Remove temporary template below - -Project file: - -```diff - - -- -+ - - -``` - -Program.cs: - -```diff --XXXXX -+XXXXX - -var builder = WebApplication.CreateBuilder(args); --XXXXX -+XXXXX -``` From 6f00420779a31823f39d3a90f6e3b7661c57927f Mon Sep 17 00:00:00 2001 From: Bart Koelman <104792814+bart-vmware@users.noreply.github.com> Date: Mon, 25 Aug 2025 12:39:44 +0200 Subject: [PATCH 4/6] Link to migration steps from whats-new page --- docs/docs/v4/welcome/whats-new.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/docs/v4/welcome/whats-new.md b/docs/docs/v4/welcome/whats-new.md index 6517d576..9724bc58 100644 --- a/docs/docs/v4/welcome/whats-new.md +++ b/docs/docs/v4/welcome/whats-new.md @@ -19,9 +19,13 @@ underscored that this is the time to refocus on Steeltoe's core goals and re-eva Steeltoe 4 is a major release that brings many improvements and changes to the library. The goal of this release is to make Steeltoe better integrated in the .NET ecosystem in a more developer-friendly way, compatible with the latest versions of .NET and third-party libraries/products, and to improve the overall quality of the library. -This document provides an overview of the changes in Steeltoe 4, the impact on existing applications, and serves as the upgrade guide (with a searchable API diff and replacement notes). Steeltoe 4 requires .NET 8 or higher. +This document provides an overview of the changes in Steeltoe 4 and the impact on existing applications (with a searchable API diff and replacement notes). + +> [!TIP] +> For quick steps to upgrade an existing application from Steeltoe 3, see [Migrating from Steeltoe 3](./migrate-quick-steps.md). + ### Quality of Life improvements - Annotated for [nullable reference types](https://learn.microsoft.com/dotnet/csharp/nullable-references) From b2f3cbc39ded953428ba4c7e11ae4a656adffeb7 Mon Sep 17 00:00:00 2001 From: Bart Koelman <104792814+bart-vmware@users.noreply.github.com> Date: Fri, 29 Aug 2025 11:51:33 +0200 Subject: [PATCH 5/6] Turn off colors in unchanged diff --- docs/docs/v4/welcome/migrate-quick-steps.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/v4/welcome/migrate-quick-steps.md b/docs/docs/v4/welcome/migrate-quick-steps.md index 61279985..f3b7ecea 100644 --- a/docs/docs/v4/welcome/migrate-quick-steps.md +++ b/docs/docs/v4/welcome/migrate-quick-steps.md @@ -640,7 +640,7 @@ appsettings.json: launchSettings.json: -``` +```text { "profiles": { "http": { @@ -771,7 +771,7 @@ appsettings.json: launchSettings.json: -``` +```text { "profiles": { "http": { From 6049e3af22e5f29e6393308dfaf53849cd4f1f39 Mon Sep 17 00:00:00 2001 From: Bart Koelman <104792814+bart-vmware@users.noreply.github.com> Date: Fri, 29 Aug 2025 11:54:54 +0200 Subject: [PATCH 6/6] Change colors-off to diff mode --- docs/docs/v4/welcome/migrate-quick-steps.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/v4/welcome/migrate-quick-steps.md b/docs/docs/v4/welcome/migrate-quick-steps.md index f3b7ecea..48b8b966 100644 --- a/docs/docs/v4/welcome/migrate-quick-steps.md +++ b/docs/docs/v4/welcome/migrate-quick-steps.md @@ -640,7 +640,7 @@ appsettings.json: launchSettings.json: -```text +```diff { "profiles": { "http": { @@ -771,7 +771,7 @@ appsettings.json: launchSettings.json: -```text +```diff { "profiles": { "http": {