Skip to content

Commit 76b6f32

Browse files
authored
feat: Refactor DockerComposePublishingContext for clarity (#8563)
Update property access to use `PublisherOptions` directly for consistency. Introduce a new option to control image building during the publishing process. Ensure that image building only occurs when enabled. Adjust tests to accommodate these changes.
1 parent ae93b5e commit 76b6f32

File tree

3 files changed

+66
-10
lines changed

3 files changed

+66
-10
lines changed

src/Aspire.Hosting.Docker/DockerComposePublisherOptions.cs

+5
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,9 @@ public sealed class DockerComposePublisherOptions : PublishingOptions
1919
/// The name of an existing network to be used.
2020
/// </summary>
2121
public string? ExistingNetworkName { get; set; }
22+
23+
/// <summary>
24+
/// Indicates whether to build container images during the publishing process.
25+
/// </summary>
26+
public bool BuildImages { get; set; } = true;
2227
}

src/Aspire.Hosting.Docker/DockerComposePublishingContext.cs

+11-8
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ internal sealed class DockerComposePublishingContext(
3030
CancellationToken cancellationToken = default)
3131
{
3232
public readonly IResourceContainerImageBuilder ImageBuilder = imageBuilder;
33-
33+
public readonly DockerComposePublisherOptions PublisherOptions = publisherOptions;
3434
public readonly PortAllocator PortAllocator = new();
3535
private readonly Dictionary<string, (string Description, string? DefaultValue)> _env = [];
3636
private readonly Dictionary<IResource, ComposeServiceContext> _composeServices = [];
@@ -48,7 +48,7 @@ internal async Task WriteModelAsync(DistributedApplicationModel model)
4848
logger.StartGeneratingDockerCompose();
4949

5050
ArgumentNullException.ThrowIfNull(model);
51-
ArgumentNullException.ThrowIfNull(publisherOptions.OutputPath);
51+
ArgumentNullException.ThrowIfNull(PublisherOptions.OutputPath);
5252

5353
if (model.Resources.Count == 0)
5454
{
@@ -58,7 +58,7 @@ internal async Task WriteModelAsync(DistributedApplicationModel model)
5858

5959
await WriteDockerComposeOutputAsync(model).ConfigureAwait(false);
6060

61-
logger.FinishGeneratingDockerCompose(publisherOptions.OutputPath);
61+
logger.FinishGeneratingDockerCompose(PublisherOptions.OutputPath);
6262
}
6363

6464
public void AddEnv(string name, string description, string? defaultValue = null)
@@ -70,7 +70,7 @@ private async Task WriteDockerComposeOutputAsync(DistributedApplicationModel mod
7070
{
7171
var defaultNetwork = new Network
7272
{
73-
Name = publisherOptions.ExistingNetworkName ?? "aspire",
73+
Name = PublisherOptions.ExistingNetworkName ?? "aspire",
7474
Driver = "bridge",
7575
};
7676

@@ -105,8 +105,8 @@ private async Task WriteDockerComposeOutputAsync(DistributedApplicationModel mod
105105
}
106106

107107
var composeOutput = composeFile.ToYaml();
108-
var outputFile = Path.Combine(publisherOptions.OutputPath!, "docker-compose.yaml");
109-
Directory.CreateDirectory(publisherOptions.OutputPath!);
108+
var outputFile = Path.Combine(PublisherOptions.OutputPath!, "docker-compose.yaml");
109+
Directory.CreateDirectory(PublisherOptions.OutputPath!);
110110
await File.WriteAllTextAsync(outputFile, composeOutput, cancellationToken).ConfigureAwait(false);
111111

112112
if (_env.Count == 0)
@@ -117,7 +117,7 @@ private async Task WriteDockerComposeOutputAsync(DistributedApplicationModel mod
117117

118118
// Write a .env file with the environment variable names
119119
// that are used in the compose file
120-
var envFile = Path.Combine(publisherOptions.OutputPath!, ".env");
120+
var envFile = Path.Combine(PublisherOptions.OutputPath!, ".env");
121121
using var envWriter = new StreamWriter(envFile);
122122

123123
foreach (var entry in _env)
@@ -200,7 +200,10 @@ private record struct EndpointMapping(string Scheme, string Host, int InternalPo
200200

201201
public async Task<Service> BuildComposeServiceAsync(CancellationToken cancellationToken)
202202
{
203-
await composePublishingContext.ImageBuilder.BuildImageAsync(resource, cancellationToken).ConfigureAwait(false);
203+
if (composePublishingContext.PublisherOptions.BuildImages)
204+
{
205+
await composePublishingContext.ImageBuilder.BuildImageAsync(resource, cancellationToken).ConfigureAwait(false);
206+
}
204207

205208
if (!TryGetContainerImageName(resource, out var containerImageName))
206209
{

tests/Aspire.Hosting.Docker.Tests/DockerComposePublisherTests.cs

+50-2
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ public async Task PublishAsync_GeneratesValidDockerComposeFile()
6161

6262
await ExecuteBeforeStartHooksAsync(app, default);
6363

64-
var publisher = new DockerComposePublisher("test", options,
64+
var publisher = new DockerComposePublisher("test",
65+
options,
6566
NullLogger<DockerComposePublisher>.Instance,
6667
builder.ExecutionContext,
6768
new MockImageBuilder()
@@ -130,7 +131,7 @@ public async Task PublishAsync_GeneratesValidDockerComposeFile()
130131
networks:
131132
aspire:
132133
driver: "bridge"
133-
134+
134135
""",
135136
content, ignoreAllWhiteSpace: true, ignoreLineEndingDifferences: true);
136137

@@ -195,13 +196,60 @@ public async Task DockerComposeCorrectlyEmitsPortMappings()
195196
content, ignoreAllWhiteSpace: true, ignoreLineEndingDifferences: true);
196197
}
197198

199+
[Theory]
200+
[InlineData(true)]
201+
[InlineData(false)]
202+
public async Task DockerComposeHandleImageBuilding(bool shouldBuildImages)
203+
{
204+
using var tempDir = new TempDirectory();
205+
using var builder = TestDistributedApplicationBuilder.Create(["--operation", "publish", "--publisher", "docker-compose", "--output-path", tempDir.Path])
206+
.WithTestAndResourceLogging(outputHelper);
207+
208+
var options = new OptionsMonitor(new DockerComposePublisherOptions
209+
{
210+
OutputPath = tempDir.Path,
211+
BuildImages = shouldBuildImages,
212+
});
213+
214+
var mockImageBuilder = new MockImageBuilder();
215+
216+
builder.AddDockerComposePublisher();
217+
218+
builder.AddContainer("resource", "mcr.microsoft.com/dotnet/aspnet:8.0")
219+
.WithEnvironment("ASPNETCORE_ENVIRONMENT", "Development")
220+
.WithHttpEndpoint(env: "HTTP_PORT");
221+
222+
var app = builder.Build();
223+
224+
var model = app.Services.GetRequiredService<DistributedApplicationModel>();
225+
226+
await ExecuteBeforeStartHooksAsync(app, CancellationToken.None);
227+
228+
var publisher = new DockerComposePublisher("test",
229+
options,
230+
NullLogger<DockerComposePublisher>.Instance,
231+
builder.ExecutionContext,
232+
mockImageBuilder
233+
);
234+
235+
// Act
236+
await publisher.PublishAsync(model, CancellationToken.None);
237+
238+
var composePath = Path.Combine(tempDir.Path, "docker-compose.yaml");
239+
Assert.True(File.Exists(composePath));
240+
Assert.Equal(shouldBuildImages, mockImageBuilder.BuildImageCalled);
241+
}
242+
198243
[UnsafeAccessor(UnsafeAccessorKind.Method, Name = "ExecuteBeforeStartHooksAsync")]
199244
private static extern Task ExecuteBeforeStartHooksAsync(DistributedApplication app, CancellationToken cancellationToken);
200245

201246
private sealed class MockImageBuilder : IResourceContainerImageBuilder
202247
{
248+
public bool BuildImageCalled { get; private set; }
249+
203250
public Task BuildImageAsync(IResource resource, CancellationToken cancellationToken)
204251
{
252+
BuildImageCalled = true;
205253
return Task.CompletedTask;
206254
}
207255
}

0 commit comments

Comments
 (0)