From b79f84c6c6adc309f106340f26bf81a304f184c0 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 22 Apr 2026 13:05:37 +0200 Subject: [PATCH 1/3] [devops] Add a timeout to the 'Publish to Artifact Services Drop' step. This step has taken to hanging recently, and it'll hang until the job's timeout of 3 hours is hit, at which point it'll mark the entire job as failed. So add a timeout just for this step, so that it can timeout by itself, and then the job can finish successfully. --- tools/devops/automation/templates/mac/build.yml | 1 + tools/devops/automation/templates/tests/run-tests.yml | 1 + tools/devops/automation/templates/windows/build.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/tools/devops/automation/templates/mac/build.yml b/tools/devops/automation/templates/mac/build.yml index fd4bcfacc0c4..a6c20308996f 100644 --- a/tools/devops/automation/templates/mac/build.yml +++ b/tools/devops/automation/templates/mac/build.yml @@ -219,6 +219,7 @@ steps: sourcePath: '$(BUILD_REPOSITORY_TITLE)/jenkins-results' detailedLog: true usePat: true + timeoutInMinutes: 30 continueOnError: true condition: succeededOrFailed() diff --git a/tools/devops/automation/templates/tests/run-tests.yml b/tools/devops/automation/templates/tests/run-tests.yml index 0e925c2505b3..55f5d8d5a7be 100644 --- a/tools/devops/automation/templates/tests/run-tests.yml +++ b/tools/devops/automation/templates/tests/run-tests.yml @@ -191,6 +191,7 @@ steps: sourcePath: '$(BUILD_REPOSITORY_TITLE)/jenkins-results' detailedLog: true usePat: true + timeoutInMinutes: 30 continueOnError: true condition: succeededOrFailed() diff --git a/tools/devops/automation/templates/windows/build.yml b/tools/devops/automation/templates/windows/build.yml index a3ee0e1ed478..7145baec4ee5 100644 --- a/tools/devops/automation/templates/windows/build.yml +++ b/tools/devops/automation/templates/windows/build.yml @@ -353,6 +353,7 @@ steps: sourcePath: '$(BUILD_REPOSITORY_TITLE)/jenkins-results' detailedLog: true usePat: true + timeoutInMinutes: 30 continueOnError: true condition: succeededOrFailed() From a3741591ae71b8457e0cc8c35f07aaddc3689fe7 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 22 Apr 2026 20:03:37 +0200 Subject: [PATCH 2/3] [devops] Show "(Publish failed)" instead of broken VSDrops link when html report publish fails. When the "Publish to Artifact Services Drop" step times out or fails, the VSDrops link in the GitHub comment would point to a non-existent page. Now detect the failure by setting an output variable when the step does not succeed, and show "(Publish failed)" in plain text instead of a broken link. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tools/devops/automation/scripts/TestResults.psm1 | 14 ++++++++++++-- tools/devops/automation/templates/mac/build.yml | 8 ++++++++ .../automation/templates/tests/run-tests.yml | 8 ++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/tools/devops/automation/scripts/TestResults.psm1 b/tools/devops/automation/scripts/TestResults.psm1 index ef5b0eb06457..aab88e9952f3 100644 --- a/tools/devops/automation/scripts/TestResults.psm1 +++ b/tools/devops/automation/scripts/TestResults.psm1 @@ -61,6 +61,7 @@ class TestResult { [string] $TestStage [string] $DisplayName [bool] $IsMacTest + [bool] $VSDropsPublishFailed hidden [int] $Passed hidden [int] $Failed hidden [string[]] $NotTestSummaryLabels = @() @@ -339,9 +340,13 @@ class ParallelTestsResults { } [string] GetDownloadLinks($testResult) { - $dropsIndex = "$($this.VSDropsIndex)/$($testResult.TestStage)$($testResult.Title)-$($testResult.Attempt)/;/tests/vsdrops_index.html" $artifactUrl = "$Env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI$Env:SYSTEM_TEAMPROJECT/_apis/build/builds/$Env:BUILD_BUILDID/artifacts?artifactName=HtmlReport-$($testResult.TestStage)$($testResult.Title)-$($testResult.Attempt)&api-version=6.0&`$format=zip" - $downloadInfo = "[Html Report (VSDrops)]($dropsIndex) [Download]($artifactUrl)" + if ($testResult.VSDropsPublishFailed) { + $downloadInfo = "(Publish failed) [Download]($artifactUrl)" + } else { + $dropsIndex = "$($this.VSDropsIndex)/$($testResult.TestStage)$($testResult.Title)-$($testResult.Attempt)/;/tests/vsdrops_index.html" + $downloadInfo = "[Html Report (VSDrops)]($dropsIndex) [Download]($artifactUrl)" + } return $downloadInfo } @@ -589,6 +594,7 @@ class ParallelTestsResults { $platformKey = $outputs.Keys | Where-Object { $_.EndsWith(".TESTS_PLATFORM") } $attemptKey = $outputs.Keys | Where-Object { $_.EndsWith(".TESTS_ATTEMPT") } $titleKey = $outputs.Keys | Where-Object { $_.EndsWith(".TESTS_TITLE") } + $vsdropsPublishedKey = $outputs.Keys | Where-Object { $_.EndsWith(".VSDROPS_PUBLISHED") } } else { # matrix job $jobName = $name.Substring(0, $name.IndexOf('.')) @@ -597,6 +603,7 @@ class ParallelTestsResults { $platformKey = $outputs.Keys | Where-Object { $_.StartsWith($jobName + ".") -and $_.EndsWith(".TESTS_PLATFORM") } $attemptKey = $outputs.Keys | Where-Object { $_.StartsWith($jobName + ".") -and $_.EndsWith(".TESTS_ATTEMPT") } $titleKey = $outputs.Keys | Where-Object { $_.StartsWith($jobName + ".") -and $_.EndsWith(".TESTS_TITLE") } + $vsdropsPublishedKey = $outputs.Keys | Where-Object { $_.StartsWith($jobName + ".") -and $_.EndsWith(".VSDROPS_PUBLISHED") } } Write-Host "Keys for Label='$label' and JobName='$jobName' (dotCount=$dotCount): TitleKey='$titleKey' StatusKey=$statusKey BotKey=$botKey PlatformKey=$platformKey AttemptKey=$attemptKey" @@ -611,6 +618,7 @@ class ParallelTestsResults { $platform = if ($platformKey -eq $null) { "NotFound" } else { $outputs[$platformKey] } $attempt = if ($attemptKey -eq $null) { -2 } else { [int]$outputs[$attemptKey] } $title = if ($titleKey -eq $null) { "NotFound" } else { $outputs[$titleKey] } + $vsdropsPublished = if ($vsdropsPublishedKey -eq $null) { $null } else { $outputs[$vsdropsPublishedKey] } $testResult = [PSCustomObject]@{ Label = $label Title = $title @@ -619,6 +627,7 @@ class ParallelTestsResults { Platform = $platform Attempt = $attempt TestStage = $testStage + VSDropsPublished = $vsdropsPublished } if ($tests.Contains($label)) { $testInfo = $tests[$label] @@ -675,6 +684,7 @@ class ParallelTestsResults { } $result = [TestResult]::new($testSummaryPath, $status, $testConfig, $testAttempt) + $result.VSDropsPublishFailed = ($testResult.VSDropsPublished -eq "Failed") } $testResults += $result diff --git a/tools/devops/automation/templates/mac/build.yml b/tools/devops/automation/templates/mac/build.yml index a6c20308996f..c9ef678181b5 100644 --- a/tools/devops/automation/templates/mac/build.yml +++ b/tools/devops/automation/templates/mac/build.yml @@ -212,6 +212,7 @@ steps: # Upload to VSDrops so that the Html Report link in the GitHub comment works. - task: artifactDropTask@1 displayName: 'Publish to Artifact Services Drop' + name: publishVSDrops inputs: dropServiceURI: 'https://devdiv.artifacts.visualstudio.com/DefaultCollection' dropMetadataContainerName: '${{ parameters.uploadPrefix }}DropMetadata-${{ parameters.stageName }}${{ parameters.label }}-$(System.JobAttempt)' @@ -223,6 +224,13 @@ steps: continueOnError: true condition: succeededOrFailed() +- bash: | + echo "##vso[task.setvariable variable=VSDROPS_PUBLISHED;isOutput=true]Failed" + name: setVSDropsPublishResult + displayName: 'Set VSDrops publish result' + continueOnError: true + condition: and(succeededOrFailed(), ne(steps.publishVSDrops.outcome, 'Succeeded')) + # Archive files for the Html Report so that the report can be easily uploaded as artifacts of the build. - task: ArchiveFiles@1 displayName: 'Archive HtmlReport' diff --git a/tools/devops/automation/templates/tests/run-tests.yml b/tools/devops/automation/templates/tests/run-tests.yml index 55f5d8d5a7be..3669174feb9d 100644 --- a/tools/devops/automation/templates/tests/run-tests.yml +++ b/tools/devops/automation/templates/tests/run-tests.yml @@ -184,6 +184,7 @@ steps: - task: artifactDropTask@1 displayName: 'Publish to Artifact Services Drop' + name: publishVSDrops inputs: dropServiceURI: 'https://devdiv.artifacts.visualstudio.com/DefaultCollection' dropMetadataContainerName: '${{ parameters.uploadPrefix }}DropMetadata-${{ parameters.testPrefix }}${{ parameters.labelWithPlatform }}-$(System.JobAttempt)' @@ -195,6 +196,13 @@ steps: continueOnError: true condition: succeededOrFailed() +- bash: | + echo "##vso[task.setvariable variable=VSDROPS_PUBLISHED;isOutput=true]Failed" + name: setVSDropsPublishResult + displayName: 'Set VSDrops publish result' + continueOnError: true + condition: and(succeededOrFailed(), ne(steps.publishVSDrops.outcome, 'Succeeded')) + - bash: | set -ex find . -name 'vsts-*.xml' || true From 1945e07ca58ae3c2a8ea60ec931b77f3ee2d989a Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 23 Apr 2026 12:42:09 +0200 Subject: [PATCH 3/3] Fix: replace unsupported steps.X.outcome with AGENT_JOBSTATUS comparison MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Azure DevOps agent does not recognize 'steps' in condition expressions, causing every job to crash at initialization with: Unrecognized value: 'steps'. Located at position 29 within expression: and(succeededOrFailed(), ne(steps.publishVSDrops.outcome, 'Succeeded')) Replace the broken condition with a workaround that captures AGENT_JOBSTATUS before the publish step and compares it after. If the status degraded (e.g. Succeeded → SucceededWithIssues), the publish task failed. This preserves the existing continueOnError: true behavior and job result semantics. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tools/devops/automation/templates/mac/build.yml | 13 +++++++++++-- .../devops/automation/templates/tests/run-tests.yml | 13 +++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/tools/devops/automation/templates/mac/build.yml b/tools/devops/automation/templates/mac/build.yml index c9ef678181b5..f1db621a6e39 100644 --- a/tools/devops/automation/templates/mac/build.yml +++ b/tools/devops/automation/templates/mac/build.yml @@ -210,6 +210,12 @@ steps: condition: succeededOrFailed() # Upload to VSDrops so that the Html Report link in the GitHub comment works. +- bash: | + echo "##vso[task.setvariable variable=JOB_STATUS_BEFORE_VSDROPS]$AGENT_JOBSTATUS" + displayName: 'Capture job status before VSDrops publish' + condition: succeededOrFailed() + continueOnError: true + - task: artifactDropTask@1 displayName: 'Publish to Artifact Services Drop' name: publishVSDrops @@ -225,11 +231,14 @@ steps: condition: succeededOrFailed() - bash: | - echo "##vso[task.setvariable variable=VSDROPS_PUBLISHED;isOutput=true]Failed" + if [ "$JOB_STATUS_BEFORE_VSDROPS" != "$AGENT_JOBSTATUS" ]; then + echo "VSDrops publish changed job status from '$JOB_STATUS_BEFORE_VSDROPS' to '$AGENT_JOBSTATUS'" + echo "##vso[task.setvariable variable=VSDROPS_PUBLISHED;isOutput=true]Failed" + fi name: setVSDropsPublishResult displayName: 'Set VSDrops publish result' continueOnError: true - condition: and(succeededOrFailed(), ne(steps.publishVSDrops.outcome, 'Succeeded')) + condition: succeededOrFailed() # Archive files for the Html Report so that the report can be easily uploaded as artifacts of the build. - task: ArchiveFiles@1 diff --git a/tools/devops/automation/templates/tests/run-tests.yml b/tools/devops/automation/templates/tests/run-tests.yml index 3669174feb9d..a7b123644b79 100644 --- a/tools/devops/automation/templates/tests/run-tests.yml +++ b/tools/devops/automation/templates/tests/run-tests.yml @@ -182,6 +182,12 @@ steps: continueOnError: true condition: succeededOrFailed() +- bash: | + echo "##vso[task.setvariable variable=JOB_STATUS_BEFORE_VSDROPS]$AGENT_JOBSTATUS" + displayName: 'Capture job status before VSDrops publish' + condition: succeededOrFailed() + continueOnError: true + - task: artifactDropTask@1 displayName: 'Publish to Artifact Services Drop' name: publishVSDrops @@ -197,11 +203,14 @@ steps: condition: succeededOrFailed() - bash: | - echo "##vso[task.setvariable variable=VSDROPS_PUBLISHED;isOutput=true]Failed" + if [ "$JOB_STATUS_BEFORE_VSDROPS" != "$AGENT_JOBSTATUS" ]; then + echo "VSDrops publish changed job status from '$JOB_STATUS_BEFORE_VSDROPS' to '$AGENT_JOBSTATUS'" + echo "##vso[task.setvariable variable=VSDROPS_PUBLISHED;isOutput=true]Failed" + fi name: setVSDropsPublishResult displayName: 'Set VSDrops publish result' continueOnError: true - condition: and(succeededOrFailed(), ne(steps.publishVSDrops.outcome, 'Succeeded')) + condition: succeededOrFailed() - bash: | set -ex