Skip to content

Add ConfigureEnvFile support to polyglot Docker app hosts#15819

Open
sebastienros wants to merge 7 commits intomainfrom
sebros/ts-configureenvfile-parity
Open

Add ConfigureEnvFile support to polyglot Docker app hosts#15819
sebastienros wants to merge 7 commits intomainfrom
sebros/ts-configureenvfile-parity

Conversation

@sebastienros
Copy link
Copy Markdown
Contributor

Description

This adds ConfigureEnvFile parity for Docker Compose polyglot AppHosts so generated TypeScript, Python, and Java SDKs can customize captured .env entries the same way the C# surface already can. The main goal is to unblock the TypeScript issue path without widening scope into broader Docker Compose projection work.

The change exports ConfigureEnvFile to ATS, projects CapturedEnvironmentVariable just enough for mutation, and updates the shared ATS/runtime pieces needed for typed Dict/List handles to flow through callback parameters correctly. On top of that, the Docker polyglot validation AppHosts now exercise the env-file callback in TypeScript, Python, and Java, the Java shared runtime now exposes mutable AspireDict accessors needed for that callback shape, and the focused TypeScript publish validation asserts that the generated artifacts/.env output changes.

Fixes #15704

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No
  • Does the change require an update in our Aspire docs?

Copilot AI review requested due to automatic review settings April 2, 2026 22:35
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 15819

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 15819"

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds ConfigureEnvFile support to Docker Compose polyglot AppHosts (TypeScript/Python/Java) by exporting the C# API to ATS, projecting CapturedEnvironmentVariable for mutation, and extending ATS collection plumbing so typed Dict/List handles work correctly through callback parameters.

Changes:

  • Export DockerComposeEnvironmentExtensions.ConfigureEnvFile to ATS and project CapturedEnvironmentVariable as a mutable exported handle type (with limited ignored members).
  • Update ATS collection exports + capability scanning/analyzers/tests to support non-generic IDictionary/IList and typed mutable Dict/List handles.
  • Extend polyglot validation fixtures and add an E2E publish test asserting .env output is modified by the callback.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/PolyglotAppHosts/Aspire.Hosting.Docker/TypeScript/apphost.ts Exercises configureEnvFile in the TypeScript Docker Compose validation AppHost.
tests/PolyglotAppHosts/Aspire.Hosting.Docker/Python/apphost.py Exercises configure_env_file in the Python Docker Compose validation AppHost.
tests/PolyglotAppHosts/Aspire.Hosting.Docker/Java/AppHost.java Exercises configureEnvFile in the Java Docker Compose validation AppHost.
tests/Aspire.Hosting.RemoteHost.Tests/CapabilityDispatcherTests.cs Adds coverage ensuring List.get / Dict.get work for typed mutable collections returned as handles.
tests/Aspire.Hosting.RemoteHost.Tests/AtsCapabilityScannerTests.cs Validates ATS type mapping for non-generic IDictionary/IList.
tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java Updates expected generated Java snapshot for new dict helpers.
tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/AtsGeneratedAspire.verified.java Updates expected generated Java snapshot for new dict helpers.
tests/Aspire.Hosting.Analyzers.Tests/AspireExportAnalyzerTests.cs Ensures analyzer allows non-generic IDictionary/IList in exported signatures.
tests/Aspire.Cli.EndToEnd.Tests/TypeScriptPublishTests.cs Adds E2E test asserting configureEnvFile mutates published artifacts/.env.
src/Aspire.Hosting/Ats/CollectionExports.cs Switches intrinsic Dict/List capability exports to non-generic collection interfaces to support typed mutable collections.
src/Aspire.Hosting.RemoteHost/AtsCapabilityScanner.cs Maps non-generic IDictionary/IList to ATS Dict/List and emits full AtsTypeRef metadata.
src/Aspire.Hosting.Integration.Analyzers/AspireExportAnalyzer.cs Allows non-generic IDictionary/IList as ATS-compatible collection types.
src/Aspire.Hosting.Docker/DockerComposeEnvironmentExtensions.cs Exports configureEnvFile to ATS for Docker Compose environments.
src/Aspire.Hosting.Docker/CapturedEnvironmentVariable.cs Exposes env var metadata as an exported handle type with mutable properties for polyglot callbacks.
src/Aspire.Hosting.CodeGeneration.TypeScript/AtsTypeScriptCodeGenerator.cs Ensures callback parameters that are mutable dictionaries are converted to AspireDict wrappers.
src/Aspire.Hosting.CodeGeneration.Java/Resources/Base.java Adds mutable AspireDict accessors needed by the callback shape (get/put/remove/keys/etc.).

@sebastienros sebastienros force-pushed the sebros/ts-configureenvfile-parity branch from 3b9a3bd to f34819b Compare April 3, 2026 00:40
sebastienros and others added 6 commits April 3, 2026 18:33
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sebastienros sebastienros force-pushed the sebros/ts-configureenvfile-parity branch from d6d7790 to 83a6d12 Compare April 4, 2026 01:35
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sebastienros sebastienros requested a review from eerhardt as a code owner April 4, 2026 01:41
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 4, 2026

🎬 CLI E2E Test Recordings — 57 recordings uploaded (commit 7e5a46b)

View recordings
Test Recording
AddPackageInteractiveWhileAppHostRunningDetached ▶️ View Recording
AddPackageWhileAppHostRunningDetached ▶️ View Recording
AgentCommands_AllHelpOutputs_AreCorrect ▶️ View Recording
AgentInitCommand_DefaultSelection_InstallsSkillOnly ▶️ View Recording
AgentInitCommand_MigratesDeprecatedConfig ▶️ View Recording
AllPublishMethodsBuildDockerImages ▶️ View Recording
AspireAddPackageVersionToDirectoryPackagesProps ▶️ View Recording
AspireUpdateRemovesAppHostPackageVersionFromDirectoryPackagesProps ▶️ View Recording
Banner_DisplayedOnFirstRun ▶️ View Recording
Banner_DisplayedWithExplicitFlag ▶️ View Recording
Banner_NotDisplayedWithNoLogoFlag ▶️ View Recording
CertificatesClean_RemovesCertificates ▶️ View Recording
CertificatesTrust_WithNoCert_CreatesAndTrustsCertificate ▶️ View Recording
CertificatesTrust_WithUntrustedCert_TrustsCertificate ▶️ View Recording
ConfigSetGet_CreatesNestedJsonFormat ▶️ View Recording
CreateAndRunAspireStarterProject ▶️ View Recording
CreateAndRunAspireStarterProjectWithBundle ▶️ View Recording
CreateAndRunEmptyAppHostProject ▶️ View Recording
CreateAndRunJavaEmptyAppHostProject ▶️ View Recording
CreateAndRunJsReactProject ▶️ View Recording
CreateAndRunPythonReactProject ▶️ View Recording
CreateAndRunTypeScriptEmptyAppHostProject ▶️ View Recording
CreateAndRunTypeScriptStarterProject ▶️ View Recording
CreateJavaAppHostWithViteApp ▶️ View Recording
CreateStartAndStopAspireProject ▶️ View Recording
CreateTypeScriptAppHostWithViteApp ▶️ View Recording
DashboardRunWithOtelTracesReturnsNoTraces ▶️ View Recording
DescribeCommandResolvesReplicaNames ▶️ View Recording
DescribeCommandShowsRunningResources ▶️ View Recording
DetachFormatJsonProducesValidJson ▶️ View Recording
DoctorCommand_DetectsDeprecatedAgentConfig ▶️ View Recording
DoctorCommand_WithSslCertDir_ShowsTrusted ▶️ View Recording
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted ▶️ View Recording
GlobalMigration_HandlesCommentsAndTrailingCommas ▶️ View Recording
GlobalMigration_HandlesMalformedLegacyJson ▶️ View Recording
GlobalMigration_PreservesAllValueTypes ▶️ View Recording
GlobalMigration_SkipsWhenNewConfigExists ▶️ View Recording
GlobalSettings_MigratedFromLegacyFormat ▶️ View Recording
InvalidAppHostPathWithComments_IsHealedOnRun ▶️ View Recording
LegacySettingsMigration_AdjustsRelativeAppHostPath ▶️ View Recording
LogsCommandShowsResourceLogs ▶️ View Recording
PsCommandListsRunningAppHost ▶️ View Recording
PsFormatJsonOutputsOnlyJsonToStdout ▶️ View Recording
PublishWithConfigureEnvFileUpdatesEnvOutput ▶️ View Recording
PublishWithDockerComposeServiceCallbackSucceeds ▶️ View Recording
RestoreGeneratesSdkFiles ▶️ View Recording
RestoreSupportsConfigOnlyHelperPackageAndCrossPackageTypes ▶️ View Recording
RunFromParentDirectory_UsesExistingConfigNearAppHost ▶️ View Recording
RunWithMissingAwaitShowsHelpfulError ▶️ View Recording
SecretCrudOnDotNetAppHost ▶️ View Recording
SecretCrudOnTypeScriptAppHost ▶️ View Recording
StagingChannel_ConfigureAndVerifySettings_ThenSwitchChannels ▶️ View Recording
StopAllAppHostsFromAppHostDirectory ▶️ View Recording
StopAllAppHostsFromUnrelatedDirectory ▶️ View Recording
StopNonInteractiveMultipleAppHostsShowsError ▶️ View Recording
StopNonInteractiveSingleAppHost ▶️ View Recording
StopWithNoRunningAppHostExitsSuccessfully ▶️ View Recording

📹 Recordings uploaded automatically from CI run #23968629689

Copy link
Copy Markdown
Member

@JamesNK JamesNK left a comment

Choose a reason for hiding this comment

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

Two minor observations (1 empty catch block concern, 1 unintentional BOM in snapshots). Overall the changes look solid — the NormalizeDictionaryKey approach, the analyzer + scanner extensions for non-generic collections, and the TS code generator refactoring are all clean.

return key switch
{
string enumName => Enum.Parse(normalizedKeyType, enumName, ignoreCase: true),
IConvertible convertible => Enum.ToObject(normalizedKeyType, Convert.ChangeType(convertible, Enum.GetUnderlyingType(normalizedKeyType), CultureInfo.InvariantCulture)!),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The empty catch body here means that when key-type conversion fails, the original unconverted key is silently returned. Downstream, IDictionary.Contains returns false for the type-mismatched key, so operations like get return null and remove returns false with no indication of why. While this "best-effort conversion" design is defensible, the empty catch makes it hard to diagnose issues when a polyglot host passes an unexpected key type. Consider adding at least a comment explaining the intent, or emitting a diagnostic trace.

@@ -1,4 +1,4 @@
// ===== Aspire.java =====
// ===== Aspire.java =====
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Both Java snapshot files gained a UTF-8 BOM (EF BB BF) on the first line (the  visible in the diff). The .editorconfig specifies charset = utf-8 (no BOM) for the default file set. If the BOM originates from the Java code generator rather than just a Verify accept artifact, actual generated Java files that users receive would also carry an unnecessary BOM.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TypeScript AppHost: Add ConfigureEnvFile API for Docker Compose

4 participants