Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .github/workflows/polyglot-validation/Dockerfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ ENTRYPOINT ["/bin/bash", "-c", "\
echo '' && \
echo '=== Enabling polyglot support ===' && \
aspire config set features:polyglotSupportEnabled true --global && \
aspire config set features:experimentalPolyglot:go true --global && \
echo '' && \
echo '=== Running validation ===' && \
/scripts/test-go.sh \
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/polyglot-validation/Dockerfile.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
echo '' && \
echo '=== Enabling polyglot support ===' && \
aspire config set features:polyglotSupportEnabled true --global && \
aspire config set features:experimentalPolyglot:java true --global && \
echo '' && \
echo '=== Running validation ===' && \
/scripts/test-java.sh \
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/polyglot-validation/Dockerfile.python
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ ENTRYPOINT ["/bin/bash", "-c", "\
echo '' && \
echo '=== Enabling polyglot support ===' && \
aspire config set features:polyglotSupportEnabled true --global && \
aspire config set features:experimentalPolyglot:python true --global && \
echo '' && \
echo '=== Running validation ===' && \
/scripts/test-python.sh \
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/polyglot-validation/Dockerfile.rust
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ ENTRYPOINT ["/bin/bash", "-c", "\
echo '' && \
echo '=== Enabling polyglot support ===' && \
aspire config set features:polyglotSupportEnabled true --global && \
aspire config set features:experimentalPolyglot:rust true --global && \
echo '' && \
echo '=== Running validation ===' && \
/scripts/test-rust.sh \
Expand Down
71 changes: 71 additions & 0 deletions extension/schemas/aspire-global-settings.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,77 @@
"description": "Enable or disable support for non-.NET (polyglot) languages and runtimes in Aspire applications",
"default": false
},
"experimentalPolyglot": {
"description": "Per-language feature flags for experimental polyglot languages (requires polyglotSupportEnabled).",
"type": "object",
"properties": {
"go": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "string",
"enum": [
"true",
"false"
]
}
],
"description": "Enable or disable experimental Go language support for polyglot Aspire applications (requires polyglotSupportEnabled)",
"default": false
},
"java": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "string",
"enum": [
"true",
"false"
]
}
],
"description": "Enable or disable experimental Java language support for polyglot Aspire applications (requires polyglotSupportEnabled)",
"default": false
},
"python": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "string",
"enum": [
"true",
"false"
]
}
],
"description": "Enable or disable experimental Python language support for polyglot Aspire applications (requires polyglotSupportEnabled)",
"default": false
},
"rust": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "string",
"enum": [
"true",
"false"
]
}
],
"description": "Enable or disable experimental Rust language support for polyglot Aspire applications (requires polyglotSupportEnabled)",
"default": false
}
},
"additionalProperties": false
},
"runningInstanceDetectionEnabled": {
"anyOf": [
{
Expand Down
71 changes: 71 additions & 0 deletions extension/schemas/aspire-settings.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,77 @@
"description": "Enable or disable support for non-.NET (polyglot) languages and runtimes in Aspire applications",
"default": false
},
"experimentalPolyglot": {
"description": "Per-language feature flags for experimental polyglot languages (requires polyglotSupportEnabled).",
"type": "object",
"properties": {
"go": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "string",
"enum": [
"true",
"false"
]
}
],
"description": "Enable or disable experimental Go language support for polyglot Aspire applications (requires polyglotSupportEnabled)",
"default": false
},
"java": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "string",
"enum": [
"true",
"false"
]
}
],
"description": "Enable or disable experimental Java language support for polyglot Aspire applications (requires polyglotSupportEnabled)",
"default": false
},
"python": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "string",
"enum": [
"true",
"false"
]
}
],
"description": "Enable or disable experimental Python language support for polyglot Aspire applications (requires polyglotSupportEnabled)",
"default": false
},
"rust": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "string",
"enum": [
"true",
"false"
]
}
],
"description": "Enable or disable experimental Rust language support for polyglot Aspire applications (requires polyglotSupportEnabled)",
"default": false
}
},
"additionalProperties": false
},
"runningInstanceDetectionEnabled": {
"anyOf": [
{
Expand Down
2 changes: 1 addition & 1 deletion src/Aspire.Cli/Commands/InitCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public InitCommand(
{
_languageOption = new Option<string?>("--language", "-l")
{
Description = "The programming language for the AppHost (csharp, typescript, python)"
Description = "The programming language for the AppHost (csharp, typescript)"
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The --language help text now lists only “csharp, typescript”, but init --language can also accept experimental languages when their feature flags are enabled. Consider mentioning the experimental flags in the option description so --help stays accurate for those scenarios.

Suggested change
Description = "The programming language for the AppHost (csharp, typescript)"
Description = "The programming language for the AppHost (csharp, typescript; additional experimental languages may be available when enabled via feature flags)."

Copilot uses AI. Check for mistakes.
};
Options.Add(_languageOption);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Aspire.Cli/Commands/NewCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public NewCommand(
{
_languageOption = new Option<string?>("--language", "-l")
{
Description = "The programming language for the AppHost (csharp, typescript, python)"
Description = "The programming language for the AppHost (csharp, typescript)"
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The --language help text now lists only “csharp, typescript”, but the command can accept additional experimental languages when their feature flags are enabled. Consider tweaking the description to mention that more languages may be available when experimental polyglot flags are turned on, to avoid making --help misleading for users who enable them.

Suggested change
Description = "The programming language for the AppHost (csharp, typescript)"
Description = "The programming language for the AppHost (for example: csharp, typescript; additional experimental languages may be available when polyglot features are enabled)"

Copilot uses AI. Check for mistakes.
};
Options.Add(_languageOption);
}
Expand Down
24 changes: 24 additions & 0 deletions src/Aspire.Cli/KnownFeatures.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ internal static class KnownFeatures
public static string DefaultWatchEnabled => "defaultWatchEnabled";
public static string ShowAllTemplates => "showAllTemplates";
public static string PolyglotSupportEnabled => "polyglotSupportEnabled";
public static string ExperimentalPolyglotRust => "experimentalPolyglot:rust";
public static string ExperimentalPolyglotJava => "experimentalPolyglot:java";
public static string ExperimentalPolyglotGo => "experimentalPolyglot:go";
public static string ExperimentalPolyglotPython => "experimentalPolyglot:python";
public static string DotNetSdkInstallationEnabled => "dotnetSdkInstallationEnabled";
public static string RunningInstanceDetectionEnabled => "runningInstanceDetectionEnabled";

Expand Down Expand Up @@ -80,6 +84,26 @@ internal static class KnownFeatures
"Enable or disable support for non-.NET (polyglot) languages and runtimes in Aspire applications",
DefaultValue: false),

[ExperimentalPolyglotRust] = new(
ExperimentalPolyglotRust,
"Enable or disable experimental Rust language support for polyglot Aspire applications (requires polyglotSupportEnabled)",
DefaultValue: false),

[ExperimentalPolyglotJava] = new(
ExperimentalPolyglotJava,
"Enable or disable experimental Java language support for polyglot Aspire applications (requires polyglotSupportEnabled)",
DefaultValue: false),

[ExperimentalPolyglotGo] = new(
ExperimentalPolyglotGo,
"Enable or disable experimental Go language support for polyglot Aspire applications (requires polyglotSupportEnabled)",
DefaultValue: false),

[ExperimentalPolyglotPython] = new(
ExperimentalPolyglotPython,
"Enable or disable experimental Python language support for polyglot Aspire applications (requires polyglotSupportEnabled)",
DefaultValue: false),

[DotNetSdkInstallationEnabled] = new(
DotNetSdkInstallationEnabled,
"Enable or disable automatic .NET SDK installation when a required SDK version is missing",
Expand Down
71 changes: 53 additions & 18 deletions src/Aspire.Cli/Projects/DefaultLanguageDiscovery.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Aspire.Cli.Configuration;

namespace Aspire.Cli.Projects;

/// <summary>
Expand All @@ -9,12 +11,9 @@ namespace Aspire.Cli.Projects;
/// </summary>
/// <remarks>
/// This implementation provides a static list of supported languages.
/// Future implementations could discover languages from:
/// - Configuration files (~/.aspire/languages.json)
/// - NuGet search (Aspire.Hosting.Language.* packages)
/// - Remote service endpoints
/// Experimental languages (Go, Java, Rust) are filtered based on per-language feature flags.
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The remarks mention experimental languages as “(Go, Java, Rust)”, but this file also marks Python as experimental. Update the remark to include Python (or reword to avoid listing specific languages) so the documentation matches behavior.

Suggested change
/// Experimental languages (Go, Java, Rust) are filtered based on per-language feature flags.
/// Experimental languages are filtered based on per-language feature flags.

Copilot uses AI. Check for mistakes.
/// </remarks>
internal sealed class DefaultLanguageDiscovery : ILanguageDiscovery
internal sealed class DefaultLanguageDiscovery(IFeatures features) : ILanguageDiscovery
{
private static readonly LanguageInfo[] s_allLanguages =
[
Expand All @@ -38,34 +37,46 @@ internal sealed class DefaultLanguageDiscovery : ILanguageDiscovery
PackageName: "Aspire.Hosting.CodeGeneration.Python",
DetectionPatterns: ["apphost.py"],
CodeGenerator: "Python",
AppHostFileName: "apphost.py"),
AppHostFileName: "apphost.py",
IsExperimental: true),
new LanguageInfo(
LanguageId: new LanguageId(KnownLanguageId.Go),
DisplayName: KnownLanguageId.GoDisplayName,
PackageName: "Aspire.Hosting.CodeGeneration.Go",
DetectionPatterns: ["apphost.go"],
CodeGenerator: "Go",
AppHostFileName: "apphost.go"),
AppHostFileName: "apphost.go",
IsExperimental: true),
new LanguageInfo(
LanguageId: new LanguageId(KnownLanguageId.Java),
DisplayName: KnownLanguageId.JavaDisplayName,
PackageName: "Aspire.Hosting.CodeGeneration.Java",
DetectionPatterns: ["AppHost.java"],
CodeGenerator: "Java",
AppHostFileName: "AppHost.java"),
AppHostFileName: "AppHost.java",
IsExperimental: true),
new LanguageInfo(
LanguageId: new LanguageId(KnownLanguageId.Rust),
DisplayName: KnownLanguageId.RustDisplayName,
PackageName: "Aspire.Hosting.CodeGeneration.Rust",
DetectionPatterns: ["apphost.rs"],
CodeGenerator: "Rust",
AppHostFileName: "apphost.rs"),
AppHostFileName: "apphost.rs",
IsExperimental: true),
];

private static readonly Dictionary<string, string> s_experimentalFeatureFlags = new(StringComparer.OrdinalIgnoreCase)
{
[KnownLanguageId.Python] = KnownFeatures.ExperimentalPolyglotPython,
[KnownLanguageId.Go] = KnownFeatures.ExperimentalPolyglotGo,
[KnownLanguageId.Java] = KnownFeatures.ExperimentalPolyglotJava,
[KnownLanguageId.Rust] = KnownFeatures.ExperimentalPolyglotRust,
};

/// <inheritdoc />
public Task<IEnumerable<LanguageInfo>> GetAvailableLanguagesAsync(CancellationToken cancellationToken = default)
{
return Task.FromResult<IEnumerable<LanguageInfo>>(s_allLanguages);
return Task.FromResult(s_allLanguages.Where(IsLanguageEnabled));
}

/// <inheritdoc />
Expand All @@ -80,7 +91,7 @@ public Task<IEnumerable<LanguageInfo>> GetAvailableLanguagesAsync(CancellationTo
/// <inheritdoc />
public Task<LanguageId?> DetectLanguageAsync(DirectoryInfo directory, CancellationToken cancellationToken = default)
{
foreach (var language in s_allLanguages)
foreach (var language in s_allLanguages.Where(IsLanguageEnabled))
{
foreach (var pattern in language.DetectionPatterns)
{
Expand All @@ -102,25 +113,49 @@ public Task<IEnumerable<LanguageInfo>> GetAvailableLanguagesAsync(CancellationTo
var match = s_allLanguages.FirstOrDefault(l =>
string.Equals(l.LanguageId.Value, languageId.Value, StringComparison.OrdinalIgnoreCase));

if (match is not null)
{
return match;
}

// Try alias match (e.g., "typescript" -> "typescript/nodejs")
return languageId.Value switch
match ??= languageId.Value switch
{
KnownLanguageId.TypeScriptAlias => s_allLanguages.FirstOrDefault(l =>
string.Equals(l.LanguageId.Value, KnownLanguageId.TypeScript, StringComparison.OrdinalIgnoreCase)),
_ => null
};

if (match is not null && !IsLanguageEnabled(match))
{
return null;
}

return match;
}

/// <inheritdoc />
public LanguageInfo? GetLanguageByFile(FileInfo file)
{
return s_allLanguages.FirstOrDefault(l =>
var match = s_allLanguages.FirstOrDefault(l =>
l.DetectionPatterns.Any(p => MatchesPattern(file.Name, p)));

if (match is not null && !IsLanguageEnabled(match))
{
return null;
}

return match;
}

private bool IsLanguageEnabled(LanguageInfo language)
{
if (!language.IsExperimental)
{
return true;
}

if (s_experimentalFeatureFlags.TryGetValue(language.LanguageId.Value, out var featureFlag))
{
return features.IsFeatureEnabled(featureFlag, false);
}

return true;
Comment on lines +153 to +158
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DefaultLanguageDiscovery now filters experimental languages only by the per-language flag, but the feature metadata/docs state these flags require polyglotSupportEnabled. As-is, enabling experimentalPolyglot:* will surface the language even when polyglotSupportEnabled is false. Consider updating IsLanguageEnabled to also require features.IsFeatureEnabled(KnownFeatures.PolyglotSupportEnabled, false) (at least for experimental languages) so runtime behavior matches the documented requirement.

Suggested change
if (s_experimentalFeatureFlags.TryGetValue(language.LanguageId.Value, out var featureFlag))
{
return features.IsFeatureEnabled(featureFlag, false);
}
return true;
// Experimental languages require polyglot support to be enabled globally.
if (!features.IsFeatureEnabled(KnownFeatures.PolyglotSupportEnabled, false))
{
return false;
}
if (s_experimentalFeatureFlags.TryGetValue(language.LanguageId.Value, out var featureFlag))
{
return features.IsFeatureEnabled(featureFlag, false);
}
// If there is no per-language feature flag defined, treat the experimental language as disabled.
return false;

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IsLanguageEnabled returns true if an experimental language isn't present in s_experimentalFeatureFlags. That means a future contributor could mark a language as experimental but forget to add the mapping, and it would become enabled by default (opposite of the intended gating). Consider defaulting to false when IsExperimental is true and no feature-flag mapping exists.

Suggested change
return true;
return false;

Copilot uses AI. Check for mistakes.
}

private static bool MatchesPattern(string fileName, string pattern)
Expand Down
Loading
Loading