File: GoLanguageSupport.cs
Web Access
Project: src\src\Aspire.Hosting.CodeGeneration.Go\Aspire.Hosting.CodeGeneration.Go.csproj (Aspire.Hosting.CodeGeneration.Go)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Aspire.Hosting.Ats;
 
namespace Aspire.Hosting.CodeGeneration.Go;
 
/// <summary>
/// Provides language support for Go AppHosts.
/// Implements scaffolding, detection, and runtime configuration.
/// </summary>
public sealed class GoLanguageSupport : ILanguageSupport
{
    /// <summary>
    /// The language/runtime identifier for Go.
    /// </summary>
    private const string LanguageId = "go";
 
    /// <summary>
    /// The code generation target language. This maps to the ICodeGenerator.Language property.
    /// </summary>
    private const string CodeGenTarget = "Go";
 
    private const string LanguageDisplayName = "Go";
    private static readonly string[] s_detectionPatterns = ["apphost.go"];
 
    /// <inheritdoc />
    public string Language => LanguageId;
 
    /// <inheritdoc />
    public Dictionary<string, string> Scaffold(ScaffoldRequest request)
    {
        var files = new Dictionary<string, string>();
 
        // Create apphost.go
        files["apphost.go"] = """
            // Aspire Go AppHost
            // For more information, see: https://aspire.dev
 
            package main
 
            import (
            	"log"
            	"apphost/modules/aspire"
            )
 
            func main() {
            	builder, err := aspire.CreateBuilder(nil)
            	if err != nil {
            		log.Fatalf("Failed to create builder: %v", err)
            	}
 
            	// Add your resources here, for example:
            	// redis, _ := builder.AddRedis("cache")
            	// postgres, _ := builder.AddPostgres("db")
 
            	app, err := builder.Build()
            	if err != nil {
            		log.Fatalf("Failed to build: %v", err)
            	}
            	if err := app.Run(nil); err != nil {
            		log.Fatalf("Failed to run: %v", err)
            	}
            }
            """;
 
        // Create go.mod with require and replace directives for local modules
        // Go requires both a `require` directive and a `replace` directive for local modules
        files["go.mod"] = """
            module apphost
 
            go 1.23
 
            require apphost/modules/aspire v0.0.0
 
            replace apphost/modules/aspire => ./.modules
            """;
 
        // Create apphost.run.json with random ports
        var random = request.PortSeed.HasValue
            ? new Random(request.PortSeed.Value)
            : Random.Shared;
 
        var httpsPort = random.Next(10000, 65000);
        var httpPort = random.Next(10000, 65000);
        var otlpPort = random.Next(10000, 65000);
        var resourceServicePort = random.Next(10000, 65000);
 
        files["apphost.run.json"] = $$"""
            {
              "profiles": {
                "https": {
                  "applicationUrl": "https://localhost:{{httpsPort}};http://localhost:{{httpPort}}",
                  "environmentVariables": {
                    "ASPNETCORE_ENVIRONMENT": "Development",
                    "DOTNET_ENVIRONMENT": "Development",
                    "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:{{otlpPort}}",
                    "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:{{resourceServicePort}}"
                  }
                }
              }
            }
            """;
 
        return files;
    }
 
    /// <inheritdoc />
    public DetectionResult Detect(string directoryPath)
    {
        var appHostPath = Path.Combine(directoryPath, "apphost.go");
        if (!File.Exists(appHostPath))
        {
            return DetectionResult.NotFound;
        }
 
        var goModPath = Path.Combine(directoryPath, "go.mod");
        if (!File.Exists(goModPath))
        {
            return DetectionResult.NotFound;
        }
 
        return DetectionResult.Found(LanguageId, "apphost.go");
    }
 
    /// <inheritdoc />
    public RuntimeSpec GetRuntimeSpec()
    {
        // Note: InstallDependencies is null because "go run ." handles module
        // resolution automatically, and InstallDependencies runs BEFORE code
        // generation which means the .modules directory doesn't exist yet.
        return new RuntimeSpec
        {
            Language = LanguageId,
            DisplayName = LanguageDisplayName,
            CodeGenLanguage = CodeGenTarget,
            DetectionPatterns = s_detectionPatterns,
            InstallDependencies = null,
            Execute = new CommandSpec
            {
                Command = "go",
                Args = ["run", "."]
            }
        };
    }
}