diff --git a/src/frontend/src/content/docs/fundamentals/custom-resource-commands.mdx b/src/frontend/src/content/docs/fundamentals/custom-resource-commands.mdx index 6539d4679..76c3abfa8 100644 --- a/src/frontend/src/content/docs/fundamentals/custom-resource-commands.mdx +++ b/src/frontend/src/content/docs/fundamentals/custom-resource-commands.mdx @@ -1243,10 +1243,14 @@ When the command runs in the dashboard or CLI, `dotnet --version` (C#) or `node ### Dynamic process command -When the command arguments depend on runtime context — for example, a dataset name supplied by the user through the dashboard's argument dialog — use the callback overload (C# only) to build the `ProcessCommandSpec` dynamically: +When the command arguments depend on runtime context — for example, a dataset name supplied by the user through the dashboard's argument dialog — build the process specification from the command execution context: + + + ```csharp title="AppHost.cs" #pragma warning disable ASPIREPROCESSCOMMAND001 +#pragma warning disable ASPIREINTERACTION001 using Aspire.Hosting.ApplicationModel; @@ -1268,16 +1272,73 @@ builder.AddRedis("cache") ], EnvironmentVariables = { ["ConnectionStrings__db"] = "Host=localhost;Database=db" }, }, - options: new ProcessCommandOptions { MaxOutputLineCount = 20 }); + options: new ProcessCommandOptions + { + MaxOutputLineCount = 20, + Arguments = + [ + new InteractionInput { Name = "dataset", Label = "Dataset", InputType = InputType.Text, Required = true }, + ], + }); builder.Build().Run(); ``` -The `context` parameter is an `ExecuteCommandContext`, so you can read user-supplied arguments via `context.Arguments.GetString(name)`, resolve services, and check the cancellation token. + + + +```typescript title="apphost.ts" +import { + createBuilder, + type ExecuteCommandContext, + InputType, +} from './.modules/aspire.js'; + +const builder = await createBuilder(); + +const cache = await builder.addRedis('cache'); + +await cache.withProcessCommandFactory( + 'seed-data', + 'Seed data', + async (context: ExecuteCommandContext) => { + const args = await context.arguments(); + const dataset = await args.requiredValue('dataset'); + + return { + executablePath: 'node', + arguments: ['./scripts/seed-data.js', '--dataset', dataset], + environmentVariables: { + NODE_ENV: 'development', + }, + }; + }, + { + commandOptions: { + arguments: [ + { + name: 'dataset', + label: 'Dataset', + inputType: InputType.Text, + required: true, + }, + ], + }, + maxOutputLineCount: 20, + } +); + +await builder.build().run(); +``` + + + + +In C#, the callback overload receives an `ExecuteCommandContext`. In TypeScript, `withProcessCommandFactory` receives the same execution context and returns the process specification. The dashboard renders the configured arguments as a prompt dialog before starting the process, and the entered values are available through the command context. ### `ProcessCommandSpec` properties -The following configuration options are available for the process to run. In C#, these map to `ProcessCommandSpec` properties. In TypeScript, they are provided as fields in the options object passed to `withProcessCommand`. +The following configuration options are available for the process to run. In C#, these map to `ProcessCommandSpec` properties. In TypeScript, provide them as fields in the options object passed to `withProcessCommand`, or return them from the `withProcessCommandFactory` callback. - **Executable path** — the path to the process to launch. Short names (no directory separator) are resolved from the AppHost's `PATH`. - **Arguments** — a list of arguments passed to the process. Each entry is treated as a separate argument, so no shell quoting or escaping is needed. @@ -1289,7 +1350,7 @@ The following configuration options are available for the process to run. In C#, ### `ProcessCommandOptions` -The following options control how `WithProcessCommand` handles the process result. In C#, they are set on `ProcessCommandOptions`. In TypeScript, they are fields in the same options object as the process configuration above. +The following options control how `WithProcessCommand` handles the process result. In C#, set them on `ProcessCommandOptions`. In TypeScript, provide them in the same options object as the process configuration for `withProcessCommand`, or in the fourth argument to `withProcessCommandFactory`. - **Max output line count** — the maximum number of combined stdout/stderr lines captured and returned as the command result. Defaults to 50. Lines beyond this limit are silently discarded (oldest lines first). - **Display immediately** — when `true` (the default), the captured output is automatically shown in the dashboard as soon as the command finishes. @@ -1303,45 +1364,3 @@ The following options control how `WithProcessCommand` handles the process resul 2. Otherwise, the AppHost process's `PATH` is searched for the named executable. On Windows, `PATHEXT` extensions are also tried. This means you can reference tools such as `dotnet`, `node`, or `docker` by short name as long as they are on the AppHost's PATH when the app starts. - -### Combining with command arguments - -`WithProcessCommand` uses `WithCommand` internally. In C# dynamic process commands, you can combine it with the [Command arguments](#command-arguments) feature to prompt the user for input before the process runs: - -```csharp title="AppHost.cs" -#pragma warning disable ASPIREPROCESSCOMMAND001 -#pragma warning disable ASPIREINTERACTION001 - -using Aspire.Hosting.ApplicationModel; - -var builder = DistributedApplication.CreateBuilder(args); - -builder.AddPostgres("postgres") - .WithProcessCommand( - name: "run-migration", - displayName: "Run migration", - createProcessSpec: context => new ProcessCommandSpec("dotnet") - { - Arguments = - [ - "run", - "--project", - "tools/Migrations", - "--", - "--target", - context.Arguments.GetString("target") ?? "latest", - ], - }, - options: new ProcessCommandOptions - { - MaxOutputLineCount = 50, - Arguments = - [ - new InteractionInput { Name = "target", Label = "Target migration", InputType = InputType.Text }, - ], - }); - -builder.Build().Run(); -``` - -The dashboard renders the `Arguments` as a prompt dialog before starting the process, and the entered values are accessible via `context.Arguments`.