dotnet / dotnet/msbuild

Provide a task API for host-coordinated, verbatim console output

Open
#15,026 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
C#
Stars
5.5k
Forks
1.5k
Avg merge
1d 8h
Merged PRs (30d)
141

Description

### Summary

Add a supported task API for emitting verbatim console-output lines through MSBuild, rather than requiring tasks to call `Console.WriteLine` directly or disguise functional output as a diagnostic message.

The motivating case is CI service commands such as `##vso[task.setvariable ...]`, artifact attachments, and forwarding `##[...]` formatting commands from a child process. These are output contracts with the build host, not ordinary diagnostics.

### Background and Motivation

A public-source audit found actual MSBuild tasks using both direct console output and MSBuild messages for this purpose:

| Public implementation | Pattern |
|---|---|
| [Nerdbank.GitVersioning: SetCloudBuildVariables](https://github.com/dotnet/Nerdbank.GitVersioning/blob/eb0273e80366c5325cbb300307a9df108f071dcd/src/Nerdbank.GitVersioning.Tasks/SetCloudBuildVariables.cs#L14-L57) | A `Task` passes `Console.Out` to its [Azure provider](https://github.com/dotnet/Nerdbank.GitVersioning/blob/eb0273e80366c5325cbb300307a9df108f071dcd/src/NerdBank.GitVersioning/CloudBuildServices/VisualStudioTeamServices.cs#L26-L46), which sets pipeline variables and the build number. |
| [Workleap OpenAPI: CiReportRenderer](https://github.com/workleap/wl-openapi-msbuild/blob/508ef80813760ea486be23edf22c0eb2f1379448/src/Workleap.OpenApi.MSBuild/Spectral/CiReportRenderer.cs#L3-L11) | Called from the `ValidateOpenApiTask` execution path; uses `Console.WriteLine` to attach a Spectral report with `##vso[task.addattachment ...]`. |
| [Microsoft.Testing.Platform: InvokeTestingPlatformTask](https://github.com/microsoft/testfx/blob/48de8d5e72dc0f609d6fc4217c090402f102dd12/src/Platform/Microsoft.Testing.Platform.MSBuild/Tasks/InvokeTestingPlatformTask.Execution.cs#L28-L59) | A `ToolTask` explicitly forwards child-output lines starting with `##vso[` or `##[` to `Console.Out` when output capture is disabled. [This change](https://github.com/microsoft/testfx/commit/7acaf58469721bec310279d2de3614c748c7f1a7) deliberately replaced the ordinary logging path for these lines. |
| [Arcade: PublishBuildToMaestro](https://github.com/dotnet/arcade/blob/1574a0ce35761b7ce5e783074cc2f9567d278396/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishBuildToMaestro.cs#L188-L206) | Emits pipeline variables and build tags through `Log.LogMessage(MessageImportance.High, ...)`. |
| [GitVersion: MsBuildAdapter](https://github.com/GitTools/GitVersion/blob/a74b087e106bf8ea02addceb8c07ebcdcf35f28d/src/GitVersion.MsBuild/Helpers/MsBuildAdapter.cs#L6-L14) | Its MSBuild integration routes build-server command output through normal message logging rather than its CLI console adapter. |

Direct console writes bypass MSBuild's coordinated output and can interfere with terminal rendering or host integration. Conversely, `LogMessage` is subject to verbosity filtering and logger presentation; recording a command in a binlog does not mean the CI agent received it on stdout.

Not every protocol requires column-zero output: the Azure agent's `##vso` parser can find a command within a line. Nevertheless, filtering, formatting, and host behavior make ordinary diagnostic logging an insufficiently explicit contract for verbatim output.

### Proposed Feature

Provide a task-accessible API, conceptually:

```csharp
// Illustrative API name, not a proposed final signature.
Log.WriteConsoleLine(command);
```

The task declares its intent, and MSBuild owns delivery:

- Preserve the line's content without diagnostic prefixes, indentation, colors, or wrapping.
- Route output from worker nodes and out-of-process task hosts to the host-owned output channel.
- Serialize complete lines with other console activity and coordinate with Terminal Logger rendering; preserve per-task emission order.
- Treat this separately from message importance. Define behavior for quiet mode, `/noconsolelogger`, redirected output, IDE hosts, and hosts without a console; do not silently downgrade to a potentially filtered message.
- Define capability discovery and unsupported-host behavior so tasks can make an explicit compatibility decision.
- Define whether/how these events are recorded and replayed, avoiding accidental re-execution of CI commands during log replay and unnecessary persistence of sensitive payloads.

Keep the API host/protocol-neutral: MSBuild need not interpret Azure Pipelines commands. Ordinary diagnostics should continue to use `LogMessage`, `LogWarning`, and `LogError`. Arbitrary cursor control and progress UI are outside this proposal.

### Alternative Designs

A dedicated output event routed to a host-owned sink could provide the same contract, potentially exposed through an `IBuildEngine` capability and a `TaskLoggingHelper` convenience API. A CI-specific logger is another option, but requires additional deployment/configuration and still needs a reliable task-to-host transport.

Using High-importance messages is an existing workaround, not guaranteed verbatim delivery. Continuing direct `Console` access leaves tasks responsible for process topology and terminal coordination.

Related: #13182 proposes flagging task console access; this API would provide a migration path for legitimate non-diagnostic output. #6944 concerns progress/cursor control and is related but broader in terminal interaction.

### Current workaround and telemetry

[PR #14859 - Forward console output from primary multithreaded task host](https://github.com/dotnet/msbuild/pull/14859) provides a compatibility workaround for tasks that write directly to `Console.Out` / `Console.Error` and are moved out of process for `-mt` compatibility. It forwards raw output from the primary task-host sidecar (logical node 2); it does not cover every task host or replace the proposed API with a general delivery guarantee.

We are also adding telemetry to understand how widespread console output from task hosts is. This will help quantify the compatibility impact and inform the scope and prioritization of a supported task-output API.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by reviewing the proposed IBuilEngine capability and TaskLoggingHelper convenience API, then read PR #14859 for the current console-forwarding workaround. Compare the requirements with related issues #13182 and #6944. Done means an agreed host-neutral API design with defined delivery, compatibility, host behavior, and replay semantics.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
build-system, developer-experience
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.