Skip to content

Validate TerminalOptions columns and rows are positive - #18992

Merged
Karol Zadora-Przylecki (karolz-ms) merged 3 commits into
mainfrom
mitchdenny-validate-terminal-dimensions
Aug 4, 2026
Merged

Validate TerminalOptions columns and rows are positive#18992
Karol Zadora-Przylecki (karolz-ms) merged 3 commits into
mainfrom
mitchdenny-validate-terminal-dimensions

Conversation

@mitchdenny

Copy link
Copy Markdown
Member

Description

WithTerminal() lets you attach an interactive terminal to a resource and optionally set the initial PTY size via TerminalOptions.Columns / TerminalOptions.Rows. Previously those properties accepted zero or negative values during AppHost model construction, but Aspire.TerminalHost rejects any dimension below 1 (its --columns / --rows validators). The mismatch turned a bad value into a hidden terminal-host startup failure that could block the parent resource, with no clear indication of what went wrong.

Now an invalid width/height fails fast at the WithTerminal() call site with a clear ArgumentOutOfRangeException, instead of surfacing later as an opaque host failure.

User-facing usage

Valid usage is unchanged:

builder.AddExecutable("myapp", "myapp", ".")
       .WithTerminal(options =>
       {
           options.Columns = 200;
           options.Rows = 50;
       });

Invalid dimensions now throw immediately at configuration time:

builder.AddExecutable("myapp", "myapp", ".")
       .WithTerminal(options => options.Columns = 0); // throws ArgumentOutOfRangeException

The boundary value 1 is still accepted (matches TerminalHost's >= 1 rule). The polyglot withTerminal dispatcher uses the built-in defaults (120×30), which remain valid, so polyglot AppHosts are unaffected.

Implementation

TerminalOptions.Columns and Rows validate in their setters with ArgumentOutOfRangeException.ThrowIfNegativeOrZero, matching the existing ProcessCommandOptions.MaxOutputLineCount convention. No generated api/*.cs files were modified (this is an experimental API and did not add new surface). Regression tests were added in WithTerminalTests covering zero/negative columns and rows and the accepted boundary value; they fail before the fix and pass after.

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

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

🚀 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 -- 18992

Or

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

@github-actions github-actions Bot added the area-app-model Issues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication label Aug 4, 2026
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds fail-fast validation for terminal dimensions, aligning AppHost configuration with TerminalHost requirements.

Changes:

  • Rejects zero or negative terminal columns and rows.
  • Documents constraints and adds boundary/regression tests.
Show a summary per file
File Description
src/Aspire.Hosting/ApplicationModel/TerminalAnnotation.cs Validates terminal dimensions in property setters.
tests/Aspire.Hosting.Tests/WithTerminalTests.cs Tests invalid dimensions and boundary value 1.

Review details

  • Files reviewed: 2/2 changed files
  • Comments generated: 0
  • Review effort level: Balanced

TerminalOptions.Columns and Rows previously accepted zero or negative
values during AppHost model construction, but Aspire.TerminalHost rejects
any dimension below 1 (its --columns / --rows validators). That mismatch
turned an invalid value into a hidden terminal-host startup failure that
could block the parent resource instead of a clear error at the call site.

Validate both dimensions in the property setters using
ArgumentOutOfRangeException.ThrowIfNegativeOrZero, matching the existing
ProcessCommandOptions.MaxOutputLineCount convention. This surfaces the
error at the WithTerminal() call site. The polyglot dispatcher uses the
built-in defaults (120x30), which remain valid.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: dc0094eb-cce9-48f8-ad4d-304d8d87f48a
Keep the public TerminalOptions documentation focused on the positive-value contract after terminal implementation types moved internal.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: dc0094eb-cce9-48f8-ad4d-304d8d87f48a
@mitchdenny
Mitch Denny (mitchdenny) force-pushed the mitchdenny-validate-terminal-dimensions branch from 22785a2 to 498439e Compare August 4, 2026 11:53
Copilot AI review requested due to automatic review settings August 4, 2026 11:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review details

  • Files reviewed: 2/2 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@github-actions

This comment has been minimized.

Copilot AI review requested due to automatic review settings August 4, 2026 12:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review details

  • Files reviewed: 2/2 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Tests selector (audit mode)

The full test matrix and all jobs still run in audit mode. The tests and jobs below are what selective CI would run under enforcement.

48 / 100 test projects · 4 jobs, from 2 changed files.

Selected test projects (48 / 100)

Aspire.EndToEnd.Tests, Aspire.Hosting.Analyzers.Tests, Aspire.Hosting.Azure.Kubernetes.Tests, Aspire.Hosting.Azure.Kusto.Tests, Aspire.Hosting.Azure.Tests, Aspire.Hosting.Blazor.Tests, Aspire.Hosting.Browsers.Tests, Aspire.Hosting.CodeGeneration.Go.Tests, Aspire.Hosting.CodeGeneration.Java.Tests, Aspire.Hosting.CodeGeneration.Python.Tests, Aspire.Hosting.CodeGeneration.Rust.Tests, Aspire.Hosting.CodeGeneration.TypeScript.Tests, Aspire.Hosting.Containers.Tests, Aspire.Hosting.DevTunnels.Tests, Aspire.Hosting.Docker.Tests, Aspire.Hosting.Dotnet.Tests, Aspire.Hosting.DotnetTool.Tests, Aspire.Hosting.EntityFrameworkCore.Tests, Aspire.Hosting.Foundry.Tests, Aspire.Hosting.Garnet.Tests, Aspire.Hosting.GitHub.Models.Tests, Aspire.Hosting.Go.Tests, Aspire.Hosting.JavaScript.Tests, Aspire.Hosting.Kafka.Tests, Aspire.Hosting.Keycloak.Tests, Aspire.Hosting.Kubernetes.Tests, Aspire.Hosting.Maui.Tests, Aspire.Hosting.Milvus.Tests, Aspire.Hosting.MongoDB.Tests, Aspire.Hosting.MySql.Tests, Aspire.Hosting.Nats.Tests, Aspire.Hosting.OpenAI.Tests, Aspire.Hosting.Oracle.Tests, Aspire.Hosting.Orleans.Tests, Aspire.Hosting.PostgreSQL.Tests, Aspire.Hosting.Python.Tests, Aspire.Hosting.Qdrant.Tests, Aspire.Hosting.RabbitMQ.Tests, Aspire.Hosting.Radius.Tests, Aspire.Hosting.Redis.Tests, Aspire.Hosting.RemoteHost.Tests, Aspire.Hosting.Seq.Tests, Aspire.Hosting.SqlServer.Tests, Aspire.Hosting.Testing.Tests, Aspire.Hosting.Tests, Aspire.Hosting.Valkey.Tests, Aspire.Hosting.Yarp.Tests, Aspire.Playground.Tests

Selected jobs (4)

deployment-e2e, extension-e2e, polyglot, typescript-api-compat


How these were chosen — grouped by what changed

⚠️ 43 of the 48 selected test projects come from a single change — src/Aspire.Hosting/ApplicationModel/TerminalAnnotation.cs.

🔧 src/Aspire.Hosting/ApplicationModel/TerminalAnnotation.cs (changed source)
43 via the project graph

show 43

Aspire.Hosting.Analyzers.Tests (2 hops), Aspire.Hosting.Azure.Kubernetes.Tests (2 hops), Aspire.Hosting.Azure.Kusto.Tests (2 hops), Aspire.Hosting.Azure.Tests, Aspire.Hosting.Browsers.Tests (2 hops), Aspire.Hosting.CodeGeneration.Go.Tests, Aspire.Hosting.CodeGeneration.Java.Tests, Aspire.Hosting.CodeGeneration.Python.Tests, Aspire.Hosting.CodeGeneration.Rust.Tests, Aspire.Hosting.CodeGeneration.TypeScript.Tests, Aspire.Hosting.Containers.Tests (2 hops), Aspire.Hosting.DevTunnels.Tests (2 hops), Aspire.Hosting.Docker.Tests (2 hops), Aspire.Hosting.DotnetTool.Tests (2 hops), Aspire.Hosting.EntityFrameworkCore.Tests (2 hops), Aspire.Hosting.Foundry.Tests (2 hops), Aspire.Hosting.Garnet.Tests (2 hops), Aspire.Hosting.GitHub.Models.Tests (2 hops), Aspire.Hosting.Go.Tests (2 hops), Aspire.Hosting.JavaScript.Tests (2 hops), Aspire.Hosting.Kafka.Tests (2 hops), Aspire.Hosting.Keycloak.Tests (2 hops), Aspire.Hosting.Kubernetes.Tests (2 hops), Aspire.Hosting.Maui.Tests, Aspire.Hosting.Milvus.Tests (2 hops), Aspire.Hosting.MongoDB.Tests (2 hops), Aspire.Hosting.MySql.Tests (2 hops), Aspire.Hosting.Nats.Tests (2 hops), Aspire.Hosting.OpenAI.Tests (2 hops), Aspire.Hosting.Oracle.Tests (2 hops), Aspire.Hosting.Orleans.Tests (2 hops), Aspire.Hosting.PostgreSQL.Tests (2 hops), Aspire.Hosting.Python.Tests (2 hops), Aspire.Hosting.Qdrant.Tests (2 hops), Aspire.Hosting.RabbitMQ.Tests (2 hops), Aspire.Hosting.Redis.Tests (2 hops), Aspire.Hosting.RemoteHost.Tests, Aspire.Hosting.Seq.Tests (2 hops), Aspire.Hosting.SqlServer.Tests (2 hops), Aspire.Hosting.Testing.Tests (2 hops), Aspire.Hosting.Valkey.Tests (2 hops), Aspire.Hosting.Yarp.Tests (2 hops), Aspire.Playground.Tests

🧪 tests/Aspire.Hosting.Tests/WithTerminalTests.cs (changed test)
1 directly: Aspire.Hosting.Tests
3 via the project graph: Aspire.Hosting.Blazor.Tests, Aspire.Hosting.Dotnet.Tests, Aspire.Hosting.Radius.Tests

📦 affected project Aspire.Hosting
1 test: Aspire.EndToEnd.Tests

Job reasons

Job Triggered by
deployment-e2e affected project Aspire.Hosting.Azure
extension-e2e src/Aspire.Hosting/ApplicationModel/TerminalAnnotation.cs
• affected project Aspire.Hosting
polyglot affected project Aspire.Hosting.Go
typescript-api-compat affected project Aspire.Hosting

Selection computed for commit e620004.

@mitchdenny

Copy link
Copy Markdown
Member Author

PR Testing Report

PR Information

Artifact Version Verification

  • Expected Commit: e620004
  • Installed Version: 13.5.0-pr.18992.ge6200047
  • Status: ✅ Verified

Changes Analyzed

Files Changed

  • src/Aspire.Hosting/ApplicationModel/TerminalAnnotation.cs - Validates positive terminal columns and rows.
  • tests/Aspire.Hosting.Tests/WithTerminalTests.cs - Adds invalid-input and boundary regression coverage.

Change Categories

  • CLI changes
  • Hosting changes
  • Dashboard changes
  • Template changes
  • Client/component changes
  • VS Code extension changes
  • CI infrastructure changes
  • Test changes

Test Scenarios Executed

Scenario 1: PR artifact and package verification

Objective: Verify the tested CLI and template packages were produced from the latest PR head.
Coverage Type: Artifact verification
Status: ✅ Passed

Steps:

  1. Installed PR Validate TerminalOptions columns and rows are positive #18992 with the repository dogfood installer into an isolated temp directory.
  2. Used the installed binary at dogfood/pr-18992/bin/aspire.
  3. Compared its reported version with the PR head SHA.
  4. Generated fresh C# AppHosts from the PR NuGet hive.

Evidence:

  • version.txt
  • install.log
  • aspire-empty-help.txt
  • new-TerminalValid.log
  • new-InvalidColumns.log
  • new-InvalidRows.log

Observations:

  • The installed version contains short commit e6200047, matching the PR head.
  • The first download attempt timed out while fetching a GitHub Actions artifact; a fresh retry completed successfully.

Scenario 2: Default and minimum terminal dimensions

Objective: Verify default WithTerminal() configuration and the minimum valid dimensions construct an AppHost model successfully.
Coverage Type: Happy path / Boundary
Status: ✅ Passed

Steps:

  1. Created a fresh C# aspire-empty AppHost from the PR hive.
  2. Added one executable with default WithTerminal() configuration.
  3. Added another executable with Columns = 1 and Rows = 1.
  4. Built the distributed application model without starting external resources.

Evidence:

  • scenarios/TerminalValid/apphost.cs
  • valid-model.log

Observations:

  • The AppHost compiled and model construction completed with exit status 0.
  • Default behavior remained valid and the lower boundary value 1 was accepted.

Scenario 3: Zero terminal columns

Objective: Verify zero columns fail during AppHost model construction rather than later in TerminalHost startup.
Coverage Type: Unhappy path
Status: ✅ Passed

Steps:

  1. Created a fresh C# AppHost from the PR hive.
  2. Configured WithTerminal(options => options.Columns = 0).
  3. Ran the AppHost and captured its exit status and startup output.

Evidence:

  • scenarios/InvalidColumns/apphost.cs
  • invalid-columns.log

Expected Unhappy-Path Outcome: A clear ArgumentOutOfRangeException from TerminalOptions.Columns and a nonzero process exit.

Observations:

  • Exit status was 134.
  • The exception was thrown from TerminalOptions.set_Columns with parameter name value and actual value 0.

Scenario 4: Negative terminal rows

Objective: Verify negative rows fail during AppHost model construction rather than later in TerminalHost startup.
Coverage Type: Unhappy path
Status: ✅ Passed

Steps:

  1. Created a fresh C# AppHost from the PR hive.
  2. Configured WithTerminal(options => options.Rows = -1).
  3. Ran the AppHost and captured its exit status and startup output.

Evidence:

  • scenarios/InvalidRows/apphost.cs
  • invalid-rows.log

Expected Unhappy-Path Outcome: A clear ArgumentOutOfRangeException from TerminalOptions.Rows and a nonzero process exit.

Observations:

  • Exit status was 134.
  • The exception was thrown from TerminalOptions.set_Rows with parameter name value and actual value -1.

Scenario 5: Focused source regression tests

Objective: Verify invalid values, defaults, the accepted lower boundary, and the internal terminal implementation shape.
Coverage Type: Unit/regression
Status: ✅ Passed

Steps:

  1. Ran the focused Aspire.Hosting.Tests methods for terminal dimensions, defaults, and internal types.
  2. Excluded quarantined and outerloop tests.

Evidence:

  • source-tests.log

Observations:

  • 7 tests passed, 0 failed, and 0 skipped.

GitHub CI Status

  • Passed: 337
  • Skipped: 3
  • Pending: 0
  • Failed: 0
  • Evidence: checks.json

Summary

Scenario Status Notes
Artifact version verification ✅ Passed Installed version matches PR head
Defaults and minimum dimensions ✅ Passed Defaults and 1 × 1 accepted
Zero columns ✅ Passed Early ArgumentOutOfRangeException
Negative rows ✅ Passed Early ArgumentOutOfRangeException
Focused source tests ✅ Passed 7/7 passed
GitHub CI ✅ Passed 337 passed, 3 skipped

Overall Result

✅ PR VERIFIED

The PR artifact matches the latest commit, valid terminal dimensions preserve existing behavior, invalid dimensions fail at AppHost model construction with clear exceptions, focused regression tests pass, and all GitHub checks have completed without failures.

@karolz-ms
Karol Zadora-Przylecki (karolz-ms) merged commit c3615c8 into main Aug 4, 2026
342 checks passed
@karolz-ms
Karol Zadora-Przylecki (karolz-ms) deleted the mitchdenny-validate-terminal-dimensions branch August 4, 2026 23:05
@microsoft-github-policy-service microsoft-github-policy-service Bot added this to the 13.5 milestone Aug 4, 2026
@aspire-repo-bot

Copy link
Copy Markdown
Contributor

⚠️ Documentation was required for this change, but a docs PR could not be drafted automatically.

Documentation was required for this PR (pr_body_has_user_facing_section, pr_body_has_cli_flag_mention). The intended change — updating src/frontend/src/content/docs/app-host/with-terminal.mdx to document that TerminalOptions.Columns and Rows must be greater than zero and throw ArgumentOutOfRangeException for zero/negative values — was committed to branch docs/withterminal-options-validation against release/13.5, but the create_pull_request safe-output tool failed twice with Git command failed with status 1 (shallow-clone patch generation error). A human will need to open the docs PR manually from that branch.

See the workflow run for details: https://github.com/microsoft/aspire/actions/runs/30958805880

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

Labels

area-app-model Issues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants