Skip to content

Support dynamic resource command inputs#17249

Merged
davidfowl merged 10 commits into
mainfrom
davidfowl/dynamic-command-inputs-main
May 25, 2026
Merged

Support dynamic resource command inputs#17249
davidfowl merged 10 commits into
mainfrom
davidfowl/dynamic-command-inputs-main

Conversation

@davidfowl
Copy link
Copy Markdown
Contributor

Description

Resource commands can define dynamic inputs whose enabled state, defaults, or choice options depend on earlier answers. This change carries that metadata from Hosting through the auxiliary backchannel and CLI JSON so tool clients can prompt correctly instead of blocking or relying on stale command snapshots.

The implementation keeps dynamic input loading authoritative in the AppHost. VS Code uses a hidden aspire resource ... --load-arguments path to send the current partial prompt state, Hosting runs the dynamic load callbacks with ValidateOnly, and the refreshed argument metadata is returned as JSON. Regular CLI execution still works with supplied arguments, while dynamic required/disabled/choice validation is deferred until after Hosting has loaded the current metadata.

User-facing usage

VS Code resource command prompts now refresh dynamic inputs as earlier values are selected, including initially-disabled dependent inputs. If dynamic input loading fails, VS Code blocks the command and tells the user to run it from the Dashboard or CLI instead of prompting with stale metadata.

CLI execution works with supplied dynamic arguments:

aspire resource argument-commands dependent-arguments \
  --apphost playground/Stress/Stress.AppHost/Stress.AppHost.csproj \
  --non-interactive \
  -- --category=fruit --item=banana --quantity=2 --priority=express

Invalid dynamic state is reported as validation instead of execution failure:

Validating and executing command 'dependent-arguments' on resource 'argument-commands'...
❌ Failed to validate command arguments for command 'dependent-arguments' on resource 'argument-commands':
--item: Value is required.
--priority: Argument is disabled.

Validation:

  • dotnet test --project tests/Aspire.Cli.Tests/Aspire.Cli.Tests.csproj --no-launch-profile -- --filter-class "*.ResourceCommandTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"
  • dotnet test --project tests/Aspire.Cli.Tests/Aspire.Cli.Tests.csproj --no-launch-profile -- --filter-method "*.ExecuteGenericCommandAsync_WithValidationErrors_DisplaysArgumentErrors" --filter-method "*.ResourceCommand_DisplaysValidationErrorArgumentNamesAsCliOptions" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"
  • dotnet test --project tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj --no-launch-profile -- --filter-method "*.ExecuteCommandAsync_SubmittedDynamicArgumentStillDisabledAfterLoading_ReturnsDisabledValidationError" --filter-method "*.ExecuteCommandAsync_LoadsDependentChoiceOptionsBeforeBuiltInValidation" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"
  • npm run compile-tests, npm run compile -- --mode development, npm run lint -- --quiet from extension/
  • npm run unit-test -- --grep ResourceCommandArguments from extension/
  • Live Stress playground verification of argument-commands dependent-arguments

Fixes # (issue)

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

Add dynamic command input metadata to Hosting, the auxiliary backchannel, CLI JSON output, and VS Code resource command prompting. The CLI now exposes a hidden load-arguments path for tools to refresh dynamic input metadata and improves validation errors for dynamically disabled arguments.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 19, 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 -- 17249

Or

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

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@davidfowl davidfowl marked this pull request as ready for review May 22, 2026 03:15
Copilot AI review requested due to automatic review settings May 22, 2026 03:15
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

This PR adds end-to-end support for dynamic resource command inputs (disabled/required/options/defaults that change based on earlier answers) by flowing dynamic-loading metadata from Hosting → auxiliary backchannel → CLI JSON, and by adding a VS Code prompt path that can re-query the AppHost for refreshed argument metadata.

Changes:

  • Extend Hosting/backchannel contracts to carry dynamic-loading metadata and (optionally) return post-load argument inputs for validation-only requests.
  • Update CLI resource command parsing/validation to defer dynamic argument validation to Hosting, plus add a hidden --load-arguments mode that returns loaded argument inputs as JSON.
  • Update VS Code prompting to reload dynamic inputs during the argument-collection flow, including initial loads to surface initially-disabled dependent inputs.
Show a summary per file
File Description
tests/Aspire.Hosting.Tests/ResourceCommandServiceTests.cs Adds tests ensuring dynamic-disabled arguments aren’t rejected during argument creation and that disabled-after-load yields validation errors.
tests/Aspire.Hosting.Tests/Backchannel/AuxiliaryBackchannelRpcTargetTests.cs Adds coverage for returning loaded argument inputs and allowing arguments enabled by dynamic loading.
tests/Aspire.Cli.Tests/Commands/ResourceCommandTests.cs Adds CLI tests for --load-arguments JSON output and for deferring dynamic validation to Hosting during execution.
tests/Aspire.Cli.Tests/Commands/ResourceCommandHelperTests.cs Updates tests for new validation-focused CLI error messaging.
tests/Aspire.Cli.Tests/Backchannel/ResourceSnapshotMapperTests.cs Verifies dynamic-loading metadata is mapped into CLI JSON.
src/Shared/Model/Serialization/ResourceJson.cs Adds JSON contract for dynamic-loading metadata on command arguments.
src/Aspire.Hosting/Backchannel/BackchannelDataTypes.cs Extends backchannel request/options/response and snapshot argument types to support returning loaded inputs and dynamic-loading metadata.
src/Aspire.Hosting/Backchannel/AuxiliaryBackchannelRpcTarget.cs Implements ReturnArgumentInputs behavior for validation-only requests and factors argument mapping into a helper.
src/Aspire.Hosting/ApplicationModel/ResourceCommandService.cs Defers rejection of dynamically-disabled inputs until after dynamic loading and adds disabled-with-value validation.
src/Aspire.Cli/Commands/ResourceCommandHelper.cs Improves CLI messaging by distinguishing argument-validation failures from execution failures.
src/Aspire.Cli/Commands/ResourceCommand.cs Adds hidden --load-arguments execution path and relaxes CLI-side validation for dynamic inputs / load-arguments mode.
src/Aspire.Cli/Backchannel/ResourceSnapshotMapper.cs Maps dynamic-loading metadata into the CLI JSON model for consumers (VS Code).
src/Aspire.Cli/Backchannel/BackchannelJsonSerializerContext.cs Adds ResourceSnapshotCommandArgument[] to the CLI backchannel JSON source-gen context.
src/Aspire.Cli/Backchannel/AppHostAuxiliaryBackchannel.cs Forwards the ReturnArgumentInputs option to Hosting.
extension/src/views/ResourceCommandArguments.ts Adds dynamic-reload prompting flow, loader abstraction, and returns { args, containsSecret } instead of a raw argv array.
extension/src/views/AspireAppHostTreeProvider.ts Wires dynamic loading via aspire resource ... --load-arguments and blocks prompting if load fails.
extension/src/views/AppHostDataRepository.ts Extends the extension-side JSON contract to include dynamicLoading.
extension/src/test/resourceCommandArguments.test.ts Adds unit coverage for dynamic argument detection and loader/blocking behavior.
extension/src/loc/strings.ts Adds localized strings for dynamic input unsupported/failed/loading states.
extension/src/extension.ts Updates CodeLens resource action handling to use the new { args, containsSecret } result shape.
extension/package.nls.json Adds string resources for dynamic-input prompting states.
extension/loc/xlf/aspire-vscode.xlf Adds corresponding XLF entries for new localized strings.

Copilot's findings

  • Files reviewed: 22/22 changed files
  • Comments generated: 1

Comment thread extension/src/extension.ts
davidfowl and others added 2 commits May 22, 2026 20:51
Share the resource command argument loader between the tree view and CodeLens resource-action flow so dynamic command inputs can be loaded with the AppHost path from CodeLens.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Member

@mitchdenny mitchdenny left a comment

Choose a reason for hiding this comment

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

Few minor issues. but approving. Also filed an issue for a potential quality of life improvement for folks in the next release (non blocking).

Copy link
Copy Markdown
Member

@mitchdenny mitchdenny left a comment

Choose a reason for hiding this comment

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

(probe review — superseded; inline comments deleted)

Copy link
Copy Markdown
Member

@mitchdenny mitchdenny left a comment

Choose a reason for hiding this comment

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

(probe review — superseded; inline comments deleted)

Copy link
Copy Markdown
Member

@mitchdenny mitchdenny left a comment

Choose a reason for hiding this comment

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

(probe review — superseded; inline comments deleted)

Copy link
Copy Markdown
Member

@mitchdenny mitchdenny left a comment

Choose a reason for hiding this comment

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

Review of dynamic resource command inputs. 3 inline findings below.

Additional minor (no inline anchor — function is on an unchanged line): hasSecretResourceCommandArguments in extension/src/views/ResourceCommandArguments.ts is no longer called from extension.ts or AspireAppHostTreeProvider.ts after this PR — the new CollectedResourceCommandArguments.containsSecret (computed against the final, post-dynamic-load values) replaces it. The function is still exported and is now only referenced by tests. Worth deleting (with its test) if no production code consumes it; otherwise it's an attractive nuisance because it computes the answer from the stale snapshot and would give wrong results for dynamic inputs.

Comment thread extension/src/views/ResourceCommandArguments.ts Outdated
Comment thread extension/src/views/AspireAppHostTreeProvider.ts
Comment thread src/Aspire.Cli/Commands/ResourceCommand.cs Outdated
Harden VS Code dynamic input prompting against changing input sets, require an AppHost path for dynamic metadata loading, remove stale secret detection, and add a fallback diagnostic when loaded argument metadata is missing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@davidfowl
Copy link
Copy Markdown
Contributor Author

Addressed the additional review note from review 4352954787. hasSecretResourceCommandArguments is no longer used by production code and could be misleading for dynamic inputs, so I removed the export and its tests; callers now rely on CollectedResourceCommandArguments.containsSecret, which is computed from final submitted values after dynamic loading.

davidfowl and others added 2 commits May 24, 2026 14:24
Only report submitted values for disabled dynamic inputs when their load callback did not run because dependencies were incomplete. This preserves Browser Logs default profile values while keeping stale dependent submissions actionable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Cover the case where a dynamic input loads, remains disabled, and carries a default value so it does not report a disabled-argument validation error.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

Re-running the failed jobs in the CI workflow for this pull request because 1 job was identified as retry-safe transient failures in the CI run attempt.
GitHub was asked to rerun all failed jobs for that attempt, and the rerun is being tracked in the rerun attempt.
The job links below point to the failed attempt jobs that matched the retry-safe transient failure rules.

Make the VS Code dynamic argument loader parse the entire hidden CLI stdout as a single JSON payload and fail if any extra text is emitted, surfacing CLI output bugs instead of ignoring them.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@JamesNK
Copy link
Copy Markdown
Member

JamesNK commented May 25, 2026

dynamic-command-vscode

While waiting for the next input in VS there is no indication that something is happening. Is it possible to display the UI with the input disable + loading indicator?

@JamesNK
Copy link
Copy Markdown
Member

JamesNK commented May 25, 2026

The delay to load values is much slower than the dashboard UI:

dynamic-command-dashboard

@davidfowl
Copy link
Copy Markdown
Contributor Author

davidfowl commented May 25, 2026

Yea I gotta admit that is a terrible experience. I think the only way to fix it is to not use the terminal here. I'll see what can be done.

davidfowl and others added 2 commits May 25, 2026 08:03
Rename the dynamic argument loader dependency to emphasize extension-host CLI execution instead of visible terminal usage, and document that only final resource command invocation uses the VS Code terminal.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Display a busy QuickInput while dynamic resource command inputs reload between prompts, and use the clearer text 'Updating command inputs...' for the transient state.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@davidfowl davidfowl enabled auto-merge (squash) May 25, 2026 20:25
@davidfowl davidfowl merged commit 4c25958 into main May 25, 2026
616 of 619 checks passed
@microsoft-github-policy-service microsoft-github-policy-service Bot added this to the 13.4 milestone May 25, 2026
@github-actions
Copy link
Copy Markdown
Contributor

CLI E2E Tests failed — 95 passed, 1 failed, 5 unknown (commit 666640a)

Failed Tests

View all recordings
Status Test Recording
AddPackageInteractiveWhileAppHostRunningDetached ▶️ View recording
AddPackageWhileAppHostRunningDetached ▶️ View recording
AgentCommands_AllHelpOutputs_AreCorrect ▶️ View recording
AgentInitCommand_DefaultSelection_InstallsDefaultSkills ▶️ View recording
AgentInitCommand_MigratesDeprecatedConfig ▶️ View recording
AgentMcpListStructuredLogsFromStarterAppCore ▶️ View recording
AllPublishMethodsBuildDockerImages ▶️ View recording
AspireAddPackageVersionToDirectoryPackagesProps ▶️ View recording
AspireInitSingleFileAppHostRunsViaDotnetRunAppHost ▶️ View recording
AspireInitWithExistingAppHostDirRecreatesMissingNuGetConfigAndPreservesFiles ▶️ View recording
AspireInitWithSolutionFileGeneratesAppHostThatBuildsAgainstChannelHive ▶️ View recording
AspireStartUpdatesStaleTypeScriptAppHostPath ▶️ View recording
AspireUpdateRemovesAppHostPackageVersionFromDirectoryPackagesProps ▶️ View recording
AspireUpdateRemovesOrphanAppHostPackageVersionWhenSdkAlreadyCurrent ▶️ 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
CreateTypeScriptAppHostWithViteApp_UsesConfiguredToolchain ▶️ View recording
DashboardRunWithAgentMcpCore ▶️ View recording
DashboardRunWithOtelTracesReturnsNoTracesCore ▶️ View recording
DeployK8sBasicApiService ▶️ View recording
DeployK8sWithExternalHelmChart ▶️ View recording
DeployK8sWithGarnet ▶️ View recording
DeployK8sWithMongoDB ▶️ View recording
DeployK8sWithMySql ▶️ View recording
DeployK8sWithPostgres ▶️ View recording
DeployK8sWithRabbitMQ ▶️ View recording
DeployK8sWithRedis ▶️ View recording
DeployK8sWithSqlServer ▶️ View recording
DeployK8sWithValkey ▶️ View recording
DeployTypeScriptAppToKubernetes ▶️ View recording
DescribeCommandResolvesReplicaNames ▶️ View recording
DescribeCommandShowsRunningResources ▶️ View recording
DetachFormatJsonProducesValidJson ▶️ View recording
DetachFormatJsonProducesValidJsonWhenRestartingExistingInstance ▶️ View recording
DoListStepsShowsPipelineSteps ▶️ View recording
DocsCommand_RendersInteractiveMarkdownFromLocalSource ▶️ View recording
DoctorCommand_DetectsDeprecatedAgentConfig ▶️ View recording
DoctorCommand_TypeScriptAppHostReportsMissingConfiguredToolchain ▶️ View recording
DoctorCommand_WithSslCertDir_ShowsTrusted ▶️ View recording
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted ▶️ View recording
GeneratedAspireDevScript_StartsWatchMode_WithConfiguredToolchain ▶️ View recording
GlobalMigration_HandlesCommentsAndTrailingCommas ▶️ View recording
GlobalMigration_HandlesMalformedLegacyJson ▶️ View recording
GlobalMigration_PreservesAllValueTypes ▶️ View recording
GlobalMigration_SkipsWhenNewConfigExists ▶️ View recording
GlobalSettings_MigratedFromLegacyFormat ▶️ View recording
InitTypeScriptAppHost_AugmentsExistingViteRepoAtRoot ▶️ View recording
InteractiveCSharpInitCreatesExpectedFiles ▶️ View recording
InvalidAppHostPathWithComments_IsHealedOnRun ▶️ View recording
JavaScriptHostingApisRunFromTypeScriptAppHost ▶️ View recording
LatestCliCanStartStableChannelAppHost ▶️ View failure recording
LatestCliCanStartStableChannelTypeScriptAppHost ▶️ View recording
LegacySettingsMigration_AdjustsRelativeAppHostPath ▶️ View recording
LogLevelTrace_ProducesTraceEntriesInCliLogFile ▶️ View recording
LogsCommandShowsResourceLogs ▶️ View recording
OtelLogsReturnsStructuredLogsFromStarterApp ▶️ View recording
OtelLogsReturnsStructuredLogsFromStarterAppIsolated ▶️ View recording
PsCommandListsRunningAppHost ▶️ View recording
PsFormatJsonOutputsOnlyJsonToStdout ▶️ View recording
PublishJavaScriptPatternsGeneratesExpectedDockerComposeArtifacts ▶️ View recording
PublishWithConfigureEnvFileUpdatesEnvOutput ▶️ View recording
PublishWithDockerComposeServiceCallbackSucceeds ▶️ View recording
PublishWithoutOutputPathUsesAppHostDirectoryDefault ▶️ View recording
ResourceCommand_FailedExecution_DisplaysAppHostLogPathAndLogContainsEntries ▶️ View recording
ResourceCommand_FailsWhenInteractionServiceIsRequired ▶️ View recording
ResourceCommand_SetAndDeleteParameterUpdatesDescribeOutput ▶️ View recording
RestoreGeneratesSdkFiles ▶️ View recording
RestoreGeneratesSdkFiles_WithConfiguredToolchain ▶️ View recording
RestoreRefreshesGeneratedSdkAfterAddingIntegration ▶️ View recording
RestoreSupportsConfigOnlyHelperPackageAndCrossPackageTypes ▶️ View recording
RunFromParentDirectory_UsesExistingConfigNearAppHost ▶️ View recording
RunPublishFailureScenarioAsync ▶️ View recording
RunReportsSyntaxErrorsForDotNetAppHost ▶️ View recording
RunReportsSyntaxErrorsForTypeScriptAppHost ▶️ View recording
SecretCrudOnDotNetAppHost ▶️ View recording
SecretCrudOnTypeScriptAppHost ▶️ View recording
StagingChannel_ConfigureAndVerifySettings_ThenSwitchChannels ▶️ View recording
StartAndWaitForTypeScriptSqlServerAppHostWithNativeAssets ▶️ View recording
StartReportsSyntaxErrorsForDotNetAppHost ▶️ View recording
StartReportsSyntaxErrorsForTypeScriptAppHost ▶️ View recording
StopAllAppHostsFromAppHostDirectory ▶️ View recording
StopJavaPolyglotAppHostUsingApphostDirectory ▶️ View recording
StopNonInteractiveSingleAppHost ▶️ View recording
StopTypeScriptPolyglotAppHostUsingApphostDirectory ▶️ View recording
StopWithNoRunningAppHostExitsSuccessfully ▶️ View recording
UnAwaitedChainsCompileWithAutoResolvePromises ▶️ View recording
UpdateProjectChannelToStable_TypeScript_PicksUpStablePackages ▶️ View recording

📹 Recordings uploaded automatically from CI run #26418223128

@aspire-repo-bot
Copy link
Copy Markdown
Contributor

✅ No documentation update needed.

⚠️ Docs PR creation failed due to infrastructure issue.

The documentation changes were written and committed locally to branch docs/dynamic-resource-command-inputs in the microsoft/aspire.dev checkout, but the create_pull_request tool failed with "No commits found" and the PR could not be published. A report_incomplete signal has been filed.

Signals triggered (4): cli_command_file_changed, cli_option_added, pr_body_has_cli_flag_mention, pr_body_has_user_facing_section. Evidence: src/Aspire.Cli/Commands/ResourceCommand.cs added a hidden --load-arguments option; PR body includes a ### User-facing usage section describing VS Code dynamic input refresh behavior.

Prepared documentation change: A new ### Dynamic arguments subsection was prepared for src/frontend/src/content/docs/fundamentals/custom-resource-commands.mdx covering InputLoadOptions/DynamicLoading configuration, VS Code prompt refresh behavior, and CLI deferred validation error output.

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.

4 participants