From 265b4c1b1f0762faadd5f289a796f49e9b0c3541 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 14:21:47 +1100 Subject: [PATCH 01/26] feat(adminer): add Go AppHost example Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../actions/setup-runtimes-caching/action.yml | 2 +- docs/setup.md | 4 ++- .../apphost.go | 28 +++++++++++++++++++ .../aspire.config.json | 12 ++++++++ .../go.mod | 7 +++++ .../GoAppHostTests.cs | 17 +++++++++++ 6 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 examples/adminer/CommunityToolkit.Aspire.Hosting.Adminer.AppHost.Go/apphost.go create mode 100644 examples/adminer/CommunityToolkit.Aspire.Hosting.Adminer.AppHost.Go/aspire.config.json create mode 100644 examples/adminer/CommunityToolkit.Aspire.Hosting.Adminer.AppHost.Go/go.mod create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Adminer.Tests/GoAppHostTests.cs diff --git a/.github/actions/setup-runtimes-caching/action.yml b/.github/actions/setup-runtimes-caching/action.yml index 709b06e63..60c40de10 100644 --- a/.github/actions/setup-runtimes-caching/action.yml +++ b/.github/actions/setup-runtimes-caching/action.yml @@ -77,7 +77,7 @@ runs: - uses: actions/setup-go@v7 name: Set up Go with: - go-version: "^1.25.4" + go-version: "^1.26" cache-dependency-path: | ./examples/golang/gin-api/go.sum diff --git a/docs/setup.md b/docs/setup.md index ae90d947a..b2f32f282 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -22,7 +22,7 @@ If you prefer not to use `devcontainer`, you can manually set up your developmen - You'll also need [Gradle](https://gradle.org/install/) and [Apache Maven](https://maven.apache.org/download.cgi) - [Bun](https://bun.sh) - [Deno 2](https://deno.land/) -- [Go 1.23](https://golang.org/) +- [Go 1.26 or later](https://go.dev/dl/) (required for Go AppHosts) - [Python 3](https://www.python.org/downloads/) - [Uvicorn](https://www.uvicorn.org/) - [Rust](https://www.rust-lang.org/tools/install) @@ -40,3 +40,5 @@ The repository currently targets Aspire **17.1.0-preview.1.26559.8**, an unsuppo The C# AppHosts that host .NET projects now reference `Aspire.Hosting.Dotnet` and use `AddDotnetProject`; the TypeScript equivalents use `addDotnetProject`. `Aspire.Hosting.Dotnet` is centrally pinned to the same nightly version. The AppHosts still use `Aspire.AppHost.Sdk` for staged compatibility; Aspire 17 emits `ASPIRE012` for that obsolete SDK, and migrating to explicit `Aspire.Hosting.AppHost` references remains separate from this project-resource migration. Install the matching CLI with `aspire.dev`'s installer and its explicit version option (`-Version 17.1.0-preview.1.26559.8` on Windows or `--version 17.1.0-preview.1.26559.8` on Linux/macOS), rather than selecting a moving daily channel. See the [upstream daily-build guide](https://github.com/microsoft/aspire/blob/main/docs/using-latest-daily.md) for installer instructions. + +Go AppHosts are a preview feature. In a Go AppHost directory, run `aspire restore` to generate the SDK in `.aspire/modules`, then `go test ./...` to check compilation. Run the application with `aspire run`, rather than `go run`, so the CLI starts the AppHost server. diff --git a/examples/adminer/CommunityToolkit.Aspire.Hosting.Adminer.AppHost.Go/apphost.go b/examples/adminer/CommunityToolkit.Aspire.Hosting.Adminer.AppHost.Go/apphost.go new file mode 100644 index 000000000..5bb14ae9b --- /dev/null +++ b/examples/adminer/CommunityToolkit.Aspire.Hosting.Adminer.AppHost.Go/apphost.go @@ -0,0 +1,28 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + adminer := builder.AddAdminer("adminer", &aspire.AddAdminerOptions{ + Port: aspire.Float64Ptr(18080), + }).WithHostPort(aspire.Float64Ptr(18081)) + if err := adminer.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/adminer/CommunityToolkit.Aspire.Hosting.Adminer.AppHost.Go/aspire.config.json b/examples/adminer/CommunityToolkit.Aspire.Hosting.Adminer.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..44f0af1d6 --- /dev/null +++ b/examples/adminer/CommunityToolkit.Aspire.Hosting.Adminer.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Adminer": "../../../src/CommunityToolkit.Aspire.Hosting.Adminer/CommunityToolkit.Aspire.Hosting.Adminer.csproj" + } +} diff --git a/examples/adminer/CommunityToolkit.Aspire.Hosting.Adminer.AppHost.Go/go.mod b/examples/adminer/CommunityToolkit.Aspire.Hosting.Adminer.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/adminer/CommunityToolkit.Aspire.Hosting.Adminer.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/tests/CommunityToolkit.Aspire.Hosting.Adminer.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Adminer.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..c58c90e1f --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Adminer.Tests/GoAppHostTests.cs @@ -0,0 +1,17 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Adminer.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + string repoRoot = Path.GetFullPath(Path.Combine("..", "..", "..", "..", "..")); + string appHostDirectory = Path.Combine(repoRoot, "examples", "adminer", "CommunityToolkit.Aspire.Hosting.Adminer.AppHost.Go"); + CancellationToken cancellationToken = TestContext.Current.CancellationToken; + + await ProcessTestUtilities.RunProcessAsync("aspire", ["restore", "--apphost", "apphost.go", "--non-interactive"], appHostDirectory, cancellationToken); + await ProcessTestUtilities.RunProcessAsync("go", ["test", "./..."], appHostDirectory, cancellationToken); + } +} From 32756d4f4508a66a5351e83c7bb4f46d396af780 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 14:35:11 +1100 Subject: [PATCH 02/26] feat: add six Go AppHost examples Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- eng/testing/select-affected-tests.cs | 18 ++++++--- .../apphost.go | 33 +++++++++++++++++ .../aspire.config.json | 12 ++++++ .../go.mod | 7 ++++ .../apphost.go | 28 ++++++++++++++ .../aspire.config.json | 12 ++++++ .../go.mod | 7 ++++ .../apphost.go | 32 ++++++++++++++++ .../aspire.config.json | 12 ++++++ .../go.mod | 7 ++++ .../apphost.go | 36 ++++++++++++++++++ .../aspire.config.json | 12 ++++++ .../go.mod | 7 ++++ .../seaweedfs/SeaweedFS.AppHost.Go/apphost.go | 34 +++++++++++++++++ .../SeaweedFS.AppHost.Go/aspire.config.json | 12 ++++++ .../seaweedfs/SeaweedFS.AppHost.Go/go.mod | 7 ++++ .../apphost.go | 37 +++++++++++++++++++ .../aspire.config.json | 12 ++++++ .../go.mod | 7 ++++ .../GoAppHostTests.cs | 10 ++--- .../GoAppHostTests.cs | 15 ++++++++ .../GoAppHostTests.cs | 15 ++++++++ .../GoAppHostTests.cs | 15 ++++++++ .../GoAppHostTests.cs | 15 ++++++++ .../GoAppHostTests.cs | 15 ++++++++ .../GoAppHostTests.cs | 15 ++++++++ .../GoAppHostTest.cs | 25 +++++++++++++ 27 files changed, 446 insertions(+), 11 deletions(-) create mode 100644 examples/chromadb/CommunityToolkit.Aspire.Hosting.Chroma.AppHost.Go/apphost.go create mode 100644 examples/chromadb/CommunityToolkit.Aspire.Hosting.Chroma.AppHost.Go/aspire.config.json create mode 100644 examples/chromadb/CommunityToolkit.Aspire.Hosting.Chroma.AppHost.Go/go.mod create mode 100644 examples/dbx/CommunityToolkit.Aspire.Hosting.Dbx.AppHost.Go/apphost.go create mode 100644 examples/dbx/CommunityToolkit.Aspire.Hosting.Dbx.AppHost.Go/aspire.config.json create mode 100644 examples/dbx/CommunityToolkit.Aspire.Hosting.Dbx.AppHost.Go/go.mod create mode 100644 examples/mosquitto/CommunityToolkit.Aspire.Hosting.Mosquitto.AppHost.Go/apphost.go create mode 100644 examples/mosquitto/CommunityToolkit.Aspire.Hosting.Mosquitto.AppHost.Go/aspire.config.json create mode 100644 examples/mosquitto/CommunityToolkit.Aspire.Hosting.Mosquitto.AppHost.Go/go.mod create mode 100644 examples/redpanda/CommunityToolkit.Aspire.Hosting.RedPanda.AppHost.Go/apphost.go create mode 100644 examples/redpanda/CommunityToolkit.Aspire.Hosting.RedPanda.AppHost.Go/aspire.config.json create mode 100644 examples/redpanda/CommunityToolkit.Aspire.Hosting.RedPanda.AppHost.Go/go.mod create mode 100644 examples/seaweedfs/SeaweedFS.AppHost.Go/apphost.go create mode 100644 examples/seaweedfs/SeaweedFS.AppHost.Go/aspire.config.json create mode 100644 examples/seaweedfs/SeaweedFS.AppHost.Go/go.mod create mode 100644 examples/sqlite/CommunityToolkit.Aspire.Hosting.Sqlite.AppHost.Go/apphost.go create mode 100644 examples/sqlite/CommunityToolkit.Aspire.Hosting.Sqlite.AppHost.Go/aspire.config.json create mode 100644 examples/sqlite/CommunityToolkit.Aspire.Hosting.Sqlite.AppHost.Go/go.mod create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Chroma.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Dbx.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Mosquitto.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.RedPanda.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.SeaweedFS.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Sqlite.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Testing/GoAppHostTest.cs diff --git a/eng/testing/select-affected-tests.cs b/eng/testing/select-affected-tests.cs index ed1defea3..87d54bb6a 100644 --- a/eng/testing/select-affected-tests.cs +++ b/eng/testing/select-affected-tests.cs @@ -57,7 +57,8 @@ public static async Task MainAsync(string[] args) var packageToTests = BuildPackageToTests(packageRefs, nodeToTests); var packageToProjects = BuildPackageToProjects(packageRefs); var testInfraPackages = LoadTestInfraPackages(repoRoot); - var typeScriptAppHostToTests = BuildTypeScriptAppHostToTests(repoRoot, projectPaths); + var typeScriptAppHostToTests = BuildAppHostToTests(repoRoot, projectPaths, "TypeScriptAppHostTests.cs"); + var goAppHostToTests = BuildAppHostToTests(repoRoot, projectPaths, "GoAppHostTests.cs"); foreach (var filePath in diffFiles) { @@ -165,13 +166,20 @@ public static async Task MainAsync(string[] args) continue; } - if (TryGetTypeScriptAppHostTests(filePath, typeScriptAppHostToTests, out impactedTypeScriptTests)) + if (TryGetAppHostTests(filePath, typeScriptAppHostToTests, out impactedTypeScriptTests)) { selected.UnionWith(impactedTypeScriptTests); reasons.Add($"Selected {impactedTypeScriptTests.Count} tests because {filePath} belongs to a TypeScript app host test fixture."); continue; } + if (TryGetAppHostTests(filePath, goAppHostToTests, out var impactedGoTests)) + { + selected.UnionWith(impactedGoTests); + reasons.Add($"Selected {impactedGoTests.Count} tests because {filePath} belongs to a Go app host test fixture."); + continue; + } + if (TryGetExampleTests(filePath, projectPaths, nodeToTests, out var exampleName, out var impactedExampleTests)) { selected.UnionWith(impactedExampleTests); @@ -521,7 +529,7 @@ private static Dictionary> BuildPackageToProjects(Dictio return appHostProjects.Count == 1 ? appHostProjects[0] : null; } - private static Dictionary> BuildTypeScriptAppHostToTests(string repoRoot, List projectPaths) + private static Dictionary> BuildAppHostToTests(string repoRoot, List projectPaths, string testFileName) { var appHostToTests = new Dictionary>(StringComparer.Ordinal); var testsRoot = Path.Combine(repoRoot, "tests"); @@ -530,7 +538,7 @@ private static Dictionary> BuildTypeScriptAppHostToTests return appHostToTests; } - foreach (var testFilePath in Directory.EnumerateFiles(testsRoot, "TypeScriptAppHostTests.cs", SearchOption.AllDirectories).OrderBy(static path => path, StringComparer.Ordinal)) + foreach (var testFilePath in Directory.EnumerateFiles(testsRoot, testFileName, SearchOption.AllDirectories).OrderBy(static path => path, StringComparer.Ordinal)) { var relativeTestFilePath = RelativePath(repoRoot, testFilePath); var testProjectPath = NearestProject(relativeTestFilePath, projectPaths); @@ -560,7 +568,7 @@ private static Dictionary> BuildTypeScriptAppHostToTests return appHostToTests; } - private static bool TryGetTypeScriptAppHostTests( + private static bool TryGetAppHostTests( string filePath, Dictionary> appHostToTests, out HashSet tests) diff --git a/examples/chromadb/CommunityToolkit.Aspire.Hosting.Chroma.AppHost.Go/apphost.go b/examples/chromadb/CommunityToolkit.Aspire.Hosting.Chroma.AppHost.Go/apphost.go new file mode 100644 index 000000000..d0bf7058c --- /dev/null +++ b/examples/chromadb/CommunityToolkit.Aspire.Hosting.Chroma.AppHost.Go/apphost.go @@ -0,0 +1,33 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + chroma := builder.AddChroma("chroma") + chroma.WithHostPort(aspire.Float64Ptr(18080)) + chroma.WithDataVolume(&aspire.WithDataVolumeOptions{Name: aspire.StringPtr("chroma-data")}) + _ = chroma.PrimaryEndpoint() + _ = chroma.Host() + _ = chroma.Port() + _ = chroma.ConnectionStringExpression() + _ = chroma.UriExpression() + if err := chroma.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/chromadb/CommunityToolkit.Aspire.Hosting.Chroma.AppHost.Go/aspire.config.json b/examples/chromadb/CommunityToolkit.Aspire.Hosting.Chroma.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..c2f0485e1 --- /dev/null +++ b/examples/chromadb/CommunityToolkit.Aspire.Hosting.Chroma.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Chroma": "../../../src/CommunityToolkit.Aspire.Hosting.Chroma/CommunityToolkit.Aspire.Hosting.Chroma.csproj" + } +} diff --git a/examples/chromadb/CommunityToolkit.Aspire.Hosting.Chroma.AppHost.Go/go.mod b/examples/chromadb/CommunityToolkit.Aspire.Hosting.Chroma.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/chromadb/CommunityToolkit.Aspire.Hosting.Chroma.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/dbx/CommunityToolkit.Aspire.Hosting.Dbx.AppHost.Go/apphost.go b/examples/dbx/CommunityToolkit.Aspire.Hosting.Dbx.AppHost.Go/apphost.go new file mode 100644 index 000000000..f355f2f9b --- /dev/null +++ b/examples/dbx/CommunityToolkit.Aspire.Hosting.Dbx.AppHost.Go/apphost.go @@ -0,0 +1,28 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + dbx := builder.AddDbx(&aspire.AddDbxOptions{Name: aspire.StringPtr("dbx")}) + dbx.WithHostPort(aspire.Float64Ptr(3310)) + _ = dbx.PrimaryEndpoint() + if err := dbx.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/dbx/CommunityToolkit.Aspire.Hosting.Dbx.AppHost.Go/aspire.config.json b/examples/dbx/CommunityToolkit.Aspire.Hosting.Dbx.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..e95d2c910 --- /dev/null +++ b/examples/dbx/CommunityToolkit.Aspire.Hosting.Dbx.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Dbx": "../../../src/CommunityToolkit.Aspire.Hosting.Dbx/CommunityToolkit.Aspire.Hosting.Dbx.csproj" + } +} diff --git a/examples/dbx/CommunityToolkit.Aspire.Hosting.Dbx.AppHost.Go/go.mod b/examples/dbx/CommunityToolkit.Aspire.Hosting.Dbx.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/dbx/CommunityToolkit.Aspire.Hosting.Dbx.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/mosquitto/CommunityToolkit.Aspire.Hosting.Mosquitto.AppHost.Go/apphost.go b/examples/mosquitto/CommunityToolkit.Aspire.Hosting.Mosquitto.AppHost.Go/apphost.go new file mode 100644 index 000000000..faef22501 --- /dev/null +++ b/examples/mosquitto/CommunityToolkit.Aspire.Hosting.Mosquitto.AppHost.Go/apphost.go @@ -0,0 +1,32 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + mqtt := builder.AddMosquitto("mqtt") + mqtt.WithDataVolume(&aspire.WithDataVolumeOptions{Name: aspire.StringPtr("mosquitto-data")}) + _ = mqtt.PrimaryEndpoint() + _ = mqtt.Host() + _ = mqtt.Port() + _ = mqtt.ConnectionStringExpression() + _ = mqtt.UriExpression() + if err := mqtt.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/mosquitto/CommunityToolkit.Aspire.Hosting.Mosquitto.AppHost.Go/aspire.config.json b/examples/mosquitto/CommunityToolkit.Aspire.Hosting.Mosquitto.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..079948828 --- /dev/null +++ b/examples/mosquitto/CommunityToolkit.Aspire.Hosting.Mosquitto.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Mosquitto": "../../../src/CommunityToolkit.Aspire.Hosting.Mosquitto/CommunityToolkit.Aspire.Hosting.Mosquitto.csproj" + } +} diff --git a/examples/mosquitto/CommunityToolkit.Aspire.Hosting.Mosquitto.AppHost.Go/go.mod b/examples/mosquitto/CommunityToolkit.Aspire.Hosting.Mosquitto.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/mosquitto/CommunityToolkit.Aspire.Hosting.Mosquitto.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/redpanda/CommunityToolkit.Aspire.Hosting.RedPanda.AppHost.Go/apphost.go b/examples/redpanda/CommunityToolkit.Aspire.Hosting.RedPanda.AppHost.Go/apphost.go new file mode 100644 index 000000000..a6c604035 --- /dev/null +++ b/examples/redpanda/CommunityToolkit.Aspire.Hosting.RedPanda.AppHost.Go/apphost.go @@ -0,0 +1,36 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + redpanda := builder.AddRedPanda("redpanda") + redpanda.WithDataVolume(&aspire.WithDataVolumeOptions{Name: aspire.StringPtr("redpanda-data")}) + _ = redpanda.PrimaryEndpoint() + _ = redpanda.Host() + _ = redpanda.Port() + _ = redpanda.ConnectionStringExpression() + if err := redpanda.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + pinned := builder.AddRedPanda("redpanda-pinned", &aspire.AddRedPandaOptions{Port: aspire.Float64Ptr(9092)}) + if err := pinned.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/redpanda/CommunityToolkit.Aspire.Hosting.RedPanda.AppHost.Go/aspire.config.json b/examples/redpanda/CommunityToolkit.Aspire.Hosting.RedPanda.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..12bfa3db7 --- /dev/null +++ b/examples/redpanda/CommunityToolkit.Aspire.Hosting.RedPanda.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.RedPanda": "../../../src/CommunityToolkit.Aspire.Hosting.RedPanda/CommunityToolkit.Aspire.Hosting.RedPanda.csproj" + } +} diff --git a/examples/redpanda/CommunityToolkit.Aspire.Hosting.RedPanda.AppHost.Go/go.mod b/examples/redpanda/CommunityToolkit.Aspire.Hosting.RedPanda.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/redpanda/CommunityToolkit.Aspire.Hosting.RedPanda.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/seaweedfs/SeaweedFS.AppHost.Go/apphost.go b/examples/seaweedfs/SeaweedFS.AppHost.Go/apphost.go new file mode 100644 index 000000000..7de8eabee --- /dev/null +++ b/examples/seaweedfs/SeaweedFS.AppHost.Go/apphost.go @@ -0,0 +1,34 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + s3 := builder.AddSeaweedFS("typescript-seaweedfs-s3") + s3.WithS3() + s3.WithDataVolume() + if err := s3.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + filer := builder.AddSeaweedFS("typescript-seaweedfs-filer") + filer.WithFiler() + if err := filer.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/seaweedfs/SeaweedFS.AppHost.Go/aspire.config.json b/examples/seaweedfs/SeaweedFS.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..db3b66b1c --- /dev/null +++ b/examples/seaweedfs/SeaweedFS.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.SeaweedFS": "../../../src/CommunityToolkit.Aspire.Hosting.SeaweedFS/CommunityToolkit.Aspire.Hosting.SeaweedFS.csproj" + } +} diff --git a/examples/seaweedfs/SeaweedFS.AppHost.Go/go.mod b/examples/seaweedfs/SeaweedFS.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/seaweedfs/SeaweedFS.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/sqlite/CommunityToolkit.Aspire.Hosting.Sqlite.AppHost.Go/apphost.go b/examples/sqlite/CommunityToolkit.Aspire.Hosting.Sqlite.AppHost.Go/apphost.go new file mode 100644 index 000000000..1d7b56811 --- /dev/null +++ b/examples/sqlite/CommunityToolkit.Aspire.Hosting.Sqlite.AppHost.Go/apphost.go @@ -0,0 +1,37 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + sqlite := builder.AddSqlite("sqlite", &aspire.AddSqliteOptions{ + DatabasePath: aspire.StringPtr(".data/sqlite"), + DatabaseFileName: aspire.StringPtr("app.db"), + }) + sqlite.WithSqliteWeb(&aspire.WithSqliteWebOptions{ContainerName: aspire.StringPtr("sqlite-browser")}) + _ = sqlite.ConnectionStringExpression() + if err := sqlite.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + sqliteDefault := builder.AddSqlite("sqlite-default") + sqliteDefault.WithSqliteWeb() + if err := sqliteDefault.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/sqlite/CommunityToolkit.Aspire.Hosting.Sqlite.AppHost.Go/aspire.config.json b/examples/sqlite/CommunityToolkit.Aspire.Hosting.Sqlite.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..3213f77ae --- /dev/null +++ b/examples/sqlite/CommunityToolkit.Aspire.Hosting.Sqlite.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Sqlite": "../../../src/CommunityToolkit.Aspire.Hosting.Sqlite/CommunityToolkit.Aspire.Hosting.Sqlite.csproj" + } +} diff --git a/examples/sqlite/CommunityToolkit.Aspire.Hosting.Sqlite.AppHost.Go/go.mod b/examples/sqlite/CommunityToolkit.Aspire.Hosting.Sqlite.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/sqlite/CommunityToolkit.Aspire.Hosting.Sqlite.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/tests/CommunityToolkit.Aspire.Hosting.Adminer.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Adminer.Tests/GoAppHostTests.cs index c58c90e1f..0b9cbef0e 100644 --- a/tests/CommunityToolkit.Aspire.Hosting.Adminer.Tests/GoAppHostTests.cs +++ b/tests/CommunityToolkit.Aspire.Hosting.Adminer.Tests/GoAppHostTests.cs @@ -7,11 +7,9 @@ public class GoAppHostTests [Fact] public async Task GoAppHostCompiles() { - string repoRoot = Path.GetFullPath(Path.Combine("..", "..", "..", "..", "..")); - string appHostDirectory = Path.Combine(repoRoot, "examples", "adminer", "CommunityToolkit.Aspire.Hosting.Adminer.AppHost.Go"); - CancellationToken cancellationToken = TestContext.Current.CancellationToken; - - await ProcessTestUtilities.RunProcessAsync("aspire", ["restore", "--apphost", "apphost.go", "--non-interactive"], appHostDirectory, cancellationToken); - await ProcessTestUtilities.RunProcessAsync("go", ["test", "./..."], appHostDirectory, cancellationToken); + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Adminer.AppHost.Go", + exampleName: "adminer", + cancellationToken: TestContext.Current.CancellationToken); } } diff --git a/tests/CommunityToolkit.Aspire.Hosting.Chroma.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Chroma.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..08425ef5d --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Chroma.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Chroma.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Chroma.AppHost.Go", + exampleName: "chromadb", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Dbx.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Dbx.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..344f18d3d --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Dbx.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Dbx.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Dbx.AppHost.Go", + exampleName: "dbx", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Mosquitto.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Mosquitto.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..4f886b854 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Mosquitto.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Mosquitto.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Mosquitto.AppHost.Go", + exampleName: "mosquitto", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.RedPanda.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.RedPanda.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..b08f0111d --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.RedPanda.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.RedPanda.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.RedPanda.AppHost.Go", + exampleName: "redpanda", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.SeaweedFS.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.SeaweedFS.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..a8ed9cd02 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.SeaweedFS.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.SeaweedFS.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "SeaweedFS.AppHost.Go", + exampleName: "seaweedfs", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Sqlite.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Sqlite.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..0432d3557 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Sqlite.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Sqlite.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Sqlite.AppHost.Go", + exampleName: "sqlite", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Testing/GoAppHostTest.cs b/tests/CommunityToolkit.Aspire.Testing/GoAppHostTest.cs new file mode 100644 index 000000000..e8f3f49ba --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Testing/GoAppHostTest.cs @@ -0,0 +1,25 @@ +namespace CommunityToolkit.Aspire.Testing; + +/// +/// Runs the Go AppHost SDK generation, compilation, and model checks. +/// +public static class GoAppHostTest +{ + /// + /// Restores, compiles, and builds the model for an example Go AppHost. + /// + /// The AppHost directory name under the example folder. + /// The example folder name. + /// The cancellation token. + public static async Task Run(string appHostProject, string exampleName, CancellationToken cancellationToken = default) + { + ArgumentException.ThrowIfNullOrWhiteSpace(appHostProject); + ArgumentException.ThrowIfNullOrWhiteSpace(exampleName); + + string repoRoot = Path.GetFullPath(Path.Combine("..", "..", "..", "..", "..")); + string appHostDirectory = Path.Combine(repoRoot, "examples", exampleName, appHostProject); + await ProcessTestUtilities.RunProcessAsync("aspire", ["restore", "--apphost", "apphost.go", "--non-interactive"], appHostDirectory, cancellationToken); + await ProcessTestUtilities.RunProcessAsync("go", ["test", "./..."], appHostDirectory, cancellationToken); + await ProcessTestUtilities.RunProcessAsync("aspire", ["publish", "--apphost", "apphost.go", "--list-steps", "--non-interactive"], appHostDirectory, cancellationToken); + } +} From 0c8803c3cb9f8198ac2d724d659884f499042c17 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 14:48:57 +1100 Subject: [PATCH 03/26] feat: add five more Go AppHost examples Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- docs/go-apphost-parity.md | 20 ++++++++ .../apphost.go | 31 ++++++++++++ .../aspire.config.json | 13 +++++ .../go.mod | 7 +++ .../apphost.go | 33 +++++++++++++ .../aspire.config.json | 12 +++++ .../go.mod | 7 +++ .../apphost.go | 49 +++++++++++++++++++ .../aspire.config.json | 12 +++++ .../go.mod | 7 +++ .../apphost.go | 36 ++++++++++++++ .../aspire.config.json | 12 +++++ .../go.mod | 7 +++ .../apphost.go | 27 ++++++++++ .../aspire.config.json | 12 +++++ .../go.mod | 7 +++ .../GoAppHostTests.cs | 15 ++++++ .../GoAppHostTests.cs | 15 ++++++ .../GoAppHostTests.cs | 15 ++++++ .../GoAppHostTests.cs | 15 ++++++ .../GoAppHostTests.cs | 16 ++++++ .../GoAppHostTest.cs | 8 ++- 22 files changed, 374 insertions(+), 2 deletions(-) create mode 100644 docs/go-apphost-parity.md create mode 100644 examples/duckdb/CommunityToolkit.Aspire.DuckDB.AppHost.Go/apphost.go create mode 100644 examples/duckdb/CommunityToolkit.Aspire.DuckDB.AppHost.Go/aspire.config.json create mode 100644 examples/duckdb/CommunityToolkit.Aspire.DuckDB.AppHost.Go/go.mod create mode 100644 examples/flyway/CommunityToolkit.Aspire.Hosting.Flyway.AppHost.Go/apphost.go create mode 100644 examples/flyway/CommunityToolkit.Aspire.Hosting.Flyway.AppHost.Go/aspire.config.json create mode 100644 examples/flyway/CommunityToolkit.Aspire.Hosting.Flyway.AppHost.Go/go.mod create mode 100644 examples/ollama/CommunityToolkit.Aspire.Hosting.Ollama.AppHost.Go/apphost.go create mode 100644 examples/ollama/CommunityToolkit.Aspire.Hosting.Ollama.AppHost.Go/aspire.config.json create mode 100644 examples/ollama/CommunityToolkit.Aspire.Hosting.Ollama.AppHost.Go/go.mod create mode 100644 examples/opentelemetry-collector/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.AppHost.Go/apphost.go create mode 100644 examples/opentelemetry-collector/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.AppHost.Go/aspire.config.json create mode 100644 examples/opentelemetry-collector/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.AppHost.Go/go.mod create mode 100644 examples/python/CommunityToolkit.Aspire.Hosting.Python.Extensions.AppHost.Go/apphost.go create mode 100644 examples/python/CommunityToolkit.Aspire.Hosting.Python.Extensions.AppHost.Go/aspire.config.json create mode 100644 examples/python/CommunityToolkit.Aspire.Hosting.Python.Extensions.AppHost.Go/go.mod create mode 100644 tests/CommunityToolkit.Aspire.Hosting.DuckDB.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Flyway.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Ollama.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Python.Extensions.Tests/GoAppHostTests.cs diff --git a/docs/go-apphost-parity.md b/docs/go-apphost-parity.md new file mode 100644 index 000000000..8c8038d9f --- /dev/null +++ b/docs/go-apphost-parity.md @@ -0,0 +1,20 @@ +# Go AppHost parity gaps + +These TypeScript examples cannot currently be translated faithfully using the +repository's pinned Aspire SDK (`17.1.0-preview.1.26559.8`): + +- **RustFs:** `AddRustFsOptions.AccessKey` and `SecretKey` are generated as + `*aspire.ParameterResource` (pointers to interfaces). Passing addresses of + the two parameter resources compiles, but the generated `serializeValue` + does not recognize those pointers as resource handles. During AppHost model + construction, `addRustFs` fails with `INVALID_ARGUMENT`: `Parameter type + 'IResourceBuilder\`1' must have [AspireDto] attribute to be deserialized from + JSON` for `accessKey`. The TypeScript example explicitly supplies both + credentials, so omitting them would not preserve its resource graph. + +The Python extensions Go example runs, but its publish-mode model fails: +`AddStreamlitApp` removes the Python resource before adding its Streamlit +replacement, while the publish-mode executable container with the same name +remains in the model. The resulting duplicate `dashboard` resource causes +`AddStreamlitApp` to fail. Its Go compilation test therefore does not run the +publish-model check; fixing the integration is outside this example-only work. diff --git a/examples/duckdb/CommunityToolkit.Aspire.DuckDB.AppHost.Go/apphost.go b/examples/duckdb/CommunityToolkit.Aspire.DuckDB.AppHost.Go/apphost.go new file mode 100644 index 000000000..03aff6d98 --- /dev/null +++ b/examples/duckdb/CommunityToolkit.Aspire.DuckDB.AppHost.Go/apphost.go @@ -0,0 +1,31 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + duckdb := builder.AddDuckDB("analytics") + if err := duckdb.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + api := builder.AddDotnetProject("api", "../CommunityToolkit.Aspire.DuckDB.Api/CommunityToolkit.Aspire.DuckDB.Api.csproj") + api.WithReference(duckdb) + if err := api.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/duckdb/CommunityToolkit.Aspire.DuckDB.AppHost.Go/aspire.config.json b/examples/duckdb/CommunityToolkit.Aspire.DuckDB.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..276f3357e --- /dev/null +++ b/examples/duckdb/CommunityToolkit.Aspire.DuckDB.AppHost.Go/aspire.config.json @@ -0,0 +1,13 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.DuckDB": "../../../src/CommunityToolkit.Aspire.Hosting.DuckDB/CommunityToolkit.Aspire.Hosting.DuckDB.csproj", + "Aspire.Hosting.Dotnet": "17.1.0-preview.1.26559.8" + } +} diff --git a/examples/duckdb/CommunityToolkit.Aspire.DuckDB.AppHost.Go/go.mod b/examples/duckdb/CommunityToolkit.Aspire.DuckDB.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/duckdb/CommunityToolkit.Aspire.DuckDB.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/flyway/CommunityToolkit.Aspire.Hosting.Flyway.AppHost.Go/apphost.go b/examples/flyway/CommunityToolkit.Aspire.Hosting.Flyway.AppHost.Go/apphost.go new file mode 100644 index 000000000..59be212d1 --- /dev/null +++ b/examples/flyway/CommunityToolkit.Aspire.Hosting.Flyway.AppHost.Go/apphost.go @@ -0,0 +1,33 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + flyway := builder.AddFlyway("flyway", "../CommunityToolkit.Aspire.Hosting.Flyway.AppHost.TypeScript/migrations") + flyway.WithArgs([]string{"-v"}) + if err := flyway.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + telemetry := builder.AddFlyway("flyway-telemetry", "../CommunityToolkit.Aspire.Hosting.Flyway.AppHost.TypeScript/migrations") + telemetry.WithTelemetryOptIn().WithArgs([]string{"-v"}) + if err := telemetry.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/flyway/CommunityToolkit.Aspire.Hosting.Flyway.AppHost.Go/aspire.config.json b/examples/flyway/CommunityToolkit.Aspire.Hosting.Flyway.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..c67697a9d --- /dev/null +++ b/examples/flyway/CommunityToolkit.Aspire.Hosting.Flyway.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Flyway": "../../../src/CommunityToolkit.Aspire.Hosting.Flyway/CommunityToolkit.Aspire.Hosting.Flyway.csproj" + } +} diff --git a/examples/flyway/CommunityToolkit.Aspire.Hosting.Flyway.AppHost.Go/go.mod b/examples/flyway/CommunityToolkit.Aspire.Hosting.Flyway.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/flyway/CommunityToolkit.Aspire.Hosting.Flyway.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/ollama/CommunityToolkit.Aspire.Hosting.Ollama.AppHost.Go/apphost.go b/examples/ollama/CommunityToolkit.Aspire.Hosting.Ollama.AppHost.Go/apphost.go new file mode 100644 index 000000000..bcd700180 --- /dev/null +++ b/examples/ollama/CommunityToolkit.Aspire.Hosting.Ollama.AppHost.Go/apphost.go @@ -0,0 +1,49 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + ollama := builder.AddOllama("ollama") + ollama.WithOpenWebUI() + phi3 := ollama.AddNamedModel("phi3", "phi3") + llama := ollama.AddHuggingFaceModel("llama", "bartowski/Llama-3.2-1B-Instruct-GGUF:IQ4_XS") + if err := ollama.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + _ = phi3.ConnectionStringExpression() + if err := phi3.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + _ = llama.ConnectionStringExpression() + if err := llama.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + ollama2 := builder.AddOllama("ollama2") + ollama2.WithDataVolume() + ollama2.WithOpenWebUI() + tinyllama := ollama2.AddNamedModel("tinyllama", "tinyllama") + if err := ollama2.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + _ = tinyllama.ConnectionStringExpression() + if err := tinyllama.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/ollama/CommunityToolkit.Aspire.Hosting.Ollama.AppHost.Go/aspire.config.json b/examples/ollama/CommunityToolkit.Aspire.Hosting.Ollama.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..bf58e8d5e --- /dev/null +++ b/examples/ollama/CommunityToolkit.Aspire.Hosting.Ollama.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Ollama": "../../../src/CommunityToolkit.Aspire.Hosting.Ollama/CommunityToolkit.Aspire.Hosting.Ollama.csproj" + } +} diff --git a/examples/ollama/CommunityToolkit.Aspire.Hosting.Ollama.AppHost.Go/go.mod b/examples/ollama/CommunityToolkit.Aspire.Hosting.Ollama.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/ollama/CommunityToolkit.Aspire.Hosting.Ollama.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/opentelemetry-collector/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.AppHost.Go/apphost.go b/examples/opentelemetry-collector/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.AppHost.Go/apphost.go new file mode 100644 index 000000000..a908bca44 --- /dev/null +++ b/examples/opentelemetry-collector/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.AppHost.Go/apphost.go @@ -0,0 +1,36 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + collector := builder.AddOpenTelemetryCollector("collector") + collector.WithConfig("../CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.AppHost.TypeScript/otel-config.yaml") + collector.WithAppForwarding() + grpc := collector.GrpcEndpoint() + http := collector.HttpEndpoint() + if err := collector.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + if _, err := grpc.EndpointName(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + if _, err := http.EndpointName(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/opentelemetry-collector/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.AppHost.Go/aspire.config.json b/examples/opentelemetry-collector/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..5d26ec59e --- /dev/null +++ b/examples/opentelemetry-collector/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector": "../../../src/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.csproj" + } +} diff --git a/examples/opentelemetry-collector/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.AppHost.Go/go.mod b/examples/opentelemetry-collector/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/opentelemetry-collector/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/python/CommunityToolkit.Aspire.Hosting.Python.Extensions.AppHost.Go/apphost.go b/examples/python/CommunityToolkit.Aspire.Hosting.Python.Extensions.AppHost.Go/apphost.go new file mode 100644 index 000000000..d2b336f41 --- /dev/null +++ b/examples/python/CommunityToolkit.Aspire.Hosting.Python.Extensions.AppHost.Go/apphost.go @@ -0,0 +1,27 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + dashboard := builder.AddStreamlitApp("dashboard", "../CommunityToolkit.Aspire.Hosting.Python.Extensions.AppHost.TypeScript/streamlit-app", "app.py") + dashboard.WithExplicitStart() + if err := dashboard.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/python/CommunityToolkit.Aspire.Hosting.Python.Extensions.AppHost.Go/aspire.config.json b/examples/python/CommunityToolkit.Aspire.Hosting.Python.Extensions.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..128d29f41 --- /dev/null +++ b/examples/python/CommunityToolkit.Aspire.Hosting.Python.Extensions.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Python.Extensions": "../../../src/CommunityToolkit.Aspire.Hosting.Python.Extensions/CommunityToolkit.Aspire.Hosting.Python.Extensions.csproj" + } +} diff --git a/examples/python/CommunityToolkit.Aspire.Hosting.Python.Extensions.AppHost.Go/go.mod b/examples/python/CommunityToolkit.Aspire.Hosting.Python.Extensions.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/python/CommunityToolkit.Aspire.Hosting.Python.Extensions.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/tests/CommunityToolkit.Aspire.Hosting.DuckDB.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.DuckDB.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..ce68e53cc --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.DuckDB.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.DuckDB.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.DuckDB.AppHost.Go", + exampleName: "duckdb", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Flyway.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Flyway.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..573e4ff4d --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Flyway.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Flyway.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Flyway.AppHost.Go", + exampleName: "flyway", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Ollama.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Ollama.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..33b8c65ad --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Ollama.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Ollama.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Ollama.AppHost.Go", + exampleName: "ollama", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..53022829f --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.AppHost.Go", + exampleName: "opentelemetry-collector", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Python.Extensions.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Python.Extensions.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..87157412d --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Python.Extensions.Tests/GoAppHostTests.cs @@ -0,0 +1,16 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Python.Extensions.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Python.Extensions.AppHost.Go", + exampleName: "python", + validatePublish: false, + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Testing/GoAppHostTest.cs b/tests/CommunityToolkit.Aspire.Testing/GoAppHostTest.cs index e8f3f49ba..6bdfff3dd 100644 --- a/tests/CommunityToolkit.Aspire.Testing/GoAppHostTest.cs +++ b/tests/CommunityToolkit.Aspire.Testing/GoAppHostTest.cs @@ -10,8 +10,9 @@ public static class GoAppHostTest /// /// The AppHost directory name under the example folder. /// The example folder name. + /// Whether to validate the publish-mode model. /// The cancellation token. - public static async Task Run(string appHostProject, string exampleName, CancellationToken cancellationToken = default) + public static async Task Run(string appHostProject, string exampleName, bool validatePublish = true, CancellationToken cancellationToken = default) { ArgumentException.ThrowIfNullOrWhiteSpace(appHostProject); ArgumentException.ThrowIfNullOrWhiteSpace(exampleName); @@ -20,6 +21,9 @@ public static async Task Run(string appHostProject, string exampleName, Cancella string appHostDirectory = Path.Combine(repoRoot, "examples", exampleName, appHostProject); await ProcessTestUtilities.RunProcessAsync("aspire", ["restore", "--apphost", "apphost.go", "--non-interactive"], appHostDirectory, cancellationToken); await ProcessTestUtilities.RunProcessAsync("go", ["test", "./..."], appHostDirectory, cancellationToken); - await ProcessTestUtilities.RunProcessAsync("aspire", ["publish", "--apphost", "apphost.go", "--list-steps", "--non-interactive"], appHostDirectory, cancellationToken); + if (validatePublish) + { + await ProcessTestUtilities.RunProcessAsync("aspire", ["publish", "--apphost", "apphost.go", "--list-steps", "--non-interactive"], appHostDirectory, cancellationToken); + } } } From ccce2b9b4675c301deff4c02f0d271a11f3f3333 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 14:53:10 +1100 Subject: [PATCH 04/26] feat: add Go messaging AppHost examples Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../apphost.go | 49 ++++++++++++++++++ .../aspire.config.json | 12 +++++ .../go.mod | 7 +++ .../apphost.go | 51 +++++++++++++++++++ .../aspire.config.json | 12 +++++ .../go.mod | 7 +++ .../apphost.go | 42 +++++++++++++++ .../aspire.config.json | 12 +++++ .../go.mod | 7 +++ .../GoAppHostTests.cs | 15 ++++++ .../GoAppHostTests.cs | 15 ++++++ .../GoAppHostTests.cs | 15 ++++++ 12 files changed, 244 insertions(+) create mode 100644 examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost.Go/apphost.go create mode 100644 examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost.Go/aspire.config.json create mode 100644 examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost.Go/go.mod create mode 100644 examples/mailpit/CommunityToolkit.Aspire.Hosting.MailPit.AppHost.Go/apphost.go create mode 100644 examples/mailpit/CommunityToolkit.Aspire.Hosting.MailPit.AppHost.Go/aspire.config.json create mode 100644 examples/mailpit/CommunityToolkit.Aspire.Hosting.MailPit.AppHost.Go/go.mod create mode 100644 examples/papercut/CommunityToolkit.Aspire.Hosting.PapercutSmtp.AppHost.Go/apphost.go create mode 100644 examples/papercut/CommunityToolkit.Aspire.Hosting.PapercutSmtp.AppHost.Go/aspire.config.json create mode 100644 examples/papercut/CommunityToolkit.Aspire.Hosting.PapercutSmtp.AppHost.Go/go.mod create mode 100644 tests/CommunityToolkit.Aspire.Hosting.LavinMQ.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.MailPit.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.PapercutSmtp.Tests/GoAppHostTests.cs diff --git a/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost.Go/apphost.go b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost.Go/apphost.go new file mode 100644 index 000000000..9c71f4083 --- /dev/null +++ b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost.Go/apphost.go @@ -0,0 +1,49 @@ +package main + +import ( + "apphost/modules/aspire" + "log" + "os" +) + +func main() { + bindMountPath, err := os.MkdirTemp("", "lavinmq-") + if err != nil { + log.Fatal(err) + } + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + volume := builder.AddLavinMQ("volume-broker", &aspire.AddLavinMQOptions{ + AmqpPort: aspire.Float64Ptr(35672), + ManagementPort: aspire.Float64Ptr(35673), + }) + volume.WithDataVolume("lavinmq-volume") + _ = volume.PrimaryEndpoint() + _ = volume.Host() + _ = volume.Port() + _ = volume.UriExpression() + _ = volume.ConnectionStringExpression() + if err := volume.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + mounted := builder.AddLavinMQ("bind-broker", &aspire.AddLavinMQOptions{ + AmqpPort: aspire.Float64Ptr(35674), + ManagementPort: aspire.Float64Ptr(35675), + }) + mounted.WithDataBindMount(bindMountPath) + if err := mounted.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost.Go/aspire.config.json b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..371a57398 --- /dev/null +++ b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.LavinMQ": "../../../src/CommunityToolkit.Aspire.Hosting.LavinMQ/CommunityToolkit.Aspire.Hosting.LavinMQ.csproj" + } +} diff --git a/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost.Go/go.mod b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/mailpit/CommunityToolkit.Aspire.Hosting.MailPit.AppHost.Go/apphost.go b/examples/mailpit/CommunityToolkit.Aspire.Hosting.MailPit.AppHost.Go/apphost.go new file mode 100644 index 000000000..a91a24cd9 --- /dev/null +++ b/examples/mailpit/CommunityToolkit.Aspire.Hosting.MailPit.AppHost.Go/apphost.go @@ -0,0 +1,51 @@ +package main + +import ( + "apphost/modules/aspire" + "log" + "os" +) + +func main() { + bindMountPath, err := os.MkdirTemp("", "mailpit-") + if err != nil { + log.Fatal(err) + } + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + mailpit := builder.AddMailPit("mailpit", &aspire.AddMailPitOptions{ + HttpPort: aspire.Float64Ptr(18025), + SmtpPort: aspire.Float64Ptr(11025), + }) + mailpit.WithDataVolume("mailpit-data") + _ = mailpit.PrimaryEndpoint() + _ = mailpit.Host() + _ = mailpit.Port() + _ = mailpit.UriExpression() + _ = mailpit.ConnectionStringExpression() + if err := mailpit.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + defaults := builder.AddMailPit("mailpit-default") + defaults.WithDataBindMount(bindMountPath) + _ = defaults.PrimaryEndpoint() + _ = defaults.Host() + _ = defaults.Port() + _ = defaults.UriExpression() + _ = defaults.ConnectionStringExpression() + if err := defaults.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/mailpit/CommunityToolkit.Aspire.Hosting.MailPit.AppHost.Go/aspire.config.json b/examples/mailpit/CommunityToolkit.Aspire.Hosting.MailPit.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..3a032f8d2 --- /dev/null +++ b/examples/mailpit/CommunityToolkit.Aspire.Hosting.MailPit.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.MailPit": "../../../src/CommunityToolkit.Aspire.Hosting.MailPit/CommunityToolkit.Aspire.Hosting.MailPit.csproj" + } +} diff --git a/examples/mailpit/CommunityToolkit.Aspire.Hosting.MailPit.AppHost.Go/go.mod b/examples/mailpit/CommunityToolkit.Aspire.Hosting.MailPit.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/mailpit/CommunityToolkit.Aspire.Hosting.MailPit.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/papercut/CommunityToolkit.Aspire.Hosting.PapercutSmtp.AppHost.Go/apphost.go b/examples/papercut/CommunityToolkit.Aspire.Hosting.PapercutSmtp.AppHost.Go/apphost.go new file mode 100644 index 000000000..8f932e451 --- /dev/null +++ b/examples/papercut/CommunityToolkit.Aspire.Hosting.PapercutSmtp.AppHost.Go/apphost.go @@ -0,0 +1,42 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + papercut := builder.AddPapercutSmtp("papercut", &aspire.AddPapercutSmtpOptions{ + HttpPort: aspire.Float64Ptr(8080), + SmtpPort: aspire.Float64Ptr(2525), + }) + _ = papercut.Host() + _ = papercut.Port() + _ = papercut.UriExpression() + _ = papercut.ConnectionStringExpression() + if err := papercut.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + defaults := builder.AddPapercutSmtp("papercut-default") + _ = defaults.Host() + _ = defaults.Port() + _ = defaults.UriExpression() + _ = defaults.ConnectionStringExpression() + if err := defaults.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/papercut/CommunityToolkit.Aspire.Hosting.PapercutSmtp.AppHost.Go/aspire.config.json b/examples/papercut/CommunityToolkit.Aspire.Hosting.PapercutSmtp.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..a9198c5de --- /dev/null +++ b/examples/papercut/CommunityToolkit.Aspire.Hosting.PapercutSmtp.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.PapercutSmtp": "../../../src/CommunityToolkit.Aspire.Hosting.PapercutSmtp/CommunityToolkit.Aspire.Hosting.PapercutSmtp.csproj" + } +} diff --git a/examples/papercut/CommunityToolkit.Aspire.Hosting.PapercutSmtp.AppHost.Go/go.mod b/examples/papercut/CommunityToolkit.Aspire.Hosting.PapercutSmtp.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/papercut/CommunityToolkit.Aspire.Hosting.PapercutSmtp.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/tests/CommunityToolkit.Aspire.Hosting.LavinMQ.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.LavinMQ.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..9ad8c30dd --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.LavinMQ.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.LavinMQ.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost.Go", + exampleName: "lavinmq", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.MailPit.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.MailPit.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..e3a8a8425 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.MailPit.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.MailPit.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.MailPit.AppHost.Go", + exampleName: "mailpit", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.PapercutSmtp.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.PapercutSmtp.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..e49a8f082 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.PapercutSmtp.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.PapercutSmtp.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.PapercutSmtp.AppHost.Go", + exampleName: "papercut", + cancellationToken: TestContext.Current.CancellationToken); + } +} From 07404cb7cec760c0071d69471677b6038f35161b Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 15:07:24 +1100 Subject: [PATCH 05/26] feat(examples): add five Go AppHost counterparts Check endpoint wrapper errors in existing Go examples. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../apphost.go | 12 +++- .../apphost.go | 4 +- .../apphost.go | 57 +++++++++++++++++++ .../aspire.config.json | 12 ++++ .../go.mod | 7 +++ .../apphost.go | 12 +++- .../apphost.go | 18 ++++-- .../apphost.go | 42 ++++++++++++++ .../aspire.config.json | 12 ++++ .../go.mod | 7 +++ .../apphost.go | 12 +++- .../apphost.go | 14 +++-- .../apphost.go | 52 +++++++++++++++++ .../aspire.config.json | 12 ++++ .../go.mod | 7 +++ .../apphost.go | 35 ++++++++++++ .../aspire.config.json | 13 +++++ .../go.mod | 7 +++ .../apphost.go | 12 +++- .../apphost.go | 40 +++++++++++++ .../aspire.config.json | 12 ++++ .../go.mod | 7 +++ .../GoAppHostTests.cs | 15 +++++ .../GoAppHostTests.cs | 15 +++++ .../GoAppHostTests.cs | 15 +++++ .../GoAppHostTests.cs | 15 +++++ .../GoAppHostTests.cs | 15 +++++ 27 files changed, 458 insertions(+), 23 deletions(-) create mode 100644 examples/flagd/CommunityToolkit.Aspire.Hosting.Flagd.AppHost.Go/apphost.go create mode 100644 examples/flagd/CommunityToolkit.Aspire.Hosting.Flagd.AppHost.Go/aspire.config.json create mode 100644 examples/flagd/CommunityToolkit.Aspire.Hosting.Flagd.AppHost.Go/go.mod create mode 100644 examples/mongodb-ext/CommunityToolkit.Aspire.Hosting.MongoDB.Extensions.AppHost.Go/apphost.go create mode 100644 examples/mongodb-ext/CommunityToolkit.Aspire.Hosting.MongoDB.Extensions.AppHost.Go/aspire.config.json create mode 100644 examples/mongodb-ext/CommunityToolkit.Aspire.Hosting.MongoDB.Extensions.AppHost.Go/go.mod create mode 100644 examples/postgres-ext/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.AppHost.Go/apphost.go create mode 100644 examples/postgres-ext/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.AppHost.Go/aspire.config.json create mode 100644 examples/postgres-ext/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.AppHost.Go/go.mod create mode 100644 examples/ravendb/CommunityToolkit.Aspire.Hosting.RavenDB.AppHost.Go/apphost.go create mode 100644 examples/ravendb/CommunityToolkit.Aspire.Hosting.RavenDB.AppHost.Go/aspire.config.json create mode 100644 examples/ravendb/CommunityToolkit.Aspire.Hosting.RavenDB.AppHost.Go/go.mod create mode 100644 examples/squad/CommunityToolkit.Aspire.Hosting.Squad.AppHost.Go/apphost.go create mode 100644 examples/squad/CommunityToolkit.Aspire.Hosting.Squad.AppHost.Go/aspire.config.json create mode 100644 examples/squad/CommunityToolkit.Aspire.Hosting.Squad.AppHost.Go/go.mod create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Flagd.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.MongoDB.Extensions.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.RavenDB.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Squad.Tests/GoAppHostTests.cs diff --git a/examples/chromadb/CommunityToolkit.Aspire.Hosting.Chroma.AppHost.Go/apphost.go b/examples/chromadb/CommunityToolkit.Aspire.Hosting.Chroma.AppHost.Go/apphost.go index d0bf7058c..d1842e4ec 100644 --- a/examples/chromadb/CommunityToolkit.Aspire.Hosting.Chroma.AppHost.Go/apphost.go +++ b/examples/chromadb/CommunityToolkit.Aspire.Hosting.Chroma.AppHost.Go/apphost.go @@ -14,9 +14,9 @@ func main() { chroma := builder.AddChroma("chroma") chroma.WithHostPort(aspire.Float64Ptr(18080)) chroma.WithDataVolume(&aspire.WithDataVolumeOptions{Name: aspire.StringPtr("chroma-data")}) - _ = chroma.PrimaryEndpoint() - _ = chroma.Host() - _ = chroma.Port() + check(chroma.PrimaryEndpoint().Err()) + check(chroma.Host().Err()) + check(chroma.Port().Err()) _ = chroma.ConnectionStringExpression() _ = chroma.UriExpression() if err := chroma.Err(); err != nil { @@ -31,3 +31,9 @@ func main() { log.Fatal(aspire.FormatError(err)) } } + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/dbx/CommunityToolkit.Aspire.Hosting.Dbx.AppHost.Go/apphost.go b/examples/dbx/CommunityToolkit.Aspire.Hosting.Dbx.AppHost.Go/apphost.go index f355f2f9b..fc12efbd1 100644 --- a/examples/dbx/CommunityToolkit.Aspire.Hosting.Dbx.AppHost.Go/apphost.go +++ b/examples/dbx/CommunityToolkit.Aspire.Hosting.Dbx.AppHost.Go/apphost.go @@ -13,7 +13,9 @@ func main() { dbx := builder.AddDbx(&aspire.AddDbxOptions{Name: aspire.StringPtr("dbx")}) dbx.WithHostPort(aspire.Float64Ptr(3310)) - _ = dbx.PrimaryEndpoint() + if err := dbx.PrimaryEndpoint().Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } if err := dbx.Err(); err != nil { log.Fatal(aspire.FormatError(err)) } diff --git a/examples/flagd/CommunityToolkit.Aspire.Hosting.Flagd.AppHost.Go/apphost.go b/examples/flagd/CommunityToolkit.Aspire.Hosting.Flagd.AppHost.Go/apphost.go new file mode 100644 index 000000000..ef7af47bc --- /dev/null +++ b/examples/flagd/CommunityToolkit.Aspire.Hosting.Flagd.AppHost.Go/apphost.go @@ -0,0 +1,57 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + flagsPath := "../CommunityToolkit.Aspire.Hosting.Flagd.AppHost.TypeScript/flags" + + flagd := builder.AddFlagd("flagd", &aspire.AddFlagdOptions{ + Port: aspire.Float64Ptr(18013), + OfrepPort: aspire.Float64Ptr(18016), + }) + flagd.WithBindFileSync(flagsPath) + check(flagd.PrimaryEndpoint().Err()) + check(flagd.Host().Err()) + check(flagd.Port().Err()) + check(flagd.GetEndpoint("health").Err()) + check(flagd.GetEndpoint("ofrep").Err()) + _ = flagd.UriExpression() + _ = flagd.ConnectionStringExpression() + if err := flagd.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + defaults := builder.AddFlagd("flagd-default") + defaults.WithBindFileSync(flagsPath, &aspire.WithBindFileSyncOptions{Filename: aspire.StringPtr("custom-flagd.json")}) + check(defaults.PrimaryEndpoint().Err()) + check(defaults.Host().Err()) + check(defaults.Port().Err()) + check(defaults.GetEndpoint("health").Err()) + check(defaults.GetEndpoint("ofrep").Err()) + _ = defaults.UriExpression() + _ = defaults.ConnectionStringExpression() + if err := defaults.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/flagd/CommunityToolkit.Aspire.Hosting.Flagd.AppHost.Go/aspire.config.json b/examples/flagd/CommunityToolkit.Aspire.Hosting.Flagd.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..9a8b24c30 --- /dev/null +++ b/examples/flagd/CommunityToolkit.Aspire.Hosting.Flagd.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Flagd": "../../../src/CommunityToolkit.Aspire.Hosting.Flagd/CommunityToolkit.Aspire.Hosting.Flagd.csproj" + } +} diff --git a/examples/flagd/CommunityToolkit.Aspire.Hosting.Flagd.AppHost.Go/go.mod b/examples/flagd/CommunityToolkit.Aspire.Hosting.Flagd.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/flagd/CommunityToolkit.Aspire.Hosting.Flagd.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost.Go/apphost.go b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost.Go/apphost.go index 9c71f4083..8d1593c0c 100644 --- a/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost.Go/apphost.go +++ b/examples/lavinmq/CommunityToolkit.Aspire.Hosting.LavinMQ.AppHost.Go/apphost.go @@ -21,9 +21,9 @@ func main() { ManagementPort: aspire.Float64Ptr(35673), }) volume.WithDataVolume("lavinmq-volume") - _ = volume.PrimaryEndpoint() - _ = volume.Host() - _ = volume.Port() + check(volume.PrimaryEndpoint().Err()) + check(volume.Host().Err()) + check(volume.Port().Err()) _ = volume.UriExpression() _ = volume.ConnectionStringExpression() if err := volume.Err(); err != nil { @@ -47,3 +47,9 @@ func main() { log.Fatal(aspire.FormatError(err)) } } + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/mailpit/CommunityToolkit.Aspire.Hosting.MailPit.AppHost.Go/apphost.go b/examples/mailpit/CommunityToolkit.Aspire.Hosting.MailPit.AppHost.Go/apphost.go index a91a24cd9..ac2ed80cd 100644 --- a/examples/mailpit/CommunityToolkit.Aspire.Hosting.MailPit.AppHost.Go/apphost.go +++ b/examples/mailpit/CommunityToolkit.Aspire.Hosting.MailPit.AppHost.Go/apphost.go @@ -21,9 +21,9 @@ func main() { SmtpPort: aspire.Float64Ptr(11025), }) mailpit.WithDataVolume("mailpit-data") - _ = mailpit.PrimaryEndpoint() - _ = mailpit.Host() - _ = mailpit.Port() + check(mailpit.PrimaryEndpoint().Err()) + check(mailpit.Host().Err()) + check(mailpit.Port().Err()) _ = mailpit.UriExpression() _ = mailpit.ConnectionStringExpression() if err := mailpit.Err(); err != nil { @@ -32,9 +32,9 @@ func main() { defaults := builder.AddMailPit("mailpit-default") defaults.WithDataBindMount(bindMountPath) - _ = defaults.PrimaryEndpoint() - _ = defaults.Host() - _ = defaults.Port() + check(defaults.PrimaryEndpoint().Err()) + check(defaults.Host().Err()) + check(defaults.Port().Err()) _ = defaults.UriExpression() _ = defaults.ConnectionStringExpression() if err := defaults.Err(); err != nil { @@ -49,3 +49,9 @@ func main() { log.Fatal(aspire.FormatError(err)) } } + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/mongodb-ext/CommunityToolkit.Aspire.Hosting.MongoDB.Extensions.AppHost.Go/apphost.go b/examples/mongodb-ext/CommunityToolkit.Aspire.Hosting.MongoDB.Extensions.AppHost.Go/apphost.go new file mode 100644 index 000000000..3f2a8b8b5 --- /dev/null +++ b/examples/mongodb-ext/CommunityToolkit.Aspire.Hosting.MongoDB.Extensions.AppHost.Go/apphost.go @@ -0,0 +1,42 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + mongo := builder.AddMongoDB("mongo") + mongo.WithDbGate() + catalog := mongo.AddDatabase("catalog") + mongo.WithDataVolume() + if err := mongo.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := catalog.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + named := builder.AddMongoDB("mongo-named") + named.WithDbGate(&aspire.WithDbGateOptions{ContainerName: aspire.StringPtr("mongo-ui")}) + orders := named.AddDatabase("orders", &aspire.AddDatabaseOptions{DatabaseName: aspire.StringPtr("orders-db")}) + if err := named.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := orders.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/mongodb-ext/CommunityToolkit.Aspire.Hosting.MongoDB.Extensions.AppHost.Go/aspire.config.json b/examples/mongodb-ext/CommunityToolkit.Aspire.Hosting.MongoDB.Extensions.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..9c27d4052 --- /dev/null +++ b/examples/mongodb-ext/CommunityToolkit.Aspire.Hosting.MongoDB.Extensions.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.MongoDB.Extensions": "../../../src/CommunityToolkit.Aspire.Hosting.MongoDB.Extensions/CommunityToolkit.Aspire.Hosting.MongoDB.Extensions.csproj" + } +} diff --git a/examples/mongodb-ext/CommunityToolkit.Aspire.Hosting.MongoDB.Extensions.AppHost.Go/go.mod b/examples/mongodb-ext/CommunityToolkit.Aspire.Hosting.MongoDB.Extensions.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/mongodb-ext/CommunityToolkit.Aspire.Hosting.MongoDB.Extensions.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/mosquitto/CommunityToolkit.Aspire.Hosting.Mosquitto.AppHost.Go/apphost.go b/examples/mosquitto/CommunityToolkit.Aspire.Hosting.Mosquitto.AppHost.Go/apphost.go index faef22501..7ac3b602a 100644 --- a/examples/mosquitto/CommunityToolkit.Aspire.Hosting.Mosquitto.AppHost.Go/apphost.go +++ b/examples/mosquitto/CommunityToolkit.Aspire.Hosting.Mosquitto.AppHost.Go/apphost.go @@ -13,9 +13,9 @@ func main() { mqtt := builder.AddMosquitto("mqtt") mqtt.WithDataVolume(&aspire.WithDataVolumeOptions{Name: aspire.StringPtr("mosquitto-data")}) - _ = mqtt.PrimaryEndpoint() - _ = mqtt.Host() - _ = mqtt.Port() + check(mqtt.PrimaryEndpoint().Err()) + check(mqtt.Host().Err()) + check(mqtt.Port().Err()) _ = mqtt.ConnectionStringExpression() _ = mqtt.UriExpression() if err := mqtt.Err(); err != nil { @@ -30,3 +30,9 @@ func main() { log.Fatal(aspire.FormatError(err)) } } + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/papercut/CommunityToolkit.Aspire.Hosting.PapercutSmtp.AppHost.Go/apphost.go b/examples/papercut/CommunityToolkit.Aspire.Hosting.PapercutSmtp.AppHost.Go/apphost.go index 8f932e451..4409c4424 100644 --- a/examples/papercut/CommunityToolkit.Aspire.Hosting.PapercutSmtp.AppHost.Go/apphost.go +++ b/examples/papercut/CommunityToolkit.Aspire.Hosting.PapercutSmtp.AppHost.Go/apphost.go @@ -15,8 +15,8 @@ func main() { HttpPort: aspire.Float64Ptr(8080), SmtpPort: aspire.Float64Ptr(2525), }) - _ = papercut.Host() - _ = papercut.Port() + check(papercut.Host().Err()) + check(papercut.Port().Err()) _ = papercut.UriExpression() _ = papercut.ConnectionStringExpression() if err := papercut.Err(); err != nil { @@ -24,8 +24,8 @@ func main() { } defaults := builder.AddPapercutSmtp("papercut-default") - _ = defaults.Host() - _ = defaults.Port() + check(defaults.Host().Err()) + check(defaults.Port().Err()) _ = defaults.UriExpression() _ = defaults.ConnectionStringExpression() if err := defaults.Err(); err != nil { @@ -40,3 +40,9 @@ func main() { log.Fatal(aspire.FormatError(err)) } } + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/postgres-ext/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.AppHost.Go/apphost.go b/examples/postgres-ext/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.AppHost.Go/apphost.go new file mode 100644 index 000000000..beec35265 --- /dev/null +++ b/examples/postgres-ext/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.AppHost.Go/apphost.go @@ -0,0 +1,52 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + postgres := builder.AddPostgres("postgres") + postgres.WithDbGate(&aspire.WithDbGateOptions{ContainerName: aspire.StringPtr("postgres-dbgate")}) + postgres.WithAdminer(&aspire.WithAdminerOptions{ContainerName: aspire.StringPtr("postgres-adminer")}) + database := postgres.AddDatabase("appdb") + check(postgres.PrimaryEndpoint().Err()) + check(postgres.Host().Err()) + check(postgres.Port().Err()) + _ = postgres.UriExpression() + _ = postgres.JdbcConnectionString() + if err := postgres.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + migrations := "../CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.AppHost.TypeScript/migrations" + database.WithFlywayMigration("flyway-migration", migrations) + database.WithFlywayRepair("flyway-repair", migrations) + if _, err := database.DatabaseName(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + _ = database.UriExpression() + _ = database.JdbcConnectionString() + if err := database.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/postgres-ext/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.AppHost.Go/aspire.config.json b/examples/postgres-ext/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..e62ca06c4 --- /dev/null +++ b/examples/postgres-ext/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions": "../../../src/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.csproj" + } +} diff --git a/examples/postgres-ext/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.AppHost.Go/go.mod b/examples/postgres-ext/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/postgres-ext/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/ravendb/CommunityToolkit.Aspire.Hosting.RavenDB.AppHost.Go/apphost.go b/examples/ravendb/CommunityToolkit.Aspire.Hosting.RavenDB.AppHost.Go/apphost.go new file mode 100644 index 000000000..f1be307ec --- /dev/null +++ b/examples/ravendb/CommunityToolkit.Aspire.Hosting.RavenDB.AppHost.Go/apphost.go @@ -0,0 +1,35 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + ravendb := builder.AddRavenDB("ravendb") + database := ravendb.AddDatabase("ravenDatabase") + if err := ravendb.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := database.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + api := builder.AddDotnetProject("apiservice", "../CommunityToolkit.Aspire.Hosting.RavenDB.ApiService/CommunityToolkit.Aspire.Hosting.RavenDB.ApiService.csproj") + api.WithReference(ravendb).WaitFor(ravendb) + if err := api.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/ravendb/CommunityToolkit.Aspire.Hosting.RavenDB.AppHost.Go/aspire.config.json b/examples/ravendb/CommunityToolkit.Aspire.Hosting.RavenDB.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..c264168e9 --- /dev/null +++ b/examples/ravendb/CommunityToolkit.Aspire.Hosting.RavenDB.AppHost.Go/aspire.config.json @@ -0,0 +1,13 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.RavenDB": "../../../src/CommunityToolkit.Aspire.Hosting.RavenDB/CommunityToolkit.Aspire.Hosting.RavenDB.csproj", + "Aspire.Hosting.Dotnet": "17.1.0-preview.1.26559.8" + } +} diff --git a/examples/ravendb/CommunityToolkit.Aspire.Hosting.RavenDB.AppHost.Go/go.mod b/examples/ravendb/CommunityToolkit.Aspire.Hosting.RavenDB.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/ravendb/CommunityToolkit.Aspire.Hosting.RavenDB.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/redpanda/CommunityToolkit.Aspire.Hosting.RedPanda.AppHost.Go/apphost.go b/examples/redpanda/CommunityToolkit.Aspire.Hosting.RedPanda.AppHost.Go/apphost.go index a6c604035..6ba9ccb0b 100644 --- a/examples/redpanda/CommunityToolkit.Aspire.Hosting.RedPanda.AppHost.Go/apphost.go +++ b/examples/redpanda/CommunityToolkit.Aspire.Hosting.RedPanda.AppHost.Go/apphost.go @@ -13,9 +13,9 @@ func main() { redpanda := builder.AddRedPanda("redpanda") redpanda.WithDataVolume(&aspire.WithDataVolumeOptions{Name: aspire.StringPtr("redpanda-data")}) - _ = redpanda.PrimaryEndpoint() - _ = redpanda.Host() - _ = redpanda.Port() + check(redpanda.PrimaryEndpoint().Err()) + check(redpanda.Host().Err()) + check(redpanda.Port().Err()) _ = redpanda.ConnectionStringExpression() if err := redpanda.Err(); err != nil { log.Fatal(aspire.FormatError(err)) @@ -34,3 +34,9 @@ func main() { log.Fatal(aspire.FormatError(err)) } } + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.AppHost.Go/apphost.go b/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.AppHost.Go/apphost.go new file mode 100644 index 000000000..193d4cfbe --- /dev/null +++ b/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.AppHost.Go/apphost.go @@ -0,0 +1,40 @@ +package main + +import ( + "apphost/modules/aspire" + "log" + "path/filepath" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + appHostDirectory, err := builder.AppHostDirectory() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + csharpAppHost := filepath.Join(appHostDirectory, "..", "CommunityToolkit.Aspire.Hosting.Squad.AppHost") + + research := builder.AddSquad("research-squad", &aspire.AddSquadOptions{ + TeamRoot: aspire.StringPtr(filepath.Join(csharpAppHost, "research-squad")), + }) + if err := research.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + development := builder.AddSquad("dev-squad", &aspire.AddSquadOptions{ + TeamRoot: aspire.StringPtr(filepath.Join(csharpAppHost, "dev-squad")), + }) + if err := development.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.AppHost.Go/aspire.config.json b/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..2778dc650 --- /dev/null +++ b/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Squad": "../../../src/CommunityToolkit.Aspire.Hosting.Squad/CommunityToolkit.Aspire.Hosting.Squad.csproj" + } +} diff --git a/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.AppHost.Go/go.mod b/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/tests/CommunityToolkit.Aspire.Hosting.Flagd.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Flagd.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..b6cd6c81d --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Flagd.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Flagd.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Flagd.AppHost.Go", + exampleName: "flagd", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.MongoDB.Extensions.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.MongoDB.Extensions.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..c17608beb --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.MongoDB.Extensions.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.MongoDB.Extensions.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.MongoDB.Extensions.AppHost.Go", + exampleName: "mongodb-ext", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..e653321ea --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.AppHost.Go", + exampleName: "postgres-ext", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.RavenDB.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.RavenDB.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..f6c1ac721 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.RavenDB.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.RavenDB.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.RavenDB.AppHost.Go", + exampleName: "ravendb", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Squad.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Squad.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..c72003ff5 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Squad.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Squad.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Squad.AppHost.Go", + exampleName: "squad", + cancellationToken: TestContext.Current.CancellationToken); + } +} From a43242c181e25b3ea7ef0fc58c9e0acaf21c8dd6 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 15:10:58 +1100 Subject: [PATCH 06/26] feat(examples): add Loki and Tempo Go AppHosts Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../apphost.go | 44 +++++++++++++++++ .../aspire.config.json | 12 +++++ .../go.mod | 7 +++ .../apphost.go | 48 +++++++++++++++++++ .../aspire.config.json | 12 +++++ .../go.mod | 7 +++ .../GoAppHostTests.cs | 15 ++++++ .../GoAppHostTests.cs | 15 ++++++ 8 files changed, 160 insertions(+) create mode 100644 examples/loki/CommunityToolkit.Aspire.Hosting.Loki.AppHost.Go/apphost.go create mode 100644 examples/loki/CommunityToolkit.Aspire.Hosting.Loki.AppHost.Go/aspire.config.json create mode 100644 examples/loki/CommunityToolkit.Aspire.Hosting.Loki.AppHost.Go/go.mod create mode 100644 examples/tempo/CommunityToolkit.Aspire.Hosting.Tempo.AppHost.Go/apphost.go create mode 100644 examples/tempo/CommunityToolkit.Aspire.Hosting.Tempo.AppHost.Go/aspire.config.json create mode 100644 examples/tempo/CommunityToolkit.Aspire.Hosting.Tempo.AppHost.Go/go.mod create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Loki.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Tempo.Tests/GoAppHostTests.cs diff --git a/examples/loki/CommunityToolkit.Aspire.Hosting.Loki.AppHost.Go/apphost.go b/examples/loki/CommunityToolkit.Aspire.Hosting.Loki.AppHost.Go/apphost.go new file mode 100644 index 000000000..85780b2a9 --- /dev/null +++ b/examples/loki/CommunityToolkit.Aspire.Hosting.Loki.AppHost.Go/apphost.go @@ -0,0 +1,44 @@ +package main + +import ( + "apphost/modules/aspire" + "log" + "os" + "path/filepath" + "runtime" +) + +func main() { + builder, err := aspire.CreateBuilder() + check(err) + directory, err := builder.AppHostDirectory() + check(err) + + loki := builder.AddLoki("loki") + loki.WithHttpHostPort(nil) + loki.WithGrpcHostPort(nil) + loki.WithDataVolume() + check(loki.HttpEndpoint().Err()) + check(loki.GrpcEndpoint().Err()) + check(loki.Err()) + + mount, err := os.MkdirTemp("", "loki-") + check(err) + if runtime.GOOS != "windows" { + check(os.Chmod(mount, 0777)) + } + mounted := builder.AddLoki("loki-mounted") + mounted.WithDataBindMount(mount) + mounted.WithConfigFile(filepath.Join(directory, "..", "CommunityToolkit.Aspire.Hosting.Loki.AppHost.TypeScript", "loki.yaml")) + check(mounted.Err()) + + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/loki/CommunityToolkit.Aspire.Hosting.Loki.AppHost.Go/aspire.config.json b/examples/loki/CommunityToolkit.Aspire.Hosting.Loki.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..3984ed3da --- /dev/null +++ b/examples/loki/CommunityToolkit.Aspire.Hosting.Loki.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Loki": "../../../src/CommunityToolkit.Aspire.Hosting.Loki/CommunityToolkit.Aspire.Hosting.Loki.csproj" + } +} diff --git a/examples/loki/CommunityToolkit.Aspire.Hosting.Loki.AppHost.Go/go.mod b/examples/loki/CommunityToolkit.Aspire.Hosting.Loki.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/loki/CommunityToolkit.Aspire.Hosting.Loki.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/tempo/CommunityToolkit.Aspire.Hosting.Tempo.AppHost.Go/apphost.go b/examples/tempo/CommunityToolkit.Aspire.Hosting.Tempo.AppHost.Go/apphost.go new file mode 100644 index 000000000..ec0247f23 --- /dev/null +++ b/examples/tempo/CommunityToolkit.Aspire.Hosting.Tempo.AppHost.Go/apphost.go @@ -0,0 +1,48 @@ +package main + +import ( + "apphost/modules/aspire" + "log" + "os" + "path/filepath" + "runtime" +) + +func main() { + builder, err := aspire.CreateBuilder() + check(err) + directory, err := builder.AppHostDirectory() + check(err) + + tempo := builder.AddTempo("tempo") + tempo.WithHttpHostPort(nil) + tempo.WithGrpcHostPort(nil) + tempo.WithOtlpGrpcHostPort(nil) + tempo.WithOtlpHttpHostPort(nil) + tempo.WithDataVolume() + check(tempo.HttpEndpoint().Err()) + check(tempo.GrpcEndpoint().Err()) + check(tempo.OtlpGrpcEndpoint().Err()) + check(tempo.OtlpHttpEndpoint().Err()) + check(tempo.Err()) + + mount, err := os.MkdirTemp("", "tempo-") + check(err) + if runtime.GOOS != "windows" { + check(os.Chmod(mount, 0777)) + } + mounted := builder.AddTempo("tempo-mounted") + mounted.WithDataBindMount(mount) + mounted.WithConfigFile(filepath.Join(directory, "..", "CommunityToolkit.Aspire.Hosting.Tempo.AppHost.TypeScript", "tempo.yaml")) + check(mounted.Err()) + + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/tempo/CommunityToolkit.Aspire.Hosting.Tempo.AppHost.Go/aspire.config.json b/examples/tempo/CommunityToolkit.Aspire.Hosting.Tempo.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..90cdab64b --- /dev/null +++ b/examples/tempo/CommunityToolkit.Aspire.Hosting.Tempo.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Tempo": "../../../src/CommunityToolkit.Aspire.Hosting.Tempo/CommunityToolkit.Aspire.Hosting.Tempo.csproj" + } +} diff --git a/examples/tempo/CommunityToolkit.Aspire.Hosting.Tempo.AppHost.Go/go.mod b/examples/tempo/CommunityToolkit.Aspire.Hosting.Tempo.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/tempo/CommunityToolkit.Aspire.Hosting.Tempo.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/tests/CommunityToolkit.Aspire.Hosting.Loki.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Loki.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..5492d3adb --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Loki.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Loki.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Loki.AppHost.Go", + exampleName: "loki", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Tempo.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Tempo.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..dca151472 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Tempo.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Tempo.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Tempo.AppHost.Go", + exampleName: "tempo", + cancellationToken: TestContext.Current.CancellationToken); + } +} From 4aed0fb92bcdf386360d9883f47b81defa9e2e42 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 15:16:13 +1100 Subject: [PATCH 07/26] feat(examples): add GoFeatureFlag and KurrentDB Go AppHosts Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../apphost.go | 45 +++++++++++++++++++ .../aspire.config.json | 12 +++++ .../go.mod | 7 +++ .../apphost.go | 43 ++++++++++++++++++ .../aspire.config.json | 12 +++++ .../go.mod | 7 +++ .../GoAppHostTests.cs | 15 +++++++ .../GoAppHostTests.cs | 15 +++++++ 8 files changed, 156 insertions(+) create mode 100644 examples/goff/CommunityToolkit.Aspire.Hosting.GoFeatureFlag.AppHost.Go/apphost.go create mode 100644 examples/goff/CommunityToolkit.Aspire.Hosting.GoFeatureFlag.AppHost.Go/aspire.config.json create mode 100644 examples/goff/CommunityToolkit.Aspire.Hosting.GoFeatureFlag.AppHost.Go/go.mod create mode 100644 examples/kurrentdb/CommunityToolkit.Aspire.Hosting.KurrentDB.AppHost.Go/apphost.go create mode 100644 examples/kurrentdb/CommunityToolkit.Aspire.Hosting.KurrentDB.AppHost.Go/aspire.config.json create mode 100644 examples/kurrentdb/CommunityToolkit.Aspire.Hosting.KurrentDB.AppHost.Go/go.mod create mode 100644 tests/CommunityToolkit.Aspire.Hosting.GoFeatureFlag.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.KurrentDB.Tests/GoAppHostTests.cs diff --git a/examples/goff/CommunityToolkit.Aspire.Hosting.GoFeatureFlag.AppHost.Go/apphost.go b/examples/goff/CommunityToolkit.Aspire.Hosting.GoFeatureFlag.AppHost.Go/apphost.go new file mode 100644 index 000000000..218619038 --- /dev/null +++ b/examples/goff/CommunityToolkit.Aspire.Hosting.GoFeatureFlag.AppHost.Go/apphost.go @@ -0,0 +1,45 @@ +package main + +import ( + "apphost/modules/aspire" + "log" + "path/filepath" +) + +func main() { + builder, err := aspire.CreateBuilder() + check(err) + directory, err := builder.AppHostDirectory() + check(err) + config := filepath.Join(directory, "..", "CommunityToolkit.Aspire.Hosting.GoFeatureFlag.AppHost.TypeScript", "goff") + + goff := builder.AddGoFeatureFlag("goff", &aspire.AddGoFeatureFlagOptions{ + PathToConfigFile: aspire.StringPtr("/goff/goff-proxy.yaml"), + Port: aspire.Float64Ptr(11031), + }) + goff.WithDataVolume(&aspire.WithDataVolumeOptions{Name: aspire.StringPtr("goff-data")}) + goff.WithGoffBindMount(config) + goff.WithLogLevel(aspire.GoFeatureFlagLogLevelDebug) + + goff2 := builder.AddGoFeatureFlag("goff2") + goff2.WithGoffBindMount(config) + + for _, resource := range []aspire.GoFeatureFlagResource{goff, goff2} { + check(resource.PrimaryEndpoint().Err()) + check(resource.Host().Err()) + check(resource.Port().Err()) + _ = resource.UriExpression() + _ = resource.ConnectionStringExpression() + check(resource.Err()) + } + + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/goff/CommunityToolkit.Aspire.Hosting.GoFeatureFlag.AppHost.Go/aspire.config.json b/examples/goff/CommunityToolkit.Aspire.Hosting.GoFeatureFlag.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..e12ab0e3e --- /dev/null +++ b/examples/goff/CommunityToolkit.Aspire.Hosting.GoFeatureFlag.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.GoFeatureFlag": "../../../src/CommunityToolkit.Aspire.Hosting.GoFeatureFlag/CommunityToolkit.Aspire.Hosting.GoFeatureFlag.csproj" + } +} diff --git a/examples/goff/CommunityToolkit.Aspire.Hosting.GoFeatureFlag.AppHost.Go/go.mod b/examples/goff/CommunityToolkit.Aspire.Hosting.GoFeatureFlag.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/goff/CommunityToolkit.Aspire.Hosting.GoFeatureFlag.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/kurrentdb/CommunityToolkit.Aspire.Hosting.KurrentDB.AppHost.Go/apphost.go b/examples/kurrentdb/CommunityToolkit.Aspire.Hosting.KurrentDB.AppHost.Go/apphost.go new file mode 100644 index 000000000..30df78a98 --- /dev/null +++ b/examples/kurrentdb/CommunityToolkit.Aspire.Hosting.KurrentDB.AppHost.Go/apphost.go @@ -0,0 +1,43 @@ +package main + +import ( + "apphost/modules/aspire" + "log" + "os" + "path/filepath" +) + +func main() { + builder, err := aspire.CreateBuilder() + check(err) + directory, err := builder.AppHostDirectory() + check(err) + mount := filepath.Join(directory, "..", "CommunityToolkit.Aspire.Hosting.KurrentDB.AppHost.TypeScript", "data", "kurrentdb") + check(os.MkdirAll(mount, 0755)) + + volume := builder.AddKurrentDB("kurrentdb", &aspire.AddKurrentDBOptions{Port: aspire.Float64Ptr(22113)}) + volume.WithDataVolume(&aspire.WithDataVolumeOptions{Name: aspire.StringPtr("kurrentdb-data")}) + mounted := builder.AddKurrentDB("kurrentdb2") + mounted.WithDataBindMount(mount) + defaultVolume := builder.AddKurrentDB("kurrentdb3") + defaultVolume.WithDataVolume() + + for _, resource := range []aspire.KurrentDBResource{volume, mounted, defaultVolume} { + check(resource.PrimaryEndpoint().Err()) + check(resource.Host().Err()) + check(resource.Port().Err()) + _ = resource.UriExpression() + _ = resource.ConnectionStringExpression() + check(resource.Err()) + } + + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/kurrentdb/CommunityToolkit.Aspire.Hosting.KurrentDB.AppHost.Go/aspire.config.json b/examples/kurrentdb/CommunityToolkit.Aspire.Hosting.KurrentDB.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..a6d2bf7f6 --- /dev/null +++ b/examples/kurrentdb/CommunityToolkit.Aspire.Hosting.KurrentDB.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.KurrentDB": "../../../src/CommunityToolkit.Aspire.Hosting.KurrentDB/CommunityToolkit.Aspire.Hosting.KurrentDB.csproj" + } +} diff --git a/examples/kurrentdb/CommunityToolkit.Aspire.Hosting.KurrentDB.AppHost.Go/go.mod b/examples/kurrentdb/CommunityToolkit.Aspire.Hosting.KurrentDB.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/kurrentdb/CommunityToolkit.Aspire.Hosting.KurrentDB.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/tests/CommunityToolkit.Aspire.Hosting.GoFeatureFlag.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.GoFeatureFlag.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..948cc6885 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.GoFeatureFlag.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.GoFeatureFlag.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.GoFeatureFlag.AppHost.Go", + exampleName: "goff", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.KurrentDB.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.KurrentDB.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..ca89021f5 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.KurrentDB.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.KurrentDB.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.KurrentDB.AppHost.Go", + exampleName: "kurrentdb", + cancellationToken: TestContext.Current.CancellationToken); + } +} From 1ca4582f661756c92752d1ca99a0614dc4a972c2 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 15:19:47 +1100 Subject: [PATCH 08/26] feat(examples): add Logto Go AppHost Document the Umami parameter serialization blocker. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- docs/go-apphost-parity.md | 7 ++++ .../apphost.go | 38 +++++++++++++++++++ .../aspire.config.json | 13 +++++++ .../go.mod | 7 ++++ .../GoAppHostTests.cs | 15 ++++++++ 5 files changed, 80 insertions(+) create mode 100644 examples/logto/CommunityToolkit.Aspire.Hosting.Logto.AppHost.Go/apphost.go create mode 100644 examples/logto/CommunityToolkit.Aspire.Hosting.Logto.AppHost.Go/aspire.config.json create mode 100644 examples/logto/CommunityToolkit.Aspire.Hosting.Logto.AppHost.Go/go.mod create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Logto.Tests/GoAppHostTests.cs diff --git a/docs/go-apphost-parity.md b/docs/go-apphost-parity.md index 8c8038d9f..d64a9340d 100644 --- a/docs/go-apphost-parity.md +++ b/docs/go-apphost-parity.md @@ -11,6 +11,13 @@ repository's pinned Aspire SDK (`17.1.0-preview.1.26559.8`): 'IResourceBuilder\`1' must have [AspireDto] attribute to be deserialized from JSON` for `accessKey`. The TypeScript example explicitly supplies both credentials, so omitting them would not preserve its resource graph. +- **Umami:** `AddUmamiOptions.Secret` is likewise a `*aspire.ParameterResource`. + Passing the address of the explicitly configured `umami-secret` compiles, + but `aspire publish --apphost apphost.go --list-steps --non-interactive` + fails while constructing the model: `CommunityToolkit.Aspire.Hosting.Umami/addUmami` + reports `INVALID_ARGUMENT` for `secret`, with the same `IResourceBuilder\`1` + deserialization error as RustFs. Omitting the secret would not preserve the + TypeScript example. The Python extensions Go example runs, but its publish-mode model fails: `AddStreamlitApp` removes the Python resource before adding its Streamlit diff --git a/examples/logto/CommunityToolkit.Aspire.Hosting.Logto.AppHost.Go/apphost.go b/examples/logto/CommunityToolkit.Aspire.Hosting.Logto.AppHost.Go/apphost.go new file mode 100644 index 000000000..d803fe414 --- /dev/null +++ b/examples/logto/CommunityToolkit.Aspire.Hosting.Logto.AppHost.Go/apphost.go @@ -0,0 +1,38 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + check(err) + + postgres := builder.AddPostgres("postgres") + redis := builder.AddRedis("redis") + check(postgres.Err()) + check(redis.Err()) + + logto := builder.AddLogto("logto", postgres) + logto.WithRedis(redis) + logto.WithDatabaseSeeding() + check(logto.Err()) + + oidc := builder.AddDotnetProject("clientOIDC", "../CommunityToolkit.Aspire.Logto.ClientOIDC/CommunityToolkit.Aspire.Logto.ClientOIDC.csproj") + oidc.WithReference(logto).WaitFor(logto) + check(oidc.Err()) + jwt := builder.AddDotnetProject("clientJWT", "../CommunityToolkit.Aspire.Logto.ClientJWT/CommunityToolkit.Aspire.Logto.ClientJWT.csproj") + jwt.WithReference(logto).WaitFor(logto) + check(jwt.Err()) + + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/logto/CommunityToolkit.Aspire.Hosting.Logto.AppHost.Go/aspire.config.json b/examples/logto/CommunityToolkit.Aspire.Hosting.Logto.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..b0ecf74d7 --- /dev/null +++ b/examples/logto/CommunityToolkit.Aspire.Hosting.Logto.AppHost.Go/aspire.config.json @@ -0,0 +1,13 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Logto": "../../../src/CommunityToolkit.Aspire.Hosting.Logto/CommunityToolkit.Aspire.Hosting.Logto.csproj", + "Aspire.Hosting.Dotnet": "17.1.0-preview.1.26559.8" + } +} diff --git a/examples/logto/CommunityToolkit.Aspire.Hosting.Logto.AppHost.Go/go.mod b/examples/logto/CommunityToolkit.Aspire.Hosting.Logto.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/logto/CommunityToolkit.Aspire.Hosting.Logto.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/tests/CommunityToolkit.Aspire.Hosting.Logto.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Logto.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..cd92d9816 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Logto.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Logto.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Logto.AppHost.Go", + exampleName: "logto", + cancellationToken: TestContext.Current.CancellationToken); + } +} From 4a5890251143a0d5d80f1344ddb9f6b379f17703 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 15:21:35 +1100 Subject: [PATCH 09/26] feat(examples): add SFTP Go AppHost Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../apphost.go | 45 +++++++++++++++++++ .../aspire.config.json | 12 +++++ .../go.mod | 7 +++ .../GoAppHostTests.cs | 15 +++++++ 4 files changed, 79 insertions(+) create mode 100644 examples/sftp/CommunityToolkit.Aspire.Hosting.Sftp.AppHost.Go/apphost.go create mode 100644 examples/sftp/CommunityToolkit.Aspire.Hosting.Sftp.AppHost.Go/aspire.config.json create mode 100644 examples/sftp/CommunityToolkit.Aspire.Hosting.Sftp.AppHost.Go/go.mod create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Sftp.Tests/GoAppHostTests.cs diff --git a/examples/sftp/CommunityToolkit.Aspire.Hosting.Sftp.AppHost.Go/apphost.go b/examples/sftp/CommunityToolkit.Aspire.Hosting.Sftp.AppHost.Go/apphost.go new file mode 100644 index 000000000..a9ed757a0 --- /dev/null +++ b/examples/sftp/CommunityToolkit.Aspire.Hosting.Sftp.AppHost.Go/apphost.go @@ -0,0 +1,45 @@ +package main + +import ( + "apphost/modules/aspire" + "log" + "os" + "path/filepath" +) + +func main() { + builder, err := aspire.CreateBuilder() + check(err) + directory, err := builder.AppHostDirectory() + check(err) + fixtures := filepath.Join(directory, "..", "CommunityToolkit.Aspire.Hosting.Sftp.AppHost") + + sftp := builder.AddSftp("sftp", &aspire.AddSftpOptions{Port: aspire.Float64Ptr(2222)}) + if os.Getenv("ASPIRE_RUNTIME_SMOKE") == "1" { + sftp.WithEnvironment("SFTP_USERS", "foo:pass:::upload") + } else { + sftp.WithUsersFile(filepath.Join(fixtures, "etc", "sftp", "users.conf")) + sftp.WithHostKeyFile(filepath.Join(fixtures, "etc", "ssh", "ssh_host_ed25519_key"), aspire.KeyTypeEd25519) + sftp.WithUserKeyFile("foo", filepath.Join(fixtures, "home", "foo", ".ssh", "keys", "id_ed25519.pub"), aspire.KeyTypeEd25519) + } + defaults := builder.AddSftp("sftp-defaults") + defaults.WithEnvironment("SFTP_USERS", "bar:pass:::upload") + + for _, resource := range []aspire.SftpContainerResource{sftp, defaults} { + check(resource.Host().Err()) + check(resource.Port().Err()) + _ = resource.UriExpression() + _ = resource.ConnectionStringExpression() + check(resource.Err()) + } + + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/sftp/CommunityToolkit.Aspire.Hosting.Sftp.AppHost.Go/aspire.config.json b/examples/sftp/CommunityToolkit.Aspire.Hosting.Sftp.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..1894a7e4f --- /dev/null +++ b/examples/sftp/CommunityToolkit.Aspire.Hosting.Sftp.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Sftp": "../../../src/CommunityToolkit.Aspire.Hosting.Sftp/CommunityToolkit.Aspire.Hosting.Sftp.csproj" + } +} diff --git a/examples/sftp/CommunityToolkit.Aspire.Hosting.Sftp.AppHost.Go/go.mod b/examples/sftp/CommunityToolkit.Aspire.Hosting.Sftp.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/sftp/CommunityToolkit.Aspire.Hosting.Sftp.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/tests/CommunityToolkit.Aspire.Hosting.Sftp.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Sftp.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..88da7e98d --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Sftp.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Sftp.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Sftp.AppHost.Go", + exampleName: "sftp", + cancellationToken: TestContext.Current.CancellationToken); + } +} From 9871ebc8b54e7ceb7dbf03e4b6aaf3a6dba02993 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 15:23:57 +1100 Subject: [PATCH 10/26] feat(examples): add Solr Go AppHost Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../apphost.go | 58 +++++++++++++++++++ .../aspire.config.json | 12 ++++ .../go.mod | 7 +++ .../GoAppHostTests.cs | 15 +++++ 4 files changed, 92 insertions(+) create mode 100644 examples/solr/CommunityToolkit.Aspire.Hosting.Solr.AppHost.Go/apphost.go create mode 100644 examples/solr/CommunityToolkit.Aspire.Hosting.Solr.AppHost.Go/aspire.config.json create mode 100644 examples/solr/CommunityToolkit.Aspire.Hosting.Solr.AppHost.Go/go.mod create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Solr.Tests/GoAppHostTests.cs diff --git a/examples/solr/CommunityToolkit.Aspire.Hosting.Solr.AppHost.Go/apphost.go b/examples/solr/CommunityToolkit.Aspire.Hosting.Solr.AppHost.Go/apphost.go new file mode 100644 index 000000000..0941ef3cc --- /dev/null +++ b/examples/solr/CommunityToolkit.Aspire.Hosting.Solr.AppHost.Go/apphost.go @@ -0,0 +1,58 @@ +package main + +import ( + "apphost/modules/aspire" + "log" + "os" + "path/filepath" +) + +func main() { + builder, err := aspire.CreateBuilder() + check(err) + directory, err := builder.AppHostDirectory() + check(err) + data := filepath.Join(directory, "..", "CommunityToolkit.Aspire.Hosting.Solr.AppHost.TypeScript", "solr-data") + check(os.MkdirAll(data, 0777)) + check(os.Chmod(data, 0777)) + + solr := builder.AddSolr("solr") + core, err := solr.CoreName() + check(err) + solr.SetCoreName(core) + solr.WithDataVolume(&aspire.WithDataVolumeOptions{Name: aspire.StringPtr("solr-data")}) + check(solr.PrimaryEndpoint().Err()) + check(solr.Host().Err()) + check(solr.Port().Err()) + _, err = solr.CoreName() + check(err) + _ = solr.ConnectionStringExpression() + _ = solr.UriExpression() + check(solr.Err()) + + mounted := builder.AddSolr("solr-bind", &aspire.AddSolrOptions{ + Port: aspire.Float64Ptr(8984), + CoreName: aspire.StringPtr("bindcore"), + }) + mountedCore, err := mounted.CoreName() + check(err) + mounted.SetCoreName(mountedCore) + mounted.WithDataBindMount(data) + check(mounted.Err()) + + if false { + configset := builder.AddSolr("solr-configset", &aspire.AddSolrOptions{CoreName: aspire.StringPtr("configset-core")}) + configset.WithConfigset("sample-configset", filepath.Join(directory, "..", "CommunityToolkit.Aspire.Hosting.Solr.AppHost.TypeScript", "configsets", "sample-configset")) + check(configset.Err()) + } + + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/solr/CommunityToolkit.Aspire.Hosting.Solr.AppHost.Go/aspire.config.json b/examples/solr/CommunityToolkit.Aspire.Hosting.Solr.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..ef50bfb36 --- /dev/null +++ b/examples/solr/CommunityToolkit.Aspire.Hosting.Solr.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Solr": "../../../src/CommunityToolkit.Aspire.Hosting.Solr/CommunityToolkit.Aspire.Hosting.Solr.csproj" + } +} diff --git a/examples/solr/CommunityToolkit.Aspire.Hosting.Solr.AppHost.Go/go.mod b/examples/solr/CommunityToolkit.Aspire.Hosting.Solr.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/solr/CommunityToolkit.Aspire.Hosting.Solr.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/tests/CommunityToolkit.Aspire.Hosting.Solr.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Solr.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..1c6900de0 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Solr.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Solr.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Solr.AppHost.Go", + exampleName: "solr", + cancellationToken: TestContext.Current.CancellationToken); + } +} From d84e31f7971818f3096200d962be5efc325777e6 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 15:25:34 +1100 Subject: [PATCH 11/26] feat(examples): add Listmonk Go AppHost Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../apphost.go | 55 +++++++++++++++++++ .../aspire.config.json | 12 ++++ .../go.mod | 7 +++ .../GoAppHostTests.cs | 15 +++++ 4 files changed, 89 insertions(+) create mode 100644 examples/listmonk/CommunityToolkit.Aspire.Hosting.Listmonk.AppHost.Go/apphost.go create mode 100644 examples/listmonk/CommunityToolkit.Aspire.Hosting.Listmonk.AppHost.Go/aspire.config.json create mode 100644 examples/listmonk/CommunityToolkit.Aspire.Hosting.Listmonk.AppHost.Go/go.mod create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Listmonk.Tests/GoAppHostTests.cs diff --git a/examples/listmonk/CommunityToolkit.Aspire.Hosting.Listmonk.AppHost.Go/apphost.go b/examples/listmonk/CommunityToolkit.Aspire.Hosting.Listmonk.AppHost.Go/apphost.go new file mode 100644 index 000000000..e0210a015 --- /dev/null +++ b/examples/listmonk/CommunityToolkit.Aspire.Hosting.Listmonk.AppHost.Go/apphost.go @@ -0,0 +1,55 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + check(err) + password := builder.AddParameter("admin-password", &aspire.AddParameterOptions{ + Value: aspire.StringPtr("SuperSecret123!"), + Secret: aspire.BoolPtr(true), + }) + check(password.Err()) + postgres := builder.AddPostgres("postgres") + database := postgres.AddDatabase("listmonkdb") + defaultPostgres := builder.AddPostgres("listmonk-default-postgres") + defaultDatabase := defaultPostgres.AddDatabase("listmonk-default-db") + check(postgres.Err()) + check(database.Err()) + check(defaultPostgres.Err()) + check(defaultDatabase.Err()) + + listmonk := builder.AddListmonk("listmonk", &aspire.AddListmonkOptions{Port: aspire.Float64Ptr(31900)}) + defaults := builder.AddListmonk("listmonk-default") + listmonk.WithReference(database) + listmonk.WithAdminCredentials("admin", password) + listmonk.WithDatabaseMaxOpenConnections(25) + listmonk.WithDatabaseMaxIdleConnections(25) + listmonk.WithDatabaseMaxLifetime("300s") + listmonk.WithTimeZone("Etc/UTC") + listmonk.WithUserId(0) + listmonk.WithGroupId(0) + listmonk.WithUploadsVolume() + defaults.WithReference(defaultDatabase) + defaults.WithDatabaseSslMode("disable") + check(listmonk.PrimaryEndpoint().Err()) + check(listmonk.Host().Err()) + check(listmonk.Port().Err()) + _ = listmonk.UriExpression() + _ = listmonk.ConnectionStringExpression() + check(listmonk.Err()) + check(defaults.Err()) + + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/listmonk/CommunityToolkit.Aspire.Hosting.Listmonk.AppHost.Go/aspire.config.json b/examples/listmonk/CommunityToolkit.Aspire.Hosting.Listmonk.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..d65a0efaf --- /dev/null +++ b/examples/listmonk/CommunityToolkit.Aspire.Hosting.Listmonk.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Listmonk": "../../../src/CommunityToolkit.Aspire.Hosting.Listmonk/CommunityToolkit.Aspire.Hosting.Listmonk.csproj" + } +} diff --git a/examples/listmonk/CommunityToolkit.Aspire.Hosting.Listmonk.AppHost.Go/go.mod b/examples/listmonk/CommunityToolkit.Aspire.Hosting.Listmonk.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/listmonk/CommunityToolkit.Aspire.Hosting.Listmonk.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/tests/CommunityToolkit.Aspire.Hosting.Listmonk.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Listmonk.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..45b089a4d --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Listmonk.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Listmonk.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Listmonk.AppHost.Go", + exampleName: "listmonk", + cancellationToken: TestContext.Current.CancellationToken); + } +} From 5705874887b0d34cc02640c36233017dd6c143c1 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 15:27:14 +1100 Subject: [PATCH 12/26] docs: document Meilisearch Go SDK blocker Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- docs/go-apphost-parity.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/go-apphost-parity.md b/docs/go-apphost-parity.md index d64a9340d..7c18ceefe 100644 --- a/docs/go-apphost-parity.md +++ b/docs/go-apphost-parity.md @@ -18,6 +18,12 @@ repository's pinned Aspire SDK (`17.1.0-preview.1.26559.8`): reports `INVALID_ARGUMENT` for `secret`, with the same `IResourceBuilder\`1` deserialization error as RustFs. Omitting the secret would not preserve the TypeScript example. +- **Meilisearch:** `AddMeilisearchOptions.MasterKey` is also a pointer to a + parameter interface. An explicit `search-master-key` compiles but + `CommunityToolkit.Aspire.Hosting.Meilisearch/addMeilisearch` fails during + publish-model construction with `INVALID_ARGUMENT` for `masterKey` and + the same `IResourceBuilder\`1` deserialization error. The TypeScript + example explicitly supplies the key. The Python extensions Go example runs, but its publish-mode model fails: `AddStreamlitApp` removes the Python resource before adding its Streamlit From 15b747e75d636c44016a32a6959c68e984ed1001 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 15:29:18 +1100 Subject: [PATCH 13/26] feat(examples): add MySQL extensions Go AppHost Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../apphost.go | 36 +++++++++++++++++++ .../aspire.config.json | 13 +++++++ .../go.mod | 7 ++++ .../GoAppHostTests.cs | 15 ++++++++ 4 files changed, 71 insertions(+) create mode 100644 examples/mysql-ext/CommunityToolkit.Aspire.Hosting.MySql.Extensions.AppHost.Go/apphost.go create mode 100644 examples/mysql-ext/CommunityToolkit.Aspire.Hosting.MySql.Extensions.AppHost.Go/aspire.config.json create mode 100644 examples/mysql-ext/CommunityToolkit.Aspire.Hosting.MySql.Extensions.AppHost.Go/go.mod create mode 100644 tests/CommunityToolkit.Aspire.Hosting.MySql.Extensions.Tests/GoAppHostTests.cs diff --git a/examples/mysql-ext/CommunityToolkit.Aspire.Hosting.MySql.Extensions.AppHost.Go/apphost.go b/examples/mysql-ext/CommunityToolkit.Aspire.Hosting.MySql.Extensions.AppHost.Go/apphost.go new file mode 100644 index 000000000..b1a068796 --- /dev/null +++ b/examples/mysql-ext/CommunityToolkit.Aspire.Hosting.MySql.Extensions.AppHost.Go/apphost.go @@ -0,0 +1,36 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + check(err) + + mysql := builder.AddMySql("mysql") + mysql.WithAdminer(&aspire.WithAdminerOptions{ + ConfigureContainer: func(adminer aspire.AdminerContainerResource) {}, + ContainerName: aspire.StringPtr("mysql-adminer"), + }) + mysql.WithDbGate(&aspire.WithDbGateOptions{ + ConfigureContainer: func(dbgate aspire.DbGateContainerResource) {}, + ContainerName: aspire.StringPtr("mysql-dbgate"), + }) + check(mysql.PrimaryEndpoint().Err()) + check(mysql.Host().Err()) + check(mysql.Port().Err()) + _ = mysql.ConnectionStringExpression() + check(mysql.Err()) + + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/mysql-ext/CommunityToolkit.Aspire.Hosting.MySql.Extensions.AppHost.Go/aspire.config.json b/examples/mysql-ext/CommunityToolkit.Aspire.Hosting.MySql.Extensions.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..2acb654e1 --- /dev/null +++ b/examples/mysql-ext/CommunityToolkit.Aspire.Hosting.MySql.Extensions.AppHost.Go/aspire.config.json @@ -0,0 +1,13 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "Aspire.Hosting.MySql": "", + "CommunityToolkit.Aspire.Hosting.MySql.Extensions": "../../../src/CommunityToolkit.Aspire.Hosting.MySql.Extensions/CommunityToolkit.Aspire.Hosting.MySql.Extensions.csproj" + } +} diff --git a/examples/mysql-ext/CommunityToolkit.Aspire.Hosting.MySql.Extensions.AppHost.Go/go.mod b/examples/mysql-ext/CommunityToolkit.Aspire.Hosting.MySql.Extensions.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/mysql-ext/CommunityToolkit.Aspire.Hosting.MySql.Extensions.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/tests/CommunityToolkit.Aspire.Hosting.MySql.Extensions.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.MySql.Extensions.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..3193aa663 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.MySql.Extensions.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.MySql.Extensions.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.MySql.Extensions.AppHost.Go", + exampleName: "mysql-ext", + cancellationToken: TestContext.Current.CancellationToken); + } +} From f40c9e14fcde88ca277d3e9996ddfcd8013c3249 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 15:31:08 +1100 Subject: [PATCH 14/26] docs: document Redis Go SDK blocker Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- docs/go-apphost-parity.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/go-apphost-parity.md b/docs/go-apphost-parity.md index 7c18ceefe..794f89847 100644 --- a/docs/go-apphost-parity.md +++ b/docs/go-apphost-parity.md @@ -24,6 +24,12 @@ repository's pinned Aspire SDK (`17.1.0-preview.1.26559.8`): publish-model construction with `INVALID_ARGUMENT` for `masterKey` and the same `IResourceBuilder\`1` deserialization error. The TypeScript example explicitly supplies the key. +- **Redis extensions:** `AddRedisOptions.Password` is a pointer to a parameter + interface. Passing the explicit `redis-password` compiles, but + `Aspire.Hosting.Redis/addRedis` fails during publish-model construction with + `INVALID_ARGUMENT` for `password` and the same `IResourceBuilder\`1` + deserialization error. Generating a different password would change the + TypeScript example. The Python extensions Go example runs, but its publish-mode model fails: `AddStreamlitApp` removes the Python resource before adding its Streamlit From 8f0bd6ca4c4e48299a5c1565a3b19410ee79edf5 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 15:33:23 +1100 Subject: [PATCH 15/26] docs: document SQL Server Go SDK blocker Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- docs/go-apphost-parity.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/go-apphost-parity.md b/docs/go-apphost-parity.md index 794f89847..297ba076a 100644 --- a/docs/go-apphost-parity.md +++ b/docs/go-apphost-parity.md @@ -30,6 +30,11 @@ repository's pinned Aspire SDK (`17.1.0-preview.1.26559.8`): `INVALID_ARGUMENT` for `password` and the same `IResourceBuilder\`1` deserialization error. Generating a different password would change the TypeScript example. +- **SQL Server extensions:** `AddSqlServerOptions.Password` has the same + pointer-to-parameter shape. The explicit `sql-password` compiles, but + `Aspire.Hosting.SqlServer/addSqlServer` reports `INVALID_ARGUMENT` for + `password` and the same `IResourceBuilder\`1` deserialization error during + publish-model construction. Omitting it changes the TypeScript example. The Python extensions Go example runs, but its publish-mode model fails: `AddStreamlitApp` removes the Python resource before adding its Streamlit From 64e44e73f05d95f6db7cc3e07c2e7b3209394eb4 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 15:35:27 +1100 Subject: [PATCH 16/26] docs: document MCP Inspector Go SDK blocker Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- docs/go-apphost-parity.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/go-apphost-parity.md b/docs/go-apphost-parity.md index 297ba076a..0a059fdcf 100644 --- a/docs/go-apphost-parity.md +++ b/docs/go-apphost-parity.md @@ -35,6 +35,11 @@ repository's pinned Aspire SDK (`17.1.0-preview.1.26559.8`): `Aspire.Hosting.SqlServer/addSqlServer` reports `INVALID_ARGUMENT` for `password` and the same `IResourceBuilder\`1` deserialization error during publish-model construction. Omitting it changes the TypeScript example. +- **MCP Inspector:** `AddMcpInspectorOptions.ProxyToken` is a pointer to a + parameter interface. Passing the explicit `proxy-token` compiles, but + `CommunityToolkit.Aspire.Hosting.McpInspector/addMcpInspectorPolyglot` fails + during publish-model construction with `INVALID_ARGUMENT` for + `proxyToken` and the same `IResourceBuilder\`1` deserialization error. The Python extensions Go example runs, but its publish-mode model fails: `AddStreamlitApp` removes the Python resource before adding its Streamlit From 072ee913bae960995cdc483372dd761fda79c59f Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 15:43:05 +1100 Subject: [PATCH 17/26] test: cover affected Go AppHost test selection Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../AffectedGoAppHostSelectionTests.cs | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Dbx.Tests/AffectedGoAppHostSelectionTests.cs diff --git a/tests/CommunityToolkit.Aspire.Hosting.Dbx.Tests/AffectedGoAppHostSelectionTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Dbx.Tests/AffectedGoAppHostSelectionTests.cs new file mode 100644 index 000000000..21b11bd9f --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Dbx.Tests/AffectedGoAppHostSelectionTests.cs @@ -0,0 +1,90 @@ +using System.Diagnostics; +using System.Text.Json; + +namespace CommunityToolkit.Aspire.Hosting.Dbx.Tests; + +public class AffectedGoAppHostSelectionTests +{ + [Theory] + [InlineData("one", "One.AppHost.Go", "apphost.go", "Hosting.One.Tests")] + [InlineData("one", "One.AppHost.Go", "assets/config.json", "Hosting.One.Tests")] + [InlineData("two", "Two.AppHost.Go", "apphost.go", "Hosting.Two.Tests")] + public async Task GoAppHostChangesSelectTheirTestProject(string example, string appHost, string changedFile, string expectedTest) + { + var cancellationToken = TestContext.Current.CancellationToken; + var repositoryRoot = Path.GetFullPath(Path.Combine("..", "..", "..", "..", "..")); + var selector = Path.Combine(repositoryRoot, "eng", "testing", "select-affected-tests.cs"); + var fixture = Path.Combine(Path.GetTempPath(), $"go-selection-{Guid.NewGuid():N}"); + Directory.CreateDirectory(fixture); + + try + { + WriteFile(fixture, "tests/Directory.Build.props", ""); + WriteFile(fixture, "tests/CommunityToolkit.Aspire.Hosting.One.Tests/CommunityToolkit.Aspire.Hosting.One.Tests.csproj", ""); + WriteFile(fixture, "tests/CommunityToolkit.Aspire.Hosting.One.Tests/GoAppHostTests.cs", + """GoAppHostTest.Run(appHostProject: "One.AppHost.Go", exampleName: "one");"""); + WriteFile(fixture, "tests/CommunityToolkit.Aspire.Hosting.Two.Tests/CommunityToolkit.Aspire.Hosting.Two.Tests.csproj", ""); + WriteFile(fixture, "tests/CommunityToolkit.Aspire.Hosting.Two.Tests/GoAppHostTests.cs", + """const string Project = "Two.AppHost.Go"; const string Example = "two"; GoAppHostTest.Run(appHostProject: Project, exampleName: Example);"""); + WriteFile(fixture, "examples/one/One.AppHost.Go/apphost.go", "before"); + WriteFile(fixture, "examples/two/Two.AppHost.Go/apphost.go", "before"); + WriteFile(fixture, $"examples/{example}/{appHost}/{changedFile}", "before"); + + await RunAsync("git", ["init", "--quiet"], fixture, cancellationToken); + await RunAsync("git", ["add", "."], fixture, cancellationToken); + await RunAsync("git", ["-c", "user.name=Test", "-c", "user.email=test@example.invalid", "-c", "commit.gpgsign=false", "commit", "--quiet", "-m", "base"], fixture, cancellationToken); + WriteFile(fixture, $"examples/{example}/{appHost}/{changedFile}", "after"); + await RunAsync("git", ["add", "."], fixture, cancellationToken); + await RunAsync("git", ["-c", "user.name=Test", "-c", "user.email=test@example.invalid", "-c", "commit.gpgsign=false", "commit", "--quiet", "-m", "change"], fixture, cancellationToken); + + var outputDirectory = Path.Combine(fixture, "output"); + var fixtureSelector = Path.Combine(fixture, "select-affected-tests.cs"); + File.Copy(selector, fixtureSelector); + await RunAsync("dotnet", ["run", fixtureSelector, "--", "--repo-root", fixture, "--base-sha", "HEAD~1", "--head-sha", "HEAD", "--output-dir", outputDirectory], fixture, cancellationToken); + using var selection = JsonDocument.Parse(await File.ReadAllTextAsync(Path.Combine(outputDirectory, "selection.json"), cancellationToken)); + Assert.False(selection.RootElement.GetProperty("runAll").GetBoolean()); + var selected = selection.RootElement.GetProperty("selectedTests").EnumerateArray().Select(static item => item.GetString()!).ToArray(); + Assert.Equal([expectedTest], selected); + Assert.Contains(selection.RootElement.GetProperty("reasons").EnumerateArray(), static reason => + reason.GetString()!.Contains("Go app host test fixture", StringComparison.Ordinal)); + } + finally + { + foreach (var file in Directory.EnumerateFiles(fixture, "*", SearchOption.AllDirectories)) + { + File.SetAttributes(file, FileAttributes.Normal); + } + Directory.Delete(fixture, recursive: true); + } + } + + private static void WriteFile(string root, string path, string content) + { + var fullPath = Path.Combine(root, path); + Directory.CreateDirectory(Path.GetDirectoryName(fullPath)!); + File.WriteAllText(fullPath, content); + } + + private static async Task RunAsync(string executable, string[] arguments, string directory, CancellationToken cancellationToken) + { + var startInfo = new ProcessStartInfo(executable) + { + WorkingDirectory = directory, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + }; + foreach (var argument in arguments) + { + startInfo.ArgumentList.Add(argument); + } + startInfo.Environment.Remove("GITHUB_OUTPUT"); + startInfo.Environment.Remove("GITHUB_STEP_SUMMARY"); + + using var process = Process.Start(startInfo) ?? throw new InvalidOperationException($"Could not start {executable}."); + var output = process.StandardOutput.ReadToEndAsync(cancellationToken); + var error = process.StandardError.ReadToEndAsync(cancellationToken); + await process.WaitForExitAsync(cancellationToken); + Assert.True(process.ExitCode == 0, $"{executable} exited with {process.ExitCode}: {await output}\n{await error}"); + } +} From 9d2b5b95e470ecfe10a5d0a383f528454f1dd672 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 15:48:03 +1100 Subject: [PATCH 18/26] docs: record additional Go AppHost blockers Distinguish Floci run-only behavior from SDK parity gaps. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- docs/go-apphost-parity.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/go-apphost-parity.md b/docs/go-apphost-parity.md index 0a059fdcf..8f628a3b8 100644 --- a/docs/go-apphost-parity.md +++ b/docs/go-apphost-parity.md @@ -40,6 +40,19 @@ repository's pinned Aspire SDK (`17.1.0-preview.1.26559.8`): `CommunityToolkit.Aspire.Hosting.McpInspector/addMcpInspectorPolyglot` fails during publish-model construction with `INVALID_ARGUMENT` for `proxyToken` and the same `IResourceBuilder\`1` deserialization error. +- **Zitadel:** `AddZitadelOptions.Username`, `Password`, and `MasterKey` are + optional parameter-resource pointers. Supplying the credentials used by the + TypeScript example fails ATS model construction with `INVALID_ARGUMENT` due + to the same pointer-to-interface serialization defect. Omitting them would + change the example. +- **Flare:** `WithPublicUrlsOptions.DashboardUrl` is a pointer to a parameter + interface. Passing the example's `flare-dashboard-url` fails model + construction because of the same serialization defect; omitting it changes + the configured public URL. +- **Keycloak extensions:** The example's PostgreSQL `userName` and `password` + are parameter handles supplied to `addPostgres`. The generated Go options use + parameter-interface pointers, which fail model construction with the same + serialization defect. Replacing the explicit credentials changes the example. The Python extensions Go example runs, but its publish-mode model fails: `AddStreamlitApp` removes the Python resource before adding its Streamlit @@ -47,3 +60,7 @@ replacement, while the publish-mode executable container with the same name remains in the model. The resulting duplicate `dashboard` resource causes `AddStreamlitApp` to fail. Its Go compilation test therefore does not run the publish-model check; fixing the integration is outside this example-only work. + +Floci's `WithServiceBus` is run-only by design and throws in publish mode, as +documented in the Floci README. Validate the example's Service Bus graph in run +mode rather than treating its publish-mode rejection as a Go SDK parity gap. From c1d907326a9869fc8b56e506bcbc36b5bae9c210 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 15:53:37 +1100 Subject: [PATCH 19/26] test(k3s): validate existing Go AppHost parity Align compile-only project APIs and surface model construction errors. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../apphost.go | 35 ++++++++++++------- .../aspire.config.json | 6 ++-- .../GoAppHostTests.cs | 15 ++++++++ 3 files changed, 39 insertions(+), 17 deletions(-) create mode 100644 tests/CommunityToolkit.Aspire.Hosting.K3s.Tests/GoAppHostTests.cs diff --git a/examples/k3s/CommunityToolkit.Aspire.Hosting.K3s.AppHost.Go/apphost.go b/examples/k3s/CommunityToolkit.Aspire.Hosting.K3s.AppHost.Go/apphost.go index afb0046ae..91e4755c9 100644 --- a/examples/k3s/CommunityToolkit.Aspire.Hosting.K3s.AppHost.Go/apphost.go +++ b/examples/k3s/CommunityToolkit.Aspire.Hosting.K3s.AppHost.Go/apphost.go @@ -21,39 +21,42 @@ func main() { // by the compile-time section below. cluster := builder.AddK3sCluster("k8s") - _ = cluster.ApiEndpoint() + check(cluster.ApiEndpoint().Err()) + check(cluster.Err()) setupPodinfo := cluster.AddHelmRelease("podinfo", "podinfo", &aspire.AddHelmReleaseOptions{ Repo: aspire.StringPtr("https://stefanprodan.github.io/podinfo"), Version: aspire.StringPtr("6.7.1"), Namespace: aspire.StringPtr("podinfo"), }) + check(setupPodinfo.Err()) - cluster.AddServiceEndpoint("podinfo-web", "podinfo", 9898, &aspire.AddServiceEndpointOptions{ + endpoint := cluster.AddServiceEndpoint("podinfo-web", "podinfo", 9898, &aspire.AddServiceEndpointOptions{ Namespace: aspire.StringPtr("podinfo"), }).WaitForCompletion(setupPodinfo) + check(endpoint.Err()) // ── Compile-time coverage ───────────────────────────────────────────────────── // Guards with false so these are type-checked but never executed. // Covers the full exported API surface without requiring Docker/k3s in CI. const includeCompileOnlyScenarios = false - if (includeCompileOnlyScenarios) { + if includeCompileOnlyScenarios { // ── Cluster configuration ──────────────────────────────────────────────── // All K3sClusterOptions are now available as fluent builder methods. - configuredCluster := builder.AddK3sCluster("k8s-configured", + configuredCluster := builder.AddK3sCluster("k8s-configured", &aspire.AddK3sClusterOptions{ ApiServerPort: aspire.Float64Ptr(6443), - AgentCount: aspire.Float64Ptr(2), + AgentCount: aspire.Float64Ptr(2), }). WithK3sVersion("v1.32.3-k3s1"). WithPodSubnet("10.42.0.0/16"). WithServiceSubnet("10.43.0.0/16"). WithDisabledComponent("traefik"). WithExtraArg("--write-kubeconfig-mode=644"). - WithDataVolume(&aspire.WithDataVolumeOptions{ Name: aspire.StringPtr("k8s-data") }). - WithHelmImage(&aspire.WithHelmImageOptions{ Tag: aspire.StringPtr("3.18.0") }). - WithKubectlImage(&aspire.WithKubectlImageOptions{ Tag: aspire.StringPtr("1.37.0") }). + WithDataVolume(&aspire.WithDataVolumeOptions{Name: aspire.StringPtr("k8s-data")}). + WithHelmImage(&aspire.WithHelmImageOptions{Tag: aspire.StringPtr("3.18.0")}). + WithKubectlImage(&aspire.WithKubectlImageOptions{Tag: aspire.StringPtr("1.37.0")}). WithLifetime(aspire.ContainerLifetimePersistent) _ = configuredCluster.ApiEndpoint() @@ -66,7 +69,7 @@ func main() { }) _ = podinfo.Parent() - + _, _ = podinfo.ReleaseName() _, _ = podinfo.Namespace() @@ -107,7 +110,7 @@ func main() { // • Project/executable: KUBECONFIG=/.k3s/k8s/local/kubeconfig.yaml // • Container: KUBECONFIG=/tmp/k3s-kubeconfig.yaml + bind-mount _ = builder. - AddProject("operator", "../WidgetOperator/WidgetOperator.csproj"). + AddDotnetProject("operator", "../WidgetOperator/WidgetOperator.csproj"). WithReference(configuredCluster) _ = builder. @@ -115,9 +118,9 @@ func main() { WithReference(configuredCluster) // ── WithReference — service endpoint URL injection ──────────────────────── - + _ = builder. - AddProject("api", "../WidgetApi/WidgetApi.csproj"). + AddDotnetProject("api", "../WidgetApi/WidgetApi.csproj"). WithReference(podinfoWeb) } @@ -128,4 +131,10 @@ func main() { if err := app.Run(); err != nil { log.Fatal(aspire.FormatError(err)) } -} \ No newline at end of file +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/k3s/CommunityToolkit.Aspire.Hosting.K3s.AppHost.Go/aspire.config.json b/examples/k3s/CommunityToolkit.Aspire.Hosting.K3s.AppHost.Go/aspire.config.json index d16fd715e..bbca7a6f4 100644 --- a/examples/k3s/CommunityToolkit.Aspire.Hosting.K3s.AppHost.Go/aspire.config.json +++ b/examples/k3s/CommunityToolkit.Aspire.Hosting.K3s.AppHost.Go/aspire.config.json @@ -3,13 +3,11 @@ "path": "apphost.go", "language": "go" }, - "features.experimentalPolyglot": { - "go": "true" - }, "sdk": { "version": "17.1.0-preview.1.26559.8" }, "packages": { - "CommunityToolkit.Aspire.Hosting.K3s": "../../../src/CommunityToolkit.Aspire.Hosting.K3s/CommunityToolkit.Aspire.Hosting.K3s.csproj" + "CommunityToolkit.Aspire.Hosting.K3s": "../../../src/CommunityToolkit.Aspire.Hosting.K3s/CommunityToolkit.Aspire.Hosting.K3s.csproj", + "Aspire.Hosting.Dotnet": "17.1.0-preview.1.26559.8" } } diff --git a/tests/CommunityToolkit.Aspire.Hosting.K3s.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.K3s.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..21980e2bb --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.K3s.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.K3s.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.K3s.AppHost.Go", + exampleName: "k3s", + cancellationToken: TestContext.Current.CancellationToken); + } +} From 2caf2cfcd67bdcd10185b6ca348777857578b8be Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 15:53:29 +1100 Subject: [PATCH 20/26] feat(examples): add first batch of Go AppHost counterparts Add ActiveMQ, Azure extensions, Dapr, Azure Dapr, Data API Builder, DbGate, Deno, and Elasticsearch extensions examples. Preserve the TypeScript graphs and use exported sidecar references for Dapr. Document the ActiveMQ preview Go SDK serialization blocker. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../README.md | 26 +++++++ .../apphost.go | 76 +++++++++++++++++++ .../aspire.config.json | 12 +++ .../go.mod | 7 ++ .../README.md | 19 +++++ .../apphost.go | 35 +++++++++ .../aspire.config.json | 12 +++ .../go.mod | 7 ++ .../README.md | 24 ++++++ .../apphost.go | 45 +++++++++++ .../aspire.config.json | 13 ++++ .../go.mod | 7 ++ .../README.md | 23 ++++++ .../apphost.go | 42 ++++++++++ .../aspire.config.json | 13 ++++ .../go.mod | 7 ++ .../README.md | 22 ++++++ .../apphost.go | 51 +++++++++++++ .../aspire.config.json | 13 ++++ .../go.mod | 7 ++ .../README.md | 20 +++++ .../apphost.go | 35 +++++++++ .../aspire.config.json | 12 +++ .../go.mod | 7 ++ .../README.md | 18 +++++ .../apphost.go | 34 +++++++++ .../aspire.config.json | 12 +++ .../go.mod | 7 ++ .../README.md | 19 +++++ .../apphost.go | 34 +++++++++ .../aspire.config.json | 12 +++ .../go.mod | 7 ++ 32 files changed, 678 insertions(+) create mode 100644 examples/activemq/CommunityToolkit.Aspire.Hosting.ActiveMQ.AppHost.Go/README.md create mode 100644 examples/activemq/CommunityToolkit.Aspire.Hosting.ActiveMQ.AppHost.Go/apphost.go create mode 100644 examples/activemq/CommunityToolkit.Aspire.Hosting.ActiveMQ.AppHost.Go/aspire.config.json create mode 100644 examples/activemq/CommunityToolkit.Aspire.Hosting.ActiveMQ.AppHost.Go/go.mod create mode 100644 examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost.Go/README.md create mode 100644 examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost.Go/apphost.go create mode 100644 examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost.Go/aspire.config.json create mode 100644 examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost.Go/go.mod create mode 100644 examples/dapr/CommunityToolkit.Aspire.Hosting.Azure.Dapr.AppHost.Go/README.md create mode 100644 examples/dapr/CommunityToolkit.Aspire.Hosting.Azure.Dapr.AppHost.Go/apphost.go create mode 100644 examples/dapr/CommunityToolkit.Aspire.Hosting.Azure.Dapr.AppHost.Go/aspire.config.json create mode 100644 examples/dapr/CommunityToolkit.Aspire.Hosting.Azure.Dapr.AppHost.Go/go.mod create mode 100644 examples/dapr/CommunityToolkit.Aspire.Hosting.Dapr.AppHost.Go/README.md create mode 100644 examples/dapr/CommunityToolkit.Aspire.Hosting.Dapr.AppHost.Go/apphost.go create mode 100644 examples/dapr/CommunityToolkit.Aspire.Hosting.Dapr.AppHost.Go/aspire.config.json create mode 100644 examples/dapr/CommunityToolkit.Aspire.Hosting.Dapr.AppHost.Go/go.mod create mode 100644 examples/data-api-builder/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.AppHost.Go/README.md create mode 100644 examples/data-api-builder/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.AppHost.Go/apphost.go create mode 100644 examples/data-api-builder/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.AppHost.Go/aspire.config.json create mode 100644 examples/data-api-builder/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.AppHost.Go/go.mod create mode 100644 examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost.Go/README.md create mode 100644 examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost.Go/apphost.go create mode 100644 examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost.Go/aspire.config.json create mode 100644 examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost.Go/go.mod create mode 100644 examples/deno/CommunityToolkit.Aspire.Hosting.Deno.AppHost.Go/README.md create mode 100644 examples/deno/CommunityToolkit.Aspire.Hosting.Deno.AppHost.Go/apphost.go create mode 100644 examples/deno/CommunityToolkit.Aspire.Hosting.Deno.AppHost.Go/aspire.config.json create mode 100644 examples/deno/CommunityToolkit.Aspire.Hosting.Deno.AppHost.Go/go.mod create mode 100644 examples/elasticsearch-ext/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions.AppHost.Go/README.md create mode 100644 examples/elasticsearch-ext/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions.AppHost.Go/apphost.go create mode 100644 examples/elasticsearch-ext/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions.AppHost.Go/aspire.config.json create mode 100644 examples/elasticsearch-ext/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions.AppHost.Go/go.mod diff --git a/examples/activemq/CommunityToolkit.Aspire.Hosting.ActiveMQ.AppHost.Go/README.md b/examples/activemq/CommunityToolkit.Aspire.Hosting.ActiveMQ.AppHost.Go/README.md new file mode 100644 index 000000000..25cc42178 --- /dev/null +++ b/examples/activemq/CommunityToolkit.Aspire.Hosting.ActiveMQ.AppHost.Go/README.md @@ -0,0 +1,26 @@ +# ActiveMQ Go AppHost + +This is the Go counterpart of the adjacent TypeScript AppHost. It configures +Classic and Artemis brokers, explicit credentials, ports, volumes, bind mounts, +and exported endpoint properties. + +Use the Aspire 17 preview CLI matching `aspire.config.json` and Go 1.26 or later. +From this directory: + +```shell +aspire restore --non-interactive +go build ./... +go vet ./... +aspire start --isolated --non-interactive +``` + +Running the brokers requires Docker. + +## Preview SDK limitation + +With Aspire `17.1.0-preview.1.26559.8`, compilation succeeds but model execution +fails at `addActiveMQ` with `INVALID_ARGUMENT` for `userName`. The generated Go +options use `*ParameterResource` for optional resource parameters, but their +serializer does not unwrap the pointer to the interface into a resource handle. +The explicit credentials are retained for parity rather than removing them or +modifying generated SDK files. diff --git a/examples/activemq/CommunityToolkit.Aspire.Hosting.ActiveMQ.AppHost.Go/apphost.go b/examples/activemq/CommunityToolkit.Aspire.Hosting.ActiveMQ.AppHost.Go/apphost.go new file mode 100644 index 000000000..34a53be03 --- /dev/null +++ b/examples/activemq/CommunityToolkit.Aspire.Hosting.ActiveMQ.AppHost.Go/apphost.go @@ -0,0 +1,76 @@ +package main + +import ( + "apphost/modules/aspire" + "log" + "os" + "path/filepath" +) + +func main() { + builder, err := aspire.CreateBuilder() + check(err) + + bindMountRoot, err := os.MkdirTemp("", "activemq-polyglot-") + check(err) + artemisDataPath := filepath.Join(bindMountRoot, "artemis-data") + artemisConfPath := filepath.Join(bindMountRoot, "artemis-conf") + check(os.MkdirAll(artemisDataPath, 0755)) + check(os.MkdirAll(artemisConfPath, 0755)) + + password := builder.AddParameter("mq-password", &aspire.AddParameterOptions{ + Value: aspire.StringPtr("admin"), Secret: aspire.BoolPtr(true), + }) + user := builder.AddParameter("mq-user", &aspire.AddParameterOptions{ + Value: aspire.StringPtr("admin"), PublishValueAsDefault: aspire.BoolPtr(true), + }) + check(password.Err()) + check(user.Err()) + + classic := builder.AddActiveMQ("classic", &aspire.AddActiveMQOptions{ + UserName: &user, Password: &password, Port: aspire.Float64Ptr(36161), + Scheme: aspire.StringPtr("activemq"), WebPort: aspire.Float64Ptr(38161), + }).WithDataVolume(&aspire.WithDataVolumeOptions{Name: aspire.StringPtr("classic-data")}). + WithConfVolume(&aspire.WithConfVolumeOptions{Name: aspire.StringPtr("classic-conf")}) + classic2 := builder.AddActiveMQ("classic2", &aspire.AddActiveMQOptions{ + UserName: &user, Password: &password, + }) + artemis := builder.AddActiveMQArtemis("artemis", &aspire.AddActiveMQArtemisOptions{ + UserName: &user, Password: &password, Port: aspire.Float64Ptr(36162), + Scheme: aspire.StringPtr("tcp"), WebPort: aspire.Float64Ptr(38162), + }).WithDataBindMount(artemisDataPath).WithConfBindMount(artemisConfPath) + artemis2 := builder.AddActiveMQArtemis("artemis2", &aspire.AddActiveMQArtemisOptions{ + UserName: &user, Password: &password, + }).WithDataVolume().WithConfVolume() + + check(classic.Err()) + check(classic2.Err()) + check(artemis.Err()) + check(artemis2.Err()) + check(classic.PrimaryEndpoint().Err()) + check(classic.Host().Err()) + check(classic.Port().Err()) + _ = classic.UriExpression() + _ = classic.ConnectionStringExpression() + check(classic2.PrimaryEndpoint().Err()) + check(classic2.Host().Err()) + check(classic2.Port().Err()) + check(artemis.PrimaryEndpoint().Err()) + check(artemis.Host().Err()) + check(artemis.Port().Err()) + _ = artemis.UriExpression() + _ = artemis.ConnectionStringExpression() + check(artemis2.PrimaryEndpoint().Err()) + check(artemis2.Host().Err()) + check(artemis2.Port().Err()) + + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/activemq/CommunityToolkit.Aspire.Hosting.ActiveMQ.AppHost.Go/aspire.config.json b/examples/activemq/CommunityToolkit.Aspire.Hosting.ActiveMQ.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..f8a28ce3d --- /dev/null +++ b/examples/activemq/CommunityToolkit.Aspire.Hosting.ActiveMQ.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.ActiveMQ": "../../../src/CommunityToolkit.Aspire.Hosting.ActiveMQ/CommunityToolkit.Aspire.Hosting.ActiveMQ.csproj" + } +} diff --git a/examples/activemq/CommunityToolkit.Aspire.Hosting.ActiveMQ.AppHost.Go/go.mod b/examples/activemq/CommunityToolkit.Aspire.Hosting.ActiveMQ.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/activemq/CommunityToolkit.Aspire.Hosting.ActiveMQ.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost.Go/README.md b/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost.Go/README.md new file mode 100644 index 000000000..ae621543f --- /dev/null +++ b/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost.Go/README.md @@ -0,0 +1,19 @@ +# Azure extensions Go AppHost + +This is the Go counterpart of the adjacent TypeScript AppHost. It configures +Azurite with blob, queue, and table ports 27000-27002, a data volume, and Azure +Storage Explorer for the blob resource. + +Use the Aspire 17 preview CLI matching `aspire.config.json` and Go 1.26 or later. +From this directory: + +```shell +aspire restore --non-interactive +go build ./... +go vet ./... +aspire start --isolated --non-interactive +``` + +Running the emulator and explorer requires Docker. `aspire publish +--non-interactive` validates the publish model without provisioning Azure +resources. diff --git a/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost.Go/apphost.go b/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost.Go/apphost.go new file mode 100644 index 000000000..2e543c586 --- /dev/null +++ b/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost.Go/apphost.go @@ -0,0 +1,35 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + check(err) + + storage := builder.AddAzureStorage("azure-storage").RunAsEmulator(&aspire.RunAsEmulatorOptions{ + ConfigureContainer: func(azurite aspire.AzureStorageEmulatorResource) { + azurite.WithArgs([]string{"--disableProductStyleUrl"}). + WithBlobPort(27000). + WithQueuePort(27001). + WithTablePort(27002). + WithDataVolume(&aspire.WithDataVolumeOptions{Name: aspire.StringPtr("storage")}) + check(azurite.Err()) + }, + }) + check(storage.Err()) + blobs := storage.AddBlobs("blobs").WithAzureStorageExplorer() + check(blobs.Err()) + + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost.Go/aspire.config.json b/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..715b53a6e --- /dev/null +++ b/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Azure.Extensions": "../../../src/CommunityToolkit.Aspire.Hosting.Azure.Extensions/CommunityToolkit.Aspire.Hosting.Azure.Extensions.csproj" + } +} diff --git a/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost.Go/go.mod b/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/azure-ext/CommunityToolkit.Aspire.Azure.Extensions.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/dapr/CommunityToolkit.Aspire.Hosting.Azure.Dapr.AppHost.Go/README.md b/examples/dapr/CommunityToolkit.Aspire.Hosting.Azure.Dapr.AppHost.Go/README.md new file mode 100644 index 000000000..17b127b59 --- /dev/null +++ b/examples/dapr/CommunityToolkit.Aspire.Hosting.Azure.Dapr.AppHost.Go/README.md @@ -0,0 +1,24 @@ +# Azure Dapr Go AppHost + +This is the Go counterpart of the adjacent TypeScript AppHost. It configures an +Azure Container Apps environment with Dapr components, actor state metadata, and +three .NET services with the same component references and startup dependencies. + +Component references are configured on the sidecars through the exported +`ConfigureDaprSidecar` callback. This preserves the intended graph without using +the generic project `WithReference`, which does not accept Dapr components. + +Use the Aspire 17 preview CLI matching `aspire.config.json`, Go 1.26 or later, +the repository's .NET SDK, and the Dapr CLI. From this directory: + +```shell +aspire restore --non-interactive +go build ./... +go vet ./... +aspire publish --non-interactive +aspire start --isolated --non-interactive +``` + +Publishing generates Azure Bicep artifacts without provisioning Azure resources. +Local execution additionally requires Docker and an initialized Dapr runtime. +The Dapr CLI must be on `PATH` even when publishing the model. diff --git a/examples/dapr/CommunityToolkit.Aspire.Hosting.Azure.Dapr.AppHost.Go/apphost.go b/examples/dapr/CommunityToolkit.Aspire.Hosting.Azure.Dapr.AppHost.Go/apphost.go new file mode 100644 index 000000000..977080b0f --- /dev/null +++ b/examples/dapr/CommunityToolkit.Aspire.Hosting.Azure.Dapr.AppHost.Go/apphost.go @@ -0,0 +1,45 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + check(err) + + cae := builder.AddAzureContainerAppEnvironment("cae").WithDaprComponents() + check(cae.Err()) + stateStore := builder.AddDaprStateStore("statestore").WithMetadata("actorStateStore", "true") + pubSub := builder.AddDaprPubSub("pubsub") + check(stateStore.Err()) + check(pubSub.Err()) + + serviceA := builder.AddDotnetProject("servicea", "../CommunityToolkit.Aspire.Hosting.Dapr.ServiceA/CommunityToolkit.Aspire.Hosting.Dapr.ServiceA.csproj"). + PublishAsAzureContainerApp(func(context aspire.AzureResourceInfrastructure, app aspire.ContainerApp) {}). + ConfigureDaprSidecar(func(sidecar aspire.DaprSidecarResource) { + check(sidecar.WithReference(stateStore).WithReference(pubSub).Err()) + }) + check(serviceA.Err()) + serviceB := builder.AddDotnetProject("serviceb", "../CommunityToolkit.Aspire.Hosting.Dapr.ServiceB/CommunityToolkit.Aspire.Hosting.Dapr.ServiceB.csproj"). + ConfigureDaprSidecar(func(sidecar aspire.DaprSidecarResource) { + check(sidecar.WithReference(pubSub).Err()) + }).WaitFor(serviceA) + check(serviceB.Err()) + serviceC := builder.AddDotnetProject("servicec", "../CommunityToolkit.Aspire.Hosting.Dapr.ServiceC/CommunityToolkit.Aspire.Hosting.Dapr.ServiceC.csproj"). + ConfigureDaprSidecar(func(sidecar aspire.DaprSidecarResource) { + check(sidecar.WithReference(stateStore).Err()) + }).WaitFor(serviceA) + check(serviceC.Err()) + + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/dapr/CommunityToolkit.Aspire.Hosting.Azure.Dapr.AppHost.Go/aspire.config.json b/examples/dapr/CommunityToolkit.Aspire.Hosting.Azure.Dapr.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..0bb194bda --- /dev/null +++ b/examples/dapr/CommunityToolkit.Aspire.Hosting.Azure.Dapr.AppHost.Go/aspire.config.json @@ -0,0 +1,13 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Azure.Dapr": "../../../src/CommunityToolkit.Aspire.Hosting.Azure.Dapr/CommunityToolkit.Aspire.Hosting.Azure.Dapr.csproj", + "Aspire.Hosting.Dotnet": "17.1.0-preview.1.26559.8" + } +} diff --git a/examples/dapr/CommunityToolkit.Aspire.Hosting.Azure.Dapr.AppHost.Go/go.mod b/examples/dapr/CommunityToolkit.Aspire.Hosting.Azure.Dapr.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/dapr/CommunityToolkit.Aspire.Hosting.Azure.Dapr.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/dapr/CommunityToolkit.Aspire.Hosting.Dapr.AppHost.Go/README.md b/examples/dapr/CommunityToolkit.Aspire.Hosting.Dapr.AppHost.Go/README.md new file mode 100644 index 000000000..5f44775f6 --- /dev/null +++ b/examples/dapr/CommunityToolkit.Aspire.Hosting.Dapr.AppHost.Go/README.md @@ -0,0 +1,23 @@ +# Dapr Go AppHost + +This is the Go counterpart of the adjacent TypeScript AppHost. It connects three +.NET services to Dapr state and pub/sub components and makes services B and C +wait for service A. + +Component references are configured on the sidecars through the exported +`ConfigureDaprSidecar` callback. This preserves the intended graph without using +the generic project `WithReference`, which does not accept Dapr components. + +Use the Aspire 17 preview CLI matching `aspire.config.json`, Go 1.26 or later, +the repository's .NET SDK, and the Dapr CLI. From this directory: + +```shell +aspire restore --non-interactive +go build ./... +go vet ./... +aspire publish --non-interactive +aspire start --isolated --non-interactive +``` + +Local execution additionally requires Docker and an initialized Dapr runtime. +The Dapr CLI must be on `PATH` even when publishing the model. diff --git a/examples/dapr/CommunityToolkit.Aspire.Hosting.Dapr.AppHost.Go/apphost.go b/examples/dapr/CommunityToolkit.Aspire.Hosting.Dapr.AppHost.Go/apphost.go new file mode 100644 index 000000000..b6c31894f --- /dev/null +++ b/examples/dapr/CommunityToolkit.Aspire.Hosting.Dapr.AppHost.Go/apphost.go @@ -0,0 +1,42 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + check(err) + + stateStore := builder.AddDaprStateStore("statestore") + pubSub := builder.AddDaprPubSub("pubsub") + check(stateStore.Err()) + check(pubSub.Err()) + + serviceA := builder.AddDotnetProject("servicea", "../CommunityToolkit.Aspire.Hosting.Dapr.ServiceA/CommunityToolkit.Aspire.Hosting.Dapr.ServiceA.csproj"). + ConfigureDaprSidecar(func(sidecar aspire.DaprSidecarResource) { + check(sidecar.WithReference(stateStore).WithReference(pubSub).Err()) + }) + check(serviceA.Err()) + serviceB := builder.AddDotnetProject("serviceb", "../CommunityToolkit.Aspire.Hosting.Dapr.ServiceB/CommunityToolkit.Aspire.Hosting.Dapr.ServiceB.csproj"). + ConfigureDaprSidecar(func(sidecar aspire.DaprSidecarResource) { + check(sidecar.WithReference(pubSub).Err()) + }).WaitFor(serviceA) + check(serviceB.Err()) + serviceC := builder.AddDotnetProject("servicec", "../CommunityToolkit.Aspire.Hosting.Dapr.ServiceC/CommunityToolkit.Aspire.Hosting.Dapr.ServiceC.csproj"). + ConfigureDaprSidecar(func(sidecar aspire.DaprSidecarResource) { + check(sidecar.WithReference(stateStore).Err()) + }).WaitFor(serviceA) + check(serviceC.Err()) + + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/dapr/CommunityToolkit.Aspire.Hosting.Dapr.AppHost.Go/aspire.config.json b/examples/dapr/CommunityToolkit.Aspire.Hosting.Dapr.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..b46f89619 --- /dev/null +++ b/examples/dapr/CommunityToolkit.Aspire.Hosting.Dapr.AppHost.Go/aspire.config.json @@ -0,0 +1,13 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Dapr": "../../../src/CommunityToolkit.Aspire.Hosting.Dapr/CommunityToolkit.Aspire.Hosting.Dapr.csproj", + "Aspire.Hosting.Dotnet": "17.1.0-preview.1.26559.8" + } +} diff --git a/examples/dapr/CommunityToolkit.Aspire.Hosting.Dapr.AppHost.Go/go.mod b/examples/dapr/CommunityToolkit.Aspire.Hosting.Dapr.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/dapr/CommunityToolkit.Aspire.Hosting.Dapr.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/data-api-builder/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.AppHost.Go/README.md b/examples/data-api-builder/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.AppHost.Go/README.md new file mode 100644 index 000000000..d665d8fed --- /dev/null +++ b/examples/data-api-builder/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.AppHost.Go/README.md @@ -0,0 +1,22 @@ +# Data API builder Go AppHost + +This is the Go counterpart of the adjacent TypeScript AppHost. It configures SQL +Server and the `trek` database, with two Data API builder resources referencing +the database and waiting for SQL Server. The second resource uses port 5001 and +two configuration files. + +The Go example reuses the configuration files and SQL initialization scripts +from the adjacent TypeScript AppHost rather than duplicating them. + +Use the Aspire 17 preview CLI matching `aspire.config.json` and Go 1.26 or later. +From this directory: + +```shell +aspire restore --non-interactive +go build ./... +go vet ./... +aspire publish --non-interactive +aspire start --isolated --non-interactive +``` + +Running the containers requires Docker. diff --git a/examples/data-api-builder/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.AppHost.Go/apphost.go b/examples/data-api-builder/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.AppHost.Go/apphost.go new file mode 100644 index 000000000..692915a4a --- /dev/null +++ b/examples/data-api-builder/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.AppHost.Go/apphost.go @@ -0,0 +1,51 @@ +package main + +import ( + "apphost/modules/aspire" + "log" + "path/filepath" +) + +func main() { + builder, err := aspire.CreateBuilder() + check(err) + + configDirectory, err := filepath.Abs("../CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.AppHost.TypeScript") + check(err) + primaryConfig := filepath.Join(configDirectory, "dab-config.json") + secondaryConfig := filepath.Join(configDirectory, "dab-config-2.json") + sql := builder.AddSqlServer("sql"). + WithBindMount(filepath.Join(configDirectory, "sql-server"), "/usr/config"). + WithBindMount("../database", "/docker-entrypoint-initdb.d"). + WithEntrypoint("/usr/config/entrypoint.sh") + check(sql.Err()) + db := sql.AddDatabase("trek") + check(db.Err()) + dab := builder.AddDataAPIBuilder("dab", &aspire.AddDataAPIBuilderOptions{ + ConfigFilePaths: []string{primaryConfig}, + }).WaitFor(sql).WithReference(db) + check(dab.Err()) + dabWithOptions := builder.AddDataAPIBuilder("dab-with-options", &aspire.AddDataAPIBuilderOptions{ + ConfigFilePaths: []string{primaryConfig, secondaryConfig}, HttpPort: aspire.Float64Ptr(5001), + }).WaitFor(sql).WithReference(db) + check(dabWithOptions.Err()) + + check(dab.PrimaryEndpoint().Err()) + check(dab.Host().Err()) + check(dab.Port().Err()) + _ = dab.UriExpression() + check(dabWithOptions.PrimaryEndpoint().Err()) + check(dabWithOptions.Host().Err()) + check(dabWithOptions.Port().Err()) + _ = dabWithOptions.UriExpression() + + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/data-api-builder/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.AppHost.Go/aspire.config.json b/examples/data-api-builder/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..cfee35369 --- /dev/null +++ b/examples/data-api-builder/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.AppHost.Go/aspire.config.json @@ -0,0 +1,13 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder": "../../../src/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.csproj", + "Aspire.Hosting.SqlServer": "17.1.0-preview.1.26559.8" + } +} diff --git a/examples/data-api-builder/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.AppHost.Go/go.mod b/examples/data-api-builder/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/data-api-builder/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost.Go/README.md b/examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost.Go/README.md new file mode 100644 index 000000000..091155b87 --- /dev/null +++ b/examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost.Go/README.md @@ -0,0 +1,20 @@ +# DbGate Go AppHost + +This is the Go counterpart of the adjacent TypeScript AppHost. It configures a +DbGate container on port 3310 with a temporary data bind mount and accesses its +primary endpoint. + +Use the Aspire 17 preview CLI matching `aspire.config.json` and Go 1.26 or later. +From this directory: + +```shell +aspire restore --non-interactive +go build ./... +go vet ./... +aspire publish --non-interactive +aspire start --isolated --non-interactive +``` + +Running the container requires Docker. Keep `ASPIRE_COMPILE_ONLY` unset for +execution: its optional volume call only covers the exported API, because the +volume and bind mount would otherwise overlap. diff --git a/examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost.Go/apphost.go b/examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost.Go/apphost.go new file mode 100644 index 000000000..77f74c6ed --- /dev/null +++ b/examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost.Go/apphost.go @@ -0,0 +1,35 @@ +package main + +import ( + "apphost/modules/aspire" + "log" + "os" +) + +func main() { + bindMountPath, err := os.MkdirTemp("", "dbgate-") + check(err) + builder, err := aspire.CreateBuilder() + check(err) + + dbgate := builder.AddDbGate(&aspire.AddDbGateOptions{Name: aspire.StringPtr("dbgate")}). + WithHostPort(aspire.Float64Ptr(3310)). + WithDataBindMount(bindMountPath, &aspire.WithDataBindMountOptions{IsReadOnly: aspire.BoolPtr(false)}) + check(dbgate.Err()) + // A bind mount and volume would otherwise overlap on the same target path. + if os.Getenv("ASPIRE_COMPILE_ONLY") == "1" { + dbgate.WithDataVolume(&aspire.WithDataVolumeOptions{Name: aspire.StringPtr("dbgate-data")}) + check(dbgate.Err()) + } + check(dbgate.PrimaryEndpoint().Err()) + + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost.Go/aspire.config.json b/examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..0eb126e92 --- /dev/null +++ b/examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.DbGate": "../../../src/CommunityToolkit.Aspire.Hosting.DbGate/CommunityToolkit.Aspire.Hosting.DbGate.csproj" + } +} diff --git a/examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost.Go/go.mod b/examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/dbgate/CommunityToolkit.Aspire.Hosting.DbGate.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/deno/CommunityToolkit.Aspire.Hosting.Deno.AppHost.Go/README.md b/examples/deno/CommunityToolkit.Aspire.Hosting.Deno.AppHost.Go/README.md new file mode 100644 index 000000000..5f9f1d9a8 --- /dev/null +++ b/examples/deno/CommunityToolkit.Aspire.Hosting.Deno.AppHost.Go/README.md @@ -0,0 +1,18 @@ +# Deno Go AppHost + +This is the Go counterpart of the adjacent TypeScript AppHost. It runs the +existing Vite and Oak examples with the same tasks, Deno permissions, HTTP +endpoints, health checks, and package installation. + +Use the Aspire 17 preview CLI matching `aspire.config.json` and Go 1.26 or later. +From this directory: + +```shell +aspire restore --non-interactive +go build ./... +go vet ./... +aspire publish --non-interactive +aspire start --isolated --non-interactive +``` + +Running the applications additionally requires Deno on `PATH`. diff --git a/examples/deno/CommunityToolkit.Aspire.Hosting.Deno.AppHost.Go/apphost.go b/examples/deno/CommunityToolkit.Aspire.Hosting.Deno.AppHost.Go/apphost.go new file mode 100644 index 000000000..62d9f2420 --- /dev/null +++ b/examples/deno/CommunityToolkit.Aspire.Hosting.Deno.AppHost.Go/apphost.go @@ -0,0 +1,34 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + check(err) + + vite := builder.AddDenoTask("vite-demo", &aspire.AddDenoTaskOptions{TaskName: aspire.StringPtr("dev")}). + WithDenoPackageInstallation(). + WithHttpEndpoint(&aspire.WithHttpEndpointOptions{Env: aspire.StringPtr("PORT")}). + WithEndpoint(). + WithHttpHealthCheck(&aspire.WithHttpHealthCheckOptions{Path: aspire.StringPtr("/")}) + check(vite.Err()) + oak := builder.AddDenoApp("oak-demo", "main.ts", &aspire.AddDenoAppOptions{ + PermissionFlags: []string{"--allow-env", "--allow-net"}, + }).WithHttpEndpoint(&aspire.WithHttpEndpointOptions{Env: aspire.StringPtr("PORT")}). + WithEndpoint(). + WithHttpHealthCheck(&aspire.WithHttpHealthCheckOptions{Path: aspire.StringPtr("/health")}) + check(oak.Err()) + + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/deno/CommunityToolkit.Aspire.Hosting.Deno.AppHost.Go/aspire.config.json b/examples/deno/CommunityToolkit.Aspire.Hosting.Deno.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..bceb4dffc --- /dev/null +++ b/examples/deno/CommunityToolkit.Aspire.Hosting.Deno.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Deno": "../../../src/CommunityToolkit.Aspire.Hosting.Deno/CommunityToolkit.Aspire.Hosting.Deno.csproj" + } +} diff --git a/examples/deno/CommunityToolkit.Aspire.Hosting.Deno.AppHost.Go/go.mod b/examples/deno/CommunityToolkit.Aspire.Hosting.Deno.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/deno/CommunityToolkit.Aspire.Hosting.Deno.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/elasticsearch-ext/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions.AppHost.Go/README.md b/examples/elasticsearch-ext/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions.AppHost.Go/README.md new file mode 100644 index 000000000..183c7c850 --- /dev/null +++ b/examples/elasticsearch-ext/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions.AppHost.Go/README.md @@ -0,0 +1,19 @@ +# Elasticsearch extensions Go AppHost + +This is the Go counterpart of the adjacent TypeScript AppHost. It provides +compile-time coverage of `WithElasticvue`, its configuration callback, and +`WithHostPort(9280)`. + +The Elasticsearch creation API is not exported by the preview SDK. Like the +TypeScript example, this AppHost deliberately creates no resources; the helper +functions validate the exported extension signatures without invoking them. + +Use the Aspire 17 preview CLI matching `aspire.config.json` and Go 1.26 or later. +From this directory: + +```shell +aspire restore --non-interactive +go build ./... +go vet ./... +aspire publish --non-interactive +``` diff --git a/examples/elasticsearch-ext/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions.AppHost.Go/apphost.go b/examples/elasticsearch-ext/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions.AppHost.Go/apphost.go new file mode 100644 index 000000000..175738102 --- /dev/null +++ b/examples/elasticsearch-ext/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions.AppHost.Go/apphost.go @@ -0,0 +1,34 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func validateWithHostPort(elasticvue aspire.ElasticvueContainerResource) aspire.ElasticvueContainerResource { + return elasticvue.WithHostPort(aspire.Float64Ptr(9280)) +} + +// Keep extension coverage compile-only until the base Elasticsearch add API is exported. +func validateWithElasticvue(elasticsearch aspire.ElasticsearchResource) aspire.ElasticsearchResource { + return elasticsearch.WithElasticvue(&aspire.WithElasticvueOptions{ + ContainerName: aspire.StringPtr("elasticvue-ui"), + ConfigureContainer: func(elasticvue aspire.ElasticvueContainerResource) { + check(validateWithHostPort(elasticvue).Err()) + }, + }) +} + +func main() { + builder, err := aspire.CreateBuilder() + check(err) + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/elasticsearch-ext/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions.AppHost.Go/aspire.config.json b/examples/elasticsearch-ext/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..7905b7d66 --- /dev/null +++ b/examples/elasticsearch-ext/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions": "../../../src/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions.csproj" + } +} diff --git a/examples/elasticsearch-ext/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions.AppHost.Go/go.mod b/examples/elasticsearch-ext/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/elasticsearch-ext/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules From 257aefe4314ee5f555961117d0f4732afb20715a Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 15:47:56 +1100 Subject: [PATCH 21/26] feat(examples): add third batch of Go AppHosts Add Perl, PowerShell, Rust, SQL Database Projects, Stripe, SurrealDB, and Zitadel counterparts preserving the TypeScript graphs. Document preview limitations for SQL native loading and optional Zitadel resource serialization. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../CpanmApiIntegration.AppHost.Go/apphost.go | 37 +++++++++ .../aspire.config.json | 12 +++ .../CpanmApiIntegration.AppHost.Go/go.mod | 7 ++ .../apphost.go | 40 ++++++++++ .../aspire.config.json | 12 +++ .../go.mod | 7 ++ .../apphost.go | 36 +++++++++ .../aspire.config.json | 12 +++ .../go.mod | 7 ++ .../README.md | 18 +++++ .../apphost.go | 55 +++++++++++++ .../aspire.config.json | 13 ++++ .../go.mod | 7 ++ .../apphost.go | 59 ++++++++++++++ .../aspire.config.json | 12 +++ .../go.mod | 7 ++ .../apphost.go | 77 +++++++++++++++++++ .../aspire.config.json | 12 +++ .../data/.gitkeep | 0 .../go.mod | 7 ++ .../seed.surql | 1 + .../README.md | 20 +++++ .../apphost.go | 49 ++++++++++++ .../aspire.config.json | 13 ++++ .../go.mod | 7 ++ 25 files changed, 527 insertions(+) create mode 100644 examples/perl/CpanmApiIntegration.AppHost.Go/apphost.go create mode 100644 examples/perl/CpanmApiIntegration.AppHost.Go/aspire.config.json create mode 100644 examples/perl/CpanmApiIntegration.AppHost.Go/go.mod create mode 100644 examples/powershell/CommunityToolkit.Aspire.PowerShell.AppHost.Go/apphost.go create mode 100644 examples/powershell/CommunityToolkit.Aspire.PowerShell.AppHost.Go/aspire.config.json create mode 100644 examples/powershell/CommunityToolkit.Aspire.PowerShell.AppHost.Go/go.mod create mode 100644 examples/rust/CommunityToolkit.Aspire.Hosting.Rust.AppHost.Go/apphost.go create mode 100644 examples/rust/CommunityToolkit.Aspire.Hosting.Rust.AppHost.Go/aspire.config.json create mode 100644 examples/rust/CommunityToolkit.Aspire.Hosting.Rust.AppHost.Go/go.mod create mode 100644 examples/sql-database-projects/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.AppHost.Go/README.md create mode 100644 examples/sql-database-projects/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.AppHost.Go/apphost.go create mode 100644 examples/sql-database-projects/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.AppHost.Go/aspire.config.json create mode 100644 examples/sql-database-projects/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.AppHost.Go/go.mod create mode 100644 examples/stripe/CommunityToolkit.Aspire.Hosting.Stripe.AppHost.Go/apphost.go create mode 100644 examples/stripe/CommunityToolkit.Aspire.Hosting.Stripe.AppHost.Go/aspire.config.json create mode 100644 examples/stripe/CommunityToolkit.Aspire.Hosting.Stripe.AppHost.Go/go.mod create mode 100644 examples/surrealdb/CommunityToolkit.Aspire.Hosting.SurrealDb.AppHost.Go/apphost.go create mode 100644 examples/surrealdb/CommunityToolkit.Aspire.Hosting.SurrealDb.AppHost.Go/aspire.config.json create mode 100644 examples/surrealdb/CommunityToolkit.Aspire.Hosting.SurrealDb.AppHost.Go/data/.gitkeep create mode 100644 examples/surrealdb/CommunityToolkit.Aspire.Hosting.SurrealDb.AppHost.Go/go.mod create mode 100644 examples/surrealdb/CommunityToolkit.Aspire.Hosting.SurrealDb.AppHost.Go/seed.surql create mode 100644 examples/zitadel/CommunityToolkit.Aspire.Hosting.Zitadel.AppHost.Go/README.md create mode 100644 examples/zitadel/CommunityToolkit.Aspire.Hosting.Zitadel.AppHost.Go/apphost.go create mode 100644 examples/zitadel/CommunityToolkit.Aspire.Hosting.Zitadel.AppHost.Go/aspire.config.json create mode 100644 examples/zitadel/CommunityToolkit.Aspire.Hosting.Zitadel.AppHost.Go/go.mod diff --git a/examples/perl/CpanmApiIntegration.AppHost.Go/apphost.go b/examples/perl/CpanmApiIntegration.AppHost.Go/apphost.go new file mode 100644 index 000000000..bb37d2fa5 --- /dev/null +++ b/examples/perl/CpanmApiIntegration.AppHost.Go/apphost.go @@ -0,0 +1,37 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + check(err) + + perlAPI := builder.AddPerlApi("perl-api", ".", "../scripts/API.pl") + perlAPI.WithCpanMinus() + perlAPI.WithPackage("Mojolicious::Lite", &aspire.WithPackageOptions{ + Force: aspire.BoolPtr(true), SkipTest: aspire.BoolPtr(true), + }) + perlAPI.WithLocalLib(&aspire.WithLocalLibOptions{Path: aspire.StringPtr("local")}) + perlAPI.WithHttpEndpoint(&aspire.WithHttpEndpointOptions{ + Name: aspire.StringPtr("http"), Env: aspire.StringPtr("PORT"), + }) + check(perlAPI.Err()) + + driver := builder.AddPerlScript("perl-driver", "../scripts", "driver.pl") + driver.WithEnvironment("API_URL", perlAPI.GetEndpoint("http")) + driver.WithReference(perlAPI).WaitFor(perlAPI) + check(driver.Err()) + + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/perl/CpanmApiIntegration.AppHost.Go/aspire.config.json b/examples/perl/CpanmApiIntegration.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..c920d7c25 --- /dev/null +++ b/examples/perl/CpanmApiIntegration.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Perl": "../../../src/CommunityToolkit.Aspire.Hosting.Perl/CommunityToolkit.Aspire.Hosting.Perl.csproj" + } +} diff --git a/examples/perl/CpanmApiIntegration.AppHost.Go/go.mod b/examples/perl/CpanmApiIntegration.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/perl/CpanmApiIntegration.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/powershell/CommunityToolkit.Aspire.PowerShell.AppHost.Go/apphost.go b/examples/powershell/CommunityToolkit.Aspire.PowerShell.AppHost.Go/apphost.go new file mode 100644 index 000000000..7b6addff5 --- /dev/null +++ b/examples/powershell/CommunityToolkit.Aspire.PowerShell.AppHost.Go/apphost.go @@ -0,0 +1,40 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + check(err) + + blob := builder.AddConnectionString("myblob") + check(blob.Err()) + powershell := builder.AddPowerShell("ps").WaitFor(blob) + check(powershell.Err()) + + script1 := powershell.AddScript("script1", + "\nparam($name)\n"+ + "write-information \"Hello, $name\"\n"+ + "write-information \"`$myblob is $myblob\"\n") + script1.WithReference(blob).WithArgs([]string{"world"}) + check(script1.Err()) + + script2 := powershell.AddScript("script2", ` +write-information "Getting there..." +write-information "the sum of 2 and 3 is $([int]$args[0] + [int]$args[1])" +`) + script2.WithArgs([]string{"2", "3"}).WaitForCompletion(script1) + check(script2.Err()) + + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/powershell/CommunityToolkit.Aspire.PowerShell.AppHost.Go/aspire.config.json b/examples/powershell/CommunityToolkit.Aspire.PowerShell.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..03c7849e7 --- /dev/null +++ b/examples/powershell/CommunityToolkit.Aspire.PowerShell.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.PowerShell": "../../../src/CommunityToolkit.Aspire.Hosting.PowerShell/CommunityToolkit.Aspire.Hosting.PowerShell.csproj" + } +} diff --git a/examples/powershell/CommunityToolkit.Aspire.PowerShell.AppHost.Go/go.mod b/examples/powershell/CommunityToolkit.Aspire.PowerShell.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/powershell/CommunityToolkit.Aspire.PowerShell.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/rust/CommunityToolkit.Aspire.Hosting.Rust.AppHost.Go/apphost.go b/examples/rust/CommunityToolkit.Aspire.Hosting.Rust.AppHost.Go/apphost.go new file mode 100644 index 000000000..25d4079aa --- /dev/null +++ b/examples/rust/CommunityToolkit.Aspire.Hosting.Rust.AppHost.Go/apphost.go @@ -0,0 +1,36 @@ +package main + +import ( + "apphost/modules/aspire" + "log" + "os" +) + +func main() { + builder, err := aspire.CreateBuilder() + check(err) + + rust := builder.AddRustApp("rust-app", "../actix_api") + rust.WithHttpEndpoint(&aspire.WithHttpEndpointOptions{Env: aspire.StringPtr("PORT")}) + rust.WithExternalHttpEndpoints() + rust.WithHttpHealthCheck(&aspire.WithHttpHealthCheckOptions{Path: aspire.StringPtr("/")}) + check(rust.Err()) + + if os.Getenv("RUST_ARGS_VALIDATION") == "1" { + withArgs := builder.AddRustApp("rust-app-with-args", "../actix_api", &aspire.AddRustAppOptions{ + Args: []string{"--", "--help"}, + }) + withArgs.WithHttpEndpoint(&aspire.WithHttpEndpointOptions{Env: aspire.StringPtr("PORT")}) + check(withArgs.Err()) + } + + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/rust/CommunityToolkit.Aspire.Hosting.Rust.AppHost.Go/aspire.config.json b/examples/rust/CommunityToolkit.Aspire.Hosting.Rust.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..1e7066eeb --- /dev/null +++ b/examples/rust/CommunityToolkit.Aspire.Hosting.Rust.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Rust": "../../../src/CommunityToolkit.Aspire.Hosting.Rust/CommunityToolkit.Aspire.Hosting.Rust.csproj" + } +} diff --git a/examples/rust/CommunityToolkit.Aspire.Hosting.Rust.AppHost.Go/go.mod b/examples/rust/CommunityToolkit.Aspire.Hosting.Rust.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/rust/CommunityToolkit.Aspire.Hosting.Rust.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/sql-database-projects/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.AppHost.Go/README.md b/examples/sql-database-projects/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.AppHost.Go/README.md new file mode 100644 index 000000000..589aafe20 --- /dev/null +++ b/examples/sql-database-projects/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.AppHost.Go/README.md @@ -0,0 +1,18 @@ +# SQL Database Projects Go AppHost + +This example mirrors the adjacent TypeScript AppHost, including both a managed SQL database and an existing connection string. + +With Go 1.26 or later and the Aspire CLI version pinned in `aspire.config.json`, run: + +```shell +aspire restore --non-interactive +go test ./... +go vet ./... +aspire publish --list-steps --non-interactive +``` + +Run the application with `aspire run`. Configure `ConnectionStrings:Aspire` for the existing database before running. + +## Preview limitation + +SDK generation and Go compilation succeed with `17.1.0-preview.1.26559.8`. On the Windows prebuilt AppHost server, model construction currently fails in `SqlProjectBuilderExtensions` when `MSBuildLocator.RegisterDefaults()` cannot load the native `hostfxr` library. Adding the installed `hostfxr.dll` directory to `PATH` did not resolve this failure. The example retains both SQL project resources rather than omitting them to bypass the prerequisite failure. diff --git a/examples/sql-database-projects/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.AppHost.Go/apphost.go b/examples/sql-database-projects/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.AppHost.Go/apphost.go new file mode 100644 index 000000000..c704b7002 --- /dev/null +++ b/examples/sql-database-projects/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.AppHost.Go/apphost.go @@ -0,0 +1,55 @@ +package main + +import ( + "apphost/modules/aspire" + "log" + "path/filepath" +) + +func main() { + builder, err := aspire.CreateBuilder() + check(err) + + appHostDirectory, err := builder.AppHostDirectory() + check(err) + projectPath := filepath.Join(appHostDirectory, "..", "SdkProject", "SdkProject.csproj") + profilePath := filepath.Join(appHostDirectory, "..", "SdkProject", "Database.publish.xml") + + sql := builder.AddSqlServer("sql") + database := sql.AddDatabase("TargetDatabase") + existingConnection := builder.AddConnectionString("Aspire") + check(sql.Err()) + check(database.Err()) + check(existingConnection.Err()) + + project := builder.AddSqlProject("database-project") + project.WithDacpac(projectPath) + project.WithDacDeployOptions(profilePath) + project.WithSkipWhenDeployed() + project.WithReference(database) + check(project.Err()) + + connectionProject := builder.AddSqlProject("connection-project") + connectionProject.WithDacpac(projectPath) + connectionProject.WithConnectionReference(existingConnection) + check(connectionProject.Err()) + + _, err = project.DacpacPath() + check(err) + _, err = project.DacDeployOptionsPath() + check(err) + _, err = project.SkipWhenDeployed() + check(err) + _, err = connectionProject.DacpacPath() + check(err) + + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/sql-database-projects/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.AppHost.Go/aspire.config.json b/examples/sql-database-projects/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..9fef8766a --- /dev/null +++ b/examples/sql-database-projects/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.AppHost.Go/aspire.config.json @@ -0,0 +1,13 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "Aspire.Hosting.SqlServer": "", + "CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects": "../../../src/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.csproj" + } +} diff --git a/examples/sql-database-projects/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.AppHost.Go/go.mod b/examples/sql-database-projects/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/sql-database-projects/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/stripe/CommunityToolkit.Aspire.Hosting.Stripe.AppHost.Go/apphost.go b/examples/stripe/CommunityToolkit.Aspire.Hosting.Stripe.AppHost.Go/apphost.go new file mode 100644 index 000000000..28ae5c1e4 --- /dev/null +++ b/examples/stripe/CommunityToolkit.Aspire.Hosting.Stripe.AppHost.Go/apphost.go @@ -0,0 +1,59 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + check(err) + + apiKey := builder.AddParameter("stripe-api-key", &aspire.AddParameterOptions{ + Value: aspire.StringPtr("sk_test_123"), Secret: aspire.BoolPtr(true), + }) + override := builder.AddParameter("stripe-api-key-override", &aspire.AddParameterOptions{ + Value: aspire.StringPtr("sk_test_override"), Secret: aspire.BoolPtr(true), + }) + target := builder.AddContainer("webhook-target", "nginx:alpine") + target.WithHttpEndpoint(&aspire.WithHttpEndpointOptions{ + TargetPort: aspire.Float64Ptr(80), Name: aspire.StringPtr("http"), + }) + external := builder.AddExternalService("external-target", "http://localhost:5082") + consumer := builder.AddContainer("consumer", "nginx:alpine") + check(apiKey.Err()) + check(override.Err()) + check(target.Err()) + check(external.Err()) + check(consumer.Err()) + + stripe := builder.AddStripe("stripe", apiKey) + stripe.WithListen(target, &aspire.WithListenOptions{ + WebhookPath: aspire.StringPtr("/webhooks/stripe"), Events: []string{"payment_intent.created"}, + }) + stripe.WithApiKey(override) + check(stripe.Err()) + + stripeExternal := builder.AddStripe("stripe-external", apiKey) + stripeExternal.WithListenExternalService(external, &aspire.WithListenExternalServiceOptions{ + WebhookPath: aspire.StringPtr("/webhooks/external"), Events: []string{"payment_intent.created", "charge.succeeded"}, + }) + check(stripeExternal.Err()) + + consumer.WithStripeReference(stripe, &aspire.WithStripeReferenceOptions{ + WebhookSigningSecretEnvVarName: aspire.StringPtr("CUSTOM_STRIPE_SECRET"), + }) + check(consumer.Err()) + _, err = stripe.WebhookSigningSecret() + check(err) + + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/stripe/CommunityToolkit.Aspire.Hosting.Stripe.AppHost.Go/aspire.config.json b/examples/stripe/CommunityToolkit.Aspire.Hosting.Stripe.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..504c567dc --- /dev/null +++ b/examples/stripe/CommunityToolkit.Aspire.Hosting.Stripe.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Stripe": "../../../src/CommunityToolkit.Aspire.Hosting.Stripe/CommunityToolkit.Aspire.Hosting.Stripe.csproj" + } +} diff --git a/examples/stripe/CommunityToolkit.Aspire.Hosting.Stripe.AppHost.Go/go.mod b/examples/stripe/CommunityToolkit.Aspire.Hosting.Stripe.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/stripe/CommunityToolkit.Aspire.Hosting.Stripe.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/surrealdb/CommunityToolkit.Aspire.Hosting.SurrealDb.AppHost.Go/apphost.go b/examples/surrealdb/CommunityToolkit.Aspire.Hosting.SurrealDb.AppHost.Go/apphost.go new file mode 100644 index 000000000..d3cabd9b0 --- /dev/null +++ b/examples/surrealdb/CommunityToolkit.Aspire.Hosting.SurrealDb.AppHost.Go/apphost.go @@ -0,0 +1,77 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + check(err) + + primary := builder.AddSurrealServer("primary", &aspire.AddSurrealServerOptions{ + Port: aspire.Float64Ptr(18000), Path: aspire.StringPtr("memory"), + }) + primary.WithDataVolume(&aspire.WithDataVolumeOptions{Name: aspire.StringPtr("surreal-primary-data")}) + primary.WithLogLevel("Information") + primary.WithSurrealist(&aspire.WithSurrealistOptions{ContainerName: aspire.StringPtr("surrealist-primary")}) + primary.WithSurrealDbOtlpExporter() + check(primary.Err()) + + namespace := primary.AddNamespace("appns", &aspire.AddNamespaceOptions{NamespaceName: aspire.StringPtr("polyglotNs")}) + namespace.WithCreationScript("DEFINE NAMESPACE IF NOT EXISTS `polyglotNs`;") + check(namespace.Err()) + database := namespace.AddDatabase("appdb", &aspire.AddDatabaseOptions{DatabaseName: aspire.StringPtr("polyglotDb")}) + database.WithCreationScript("DEFINE DATABASE IF NOT EXISTS `polyglotDb`;") + check(database.Err()) + + mounted := builder.AddSurrealServer("mounted", &aspire.AddSurrealServerOptions{ + Port: aspire.Float64Ptr(18001), Path: aspire.StringPtr("memory"), + }) + mounted.WithDataBindMount("./data") + mounted.WithInitFiles("./seed.surql") + check(mounted.Err()) + + check(primary.PrimaryEndpoint().Err()) + check(primary.Host().Err()) + check(primary.Port().Err()) + check(primary.PasswordParameter().Err()) + _ = primary.UriExpression() + _ = primary.ConnectionStringExpression() + + namespaceParent := namespace.Parent() + check(namespaceParent.Err()) + _ = namespace.ConnectionStringExpression() + _, err = namespace.NamespaceName() + check(err) + _, err = namespaceParent.Name() + check(err) + + databaseParent := database.Parent() + check(databaseParent.Err()) + _ = database.ConnectionStringExpression() + _, err = database.DatabaseName() + check(err) + _, err = databaseParent.Name() + check(err) + server := databaseParent.Parent() + check(server.Err()) + _, err = server.Name() + check(err) + + check(mounted.PrimaryEndpoint().Err()) + check(mounted.Host().Err()) + check(mounted.Port().Err()) + _ = mounted.UriExpression() + _ = mounted.ConnectionStringExpression() + + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/surrealdb/CommunityToolkit.Aspire.Hosting.SurrealDb.AppHost.Go/aspire.config.json b/examples/surrealdb/CommunityToolkit.Aspire.Hosting.SurrealDb.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..522335ca2 --- /dev/null +++ b/examples/surrealdb/CommunityToolkit.Aspire.Hosting.SurrealDb.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.SurrealDb": "../../../src/CommunityToolkit.Aspire.Hosting.SurrealDb/CommunityToolkit.Aspire.Hosting.SurrealDb.csproj" + } +} diff --git a/examples/surrealdb/CommunityToolkit.Aspire.Hosting.SurrealDb.AppHost.Go/data/.gitkeep b/examples/surrealdb/CommunityToolkit.Aspire.Hosting.SurrealDb.AppHost.Go/data/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/examples/surrealdb/CommunityToolkit.Aspire.Hosting.SurrealDb.AppHost.Go/go.mod b/examples/surrealdb/CommunityToolkit.Aspire.Hosting.SurrealDb.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/surrealdb/CommunityToolkit.Aspire.Hosting.SurrealDb.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/surrealdb/CommunityToolkit.Aspire.Hosting.SurrealDb.AppHost.Go/seed.surql b/examples/surrealdb/CommunityToolkit.Aspire.Hosting.SurrealDb.AppHost.Go/seed.surql new file mode 100644 index 000000000..851797071 --- /dev/null +++ b/examples/surrealdb/CommunityToolkit.Aspire.Hosting.SurrealDb.AppHost.Go/seed.surql @@ -0,0 +1 @@ +DEFINE TABLE IF NOT EXISTS polyglot SCHEMALESS; diff --git a/examples/zitadel/CommunityToolkit.Aspire.Hosting.Zitadel.AppHost.Go/README.md b/examples/zitadel/CommunityToolkit.Aspire.Hosting.Zitadel.AppHost.Go/README.md new file mode 100644 index 000000000..a374c5347 --- /dev/null +++ b/examples/zitadel/CommunityToolkit.Aspire.Hosting.Zitadel.AppHost.Go/README.md @@ -0,0 +1,20 @@ +# Zitadel Go AppHost + +This example mirrors the adjacent TypeScript AppHost, including explicit admin parameters, a managed database, and a second Zitadel instance using an existing database. + +With Go 1.26 or later and the Aspire CLI version pinned in `aspire.config.json`, run: + +```shell +aspire restore --non-interactive +go test ./... +go vet ./... +aspire publish --list-steps --non-interactive +``` + +Run the application with `aspire run` once the preview limitation below is resolved. The fixed credentials match the TypeScript example and are for local demonstration only. + +## Preview limitation + +SDK generation and Go compilation succeed with `17.1.0-preview.1.26559.8`, but model construction fails at `AddZitadel`. The generated options expose optional resource arguments as pointers to interfaces (`*ParameterResource`), then serialize those pointers without unwrapping their resource handles. The AppHost server rejects `username` with `INVALID_ARGUMENT`, reporting that `IResourceBuilder` requires an `AspireDto` attribute. + +This example preserves the explicit username, password, and master-key parameters. Resolving the failure requires an Aspire Go SDK serialization fix; do not edit the generated `.aspire/modules` files or drop the parameters to hide it. diff --git a/examples/zitadel/CommunityToolkit.Aspire.Hosting.Zitadel.AppHost.Go/apphost.go b/examples/zitadel/CommunityToolkit.Aspire.Hosting.Zitadel.AppHost.Go/apphost.go new file mode 100644 index 000000000..947c41c68 --- /dev/null +++ b/examples/zitadel/CommunityToolkit.Aspire.Hosting.Zitadel.AppHost.Go/apphost.go @@ -0,0 +1,49 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + check(err) + + username := builder.AddParameter("zitadel-username", &aspire.AddParameterOptions{Value: aspire.StringPtr("admin")}) + password := builder.AddParameter("zitadel-password", &aspire.AddParameterOptions{ + Value: aspire.StringPtr("AspireZitadelPassword!1"), Secret: aspire.BoolPtr(true), + }) + masterKey := builder.AddParameter("zitadel-master-key", &aspire.AddParameterOptions{ + Value: aspire.StringPtr("Aa1!Aa1!Aa1!Aa1!Aa1!Aa1!Aa1!Aa1!"), Secret: aspire.BoolPtr(true), + }) + check(username.Err()) + check(password.Err()) + check(masterKey.Err()) + + postgres := builder.AddPostgres("postgres") + existingDatabase := postgres.AddDatabase("zitadel-existing-db") + check(postgres.Err()) + check(existingDatabase.Err()) + + zitadel := builder.AddZitadel("zitadel", &aspire.AddZitadelOptions{ + Port: aspire.Float64Ptr(8080), Username: &username, Password: &password, MasterKey: &masterKey, + }) + zitadel.WithExternalDomain("auth.example.com") + zitadel.WithDatabase(postgres, &aspire.WithDatabaseOptions{DatabaseName: aspire.StringPtr("zitadel-db")}) + check(zitadel.Err()) + + minimal := builder.AddZitadel("zitadel-minimal") + minimal.WithExistingDatabase(existingDatabase) + minimal.WithExternalDomain("login.example.com") + check(minimal.Err()) + + app, err := builder.Build() + check(err) + check(app.Run()) +} + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/zitadel/CommunityToolkit.Aspire.Hosting.Zitadel.AppHost.Go/aspire.config.json b/examples/zitadel/CommunityToolkit.Aspire.Hosting.Zitadel.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..c74c6cab0 --- /dev/null +++ b/examples/zitadel/CommunityToolkit.Aspire.Hosting.Zitadel.AppHost.Go/aspire.config.json @@ -0,0 +1,13 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Zitadel": "../../../src/CommunityToolkit.Aspire.Hosting.Zitadel/CommunityToolkit.Aspire.Hosting.Zitadel.csproj", + "Aspire.Hosting.PostgreSQL": "" + } +} diff --git a/examples/zitadel/CommunityToolkit.Aspire.Hosting.Zitadel.AppHost.Go/go.mod b/examples/zitadel/CommunityToolkit.Aspire.Hosting.Zitadel.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/zitadel/CommunityToolkit.Aspire.Hosting.Zitadel.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules From bbd10ce1d7d5742c7761c39e8327a0f0cc1261ba Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 16:07:03 +1100 Subject: [PATCH 22/26] test: register Go AppHost batch coverage Document ActiveMQ and SQL model validation limitations. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- docs/go-apphost-parity.md | 15 ++++++++++++--- .../GoAppHostTests.cs | 16 ++++++++++++++++ .../GoAppHostTests.cs | 15 +++++++++++++++ .../GoAppHostTests.cs | 15 +++++++++++++++ .../GoAppHostTests.cs | 15 +++++++++++++++ .../GoAppHostTests.cs | 15 +++++++++++++++ .../GoAppHostTests.cs | 15 +++++++++++++++ .../GoAppHostTests.cs | 15 +++++++++++++++ .../GoAppHostTests.cs | 15 +++++++++++++++ .../GoAppHostTests.cs | 15 +++++++++++++++ .../GoAppHostTests.cs | 15 +++++++++++++++ .../GoAppHostTests.cs | 15 +++++++++++++++ .../GoAppHostTests.cs | 16 ++++++++++++++++ .../GoAppHostTests.cs | 15 +++++++++++++++ .../GoAppHostTests.cs | 15 +++++++++++++++ .../GoAppHostTests.cs | 16 ++++++++++++++++ 16 files changed, 240 insertions(+), 3 deletions(-) create mode 100644 tests/CommunityToolkit.Aspire.Hosting.ActiveMQ.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Azure.Dapr.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Azure.Extensions.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Dapr.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.DbGate.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Deno.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Perl.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.PowerShell.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Rust.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Stripe.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.SurrealDb.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Zitadel.Tests/GoAppHostTests.cs diff --git a/docs/go-apphost-parity.md b/docs/go-apphost-parity.md index 8f628a3b8..59f98bf9f 100644 --- a/docs/go-apphost-parity.md +++ b/docs/go-apphost-parity.md @@ -42,9 +42,13 @@ repository's pinned Aspire SDK (`17.1.0-preview.1.26559.8`): `proxyToken` and the same `IResourceBuilder\`1` deserialization error. - **Zitadel:** `AddZitadelOptions.Username`, `Password`, and `MasterKey` are optional parameter-resource pointers. Supplying the credentials used by the - TypeScript example fails ATS model construction with `INVALID_ARGUMENT` due - to the same pointer-to-interface serialization defect. Omitting them would - change the example. + TypeScript example fails `CommunityToolkit.Aspire.Hosting.Zitadel/addZitadel` + with `INVALID_ARGUMENT` for `username` due to the same pointer-to-interface + serialization defect. Omitting them would change the example. +- **ActiveMQ:** `AddActiveMQOptions.UserName` is a pointer to a parameter + interface. `addActiveMQ` fails with `INVALID_ARGUMENT` for `userName` during + model construction when the TypeScript example's explicit credentials are + supplied. Its Go counterpart retains those credentials and is compile-tested. - **Flare:** `WithPublicUrlsOptions.DashboardUrl` is a pointer to a parameter interface. Passing the example's `flare-dashboard-url` fails model construction because of the same serialization defect; omitting it changes @@ -64,3 +68,8 @@ publish-model check; fixing the integration is outside this example-only work. Floci's `WithServiceBus` is run-only by design and throws in publish mode, as documented in the Floci README. Validate the example's Service Bus graph in run mode rather than treating its publish-mode rejection as a Go SDK parity gap. + +On Windows, the SQL Database Projects Go example compiles but its model fails +when `MSBuildLocator.RegisterDefaults()` cannot load native `hostfxr`. Its +publish-model test is disabled on Windows; this is an environment/native-loading +issue, not the parameter serialization issue above. diff --git a/tests/CommunityToolkit.Aspire.Hosting.ActiveMQ.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.ActiveMQ.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..d06d7e036 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.ActiveMQ.Tests/GoAppHostTests.cs @@ -0,0 +1,16 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.ActiveMQ.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.ActiveMQ.AppHost.Go", + exampleName: "activemq", + validatePublish: false, + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Azure.Dapr.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Azure.Dapr.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..27c7fd061 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Azure.Dapr.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Azure.Dapr.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Azure.Dapr.AppHost.Go", + exampleName: "dapr", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..2cec5734b --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Azure.DataApiBuilder.AppHost.Go", + exampleName: "data-api-builder", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Azure.Extensions.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Azure.Extensions.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..6b07feb8c --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Azure.Extensions.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Azure.Extensions.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Azure.Extensions.AppHost.Go", + exampleName: "azure-ext", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Dapr.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Dapr.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..c773a1fd1 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Dapr.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Dapr.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Dapr.AppHost.Go", + exampleName: "dapr", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.DbGate.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.DbGate.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..71148e919 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.DbGate.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.DbGate.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.DbGate.AppHost.Go", + exampleName: "dbgate", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Deno.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Deno.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..ccaaa9214 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Deno.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Deno.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Deno.AppHost.Go", + exampleName: "deno", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..19536ef72 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Elasticsearch.Extensions.AppHost.Go", + exampleName: "elasticsearch-ext", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Perl.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Perl.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..c74820db2 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Perl.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Perl.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CpanmApiIntegration.AppHost.Go", + exampleName: "perl", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.PowerShell.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.PowerShell.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..6964b8bbb --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.PowerShell.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.PowerShell.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.PowerShell.AppHost.Go", + exampleName: "powershell", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Rust.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Rust.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..8a79381b6 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Rust.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Rust.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Rust.AppHost.Go", + exampleName: "rust", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..7b24f087b --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/GoAppHostTests.cs @@ -0,0 +1,16 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.AppHost.Go", + exampleName: "sql-database-projects", + validatePublish: !OperatingSystem.IsWindows(), + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Stripe.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Stripe.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..5c1db8cf1 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Stripe.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Stripe.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Stripe.AppHost.Go", + exampleName: "stripe", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.SurrealDb.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.SurrealDb.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..456b6827b --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.SurrealDb.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.SurrealDb.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.SurrealDb.AppHost.Go", + exampleName: "surrealdb", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Zitadel.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Zitadel.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..f508c125b --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Zitadel.Tests/GoAppHostTests.cs @@ -0,0 +1,16 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Zitadel.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Zitadel.AppHost.Go", + exampleName: "zitadel", + validatePublish: false, + cancellationToken: TestContext.Current.CancellationToken); + } +} From 2c68a277d58e06009d1696705fac5ec492486d02 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 15:57:50 +1100 Subject: [PATCH 23/26] feat(examples): add second batch of Go AppHost counterparts Add Flare, Floci, Java, JavaScript extensions, k6, Keycloak PostgreSQL, Kind and Ngrok Go examples with generated-SDK regression tests. Preserve preview API limitations and document compile-only coverage for optional parameter handles and the run-only Floci graph. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../.gitignore | 1 + .../README.md | 11 ++ .../apphost.go | 50 ++++++ .../aspire.config.json | 13 ++ .../go.mod | 7 + .../.gitignore | 1 + .../README.md | 19 +++ .../apphost.go | 144 ++++++++++++++++++ .../aspire.config.json | 13 ++ .../go.mod | 7 + .../apphost.go | 108 +++++++++++++ .../aspire.config.json | 12 ++ .../go.mod | 7 + .../apphost.go | 67 ++++++++ .../aspire.config.json | 12 ++ .../go.mod | 7 + .../apphost.go | 40 +++++ .../aspire.config.json | 12 ++ .../go.mod | 7 + .../README.md | 15 ++ .../apphost.go | 40 +++++ .../aspire.config.json | 12 ++ .../go.mod | 7 + .../apphost.go | 99 ++++++++++++ .../aspire.config.json | 12 ++ .../go.mod | 7 + .../apphost.go | 41 +++++ .../aspire.config.json | 12 ++ .../go.mod | 7 + .../GoAppHostTests.cs | 17 +++ .../GoAppHostTests.cs | 17 +++ .../GoAppHostTests.cs | 15 ++ .../GoAppHostTests.cs | 15 ++ .../GoAppHostTests.cs | 17 +++ .../GoAppHostTests.cs | 15 ++ .../GoAppHostTests.cs | 15 ++ .../GoAppHostTests.cs | 15 ++ 37 files changed, 916 insertions(+) create mode 100644 examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/.gitignore create mode 100644 examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/README.md create mode 100644 examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/apphost.go create mode 100644 examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/aspire.config.json create mode 100644 examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/go.mod create mode 100644 examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/.gitignore create mode 100644 examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/README.md create mode 100644 examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/apphost.go create mode 100644 examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/aspire.config.json create mode 100644 examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/go.mod create mode 100644 examples/java/CommunityToolkit.Aspire.Hosting.Java.AppHost.Go/apphost.go create mode 100644 examples/java/CommunityToolkit.Aspire.Hosting.Java.AppHost.Go/aspire.config.json create mode 100644 examples/java/CommunityToolkit.Aspire.Hosting.Java.AppHost.Go/go.mod create mode 100644 examples/javascript-ext/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.AppHost.Go/apphost.go create mode 100644 examples/javascript-ext/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.AppHost.Go/aspire.config.json create mode 100644 examples/javascript-ext/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.AppHost.Go/go.mod create mode 100644 examples/k6/CommunityToolkit.Aspire.Hosting.k6.AppHost.Go/apphost.go create mode 100644 examples/k6/CommunityToolkit.Aspire.Hosting.k6.AppHost.Go/aspire.config.json create mode 100644 examples/k6/CommunityToolkit.Aspire.Hosting.k6.AppHost.Go/go.mod create mode 100644 examples/keycloak-postgres/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.AppHost.Go/README.md create mode 100644 examples/keycloak-postgres/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.AppHost.Go/apphost.go create mode 100644 examples/keycloak-postgres/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.AppHost.Go/aspire.config.json create mode 100644 examples/keycloak-postgres/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.AppHost.Go/go.mod create mode 100644 examples/kind/CommunityToolkit.Aspire.Hosting.Kind.AppHost.Go/apphost.go create mode 100644 examples/kind/CommunityToolkit.Aspire.Hosting.Kind.AppHost.Go/aspire.config.json create mode 100644 examples/kind/CommunityToolkit.Aspire.Hosting.Kind.AppHost.Go/go.mod create mode 100644 examples/ngrok/CommunityToolkit.Aspire.Hosting.Ngrok.AppHost.Go/apphost.go create mode 100644 examples/ngrok/CommunityToolkit.Aspire.Hosting.Ngrok.AppHost.Go/aspire.config.json create mode 100644 examples/ngrok/CommunityToolkit.Aspire.Hosting.Ngrok.AppHost.Go/go.mod create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Flare.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Floci.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Java.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Ngrok.Tests/GoAppHostTests.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.k6.Tests/GoAppHostTests.cs diff --git a/examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/.gitignore b/examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/.gitignore new file mode 100644 index 000000000..d31c96078 --- /dev/null +++ b/examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/.gitignore @@ -0,0 +1 @@ +.aspire/ diff --git a/examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/README.md b/examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/README.md new file mode 100644 index 000000000..cb3e321b7 --- /dev/null +++ b/examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/README.md @@ -0,0 +1,11 @@ +# Flare Go AppHost + +This example mirrors the adjacent TypeScript AppHost using the pinned Aspire 17 +preview Go SDK. Run `aspire restore --apphost apphost.go`, then `go test ./...` +and `go vet ./...` to validate the generated SDK and example. + +The current preview SDK does not serialize optional resource-builder handles +correctly. Model construction fails at `WithPublicUrls` when passing the dashboard +URL parameter, with `INVALID_ARGUMENT` for `dashboardUrl`. The example retains +the parameter-based configuration for parity; its integration test validates +compilation only until the upstream SDK supports this argument. diff --git a/examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/apphost.go b/examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/apphost.go new file mode 100644 index 000000000..5a66218cc --- /dev/null +++ b/examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/apphost.go @@ -0,0 +1,50 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + clickhouse := builder.AddClickHouse("clickhouse") + redis := builder.AddRedis("redis") + ingestKey := builder.AddParameter("flare-ingest-key", &aspire.AddParameterOptions{ + Value: aspire.StringPtr("local-dev-ingest-key"), Secret: aspire.BoolPtr(true), + }) + dashboardURL := builder.AddParameter("flare-dashboard-url", &aspire.AddParameterOptions{ + Value: aspire.StringPtr("http://localhost:3000"), + }) + flare := builder.AddFlare("flare", clickhouse, redis, &aspire.AddFlareOptions{ + DashboardPort: aspire.Float64Ptr(3000), + }) + flare.WithDataVolume(). + WithIngestPorts(&aspire.WithIngestPortsOptions{GrpcPort: aspire.Float64Ptr(4317), HttpPort: aspire.Float64Ptr(4318)}). + WithFlareImageTag("0.6"). + WithPublicUrls(&aspire.WithPublicUrlsOptions{DashboardUrl: &dashboardURL}). + WithApiKey(ingestKey) + _ = flare.OtlpGrpcEndpoint() + _ = flare.OtlpHttpEndpoint() + protocol := aspire.OtlpProtocolHttpProtobuf + api := builder.AddDotnetProject("apiservice", "../CommunityToolkit.Aspire.Hosting.Flare.ApiService/CommunityToolkit.Aspire.Hosting.Flare.ApiService.csproj"). + WithFlareOtlpExporter(flare, &aspire.WithFlareOtlpExporterOptions{Protocol: &protocol}). + WaitFor(flare) + if err := api.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := flare.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/aspire.config.json b/examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..e5e904dc4 --- /dev/null +++ b/examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/aspire.config.json @@ -0,0 +1,13 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Flare": "../../../src/CommunityToolkit.Aspire.Hosting.Flare/CommunityToolkit.Aspire.Hosting.Flare.csproj", + "Aspire.Hosting.Dotnet": "17.1.0-preview.1.26559.8" + } +} diff --git a/examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/go.mod b/examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/.gitignore b/examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/.gitignore new file mode 100644 index 000000000..d31c96078 --- /dev/null +++ b/examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/.gitignore @@ -0,0 +1 @@ +.aspire/ diff --git a/examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/README.md b/examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/README.md new file mode 100644 index 000000000..67bae564a --- /dev/null +++ b/examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/README.md @@ -0,0 +1,19 @@ +# Floci Go AppHost + +This example mirrors the adjacent TypeScript AppHost, including all three cloud +emulators, their shared UI, the API service, and the Service Bus AMQP probe. +It requires the pinned Aspire 17 preview CLI, Go 1.26 or later, and Docker. + +From this directory, run: + +```shell +aspire restore --apphost apphost.go +go test ./... +go vet ./... +aspire run --apphost apphost.go +``` + +The Service Bus emulator supports run mode only, so publishing this development +graph is intentionally unsupported. The integration test validates compilation +without constructing a publish-mode model. The checked-in TypeScript example +remains the source of the equivalent runtime scenario. diff --git a/examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/apphost.go b/examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/apphost.go new file mode 100644 index 000000000..c106ce3b6 --- /dev/null +++ b/examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/apphost.go @@ -0,0 +1,144 @@ +package main + +import ( + "apphost/modules/aspire" + "crypto/rand" + "fmt" + "log" + "os" + "path/filepath" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + aws := builder.AddFlociAws("floci-aws") + azure := builder.AddFlociAzure("floci-az") + gcp := builder.AddFlociGcp("floci-gcp") + namespace := make([]byte, 16) + if _, err := rand.Read(namespace); err != nil { + log.Fatal(err) + } + azure.WithDockerSocket().WithEnvironment("FLOCI_AZ_DOCKER_RESOURCE_NAMESPACE", fmt.Sprintf("sample-%x", namespace)) + serviceBus := azure.WithServiceBus(&aspire.WithServiceBusOptions{Name: aspire.StringPtr("servicebus")}) + _ = serviceBus.AmqpEndpoint() + _ = serviceBus.AmqpTlsEndpoint() + _ = serviceBus.ConnectionStringExpression() + storage := azure.WithStorage(&aspire.WithStorageOptions{Name: aspire.StringPtr("storage")}) + if _, err := storage.AccountName(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + _ = storage.ServiceEndpoint() + _ = storage.ConnectionStringExpression() + aws.WithFlociUI(&aspire.WithFlociUIOptions{ + ConfigureContainer: func(ui aspire.FlociUIContainerResource) { + ui.WithAzureReference(azure).WithGcpReference(gcp) + if err := ui.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + }, + }) + api := builder.AddDotnetProject("floci-api", "../CommunityToolkit.Aspire.Hosting.Floci.ApiService/CommunityToolkit.Aspire.Hosting.Floci.ApiService.csproj"). + WithExternalHttpEndpoints(). + WithHttpHealthCheck(&aspire.WithHttpHealthCheckOptions{Path: aspire.StringPtr("/health")}). + WithFlociAwsReference(aws).WithFlociAzureReference(azure).WithFlociGcpReference(gcp). + WithFlociAzureServiceBusReference(serviceBus).WithReference(storage). + WaitFor(aws).WaitFor(azure).WaitFor(gcp).WaitFor(storage) + probe := builder.AddContainer("servicebus-probe", "node:22-alpine"). + WithFlociAzureServiceBusReference(serviceBus). + WithHttpEndpoint(&aspire.WithHttpEndpointOptions{TargetPort: aspire.Float64Ptr(8080)}). + WithHttpHealthCheck(&aspire.WithHttpHealthCheckOptions{Path: aspire.StringPtr("/")}). + WaitFor(azure).WithArgs([]string{"-e", ` + const net = require('node:net'); + const http = require('node:http'); + const assert = require('node:assert/strict'); + const connectionString = process.env.ConnectionStrings__servicebus; + const endpoint = new URL(connectionString.split(';')[0].slice('Endpoint='.length)); + const header = Buffer.from([65, 77, 81, 80, 0, 1, 0, 0]); + let ready = false; + const socket = net.connect({ host: endpoint.hostname, port: Number(endpoint.port) }, () => socket.write(header)); + socket.setTimeout(20000, () => { throw new Error('AMQP handshake timed out'); }); + let response = Buffer.alloc(0); + socket.on('data', data => { + response = Buffer.concat([response, data]); + if (response.length >= header.length) { + assert.deepEqual(response.subarray(0, header.length), header); + ready = true; + socket.destroy(); + } + }); + http.createServer((request, response) => { + response.writeHead(ready ? 200 : 503); + response.end(); + }).listen(8080, '0.0.0.0'); + `}) + custom := builder.AddFlociAws("floci-custom", &aspire.AddFlociAwsOptions{ + Port: aspire.Float64Ptr(14566), DefaultRegion: aspire.StringPtr("eu-west-1"), DefaultAccountId: aspire.StringPtr("123456789012"), + }) + gcpCustom := builder.AddFlociGcp("floci-gcp-custom", &aspire.AddFlociGcpOptions{DefaultProjectId: aspire.StringPtr("my-project")}) + persistent := builder.AddFlociAws("floci-persistent").WithDataVolume("floci-data") + dataPath, err := filepath.Abs(filepath.Join(".aspire", "floci-data")) + if err != nil { + log.Fatal(err) + } + if err := os.MkdirAll(dataPath, 0755); err != nil { + log.Fatal(err) + } + mount := builder.AddFlociAws("floci-mount").WithDataBindMount(dataPath) + + const includeCompileOnlyScenarios = false + if includeCompileOnlyScenarios { + builder.AddFlociAws("floci-with-ui").WithFlociUI(&aspire.WithFlociUIOptions{ + ContainerName: aspire.StringPtr("my-floci-ui"), + ConfigureContainer: func(ui aspire.FlociUIContainerResource) { + ui.WithHostPort(aspire.Float64Ptr(14500)) + if err := ui.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + }, + }) + socketOptions := &aspire.WithDockerSocketOptions{SocketPath: aspire.StringPtr("/run/user/1000/podman/podman.sock")} + builder.AddFlociAws("floci-podman").WithDockerSocket(socketOptions) + builder.AddFlociAzure("floci-az-podman").WithDockerSocket(socketOptions). + WithServiceBus(&aspire.WithServiceBusOptions{Name: aspire.StringPtr("podman-servicebus"), AmqpPort: aspire.Float64Ptr(15673), AmqpTlsPort: aspire.Float64Ptr(15674)}) + builder.AddFlociGcp("floci-gcp-podman").WithDockerSocket(socketOptions) + builder.AddFlociAzure("floci-az-persistent").WithDataVolume("floci-az-data") + builder.AddFlociAzure("floci-az-mount").WithDataBindMount("/tmp/floci-az-data") + builder.AddFlociGcp("floci-gcp-persistent").WithDataVolume("floci-gcp-data") + builder.AddFlociGcp("floci-gcp-mount").WithDataBindMount("/tmp/floci-gcp-data") + builder.AddFlociAws("floci-tls").WithHttpsDeveloperCertificate() + builder.AddFlociAzure("floci-az-tls").WithHttpsDeveloperCertificate() + builder.AddFlociAws("floci-configured").WithConfigFile("./floci.yml") + _ = aws.PrimaryEndpoint() + _ = aws.Host() + _ = aws.Port() + _ = aws.ConnectionStringExpression() + _ = azure.PrimaryEndpoint() + _ = azure.Host() + _ = azure.Port() + _ = azure.ConnectionStringExpression() + _ = gcp.PrimaryEndpoint() + _ = gcp.Host() + _ = gcp.Port() + _ = gcp.ConnectionStringExpression() + builder.AddDotnetProject("api", "../FlociApi/FlociApi.csproj"). + WithFlociAwsReference(aws).WithFlociAzureReference(azure).WithFlociGcpReference(gcp) + builder.AddContainer("worker", "myorg/worker"). + WithFlociAwsReference(aws).WithFlociAzureReference(azure).WithFlociGcpReference(gcp) + } + for _, resource := range []interface{ Err() error }{aws, azure, gcp, serviceBus, storage, api, probe, custom, gcpCustom, persistent, mount} { + if err := resource.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + } + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/aspire.config.json b/examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..dad8ba340 --- /dev/null +++ b/examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/aspire.config.json @@ -0,0 +1,13 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Floci": "../../../src/CommunityToolkit.Aspire.Hosting.Floci/CommunityToolkit.Aspire.Hosting.Floci.csproj", + "Aspire.Hosting.Dotnet": "17.1.0-preview.1.26559.8" + } +} diff --git a/examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/go.mod b/examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/java/CommunityToolkit.Aspire.Hosting.Java.AppHost.Go/apphost.go b/examples/java/CommunityToolkit.Aspire.Hosting.Java.AppHost.Go/apphost.go new file mode 100644 index 000000000..81a0870ef --- /dev/null +++ b/examples/java/CommunityToolkit.Aspire.Hosting.Java.AppHost.Go/apphost.go @@ -0,0 +1,108 @@ +package main + +import ( + "apphost/modules/aspire" + "log" + "path/filepath" + "runtime" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + projectDirectory, err := filepath.Abs("../CommunityToolkit.Aspire.Hosting.Java.Spring.Maven") + if err != nil { + log.Fatal(err) + } + jarPath := filepath.Join("target", "spring-maven-0.0.1-SNAPSHOT.jar") + mavenWrapper, gradleWrapper := "mvnw", "gradlew" + if runtime.GOOS == "windows" { + mavenWrapper, gradleWrapper = "mvnw.cmd", "gradlew.bat" + } + + container := builder.AddJavaContainerApp("java-container", "docker.io/aliencube/aspire-spring-maven-sample", + &aspire.AddJavaContainerAppOptions{ImageTag: aspire.StringPtr("latest")}). + WithJvmArgs([]string{"-Djava.awt.headless=true"}). + WithOtelAgent(). + WithHttpEndpoint(&aspire.WithHttpEndpointOptions{TargetPort: aspire.Float64Ptr(8080), Env: aspire.StringPtr("SERVER_PORT")}). + WithHttpHealthCheck(&aspire.WithHttpHealthCheckOptions{Path: aspire.StringPtr("/")}). + WithExplicitStart() + container.SetEntrypoint(aspire.StringPtr("java")) + _, err = container.Entrypoint() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + container.SetShellExecution(aspire.BoolPtr(false)) + if _, err := container.ShellExecution(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + if _, err := container.Name(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + jar := builder.AddJavaAppWithJar("java-jar", projectDirectory, jarPath, + &aspire.AddJavaAppWithJarOptions{Args: []string{"--spring.main.banner-mode=off"}}). + WithWrapperPath(mavenWrapper). + WithMavenBuild([]string{}). + WithJvmArgs([]string{"-Duser.timezone=UTC"}). + WithOtelAgent(). + WithExplicitStart() + if _, err := jar.Command(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + if _, err := jar.WorkingDirectory(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + if _, err := jar.Name(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + if _, err := jar.JarPath(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + jar.SetJarPath(aspire.StringPtr(jarPath)) + if _, err := jar.JarPath(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + mavenGoal := builder.AddJavaApp("java-maven-goal", projectDirectory). + WithWrapperPath(mavenWrapper). + WithMavenGoal("spring-boot:run", []string{}). + WithJvmArgs([]string{"-Dfile.encoding=UTF-8"}). + WithOtelAgent(). + WithHttpEndpoint(&aspire.WithHttpEndpointOptions{TargetPort: aspire.Float64Ptr(8080), Env: aspire.StringPtr("SERVER_PORT")}). + WithHttpHealthCheck(&aspire.WithHttpHealthCheckOptions{Path: aspire.StringPtr("/health")}). + WithExplicitStart() + if _, err := mavenGoal.Command(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + if _, err := mavenGoal.WorkingDirectory(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + if _, err := mavenGoal.Name(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + gradleBuild := builder.AddJavaApp("java-gradle-build", projectDirectory). + WithWrapperPath(gradleWrapper). + WithGradleBuild([]string{"--parallel"}). + WithExplicitStart() + gradleTask := builder.AddJavaApp("java-gradle-task", projectDirectory). + WithWrapperPath(gradleWrapper). + WithGradleTask("bootRun", []string{"--args=--spring.main.banner-mode=off"}). + WithExplicitStart() + + for _, resource := range []interface{ Err() error }{container, jar, mavenGoal, gradleBuild, gradleTask} { + if err := resource.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + } + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/java/CommunityToolkit.Aspire.Hosting.Java.AppHost.Go/aspire.config.json b/examples/java/CommunityToolkit.Aspire.Hosting.Java.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..0a7ec53e8 --- /dev/null +++ b/examples/java/CommunityToolkit.Aspire.Hosting.Java.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Java": "../../../src/CommunityToolkit.Aspire.Hosting.Java/CommunityToolkit.Aspire.Hosting.Java.csproj" + } +} diff --git a/examples/java/CommunityToolkit.Aspire.Hosting.Java.AppHost.Go/go.mod b/examples/java/CommunityToolkit.Aspire.Hosting.Java.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/java/CommunityToolkit.Aspire.Hosting.Java.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/javascript-ext/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.AppHost.Go/apphost.go b/examples/javascript-ext/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.AppHost.Go/apphost.go new file mode 100644 index 000000000..de1076479 --- /dev/null +++ b/examples/javascript-ext/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.AppHost.Go/apphost.go @@ -0,0 +1,67 @@ +package main + +import ( + "apphost/modules/aspire" + "log" + "path/filepath" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + nxRoot, err := filepath.Abs("../nx-demo") + if err != nil { + log.Fatal(err) + } + turboRoot, err := filepath.Abs("../turborepo-demo") + if err != nil { + log.Fatal(err) + } + + nx := builder.AddNxApp("nx-demo", &aspire.AddNxAppOptions{WorkingDirectory: aspire.StringPtr(nxRoot)}). + WithNpm().WithPackageManagerLaunch().WithExplicitStart() + nxBlog := nx.AddApp("blog-monorepo"). + WithHttpEndpoint(&aspire.WithHttpEndpointOptions{Env: aspire.StringPtr("PORT")}). + WithMappedEndpointPort().WithHttpHealthCheck().WithExplicitStart() + nxYarn := builder.AddNxApp("nx-yarn", &aspire.AddNxAppOptions{WorkingDirectory: aspire.StringPtr(nxRoot)}). + WithYarn().WithPackageManagerLaunch(&aspire.WithPackageManagerLaunchOptions{PackageManager: aspire.StringPtr("yarn")}). + WithExplicitStart() + nxYarnBlog := nxYarn.AddApp("blog-monorepo-yarn", &aspire.AddAppOptions{AppName: aspire.StringPtr("blog-monorepo")}). + WithHttpEndpoint(&aspire.WithHttpEndpointOptions{Env: aspire.StringPtr("PORT")}). + WithMappedEndpointPort(&aspire.WithMappedEndpointPortOptions{EndpointName: aspire.StringPtr("http")}). + WithExplicitStart() + nxPnpm := builder.AddNxApp("nx-pnpm", &aspire.AddNxAppOptions{WorkingDirectory: aspire.StringPtr(nxRoot)}). + WithPnpm().WithPackageManagerLaunch(&aspire.WithPackageManagerLaunchOptions{PackageManager: aspire.StringPtr("pnpm")}). + WithExplicitStart() + + turbo := builder.AddTurborepoApp("turborepo-demo", &aspire.AddTurborepoAppOptions{WorkingDirectory: aspire.StringPtr(turboRoot)}). + WithNpm(&aspire.WithNpmOptions{Install: aspire.BoolPtr(true)}).WithPackageManagerLaunch() + turboWeb := turbo.AddApp("turbo-web", &aspire.TurborepoResourceAddAppOptions{Filter: aspire.StringPtr("web")}). + WithHttpEndpoint(&aspire.WithHttpEndpointOptions{Env: aspire.StringPtr("PORT")}). + WithMappedEndpointPort().WithHttpHealthCheck() + turboYarn := builder.AddTurborepoApp("turborepo-yarn", &aspire.AddTurborepoAppOptions{WorkingDirectory: aspire.StringPtr(turboRoot)}). + WithYarn().WithPackageManagerLaunch(&aspire.WithPackageManagerLaunchOptions{PackageManager: aspire.StringPtr("yarn")}). + WithExplicitStart() + turboDocs := turboYarn.AddApp("docs"). + WithHttpEndpoint(&aspire.WithHttpEndpointOptions{Env: aspire.StringPtr("PORT")}). + WithMappedEndpointPort(&aspire.WithMappedEndpointPortOptions{EndpointName: aspire.StringPtr("http")}). + WithExplicitStart() + turboPnpm := builder.AddTurborepoApp("turborepo-pnpm", &aspire.AddTurborepoAppOptions{WorkingDirectory: aspire.StringPtr(turboRoot)}). + WithPnpm().WithPackageManagerLaunch(&aspire.WithPackageManagerLaunchOptions{PackageManager: aspire.StringPtr("pnpm")}). + WithExplicitStart() + + for _, resource := range []interface{ Err() error }{nx, nxBlog, nxYarn, nxYarnBlog, nxPnpm, turbo, turboWeb, turboYarn, turboDocs, turboPnpm} { + if err := resource.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + } + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/javascript-ext/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.AppHost.Go/aspire.config.json b/examples/javascript-ext/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..8c727909d --- /dev/null +++ b/examples/javascript-ext/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.JavaScript.Extensions": "../../../src/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.csproj" + } +} diff --git a/examples/javascript-ext/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.AppHost.Go/go.mod b/examples/javascript-ext/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/javascript-ext/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/k6/CommunityToolkit.Aspire.Hosting.k6.AppHost.Go/apphost.go b/examples/k6/CommunityToolkit.Aspire.Hosting.k6.AppHost.Go/apphost.go new file mode 100644 index 000000000..4c798e20e --- /dev/null +++ b/examples/k6/CommunityToolkit.Aspire.Hosting.k6.AppHost.Go/apphost.go @@ -0,0 +1,40 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + defaultK6 := builder.AddK6("k6-default") + browserK6 := builder.AddK6("k6-browser", &aspire.AddK6Options{ + EnableBrowserExtensions: aspire.BoolPtr(true), Port: aspire.Float64Ptr(6566), + }) + for _, resource := range []aspire.K6Resource{defaultK6, browserK6} { + resource.WithBindMount("../CommunityToolkit.Aspire.Hosting.k6.AppHost.TypeScript/scripts", "/scripts", &aspire.WithBindMountOptions{IsReadOnly: aspire.BoolPtr(true)}) + } + defaultK6.WithScript("/scripts/main.js").WithK6OtlpEnvironment() + browserK6.WithScript("/scripts/main.js", &aspire.WithScriptOptions{ + VirtualUsers: aspire.Float64Ptr(5), Duration: aspire.StringPtr("45s"), + }).WithK6OtlpEnvironment() + _ = defaultK6.PrimaryEndpoint() + _ = browserK6.PrimaryEndpoint() + for _, resource := range []aspire.K6Resource{defaultK6, browserK6} { + if err := resource.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/k6/CommunityToolkit.Aspire.Hosting.k6.AppHost.Go/aspire.config.json b/examples/k6/CommunityToolkit.Aspire.Hosting.k6.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..61e247cb7 --- /dev/null +++ b/examples/k6/CommunityToolkit.Aspire.Hosting.k6.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.k6": "../../../src/CommunityToolkit.Aspire.Hosting.k6/CommunityToolkit.Aspire.Hosting.k6.csproj" + } +} diff --git a/examples/k6/CommunityToolkit.Aspire.Hosting.k6.AppHost.Go/go.mod b/examples/k6/CommunityToolkit.Aspire.Hosting.k6.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/k6/CommunityToolkit.Aspire.Hosting.k6.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/keycloak-postgres/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.AppHost.Go/README.md b/examples/keycloak-postgres/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.AppHost.Go/README.md new file mode 100644 index 000000000..51311cecd --- /dev/null +++ b/examples/keycloak-postgres/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.AppHost.Go/README.md @@ -0,0 +1,15 @@ +# Keycloak PostgreSQL Go AppHost + +This example mirrors the adjacent TypeScript AppHost using the pinned Aspire 17 +preview Go SDK. Run `aspire restore --apphost apphost.go`, then `go test ./...` +and `go vet ./...` to validate the generated SDK and example. + +`AddKeycloak` is not exported by the underlying hosting package. As in the +TypeScript example, `WithPostgres` receives compile-only coverage and the runtime +graph contains PostgreSQL and its database. + +The current preview SDK also fails to serialize the optional PostgreSQL username +and password resource-builder handles. Model construction reports +`INVALID_ARGUMENT` for `userName`. The example preserves these parameters rather +than substituting plain-text credentials; its integration test validates +compilation only until the upstream SDK supports these arguments. diff --git a/examples/keycloak-postgres/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.AppHost.Go/apphost.go b/examples/keycloak-postgres/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.AppHost.Go/apphost.go new file mode 100644 index 000000000..031960439 --- /dev/null +++ b/examples/keycloak-postgres/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.AppHost.Go/apphost.go @@ -0,0 +1,40 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +// AddKeycloak is not exported by Aspire.Hosting.Keycloak yet. Match the TypeScript +// example's compile-only coverage of the generated WithPostgres signatures. +func configureKeycloak(keycloak aspire.KeycloakResource, db aspire.PostgresDatabaseResource, username, password aspire.ParameterResource) { + keycloak.WithPostgres(db) + keycloak.WithPostgres(db, &aspire.WithPostgresOptions{ + Username: &username, Password: &password, XaEnabled: aspire.BoolPtr(true), + }) +} + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + username := builder.AddParameter("db-username", &aspire.AddParameterOptions{Value: aspire.StringPtr("postgres")}) + password := builder.AddParameter("db-password", &aspire.AddParameterOptions{ + Value: aspire.StringPtr("Postgres!123"), Secret: aspire.BoolPtr(true), + }) + postgres := builder.AddPostgres("keycloak-postgres", &aspire.AddPostgresOptions{UserName: &username, Password: &password}) + db := postgres.AddDatabase("keycloak") + if err := db.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/keycloak-postgres/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.AppHost.Go/aspire.config.json b/examples/keycloak-postgres/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..889815c54 --- /dev/null +++ b/examples/keycloak-postgres/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Keycloak.Extensions": "../../../src/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.csproj" + } +} diff --git a/examples/keycloak-postgres/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.AppHost.Go/go.mod b/examples/keycloak-postgres/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/keycloak-postgres/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/kind/CommunityToolkit.Aspire.Hosting.Kind.AppHost.Go/apphost.go b/examples/kind/CommunityToolkit.Aspire.Hosting.Kind.AppHost.Go/apphost.go new file mode 100644 index 000000000..bf500a296 --- /dev/null +++ b/examples/kind/CommunityToolkit.Aspire.Hosting.Kind.AppHost.Go/apphost.go @@ -0,0 +1,99 @@ +package main + +import ( + "apphost/modules/aspire" + "fmt" + "log" + "path/filepath" +) + +func configureWait(options aspire.CrdWaitOptions, timeout float64) error { + options.SetTimeout(timeout).SetFailureBehavior(aspire.CrdWaitBehaviorBestEffort) + if err := options.Err(); err != nil { + return err + } + actualTimeout, err := options.Timeout() + if err != nil { + return err + } + behavior, err := options.FailureBehavior() + if err != nil { + return err + } + if actualTimeout != timeout || behavior != aspire.CrdWaitBehaviorBestEffort { + return fmt.Errorf("CRD-wait options did not round-trip through the generated SDK") + } + return nil +} + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + cluster := builder.AddKindCluster("ts-kind") + manifestConfigured, chartConfigured := false, false + manifest := cluster.AddManifestFromContent("ts-manifest", ` +apiVersion: v1 +kind: Namespace +metadata: + name: ts-crd-wait +`) + manifest.WithCrdWait(func(options aspire.CrdWaitOptions) { + if err := configureWait(options, 120_000); err != nil { + log.Fatal(aspire.FormatError(err)) + } + manifestConfigured = true + }) + chartPath, err := filepath.Abs("../CommunityToolkit.Aspire.Hosting.Kind.AppHost.TypeScript/chart") + if err != nil { + log.Fatal(err) + } + chart := cluster.AddHelmChart("ts-chart", chartPath) + chart.WithCrdWait(func(options aspire.CrdWaitOptions) { + if err := configureWait(options, 180_000); err != nil { + log.Fatal(aspire.FormatError(err)) + } + chartConfigured = true + }) + if !manifestConfigured || !chartConfigured { + log.Fatal("CRD-wait callbacks were not invoked for both Kind builders") + } + timeout, err := manifest.CrdWaitTimeout() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + behavior, err := manifest.CrdWaitBehavior() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if timeout != 120_000 || behavior != aspire.CrdWaitBehaviorBestEffort { + log.Fatal("Manifest CRD-wait policy did not retain the configured options") + } + manifest.WithCrdWaitTimeout(90_000).WithCrdWaitBehavior(aspire.CrdWaitBehaviorFail) + timeout, err = manifest.CrdWaitTimeout() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + behavior, err = manifest.CrdWaitBehavior() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if timeout != 90_000 || behavior != aspire.CrdWaitBehaviorFail { + log.Fatal("Legacy manifest CRD-wait methods did not update the configured options") + } + if err := chart.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := manifest.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/kind/CommunityToolkit.Aspire.Hosting.Kind.AppHost.Go/aspire.config.json b/examples/kind/CommunityToolkit.Aspire.Hosting.Kind.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..437ca1338 --- /dev/null +++ b/examples/kind/CommunityToolkit.Aspire.Hosting.Kind.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Kind": "../../../src/CommunityToolkit.Aspire.Hosting.Kind/CommunityToolkit.Aspire.Hosting.Kind.csproj" + } +} diff --git a/examples/kind/CommunityToolkit.Aspire.Hosting.Kind.AppHost.Go/go.mod b/examples/kind/CommunityToolkit.Aspire.Hosting.Kind.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/kind/CommunityToolkit.Aspire.Hosting.Kind.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/examples/ngrok/CommunityToolkit.Aspire.Hosting.Ngrok.AppHost.Go/apphost.go b/examples/ngrok/CommunityToolkit.Aspire.Hosting.Ngrok.AppHost.Go/apphost.go new file mode 100644 index 000000000..f3a67f52d --- /dev/null +++ b/examples/ngrok/CommunityToolkit.Aspire.Hosting.Ngrok.AppHost.Go/apphost.go @@ -0,0 +1,41 @@ +package main + +import ( + "apphost/modules/aspire" + "log" +) + +func main() { + builder, err := aspire.CreateBuilder() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + + authToken := builder.AddParameter("ngrok-auth-token", &aspire.AddParameterOptions{ + Value: aspire.StringPtr("ngrok-token-value"), Secret: aspire.BoolPtr(true), + }) + upstream := builder.AddContainer("upstream", "nginx:alpine"). + WithHttpEndpoint(&aspire.WithHttpEndpointOptions{Name: aspire.StringPtr("http"), TargetPort: aspire.Float64Ptr(80)}) + parameter := builder.AddNgrok("ngrok-parameter", &aspire.AddNgrokOptions{ + ConfigurationFolder: aspire.StringPtr(".ngrok-parameter"), EndpointPort: aspire.Float64Ptr(59600), + EndpointName: aspire.StringPtr("http"), ConfigurationVersion: aspire.Float64Ptr(3), + }).WithAuthToken(authToken).WithTunnelEndpoint(upstream, "http", &aspire.WithTunnelEndpointOptions{ + NgrokUrl: aspire.StringPtr("https://parameter.ngrok.test"), + Labels: map[string]string{"environment": "dev", "service": "upstream"}, + }) + value := builder.AddNgrok("ngrok-value").WithAuthTokenValue("plain-text-token").WithTunnelEndpoint(upstream, "http") + if err := parameter.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := value.Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } + + app, err := builder.Build() + if err != nil { + log.Fatal(aspire.FormatError(err)) + } + if err := app.Run(); err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/ngrok/CommunityToolkit.Aspire.Hosting.Ngrok.AppHost.Go/aspire.config.json b/examples/ngrok/CommunityToolkit.Aspire.Hosting.Ngrok.AppHost.Go/aspire.config.json new file mode 100644 index 000000000..3005b4987 --- /dev/null +++ b/examples/ngrok/CommunityToolkit.Aspire.Hosting.Ngrok.AppHost.Go/aspire.config.json @@ -0,0 +1,12 @@ +{ + "appHost": { + "path": "apphost.go", + "language": "go" + }, + "sdk": { + "version": "17.1.0-preview.1.26559.8" + }, + "packages": { + "CommunityToolkit.Aspire.Hosting.Ngrok": "../../../src/CommunityToolkit.Aspire.Hosting.Ngrok/CommunityToolkit.Aspire.Hosting.Ngrok.csproj" + } +} diff --git a/examples/ngrok/CommunityToolkit.Aspire.Hosting.Ngrok.AppHost.Go/go.mod b/examples/ngrok/CommunityToolkit.Aspire.Hosting.Ngrok.AppHost.Go/go.mod new file mode 100644 index 000000000..b152cea77 --- /dev/null +++ b/examples/ngrok/CommunityToolkit.Aspire.Hosting.Ngrok.AppHost.Go/go.mod @@ -0,0 +1,7 @@ +module apphost + +go 1.26 + +require apphost/modules/aspire v0.0.0 + +replace apphost/modules/aspire => ./.aspire/modules diff --git a/tests/CommunityToolkit.Aspire.Hosting.Flare.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Flare.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..b4f1a2396 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Flare.Tests/GoAppHostTests.cs @@ -0,0 +1,17 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Flare.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go", + exampleName: "flare", + // The preview Go SDK cannot serialize the optional dashboard URL parameter handle. + validatePublish: false, + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Floci.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Floci.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..0433bbad3 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Floci.Tests/GoAppHostTests.cs @@ -0,0 +1,17 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Floci.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go", + exampleName: "floci", + // The Service Bus emulator is run-only and rejects publish-mode model construction. + validatePublish: false, + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Java.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Java.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..7815ee65f --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Java.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Java.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Java.AppHost.Go", + exampleName: "java", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..dac432dd3 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.JavaScript.Extensions.AppHost.Go", + exampleName: "javascript-ext", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..b3422407d --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.Tests/GoAppHostTests.cs @@ -0,0 +1,17 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Keycloak.Extensions.AppHost.Go", + exampleName: "keycloak-postgres", + // The preview Go SDK cannot serialize optional PostgreSQL credential parameter handles. + validatePublish: false, + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..bb39e823e --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Kind.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Kind.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task CrdWaitApisWorkFromGeneratedGoSdk() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Kind.AppHost.Go", + exampleName: "kind", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Ngrok.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Ngrok.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..9ef05acad --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Ngrok.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Ngrok.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.Ngrok.AppHost.Go", + exampleName: "ngrok", + cancellationToken: TestContext.Current.CancellationToken); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.k6.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.k6.Tests/GoAppHostTests.cs new file mode 100644 index 000000000..7c1b041ed --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.k6.Tests/GoAppHostTests.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.k6.Tests; + +public class GoAppHostTests +{ + [Fact] + public async Task GoAppHostCompiles() + { + await GoAppHostTest.Run( + appHostProject: "CommunityToolkit.Aspire.Hosting.k6.AppHost.Go", + exampleName: "k6", + cancellationToken: TestContext.Current.CancellationToken); + } +} From d55cf3ddbb92ad3877ba38a42ebe5f05e170a78e Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 16:17:30 +1100 Subject: [PATCH 24/26] fix(examples): check Go endpoint wrapper errors Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../apphost.go | 10 +++++-- .../apphost.go | 28 +++++++++++-------- .../apphost.go | 5 ++-- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/apphost.go b/examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/apphost.go index 5a66218cc..2aa844551 100644 --- a/examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/apphost.go +++ b/examples/flare/CommunityToolkit.Aspire.Hosting.Flare.AppHost.Go/apphost.go @@ -27,8 +27,8 @@ func main() { WithFlareImageTag("0.6"). WithPublicUrls(&aspire.WithPublicUrlsOptions{DashboardUrl: &dashboardURL}). WithApiKey(ingestKey) - _ = flare.OtlpGrpcEndpoint() - _ = flare.OtlpHttpEndpoint() + check(flare.OtlpGrpcEndpoint().Err()) + check(flare.OtlpHttpEndpoint().Err()) protocol := aspire.OtlpProtocolHttpProtobuf api := builder.AddDotnetProject("apiservice", "../CommunityToolkit.Aspire.Hosting.Flare.ApiService/CommunityToolkit.Aspire.Hosting.Flare.ApiService.csproj"). WithFlareOtlpExporter(flare, &aspire.WithFlareOtlpExporterOptions{Protocol: &protocol}). @@ -48,3 +48,9 @@ func main() { log.Fatal(aspire.FormatError(err)) } } + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/apphost.go b/examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/apphost.go index c106ce3b6..10884605c 100644 --- a/examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/apphost.go +++ b/examples/floci/CommunityToolkit.Aspire.Hosting.Floci.AppHost.Go/apphost.go @@ -24,8 +24,8 @@ func main() { } azure.WithDockerSocket().WithEnvironment("FLOCI_AZ_DOCKER_RESOURCE_NAMESPACE", fmt.Sprintf("sample-%x", namespace)) serviceBus := azure.WithServiceBus(&aspire.WithServiceBusOptions{Name: aspire.StringPtr("servicebus")}) - _ = serviceBus.AmqpEndpoint() - _ = serviceBus.AmqpTlsEndpoint() + check(serviceBus.AmqpEndpoint().Err()) + check(serviceBus.AmqpTlsEndpoint().Err()) _ = serviceBus.ConnectionStringExpression() storage := azure.WithStorage(&aspire.WithStorageOptions{Name: aspire.StringPtr("storage")}) if _, err := storage.AccountName(); err != nil { @@ -112,17 +112,17 @@ func main() { builder.AddFlociAws("floci-tls").WithHttpsDeveloperCertificate() builder.AddFlociAzure("floci-az-tls").WithHttpsDeveloperCertificate() builder.AddFlociAws("floci-configured").WithConfigFile("./floci.yml") - _ = aws.PrimaryEndpoint() - _ = aws.Host() - _ = aws.Port() + check(aws.PrimaryEndpoint().Err()) + check(aws.Host().Err()) + check(aws.Port().Err()) _ = aws.ConnectionStringExpression() - _ = azure.PrimaryEndpoint() - _ = azure.Host() - _ = azure.Port() + check(azure.PrimaryEndpoint().Err()) + check(azure.Host().Err()) + check(azure.Port().Err()) _ = azure.ConnectionStringExpression() - _ = gcp.PrimaryEndpoint() - _ = gcp.Host() - _ = gcp.Port() + check(gcp.PrimaryEndpoint().Err()) + check(gcp.Host().Err()) + check(gcp.Port().Err()) _ = gcp.ConnectionStringExpression() builder.AddDotnetProject("api", "../FlociApi/FlociApi.csproj"). WithFlociAwsReference(aws).WithFlociAzureReference(azure).WithFlociGcpReference(gcp) @@ -142,3 +142,9 @@ func main() { log.Fatal(aspire.FormatError(err)) } } + +func check(err error) { + if err != nil { + log.Fatal(aspire.FormatError(err)) + } +} diff --git a/examples/k6/CommunityToolkit.Aspire.Hosting.k6.AppHost.Go/apphost.go b/examples/k6/CommunityToolkit.Aspire.Hosting.k6.AppHost.Go/apphost.go index 4c798e20e..a621c08a8 100644 --- a/examples/k6/CommunityToolkit.Aspire.Hosting.k6.AppHost.Go/apphost.go +++ b/examples/k6/CommunityToolkit.Aspire.Hosting.k6.AppHost.Go/apphost.go @@ -22,9 +22,10 @@ func main() { browserK6.WithScript("/scripts/main.js", &aspire.WithScriptOptions{ VirtualUsers: aspire.Float64Ptr(5), Duration: aspire.StringPtr("45s"), }).WithK6OtlpEnvironment() - _ = defaultK6.PrimaryEndpoint() - _ = browserK6.PrimaryEndpoint() for _, resource := range []aspire.K6Resource{defaultK6, browserK6} { + if err := resource.PrimaryEndpoint().Err(); err != nil { + log.Fatal(aspire.FormatError(err)) + } if err := resource.Err(); err != nil { log.Fatal(aspire.FormatError(err)) } From ad4220abf6f6d4394b4ec7e3cd0da5108c6adfa3 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Wed, 11 Nov 2026 16:18:59 +1100 Subject: [PATCH 25/26] docs: clarify Go model validation gaps Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- docs/go-apphost-parity.md | 12 +++++++----- .../GoAppHostTests.cs | 1 + .../GoAppHostTests.cs | 1 + .../GoAppHostTests.cs | 1 + 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/go-apphost-parity.md b/docs/go-apphost-parity.md index 59f98bf9f..7aee8c7b5 100644 --- a/docs/go-apphost-parity.md +++ b/docs/go-apphost-parity.md @@ -50,13 +50,15 @@ repository's pinned Aspire SDK (`17.1.0-preview.1.26559.8`): model construction when the TypeScript example's explicit credentials are supplied. Its Go counterpart retains those credentials and is compile-tested. - **Flare:** `WithPublicUrlsOptions.DashboardUrl` is a pointer to a parameter - interface. Passing the example's `flare-dashboard-url` fails model - construction because of the same serialization defect; omitting it changes - the configured public URL. + interface. Passing the example's `flare-dashboard-url` causes + `CommunityToolkit.Aspire.Hosting.Flare/withPublicUrls` to fail with + `INVALID_ARGUMENT` for `dashboardUrl`; omitting it changes the configured + public URL. - **Keycloak extensions:** The example's PostgreSQL `userName` and `password` are parameter handles supplied to `addPostgres`. The generated Go options use - parameter-interface pointers, which fail model construction with the same - serialization defect. Replacing the explicit credentials changes the example. + parameter-interface pointers; `Aspire.Hosting.PostgreSQL/addPostgres` fails + with `INVALID_ARGUMENT` for `userName`. Replacing the explicit credentials + changes the example. The Python extensions Go example runs, but its publish-mode model fails: `AddStreamlitApp` removes the Python resource before adding its Streamlit diff --git a/tests/CommunityToolkit.Aspire.Hosting.ActiveMQ.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.ActiveMQ.Tests/GoAppHostTests.cs index d06d7e036..75c8dec08 100644 --- a/tests/CommunityToolkit.Aspire.Hosting.ActiveMQ.Tests/GoAppHostTests.cs +++ b/tests/CommunityToolkit.Aspire.Hosting.ActiveMQ.Tests/GoAppHostTests.cs @@ -10,6 +10,7 @@ public async Task GoAppHostCompiles() await GoAppHostTest.Run( appHostProject: "CommunityToolkit.Aspire.Hosting.ActiveMQ.AppHost.Go", exampleName: "activemq", + // The preview SDK cannot serialize the optional credential handles. validatePublish: false, cancellationToken: TestContext.Current.CancellationToken); } diff --git a/tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/GoAppHostTests.cs index 7b24f087b..140772e41 100644 --- a/tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/GoAppHostTests.cs +++ b/tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/GoAppHostTests.cs @@ -10,6 +10,7 @@ public async Task GoAppHostCompiles() await GoAppHostTest.Run( appHostProject: "CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.AppHost.Go", exampleName: "sql-database-projects", + // The prebuilt Windows AppHost server fails to load hostfxr for MSBuildLocator. validatePublish: !OperatingSystem.IsWindows(), cancellationToken: TestContext.Current.CancellationToken); } diff --git a/tests/CommunityToolkit.Aspire.Hosting.Zitadel.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Zitadel.Tests/GoAppHostTests.cs index f508c125b..342a1df51 100644 --- a/tests/CommunityToolkit.Aspire.Hosting.Zitadel.Tests/GoAppHostTests.cs +++ b/tests/CommunityToolkit.Aspire.Hosting.Zitadel.Tests/GoAppHostTests.cs @@ -10,6 +10,7 @@ public async Task GoAppHostCompiles() await GoAppHostTest.Run( appHostProject: "CommunityToolkit.Aspire.Hosting.Zitadel.AppHost.Go", exampleName: "zitadel", + // The preview SDK cannot serialize the optional credential handles. validatePublish: false, cancellationToken: TestContext.Current.CancellationToken); } From 9085b97d488d66e54151dc3e7f51e291564ee1e4 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Fri, 13 Nov 2026 12:17:11 +1100 Subject: [PATCH 26/26] Fix SQL polyglot AppHost test restore environment (#2529) * test: diagnose SQL polyglot restore failures Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix: isolate SQL polyglot tests from MSBuild locator Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../GoAppHostTests.cs | 4 +- .../SqlPolyglotAppHostTest.cs | 35 ++++++++++++++++ .../SqlPolyglotAppHostTestTests.cs | 40 +++++++++++++++++++ .../TypeScriptAppHostTests.cs | 4 +- 4 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/SqlPolyglotAppHostTest.cs create mode 100644 tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/SqlPolyglotAppHostTestTests.cs diff --git a/tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/GoAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/GoAppHostTests.cs index 140772e41..1dd58489c 100644 --- a/tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/GoAppHostTests.cs +++ b/tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/GoAppHostTests.cs @@ -7,11 +7,11 @@ public class GoAppHostTests [Fact] public async Task GoAppHostCompiles() { - await GoAppHostTest.Run( + await SqlPolyglotAppHostTest.Run(() => GoAppHostTest.Run( appHostProject: "CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.AppHost.Go", exampleName: "sql-database-projects", // The prebuilt Windows AppHost server fails to load hostfxr for MSBuildLocator. validatePublish: !OperatingSystem.IsWindows(), - cancellationToken: TestContext.Current.CancellationToken); + cancellationToken: TestContext.Current.CancellationToken)); } } diff --git a/tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/SqlPolyglotAppHostTest.cs b/tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/SqlPolyglotAppHostTest.cs new file mode 100644 index 000000000..e74659b20 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/SqlPolyglotAppHostTest.cs @@ -0,0 +1,35 @@ +namespace CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests; + +internal static class SqlPolyglotAppHostTest +{ + private static readonly string[] s_msbuildEnvironmentVariables = + [ + "MSBUILD_EXE_PATH", + "MSBuildExtensionsPath", + "MSBuildSDKsPath" + ]; + + public static async Task Run(Func test) + { + // MSBuildLocator in other SQL tests configures in-process MSBuild; child dotnet builds + // must select their own SDK instead of inheriting those paths. + Dictionary originalValues = s_msbuildEnvironmentVariables.ToDictionary( + name => name, Environment.GetEnvironmentVariable); + try + { + foreach (string name in s_msbuildEnvironmentVariables) + { + Environment.SetEnvironmentVariable(name, null); + } + + await test(); + } + finally + { + foreach ((string name, string? value) in originalValues) + { + Environment.SetEnvironmentVariable(name, value); + } + } + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/SqlPolyglotAppHostTestTests.cs b/tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/SqlPolyglotAppHostTestTests.cs new file mode 100644 index 000000000..5d0eb94af --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/SqlPolyglotAppHostTestTests.cs @@ -0,0 +1,40 @@ +namespace CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests; + +public class SqlPolyglotAppHostTestTests +{ + [Fact] + public async Task RestoresMSBuildEnvironmentAfterFailure() + { + string[] variables = ["MSBUILD_EXE_PATH", "MSBuildExtensionsPath", "MSBuildSDKsPath"]; + Dictionary originalValues = variables.ToDictionary(name => name, Environment.GetEnvironmentVariable); + try + { + foreach (string variable in variables) + { + Environment.SetEnvironmentVariable(variable, "test-sdk-path"); + } + + await Assert.ThrowsAsync(() => SqlPolyglotAppHostTest.Run(() => + { + foreach (string variable in variables) + { + Assert.Null(Environment.GetEnvironmentVariable(variable)); + } + + throw new InvalidOperationException(); + })); + + foreach (string variable in variables) + { + Assert.Equal("test-sdk-path", Environment.GetEnvironmentVariable(variable)); + } + } + finally + { + foreach ((string variable, string? value) in originalValues) + { + Environment.SetEnvironmentVariable(variable, value); + } + } + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/TypeScriptAppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/TypeScriptAppHostTests.cs index 27163b8c9..fb689b2fc 100644 --- a/tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/TypeScriptAppHostTests.cs +++ b/tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/TypeScriptAppHostTests.cs @@ -9,11 +9,11 @@ public class TypeScriptAppHostTests [Fact] public async Task TypeScriptAppHostCompilesAndStarts() { - await TypeScriptAppHostTest.Run( + await SqlPolyglotAppHostTest.Run(() => TypeScriptAppHostTest.Run( appHostProject: "CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.AppHost.TypeScript", packageName: "CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects", exampleName: "sql-database-projects", waitForResources: [], - cancellationToken: TestContext.Current.CancellationToken); + cancellationToken: TestContext.Current.CancellationToken)); } } \ No newline at end of file