Skip to content

Conversation

@YuliiaKovalova
Copy link
Member

Document the process for enabling binary logging in CI/CD pipelines using environment variables, including supported arguments, argument processing order, and implementation flow.

Fixes #12804

Document the process for enabling binary logging in CI/CD pipelines using environment variables, including supported arguments, argument processing order, and implementation flow.

## Warning Messages

All issues are logged as **warnings**, never errors. Builds must not fail due to environment variable processing.
Copy link
Member

Choose a reason for hiding this comment

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

Would these be Messages logged through the configured Loggers, or raw CLI output? If the latter, we should strongly consider writing these to stderr - in general all 'verbose', non-Logger-provided output should go to that channel.

Copy link
Member

Choose a reason for hiding this comment

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

I agree in principal but I'm not sure that this specific scenario is the best place to start using stderr--I suspect many build scripts don't capture it well.

Copy link
Member

Choose a reason for hiding this comment

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

We've started breaking that seal in the dotnet CLI in 10.0.1xx, and busted it wide open in 10.0.200 already. It's a bigger unknown for msbuild.exe for sure though.

@@ -0,0 +1,113 @@
## Purpose

Enable binary logging in CI/CD pipelines when `-noAutoResponse` blocks response files, without modifying build scripts or project files.
Copy link
Member

Choose a reason for hiding this comment

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

Think that this is a problem even without -noAutoResponse. Using an RSP file would necessitate writing artifacts to disk or modifying an existing RSP file in place. The goal I was thinking of is how can you enable collection without touching the artifacts on disk at all.

Copy link
Member

Choose a reason for hiding this comment

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

without touching the artifacts on disk at all

Strong agree.

- `-bl` / `/bl` / `-binarylogger` / `/binarylogger` (with optional parameters)
- `-check` / `/check` (with optional parameters - requires code modification to support `deferred` mode)

**All other switches are blocked** as security risks. These two switches are diagnostic/monitoring tools that don't affect build behavior or enable code execution.
Copy link
Member

Choose a reason for hiding this comment

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

What are the security risks that this poses? My mental model is that once you're able to inject environment variables into the build then you can change a lot of operations.

Not disputing this angle, just trying to get my head into the right place here.

Copy link
Member

Choose a reason for hiding this comment

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

environment variables are assumed to be trusted in our threat model, you can specify loading of dlls or proj/targets/props files that will be executed as a part of the build

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

IMO this restriction is not a security measure, it's more about diagnosability. It is already very annoying to discover that stray environment variables have acted as build inputs (Platform is an old favorite env var that horks builds). The reason I prefer a restricted logging-only env var is to avoid being able to do so in a new, even harder-to-understand way.

Copy link
Member

Choose a reason for hiding this comment

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

We currently log the entire command line in the binlog, but we do not 'classify' where arguments of that command line came from. Same is true for various trait flag. It might be an interesting future enhancement to add this provenance information to the binlog.

1. **MSBuild.rsp** (next to MSBuild.exe) - skipped if `-noAutoResponse` present
2. **Directory.Build.rsp** (next to project) - skipped if `-noAutoResponse` present
3. **MSBUILD_LOGGING_ARGS** - always processed, regardless of `-noAutoResponse`
4. **Command-line arguments** - highest precedence
Copy link
Member

Choose a reason for hiding this comment

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

Given that you limited the set of arguments that could occur in MSBUILD_LOGGING_ARGS is the precedence between 3 and 4 relevant? Both -bl and -check add information (new location for binary logger, new check to run) not replace so seems like this shouldn't have impact.

This is another case where I'm just trying to get my head in the right place. Essentially is this significant for 3 and 4 or not?

Copy link
Member

Choose a reason for hiding this comment

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

I agree--precedence for this env var is the highest possible level, but because it's only allowed to add log outputs it can't override other argument-passing mechanisms so precedence isn't super interesting.


**Implementation Note**: MSBuild's existing argument parser handles merging logic. The environment variable arguments are prepended to the command line before parsing, ensuring natural precedence.

## Implementation Flow
Copy link
Member

Choose a reason for hiding this comment

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

Where all would this end up impacting the builds? Certainly expect that dotnet build and msbuild will process these args but what else? Guessing that say the builds done in a Roslyn msbuild workspace would not emit logs (or would they)?

Copy link
Member

Choose a reason for hiding this comment

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

Correct--only things that go through MSBuild's Main(), so dotnet build/dotnet msbuild/MSBuild.exe and not MSBuildWorkspace or VS or MyFancyCustomBuildDriverViaAPI.exe.

The only thing that makes me super worried there is devenv.exe /build which is definitely used in official-build scenarios in the wild (though is rare inside Microsoft).

We do have the ability to inject a binlog into even API-driven builds (MSBUILDDEBUGENGINE, which we use for debugging VS builds). We could use that instead of new functionality. I think this spec should discuss and address that.

@YuliiaKovalova YuliiaKovalova self-assigned this Dec 3, 2025
@YuliiaKovalova YuliiaKovalova marked this pull request as ready for review December 5, 2025 07:58
Copilot AI review requested due to automatic review settings December 5, 2025 07:58
Copilot finished reviewing on behalf of YuliiaKovalova December 5, 2025 08:02
Copy link
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 documentation for a proposed feature that would enable binary logging in CI/CD pipelines through the MSBUILD_LOGGING_ARGS environment variable. The feature is designed to work even when -noAutoResponse blocks response files, providing an alternative mechanism for enabling diagnostic logging.

Key changes:

  • Documents the MSBUILD_LOGGING_ARGS environment variable and its purpose
  • Specifies supported arguments (-bl and -check) and argument processing order
  • Describes parameter merging behavior and implementation flow

@@ -0,0 +1,113 @@
## Purpose
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

The file should have a .md extension to be consistent with other specification documents in this directory. All other spec files in documentation/specs/ use the .md extension (e.g., low-priority-switch.md, event-source.md, test-target.md).

Copilot uses AI. Check for mistakes.
Comment on lines +69 to +91
## Build Check (-check) Handling

### Deferred Analysis Mode

`-check:deferred` enables binlog replay analysis without build-time overhead:

- **During build**: Flag recorded in binlog, BuildCheck NOT activated
- **During replay**: Binlog reader activates BuildCheck for analysis

**Rationale**: BuildCheck analysis can be expensive. Environment variable is for diagnostics that can be analyzed later, allowing teams to record data without performance impact.

### Example Workflow

```bash
# 1. Configure environment
set MSBUILD_LOGGING_ARGS=-bl:build.binlog -check:deferred

# 2. Run build (normal speed, no BuildCheck overhead)
msbuild solution.sln

# 3. Later: Replay binlog (BuildCheck analyzes recorded events)
msbuild build.binlog
```
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

The documentation describes a deferred mode for the -check argument that is not documented in the existing BuildCheck specification files. This feature should either:

  1. Have corresponding implementation or specification documentation in documentation/specs/BuildCheck/
  2. Be marked as a future feature or proposal
  3. Include a reference to where this feature is documented

Without supporting documentation, readers cannot verify this functionality exists or understand its complete behavior.

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,113 @@
## Purpose
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

Missing document title. The file should start with a top-level heading (e.g., # Enable Binary Log Collection via Environment Variable) consistent with other specification documents like low-priority-switch.md, event-source.md, and test-target.md.

Copilot uses AI. Check for mistakes.
3. Process response files (if no `-noAutoResponse`)
4. Read MSBUILD_LOGGING_ARGS environment variable
5. Validate and filter arguments
6. Prepend valid arguments to command line
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

[nitpick] The implementation flow description states "Prepend valid arguments to command line" (line 57), but this is an implementation detail that cannot be verified without seeing the actual code. If this feature is not yet implemented, consider using language like "will prepend" or "should prepend" to indicate this is a design specification rather than describing existing behavior.

Suggested change
6. Prepend valid arguments to command line
6. Will prepend valid arguments to command line

Copilot uses AI. Check for mistakes.
## Supported Arguments

- `-bl` / `/bl` / `-binarylogger` / `/binarylogger` (with optional parameters)
- `-check` / `/check` (with optional parameters - requires code modification to support `deferred` mode)
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

The parenthetical comment "(requires code modification to support deferred mode)" is unclear. If code modification is required for this feature to work, this should be explained in more detail. Specifically:

  1. What code needs to be modified?
  2. Is this referring to MSBuild code or user code?
  3. If this is a future feature, it should be clearly marked as such

This unclear statement may confuse readers about the current state of the feature.

Suggested change
- `-check` / `/check` (with optional parameters - requires code modification to support `deferred` mode)
- `-check` / `/check` (with optional parameters; Note: `deferred` mode is not currently supported—enabling this feature requires changes to the MSBuild codebase and is planned for a future release. See section "Build Check (-check) Handling" below.)

Copilot uses AI. Check for mistakes.
JanProvaznik added a commit that referenced this pull request Dec 5, 2025
### Context

Docs-only PRs fail the Code Coverage CI job because tests are skipped
(no coverage artifacts generated), but the job unconditionally attempts
to download and publish those artifacts.

### Changes Made

Modified `.vsts-dotnet-ci.yml` CodeCoverage job to respect the
`onlyDocChanged` flag:

- Added dependency on `IfOnlyDocumentionChanged` job to access the flag
- Added `onlyDocChanged` variable to job variables
- Conditioned all artifact downloads on `eq(variables.onlyDocChanged,
0)`
- Conditioned coverage processing and publishing on
`eq(variables.onlyDocChanged, 0)`

This aligns with how other jobs (BootstrapMSBuildOnFullFrameworkWindows,
CoreBootstrappedOnLinux, etc.) already handle docs-only PRs.

### Testing

Pipeline configuration change - will be validated on next PR run. When
`onlyDocChanged = 1`, all coverage tasks skip; when `onlyDocChanged =
0`, coverage runs normally.

### Notes

Pre-existing typos in job/variable names (`Documention`, `Varibale`)
intentionally preserved for consistency across the pipeline.

<!-- START COPILOT CODING AGENT SUFFIX -->



<details>

<summary>Original prompt</summary>

> 
> ----
> 
> *This section details on the original issue you should resolve*
> 
> <issue_title>docs only PRs fail code coverage CI check</issue_title>
> <issue_description>#12805
> #12857
> 
> ```
> Code Coverage failed
> [3 errors / 0
warnings](https://dev.azure.com/dnceng-public/cbb18261-c48f-4abb-8651-8cdcb5474649/_build/results?buildId=1217301)
> 
> Annotations
> [Check failure on line 9 in Build
log](https://github.com/dotnet/msbuild/pull/12857/files#annotation_42398343450)
> 
> @azure-pipelines
> azure-pipelines
> / msbuild-pr (Code Coverage)
> Build log #L9
> 
> Publishing build artifacts failed with an error: Not found
PathtoPublish: D:\a\1\s\artifacts\CoverageResults\merged.coverage
> [Check failure on line 11 in Build
log](https://github.com/dotnet/msbuild/pull/12857/files#annotation_42398343487)
> 
> @azure-pipelines
> azure-pipelines
> / msbuild-pr (Code Coverage)
> Build log #L11
> 
> Artifact LinuxCoreCoverage was not found for build 1217301.
> [Check failure on line 9 in Build
log](https://github.com/dotnet/msbuild/pull/12857/files#annotation_42398343497)
> 
> @azure-pipelines
> azure-pipelines
> / msbuild-pr (Code Coverage)
> Build log #L9
> 
> Publishing build artifacts failed with an error: Not found
PathtoPublish: D:\a\1\s\artifacts\CoverageResults\merged.cobertura.xml
> ```
> 
> This is probably because testing is skipped on docs only PRs, in that
case code coverage should also be skipped</issue_description>
> 
> <agent_instructions>fix this issue, note that the CI pipeline
definition is called .vsts-dotnet-ci.yml</agent_instructions>
> 
> ## Comments on the Issue (you are @copilot in this section)
> 
> <comments>
> </comments>
> 


</details>

- Fixes #12878

<!-- START COPILOT CODING AGENT TIPS -->
---

✨ Let Copilot coding agent [set things up for
you](https://github.com/dotnet/msbuild/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.

---------

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: JanProvaznik <[email protected]>
@@ -0,0 +1,113 @@
## Purpose

Enable binary logging in CI/CD pipelines when `-noAutoResponse` blocks response files, without modifying build scripts or project files.
Copy link
Member

Choose a reason for hiding this comment

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

without touching the artifacts on disk at all

Strong agree.

- `-bl` / `/bl` / `-binarylogger` / `/binarylogger` (with optional parameters)
- `-check` / `/check` (with optional parameters - requires code modification to support `deferred` mode)

**All other switches are blocked** as security risks. These two switches are diagnostic/monitoring tools that don't affect build behavior or enable code execution.
Copy link
Member

Choose a reason for hiding this comment

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

IMO this restriction is not a security measure, it's more about diagnosability. It is already very annoying to discover that stray environment variables have acted as build inputs (Platform is an old favorite env var that horks builds). The reason I prefer a restricted logging-only env var is to avoid being able to do so in a new, even harder-to-understand way.

1. **MSBuild.rsp** (next to MSBuild.exe) - skipped if `-noAutoResponse` present
2. **Directory.Build.rsp** (next to project) - skipped if `-noAutoResponse` present
3. **MSBUILD_LOGGING_ARGS** - always processed, regardless of `-noAutoResponse`
4. **Command-line arguments** - highest precedence
Copy link
Member

Choose a reason for hiding this comment

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

I agree--precedence for this env var is the highest possible level, but because it's only allowed to add log outputs it can't override other argument-passing mechanisms so precedence isn't super interesting.

3. **MSBUILD_LOGGING_ARGS** - always processed, regardless of `-noAutoResponse`
4. **Command-line arguments** - highest precedence

The environment variable is processed **after** checking for `-noAutoResponse`, making it a reliable fallback when response files are disabled.
Copy link
Member

Choose a reason for hiding this comment

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

I don't see how this matters, since it's additive-only.

Comment on lines +29 to +32
**Binary Logger (-bl)**
- Multiple `-bl` arguments are **allowed and cumulative**
- Each creates a separate binlog file
- Example: `MSBUILD_LOGGING_ARGS=-bl:env.binlog` + command line `-bl:cmd.binlog` → both files created
Copy link
Member

Choose a reason for hiding this comment

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

This is already handled by the existing CLI logic, right? Or will be after #12706?


```bash
# Environment provides base logging
set MSBUILD_LOGGING_ARGS=-bl:base.binlog -check:deferred
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
set MSBUILD_LOGGING_ARGS=-bl:base.binlog -check:deferred
set MSBUILD_LOGGING_ARGS=-bl:base{}.binlog -check:deferred


## Supported Arguments

- `-bl` / `/bl` / `-binarylogger` / `/binarylogger` (with optional parameters)
Copy link
Member

Choose a reason for hiding this comment

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

Should we be even more specific than this? I think for the use case we're thinking of the only bl arguments that make sense have a path and a {} placeholder (to allow multiple build invocations without overwriting logs).


## Warning Messages

All issues are logged as **warnings**, never errors. Builds must not fail due to environment variable processing.
Copy link
Member

Choose a reason for hiding this comment

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

Note that new warnings can easily be errors in a customer build. I think our position is that "if you opt into new functionality by setting this environment variable, you are also opting into those warnings", right?


- **Informational**: "Using arguments from MSBUILD_LOGGING_ARGS environment variable: {0}"
- **Rejected Argument**: "MSBUILD_LOGGING_ARGS: Ignoring unsupported argument '{0}'. Only -bl and -check arguments are allowed."
- **Processing Error**: "Error processing MSBUILD_LOGGING_ARGS environment variable: {0}"
Copy link
Member

Choose a reason for hiding this comment

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

In this last error case, what should MSBuild do concretely? What's the difference between "ignoring an argument" and "error"?


### Deferred Analysis Mode

`-check:deferred` enables binlog replay analysis without build-time overhead:
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
`-check:deferred` enables binlog replay analysis without build-time overhead:
`-check:deferred` enables binlog replay analysis with reduced build-time overhead:

Right? We still need to track the additional data that checks will need on replay?

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.

Add Environment Variable for Binary Log Collection in MSBuild

6 participants