diff --git a/.github/workflows/markdownlint.yml b/.github/workflows/markdownlint.yml index 17ae996c08..81caab81c1 100644 --- a/.github/workflows/markdownlint.yml +++ b/.github/workflows/markdownlint.yml @@ -24,6 +24,6 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Run markdownlint-cli2 - uses: DavidAnson/markdownlint-cli2-action@v24 + uses: DavidAnson/markdownlint-cli2-action@v18 with: globs: '**/*.md' diff --git a/azure-pipelines.yml b/azure-pipelines.yml index cb5cbcbba8..f8693caa49 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -151,7 +151,7 @@ stages: # Seed from every trusted main merge. PR merge refs are read-only consumers, so the cache must be updated as # soon as main changes to avoid forcing the entire 204-node graph to miss. - pwsh: | - & ./eng/common/msbuild.ps1 -ci -warnAsError:$false ./TestFx.slnx ` + & ./eng/common/msbuild.ps1 -ci -prepareMachine -warnAsError:$false ./TestFx.slnx ` /restore ` /graph ` /m ` @@ -160,7 +160,10 @@ stages: /t:Build ` /v:minimal ` /p:Configuration=$(_BuildConfig) ` + /p:FastAcceptanceTest=true ` + /p:Publish=false ` "/p:RepoRoot=$(Build.SourcesDirectory)\" ` + /p:Test=false ` /p:MSBuildCachePackageEnabled=true ` /p:MSBuildCacheEnabled=true ` /p:MSBuildCacheRemoteCacheIsReadOnly=false ` @@ -290,7 +293,7 @@ stages: "-NoLogo", "-NoProfile", "-File", "./eng/common/msbuild.ps1", - "-ci", + "-prepareMachine", "-warnAsError:`$false", "./TestFx.slnx", "/restore", @@ -301,7 +304,11 @@ stages: "/t:Build", "/v:minimal", "/p:Configuration=$(_BuildConfig)", + "/p:ContinuousIntegrationBuild=true", + "/p:FastAcceptanceTest=true", + "/p:Publish=false", "/p:RepoRoot=$(Build.SourcesDirectory)\", + "/p:Test=false", "/p:MSBuildCachePackageEnabled=true", "/p:MSBuildCacheEnabled=true", "/p:MSBuildCacheRemoteCacheIsReadOnly=true", @@ -321,16 +328,68 @@ stages: if ($exitCode -ne 0) { Write-Host "##vso[task.logissue type=warning]MSBuildCache canary failed with exit code $exitCode; continuing with the regular Arcade build." + Write-Host "##vso[task.setvariable variable=MSBuildCacheBuildSucceeded]false" + } + else { + Write-Host "##vso[task.setvariable variable=MSBuildCacheBuildSucceeded]true" } exit 0 displayName: Build solution graph with MSBuildCache condition: and(succeeded(), or(and(eq(variables['Build.Reason'], 'PullRequest'), ne(variables['System.PullRequest.IsFork'], 'True')), and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.SourceBranch'], 'refs/heads/main')))) + continueOnError: true env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) - # Keep the cache canary independent from the authoritative Arcade build. Publish only the project-level - # diagnostics; CacheClient.log and the OAuth-bearing canary environment stay outside published artifacts. + # CIBuild.cmd also restores the custom toolset/workloads, signs the build outputs, and produces the packages + # consumed by the acceptance tests. Run those phases without the Build phase when the cache supplied the + # compiled outputs. Keep this non-blocking so any failure switches the regular Arcade build back on. + - pwsh: | + Write-Host "##vso[task.setvariable variable=MSBuildCacheBuildSucceeded]false" + + $pwsh = Join-Path $PSHOME "pwsh.exe" + $arguments = @( + "-NoLogo", + "-NoProfile", + "-File", "./eng/common/build.ps1", + "-ci", + "-disablePipelineSetResult", + "-configuration", "$(_BuildConfig)", + "-restore", + "-sign", + "-pack", + "-prepareMachine", + "/p:ContinuousIntegrationBuild=true", + "/p:FastAcceptanceTest=true", + "/p:MSBuildCachePackageEnabled=true", + "/p:MSBuildCacheEnabled=false" + ) + + $previousPSNativeCommandUseErrorActionPreference = $PSNativeCommandUseErrorActionPreference + try { + $PSNativeCommandUseErrorActionPreference = $false + & $pwsh @arguments + $exitCode = $LASTEXITCODE + } + finally { + $PSNativeCommandUseErrorActionPreference = $previousPSNativeCommandUseErrorActionPreference + } + + if ($exitCode -ne 0) { + Write-Host "##vso[task.logissue type=warning]Preparing cached outputs failed with exit code $exitCode; continuing with the regular Arcade build." + } + else { + Write-Host "##vso[task.setvariable variable=MSBuildCacheBuildSucceeded]true" + } + + exit 0 + displayName: Prepare and pack cached build outputs + condition: and(succeeded(), eq(variables['MSBuildCacheBuildSucceeded'], 'true')) + continueOnError: true + + # Publish only the project-level diagnostics; CacheClient.log and the OAuth-bearing cache environment stay + # outside published artifacts. A successful cache build supplies the outputs consumed by the test steps. + # On failure, remove its partial outputs before running the regular Arcade build as a fallback. - pwsh: | $cacheLogSource = "$(Agent.TempDirectory)\MSBuildCache" $cacheLogDestination = "$(Build.SourcesDirectory)\artifacts\log\$(_BuildConfig)\MSBuildCache" @@ -347,14 +406,32 @@ stages: Write-Host "##vso[task.logissue type=warning]Failed to preserve MSBuildCache diagnostics. $($_.Exception.Message)" } finally { - $cleanupErrors = @() + if ("$(MSBuildCacheBuildSucceeded)" -ne "true") { + $cleanupErrors = @() + + $artifactsDirectory = "$(Build.SourcesDirectory)\artifacts" + if (Test-Path $artifactsDirectory) { + Get-ChildItem $artifactsDirectory -Force | + Where-Object Name -NotIn @("log", "msbuild-cache", "toolset") | + ForEach-Object { + $path = $_.FullName + try { + Remove-Item $path -Recurse -Force -ErrorAction Stop + } + catch { + $cleanupErrors += "${path}: $($_.Exception.Message)" + } + } + } - $artifactsDirectory = "$(Build.SourcesDirectory)\artifacts" - if (Test-Path $artifactsDirectory) { - Get-ChildItem $artifactsDirectory -Force | - Where-Object Name -NotIn @("log", "msbuild-cache", "toolset") | + @( + $cacheLogSource, + "$(Build.SourcesDirectory)\src\Package\MSTest.Sdk\Sdk\Sdk.props", + "$(Build.SourcesDirectory)\src\Package\MSTest.Sdk\Sdk\Runner\Runner.targets" + ) | + Where-Object { Test-Path $_ } | ForEach-Object { - $path = $_.FullName + $path = $_ try { Remove-Item $path -Recurse -Force -ErrorAction Stop } @@ -362,29 +439,13 @@ stages: $cleanupErrors += "${path}: $($_.Exception.Message)" } } - } - @( - $cacheLogSource, - "$(Build.SourcesDirectory)\src\Package\MSTest.Sdk\Sdk\Sdk.props", - "$(Build.SourcesDirectory)\src\Package\MSTest.Sdk\Sdk\Runner\Runner.targets" - ) | - Where-Object { Test-Path $_ } | - ForEach-Object { - $path = $_ - try { - Remove-Item $path -Recurse -Force -ErrorAction Stop - } - catch { - $cleanupErrors += "${path}: $($_.Exception.Message)" - } + if ($cleanupErrors) { + throw "Failed to clean MSBuildCache outputs before the fallback build:`n$($cleanupErrors -join "`n")" } - - if ($cleanupErrors) { - throw "Failed to clean MSBuildCache canary outputs:`n$($cleanupErrors -join "`n")" } } - displayName: Preserve cache diagnostics and clean canary outputs + displayName: Preserve cache diagnostics and clean failed outputs condition: and(always(), or(and(eq(variables['Build.Reason'], 'PullRequest'), ne(variables['System.PullRequest.IsFork'], 'True')), and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.SourceBranch'], 'refs/heads/main')))) - task: PowerShell@2 @@ -407,6 +468,7 @@ stages: /p:MSBuildCacheEnabled=false name: Build displayName: Build + condition: and(succeeded(), ne(variables['MSBuildCacheBuildSucceeded'], 'true')) - ${{ if eq(parameters.SkipTests, False) }}: diff --git a/docs/dev-guide.md b/docs/dev-guide.md index 664bf032a8..98135793e1 100644 --- a/docs/dev-guide.md +++ b/docs/dev-guide.md @@ -86,16 +86,16 @@ For more information about all the different options available, supply the argum ### MSBuildCache -The Windows PR pipeline experimentally runs [MSBuildCache](https://github.com/microsoft/MSBuildCache) before the regular Arcade build. The cache-aware build uses Arcade's `eng/common/msbuild.ps1` launcher to invoke the solution directly because Arcade's outer `Build.proj` discovers projects dynamically and cannot expose the repository's static project graph to the cache plugin. PR builds consume the immutable Azure Pipeline cache read-only, and fork PRs skip this step because they do not receive the required token scope. +The Windows PR pipeline experimentally runs [MSBuildCache](https://github.com/microsoft/MSBuildCache). The cache-aware build uses Arcade's `eng/common/msbuild.ps1` launcher to invoke the solution directly because Arcade's outer `Build.proj` discovers projects dynamically and cannot expose the repository's static project graph to the cache plugin. PR builds consume the immutable Azure Pipeline cache read-only, and fork PRs skip this step because they do not receive the required token scope. When the cache build succeeds, the pipeline runs the remaining Arcade restore, sign, and pack phases without rebuilding, then uses the cached outputs for the test steps. When the cache build or preparation phases fail, the pipeline preserves the cache diagnostics, removes partial outputs, and runs the regular Arcade build as a fallback. Every merge to `main` that touches product build inputs runs a dedicated, batched seed stage for both Debug and Release. This is required: the cache fingerprints project inputs, so entries become stale whenever shared build inputs such as `global.json`, `eng/Versions.props`, or Arcade change. This stage is the only remote cache publisher; PR, manual, and nightly canary builds consume the cache read-only so they cannot race to publish immutable entries. Debug and Release use separate cache universes because configuration-independent projects can otherwise race while the two configurations publish in parallel. -The cache's detached-process exclusions use fully rooted paths (for example, `$(WinDir)\**`). A drive-relative pattern such as `\Windows\**` does not match the absolute file-access paths reported by MSBuild and causes otherwise successful cache builds to fail after compilation. Cache builds also pass `-warnAsError:$false` to Arcade's launcher because MSBuildCache intentionally warns about allowlisted detached telemetry accesses. The regular Arcade build still treats warnings as errors and remains authoritative. +The cache's detached-process exclusions use fully rooted paths (for example, `$(WinDir)\**`). A drive-relative pattern such as `\Windows\**` does not match the absolute file-access paths reported by MSBuild and causes otherwise successful cache builds to fail after compilation. Cache builds also pass `-warnAsError:$false` to Arcade's launcher because MSBuildCache intentionally warns about allowlisted detached telemetry accesses. The fallback Arcade build still treats warnings as errors. MSBuildCache currently requires Windows, the Visual Studio version pinned in `global.json`, Git on `PATH`, and a clean repository. It does not support incremental developer builds. To validate the local cache from a clean checkout, run: ```powershell -eng\common\msbuild.ps1 -msbuildEngine vs -warnAsError:$false TestFx.slnx /restore /graph /m /reportfileaccesses /t:Build /p:Configuration=Release /p:MSBuildCachePackageEnabled=true /p:MSBuildCacheEnabled=true /p:MSBuildCacheLogDirectory=artifacts\log\Release\MSBuildCache\Plugin /bl:artifacts\log\Release\MSBuildCache\Build.binlog +eng\common\msbuild.ps1 -msbuildEngine vs -warnAsError:$false TestFx.slnx /restore /graph /m /reportfileaccesses /t:Build /p:Configuration=Release /p:FastAcceptanceTest=true /p:Publish=false /p:Test=false /p:MSBuildCachePackageEnabled=true /p:MSBuildCacheEnabled=true /p:MSBuildCacheLogDirectory=artifacts\log\Release\MSBuildCache\Plugin /bl:artifacts\log\Release\MSBuildCache\Build.binlog ``` Use `MSTest.slnf` or `Microsoft.Testing.Platform.slnf` in place of `TestFx.slnx` to validate a filtered graph. Delete `artifacts\msbuild-cache` to clear the local content cache. Change the prefix of `MSBuildCacheCacheUniverse` in `Directory.Build.props`, or pass `/p:MSBuildCacheCacheUniverse=` for one invocation, to invalidate cache entries. The default universe includes `$(Configuration)`, so use the same configuration when populating and consuming a cache. diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalReporterContract.props b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalReporterContract.props index fe2ee6e858..64c94b611c 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalReporterContract.props +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalReporterContract.props @@ -15,7 +15,8 @@ SCOPE The set is the reporter + its rendering/state types + the small platform abstractions it depends on (IConsole/IStopwatch/IColor/System* + ILogger/LogLevel/LoggingExtensions/NopLogger + - RoslynString/ApplicationStateGuard/StackTraceHelper/TargetFrameworkParser/TestRunSummaryHelper/ZeroTestsPolicy). + RoslynString/ApplicationStateGuard/StackTraceHelper/SlowTestThresholdState/TargetFrameworkParser/ + TestRunSummaryHelper/ZeroTestsPolicy). It deliberately EXCLUDES TerminalTestReporterCommandLineOptionsProvider.cs (MTP command-line specific - the SDK has its own CLI) and TerminalResources.cs (the hand-written [Embedded] accessor used by in-repo IVT-linked consumers; standalone consumers compile the resx instead - see below). @@ -23,9 +24,9 @@ The reporter + rendering + state types are [Microsoft.CodeAnalysis.Embedded], so they are NOT exported by Microsoft.Testing.Platform; a consumer that also references the platform compiles its own (single-source) copy and never sees a conflict. The small internal platform abstractions in this set (IConsole/IStopwatch/ - SystemConsole/SystemStopwatch + RoslynString/ApplicationStateGuard/StackTraceHelper/TargetFrameworkParser/ - TestRunSummaryHelper/ZeroTestsPolicy) are 'internal' and not exported either (no InternalsVisibleTo to an - external consumer), so they also never conflict. + SystemConsole/SystemStopwatch + RoslynString/ApplicationStateGuard/StackTraceHelper/SlowTestThresholdState/ + TargetFrameworkParser/TestRunSummaryHelper/ZeroTestsPolicy) are 'internal' and not exported either (no + InternalsVisibleTo to an external consumer), so they also never conflict. The exceptions are the public logging abstractions (ILogger, LogLevel, and LoggingExtensions) plus IColor and SystemConsoleColor, which are shipped *public* API of Microsoft.Testing.Platform. A consumer that ALSO references @@ -80,6 +81,7 @@ true +