Skip to content
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
40e8537
Add python support
sebastienros Jan 16, 2026
5759866
Add go support
sebastienros Jan 16, 2026
c9e8de9
Add Java code generator support
sebastienros Jan 16, 2026
c8ce76e
Add Rust code generator support for polyglot AppHosts (WIP)
sebastienros Jan 16, 2026
ef59cd9
Fix Rust code generator issues
sebastienros Jan 16, 2026
9b40815
Fix Java and Rust code generators - Fix RuntimeSpec timing: InstallDe…
sebastienros Jan 16, 2026
46ad57f
Fix Go code generator RuntimeSpec and module config
sebastienros Jan 16, 2026
5865a70
Add E2E test
sebastienros Jan 16, 2026
adb0fae
Fix named pipes for rust/java
sebastienros Jan 17, 2026
cffe834
Merge main - adopt additionalProjectReferences approach for code gene…
sebastienros Jan 17, 2026
467ddf4
Run PolyglotPythonTests as E2E
sebastienros Jan 17, 2026
9cffcc0
Move PolyglotPythonTests to correct E2E test folder for CI discovery
sebastienros Jan 17, 2026
b4f8139
Add comprehensive Python code generator tests with Verify snapshots
sebastienros Jan 17, 2026
2f1fc9f
Add unit tests for Go, Java, and Rust code generators
sebastienros Jan 17, 2026
5dbb4bd
Merge remote-tracking branch 'origin/main' into sebros/poly
sebastienros Jan 17, 2026
9505fcf
Apply list property improvements from TypeScript to Python, Go, Java,…
sebastienros Jan 17, 2026
1ee5bb8
Enable polyglot support feature flag before init -l in E2E test
sebastienros Jan 17, 2026
f85aca2
Fix MethodAccessException when calling AtsConstants.IsDict from code …
sebastienros Jan 17, 2026
cf6be2e
Simplify Python E2E test to not require Python runtime
sebastienros Jan 17, 2026
e70b1d1
Add file content verification to Python E2E test
sebastienros Jan 17, 2026
82f21f8
Merge remote-tracking branch 'origin/main' into sebros/poly
sebastienros Jan 20, 2026
d4f50d7
Add instructions
sebastienros Jan 20, 2026
443c51e
Fix Python E2E test to handle version selection prompt in CI
sebastienros Jan 20, 2026
600f52b
Fix Python E2E test pattern to match actual CLI output
sebastienros Jan 21, 2026
cbf1b51
Fix Python E2E test: verify settings.json instead of .modules (code g…
sebastienros Jan 21, 2026
b3c5110
Fix Python E2E test: verify .modules exists after init, settings.json…
sebastienros Jan 21, 2026
e96f8bf
Fix: Continue SDK code generation even if dependency install fails
sebastienros Jan 21, 2026
10f8acc
Add polyglot E2E tests for Go, Rust, and Java
sebastienros Jan 21, 2026
1137f4a
Fix polyglot E2E tests with correct code patterns
sebastienros Jan 21, 2026
358da78
Simplify polyglot E2E test assertions for SDK patterns
sebastienros Jan 21, 2026
f0c026f
Remove static base/transport snapshot tests and add missing polyglot …
sebastienros Jan 21, 2026
5e8525c
Fix Rust
sebastienros Jan 21, 2026
03c070d
Detect local python command
sebastienros Jan 21, 2026
41515b8
Fix Rust
sebastienros Jan 21, 2026
4d8e786
Add build checks
sebastienros Jan 21, 2026
3edd328
Add polyglot validation to CI workflow
sebastienros Jan 21, 2026
9e00e43
Use Docker containers from mcr.microsoft.com for polyglot validation
sebastienros Jan 21, 2026
fa4be8a
Remove ASPIRE_CLI_PATH requirement from validation scripts
sebastienros Jan 21, 2026
92e964e
Use native CLI archive for polyglot validation with .NET SDK for AppH…
sebastienros Jan 21, 2026
3081dc2
Merge branch 'main' into sebros/poly
sebastienros Jan 21, 2026
befcfa2
Update codegen snapshots for AppHostFilePath property
sebastienros Jan 21, 2026
4dffab0
Fix CLI archive path with find command
sebastienros Jan 21, 2026
8eb84ca
Use dogfood script to install CLI and NuGet packages from PR
sebastienros Jan 21, 2026
44009a0
TEMP: Install gh CLI in Docker, disable other tests for faster polygl…
sebastienros Jan 21, 2026
891d9b0
Merge branch 'main' into sebros/poly
sebastienros Jan 21, 2026
8b10909
Fix polyglot validation workflow to include Docker CLI
sebastienros Jan 22, 2026
a17c100
Update polyglot test scripts to use aspire run --detach and aspire stop
sebastienros Jan 22, 2026
1e75f6a
Update polyglot test scripts to use aspire run --detach and aspire stop
sebastienros Jan 22, 2026
b9eecd9
Make channel config setting non-fatal in get-aspire-cli-pr.sh
sebastienros Jan 22, 2026
e115c18
Fix aspire add --non-interactive to auto-select latest version
sebastienros Jan 22, 2026
762d7d6
Add -d flag to aspire run --detach for debug logging
sebastienros Jan 22, 2026
ca76992
Use local get-aspire-cli-pr.sh script instead of downloading from main
sebastienros Jan 22, 2026
734040b
Use background job for aspire run instead of --detach
sebastienros Jan 22, 2026
1581358
Use kill -9 and remove wait to prevent blocking on cleanup
sebastienros Jan 22, 2026
1e76779
Use go:1-trixie image for GLIBC 2.38 support
sebastienros Jan 22, 2026
e77122f
Re-enable all tests that were temporarily disabled
sebastienros Jan 22, 2026
7bfa895
Add TypeScript polyglot SDK validation
sebastienros Jan 22, 2026
f38a13c
Remove Base.verified.java, Transport.verified.java and their tests
sebastienros Jan 22, 2026
73392bc
Remove PolyglotPythonTests, PolyglotGoTests, PolyglotRustTests, Polyg…
sebastienros Jan 22, 2026
0ba21de
Use setsid and kill process group to properly stop apphost
sebastienros Jan 22, 2026
0b8cc23
Add Redis in a consistent way across scripts
sebastienros Jan 22, 2026
58fd469
Add Dockerfiles for polyglot SDK validation
sebastienros Jan 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/cli-e2e-recording-comment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ jobs:
a.name.includes('JsReactTemplateTests') ||
a.name.includes('PythonReactTemplateTests') ||
a.name.includes('DockerDeploymentTests') ||
a.name.includes('TypeScriptPolyglotTests'))
a.name.includes('TypeScriptPolyglotTests') ||
a.name.includes('PolyglotPythonTests'))
);

console.log(`Found ${cliE2eArtifacts.length} CLI E2E artifacts`);
Expand Down
5 changes: 5 additions & 0 deletions Aspire.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,10 @@
</Folder>
<Folder Name="/src/">
<Project Path="src/Aspire.Hosting.CodeGeneration.TypeScript/Aspire.Hosting.CodeGeneration.TypeScript.csproj" />
<Project Path="src/Aspire.Hosting.CodeGeneration.Python/Aspire.Hosting.CodeGeneration.Python.csproj" />
<Project Path="src/Aspire.Hosting.CodeGeneration.Go/Aspire.Hosting.CodeGeneration.Go.csproj" />
<Project Path="src/Aspire.Hosting.CodeGeneration.Java/Aspire.Hosting.CodeGeneration.Java.csproj" />
<Project Path="src/Aspire.Hosting.CodeGeneration.Rust/Aspire.Hosting.CodeGeneration.Rust.csproj" />
<Project Path="src/Aspire.Hosting.RemoteHost/Aspire.Hosting.RemoteHost.csproj" />
</Folder>
<Folder Name="/Testing/">
Expand Down Expand Up @@ -447,6 +451,7 @@
<Project Path="tests/Aspire.Hosting.Redis.Tests/Aspire.Hosting.Redis.Tests.csproj" />
<Project Path="tests/Aspire.Hosting.RemoteHost.Tests/Aspire.Hosting.RemoteHost.Tests.csproj" />
<Project Path="tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests.csproj" />
<Project Path="tests/Aspire.Hosting.CodeGeneration.Python.Tests/Aspire.Hosting.CodeGeneration.Python.Tests.csproj" />
<Project Path="tests/Aspire.Hosting.Seq.Tests/Aspire.Hosting.Seq.Tests.csproj" />
<Project Path="tests/Aspire.Hosting.SqlServer.Tests/Aspire.Hosting.SqlServer.Tests.csproj" />
<Project Path="tests/Aspire.Hosting.Testing.Tests/Aspire.Hosting.Testing.Tests.csproj" />
Expand Down
109 changes: 108 additions & 1 deletion docs/specs/polyglot-apphost.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ This document describes how the Aspire CLI supports non-.NET app hosts using the
8. [CLI Integration](#cli-integration)
9. [Configuration](#configuration)
10. [Adding New Guest Languages](#adding-new-guest-languages)
11. [Security](#security)
11. [Local Development Workflow](#local-development-workflow)
12. [Security](#security)

---

Expand Down Expand Up @@ -1404,6 +1405,112 @@ public sealed class AtsContext

---

## Local Development Workflow

This section explains how to develop and test custom language SDKs using a local clone of the Aspire repository.

### Prerequisites

1. Clone the Aspire repository:

```bash
git clone https://github.com/dotnet/aspire.git
cd aspire
```

2. Build the repository:

```bash
./build.sh # macOS/Linux
./build.cmd # Windows
```

### Enabling Polyglot Support

Polyglot support is behind a feature flag that must be enabled before you can use non-.NET languages. Enable it using the `aspire config` command:

```bash
# Enable polyglot support globally (recommended for SDK development)
aspire config set features:polyglotSupportEnabled true --global

# Or enable it locally for a specific project
aspire config set features:polyglotSupportEnabled true
```

This enables the `--language` option on `aspire init` and `aspire new` commands, and allows the CLI to detect and run non-.NET app hosts.

> **Note:** When running the CLI from source with `dotnet run`, use the full command:
> ```bash
> dotnet run --project /path/to/aspire/src/Aspire.Cli/Aspire.Cli.csproj -- config set features:polyglotSupportEnabled true --global
> ```

### Setting Up Local Development Mode

Set the `ASPIRE_REPO_ROOT` environment variable to point to your local Aspire clone. This enables **development mode**, which:

- Uses **project references** instead of NuGet package references for the AppHost server
- **Forces code regeneration** on every run (bypasses the package hash cache)
- Allows you to immediately test changes without publishing packages

```bash
# macOS/Linux
export ASPIRE_REPO_ROOT="/path/to/aspire"

# Windows (PowerShell)
$env:ASPIRE_REPO_ROOT = "D:\aspire"

# Windows (Command Prompt)
set ASPIRE_REPO_ROOT=D:\aspire
```

### Scaffolding a New App

Use `dotnet run` to execute the CLI directly from source:

```bash
# Create a new Python app
dotnet run --project /path/to/aspire/src/Aspire.Cli/Aspire.Cli.csproj -- init -l python

# Create a new TypeScript app
dotnet run --project /path/to/aspire/src/Aspire.Cli/Aspire.Cli.csproj -- init -l typescript
```

The `-l` (or `--language`) flag specifies the target language for scaffolding.

### Running the App

```bash
# Run the app (standard mode)
dotnet run --project /path/to/aspire/src/Aspire.Cli/Aspire.Cli.csproj -- run

# Run with debug output
dotnet run --project /path/to/aspire/src/Aspire.Cli/Aspire.Cli.csproj -- run -d
```

The `-d` (or `--debug`) flag enables additional diagnostic output, useful when developing and troubleshooting language support.

### Development Workflow Tips

1. **Rapid iteration**: With `ASPIRE_REPO_ROOT` set, the generated SDK in `.modules/` is regenerated on each run, so changes to code generation logic are immediately reflected.

2. **Testing code generators**: Modify your `ICodeGenerator` implementation, then run `aspire run` in a test app—the new generated code will be produced automatically.

3. **Testing language support**: Modify your `ILanguageSupport` implementation, then use `aspire init -l <language>` to test scaffolding or `aspire run` to test detection and execution.

4. **Inspecting generated code**: Check the `.modules/` folder in your test app to see the generated SDK files and verify they match your expectations.

### Quick Reference

| Task | Command |
|------|---------|
| Scaffold Python app | `dotnet run --project $ASPIRE_REPO_ROOT/src/Aspire.Cli/Aspire.Cli.csproj -- init -l python` |
| Scaffold TypeScript app | `dotnet run --project $ASPIRE_REPO_ROOT/src/Aspire.Cli/Aspire.Cli.csproj -- init -l typescript` |
| Run app | `dotnet run --project $ASPIRE_REPO_ROOT/src/Aspire.Cli/Aspire.Cli.csproj -- run` |
| Run with debug output | `dotnet run --project $ASPIRE_REPO_ROOT/src/Aspire.Cli/Aspire.Cli.csproj -- run -d` |
| Add integration | `dotnet run --project $ASPIRE_REPO_ROOT/src/Aspire.Cli/Aspire.Cli.csproj -- add` |

---

## Security

Both guest and host run locally on the same machine, started by the CLI. This is **not** remote execution.
Expand Down
11 changes: 11 additions & 0 deletions src/Aspire.Cli/Projects/AppHostServerProject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -618,12 +618,23 @@ private XDocument CreateProductionProjectFile(string sdkVersion, IEnumerable<(st

/// <summary>
/// Gets the socket path for the AppHost server based on the app path.
/// On Windows, returns just the pipe name (named pipes don't use file paths).
/// On Unix/macOS, returns the full socket file path.
/// </summary>
public string GetSocketPath()
{
var pathHash = SHA256.HashData(Encoding.UTF8.GetBytes(_appPath));
var socketName = Convert.ToHexString(pathHash)[..12].ToLowerInvariant() + ".sock";

// On Windows, named pipes use just a name, not a file path.
// The .NET NamedPipeServerStream and clients will automatically
// use the \\.\pipe\ prefix.
if (OperatingSystem.IsWindows())
{
return socketName;
}

// On Unix/macOS, use Unix domain sockets with a file path
var socketDir = Path.Combine(Path.GetTempPath(), FolderPrefix, "sockets");
Directory.CreateDirectory(socketDir);

Expand Down
28 changes: 28 additions & 0 deletions src/Aspire.Cli/Projects/DefaultLanguageDiscovery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,34 @@ internal sealed class DefaultLanguageDiscovery : ILanguageDiscovery
DetectionPatterns: ["apphost.ts"],
CodeGenerator: "TypeScript", // Matches ICodeGenerator.Language
AppHostFileName: "apphost.ts"),
new LanguageInfo(
LanguageId: new LanguageId(KnownLanguageId.Python),
DisplayName: KnownLanguageId.PythonDisplayName,
PackageName: "Aspire.Hosting.CodeGeneration.Python",
DetectionPatterns: ["apphost.py"],
CodeGenerator: "Python",
AppHostFileName: "apphost.py"),
new LanguageInfo(
LanguageId: new LanguageId(KnownLanguageId.Go),
DisplayName: KnownLanguageId.GoDisplayName,
PackageName: "Aspire.Hosting.CodeGeneration.Go",
DetectionPatterns: ["apphost.go"],
CodeGenerator: "Go",
AppHostFileName: "apphost.go"),
new LanguageInfo(
LanguageId: new LanguageId(KnownLanguageId.Java),
DisplayName: KnownLanguageId.JavaDisplayName,
PackageName: "Aspire.Hosting.CodeGeneration.Java",
DetectionPatterns: ["AppHost.java"],
CodeGenerator: "Java",
AppHostFileName: "AppHost.java"),
new LanguageInfo(
LanguageId: new LanguageId(KnownLanguageId.Rust),
DisplayName: KnownLanguageId.RustDisplayName,
PackageName: "Aspire.Hosting.CodeGeneration.Rust",
DetectionPatterns: ["apphost.rs"],
CodeGenerator: "Rust",
AppHostFileName: "apphost.rs"),
];

/// <inheritdoc />
Expand Down
8 changes: 2 additions & 6 deletions src/Aspire.Cli/Projects/GuestAppHostProject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,12 +198,8 @@ private async Task BuildAndGenerateSdkAsync(DirectoryInfo directory, Cancellatio
// Step 3: Connect to server
await using var rpcClient = await AppHostRpcClient.ConnectAsync(socketPath, cancellationToken);

// Step 4: Install dependencies using GuestRuntime
var installResult = await InstallDependenciesAsync(directory, rpcClient, cancellationToken);
if (installResult != 0)
{
return;
}
// Step 4: Install dependencies using GuestRuntime (best effort - don't block code generation)
await InstallDependenciesAsync(directory, rpcClient, cancellationToken);

// Step 5: Generate SDK code via RPC
await GenerateCodeViaRpcAsync(
Expand Down
40 changes: 40 additions & 0 deletions src/Aspire.Cli/Projects/KnownLanguageId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,44 @@ internal static class KnownLanguageId
/// Short alias for TypeScript that can be used on the command line.
/// </summary>
public const string TypeScriptAlias = "typescript";

/// <summary>
/// The language ID for Python AppHost projects.
/// </summary>
public const string Python = "python";

/// <summary>
/// The display name for Python AppHost projects.
/// </summary>
public const string PythonDisplayName = "Python";

/// <summary>
/// The language ID for Go AppHost projects.
/// </summary>
public const string Go = "go";

/// <summary>
/// The display name for Go AppHost projects.
/// </summary>
public const string GoDisplayName = "Go";

/// <summary>
/// The language ID for Java AppHost projects.
/// </summary>
public const string Java = "java";

/// <summary>
/// The display name for Java AppHost projects.
/// </summary>
public const string JavaDisplayName = "Java";

/// <summary>
/// The language ID for Rust AppHost projects.
/// </summary>
public const string Rust = "rust";

/// <summary>
/// The display name for Rust AppHost projects.
/// </summary>
public const string RustDisplayName = "Rust";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>$(DefaultTargetFramework)</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Aspire.Hosting.CodeGeneration.Go</RootNamespace>
<!-- Required for Go AppHost support -->
<IsPackable>true</IsPackable>
<!-- No baseline exists since this package was previously not published -->
<DisablePackageBaselineValidation>true</DisablePackageBaselineValidation>
<IsAotCompatible>true</IsAotCompatible>
</PropertyGroup>

<ItemGroup>
<InternalsVisibleTo Include="aspire" />
<InternalsVisibleTo Include="Aspire.Hosting.CodeGeneration.Go.Tests" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="Resources\transport.go" LogicalName="Aspire.Hosting.CodeGeneration.Go.Resources.transport.go" />
<EmbeddedResource Include="Resources\base.go" LogicalName="Aspire.Hosting.CodeGeneration.Go.Resources.base.go" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Aspire.Hosting\Aspire.Hosting.csproj" />
</ItemGroup>

</Project>
Loading
Loading