Skip to content

Commit

Permalink
Merge branch 'main' into RavenDB-Aspire
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronpowell authored Jan 31, 2025
2 parents 94363bd + 2f30c53 commit 68c661f
Show file tree
Hide file tree
Showing 32 changed files with 274 additions and 77 deletions.
38 changes: 38 additions & 0 deletions .github/workflows/remove-stale-label.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Remove stale label on comment

on:
issue_comment:
types: [created]

permissions:
issues: write
pull-requests: write

jobs:
remove-stale-label:
if: github.event.comment.body == '/stale-extend'
runs-on: ubuntu-latest
steps:
- name: Remove stale label from pull request
uses: "actions/github-script@v7"
with:
script: |
github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
name: 'stale'
});
if: github.event_name == 'pull_request'

- name: Remove stale label from issue
uses: "actions/github-script@v7"
with:
script: |
github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.issue.number,
name: 'stale'
});
if: github.event_name == 'issue'
26 changes: 22 additions & 4 deletions .github/workflows/stale-issues.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,45 @@ jobs:

steps:
- uses: actions/stale@v9
name: Standard issue and PR stale bot
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 21
days-before-close: 14
enable-statistics: true
any-of-labels: "Awaiting Response"
exempt-issue-labels: "awaiting-response"
exempt-pr-labels: "awaiting-response"
remove-stale-when-updated: true
stale-issue-message: "We have noticed this issue has not been updated within 21 days. If there is no action on this issue in the next 14 days, we will automatically close it."
stale-pr-message: "We have noticed this PR has not been updated within 21 days. If there is no action on this PR in the next 14 days, we will automatically close it."
stale-issue-message: "We have noticed this issue has not been updated within 21 days. If there is no action on this issue in the next 14 days, we will automatically close it. You can use `/stale-extend` to extend the window."
stale-pr-message: "We have noticed this PR has not been updated within 21 days. If there is no action on this PR in the next 14 days, we will automatically close it. You can use `/stale-extend` to extend the window."
close-issue-message: "This issue has been stale for 5 weeks and has been automatically closed."
close-pr-message: "This PR has been stale for 5 weeks and has been automatically closed."
stale-issue-label: "stale"
stale-pr-label: "stale"

- uses: actions/stale@v9
name: Question and Resolved issue stale bot
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 5
days-before-close: 2
enable-statistics: true
only-issue-labels: "Question,Resolved"
remove-stale-when-updated: true
stale-issue-message: "We have noticed this issue has been resolved for 5 days. If there is not action on this issue in the next 2 days, we will automatically close it."
stale-issue-message: "We have noticed this issue has been resolved for 5 days. If there is no action on this issue in the next 2 days, we will automatically close it. You can use `/stale-extend` to extend the window."
stale-issue-label: "stale"

- uses: actions/stale@v9
name: Awaiting response issue and PR stale bot
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 7
days-before-close: 3
enable-statistics: true
only-issue-labels: "awaiting-response"
only-pr-labels: "awaiting-response"
remove-stale-when-updated: true
stale-issue-message: "We have noticed this issue has been awaiting response for 7 days. If there is no action on this issue in the next 3 days, we will automatically close it. You can use `/stale-extend` to extend the window."
stale-pr-message: "We have noticed this PR has been awaiting response for 7 days. If there is no action on this PR in the next 3 days, we will automatically close it. You can use `/stale-extend` to extend the window."
stale-issue-label: "stale"
stale-pr-label: "stale"
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<AspireAppHostSdkVersion>$(AspireVersion)</AspireAppHostSdkVersion>
<AspNetCoreVersion>9.0.0</AspNetCoreVersion>
<DotNetExtensionsVersion>9.0.0</DotNetExtensionsVersion>
<OpenTelemetryVersion>1.10.0</OpenTelemetryVersion>
<OpenTelemetryVersion>1.11.0</OpenTelemetryVersion>
<TestContainersVersion>4.1.0</TestContainersVersion>
<IsPackable>false</IsPackable>
<UsePublicApiAnalyzers>true</UsePublicApiAnalyzers>
Expand Down
6 changes: 3 additions & 3 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@
<PackageVersion Include="OpenTelemetry.Exporter.InMemory" Version="$(OpenTelemetryVersion)" />
<!-- Testing packages -->
<PackageVersion Include="Aspire.Hosting.Testing" Version="$(AspireVersion)" />
<PackageVersion Include="coverlet.collector" Version="6.0.3" />
<PackageVersion Include="FluentAssertions" Version="7.0.0" />
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
<PackageVersion Include="MartinCostello.Logging.XUnit" Version="0.5.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageVersion Include="xunit" Version="2.9.2" />
Expand All @@ -51,7 +50,7 @@
<PackageVersion Include="OllamaSharp" Version="4.0.22" />
<PackageVersion Include="MassTransit" Version="8.3.4" />
<PackageVersion Include="MassTransit.ActiveMQ" Version="8.3.1" />
<PackageVersion Include="MeiliSearch" Version="0.15.5" />
<PackageVersion Include="MeiliSearch" Version="0.16.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.1" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.1" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="8.0.11" />
Expand All @@ -69,5 +68,6 @@
<PackageVersion Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" Version="3.3.4" />
<!-- Testcontainers packages -->
<PackageVersion Include="Testcontainers" Version="$(TestContainersVersion)" />
<PackageVersion Include="Testcontainers.MsSql" Version="$(TestContainersVersion)" />
</ItemGroup>
</Project>
4 changes: 2 additions & 2 deletions docs/create-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ To demonstrate how to use the integration, you should create an example applicat

The testing framework used is [`xunit`](https://xunit.net/), and you'll need to create a new test project in the `tests/` directory. The test project should be named `CommunityToolkit.Aspire.Hosting.MyIntegration.Tests` or `CommunityToolkit.Aspire.MyIntegration.Tests` following the same naming guidelines as the integration project. It's easiest to create a **Class Library** project as the `Directory.Build.props` will automatically add the necessary test dependencies.

Asserts can be written using the `Assert` type or `FluentAssertions` if that is your preferred style, both are supported.
Asserts can be written using the `Assert` type.

### Unit Tests

Expand Down Expand Up @@ -85,7 +85,7 @@ public async Task ResourceStartsAndRespondsOk(string appName)

var response = await httpClient.GetAsync("/");

response.StatusCode.Should().Be(HttpStatusCode.OK);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,9 @@
builder.AddSqlPackage<Packages.ErikEJ_Dacpac_Chinook>("chinook")
.WithReference(database);

var connection = builder.AddConnectionString("Aspire");

builder.AddSqlProject<Projects.SdkProject>("existing-db")
.WithReference(connection);

builder.Build().Run();
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@
"Microsoft.AspNetCore": "Warning",
"Aspire.Hosting.Dcp": "Warning"
}
},
"ConnectionStrings": {
"Aspire": "Server=(localdb)\\mssqllocaldb;Database=aspire;Trusted_Connection=True;Encrypt=False"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ internal static class OllamaContainerImageTags

public const string OpenWebUIRegistry = "ghcr.io";
public const string OpenWebUIImage = "open-webui/open-webui";
public const string OpenWebUITag = "0.4.8";
}
public const string OpenWebUITag = "0.5.7";
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Logging;
using Microsoft.SqlServer.Dac;

Expand All @@ -9,11 +10,23 @@ namespace CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects;
internal class DacpacDeployer : IDacpacDeployer
{
/// <inheritdoc cref="IDacpacDeployer.Deploy(string, DacDeployOptions, string, string, ILogger, CancellationToken)" />
public void Deploy(string dacpacPath, DacDeployOptions options, string targetConnectionString, string targetDatabaseName, ILogger deploymentLogger, CancellationToken cancellationToken)
public void Deploy(string dacpacPath, DacDeployOptions options, string targetConnectionString, string? targetDatabaseName, ILogger deploymentLogger, CancellationToken cancellationToken)
{
using var dacPackage = DacPackage.Load(dacpacPath, DacSchemaModelStorageType.Memory);

if (string.IsNullOrWhiteSpace(targetDatabaseName))
{
targetDatabaseName = GetDatabaseName(targetConnectionString);
}

var dacServices = new DacServices(targetConnectionString);
dacServices.Message += (sender, args) => deploymentLogger.LogInformation(args.Message.ToString());
dacServices.Deploy(dacPackage, targetDatabaseName, true, options, cancellationToken);
}

private static string GetDatabaseName(string connectionString)
{
var builder = new SqlConnectionStringBuilder(connectionString);
return builder.InitialCatalog;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ internal interface IDacpacDeployer
/// <param name="targetDatabaseName">Name of the target database to deploy to.</param>
/// <param name="deploymentLogger">An <see cref="ILogger" /> to write the deployment log to.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the deployment operation.</param>
void Deploy(string dacpacPath, DacDeployOptions options, string targetConnectionString, string targetDatabaseName, ILogger deploymentLogger, CancellationToken cancellationToken);
void Deploy(string dacpacPath, DacDeployOptions options, string targetConnectionString, string? targetDatabaseName, ILogger deploymentLogger, CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Aspire.Hosting.ApplicationModel.IResourceWithDacpac
Aspire.Hosting.ApplicationModel.SqlPackageResource<TPackage>
Aspire.Hosting.ApplicationModel.SqlPackageResource<TPackage>.SqlPackageResource(string! name) -> void
static Aspire.Hosting.SqlProjectBuilderExtensions.AddSqlPackage<TPackage>(this Aspire.Hosting.IDistributedApplicationBuilder! builder, string! name) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.ApplicationModel.SqlPackageResource<TPackage>!>!
static Aspire.Hosting.SqlProjectBuilderExtensions.WithReference(this Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.ApplicationModel.SqlProjectResource!>! builder, Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.ApplicationModel.IResourceWithConnectionString!>! target) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.ApplicationModel.SqlProjectResource!>!
static Aspire.Hosting.SqlProjectBuilderExtensions.WithReference<TPackage>(this Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.ApplicationModel.SqlPackageResource<TPackage>!>! builder, Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.ApplicationModel.IResourceWithConnectionString!>! target) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.ApplicationModel.SqlPackageResource<TPackage>!>!
static Aspire.Hosting.SqlProjectBuilderExtensions.WithReference<TPackage>(this Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.ApplicationModel.SqlPackageResource<TPackage>!>! builder, Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.ApplicationModel.SqlServerDatabaseResource!>! target) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.ApplicationModel.SqlPackageResource<TPackage>!>!
static Aspire.Hosting.SqlProjectBuilderExtensions.WithDacpac<TPackage>(this Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.ApplicationModel.SqlPackageResource<TPackage>!>! builder, string! dacpacPath) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.ApplicationModel.SqlPackageResource<TPackage>!>!
static Aspire.Hosting.SqlProjectBuilderExtensions.WithConfigureDacDeployOptions<TPackage>(this Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.ApplicationModel.SqlPackageResource<TPackage>!>! builder, System.Action<Microsoft.SqlServer.Dac.DacDeployOptions!>! configureDeploymentOptions) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.ApplicationModel.SqlPackageResource<TPackage>!>!
15 changes: 15 additions & 0 deletions src/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,21 @@ builder.AddSqlProject("mysqlproj")
builder.Build().Run();
```

## Support for existing SQL Server
Instead of using the `AddSqlServer` method to use a SQL Server container, you can specify a connection string to an existing server:

```csharp
var builder = DistributedApplication.CreateBuilder(args);

// Get an existing connection string from the configuration
var connection = builder.AddConnectionString("Aspire");

builder.AddSqlProject<Projects.SdkProject>("mysqlproj")
.WithReference(connection);

builder.Build().Run();
```

## Deployment options support
Define options that affect the behavior of package deployment.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,16 @@ internal static IResourceBuilder<TResource> InternalWithConfigureDacDeployOption
/// <param name="target">An <see cref="IResourceBuilder{T}"/> representing the target <see cref="SqlServerDatabaseResource"/> to publish the SQL Server Database project to.</param>
/// <returns>An <see cref="IResourceBuilder{T}"/> that can be used to further customize the resource.</returns>
public static IResourceBuilder<SqlProjectResource> WithReference(
this IResourceBuilder<SqlProjectResource> builder, IResourceBuilder<SqlServerDatabaseResource> target) => InternalWithReference(builder, target);
this IResourceBuilder<SqlProjectResource> builder, IResourceBuilder<SqlServerDatabaseResource> target) => InternalWithReference(builder, target, target.Resource.DatabaseName);

/// <summary>
/// Publishes the SQL Server Database project to the target <see cref="IResourceWithConnectionString"/>.
/// </summary>
/// <param name="builder">An <see cref="IResourceBuilder{T}"/> representing the SQL Server Database project to publish.</param>
/// <param name="target">An <see cref="IResourceBuilder{T}"/> representing the target <see cref="IResourceWithConnectionString"/> to publish the SQL Server Database project to.</param>
/// <returns>An <see cref="IResourceBuilder{T}"/> that can be used to further customize the resource.</returns>
public static IResourceBuilder<SqlProjectResource> WithReference(
this IResourceBuilder<SqlProjectResource> builder, IResourceBuilder<IResourceWithConnectionString> target) => InternalWithReference(builder, target);

/// <summary>
/// Publishes the SQL Server Database project to the target <see cref="SqlServerDatabaseResource"/>.
Expand All @@ -160,28 +169,49 @@ public static IResourceBuilder<SqlProjectResource> WithReference(
public static IResourceBuilder<SqlPackageResource<TPackage>> WithReference<TPackage>(
this IResourceBuilder<SqlPackageResource<TPackage>> builder, IResourceBuilder<SqlServerDatabaseResource> target)
where TPackage : IPackageMetadata
{
return InternalWithReference(builder, target, target.Resource.DatabaseName);
}

/// <summary>
/// Publishes the SQL Server Database project to the target <see cref="IResourceWithConnectionString"/>.
/// </summary>
/// <param name="builder">An <see cref="IResourceBuilder{T}"/> representing the SQL Server Database project to publish.</param>
/// <param name="target">An <see cref="IResourceBuilder{T}"/> representing the target <see cref="IResourceWithConnectionString"/> to publish the SQL Server Database project to.</param>
/// <returns>An <see cref="IResourceBuilder{T}"/> that can be used to further customize the resource.</returns>
public static IResourceBuilder<SqlPackageResource<TPackage>> WithReference<TPackage>(
this IResourceBuilder<SqlPackageResource<TPackage>> builder, IResourceBuilder<IResourceWithConnectionString> target)
where TPackage : IPackageMetadata
{
return InternalWithReference(builder, target);
}

internal static IResourceBuilder<TResource> InternalWithReference<TResource>(this IResourceBuilder<TResource> builder, IResourceBuilder<SqlServerDatabaseResource> target)
internal static IResourceBuilder<TResource> InternalWithReference<TResource>(this IResourceBuilder<TResource> builder, IResourceBuilder<IResourceWithConnectionString> target, string? targetDatabaseName = null)
where TResource : IResourceWithDacpac
{
builder.ApplicationBuilder.Services.TryAddSingleton<IDacpacDeployer, DacpacDeployer>();
builder.ApplicationBuilder.Services.TryAddSingleton<SqlProjectPublishService>();

builder.ApplicationBuilder.Eventing.Subscribe<ResourceReadyEvent>(target.Resource, async (resourceReady, ct) =>
if (target.Resource is SqlServerDatabaseResource)
{
builder.ApplicationBuilder.Eventing.Subscribe<ResourceReadyEvent>(target.Resource, async (resourceReady, ct) =>
{
await RunPublish(builder, target, targetDatabaseName, resourceReady.Services, ct);
});
}
else
{
var service = resourceReady.Services.GetRequiredService<SqlProjectPublishService>();
await service.PublishSqlProject(builder.Resource, target.Resource, ct);
});
builder.ApplicationBuilder.Eventing.Subscribe<AfterResourcesCreatedEvent>(async (@event, ct) => {
await RunPublish(builder, target, targetDatabaseName, @event.Services, ct);
});
}

builder.WaitFor(target);

builder.WithCommand("redeploy", "Redeploy", async (context) =>
{
var service = context.ServiceProvider.GetRequiredService<SqlProjectPublishService>();
await service.PublishSqlProject(builder.Resource, target.Resource, context.CancellationToken);
await service.PublishSqlProject(builder.Resource, target.Resource, targetDatabaseName, context.CancellationToken);
return new ExecuteCommandResult { Success = true };
}, updateState: (context) => context.ResourceSnapshot?.State?.Text == KnownResourceStates.Finished ? ResourceCommandState.Enabled : ResourceCommandState.Disabled,
displayDescription: "Redeploys the SQL Server Database Project to the target database.",
Expand All @@ -191,4 +221,11 @@ internal static IResourceBuilder<TResource> InternalWithReference<TResource>(thi

return builder;
}

private static async Task RunPublish<TResource>(IResourceBuilder<TResource> builder, IResourceBuilder<IResourceWithConnectionString> target, string? targetDatabaseName, IServiceProvider serviceProvider, CancellationToken ct)
where TResource : IResourceWithDacpac
{
var service = serviceProvider.GetRequiredService<SqlProjectPublishService>();
await service.PublishSqlProject(builder.Resource, target.Resource, targetDatabaseName, ct);
}
}
Loading

0 comments on commit 68c661f

Please sign in to comment.