diff --git a/src/CommunityToolkit.Aspire.Hosting.Golang/GoModInstallerResource.cs b/src/CommunityToolkit.Aspire.Hosting.Golang/GoModInstallerResource.cs
new file mode 100644
index 00000000..64741561
--- /dev/null
+++ b/src/CommunityToolkit.Aspire.Hosting.Golang/GoModInstallerResource.cs
@@ -0,0 +1,9 @@
+namespace Aspire.Hosting.ApplicationModel;
+
+///
+/// A resource that represents a Go module installer that runs go mod tidy or go mod download.
+///
+/// The name of the resource.
+/// The working directory to use for the command.
+public class GoModInstallerResource(string name, string workingDirectory)
+ : ExecutableResource(name, "go", workingDirectory);
diff --git a/src/CommunityToolkit.Aspire.Hosting.Golang/GolangAppHostingExtension.cs b/src/CommunityToolkit.Aspire.Hosting.Golang/GolangAppHostingExtension.cs
index 964dc135..62d62dfe 100644
--- a/src/CommunityToolkit.Aspire.Hosting.Golang/GolangAppHostingExtension.cs
+++ b/src/CommunityToolkit.Aspire.Hosting.Golang/GolangAppHostingExtension.cs
@@ -234,4 +234,68 @@ private static string GetDefaultGoBaseImage(string workingDirectory, IServicePro
logger.LogDebug("No Go version detected, will use default version");
return null;
}
+
+ ///
+ /// Ensures Go module dependencies are tidied before the application starts using go mod tidy.
+ ///
+ /// The Golang app resource builder.
+ /// Optional action to configure the installer resource.
+ /// A reference to the .
+ public static IResourceBuilder WithGoModTidy(
+ this IResourceBuilder builder,
+ Action>? configureInstaller = null)
+ {
+ ArgumentNullException.ThrowIfNull(builder, nameof(builder));
+
+ // Only install packages during development, not in publish mode
+ if (!builder.ApplicationBuilder.ExecutionContext.IsPublishMode)
+ {
+ var installerName = $"{builder.Resource.Name}-go-mod-tidy";
+ var installer = new GoModInstallerResource(installerName, builder.Resource.WorkingDirectory);
+
+ var installerBuilder = builder.ApplicationBuilder.AddResource(installer)
+ .WithArgs("mod", "tidy")
+ .WithParentRelationship(builder.Resource)
+ .ExcludeFromManifest();
+
+ // Make the parent resource wait for the installer to complete
+ builder.WaitForCompletion(installerBuilder);
+
+ configureInstaller?.Invoke(installerBuilder);
+ }
+
+ return builder;
+ }
+
+ ///
+ /// Ensures Go module dependencies are downloaded before the application starts using go mod download.
+ ///
+ /// The Golang app resource builder.
+ /// Optional action to configure the installer resource.
+ /// A reference to the .
+ public static IResourceBuilder WithGoModDownload(
+ this IResourceBuilder builder,
+ Action>? configureInstaller = null)
+ {
+ ArgumentNullException.ThrowIfNull(builder, nameof(builder));
+
+ // Only install packages during development, not in publish mode
+ if (!builder.ApplicationBuilder.ExecutionContext.IsPublishMode)
+ {
+ var installerName = $"{builder.Resource.Name}-go-mod-download";
+ var installer = new GoModInstallerResource(installerName, builder.Resource.WorkingDirectory);
+
+ var installerBuilder = builder.ApplicationBuilder.AddResource(installer)
+ .WithArgs("mod", "download")
+ .WithParentRelationship(builder.Resource)
+ .ExcludeFromManifest();
+
+ // Make the parent resource wait for the installer to complete
+ builder.WaitForCompletion(installerBuilder);
+
+ configureInstaller?.Invoke(installerBuilder);
+ }
+
+ return builder;
+ }
}
\ No newline at end of file
diff --git a/src/CommunityToolkit.Aspire.Hosting.Golang/README.md b/src/CommunityToolkit.Aspire.Hosting.Golang/README.md
index e7de82f2..43416009 100644
--- a/src/CommunityToolkit.Aspire.Hosting.Golang/README.md
+++ b/src/CommunityToolkit.Aspire.Hosting.Golang/README.md
@@ -29,6 +29,45 @@ To have the Golang application listen on the correct port, you can use the follo
r.Run(":"+os.Getenv("PORT"))
```
+## Dependency Management
+
+The integration provides support for Go module dependency management using `go mod tidy` or `go mod download`.
+
+### Using `go mod tidy`
+
+To run `go mod tidy` before your application starts (to clean up and verify dependencies):
+
+```csharp
+var golang = builder.AddGolangApp("golang", "../gin-api")
+ .WithGoModTidy()
+ .WithHttpEndpoint(env: "PORT");
+```
+
+### Using `go mod download`
+
+To run `go mod download` before your application starts (to download dependencies without verification):
+
+```csharp
+var golang = builder.AddGolangApp("golang", "../gin-api")
+ .WithGoModDownload()
+ .WithHttpEndpoint(env: "PORT");
+```
+
+Both methods create an installer resource that runs before your application starts, ensuring dependencies are available. The installer resource appears as a child resource in the Aspire dashboard.
+
+You can also customize the installer resource using the optional `configureInstaller` parameter:
+
+```csharp
+var golang = builder.AddGolangApp("golang", "../gin-api")
+ .WithGoModTidy(configureInstaller: installer =>
+ {
+ installer.WithEnvironment("GOPROXY", "https://proxy.golang.org,direct");
+ })
+ .WithHttpEndpoint(env: "PORT");
+```
+
+> **Note:** The `WithGoModTidy` and `WithGoModDownload` methods only run during development. When publishing, the generated Dockerfile handles dependency management automatically.
+
## Publishing
When publishing your Aspire application, the Golang resource automatically generates a multi-stage Dockerfile for containerization. This means you don't need to manually create a Dockerfile for your Golang application.
diff --git a/tests/CommunityToolkit.Aspire.Hosting.Golang.Tests/ResourceCreationTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Golang.Tests/ResourceCreationTests.cs
index c0ed2d21..48aa310d 100644
--- a/tests/CommunityToolkit.Aspire.Hosting.Golang.Tests/ResourceCreationTests.cs
+++ b/tests/CommunityToolkit.Aspire.Hosting.Golang.Tests/ResourceCreationTests.cs
@@ -88,4 +88,98 @@ public async Task GolangAppWithExecutableAsync()
}
);
}
+
+ [Fact]
+ public void GolangAppWithGoModTidyCreatesInstallerResource()
+ {
+ var builder = DistributedApplication.CreateBuilder();
+
+ builder.AddGolangApp("golang", "../../examples/golang/gin-api").WithGoModTidy();
+
+ using var app = builder.Build();
+
+ var appModel = app.Services.GetRequiredService();
+
+ var golangResource = Assert.Single(appModel.Resources.OfType());
+ var installerResource = Assert.Single(appModel.Resources.OfType());
+
+ Assert.Equal("golang-go-mod-tidy", installerResource.Name);
+ Assert.Equal("go", installerResource.Command);
+ }
+
+ [Fact]
+ public async Task GolangAppWithGoModTidyHasCorrectArgsAsync()
+ {
+ var builder = DistributedApplication.CreateBuilder();
+
+ builder.AddGolangApp("golang", "../../examples/golang/gin-api").WithGoModTidy();
+
+ using var app = builder.Build();
+
+ var appModel = app.Services.GetRequiredService();
+
+ var installerResource = Assert.Single(appModel.Resources.OfType());
+
+ var args = await installerResource.GetArgumentValuesAsync();
+ Assert.Collection(
+ args,
+ arg => Assert.Equal("mod", arg),
+ arg => Assert.Equal("tidy", arg)
+ );
+ }
+
+ [Fact]
+ public void GolangAppWithGoModDownloadCreatesInstallerResource()
+ {
+ var builder = DistributedApplication.CreateBuilder();
+
+ builder.AddGolangApp("golang", "../../examples/golang/gin-api").WithGoModDownload();
+
+ using var app = builder.Build();
+
+ var appModel = app.Services.GetRequiredService();
+
+ var golangResource = Assert.Single(appModel.Resources.OfType());
+ var installerResource = Assert.Single(appModel.Resources.OfType());
+
+ Assert.Equal("golang-go-mod-download", installerResource.Name);
+ Assert.Equal("go", installerResource.Command);
+ }
+
+ [Fact]
+ public async Task GolangAppWithGoModDownloadHasCorrectArgsAsync()
+ {
+ var builder = DistributedApplication.CreateBuilder();
+
+ builder.AddGolangApp("golang", "../../examples/golang/gin-api").WithGoModDownload();
+
+ using var app = builder.Build();
+
+ var appModel = app.Services.GetRequiredService();
+
+ var installerResource = Assert.Single(appModel.Resources.OfType());
+
+ var args = await installerResource.GetArgumentValuesAsync();
+ Assert.Collection(
+ args,
+ arg => Assert.Equal("mod", arg),
+ arg => Assert.Equal("download", arg)
+ );
+ }
+
+ [Fact]
+ public void WithGoModTidyNullBuilderThrows()
+ {
+ IResourceBuilder builder = null!;
+
+ Assert.Throws(() => builder.WithGoModTidy());
+ }
+
+ [Fact]
+ public void WithGoModDownloadNullBuilderThrows()
+ {
+ IResourceBuilder builder = null!;
+
+ Assert.Throws(() => builder.WithGoModDownload());
+ }
}
\ No newline at end of file