[Draft] Add perf-build.yml: per-commit ASP.NET Core build → Build Cache Service#2
Closed
LoopedBard3 wants to merge 5 commits into
Closed
[Draft] Add perf-build.yml: per-commit ASP.NET Core build → Build Cache Service#2LoopedBard3 wants to merge 5 commits into
LoopedBard3 wants to merge 5 commits into
Conversation
Adds a new internal-only Azure DevOps pipeline that builds every commit
on main and uploads per-RID ASP.NET Core runtime packs to the Build
Cache Service (BCS, `pvscmdupload`) so dotnet/crank can resolve
`Microsoft.AspNetCore.App` runtime binaries by commit SHA for
performance-regression bisection. Modeled on dotnet/runtime's
`eng/pipelines/performance/perf-build.yml` but adapted to aspnetcore's
1ES extension model (`v1/1ES.Official.PipelineTemplate.yml@1esPipelines`
+ `.azure/pipelines/jobs/default-build.yml@self`).
Pipeline structure:
* Single `build` stage with 3 jobs that reuse the existing
`default-build.yml` template: `Windows_build` (x64+x86+arm64 in
one job, mirroring `ci.yml`'s `Windows_build` pattern),
`Linux_x64_build`, `Linux_arm64_build`. Each job runs the normal
build.cmd/build.sh `--pack --all -p:OnlyPackPlatformSpecificPackages=true`
invocation, then a pwsh afterBuild step packs the per-RID nupkg into
a BCS-shaped archive (lowercase
`microsoft.aspnetcore.app.runtime.{rid}/Release/runtimes/{rid}/...`
root — the lowercase root is a load-bearing contract for the future
aspnetcore-overlay crank PR, so the packer asserts the archive root
after creation).
* Conditional `UploadArtifacts` stage gated to
`internal + (IndividualCI | Manual)` runs (Manual is included so
the runbook's first seed-run can produce uploads). Five per-config
jobs use the new helper template
`jobs/perf-build-upload-job.yml`, one per configKey:
`aspnetcore_x64_linux`, `aspnetcore_arm64_linux`,
`aspnetcore_x64_windows`, `aspnetcore_arm64_windows`,
`aspnetcore_x86_windows`.
Per-config upload-job atomicity invariant (load-bearing): a single
`AzureCLI@2` task does, in order, (1) `az storage blob upload`
the archive to
`builds/aspnetcore/buildArtifacts/{sha}/{configKey}/{archiveFile}`,
(2) HEAD-verify the static-website URL with a short retry loop
(defaults: 6 attempts × 10 s, both tunable via pipeline parameters
`headRetryCount` / `headRetryDelaySeconds` so post-launch CDN
visibility tuning is a YAML edit), and (3) ONLY IF (2) succeeded,
upload `{"buildId":"..."}` to `buildInfo.json`. Any failure
throws, fails the job (`continueOnError: false`, the default),
which marks the overall AzDO build `PartiallySucceeded` and
causes the MissingBuildsTrigger Azure Function to skip indexing
this SHA entirely (whole-pipeline-indexing contract — operator
retries the whole build green).
Scope: this commit only adds the aspnetcore-side YAML on this
fork/branch. The three companion changes — dotnet-performance
`repoName` leaf-template PR, dotnet-performance-infra schema
PR, and dotnet-performance-infra `MissingBuildsTriggerAspNetCore`
Function App fork PR — are tracked separately and will be drafted
later.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replaces the inline .azure/pipelines/jobs/perf-build-upload-job.yml (deleted) with a single call to dotnet/performance's generalized upload-build-artifacts-jobs.yml dispatcher (the same template runtime's perf-build.yml uses). Companion change in dotnet/performance parameterizes the dispatcher + leaf templates with repoName and pool*. * Adds 'performance = internal/dotnet-performance' to resources.repositories. * Drops the headRetryCount/headRetryDelaySeconds parameters and the _aspNetCoreBcs* variables — the shared template handles account name, container, base URL, and subscription internally and runtime's pipeline has shipped without an explicit HEAD-verify step from the start. The MissingBuildsTrigger Function uses the AzDO build's overall Status == Succeeded as the sole atomicity gate. * Replaces the five inline perf-build-upload-job.yml@self template calls with a single upload-build-artifacts-jobs.yml@performance dispatch passing buildType: [aspnetcore_x64_linux, aspnetcore_arm64_linux, aspnetcore_x64_windows, aspnetcore_arm64_windows, aspnetcore_x86_windows], repoName: aspnetcore, and the dnceng internal 1ES Ubuntu pool (required because 1ES.Official mandates every job declare a pool). Net diff: 142 lines of perf-build.yml edited, perf-build-upload-job.yml (137 lines) deleted. Build stage unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two related changes:
1. Drop 1ES.Official extension. Per-commit perf-build artifacts go to a
private BCS cache (not signed / redistributed), so SDL / PoliCheck /
TSA scans add no security value here. Matches runtime's perf-build
which also runs as a plain (non-1ES) pipeline against the same
.NET Performance Azure service connection. Removes ~30 LOC of
1ES boilerplate (resources entry for 1esPipelines, extends block,
sdl config) and lets jobs inherit the pipeline's default pool
naturally.
2. Adopt runtime's three-stage shape:
RegisterBuild ─┐ parallel (dependsOn: [])
build ─┤ parallel
│
UploadArtifacts ┘ dependsOn: [build, RegisterBuild]
The new RegisterBuild stage delegates to
register-build-jobs.yml@performance (with repoName: aspnetcore)
which writes a tiny per-configKey buildInfo.json marker to
builds/aspnetcore/buildArtifacts/{sha}/{configKey}/buildInfo.json.
Matches the layout of the runtime perf-build so any future
dotnet-performance-infra dashboard / cleanup tool that reads
buildInfo.json works uniformly for both repos.
Companion change in dotnet/performance parameterizes
register-build-job.yml with repoName.
UploadArtifacts now depends on both Build and RegisterBuild with
condition: succeeded() — any stage failure sinks the overall AzDO
build to Failed and the MissingBuildsTrigger Function skips indexing
for the SHA (whole-pipeline-indexing contract preserved).
Pool params previously passed to the upload dispatcher are removed
(they only existed to satisfy 1ES.Official's pool requirement; without
1ES, jobs inherit the pipeline default pool).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ies reference
The shared dotnet/performance leaf template upload-build-artifacts-job.yml
hardcodes:
condition: eq(stageDependencies.Build.<job>.result, 'Succeeded')
In Azure Pipelines expression evaluation, stageDependencies.<stageName>
is case-sensitive (verified via Microsoft Learn / Azure DevOps docs).
The previous lowercase 'stage: build' here would silently resolve to a
null result, every per-artifact upload job's condition would evaluate
false, and NO artifacts would ever land in BCS — but the pipeline would
report as Succeeded since no job actually failed. A true silent failure.
Capitalizing to 'Build' matches dotnet/runtime's perf-build.yml convention
and the leaf template's hardcoded reference. dependsOn list for the
UploadArtifacts stage updated to match.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The three build jobs (Windows x64/x86/arm64, Linux x64, Linux arm64) each
carried a near-verbatim copy of the "find the runtime-pack nupkg, extract it
into the lowercase microsoft.aspnetcore.app.runtime.{rid}/Release/ layout, and
zip it" recipe in their afterBuild hook. Replace all three with a single,
parameterized .azure/pipelines/tools/pack-bcs-archives.ps1 (-Rids / -Format)
so there is exactly one copy of the find-and-zip logic to maintain.
Verified the script's nupkg assumptions against real published packages
(Microsoft.AspNetCore.App.Runtime.{linux-x64,win-x64} 8.0.28): the runtime
pack name, the runtimes/{rid}/lib managed-assembly layout, the win native dir,
and the absence of pdb/dbg all match what the recipe expects.
Invoke via pwsh (PowerShell 7), not Windows PowerShell 5.1: 5.1's
Compress-Archive writes backslash path separators into the zip, which a .NET
ZipFile extraction on a Linux crank agent treats as one flat filename, so
crank's FindDirectory("microsoft.aspnetcore.app.runtime.{rid}") would never
match. The prior inline Windows block used powershell: (5.1) and would have
produced corrupt Windows archives. The archive-root assertion now also runs
under pwsh and fails the build loudly if the contract is ever broken.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Owner
Author
|
Superseded: this perf-build pipeline is being moved into dotnet/performance for perf-team merge control. The performance-hosted version rebuilds aspnetcore from source (it cannot reuse aspnetcore's default-build.yml due to @self resolving to the host repo) and triggers per-commit via a repository-resource CI trigger on the dotnet-aspnetcore mirror. Closing in favor of that work. Branch retained for reference. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Draft — fork-staged for review.
What
Adds
.azure/pipelines/perf-build.yml— a new Azure DevOps pipeline that builds every commit onmainfor dotnet/aspnetcore (Windows multi-arch + Linux x64 + Linux arm64) and uploads the per-RIDMicrosoft.AspNetCore.App.Runtime.{rid}packs to the .NET Performance team's Build Cache Service (BCS) underbuilds/aspnetcore/buildArtifacts/{sha}/{configKey}/.... dotnet/crank then resolves Microsoft.AspNetCore.App runtime binaries by commit SHA for performance-regression bisection.Modeled directly on dotnet/runtime's
eng/pipelines/performance/perf-build.yml.Architecture — three-stage shape (mirrors runtime)
RegisterBuild — delegates to
register-build-jobs.yml@performance(companion Generalize BCS upload + register-build templates with repoName parameter dotnet/performance#5241), which writesbuilds/aspnetcore/buildArtifacts/{sha}/{configKey}/buildInfo.jsonfor each config in parallel with the actual build. Cheap, but matters: it gives this repo BCS-layout symmetry with runtime, so any future dotnet-performance-infra tooling that readsbuildInfo.jsonworks uniformly across both repos.build — three jobs (Windows multi-arch in one job following
ci.yml'sWindows_buildpattern; Linux x64; Linux arm64 cross-built from x64 host). Each invokes the existingdefault-build.yml@selftemplate with-pack -all --no-build-java -p:OnlyPackPlatformSpecificPackages=true, then a post-build step unzips the per-RIDMicrosoft.AspNetCore.App.Runtime.{rid}.{ver}.nupkginto amicrosoft.aspnetcore.app.runtime.{rid}/Release/runtimes/{rid}/...layout and zips/tars it as a pipeline artifact.UploadArtifacts (internal + IndividualCI/Manual only) — delegates to
upload-build-artifacts-jobs.yml@performance(also companion Angular 6 multi project SPA dotnet/aspnetcore#5241). One template call dispatches into five per-configKey upload jobs:The shared template handles the
az storage blob uploadagainstpvscmdupload/$web/builds/aspnetcore/buildArtifacts/{sha}/{configKey}/{file}.Not extending 1ES.Official
Unlike
ci.yml(which ships signed bits) orci-unofficial.yml(which validates PRs), this pipeline produces intermediate build artifacts that go to a private BCS cache — they aren't signed or redistributed. SDL / PoliCheck / TSA scans add no security value for that workload. Runtime's own perf-build runs as a plain non-1ES pipeline against the same.NET PerformanceAzure service connection and has done so for years; we follow the same pattern. This drops ~30 LOC of 1ES boilerplate (the1esPipelinesresource, theextends:block, thesdl:config) and lets jobs inherit the pipeline's default pool naturally.If dnceng/internal AzDO ever rejects a non-1ES pipeline in this project (unverified — we'd be the first non-1ES one in
.azure/pipelines/), the fallback is a one-line switch to1ES.Unofficial.PipelineTemplate.yml(used today byci-unofficial.ymlandidentitymodel-helix-matrix.yml).Atomicity
The MissingBuildsTrigger Azure Function in
internal/dotnet-performance-infraindexes only Azure DevOps builds with overallStatus == Succeeded. All stages run with the defaultcontinueOnError: false, so any RegisterBuild / build / UploadArtifacts failure sinks the overall build to Failed and the Function skips indexing for that SHA. Operator retries the whole build green. The atomic unit is the AzDO build, not the per-config job.Archive contract (load-bearing)
Per-RID archive root MUST be
microsoft.aspnetcore.app.runtime.{rid}(lowercase, matching the canonical name an extracted nupkg lands at). The future aspnetcore-overlay crank PR will hardcode this name in aFindDirectory(extractDir, $"microsoft.aspnetcore.app.runtime.{rid}")call mirroring crank PR 878's runtime overlay. Renaming the archive root breaks every future crank consumer.Archive contents:
configKey set (v1 lock)
aspnetcore_x64_linux,aspnetcore_arm64_linux,aspnetcore_x64_windows,aspnetcore_arm64_windows,aspnetcore_x86_windows. macOS + musl + Linux arm32 deferred to v2 (not current crank perf scenarios). Once uploaded, renaming requires storage-level migration — crank cacheslatestBuilds.jsonfor 1 hour.Sequencing / dependencies
register-build-jobs.yml@performanceandupload-build-artifacts-jobs.yml@performancethrough theinternal/dotnet-performanceAzDO mirror, which only updates after dotnet/performance/main advances.pvscmdupload/$web/builds/aspnetcore/buildArtifacts/{sha}/{configKey}/.internal/dotnet-performance-infraso MissingBuildsTrigger handles aspnetcore configKeys + the aspnetcore pipeline. After deploy,latestBuilds.jsonatbuilds/aspnetcore/latest/main/latestBuilds.jsonlands on the next 8h tick. End-to-end with crank.Out of scope (tracked, not done here)
PlatformToBcsConfigwith the aspnetcore mapping + add an aspnetcore overlay code path. Separate future crank PR.internal/dotnet-performance-infraindexer/schema changes. Separate future PR — needs (a) extending the dispatcher's known config-key list, (b) registering the new aspnetcore pipeline ID, and (c) resolving thebuilds/{repoName}/blob-path mystery on the indexer side.File-level changes
.azure/pipelines/perf-build.yml(three-stage, non-1ES).azure/pipelines/jobs/perf-build-upload-job.yml(functionality replaced by the dotnet/performance shared template)