Add an launch inspector command to Aspire Dashboard (#1283)

* Upgrade to Aspire 9.2.0

Signed-off-by: Jonathan Mezach <jonathan.mezach@rr-wfm.com>

* Remove workload installs from CI pipeline

Signed-off-by: Jonathan Mezach <jonathan.mezach@rr-wfm.com>

* Missed package upgrade

Signed-off-by: Jonathan Mezach <jonathan.mezach@rr-wfm.com>

* Fix usings

Signed-off-by: Jonathan Mezach <jonathan.mezach@rr-wfm.com>

* Add Open Inspector command

Signed-off-by: Jonathan Mezach <jonathan.mezach@rr-wfm.com>

* Fix broken test

Signed-off-by: Jonathan Mezach <jonathan.mezach@rr-wfm.com>

* PR comments

Signed-off-by: Jonathan Mezach <jonathan.mezach@rr-wfm.com>

* More PR comments

Signed-off-by: Jonathan Mezach <jonathan.mezach@rr-wfm.com>

---------

Signed-off-by: Jonathan Mezach <jonathan.mezach@rr-wfm.com>
This commit is contained in:
Jonathan Mezach
2025-04-25 20:23:19 +02:00
committed by GitHub
parent 9392069f8a
commit 8a07286b89
9 changed files with 706 additions and 618 deletions

View File

@@ -14,20 +14,11 @@ jobs:
echo "BuildId = $(buildId)" echo "BuildId = $(buildId)"
displayName: 'Print buildId' displayName: 'Print buildId'
- task: CmdLine@2
displayName: 'Install .NET Aspire workload'
inputs:
script: 'dotnet workload install aspire'
- script: | - script: |
dotnet tool install --global dotnet-sonarscanner dotnet tool install --global dotnet-sonarscanner
dotnet tool install --global dotnet-coverage dotnet tool install --global dotnet-coverage
displayName: 'Install dotnet tools' displayName: 'Install dotnet tools'
- script: |
dotnet workload install aspire
displayName: 'Install aspire'
- task: PowerShell@2 - task: PowerShell@2
displayName: "Use JDK17 by default" displayName: "Use JDK17 by default"
inputs: inputs:

View File

@@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="Aspire.AppHost.Sdk" Version="9.2.0" />
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<IsAspireHost>true</IsAspireHost>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -17,7 +18,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Aspire.Hosting.AppHost" Version="8.2.0" /> <PackageReference Include="Aspire.Hosting.AppHost" Version="9.2.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="Aspire.AppHost.Sdk" Version="9.2.0" />
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<IsAspireHost>true</IsAspireHost>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -14,7 +15,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Aspire.Hosting.AppHost" Version="8.2.0" /> <PackageReference Include="Aspire.Hosting.AppHost" Version="9.2.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -39,7 +39,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Aspire.Hosting" Version="8.2.2" /> <PackageReference Include="Aspire.Hosting" Version="9.2.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -0,0 +1,43 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace Aspire.Hosting.WireMock;
internal static class WireMockInspector
{
/// <summary>
/// Opens the WireMockInspector tool to inspect the WireMock server.
/// </summary>
/// <param name="wireMockUrl"></param>
/// <param name="title"></param>
/// <exception cref="InvalidOperationException"></exception>
/// <remarks>
/// Copy of <see href="https://github.com/WireMock-Net/WireMockInspector/blob/main/src/WireMock.Net.Extensions.WireMockInspector/WireMockServerExtensions.cs" />
/// without requestFilters and no call to WaitForExit() method in the process so it doesn't block the caller.
/// </remarks>
public static void Inspect(string wireMockUrl, [CallerMemberName] string title = "")
{
try
{
var arguments = $"attach --adminUrl {wireMockUrl} --autoLoad --instanceName \"{title}\"";
Process.Start(new ProcessStartInfo
{
FileName = "wiremockinspector",
Arguments = arguments,
UseShellExecute = false
});
}
catch (Exception e)
{
throw new InvalidOperationException
(
message: @"Cannot find installation of WireMockInspector.
Execute the following command to install WireMockInspector dotnet tool:
> dotnet tool install WireMockInspector --global --no-cache --ignore-failed-sources
To get more info please visit https://github.com/WireMock-Net/WireMockInspector",
innerException: e
);
}
}
}

View File

@@ -2,6 +2,8 @@
using Aspire.Hosting.ApplicationModel; using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Lifecycle; using Aspire.Hosting.Lifecycle;
using Aspire.Hosting.WireMock;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Stef.Validation; using Stef.Validation;
using WireMock.Client.Builders; using WireMock.Client.Builders;
using WireMock.Net.Aspire; using WireMock.Net.Aspire;
@@ -55,7 +57,8 @@ public static class WireMockServerBuilderExtensions
.AddResource(wireMockContainerResource) .AddResource(wireMockContainerResource)
.WithImage(DefaultLinuxImage) .WithImage(DefaultLinuxImage)
.WithEnvironment(ctx => ctx.EnvironmentVariables.Add("DOTNET_USE_POLLING_FILE_WATCHER", "1")) // https://khalidabuhakmeh.com/aspnet-docker-gotchas-and-workarounds#configuration-reloads-and-filesystemwatcher .WithEnvironment(ctx => ctx.EnvironmentVariables.Add("DOTNET_USE_POLLING_FILE_WATCHER", "1")) // https://khalidabuhakmeh.com/aspnet-docker-gotchas-and-workarounds#configuration-reloads-and-filesystemwatcher
.WithHttpEndpoint(port: arguments.HttpPort, targetPort: WireMockServerArguments.HttpContainerPort); .WithHttpEndpoint(port: arguments.HttpPort, targetPort: WireMockServerArguments.HttpContainerPort)
.WithWireMockInspectorCommand();
if (!string.IsNullOrEmpty(arguments.MappingsPath)) if (!string.IsNullOrEmpty(arguments.MappingsPath))
{ {
@@ -172,4 +175,48 @@ public static class WireMockServerBuilderExtensions
return wiremock; return wiremock;
} }
/// <summary>
/// Enables the WireMockInspect, a cross-platform UI app that facilitates WireMock troubleshooting.
/// This requires installation of the WireMockInspector tool.
/// <code>
/// dotnet tool install WireMockInspector --global --no-cache --ignore-failed-sources
/// </code>
/// </summary>
/// <param name="builder">The <see cref="IResourceBuilder{WireMockNetResource}"/>.</param>
/// <returns></returns>
public static IResourceBuilder<WireMockServerResource> WithWireMockInspectorCommand(this IResourceBuilder<WireMockServerResource> builder)
{
Guard.NotNull(builder);
CommandOptions commandOptions = new()
{
Description = "Requires installation of the WireMockInspector (https://github.com/WireMock-Net/WireMockInspector) tool:\ndotnet tool install WireMockInspector --global --no-cache --ignore-failed-sources",
UpdateState = OnUpdateResourceState,
IconName = "BoxSearch",
IconVariant = IconVariant.Filled
};
builder.WithCommand(
name: "wiremock-inspector",
displayName: "WireMock Inspector",
executeCommand: context => OnRunOpenInspectorCommandAsync(builder),
commandOptions: commandOptions);
return builder;
}
private static Task<ExecuteCommandResult> OnRunOpenInspectorCommandAsync(IResourceBuilder<WireMockServerResource> builder)
{
WireMockInspector.Inspect(builder.Resource.GetEndpoint().Url);
return Task.FromResult(CommandResults.Success());
}
private static ResourceCommandState OnUpdateResourceState(UpdateCommandStateContext context)
{
return context.ResourceSnapshot.HealthStatus is HealthStatus.Healthy
? ResourceCommandState.Enabled
: ResourceCommandState.Disabled;
}
} }

View File

@@ -1,11 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="Aspire.AppHost.Sdk" Version="9.2.0" />
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<IsAspireHost>true</IsAspireHost>
<SignAssembly>true</SignAssembly> <SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>../../src/WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>../../src/WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
@@ -18,7 +19,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Aspire.Hosting.AppHost" Version="8.2.2" /> <PackageReference Include="Aspire.Hosting.AppHost" Version="9.2.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -13,7 +13,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Aspire.Hosting.Testing" Version="8.2.2" /> <PackageReference Include="Aspire.Hosting.Testing" Version="9.2.0" />
<PackageReference Include="Codecov" Version="1.13.0" /> <PackageReference Include="Codecov" Version="1.13.0" />
<PackageReference Include="coverlet.msbuild" Version="6.0.2"> <PackageReference Include="coverlet.msbuild" Version="6.0.2">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
@@ -39,6 +39,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Using Include="Aspire.Hosting" />
<Using Include="Aspire.Hosting.ApplicationModel" />
<Using Include="Aspire.Hosting.Testing" /> <Using Include="Aspire.Hosting.Testing" />
<Using Include="Xunit" /> <Using Include="Xunit" />
</ItemGroup> </ItemGroup>

View File

@@ -67,7 +67,7 @@ public class WireMockServerBuilderExtensionsTests
MappingsPath = null, MappingsPath = null,
HttpPort = port HttpPort = port
}); });
wiremock.Resource.Annotations.Should().HaveCount(4); wiremock.Resource.Annotations.Should().HaveCount(5);
var containerImageAnnotation = wiremock.Resource.Annotations.OfType<ContainerImageAnnotation>().FirstOrDefault(); var containerImageAnnotation = wiremock.Resource.Annotations.OfType<ContainerImageAnnotation>().FirstOrDefault();
containerImageAnnotation.Should().BeEquivalentTo(new ContainerImageAnnotation containerImageAnnotation.Should().BeEquivalentTo(new ContainerImageAnnotation
@@ -92,5 +92,7 @@ public class WireMockServerBuilderExtensionsTests
wiremock.Resource.Annotations.OfType<EnvironmentCallbackAnnotation>().FirstOrDefault().Should().NotBeNull(); wiremock.Resource.Annotations.OfType<EnvironmentCallbackAnnotation>().FirstOrDefault().Should().NotBeNull();
wiremock.Resource.Annotations.OfType<CommandLineArgsCallbackAnnotation>().FirstOrDefault().Should().NotBeNull(); wiremock.Resource.Annotations.OfType<CommandLineArgsCallbackAnnotation>().FirstOrDefault().Should().NotBeNull();
wiremock.Resource.Annotations.OfType<ResourceCommandAnnotation>().FirstOrDefault().Should().NotBeNull();
} }
} }