Skip to content

Commit 72b1387

Browse files
Sync eng/common directory with azure-sdk-tools for PR 16052 (#7206)
* Port Vally eval CI scripts from PowerShell to JavaScript Rewrite all eval pipeline glue scripts as dependency-free ESM with node:test unit tests, under eng/common/scripts/eval/: - collect-stimuli.js (+ lib/glob.js) <- Split-EvalSuite.ps1 - invoke-eval-shard.js + lib/verdict.js <- Invoke-EvalShard.ps1 + EvalVerdict.Helpers.ps1 - build-eval-summary.js <- Build-EvalSummary.ps1 - sync-eval-git-repo.js <- Sync-EvalGitRepo.ps1 - init-eval-git-fixtures.js <- Initialize-EvalGitFixtures.ps1 Delete all .ps1 + Pester tests; rewire vally-eval.yml to invoke the JS via node; update README/.vally.yaml/eval-comment references. 40 node:test cases pass. * Split Vally eval pipeline into reusable templates Extract the monolithic vally-eval.yml into modular, parameterized templates under eng/common/pipelines/templates (synced to other repos), leaving a thin repo-local entrypoint: - steps/vally-build-mock.yml: build + publish the mock MCP DLL - steps/vally-collect-stimuli.yml: glob eval files -> shard matrix (repo + optional common root) - steps/vally-run-shard.yml: npm ci + download mock + prime git fixtures + run shard + publish JUnit/transcripts - steps/vally-summarize.yml: aggregate shard JUnit + gate rollup - stages/archetype-vally-eval.yml: Eval stage (BuildMock || Detect -> RunShard matrix, customMcpBuildJobs hook) + Summary stage Drop the redundant global 'npm install -g @github/copilot-sdk': it is a locked transitive dep of @microsoft/vally-cli, so npm ci in eng/skill-eval already installs the executor (D9: pin via lockfile, no global installs). Migrate all jobs to the managed pool (name: $(LINUXPOOL) / image: $(LINUXVMIMAGE) / os: linux); the repo-local entrypoint supplies the image.yml variables and the variable group. * Generalize Vally eval templates for skill + live verticals; fix missing lib modules Make the shared archetype/templates MCP-agnostic so the skill-compliance and live workflow-scenario evals can reuse the same shard fan-out as the mock tier: - collect-stimuli.js: derive file-mode shard names from the full relative path so per-skill <skill>/evals/trigger.eval.yaml files no longer collide. - vally-build-mock.yml -> vally-build-mcp.yml: build a LIST of MCP servers and publish them under a common stage root. - vally-collect-stimuli.yml: accept an explicit list of eval glob patterns. - vally-run-shard.yml: rename mock* params to mcp*; optionally wrap the eval in AzureCLI@2 (live tier) so the agent's DevOps calls inherit the service connection, else run as a plain script (hermetic tiers). - archetype-vally-eval.yml: swap mock-specific params for mcpBuilds/mcpArtifact/ mcpStageRoot/patterns/azureServiceConnection and wire them through. Add two thin entrypoints: - eng/pipelines/skill-eval-shards.yml: sharded variant of skill-eval.yml (builds cli + mock, one shard per skill). - eng/pipelines/live-eval.yml: nightly scheduled live run (builds cli, authenticated via opensource-api-connection). Fix the pipeline ERR_MODULE_NOT_FOUND failure: the root .gitignore's Python `lib/` rule silently excluded eng/common/scripts/eval/lib/{glob,verdict}.js, so the pipeline checkout was missing them. Add a targeted negation and track the two modules. Defaults preserve the existing mock vertical; vally-eval.yml only updates the renamed build-template trigger paths. * polish and rename * Switch Vally eval CI to 1ES; quiet teardown-noise warnings Pipeline (1ES migration, localized to the archetype): - archetype-vally-eval.yml now extends /eng/pipelines/templates/stages/1es-redirect.yml (chained extends, like archetype-sdk-tool-dotnet.yml). Adds Use1ESOfficial param. - 1ES Official forbids the `publish:` shortcut, so the three artifact publishes move to job-level templateContext.outputs: MCP server artifact on BuildMcp; per-shard eval-result-$(shardName) (JUnit, always) and eval-debug-$(shardName) (transcripts, on failure) on the RunShard matrix job. - Drop the publish steps from vally-build-mcp.yml / vally-run-shard.yml (keep the staging script steps) and remove the now-unused stageRoot/artifactName params from vally-build-mcp.yml. Clears the 17 [1ES PT] warnings; CG is opt-in under 1ES. Shard gate (invoke-eval-shard.js): - Demote the two "passing verdict" warnings (hadExecutionErrors and non-zero vally exit) from ##vso build warnings to plain log lines. Both are the same expected post-run agent-teardown noise and say nothing about the eval; the results.jsonl verdict is authoritative. Removes 8 confusing build annotations. - Replace em-dash with ASCII hyphen in the task.logissue strings (rendered as mojibake in ADO annotations). Earlier review fixes (carried in this PR): - collect-stimuli.js: guard against area-mode shard-name collisions (matches file mode); add a regression test. - vally-run-shard.yml: prime git fixtures from this shard's own evalArgs (--pattern per -e file) instead of the built-in defaults. * Fix npm ci EPERM under 1ES by restoring via azure-sdk npm mirror The 1ES Official managed image blocks direct egress to registry.npmjs.org, so the shard's `npm ci` failed with EPERM ("rejected by your operating system"), which cascaded into the collect-JUnit find errors. Restore the Vally CLI through the allowlisted azure-sdk Azure Artifacts npm mirror via an authenticated .npmrc (the same pattern typespec-author-eval-setup uses). * Simplify eval archetype: build Mock+Cli MCP by default, drop never-overridden params and mcpBuilds * Rework MCP build to useCustomMcp + runtime repo detection per review * Copy Vally CLI pin to eng/common/vally-eval and repoint eval pipeline Adds eng/common/vally-eval (renamed from skill-eval) so the pinned @microsoft/vally-cli lockfile syncs via eng/common. Repoints the Vally eval pipeline (vally-eval.yml, skill-eval-2.yml triggers; vally-run-shard skillEvalDir default; invoke-eval-shard comment) to the new path. eng/skill-eval is left intact for legacy GitHub Actions and the going-away skill-eval.yml; it can be removed later. * Move eval-mcp-setup.yml step template into eng/common Co-locates the custom-MCP override step template with the other vally-* eval step templates in eng/common/pipelines/templates/steps and repoints the archetype useCustomMcp branch + comments. * Repoint eval-mcp-setup.yml references to eng/common path * Eval CI: move entrypoints/templates to eng/common, rename vally-* templates to eval-*, extract generate-eval-matrix + eval-shard jobs, merge eval package.json, drop variable group, add maxParallel * Eval CI: make eng/common templates MCP- and auth-agnostic - Replace MCP build coupling with a single mcpSetupTemplate path param; move the actual builds to repo-local eng/pipelines/eval/mcp-mock.yml + mcp-live.yml and delete the shared eval-build-mcp.yml. - Remove azureServiceConnection param; the live tier's AzureCLI@2 wrapping now lives in a repo-local evalInvokeTemplate (eval-invoke-live.yml). Default is the hermetic node runner eval-invoke.yml. This single run-time hook also covers chatbot/custom-MCP startup. - Convert shardTimeoutInMinutes from a param to an entrypoint-declared variable (hermetic 20, live 45); document the required-variable contract in the archetype + shard job. - Pass vallyRoot explicitly from every entrypoint; update trigger path filters for the new templates. - Fix sync-eval-git-repo.js cold-cache clone: init + fetch <ref> + checkout FETCH_HEAD so branches, tags, and SHAs all resolve (matches the refresh path), not just the remote default branch. * Simplify eval pipeline templates: inline non-varying parameters Audit and remove pipeline parameters that no caller overrides, inlining the constant value instead. Make the archetype evalGlobs default explicit and verified equivalent to the collect-stimuli.js fallback. Fix latent shardBy and githubTokenVariable passthrough bugs. * Flatten eval pipeline to stages -> jobs (steps inline) Make archetype-eval.yml a pure orchestrator (only job-template references, no inline steps) and fold each single-use steps template into its owning job template. Add build-mcp.yml and eval-summarize.yml job templates; delete the merged eval-collect-stimuli, eval-run-shard, and eval-summarize step templates. Only the two swap-point step templates (mcpSetupTemplate, evalInvokeTemplate) remain. Update trigger path-filters to templates/jobs/**. * Trim verbose comments across eval pipeline and scripts Reduce oversized comment blocks to terse basic summaries: collapse multi-paragraph JS file headers, drop ported-from-PowerShell references and why/teardown rationale, and remove Phase/D2/D3 jargon from the pipeline YAML headers. * Reject eval paths with whitespace; note JUnit parse coupling Fail fast in collect-stimuli when a discovered eval path contains whitespace, so a bad path errors at matrix time instead of mis-splitting the space-delimited -e args downstream. Add a comment in build-eval-summary flagging the regex coupling to Vally's JUnit output. * Make shard timeout a compile-time parameter 1ES requires job timeoutInMinutes to be an integer at compile time, so it cannot read a runtime variable macro. Thread shardTimeoutInMinutes as a number parameter (default 20) from each entrypoint through the archetype to the shard job; live tier sets 45. * Map copilot PAT into GITHUB_TOKEN env for eval invoke steps * Attach eval variable group to entrypoint pipelines so copilot PAT resolves * Land eval cache on a local branch so worktree fixtures resolve the ref * Consolidate eval invoke into one auth-conditional template and tidy shard/MCP seams - Fold eval-invoke-live.yml into eval-invoke.yml via UseAzSdkAuthentication flag (AzureCLI@2 vs hermetic script); delete the live copy - Derive npm --prefix from the script dir in invoke-eval-shard.js; drop the skill-eval-prefix arg/param everywhere - Make vallyRoot required (no default) in archetype + shard; thread UseAzSdkAuthentication through - Move checkout into the BuildMcp job; remove it from the MCP setup templates; make the example MCP template a mock/live if-else - Add pre/post eval step hooks and blank-line spacing in eval-shard.yml * Add eval hooks, parameterize gate threshold, require mcpSetupTemplate - Add eval-hook-example.yml placeholder documenting pre/post-eval hooks; surface commented preEvalTemplate/postEvalTemplate in both entrypoints. - Parameterize the pass-rate gate as a threshold param (default 0.8) threaded entrypoint -> archetype -> eval-shard -> eval-invoke; surface it explicitly per tier. - Make mcpSetupTemplate required in archetype-eval.yml (example default was tools-repo specific). * Consolidate MCP templates and add opt-in cross-repo checkout - Merge mcp-mock.yml/mcp-live.yml into eval-mcp-setup.yml with an McpServer if/else selector threaded through build-mcp and the archetype. - Add opt-in toolsRepo param (default '') so repos outside azure-sdk-tools can side-checkout the tools sources to build the MCP; no-op for the tools repo. * Fix eval archetype: declare toolsRepo param and default mcpServer The archetype forwarded toolsRepo/mcpServer without declaring toolsRepo (ADO 'Key not found' compile error) and left mcpServer without a default despite being optional. Declare toolsRepo (default '') and default mcpServer to ''. * Address review feedback: portable lib/ gitignore, rerun-safe shard artifacts, TestType rename, exec.js note * Always stage debug dir so failed()-gated transcript publish can't error on missing path --------- Co-authored-by: helen229 <gaoh@microsoft.com>
1 parent e5c3d98 commit 72b1387

26 files changed

Lines changed: 3541 additions & 0 deletions

eng/common/pipelines/live-eval.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Live-tier eval CI: nightly end-to-end run of the live workflow scenarios against the real
2+
# azsdk-cli MCP and real Azure DevOps (writes confined to a test area).
3+
4+
# Nightly only — no CI/PR trigger.
5+
trigger: none
6+
pr: none
7+
8+
schedules:
9+
- cron: '0 9 * * *' # 09:00 UTC daily
10+
displayName: 'Nightly live eval'
11+
branches:
12+
include:
13+
- main
14+
always: true # run even when main has not changed
15+
16+
variables:
17+
# Managed-pool image selection (LINUXPOOL/LINUXVMIMAGE). Repo-local.
18+
- template: /eng/pipelines/templates/variables/image.yml
19+
# Provides the secret azuresdk-copilot-github-pat, mapped into GITHUB_TOKEN in the invoke step.
20+
- group: AzSDK_Eval_Variable_group
21+
22+
extends:
23+
template: /eng/common/pipelines/templates/stages/archetype-eval.yml
24+
parameters:
25+
# Shared mock/live builder; select the live tier (real Cli MCP).
26+
mcpSetupTemplate: /eng/common/pipelines/templates/steps/eval-mcp-setup.yml
27+
TestType: live
28+
vallyRoot: tools/azsdk-cli/Azure.Sdk.Tools.Vally
29+
evalGlobs:
30+
- 'evals/workflow-scenarios/live/*.eval.yaml'
31+
# Run each shard under AzureCLI@2 so the real MCP's DevOps calls are authenticated.
32+
UseAzSdkAuthentication: true
33+
failOnFailedTests: true
34+
# Live scenarios are end-to-end, so give each shard more headroom than the report-only tiers.
35+
shardTimeoutInMinutes: 45
36+
# Pass-rate gate for `vally eval`. This tier gates (failOnFailedTests: true), so the threshold
37+
# is the real bar the nightly run must clear; raise it here as live coverage stabilizes.
38+
threshold: 0.8
39+
# This repo needs no repo-specific setup (the live MCP is built by the common BuildMcp job).
40+
# A spec/language repo that must start its own bot / server / MCP copies the example hook and
41+
# points these at it — see eng/common/pipelines/templates/steps/eval-hook-example.yml.
42+
# preEvalTemplate: /eng/pipelines/eval/start-my-bot.yml
43+
# postEvalTemplate: /eng/pipelines/eval/stop-my-bot.yml
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Skill-compliance eval CI: runs the per-skill Vally evals under .github/skills (one job per skill).
2+
3+
trigger:
4+
branches:
5+
include:
6+
- main
7+
paths:
8+
include:
9+
- .github/skills/**
10+
# Retrigger when the mock MCP's tool catalog changes (it can move results).
11+
- tools/azsdk-cli/Azure.Sdk.Tools.Mock/**
12+
- eng/common/pipelines/skill-eval.yml
13+
- eng/common/pipelines/templates/jobs/**
14+
- eng/common/pipelines/templates/steps/eval-invoke.yml
15+
- eng/common/pipelines/templates/steps/eval-mcp-setup.yml
16+
- eng/common/pipelines/templates/stages/archetype-eval.yml
17+
- eng/common/scripts/eval/**
18+
19+
pr: none
20+
21+
variables:
22+
# Managed-pool image selection (LINUXPOOL/LINUXVMIMAGE). Repo-local.
23+
- template: /eng/pipelines/templates/variables/image.yml
24+
# Provides the secret azuresdk-copilot-github-pat, mapped into GITHUB_TOKEN in the invoke step.
25+
- group: AzSDK_Eval_Variable_group
26+
27+
extends:
28+
template: /eng/common/pipelines/templates/stages/archetype-eval.yml
29+
parameters:
30+
# Shared mock/live builder; select the mock tier.
31+
mcpSetupTemplate: /eng/common/pipelines/templates/steps/eval-mcp-setup.yml
32+
TestType: mock
33+
vallyRoot: .github/skills
34+
# Single-level glob excludes azure-typespec-author/evaluate/ (its own benchmark pipeline).
35+
evalGlobs:
36+
- '*/evals/*.eval.yaml'
37+
# Per-shard job timeout (report-only tier).
38+
shardTimeoutInMinutes: 20
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# BuildMcp job: builds the MCP server(s) via the swap-point mcpSetupTemplate and ships the
2+
# artifacts/mcp tree as the `mcp-servers` artifact every shard re-stages.
3+
4+
parameters:
5+
- name: mcpSetupTemplate
6+
type: string
7+
- name: TestType
8+
# Which MCP tier to build (mock/live), forwarded to templates that build both tiers
9+
# (e.g. eval-mcp-setup.yml). Use 'none' for param-free repo templates.
10+
type: string
11+
default: none
12+
- name: toolsRepo
13+
# Optional azure-sdk-tools repository-resource alias, forwarded to the MCP template when this
14+
# pipeline runs OUTSIDE azure-sdk-tools (cross-repo build). Blank in the tools repo.
15+
type: string
16+
default: ''
17+
18+
jobs:
19+
- job: BuildMcp
20+
displayName: 'Build + publish MCP server(s)'
21+
pool:
22+
name: $(LINUXPOOL)
23+
image: $(LINUXVMIMAGE)
24+
os: linux
25+
# 1ES publishes via templateContext.outputs, not the `publish` shortcut.
26+
templateContext:
27+
outputs:
28+
- output: pipelineArtifact
29+
displayName: 'Publish MCP server artifact'
30+
artifactName: mcp-servers
31+
targetPath: $(Build.SourcesDirectory)/artifacts/mcp
32+
steps:
33+
- checkout: self
34+
fetchDepth: 1
35+
36+
# Pass TestType only when not 'none', so param-free repo templates still resolve.
37+
- ${{ if ne(parameters.TestType, 'none') }}:
38+
- template: ${{ parameters.mcpSetupTemplate }}
39+
parameters:
40+
TestType: ${{ parameters.TestType }}
41+
toolsRepo: ${{ parameters.toolsRepo }}
42+
- ${{ else }}:
43+
- template: ${{ parameters.mcpSetupTemplate }}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# Fan-out shard job: one leg per shard runs its evals against the prebuilt MCP and publishes
2+
# per-leg JUnit (always) + transcripts (on failure). Pass/fail is the eval VERDICT, not the
3+
# `vally` exit code (vally can exit 1 on a teardown flake after a pass).
4+
5+
parameters:
6+
- name: vallyRoot
7+
# Required (no default) so each repo passes its own eval root explicitly.
8+
type: string
9+
- name: evalInvokeTemplate
10+
# Swap point: runs `vally eval` for the shard (the common template is auth-conditional).
11+
type: string
12+
default: /eng/common/pipelines/templates/steps/eval-invoke.yml
13+
- name: UseAzSdkAuthentication
14+
# Forwarded to the invoke template: true runs the shard under AzureCLI@2.
15+
type: boolean
16+
default: false
17+
- name: threshold
18+
# Pass-rate gate forwarded to the invoke template (`vally eval --threshold` + shard verdict).
19+
type: number
20+
default: 0.8
21+
- name: preEvalTemplate
22+
# Optional repo-specific setup run before the eval (e.g. start a bot/agent). Placeholder by default.
23+
type: string
24+
default: ''
25+
- name: postEvalTemplate
26+
# Optional repo-specific teardown run after the eval. Placeholder by default.
27+
type: string
28+
default: ''
29+
- name: shardTimeoutInMinutes
30+
# Per-shard job timeout. Must be a parameter (not a runtime variable) so 1ES resolves it at compile time.
31+
type: number
32+
default: 20
33+
34+
jobs:
35+
- job: RunShard
36+
displayName: 'Eval'
37+
pool:
38+
name: $(LINUXPOOL)
39+
image: $(LINUXVMIMAGE)
40+
os: linux
41+
# A red shard shouldn't abort its siblings; the rollup gates in Summary.
42+
continueOnError: true
43+
timeoutInMinutes: ${{ parameters.shardTimeoutInMinutes }}
44+
strategy:
45+
matrix: $[ stageDependencies.Prepare.generate_eval_matrix.outputs['detect.matrix'] ]
46+
maxParallel: 10
47+
templateContext:
48+
outputs:
49+
# Artifact names carry $(System.JobAttempt) so "Rerun failed jobs" publishes a fresh
50+
# name instead of colliding with the prior attempt's artifact (which fails the rerun).
51+
# The Summary job keeps only the highest attempt per shard so retries supersede cleanly.
52+
- output: pipelineArtifact
53+
displayName: 'Publish $(shardName) JUnit'
54+
artifactName: 'eval-result-$(shardName)-$(System.JobAttempt)'
55+
targetPath: $(Build.ArtifactStagingDirectory)/junit
56+
condition: always()
57+
- output: pipelineArtifact
58+
displayName: 'Publish $(shardName) transcripts (on failure)'
59+
artifactName: 'eval-debug-$(shardName)-$(System.JobAttempt)'
60+
targetPath: $(Build.ArtifactStagingDirectory)/debug
61+
condition: failed()
62+
steps:
63+
- checkout: self
64+
fetchDepth: 1
65+
66+
- task: UseNode@1
67+
displayName: 'Use Node.js 22.x'
68+
inputs:
69+
version: 22.x
70+
71+
# 1ES Official image blocks registry.npmjs.org; restore via the azure-sdk npm mirror.
72+
- template: /eng/common/pipelines/templates/steps/create-authenticated-npmrc.yml
73+
parameters:
74+
npmrcPath: $(Build.SourcesDirectory)/.npmrc
75+
registryUrl: https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-tools/npm/registry/
76+
77+
# Installs vally-cli + its pinned copilot-sdk executor into node_modules.
78+
- script: npm ci --userconfig $(Build.SourcesDirectory)/.npmrc
79+
displayName: 'Install Vally CLI + copilot-sdk (pinned)'
80+
workingDirectory: eng/common/scripts/eval
81+
82+
- download: current
83+
artifact: mcp-servers
84+
displayName: 'Download MCP server(s)'
85+
86+
- script: |
87+
mkdir -p "$(Build.SourcesDirectory)/artifacts/mcp"
88+
cp -r "$(Pipeline.Workspace)/mcp-servers/." "$(Build.SourcesDirectory)/artifacts/mcp/"
89+
displayName: 'Stage MCP server(s) at expected path'
90+
91+
# Optional repo-specific setup (e.g. start a bot/agent) before the eval runs.
92+
- ${{ if parameters.preEvalTemplate }}:
93+
- template: ${{ parameters.preEvalTemplate }}
94+
95+
# Clone only the git fixtures this shard's evals reference (Vally won't clone them).
96+
- script: |
97+
patterns=()
98+
set -f
99+
for tok in $EVAL_ARGS; do
100+
[ "$tok" = "-e" ] && continue
101+
patterns+=(--pattern "$tok")
102+
done
103+
set +f
104+
node "$(Build.SourcesDirectory)/eng/common/scripts/eval/init-eval-git-fixtures.js" \
105+
--eval-root "$(Build.SourcesDirectory)/${{ parameters.vallyRoot }}" \
106+
"${patterns[@]}"
107+
displayName: 'Prime eval git fixtures'
108+
env:
109+
EVAL_ARGS: $(evalArgs)
110+
111+
# Runs the eval via the invoke template (auth-conditional in the common default).
112+
- template: ${{ parameters.evalInvokeTemplate }}
113+
parameters:
114+
vallyRoot: ${{ parameters.vallyRoot }}
115+
UseAzSdkAuthentication: ${{ parameters.UseAzSdkAuthentication }}
116+
threshold: ${{ parameters.threshold }}
117+
118+
# Optional repo-specific teardown after the eval.
119+
- ${{ if parameters.postEvalTemplate }}:
120+
- template: ${{ parameters.postEvalTemplate }}
121+
122+
# JUnit is tiny — stage it always; heavy transcripts stage separately on failure.
123+
# Also create the debug dir here so the failed()-gated debug publish always has a target
124+
# (otherwise the publish errors "Path does not exist" when the dir was never created).
125+
- script: |
126+
mkdir -p "$(Build.ArtifactStagingDirectory)/junit" "$(Build.ArtifactStagingDirectory)/debug"
127+
find "$(Build.SourcesDirectory)/artifacts/vally-results/$(shardName)" -name '*.junit.xml' -exec cp {} "$(Build.ArtifactStagingDirectory)/junit/" \;
128+
displayName: 'Collect $(shardName) JUnit'
129+
condition: always()
130+
131+
# On a real failure keep transcripts; strip JUnit so Summary can't double-count it.
132+
- script: |
133+
src="$(Build.SourcesDirectory)/artifacts/vally-results/$(shardName)"
134+
dest="$(Build.ArtifactStagingDirectory)/debug"
135+
mkdir -p "$dest"
136+
if [ -d "$src" ]; then
137+
cp -r "$src/." "$dest/"
138+
find "$dest" -name '*.junit.xml' -delete
139+
fi
140+
displayName: 'Collect $(shardName) transcripts (on failure)'
141+
condition: failed()
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Summary job: merges every shard's JUnit into one rollup (Markdown summary + Tests tab) and
2+
# gates on it when failOnFailedTests is true.
3+
4+
parameters:
5+
- name: failOnFailedTests
6+
type: boolean
7+
default: false
8+
9+
jobs:
10+
- job: Summarize
11+
displayName: 'Publish + gate rollup'
12+
pool:
13+
name: $(LINUXPOOL)
14+
image: $(LINUXVMIMAGE)
15+
os: linux
16+
steps:
17+
- task: UseNode@1
18+
displayName: 'Use Node.js 22.x'
19+
inputs:
20+
version: 22.x
21+
22+
# Match *.junit.xml only so stray XML (e.g. a DLL's doc XML) can't fake a shard.
23+
- task: DownloadPipelineArtifact@2
24+
displayName: 'Download all shard results'
25+
inputs:
26+
buildType: 'current'
27+
itemPattern: '**/*.junit.xml'
28+
targetPath: $(Pipeline.Workspace)/eval-results
29+
30+
# Renders the rollup to the run's Summary tab (##vso[task.uploadsummary]).
31+
- script: |
32+
node "$(Build.SourcesDirectory)/eng/common/scripts/eval/build-eval-summary.js" \
33+
--results-root "$(Pipeline.Workspace)/eval-results" \
34+
--output-path "$(Build.ArtifactStagingDirectory)/eval-summary.md"
35+
displayName: 'Render Markdown rollup'
36+
condition: always()
37+
38+
- task: PublishTestResults@2
39+
displayName: 'Publish eval rollup'
40+
inputs:
41+
testResultsFormat: 'JUnit'
42+
testResultsFiles: '$(Pipeline.Workspace)/eval-results/**/*.junit.xml'
43+
mergeTestResults: true
44+
failTaskOnFailedTests: ${{ parameters.failOnFailedTests }}
45+
testRunTitle: 'Vally evals'
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Globs the eval files into a fan-out matrix (output var detect.matrix). Runs in Prepare
2+
# alongside BuildMcp; the Eval stage's RunShard job reads it cross-stage.
3+
4+
parameters:
5+
- name: vallyRoot
6+
type: string
7+
- name: evalGlobs
8+
type: object
9+
default: []
10+
11+
jobs:
12+
# Job/step/output names are fixed: the Eval stage reads the matrix cross-stage at
13+
# stageDependencies.Prepare.generate_eval_matrix.outputs['detect.matrix'].
14+
- job: generate_eval_matrix
15+
displayName: 'Glob eval files -> matrix'
16+
pool:
17+
name: $(LINUXPOOL)
18+
image: $(LINUXVMIMAGE)
19+
os: linux
20+
steps:
21+
- checkout: self
22+
fetchDepth: 1
23+
24+
- task: UseNode@1
25+
displayName: 'Use Node.js 22.x'
26+
inputs:
27+
version: 22.x
28+
29+
# set -f stops the shell expanding the glob `*` before patterns reach the script.
30+
- script: |
31+
patterns=()
32+
if [ -n "$EVAL_PATTERNS" ]; then
33+
set -f
34+
for p in $EVAL_PATTERNS; do
35+
patterns+=(--pattern "$p")
36+
done
37+
set +f
38+
fi
39+
base="$(Build.SourcesDirectory)/${{ parameters.vallyRoot }}"
40+
node "$(Build.SourcesDirectory)/eng/common/scripts/eval/collect-stimuli.js" \
41+
--eval-root "$base" \
42+
--path-base "$base" \
43+
"${patterns[@]}" \
44+
--output-variable matrix
45+
name: detect
46+
displayName: 'Build fan-out matrix'
47+
env:
48+
EVAL_PATTERNS: ${{ join(' ', parameters.evalGlobs) }}

0 commit comments

Comments
 (0)