Skip to content

Commit 2c143e3

Browse files
authored
feat: aspire exec (#10061)
1 parent 2bd320b commit 2c143e3

File tree

55 files changed

+2037
-55
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+2037
-55
lines changed

playground/DatabaseMigration/DatabaseMigration.ApiModel/DatabaseMigration.ApiModel.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@
1010

1111
<ItemGroup>
1212
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" />
13+
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" />
14+
1315
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" />
16+
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" />
1417
</ItemGroup>
1518

1619
</Project>

playground/DatabaseMigration/DatabaseMigration.ApiModel/Migrations/MyDb1ContextModelSnapshot.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
1616
{
1717
#pragma warning disable 612, 618
1818
modelBuilder
19-
.HasAnnotation("ProductVersion", "8.0.2")
19+
.HasAnnotation("ProductVersion", "8.0.17")
2020
.HasAnnotation("Relational:MaxIdentifierLength", 128);
2121

2222
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
@@ -27,6 +27,9 @@ protected override void BuildModel(ModelBuilder modelBuilder)
2727
.ValueGeneratedOnAdd()
2828
.HasColumnType("uniqueidentifier");
2929

30+
b.Property<long>("Version")
31+
.HasColumnType("bigint");
32+
3033
b.HasKey("Id");
3134

3235
b.ToTable("Entries");

playground/DatabaseMigration/DatabaseMigration.ApiModel/MyDb1Context.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@ public class MyDb1Context(DbContextOptions<MyDb1Context> options) : DbContext(op
1111
public class Entry
1212
{
1313
public Guid Id { get; set; } = Guid.NewGuid();
14+
15+
public long Version { get; set; } = 1;
1416
}

playground/DatabaseMigration/DatabaseMigration.ApiService/DatabaseMigration.ApiService.csproj

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@
55
<Nullable>enable</Nullable>
66
<ImplicitUsings>enable</ImplicitUsings>
77
</PropertyGroup>
8-
8+
99
<ItemGroup>
10-
<AspireProjectOrPackageReference Include="Aspire.Microsoft.EntityFrameworkCore.SqlServer" />
1110
<ProjectReference Include="..\..\Playground.ServiceDefaults\Playground.ServiceDefaults.csproj" />
1211
<ProjectReference Include="..\DatabaseMigration.ApiModel\DatabaseMigration.ApiModel.csproj" />
1312
</ItemGroup>
1413

14+
<ItemGroup>
15+
<AspireProjectOrPackageReference Include="Aspire.Microsoft.EntityFrameworkCore.SqlServer" />
16+
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" />
17+
</ItemGroup>
18+
1519
</Project>

playground/DatabaseMigration/DatabaseMigration.ApiService/Program.cs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,27 @@
77

88
builder.AddServiceDefaults();
99

10-
builder.AddSqlServerDbContext<MyDb1Context>("db1", configureDbContextOptions: options =>
10+
if (args.Contains("--postgres"))
1111
{
12-
options.UseSqlServer(sqlServerOptions =>
12+
builder.Services.AddDbContextPool<MyDb1Context>(options =>
1313
{
14-
sqlServerOptions.MigrationsAssembly("DatabaseMigration.ApiModel");
14+
var connectionString = builder.Configuration.GetConnectionString("db1");
15+
options.UseNpgsql(connectionString, options =>
16+
{
17+
options.MigrationsAssembly("DatabaseMigration.ApiModel");
18+
});
1519
});
16-
});
20+
}
21+
else
22+
{
23+
builder.AddSqlServerDbContext<MyDb1Context>("db1", configureDbContextOptions: options =>
24+
{
25+
options.UseSqlServer(sqlServerOptions =>
26+
{
27+
sqlServerOptions.MigrationsAssembly("DatabaseMigration.ApiModel");
28+
});
29+
});
30+
}
1731

1832
var app = builder.Build();
1933

playground/DatabaseMigration/DatabaseMigration.AppHost/DatabaseMigration.AppHost.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
<ItemGroup>
1616
<AspireProjectOrPackageReference Include="Aspire.Hosting.AppHost" />
1717
<AspireProjectOrPackageReference Include="Aspire.Hosting.SqlServer" />
18+
<AspireProjectOrPackageReference Include="Aspire.Hosting.PostgreSQL" />
19+
<AspireProjectOrPackageReference Include="Aspire.Hosting.MySql" />
1820

1921
<ProjectReference Include="..\DatabaseMigration.ApiService\DatabaseMigration.ApiService.csproj" />
2022
<ProjectReference Include="..\DatabaseMigration.MigrationService\DatabaseMigration.MigrationService.csproj" />

playground/DatabaseMigration/DatabaseMigration.AppHost/Program.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,23 @@
33

44
var builder = DistributedApplication.CreateBuilder(args);
55

6-
var db1 = builder.AddSqlServer("sql1").AddDatabase("db1");
6+
IResourceBuilder<IResourceWithConnectionString> database;
7+
8+
if (args.Contains("--postgres"))
9+
{
10+
database = builder.AddPostgres("sql1").AddDatabase("db1");
11+
}
12+
else
13+
{
14+
database = builder.AddSqlServer("sql1").AddDatabase("db1");
15+
}
716

817
builder.AddProject<Projects.DatabaseMigration_ApiService>("api")
918
.WithExternalHttpEndpoints()
10-
.WithReference(db1);
19+
.WithReference(database);
1120

1221
builder.AddProject<Projects.DatabaseMigration_MigrationService>("migration")
13-
.WithReference(db1);
22+
.WithReference(database);
1423

1524
#if !SKIP_DASHBOARD_REFERENCE
1625
// This project is only added in playground projects to support development/debugging

src/Aspire.Cli/Aspire.Cli.csproj

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@
5555
</ItemGroup>
5656

5757
<ItemGroup>
58+
<Compile Update="Resources\ExecCommandStrings.Designer.cs">
59+
<DesignTime>True</DesignTime>
60+
<AutoGen>True</AutoGen>
61+
<DependentUpon>ExecCommandStrings.resx</DependentUpon>
62+
</Compile>
5863
<Compile Update="Resources\InteractionServiceStrings.Designer.cs">
5964
<DesignTime>True</DesignTime>
6065
<AutoGen>True</AutoGen>
@@ -108,6 +113,10 @@
108113
</ItemGroup>
109114

110115
<ItemGroup>
116+
<EmbeddedResource Update="Resources\ExecCommandStrings.resx">
117+
<Generator>ResXFileCodeGenerator</Generator>
118+
<LastGenOutput>ExecCommandStrings.Designer.cs</LastGenOutput>
119+
</EmbeddedResource>
111120
<EmbeddedResource Update="Resources\InteractionServiceStrings.resx">
112121
<XlfSourceFormat>Resx</XlfSourceFormat>
113122
<XlfOutputItem>EmbeddedResource</XlfOutputItem>

src/Aspire.Cli/Backchannel/AppHostBackchannel.cs

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ internal interface IAppHostBackchannel
2222
IAsyncEnumerable<PublishingActivity> GetPublishingActivitiesAsync(CancellationToken cancellationToken);
2323
Task<string[]> GetCapabilitiesAsync(CancellationToken cancellationToken);
2424
Task CompletePromptResponseAsync(string promptId, string?[] answers, CancellationToken cancellationToken);
25+
IAsyncEnumerable<CommandOutput> ExecAsync(CancellationToken cancellationToken);
2526
}
2627

2728
internal sealed class AppHostBackchannel(ILogger<AppHostBackchannel> logger, AspireCliTelemetry telemetry) : IAppHostBackchannel
@@ -32,8 +33,7 @@ internal sealed class AppHostBackchannel(ILogger<AppHostBackchannel> logger, Asp
3233
public async Task<long> PingAsync(long timestamp, CancellationToken cancellationToken)
3334
{
3435
using var activity = telemetry.ActivitySource.StartActivity();
35-
36-
var rpc = await _rpcTaskCompletionSource.Task;
36+
var rpc = await _rpcTaskCompletionSource.Task.WaitAsync(cancellationToken).ConfigureAwait(false);
3737

3838
logger.LogDebug("Sent ping with timestamp {Timestamp}", timestamp);
3939

@@ -52,8 +52,7 @@ public async Task RequestStopAsync(CancellationToken cancellationToken)
5252
// which will allow the CLI to await the pending run.
5353

5454
using var activity = telemetry.ActivitySource.StartActivity();
55-
56-
var rpc = await _rpcTaskCompletionSource.Task;
55+
var rpc = await _rpcTaskCompletionSource.Task.WaitAsync(cancellationToken).ConfigureAwait(false);
5756

5857
logger.LogDebug("Requesting stop");
5958

@@ -66,8 +65,7 @@ await rpc.InvokeWithCancellationAsync(
6665
public async Task<(string BaseUrlWithLoginToken, string? CodespacesUrlWithLoginToken)> GetDashboardUrlsAsync(CancellationToken cancellationToken)
6766
{
6867
using var activity = telemetry.ActivitySource.StartActivity();
69-
70-
var rpc = await _rpcTaskCompletionSource.Task;
68+
var rpc = await _rpcTaskCompletionSource.Task.WaitAsync(cancellationToken).ConfigureAwait(false);
7169

7270
logger.LogDebug("Requesting dashboard URL");
7371

@@ -82,8 +80,7 @@ await rpc.InvokeWithCancellationAsync(
8280
public async IAsyncEnumerable<BackchannelLogEntry> GetAppHostLogEntriesAsync([EnumeratorCancellation] CancellationToken cancellationToken)
8381
{
8482
using var activity = telemetry.ActivitySource.StartActivity();
85-
86-
var rpc = await _rpcTaskCompletionSource.Task;
83+
var rpc = await _rpcTaskCompletionSource.Task.WaitAsync(cancellationToken).ConfigureAwait(false);
8784

8885
logger.LogDebug("Requesting AppHost log entries");
8986

@@ -103,8 +100,7 @@ public async IAsyncEnumerable<BackchannelLogEntry> GetAppHostLogEntriesAsync([En
103100
public async IAsyncEnumerable<RpcResourceState> GetResourceStatesAsync([EnumeratorCancellation] CancellationToken cancellationToken)
104101
{
105102
using var activity = telemetry.ActivitySource.StartActivity();
106-
107-
var rpc = await _rpcTaskCompletionSource.Task;
103+
var rpc = await _rpcTaskCompletionSource.Task.WaitAsync(cancellationToken).ConfigureAwait(false);
108104

109105
logger.LogDebug("Requesting resource states");
110106

@@ -170,8 +166,7 @@ public async Task ConnectAsync(string socketPath, CancellationToken cancellation
170166
public async IAsyncEnumerable<PublishingActivity> GetPublishingActivitiesAsync([EnumeratorCancellation] CancellationToken cancellationToken)
171167
{
172168
using var activity = telemetry.ActivitySource.StartActivity();
173-
174-
var rpc = await _rpcTaskCompletionSource.Task;
169+
var rpc = await _rpcTaskCompletionSource.Task.WaitAsync(cancellationToken).ConfigureAwait(false);
175170

176171
logger.LogDebug("Requesting publishing activities.");
177172

@@ -191,8 +186,7 @@ public async IAsyncEnumerable<PublishingActivity> GetPublishingActivitiesAsync([
191186
public async Task<string[]> GetCapabilitiesAsync(CancellationToken cancellationToken)
192187
{
193188
using var activity = telemetry.ActivitySource.StartActivity();
194-
195-
var rpc = await _rpcTaskCompletionSource.Task.ConfigureAwait(false);
189+
var rpc = await _rpcTaskCompletionSource.Task.WaitAsync(cancellationToken).ConfigureAwait(false);
196190

197191
logger.LogDebug("Requesting capabilities");
198192

@@ -207,8 +201,7 @@ public async Task<string[]> GetCapabilitiesAsync(CancellationToken cancellationT
207201
public async Task CompletePromptResponseAsync(string promptId, string?[] answers, CancellationToken cancellationToken)
208202
{
209203
using var activity = telemetry.ActivitySource.StartActivity();
210-
211-
var rpc = await _rpcTaskCompletionSource.Task.ConfigureAwait(false);
204+
var rpc = await _rpcTaskCompletionSource.Task.WaitAsync(cancellationToken).ConfigureAwait(false);
212205

213206
logger.LogDebug("Providing prompt responses for prompt ID {PromptId}", promptId);
214207

@@ -217,5 +210,23 @@ await rpc.InvokeWithCancellationAsync(
217210
[promptId, answers],
218211
cancellationToken).ConfigureAwait(false);
219212
}
213+
214+
public async IAsyncEnumerable<CommandOutput> ExecAsync([EnumeratorCancellation] CancellationToken cancellationToken)
215+
{
216+
using var activity = telemetry.ActivitySource.StartActivity();
217+
var rpc = await _rpcTaskCompletionSource.Task.WaitAsync(cancellationToken).ConfigureAwait(false);
218+
219+
logger.LogDebug("Requesting execution.");
220+
var commandOutputs = await rpc.InvokeWithCancellationAsync<IAsyncEnumerable<CommandOutput>>(
221+
"ExecAsync",
222+
Array.Empty<object>(),
223+
cancellationToken);
224+
225+
logger.LogDebug("Requested execution.");
226+
await foreach (var commandOutput in commandOutputs.WithCancellation(cancellationToken))
227+
{
228+
yield return commandOutput;
229+
}
230+
}
220231
}
221232

src/Aspire.Cli/Backchannel/BackchannelJsonSerializerContext.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ namespace Aspire.Cli.Backchannel;
2424
[JsonSerializable(typeof(RequestId))]
2525
[JsonSerializable(typeof(IEnumerable<DisplayLineState>))]
2626
[JsonSerializable(typeof(ValidationResult))]
27+
[JsonSerializable(typeof(IAsyncEnumerable<CommandOutput>))]
28+
[JsonSerializable(typeof(MessageFormatterEnumerableTracker.EnumeratorResults<CommandOutput>))]
2729
internal partial class BackchannelJsonSerializerContext : JsonSerializerContext
2830
{
2931
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Using the Json source generator.")]

0 commit comments

Comments
 (0)