Automate resource terminals with tape files

Цей контент ще не доступний вашою мовою.

Use aspire terminal tape play to send scripted input to an existing resource terminal, wait for expected output, and save text recordings. A tape is a plain-text .tape file containing commands such as Type, Enter, and Wait+Screen.

Playback joins a running WithTerminal() resource as a secondary peer. It doesn’t create a shell, take primary ownership, resize the terminal, or stop the resource when the tape completes or fails. It cannot target an AppHost-owned dock or interaction terminal, including a client opened by WithRepl(). See Dashboard terminals for the different terminal surfaces.

This example runs an interactive Bash shell in your own AppHost and doesn’t require a container runtime.

Use an existing AppHost or create one with aspire init. Replace its application code with the following complete example. For a file-based C# AppHost, retain the generated #:sdk and #:package directives above the code.

apphost.mts
import {
function createBuilder(): IDistributedApplicationBuilder

Creates a new distributed application builder

createBuilder
} from './.aspire/modules/aspire.mjs';
const
const builder: IDistributedApplicationBuilder
builder
= await
function createBuilder(): IDistributedApplicationBuilder

Creates a new distributed application builder

createBuilder
();
const
const shell: ExecutableResource
shell
= await
const builder: IDistributedApplicationBuilder
builder
.
IDistributedApplicationBuilder.addExecutable(name: string, command: string, workingDirectory: string, args: string[]): ExecutableResource

Adds an executable resource to the application model.

addExecutable
('shell', '/bin/bash', '.', [
'--noprofile',
'--norc',
'-i',
]);
await
const shell: ExecutableResource
shell
.
ExecutableResource.withTerminal(): ExecutableResource

Adds an interactive terminal session to a resource using the default terminal options.

withTerminal
();
await
const builder: IDistributedApplicationBuilder
builder
.
IDistributedApplicationBuilder.build(): DistributedApplication

Builds the distributed application

build
().
DistributedApplication.run(cancellationToken?: cancellationToken): void

Runs the distributed application

run
();

This creates an interactive Bash resource without loading user startup scripts. WithTerminal() exposes its existing terminal for playback; you don’t need a terminal library or an adapter.

From the directory containing your app’s aspire.config.json, start the AppHost:

Start your app
aspire run

Leave that terminal running. In a second terminal in the same directory, confirm that shell is alive:

Check the shell resource
aspire terminal ps

Start at an idle prompt with no partially typed input. To inspect it, run aspire terminal attach shell, then detach with Ctrl+B D before playback. If you have multiple running AppHosts, add --apphost with your AppHost’s file path to select the correct one.

Save this complete tape as shell-smoke.tape in the same directory:

shell-smoke.tape
Set TypingSpeed 0
Set WaitTimeout 10s
Wait+Line /[$#>]$/
Type "printf 'ASPIRE_TAPE_%s\n' ready"
Enter
Wait+Screen /ASPIRE_TAPE_ready/
Wait+Line /[$#>]$/

Run it against the existing shell resource:

Play the shell smoke test
aspire terminal tape play shell --tape-file shell-smoke.tape

The final screen printed to stdout contains ASPIRE_TAPE_ready. The expected marker deliberately doesn’t appear contiguously in the typed command, so the shell’s input echo cannot satisfy the wait before the command produces its output.

Use a fresh marker or clear old output when adapting a tape for repeated checks. A wait can match text already present on the screen; it doesn’t assert that the text was produced by the preceding command. Exit code zero means the tape completed, not that every program invoked inside the shell succeeded.

To record the shell smoke test, create record-shell.tape beside shell-smoke.tape. Restart the shell resource before repeating the check so previous output cannot satisfy its waits.

record-shell.tape
Output shell-recording.txt
Source shell-smoke.tape

Output selects the recording file. Source includes and executes the commands from another .tape file at that point, so this example records the smoke test without duplicating its commands. You can also put Output shell-recording.txt at the top of shell-smoke.tape and run that tape directly.

Run the tape:

Record the shell smoke test
aspire terminal tape play shell --tape-file record-shell.tape

Open shell-recording.txt beside the root tape. Output records plain-text screen snapshots after visible executed commands, separated by horizontal lines. It is a sequence of screens, not just the final screen, a stream of raw console output, or a video. Identical snapshots can appear when consecutive commands don’t change the screen.

Only .txt and .ascii text destinations are supported. Output directories must already exist, and existing files aren’t overwritten. Move the previous recording or choose a new destination before running the tape again. There is no CLI overwrite option.

Paths in both Source and Output resolve on the CLI machine, relative to the root tape’s directory, not the resource’s working directory. This also applies to Source directives inside nested tapes. Included files must use the exact .tape extension; their own Output directives are ignored. Put the recording destination in the root tape.

To save only the final screen instead, use shell redirection:

Save the final screen and diagnostics separately
aspire terminal tape play shell --tape-file shell-smoke.tape > final-screen.txt 2> tape-diagnostics.txt

Shell redirection has its own overwrite behavior, separate from Output protection. Discovery messages, warnings, and failures go to stderr; the final plain-text screen goes to stdout.

Automate a resource terminal from AppHost code

Section titled “Automate a resource terminal from AppHost code”

For workflows that combine terminal input with independent application checks, use TerminalService directly rather than a tape. Automate terminals from your AppHost walks through a sample that drives a REST client and verifies each operation against its API. That sample uses programmatic automation, not .tape files.

Aspire uses a subset of VHS tape syntax, based on VHS v0.11.0. See the Hex1b tape syntax and compatibility reference for parser details. That reference also describes standalone terminal and library features that aren’t exposed by Aspire.

Use these commands when targeting an existing Aspire resource terminal:

CommandExamplePurpose
TypeType "help"Send text without implicitly pressing Enter.
Keys and chordsEnter, Tab, Ctrl+CSend supported terminal keys; the application decides their effect.
SleepSleep 100msPause for a fixed duration. Prefer output waits for synchronization.
Wait, Wait+LineWait+Line /repl#[0-9]+>$/Match a regular expression on the current terminal line.
Wait+ScreenWait+Screen@5s /Ready/Match the visible screen, with an optional per-wait timeout.
Timing and wait settingsSet TypingSpeed 50ms, Set WaitTimeout 10sConfigure typing speed and the default wait timeout.
Set WaitPatternSet WaitPattern />$/Set the pattern used by waits without an explicit pattern.
SourceSource repl-check.tapeInclude another tape.
OutputOutput recording.txtRecord per-command text screens.
Hide, ShowHide followed later by ShowExclude or resume recording while commands continue executing.

Put wait settings at the beginning of the tape, alongside Output, before input commands. TypingSpeed can change later, but late WaitPattern and WaitTimeout changes are ignored with diagnostics. Use comments beginning with # and quote text operands. Durations can use units such as ms and s.

Regular expressions follow a supported subset of Go regex syntax. A pattern that parses in VHS isn’t necessarily executable here: unsupported flags, escapes, and character classes are rejected during preflight.

The following effects are rejected before any tape input is sent:

  • Video, GIF, PNG, and screenshot output.
  • Clipboard actions and VHS viewport scrolling.
  • Fonts, presentation settings, and pixel-based Set Width or Set Height.
  • Shell selection, environment declarations (Env), and executable requirements (Require) that would configure a newly launched shell.

Aspire doesn’t launch or resize a process for playback. Configure the resource’s command, environment, and initial terminal dimensions in the AppHost instead.