Add Empty (Python AppHost) top-level template entry#16714
Add Empty (Python AppHost) top-level template entry#16714mitchdenny wants to merge 4 commits intomainfrom
Conversation
When the experimentalPolyglot:python feature flag is enabled, expose a dedicated 'Empty (Python AppHost)' template via 'aspire new', mirroring the existing Java pattern. Previously Python was only reachable through the generic 'Empty AppHost' language picker, leading to inconsistent behavior across enabled experimental AppHost languages.
Adds KnownTemplateId.PythonEmptyAppHost ('aspire-py-empty') and the matching CallbackTemplate registration in CliTemplateFactory. The template is automatically gated by ILanguageDiscovery.GetLanguageById, which already returns null for Python when the experimentalPolyglot:python feature flag is off.
Fixes #16662
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 16714Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 16714" |
There was a problem hiding this comment.
Pull request overview
Adds a dedicated top-level aspire new template entry for an Empty (Python AppHost), bringing Python in line with the existing “Empty (Java AppHost)” behavior when the experimental polyglot Python feature flag is enabled.
Changes:
- Introduces
KnownTemplateId.PythonEmptyAppHost(aspire-py-empty) for a Python empty AppHost CLI template. - Registers a new
CallbackTemplateinCliTemplateFactoryforEmpty (Python AppHost)withlanguageId: KnownLanguageId.PythonandisEmpty: true, relying on existing language-discovery gating to hide it when the feature is off. - Adds CLI unit tests validating that the Python empty AppHost subcommand is exposed only when
ExperimentalPolyglotPythonis enabled.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| tests/Aspire.Cli.Tests/Commands/NewCommandTests.cs | Adds coverage for the presence/absence of the Python empty AppHost subcommand based on the Python polyglot feature flag. |
| src/Aspire.Cli/Templating/KnownTemplateId.cs | Defines the new known template ID constant for the Python empty AppHost template. |
| src/Aspire.Cli/Templating/CliTemplateFactory.cs | Registers the new top-level Empty (Python AppHost) callback template entry gated by language discovery. |
Adds CreateAndRunPythonEmptyAppHostProject which mirrors CreateAndRunJavaEmptyAppHostProject, exercising the new top-level Empty (Python AppHost) entry that surfaces when features.experimentalPolyglot:python is enabled. Adds the supporting shared infrastructure: - AspireTemplate.PythonEmptyAppHost enum value - Selection cases in Hex1bAutomatorTestHelpers (async automator API) and Hex1bTestHelpers (legacy builder API) that type "Empty (Python AppHost)" and verify the highlighted entry - EnableExperimentalPythonSupportAsync helper in CliE2EAutomatorHelpers Uses the default DockerfileVariant.DotNet image, which already includes Python (matches PythonReactTemplateTests). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Python language scaffolder did not emit a .gitignore, leaving Aspire- generated runtime artifacts (.venv, .modules, .aspire) and Python build artifacts (__pycache__, *.pyc) untracked-by-default but uncommitted-by- default — i.e., subject to accidental commit. Java and TypeScript both ship a .gitignore from their scaffolders. Add a .gitignore to PythonLanguageSupport.Scaffold mirroring the Java/ TypeScript pattern, with Python-specific additions for .venv/, __pycache__/, and *.pyc. Discovered by the new PythonEmptyAppHostTemplateTests.CreateAndRunPython EmptyAppHostProject E2E test, which mirrors the Java equivalent and asserts the scaffolded project's .gitignore contains '.aspire/'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The aspire start/stop steps were exceeding the CLI's hard 120s "Wait for AppHost to start" timeout in CI runners. Python AppHost cold-start (microvenv creation + pip install of packages from .modules/PyPI) is genuinely slower than the Java/.NET equivalents because Java's startup is just JVM bring-up while Python has to materialize a virtual environment from scratch. That cold-start time vs the CLI's hard timeout cap is a separate product concern that is not in scope for #16662 (which is about exposing the "Empty (Python AppHost)" top-level template entry). The remaining test still: - Drives the interactive aspire new flow. - Asserts the highlighted "> Empty (Python AppHost)" selection appears (via AspireNewAsync's PythonEmptyAppHost case). - Asserts the scaffolded project includes a .gitignore with .aspire/ (parity with Java/TypeScript scaffolds). The renamed test method (CreateAndScaffoldPythonEmptyAppHostProject) reflects the narrower scope. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🎬 CLI E2E Test Recordings — 77 recordings uploaded (commit View all recordings
📹 Recordings uploaded automatically from CI run #25298551254 |
|
/deployment-test |
|
🚀 Deployment tests starting on PR #16714... This will deploy to real Azure infrastructure. Results will be posted here when complete. |
|
❌ Deployment E2E Tests failed — 26 passed, 7 failed, 0 cancelled View test results and recordings
|
JamesNK
left a comment
There was a problem hiding this comment.
LGTM. Clean, consistent addition that mirrors the existing Java/TypeScript/Go empty AppHost patterns. Template registration, feature gating, .gitignore parity, and test coverage all look good.
|
I'm not certain if the added test cases include logic to help prevent this bug from surfacing again in future iterations, but we should consider having test coverage that validates all listed template options that required a template-callback have one. |
|
Closing because this was addressed by: #16666 |
Description
Add a top-level
Empty (Python AppHost)template entry toaspire new, mirroring the existing Java pattern.Bug: When experimental polyglot languages are enabled,
aspire newexposes a dedicated top-level empty AppHost entry for Java but not for Python:Python was only reachable through the generic
Empty AppHostlanguage picker, while Java was reachable both ways.Fix: Add
KnownTemplateId.PythonEmptyAppHost = "aspire-py-empty"and register a matchingCallbackTemplateinCliTemplateFactorywithlanguageId: KnownLanguageId.PythonandisEmpty: true. The new entry is automatically gated behindKnownFeatures.ExperimentalPolyglotPythonvia the existingIsTemplateAvailable→ILanguageDiscovery.GetLanguageByIdpath (Python returnsnullwhen the flag is off).Behavior:
experimentalPolyglot:pythonenabled →Empty (Python AppHost)appears as a top-level entry alongside Java and TypeScript.experimentalPolyglot:pythondisabled → entry is hidden (existing gating).Drive-by fix: The new E2E test surfaced that
PythonLanguageSupport.Scaffoldwas not emitting a.gitignore, while the Java and TypeScript scaffolders both do. Added a.gitignoreto the Python scaffold output covering Aspire-generated artifacts (.modules/,.aspire/) and Python-specific build/runtime artifacts (.venv/,__pycache__/,*.pyc). This brings Python to parity with Java/TypeScript at the scaffolding level.Adds an E2E test (
PythonEmptyAppHostTemplateTests.CreateAndScaffoldPythonEmptyAppHostProject) that mirrors the structure ofJavaEmptyAppHostTemplateTests, exercising the new top-level entry interactively and verifying the scaffolded.gitignore. The test intentionally omitsaspire start/aspire stop(see Follow-up below).Fixes #16662
Validation
Install this PR build:
Scenario 1 — Python flag OFF (entry hidden)
# Make sure the experimental Python polyglot flag is OFF. aspire config delete -g features.experimentalPolyglot:python aspire newExpected: Top-level template list does NOT include
Empty (Python AppHost).Expected: Fails because the template is unavailable when the flag is off.
Scenario 2 — Python flag ON (entry visible alongside Java)
Expected: Top-level template list now includes:
Pick
Empty (Python AppHost)and confirm it scaffolds a Python AppHost project.aspire new aspire-py-empty -o testpy ls -la testpy/.gitignore # should exist and contain .venv/, __pycache__/, .modules/, .aspire/Expected: Succeeds and creates a Python AppHost in
./testpy, including a.gitignore.Cleanup
Automated coverage
tests/Aspire.Cli.Tests/Commands/NewCommandTests.cs): verify theaspire-py-emptytemplate is exposed when the flag is on and hidden when the flag is off.tests/Aspire.Cli.EndToEnd.Tests/PythonEmptyAppHostTemplateTests.cs): drives the interactiveaspire newflow inside a Linux container, typesEmpty (Python AppHost), asserts the highlighted selection appears, completes scaffolding, and asserts the generated.gitignorecontains.aspire/.Follow-up: Python AppHost cold-start vs. CLI startup timeout (out of scope)
The E2E test deliberately stops at scaffolding rather than running
aspire start/aspire stop. Python AppHost first-run cold-start (microvenv creation + pip install from.modules/PyPI) can exceed the CLI's hard-coded 120s "wait for AppHost to start" timeout (AppHostLauncher.cs:277) on resource-constrained CI runners. Java's empty AppHost doesn't hit this because its bring-up is just JVM start. This is a separate product concern (either bump the timeout, make it configurable per-language, or pre-warm the venv) and is not in scope for this PR, which is purely about exposing the top-level template entry.Note
Issue #16661 (Python being shown in the generic
Empty AppHostlanguage picker even when the flag was off) is fixed in a separate PR (#16713). This PR is purely additive and doesn't alter the language-picker filtering behavior.Checklist
<remarks />and<code />elements on your triple slash comments?aspire.devissue: