From 898e7c02e7c17ac2bdbdbe66464a1cfcef2773ea Mon Sep 17 00:00:00 2001 From: Parker Bibus Date: Fri, 19 Jun 2026 11:30:44 -0700 Subject: [PATCH 01/12] Add aspnetcore perf-build pipeline (moved from dotnet/aspnetcore) Stand up an Azure DevOps perf-build pipeline hosted in dotnet/performance that builds every commit on dotnet/aspnetcore main from source, packs per-RID Microsoft.AspNetCore.App.Runtime archives, and uploads them to the Build Cache Service for dotnet/crank bisection. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/aspnetcore-perf-build-jobs.yml | 286 ++++++++++++++++++ eng/pipelines/aspnetcore-perf-build.yml | 177 +++++++++++ eng/pipelines/register-build-jobs.yml | 6 + .../templates/register-build-job.yml | 12 +- .../templates/upload-build-artifacts-job.yml | 14 +- eng/pipelines/tools/pack-bcs-archives.ps1 | 187 ++++++++++++ eng/pipelines/upload-build-artifacts-jobs.yml | 12 + 7 files changed, 691 insertions(+), 3 deletions(-) create mode 100644 eng/pipelines/aspnetcore-perf-build-jobs.yml create mode 100644 eng/pipelines/aspnetcore-perf-build.yml create mode 100644 eng/pipelines/tools/pack-bcs-archives.ps1 diff --git a/eng/pipelines/aspnetcore-perf-build-jobs.yml b/eng/pipelines/aspnetcore-perf-build-jobs.yml new file mode 100644 index 00000000000..28f50935ffa --- /dev/null +++ b/eng/pipelines/aspnetcore-perf-build-jobs.yml @@ -0,0 +1,286 @@ +# aspnetcore-perf-build-jobs.yml +# +# Authored build jobs for the ASP.NET Core perf-build pipeline (consumed by +# eng/pipelines/aspnetcore-perf-build.yml). These jobs are written from scratch +# here in dotnet/performance instead of reusing dotnet/aspnetcore's +# `.azure/pipelines/jobs/default-build.yml`, because that template has hard +# `@self` references (e.g. /eng/common/templates-official/job/job.yml@self) and +# `@self` always resolves to the ROOT pipeline repo (performance), which does not +# carry aspnetcore's eng/common layout/contract. So we stand up the build +# ourselves: pick the dnceng internal pool, check out the aspnetcore mirror at the +# triggering commit alongside this repo (for the pack script), run aspnetcore's +# own eng/build with lean perf args (PUBLIC feeds only -- no internal runtime +# download), pack the per-RID runtime nupkgs into the BCS archive layout via the +# moved eng/pipelines/tools/pack-bcs-archives.ps1, and publish one pipeline +# artifact per config. +# +# Job names (Windows_build / Linux_x64_build / Linux_arm64_build) are load-bearing: +# they must match the `dependencyJobName` values in +# eng/pipelines/upload-build-artifacts-jobs.yml's aspnetcore_* branches. +# +# PUBLIC-FEED rationale: aspnetcore's own ci-public.yml builds every public PR with +# `_InternalRuntimeDownloadArgs` empty, proving public feeds suffice for a from- +# source pack build. We therefore drop enable-internal-runtimes / get-delegation-sas +# / the dotnetbuilds-internal-read connection entirely and pass no +# -RuntimeSourceFeed args. + +parameters: + aspnetcoreRepoAlias: aspnetcore + performanceRepoAlias: self + # Per-config enablement booleans (forward-compat with the MissingBuildsTrigger + # PerConfiguration indexer). In v1's eager per-commit CI-trigger mode all five + # are true and all configs build; the booleans let a future Function queue a + # subset per config. + aspnetcore_x64_linux: true + aspnetcore_arm64_linux: true + aspnetcore_x64_windows: true + aspnetcore_arm64_windows: true + aspnetcore_x86_windows: true + +jobs: + +# =========================================================================== +# Windows multi-arch build job (x64 + x86 + arm64 in one agent) +# +# Mirrors aspnetcore ci-public.yml's Windows_build pattern: build.cmd is invoked +# x64-first WITH native (-nativeToolsOnMachine), then x86 and arm64 with +# -noBuildNative because they cross-pack against the x64 native bits. Each +# invocation emits its own per-RID Microsoft.AspNetCore.App.Runtime.win-{arch}.*.nupkg +# into artifacts/packages/Release/Shipping. The x64 build always runs when any +# Windows arch is enabled (it produces the native base the others reuse); pack + +# publish for each arch are individually gated by that arch's boolean. +# =========================================================================== +- ${{ if or(eq(parameters.aspnetcore_x64_windows, true), eq(parameters.aspnetcore_arm64_windows, true), eq(parameters.aspnetcore_x86_windows, true)) }}: + - job: Windows_build + displayName: 'Build: Windows x64/x86/arm64 (perf-build)' + timeoutInMinutes: 180 + pool: + name: $(DncEngInternalBuildPool) + demands: ImageOverride -equals windows.vs2026preview.scout.amd64 + variables: + - _AspNetCoreRoot: $(Agent.BuildDirectory)\s\aspnetcore + - _PackScript: $(Agent.BuildDirectory)\s\performance\eng\pipelines\tools\pack-bcs-archives.ps1 + - _ShippingDir: $(Agent.BuildDirectory)\s\aspnetcore\artifacts\packages\Release\Shipping + - _StagingRoot: $(Build.ArtifactStagingDirectory)\bcs + - MSBUILDUSESERVER: "1" + steps: + - checkout: ${{ parameters.aspnetcoreRepoAlias }} + path: s/aspnetcore + fetchDepth: 1 + fetchTags: false + clean: true + - checkout: ${{ parameters.performanceRepoAlias }} + path: s/performance + fetchDepth: 1 + fetchTags: false + clean: true + + # x64 -- native base build (always runs when any Windows arch is requested). + - script: >- + eng\build.cmd + -ci + -prepareMachine + -nativeToolsOnMachine + -Configuration Release + -arch x64 + -pack + -all + -noBuildJava + /p:OnlyPackPlatformSpecificPackages=true + $(_BuildArgs) + /bl:artifacts/log/Release/Build.x64.binlog + workingDirectory: $(_AspNetCoreRoot) + displayName: Build x64 + env: + MSBUILDUSESERVER: "1" + + - ${{ if eq(parameters.aspnetcore_x86_windows, true) }}: + - script: >- + eng\build.cmd + -ci + -prepareMachine + -Configuration Release + -arch x86 + -pack + -all + -noBuildJava + -noBuildNative + /p:OnlyPackPlatformSpecificPackages=true + $(_BuildArgs) + -ExcludeCIBinaryLog + workingDirectory: $(_AspNetCoreRoot) + displayName: Build x86 + env: + MSBUILDUSESERVER: "1" + + - ${{ if eq(parameters.aspnetcore_arm64_windows, true) }}: + - script: >- + eng\build.cmd + -ci + -prepareMachine + -Configuration Release + -arch arm64 + -pack + -noBuildJava + -noBuildNative + /p:OnlyPackPlatformSpecificPackages=true + $(_BuildArgs) + /bl:artifacts/log/Release/Build.arm64.binlog + workingDirectory: $(_AspNetCoreRoot) + displayName: Build ARM64 + env: + MSBUILDUSESERVER: "1" + + - ${{ if eq(parameters.aspnetcore_x64_windows, true) }}: + - pwsh: >- + & "$(_PackScript)" -Rids win-x64 -Format zip -ShippingDir "$(_ShippingDir)" -StagingRoot "$(_StagingRoot)" + displayName: 'Pack BCS archive (win-x64)' + - task: PublishPipelineArtifact@1 + displayName: 'Publish BuildArtifacts_windows_x64_Release_aspnetcore' + inputs: + targetPath: $(_StagingRoot)\BuildArtifacts_windows_x64_Release_aspnetcore + artifactName: BuildArtifacts_windows_x64_Release_aspnetcore + + - ${{ if eq(parameters.aspnetcore_x86_windows, true) }}: + - pwsh: >- + & "$(_PackScript)" -Rids win-x86 -Format zip -ShippingDir "$(_ShippingDir)" -StagingRoot "$(_StagingRoot)" + displayName: 'Pack BCS archive (win-x86)' + - task: PublishPipelineArtifact@1 + displayName: 'Publish BuildArtifacts_windows_x86_Release_aspnetcore' + inputs: + targetPath: $(_StagingRoot)\BuildArtifacts_windows_x86_Release_aspnetcore + artifactName: BuildArtifacts_windows_x86_Release_aspnetcore + + - ${{ if eq(parameters.aspnetcore_arm64_windows, true) }}: + - pwsh: >- + & "$(_PackScript)" -Rids win-arm64 -Format zip -ShippingDir "$(_ShippingDir)" -StagingRoot "$(_StagingRoot)" + displayName: 'Pack BCS archive (win-arm64)' + - task: PublishPipelineArtifact@1 + displayName: 'Publish BuildArtifacts_windows_arm64_Release_aspnetcore' + inputs: + targetPath: $(_StagingRoot)\BuildArtifacts_windows_arm64_Release_aspnetcore + artifactName: BuildArtifacts_windows_arm64_Release_aspnetcore + + - task: PublishPipelineArtifact@1 + displayName: 'Publish Windows build logs' + condition: always() + continueOnError: true + inputs: + targetPath: $(_AspNetCoreRoot)\artifacts\log + artifactName: Windows_perf_build_Logs_Attempt_$(System.JobAttempt) + +# =========================================================================== +# Linux x64 build job +# =========================================================================== +- ${{ if eq(parameters.aspnetcore_x64_linux, true) }}: + - job: Linux_x64_build + displayName: 'Build: Linux x64 (perf-build)' + timeoutInMinutes: 180 + pool: + name: $(DncEngInternalBuildPool) + demands: ImageOverride -equals 1es-ubuntu-2204 + variables: + - _AspNetCoreRoot: $(Agent.BuildDirectory)/s/aspnetcore + - _PackScript: $(Agent.BuildDirectory)/s/performance/eng/pipelines/tools/pack-bcs-archives.ps1 + - _ShippingDir: $(Agent.BuildDirectory)/s/aspnetcore/artifacts/packages/Release/Shipping + - _StagingRoot: $(Build.ArtifactStagingDirectory)/bcs + steps: + - checkout: ${{ parameters.aspnetcoreRepoAlias }} + path: s/aspnetcore + fetchDepth: 1 + fetchTags: false + clean: true + - checkout: ${{ parameters.performanceRepoAlias }} + path: s/performance + fetchDepth: 1 + fetchTags: false + clean: true + + - script: >- + eng/build.sh + --ci + --configuration Release + --arch x64 + --pack + --all + --no-build-java + -p:OnlyPackPlatformSpecificPackages=true + $(_BuildArgs) + /bl:artifacts/log/Release/Build.linux-x64.binlog + workingDirectory: $(_AspNetCoreRoot) + displayName: Build linux-x64 + + - pwsh: >- + & "$(_PackScript)" -Rids linux-x64 -Format targz -ShippingDir "$(_ShippingDir)" -StagingRoot "$(_StagingRoot)" + displayName: 'Pack BCS archive (linux-x64)' + - task: PublishPipelineArtifact@1 + displayName: 'Publish BuildArtifacts_linux_x64_Release_aspnetcore' + inputs: + targetPath: $(_StagingRoot)/BuildArtifacts_linux_x64_Release_aspnetcore + artifactName: BuildArtifacts_linux_x64_Release_aspnetcore + + - task: PublishPipelineArtifact@1 + displayName: 'Publish Linux x64 build logs' + condition: always() + continueOnError: true + inputs: + targetPath: $(_AspNetCoreRoot)/artifacts/log + artifactName: Linux_x64_perf_build_Logs_Attempt_$(System.JobAttempt) + +# =========================================================================== +# Linux arm64 build job (cross-build from the x64 host) +# =========================================================================== +- ${{ if eq(parameters.aspnetcore_arm64_linux, true) }}: + - job: Linux_arm64_build + displayName: 'Build: Linux ARM64 (perf-build)' + timeoutInMinutes: 180 + pool: + name: $(DncEngInternalBuildPool) + demands: ImageOverride -equals 1es-ubuntu-2204 + variables: + - _AspNetCoreRoot: $(Agent.BuildDirectory)/s/aspnetcore + - _PackScript: $(Agent.BuildDirectory)/s/performance/eng/pipelines/tools/pack-bcs-archives.ps1 + - _ShippingDir: $(Agent.BuildDirectory)/s/aspnetcore/artifacts/packages/Release/Shipping + - _StagingRoot: $(Build.ArtifactStagingDirectory)/bcs + steps: + - checkout: ${{ parameters.aspnetcoreRepoAlias }} + path: s/aspnetcore + fetchDepth: 1 + fetchTags: false + clean: true + - checkout: ${{ parameters.performanceRepoAlias }} + path: s/performance + fetchDepth: 1 + fetchTags: false + clean: true + + - script: >- + eng/build.sh + --ci + --configuration Release + --arch arm64 + --pack + --all + --no-build-java + -p:OnlyPackPlatformSpecificPackages=true + $(_BuildArgs) + /bl:artifacts/log/Release/Build.linux-arm64.binlog + workingDirectory: $(_AspNetCoreRoot) + displayName: Build linux-arm64 + + - pwsh: >- + & "$(_PackScript)" -Rids linux-arm64 -Format targz -ShippingDir "$(_ShippingDir)" -StagingRoot "$(_StagingRoot)" + displayName: 'Pack BCS archive (linux-arm64)' + - task: PublishPipelineArtifact@1 + displayName: 'Publish BuildArtifacts_linux_arm64_Release_aspnetcore' + inputs: + targetPath: $(_StagingRoot)/BuildArtifacts_linux_arm64_Release_aspnetcore + artifactName: BuildArtifacts_linux_arm64_Release_aspnetcore + + - task: PublishPipelineArtifact@1 + displayName: 'Publish Linux arm64 build logs' + condition: always() + continueOnError: true + inputs: + targetPath: $(_AspNetCoreRoot)/artifacts/log + artifactName: Linux_arm64_perf_build_Logs_Attempt_$(System.JobAttempt) diff --git a/eng/pipelines/aspnetcore-perf-build.yml b/eng/pipelines/aspnetcore-perf-build.yml new file mode 100644 index 00000000000..e5a02accc79 --- /dev/null +++ b/eng/pipelines/aspnetcore-perf-build.yml @@ -0,0 +1,177 @@ +# +# aspnetcore-perf-build.yml +# +# Azure DevOps pipeline (hosted in dotnet/performance) that builds every commit +# on dotnet/aspnetcore `main` from source, packs per-RID +# Microsoft.AspNetCore.App.Runtime archives, and uploads them to the Build Cache +# Service (BCS @ pvscmdupload) so dotnet/crank can resolve ASP.NET Core runtime +# binaries by commit SHA for performance-regression bisection. +# +# WHY THIS LIVES HERE (and not in dotnet/aspnetcore): moving it into +# dotnet/performance gives the perf team full merge control. The previous +# aspnetcore-hosted version reused aspnetcore's `default-build.yml`, which cannot +# be consumed cross-repo (its `@self` references resolve to the ROOT pipeline +# repo = performance). So the build jobs are re-authored from source in +# eng/pipelines/aspnetcore-perf-build-jobs.yml. +# +# HOW PER-COMMIT FIRING WORKS: the aspnetcore AzDO mirror +# (internal/dotnet-aspnetcore) is declared as a repository resource with a CI +# `trigger` on main. A push to aspnetcore main fires this pipeline +# (Build.Reason == ResourceTrigger). The build jobs check out that resource at +# the triggering commit. +# +# SHA INVERSION (load-bearing): because the pipeline is hosted in performance, +# `Build.SourceVersion` is the PERFORMANCE commit, not the aspnetcore one. The +# correct BCS {sha} is the triggering aspnetcore commit, exposed as +# `$(resources.repositories.aspnetcore.version)`. We pass it as `sha:` to the +# shared register/upload templates (which default `sha` to $(Build.SourceVersion) +# for the runtime perf-build that is hosted in dotnet/runtime). +# +# PUBLIC FEEDS ONLY: like aspnetcore's ci-public.yml, the from-source pack build +# uses public feeds (no internal runtime download, no dotnetbuilds-internal-read +# connection). Only the BCS upload uses the proven `.NET Performance` connection. +# +# NOT 1ES: per-commit perf artifacts go to a private BCS cache (unsigned, not +# redistributed), so we do not extend 1ES.Official -- matching dotnet/runtime's +# perf-build. +# +# ATOMICITY: continueOnError:false everywhere; any failure sinks the build to +# Failed and the MissingBuildsTrigger Function skips indexing that SHA. +# +# BCS output layout: +# builds/aspnetcore/buildArtifacts/{sha}/{configKey}/buildInfo.json +# builds/aspnetcore/buildArtifacts/{sha}/{configKey}/{archiveFile} +# + +parameters: +# Per-config enablement booleans (default true). Surfaced as pipeline parameters +# so they appear in the build's templateParameters for the dotnet-performance-infra +# MissingBuildsTrigger PerConfiguration indexer to count expected configs. In v1's +# eager per-commit mode all five are true; they are forward-compat for a future +# Function that queues a subset per config. +- name: aspnetcore_x64_linux + displayName: 'Build aspnetcore_x64_linux' + type: boolean + default: true +- name: aspnetcore_arm64_linux + displayName: 'Build aspnetcore_arm64_linux' + type: boolean + default: true +- name: aspnetcore_x64_windows + displayName: 'Build aspnetcore_x64_windows' + type: boolean + default: true +- name: aspnetcore_arm64_windows + displayName: 'Build aspnetcore_arm64_windows' + type: boolean + default: true +- name: aspnetcore_x86_windows + displayName: 'Build aspnetcore_x86_windows' + type: boolean + default: true + +# Self (performance) pushes must not fire this pipeline; only the aspnetcore +# resource trigger (below) does. +trigger: none +pr: none + +variables: +- name: _TeamName + value: AspNetCore +# Lean build args mirrored from aspnetcore ci-public.yml. Deliberately omit +# signing / installers / helix / publishing and any internal runtime download +# (public feeds only) -- none contribute to the per-RID runtime pack. +- name: _BuildArgs + value: /p:TeamName=$(_TeamName) + /p:OfficialBuildId=$(Build.BuildNumber) + /p:SkipTestBuild=true +- template: /eng/common/templates/variables/pool-providers.yml + +resources: + repositories: + # aspnetcore AzDO mirror. The CI trigger on main is what fires this pipeline + # per aspnetcore commit; the build jobs check it out at the triggering commit, + # and $(resources.repositories.aspnetcore.version) is the BCS {sha}. + - repository: aspnetcore + type: git + name: internal/dotnet-aspnetcore + ref: refs/heads/main + trigger: + batch: false + branches: + include: + - main + +stages: + +# ============================================================================ +# RegisterBuild -- writes a per-configKey buildInfo.json marker to BCS at +# $web/builds/aspnetcore/buildArtifacts/{sha}/{configKey}/buildInfo.json +# Runs in parallel with Build (dependsOn: []). Gated to internal + +# (ResourceTrigger | Manual): ResourceTrigger is the steady-state per-commit +# flow, Manual covers seed/runbook queues. +# ============================================================================ +- ${{ if and(ne(variables['System.TeamProject'], 'public'), or(eq(variables['Build.Reason'], 'ResourceTrigger'), eq(variables['Build.Reason'], 'Manual'))) }}: + - stage: RegisterBuild + displayName: 'Register Build' + dependsOn: [] + jobs: + - template: /eng/pipelines/register-build-jobs.yml + parameters: + repoName: aspnetcore + sha: $(resources.repositories.aspnetcore.version) + buildType: + - ${{ if eq(parameters.aspnetcore_x64_linux, true) }}: + - aspnetcore_x64_linux + - ${{ if eq(parameters.aspnetcore_arm64_linux, true) }}: + - aspnetcore_arm64_linux + - ${{ if eq(parameters.aspnetcore_x64_windows, true) }}: + - aspnetcore_x64_windows + - ${{ if eq(parameters.aspnetcore_arm64_windows, true) }}: + - aspnetcore_arm64_windows + - ${{ if eq(parameters.aspnetcore_x86_windows, true) }}: + - aspnetcore_x86_windows + +- stage: Build + displayName: Build + dependsOn: [] + jobs: + - template: /eng/pipelines/aspnetcore-perf-build-jobs.yml + parameters: + aspnetcoreRepoAlias: aspnetcore + performanceRepoAlias: self + aspnetcore_x64_linux: ${{ parameters.aspnetcore_x64_linux }} + aspnetcore_arm64_linux: ${{ parameters.aspnetcore_arm64_linux }} + aspnetcore_x64_windows: ${{ parameters.aspnetcore_x64_windows }} + aspnetcore_arm64_windows: ${{ parameters.aspnetcore_arm64_windows }} + aspnetcore_x86_windows: ${{ parameters.aspnetcore_x86_windows }} + +# ============================================================================ +# UploadArtifacts -- downloads each Build-stage pipeline artifact and uploads it +# to $web/builds/aspnetcore/buildArtifacts/{sha}/{configKey}/{file}. Depends on +# Build + RegisterBuild; condition succeeded() so any failure skips indexing. +# Same internal + (ResourceTrigger | Manual) gate as RegisterBuild. +# ============================================================================ +- ${{ if and(ne(variables['System.TeamProject'], 'public'), or(eq(variables['Build.Reason'], 'ResourceTrigger'), eq(variables['Build.Reason'], 'Manual'))) }}: + - stage: UploadArtifacts + displayName: 'Upload Artifacts to BCS' + dependsOn: + - Build + - RegisterBuild + condition: succeeded() + jobs: + - template: /eng/pipelines/upload-build-artifacts-jobs.yml + parameters: + repoName: aspnetcore + sha: $(resources.repositories.aspnetcore.version) + buildType: + - ${{ if eq(parameters.aspnetcore_x64_linux, true) }}: + - aspnetcore_x64_linux + - ${{ if eq(parameters.aspnetcore_arm64_linux, true) }}: + - aspnetcore_arm64_linux + - ${{ if eq(parameters.aspnetcore_x64_windows, true) }}: + - aspnetcore_x64_windows + - ${{ if eq(parameters.aspnetcore_arm64_windows, true) }}: + - aspnetcore_arm64_windows + - ${{ if eq(parameters.aspnetcore_x86_windows, true) }}: + - aspnetcore_x86_windows diff --git a/eng/pipelines/register-build-jobs.yml b/eng/pipelines/register-build-jobs.yml index 7b3aa53a1bd..e998dd3d9b9 100644 --- a/eng/pipelines/register-build-jobs.yml +++ b/eng/pipelines/register-build-jobs.yml @@ -10,6 +10,11 @@ parameters: # pipeline; callers from other tenants (e.g. dotnet/aspnetcore) pass # repoName explicitly. repoName: 'runtime' + # sha is forwarded to each register-build-job so the buildInfo.json marker + # lands under builds/{repoName}/buildArtifacts/{sha}/... Defaults to the self + # repo commit; resource-built tenants (e.g. aspnetcore) pass the resource's + # commit, e.g. sha: $(resources.repositories.aspnetcore.version). + sha: '$(Build.SourceVersion)' jobs: - ${{ each type in parameters.buildType }}: @@ -18,3 +23,4 @@ jobs: buildType: ${{ type }} buildId: $(Build.BuildId) repoName: ${{ parameters.repoName }} + sha: ${{ parameters.sha }} diff --git a/eng/pipelines/templates/register-build-job.yml b/eng/pipelines/templates/register-build-job.yml index 7a275f64f2b..d397cbe22f0 100644 --- a/eng/pipelines/templates/register-build-job.yml +++ b/eng/pipelines/templates/register-build-job.yml @@ -7,6 +7,16 @@ parameters: # callers from other tenants (e.g. dotnet/aspnetcore) pass repoName # explicitly. repoName: 'runtime' + # sha controls the {sha} segment of the BCS blob path + # (builds/{repoName}/buildArtifacts/{sha}/...). Defaults to the self repo's + # triggering commit ($(Build.SourceVersion)) for back-compat with the runtime + # perf-build pipeline (which is hosted in dotnet/runtime, so self == the repo + # being built). Callers where the built repo is a *resource* rather than self + # (e.g. the dotnet/aspnetcore perf-build hosted in dotnet/performance) must + # pass the resource's commit explicitly, e.g. + # sha: $(resources.repositories.aspnetcore.version), because Build.SourceVersion + # would otherwise resolve to the performance pipeline commit. + sha: '$(Build.SourceVersion)' jobs: - job: RegisterBuild_${{ parameters.buildType }} @@ -20,4 +30,4 @@ jobs: scriptType: 'pscore' scriptLocation: 'inlineScript' inlineScript: | - az storage blob upload --auth-mode login --account-name pvscmdupload --container-name '$web' --data '{"buildId":"${{ parameters.buildId }}"}' --name "builds/${{ parameters.repoName }}/buildArtifacts/${{ variables['Build.SourceVersion'] }}/${{ parameters.buildType }}/buildInfo.json" --overwrite true + az storage blob upload --auth-mode login --account-name pvscmdupload --container-name '$web' --data '{"buildId":"${{ parameters.buildId }}"}' --name "builds/${{ parameters.repoName }}/buildArtifacts/${{ parameters.sha }}/${{ parameters.buildType }}/buildInfo.json" --overwrite true diff --git a/eng/pipelines/templates/upload-build-artifacts-job.yml b/eng/pipelines/templates/upload-build-artifacts-job.yml index 5da08e5553d..fd81e2d74d3 100644 --- a/eng/pipelines/templates/upload-build-artifacts-job.yml +++ b/eng/pipelines/templates/upload-build-artifacts-job.yml @@ -8,6 +8,16 @@ parameters: # Callers building a different repo (e.g. dotnet/aspnetcore) pass repoName explicitly # so that the upload lands at a tenant-specific path the indexer/crank then read. repoName: 'runtime' + # sha controls the {sha} segment of the BCS blob path: + # builds/{repoName}/buildArtifacts/{sha}/{buildType}/{file} + # Defaults to the self repo's triggering commit ($(Build.SourceVersion)) for + # back-compat with the runtime perf-build pipeline (hosted in dotnet/runtime, + # so self == the repo being built). Callers where the built repo is a + # *resource* rather than self (e.g. dotnet/aspnetcore perf-build hosted in + # dotnet/performance) must pass the resource's commit explicitly, e.g. + # sha: $(resources.repositories.aspnetcore.version), because Build.SourceVersion + # would otherwise resolve to the performance pipeline commit. + sha: '$(Build.SourceVersion)' jobs: - ${{ each artifact in parameters.artifacts }}: @@ -20,7 +30,7 @@ jobs: displayName: Download Build Artifact ${{ artifact.artifactName }} artifact: ${{ artifact.artifactName }} - ${{ each fileName in artifact.files }}: - - script: echo "pvscmdupload/\$web/builds/${{ parameters.repoName }}/buildArtifacts/${{ variables['Build.SourceVersion'] }}/${{ parameters.buildType }}/${{ fileName }}" + - script: echo "pvscmdupload/\$web/builds/${{ parameters.repoName }}/buildArtifacts/${{ parameters.sha }}/${{ parameters.buildType }}/${{ fileName }}" displayName: 'Artifact upload path' - task: AzureCLI@2 displayName: 'Upload ${{ fileName }} file' @@ -29,4 +39,4 @@ jobs: scriptType: 'pscore' scriptLocation: 'inlineScript' inlineScript: | - az storage blob upload --auth-mode login --account-name pvscmdupload --container-name '$web' --file "$(Pipeline.Workspace)/${{ artifact.artifactName }}/${{ fileName }}" --name "builds/${{ parameters.repoName }}/buildArtifacts/${{ variables['Build.SourceVersion'] }}/${{ parameters.buildType }}/${{ fileName }}" --overwrite true + az storage blob upload --auth-mode login --account-name pvscmdupload --container-name '$web' --file "$(Pipeline.Workspace)/${{ artifact.artifactName }}/${{ fileName }}" --name "builds/${{ parameters.repoName }}/buildArtifacts/${{ parameters.sha }}/${{ parameters.buildType }}/${{ fileName }}" --overwrite true diff --git a/eng/pipelines/tools/pack-bcs-archives.ps1 b/eng/pipelines/tools/pack-bcs-archives.ps1 new file mode 100644 index 00000000000..6a943bf96fc --- /dev/null +++ b/eng/pipelines/tools/pack-bcs-archives.ps1 @@ -0,0 +1,187 @@ +<# +.SYNOPSIS + Packs per-RID Microsoft.AspNetCore.App runtime packs into Build Cache + Service (BCS) archives for the perf-build pipeline. + +.DESCRIPTION + This is the single, canonical "find the runtime-pack nupkg and zip it into + the BCS archive layout" recipe used by every job in perf-build.yml (Windows + x64/x86/arm64, Linux x64, Linux arm64). The build jobs invoke it from their + `afterBuild` hook with the RID(s) they produced and the archive format that + matches their platform convention (.zip on Windows, .tar.gz on Linux). + + For each RID it: + 1. Locates artifacts/packages/Release/Shipping/Microsoft.AspNetCore.App.Runtime.{rid}.*.nupkg + (excluding the *.symbols.nupkg). + 2. Extracts it into the LOWERCASE archive root + microsoft.aspnetcore.app.runtime.{rid}/Release/ -- this directory name + is a load-bearing contract for the future dotnet/crank aspnetcore + overlay path, which mirrors BuildCacheClient's + FindDirectory(extractDir, "microsoft.aspnetcore.app.runtime.{rid}"). + 3. Validates the managed Microsoft.AspNetCore.*.dll assemblies landed under + runtimes/{rid}/lib and strips any stray *.pdb / *.dbg files. + 4. Compresses the lowercase root directory into the per-config archive. + 5. Asserts the archive's single root entry matches the contract. + +.PARAMETER Rids + One or more .NET RIDs to pack (e.g. win-x64, win-x86, win-arm64, linux-x64, + linux-arm64). os/arch and the BCS naming are derived from each RID. + +.PARAMETER Format + Archive format: 'zip' (Windows convention) or 'targz' (Linux convention). + +.PARAMETER ShippingDir + Directory containing the runtime-pack nupkgs. Defaults to + $(Build.SourcesDirectory)/artifacts/packages/Release/Shipping so the + pipeline can call this with no path arguments; override it for local runs. + +.PARAMETER StagingRoot + Directory under which the per-config archive staging folders are created. + Defaults to $(Build.ArtifactStagingDirectory)/bcs. + +.EXAMPLE + ./pack-bcs-archives.ps1 -Rids win-x64,win-x86,win-arm64 -Format zip + +.EXAMPLE + ./pack-bcs-archives.ps1 -Rids linux-x64 -Format targz +#> +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [string[]]$Rids, + + [Parameter(Mandatory = $true)] + [ValidateSet('zip', 'targz')] + [string]$Format, + + [string]$ShippingDir = (Join-Path $env:BUILD_SOURCESDIRECTORY 'artifacts/packages/Release/Shipping'), + + [string]$StagingRoot = (Join-Path $env:BUILD_ARTIFACTSTAGINGDIRECTORY 'bcs') +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version Latest +Add-Type -AssemblyName System.IO.Compression.FileSystem + +function Get-RidParts { + param([string]$Rid) + + $dash = $Rid.IndexOf('-') + if ($dash -lt 1) { + throw "RID '$Rid' is not in the expected '-' form." + } + + $osToken = $Rid.Substring(0, $dash) + $arch = $Rid.Substring($dash + 1) + $os = switch ($osToken) { + 'win' { 'windows' } + 'linux' { 'linux' } + 'osx' { 'osx' } + default { throw "Unsupported RID os token '$osToken' in RID '$Rid'." } + } + + return [pscustomobject]@{ Os = $os; Arch = $arch } +} + +if (-not (Test-Path $ShippingDir)) { + throw "Shipping directory '$ShippingDir' does not exist." +} + +New-Item -ItemType Directory -Force -Path $StagingRoot | Out-Null + +foreach ($rid in $Rids) { + $parts = Get-RidParts -Rid $rid + $os = $parts.Os + $arch = $parts.Arch + $configKey = "aspnetcore_${arch}_${os}" + $artifactName = "BuildArtifacts_${os}_${arch}_Release_aspnetcore" + $ext = if ($Format -eq 'zip') { 'zip' } else { 'tar.gz' } + $archiveFile = "${artifactName}.${ext}" + + Write-Host '' + Write-Host "=== Packing $configKey (rid=$rid, format=$Format) ===" + + $pattern = "Microsoft.AspNetCore.App.Runtime.$rid.*.nupkg" + $nupkg = Get-ChildItem -Path $ShippingDir -Filter $pattern -File -ErrorAction SilentlyContinue | + Where-Object { $_.Name -notmatch '\.symbols\.nupkg$' } | + Sort-Object Name | + Select-Object -First 1 + if (-not $nupkg) { + throw "Could not find runtime pack nupkg matching '$pattern' under '$ShippingDir'." + } + Write-Host "Found nupkg: $($nupkg.FullName)" + + # microsoft.aspnetcore.app.runtime.{rid}/Release/ <-- archive root (lowercase!) + $stageDir = Join-Path $StagingRoot $artifactName + $payloadDir = Join-Path $stageDir "microsoft.aspnetcore.app.runtime.$rid" + $releaseDir = Join-Path $payloadDir 'Release' + if (Test-Path $payloadDir) { + Remove-Item -LiteralPath $payloadDir -Recurse -Force + } + New-Item -ItemType Directory -Force -Path $releaseDir | Out-Null + + # Extract the nupkg (it's a zip) into Release/. Using the .NET API keeps the + # extraction identical on Windows and Linux agents (no unzip/Expand-Archive split). + [System.IO.Compression.ZipFile]::ExtractToDirectory($nupkg.FullName, $releaseDir) + + $runtimesDir = Join-Path $releaseDir "runtimes/$rid" + if (-not (Test-Path $runtimesDir)) { + throw "Extracted runtime pack is missing expected directory '$runtimesDir'." + } + + # Sanity-check that managed assemblies landed where we expect. + $libMatches = @(Get-ChildItem -Path (Join-Path $runtimesDir 'lib') -Recurse -Filter 'Microsoft.AspNetCore.*.dll' -ErrorAction SilentlyContinue) + if ($libMatches.Count -eq 0) { + throw "Extracted runtime pack for $rid is missing managed Microsoft.AspNetCore.*.dll under 'runtimes/$rid/lib'." + } + Write-Host "Validated managed lib dir contains $($libMatches.Count) Microsoft.AspNetCore.*.dll(s)." + + # Defensive: strip debug-symbol files that occasionally tag along. + Get-ChildItem -Path $runtimesDir -Recurse -Include '*.pdb', '*.dbg' -ErrorAction SilentlyContinue | + ForEach-Object { Remove-Item -LiteralPath $_.FullName -Force } + + $archivePath = Join-Path $stageDir $archiveFile + if (Test-Path $archivePath) { + Remove-Item -LiteralPath $archivePath -Force + } + + $expectedRoot = "microsoft.aspnetcore.app.runtime.$rid/" + + if ($Format -eq 'zip') { + # Zip the lowercase root directory so the archive contents start with + # `microsoft.aspnetcore.app.runtime.{rid}/...`. + Compress-Archive -Path $payloadDir -DestinationPath $archivePath -CompressionLevel Optimal -Force + + # Assert the archive root matches the load-bearing contract. + $zip = [System.IO.Compression.ZipFile]::OpenRead($archivePath) + try { + $rootEntries = @($zip.Entries | ForEach-Object { ($_.FullName -split '/')[0] } | Sort-Object -Unique) + if ($rootEntries.Count -ne 1 -or "$($rootEntries[0])/" -ne $expectedRoot) { + throw "Archive '$archivePath' has root entries [$($rootEntries -join ', ')] but expected exactly '$expectedRoot' (load-bearing contract for future crank PR)." + } + } + finally { + $zip.Dispose() + } + } + else { + # Tar the lowercase root directory (so the archive contents start with + # `microsoft.aspnetcore.app.runtime.{rid}/...`). + & tar -czf $archivePath -C $stageDir "microsoft.aspnetcore.app.runtime.$rid" + if ($LASTEXITCODE -ne 0) { + throw "tar of '$payloadDir' failed (exit $LASTEXITCODE)." + } + + # Assert the archive root matches the load-bearing contract. + $firstEntry = (& tar -tzf $archivePath | Select-Object -First 1) + if (-not $firstEntry.StartsWith($expectedRoot)) { + throw "Archive '$archivePath' first entry '$firstEntry' does not start with '$expectedRoot' (load-bearing contract for future crank PR)." + } + } + + # Drop the working payload directory so only the archive ends up in the published pipeline artifact. + Remove-Item -LiteralPath $payloadDir -Recurse -Force + + $size = (Get-Item $archivePath).Length + Write-Host "Produced archive: $archivePath ($size bytes)" +} diff --git a/eng/pipelines/upload-build-artifacts-jobs.yml b/eng/pipelines/upload-build-artifacts-jobs.yml index b917db9457a..1979a48a8aa 100644 --- a/eng/pipelines/upload-build-artifacts-jobs.yml +++ b/eng/pipelines/upload-build-artifacts-jobs.yml @@ -7,6 +7,13 @@ parameters: # back-compat with the existing dotnet/runtime perf-build pipeline; callers from # other tenants (e.g. dotnet/aspnetcore) pass repoName explicitly. repoName: 'runtime' + # sha controls the {sha} segment of the BCS blob path. Defaults to the self + # repo commit ($(Build.SourceVersion)) for back-compat with the runtime + # perf-build pipeline. Resource-built tenants (e.g. aspnetcore, whose source + # is a repository resource rather than self) pass the resource's commit, e.g. + # sha: $(resources.repositories.aspnetcore.version). Runtime config branches + # below omit sha and inherit the leaf template's $(Build.SourceVersion) default. + sha: '$(Build.SourceVersion)' jobs: - ${{ if containsValue(parameters.buildType, 'coreclr_arm64_linux') }}: @@ -174,6 +181,7 @@ jobs: buildType: 'aspnetcore_x64_linux' dependencyJobName: Linux_x64_build repoName: ${{ parameters.repoName }} + sha: ${{ parameters.sha }} artifacts: - artifactName: 'BuildArtifacts_linux_x64_Release_aspnetcore' files: [ 'BuildArtifacts_linux_x64_Release_aspnetcore.tar.gz' ] @@ -184,6 +192,7 @@ jobs: buildType: 'aspnetcore_arm64_linux' dependencyJobName: Linux_arm64_build repoName: ${{ parameters.repoName }} + sha: ${{ parameters.sha }} artifacts: - artifactName: 'BuildArtifacts_linux_arm64_Release_aspnetcore' files: [ 'BuildArtifacts_linux_arm64_Release_aspnetcore.tar.gz' ] @@ -194,6 +203,7 @@ jobs: buildType: 'aspnetcore_x64_windows' dependencyJobName: Windows_build repoName: ${{ parameters.repoName }} + sha: ${{ parameters.sha }} artifacts: - artifactName: 'BuildArtifacts_windows_x64_Release_aspnetcore' files: [ 'BuildArtifacts_windows_x64_Release_aspnetcore.zip' ] @@ -204,6 +214,7 @@ jobs: buildType: 'aspnetcore_arm64_windows' dependencyJobName: Windows_build repoName: ${{ parameters.repoName }} + sha: ${{ parameters.sha }} artifacts: - artifactName: 'BuildArtifacts_windows_arm64_Release_aspnetcore' files: [ 'BuildArtifacts_windows_arm64_Release_aspnetcore.zip' ] @@ -214,6 +225,7 @@ jobs: buildType: 'aspnetcore_x86_windows' dependencyJobName: Windows_build repoName: ${{ parameters.repoName }} + sha: ${{ parameters.sha }} artifacts: - artifactName: 'BuildArtifacts_windows_x86_Release_aspnetcore' files: [ 'BuildArtifacts_windows_x86_Release_aspnetcore.zip' ] From 657cd3dc87d17ac2f358e40e9b300f239da3d156 Mon Sep 17 00:00:00 2001 From: Parker Bibus Date: Fri, 19 Jun 2026 12:00:41 -0700 Subject: [PATCH 02/12] Tag perf-build with aspnetcore-sha for infra indexer correlation Add an early, unconditional RegisterBuild job that tags the run with aspnetcore-sha:<40-char sha> (the aspnetcore commit from the repo-resource version macro). The build record's sourceVersion is the performance commit, so this tag is the dotnet-performance-infra indexer's sole aspnetcore-sha signal. Additive; the BCS {sha} override and buildInfo.json are unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/aspnetcore-perf-build.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/eng/pipelines/aspnetcore-perf-build.yml b/eng/pipelines/aspnetcore-perf-build.yml index e5a02accc79..75519150975 100644 --- a/eng/pipelines/aspnetcore-perf-build.yml +++ b/eng/pipelines/aspnetcore-perf-build.yml @@ -110,12 +110,37 @@ stages: # Runs in parallel with Build (dependsOn: []). Gated to internal + # (ResourceTrigger | Manual): ResourceTrigger is the steady-state per-commit # flow, Manual covers seed/runbook queues. +# +# The first job tags the build with `aspnetcore-sha:<40-char sha>`. Because the +# pipeline is hosted in performance, the build record's sourceVersion is the +# PERFORMANCE commit, so the dotnet-performance-infra indexer cannot correlate a +# build to its aspnetcore commit via sourceVersion. This tag is its SOLE +# aspnetcore-sha signal on the build record. It is additive and independent of +# the BCS {sha} path override (which stays $(resources.repositories.aspnetcore.version)). +# Added once, early, and unconditionally so it is present even if later stages fail. # ============================================================================ - ${{ if and(ne(variables['System.TeamProject'], 'public'), or(eq(variables['Build.Reason'], 'ResourceTrigger'), eq(variables['Build.Reason'], 'Manual'))) }}: - stage: RegisterBuild displayName: 'Register Build' dependsOn: [] jobs: + - job: TagBuild + displayName: 'Tag build with aspnetcore sha' + pool: + name: $(DncEngInternalBuildPool) + demands: ImageOverride -equals 1es-ubuntu-2204 + steps: + - checkout: none + # $(resources.repositories.aspnetcore.version) is the AzDO runtime macro for + # the triggering aspnetcore commit -- the same value passed as `sha:` to the + # register/upload templates below. AzDO expands the macro before pwsh runs, so + # $resolvedSha holds the literal 40-char commit sha. The echo lets us eyeball + # the resolved value in the first real run. + - pwsh: | + $resolvedSha = "$(resources.repositories.aspnetcore.version)" + Write-Host "Resolved aspnetcore commit sha: $resolvedSha" + Write-Host "##vso[build.addbuildtag]aspnetcore-sha:$resolvedSha" + displayName: 'Add aspnetcore-sha build tag' - template: /eng/pipelines/register-build-jobs.yml parameters: repoName: aspnetcore From ddfcf04476099456ade810c4a0576c41b7d09ddb Mon Sep 17 00:00:00 2001 From: Parker Bibus Date: Fri, 19 Jun 2026 12:11:36 -0700 Subject: [PATCH 03/12] Drop aspnetcore-sha build tag (Option B: indexer reads repo-resource version) The dotnet-performance-infra indexer derives the aspnetcore commit directly from the build's resources.repositories.aspnetcore.version via the runs API, so the dedicated TagBuild job is redundant. The BCS {sha} path override and buildInfo.json are unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/aspnetcore-perf-build.yml | 29 +++++-------------------- 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/eng/pipelines/aspnetcore-perf-build.yml b/eng/pipelines/aspnetcore-perf-build.yml index 75519150975..88e67db79e4 100644 --- a/eng/pipelines/aspnetcore-perf-build.yml +++ b/eng/pipelines/aspnetcore-perf-build.yml @@ -111,36 +111,17 @@ stages: # (ResourceTrigger | Manual): ResourceTrigger is the steady-state per-commit # flow, Manual covers seed/runbook queues. # -# The first job tags the build with `aspnetcore-sha:<40-char sha>`. Because the -# pipeline is hosted in performance, the build record's sourceVersion is the -# PERFORMANCE commit, so the dotnet-performance-infra indexer cannot correlate a -# build to its aspnetcore commit via sourceVersion. This tag is its SOLE -# aspnetcore-sha signal on the build record. It is additive and independent of -# the BCS {sha} path override (which stays $(resources.repositories.aspnetcore.version)). -# Added once, early, and unconditionally so it is present even if later stages fail. +# The aspnetcore commit this build corresponds to is NOT tagged on the run: the +# dotnet-performance-infra indexer reads it directly from the build's +# `resources.repositories.aspnetcore.version` via the runs API (intrinsic to the +# build carrying the aspnetcore repo resource). The BCS {sha} path override below +# still uses that same value. # ============================================================================ - ${{ if and(ne(variables['System.TeamProject'], 'public'), or(eq(variables['Build.Reason'], 'ResourceTrigger'), eq(variables['Build.Reason'], 'Manual'))) }}: - stage: RegisterBuild displayName: 'Register Build' dependsOn: [] jobs: - - job: TagBuild - displayName: 'Tag build with aspnetcore sha' - pool: - name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals 1es-ubuntu-2204 - steps: - - checkout: none - # $(resources.repositories.aspnetcore.version) is the AzDO runtime macro for - # the triggering aspnetcore commit -- the same value passed as `sha:` to the - # register/upload templates below. AzDO expands the macro before pwsh runs, so - # $resolvedSha holds the literal 40-char commit sha. The echo lets us eyeball - # the resolved value in the first real run. - - pwsh: | - $resolvedSha = "$(resources.repositories.aspnetcore.version)" - Write-Host "Resolved aspnetcore commit sha: $resolvedSha" - Write-Host "##vso[build.addbuildtag]aspnetcore-sha:$resolvedSha" - displayName: 'Add aspnetcore-sha build tag' - template: /eng/pipelines/register-build-jobs.yml parameters: repoName: aspnetcore From 097ec3a93c781de5cb2722ff01ad57591241d77e Mon Sep 17 00:00:00 2001 From: Parker Bibus Date: Mon, 22 Jun 2026 14:42:15 -0700 Subject: [PATCH 04/12] Address Copilot review: fix job variables syntax + doc nits - Convert job-level variables: blocks in aspnetcore-perf-build-jobs.yml from the invalid list form (- key: value) to the mapping form so AzDO schema validation passes and _AspNetCoreRoot/_PackScript/_ShippingDir/_StagingRoot expand (all 3 build jobs). - Clarify pack-bcs-archives.ps1 .DESCRIPTION (explicit pwsh step, not an afterBuild hook; reference aspnetcore-perf-build-jobs.yml) and add a local-run .EXAMPLE that passes -ShippingDir/-StagingRoot explicitly. - Clarify the ATOMICITY comment in aspnetcore-perf-build.yml to note the always()-guarded log-publish steps are intentionally continueOnError:true. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/aspnetcore-perf-build-jobs.yml | 26 ++++++++++---------- eng/pipelines/aspnetcore-perf-build.yml | 7 ++++-- eng/pipelines/tools/pack-bcs-archives.ps1 | 18 ++++++++++---- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/eng/pipelines/aspnetcore-perf-build-jobs.yml b/eng/pipelines/aspnetcore-perf-build-jobs.yml index 28f50935ffa..4eafc533745 100644 --- a/eng/pipelines/aspnetcore-perf-build-jobs.yml +++ b/eng/pipelines/aspnetcore-perf-build-jobs.yml @@ -58,11 +58,11 @@ jobs: name: $(DncEngInternalBuildPool) demands: ImageOverride -equals windows.vs2026preview.scout.amd64 variables: - - _AspNetCoreRoot: $(Agent.BuildDirectory)\s\aspnetcore - - _PackScript: $(Agent.BuildDirectory)\s\performance\eng\pipelines\tools\pack-bcs-archives.ps1 - - _ShippingDir: $(Agent.BuildDirectory)\s\aspnetcore\artifacts\packages\Release\Shipping - - _StagingRoot: $(Build.ArtifactStagingDirectory)\bcs - - MSBUILDUSESERVER: "1" + _AspNetCoreRoot: $(Agent.BuildDirectory)\s\aspnetcore + _PackScript: $(Agent.BuildDirectory)\s\performance\eng\pipelines\tools\pack-bcs-archives.ps1 + _ShippingDir: $(Agent.BuildDirectory)\s\aspnetcore\artifacts\packages\Release\Shipping + _StagingRoot: $(Build.ArtifactStagingDirectory)\bcs + MSBUILDUSESERVER: "1" steps: - checkout: ${{ parameters.aspnetcoreRepoAlias }} path: s/aspnetcore @@ -180,10 +180,10 @@ jobs: name: $(DncEngInternalBuildPool) demands: ImageOverride -equals 1es-ubuntu-2204 variables: - - _AspNetCoreRoot: $(Agent.BuildDirectory)/s/aspnetcore - - _PackScript: $(Agent.BuildDirectory)/s/performance/eng/pipelines/tools/pack-bcs-archives.ps1 - - _ShippingDir: $(Agent.BuildDirectory)/s/aspnetcore/artifacts/packages/Release/Shipping - - _StagingRoot: $(Build.ArtifactStagingDirectory)/bcs + _AspNetCoreRoot: $(Agent.BuildDirectory)/s/aspnetcore + _PackScript: $(Agent.BuildDirectory)/s/performance/eng/pipelines/tools/pack-bcs-archives.ps1 + _ShippingDir: $(Agent.BuildDirectory)/s/aspnetcore/artifacts/packages/Release/Shipping + _StagingRoot: $(Build.ArtifactStagingDirectory)/bcs steps: - checkout: ${{ parameters.aspnetcoreRepoAlias }} path: s/aspnetcore @@ -238,10 +238,10 @@ jobs: name: $(DncEngInternalBuildPool) demands: ImageOverride -equals 1es-ubuntu-2204 variables: - - _AspNetCoreRoot: $(Agent.BuildDirectory)/s/aspnetcore - - _PackScript: $(Agent.BuildDirectory)/s/performance/eng/pipelines/tools/pack-bcs-archives.ps1 - - _ShippingDir: $(Agent.BuildDirectory)/s/aspnetcore/artifacts/packages/Release/Shipping - - _StagingRoot: $(Build.ArtifactStagingDirectory)/bcs + _AspNetCoreRoot: $(Agent.BuildDirectory)/s/aspnetcore + _PackScript: $(Agent.BuildDirectory)/s/performance/eng/pipelines/tools/pack-bcs-archives.ps1 + _ShippingDir: $(Agent.BuildDirectory)/s/aspnetcore/artifacts/packages/Release/Shipping + _StagingRoot: $(Build.ArtifactStagingDirectory)/bcs steps: - checkout: ${{ parameters.aspnetcoreRepoAlias }} path: s/aspnetcore diff --git a/eng/pipelines/aspnetcore-perf-build.yml b/eng/pipelines/aspnetcore-perf-build.yml index 88e67db79e4..b5cb5ad72ea 100644 --- a/eng/pipelines/aspnetcore-perf-build.yml +++ b/eng/pipelines/aspnetcore-perf-build.yml @@ -35,8 +35,11 @@ # redistributed), so we do not extend 1ES.Official -- matching dotnet/runtime's # perf-build. # -# ATOMICITY: continueOnError:false everywhere; any failure sinks the build to -# Failed and the MissingBuildsTrigger Function skips indexing that SHA. +# ATOMICITY: the build/pack/upload steps that produce or move artifacts run +# continueOnError:false, so any failure sinks the build to Failed and the +# MissingBuildsTrigger Function skips indexing that SHA. The only exceptions are +# the always()-guarded best-effort log-publishing steps (continueOnError:true), +# which must never themselves fail an otherwise-good build. # # BCS output layout: # builds/aspnetcore/buildArtifacts/{sha}/{configKey}/buildInfo.json diff --git a/eng/pipelines/tools/pack-bcs-archives.ps1 b/eng/pipelines/tools/pack-bcs-archives.ps1 index 6a943bf96fc..b91c978eddd 100644 --- a/eng/pipelines/tools/pack-bcs-archives.ps1 +++ b/eng/pipelines/tools/pack-bcs-archives.ps1 @@ -5,10 +5,13 @@ .DESCRIPTION This is the single, canonical "find the runtime-pack nupkg and zip it into - the BCS archive layout" recipe used by every job in perf-build.yml (Windows - x64/x86/arm64, Linux x64, Linux arm64). The build jobs invoke it from their - `afterBuild` hook with the RID(s) they produced and the archive format that - matches their platform convention (.zip on Windows, .tar.gz on Linux). + the BCS archive layout" recipe used by every job in + aspnetcore-perf-build-jobs.yml (Windows x64/x86/arm64, Linux x64, Linux + arm64). Each build job invokes it as an explicit `pwsh` step (pwsh, NOT + Windows PowerShell 5.1, whose Compress-Archive writes backslash separators + that corrupt the archive for crank) with the RID(s) it produced and the + archive format that matches its platform convention (.zip on Windows, + .tar.gz on Linux). For each RID it: 1. Locates artifacts/packages/Release/Shipping/Microsoft.AspNetCore.App.Runtime.{rid}.*.nupkg @@ -40,10 +43,15 @@ Defaults to $(Build.ArtifactStagingDirectory)/bcs. .EXAMPLE + # In the pipeline (ShippingDir/StagingRoot resolved from BUILD_* env vars): ./pack-bcs-archives.ps1 -Rids win-x64,win-x86,win-arm64 -Format zip .EXAMPLE - ./pack-bcs-archives.ps1 -Rids linux-x64 -Format targz + # Local run -- the BUILD_* env vars are not set off-agent, so pass the + # directories explicitly: + ./pack-bcs-archives.ps1 -Rids linux-x64 -Format targz ` + -ShippingDir ./artifacts/packages/Release/Shipping ` + -StagingRoot ./artifacts/bcs #> [CmdletBinding()] param( From 4f417513d3a3bdb1aa1aec0891b1d73fab65e8d0 Mon Sep 17 00:00:00 2001 From: Parker Bibus Date: Mon, 22 Jun 2026 15:00:47 -0700 Subject: [PATCH 05/12] Forward sha to all upload buildTypes (Copilot review #7) Thread `sha: ${{ parameters.sha }}` into the 15 runtime upload branches in upload-build-artifacts-jobs.yml, matching the per-branch `repoName` threading already present on all branches. Behavior-neutral: sha defaults to $(Build.SourceVersion) (the leaf template default), so runtime perf-build uploads resolve to the same BCS path as before; only callers that pass an explicit sha (e.g. the aspnetcore pipeline's resource version) change the path. register-build-jobs.yml already forwards sha to every buildType via its ${{ each }} loop, so no change was needed there. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/upload-build-artifacts-jobs.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/eng/pipelines/upload-build-artifacts-jobs.yml b/eng/pipelines/upload-build-artifacts-jobs.yml index 1979a48a8aa..cab491526ea 100644 --- a/eng/pipelines/upload-build-artifacts-jobs.yml +++ b/eng/pipelines/upload-build-artifacts-jobs.yml @@ -22,6 +22,7 @@ jobs: buildType: 'coreclr_arm64_linux' dependencyJobName: build_linux_arm64_release_coreclr repoName: ${{ parameters.repoName }} + sha: ${{ parameters.sha }} artifacts: - artifactName: 'BuildArtifacts_linux_arm64_Release_coreclr' files: [ 'BuildArtifacts_linux_arm64_Release_coreclr.tar.gz' ] @@ -32,6 +33,7 @@ jobs: buildType: 'coreclr_arm64_windows' dependencyJobName: build_windows_arm64_release_coreclr repoName: ${{ parameters.repoName }} + sha: ${{ parameters.sha }} artifacts: - artifactName: 'BuildArtifacts_windows_arm64_Release_coreclr' files: [ 'BuildArtifacts_windows_arm64_Release_coreclr.zip' ] @@ -42,6 +44,7 @@ jobs: buildType: 'coreclr_x64_linux' dependencyJobName: build_linux_x64_release_coreclr repoName: ${{ parameters.repoName }} + sha: ${{ parameters.sha }} artifacts: - artifactName: 'BuildArtifacts_linux_x64_Release_coreclr' files: [ 'BuildArtifacts_linux_x64_Release_coreclr.tar.gz' ] @@ -52,6 +55,7 @@ jobs: buildType: 'coreclr_x64_windows' dependencyJobName: build_windows_x64_release_coreclr repoName: ${{ parameters.repoName }} + sha: ${{ parameters.sha }} artifacts: - artifactName: 'BuildArtifacts_windows_x64_Release_coreclr' files: [ 'BuildArtifacts_windows_x64_Release_coreclr.zip' ] @@ -62,6 +66,7 @@ jobs: buildType: 'coreclr_x86_windows' dependencyJobName: build_windows_x86_release_coreclr repoName: ${{ parameters.repoName }} + sha: ${{ parameters.sha }} artifacts: - artifactName: 'BuildArtifacts_windows_x86_Release_coreclr' files: [ 'BuildArtifacts_windows_x86_Release_coreclr.zip' ] @@ -72,6 +77,7 @@ jobs: buildType: 'coreclr_arm64_android' dependencyJobName: build_android_arm64_release_AndroidCoreCLR repoName: ${{ parameters.repoName }} + sha: ${{ parameters.sha }} artifacts: - artifactName: 'AndroidHelloWorldArm64CoreCLR' files: [ 'AndroidHelloWorldArm64CoreCLR.tar.gz' ] @@ -82,6 +88,7 @@ jobs: buildType: 'wasm' dependencyJobName: build_browser_wasm_linux_Release_wasm repoName: ${{ parameters.repoName }} + sha: ${{ parameters.sha }} artifacts: - artifactName: 'BrowserWasm' files: [ 'BrowserWasm.tar.gz' ] @@ -92,6 +99,7 @@ jobs: buildType: 'monoAot_arm64_linux' dependencyJobName: build_linux_arm64_release_AOT repoName: ${{ parameters.repoName }} + sha: ${{ parameters.sha }} artifacts: - artifactName: 'LinuxMonoAOTarm64' files: [ 'LinuxMonoAOTarm64.tar.gz' ] @@ -102,6 +110,7 @@ jobs: buildType: 'monoAot_x64_linux' dependencyJobName: build_linux_x64_release_AOT repoName: ${{ parameters.repoName }} + sha: ${{ parameters.sha }} artifacts: - artifactName: 'LinuxMonoAOTx64' files: [ 'LinuxMonoAOTx64.tar.gz' ] @@ -112,6 +121,7 @@ jobs: buildType: 'mono_x64_linux' dependencyJobName: build_linux_x64_release_mono repoName: ${{ parameters.repoName }} + sha: ${{ parameters.sha }} artifacts: - artifactName: 'BuildArtifacts_linux_x64_Release_mono' files: [ 'BuildArtifacts_linux_x64_Release_mono.tar.gz' ] @@ -122,6 +132,7 @@ jobs: buildType: 'mono_arm64_linux' dependencyJobName: build_linux_arm64_release_mono repoName: ${{ parameters.repoName }} + sha: ${{ parameters.sha }} artifacts: - artifactName: 'BuildArtifacts_linux_arm64_Release_mono' files: [ 'BuildArtifacts_linux_arm64_Release_mono.tar.gz' ] @@ -132,6 +143,7 @@ jobs: buildType: 'nativeAot_arm64_ios' dependencyJobName: build_ios_arm64_release_iOSNativeAOT repoName: ${{ parameters.repoName }} + sha: ${{ parameters.sha }} artifacts: - artifactName: 'iOSSampleAppNativeAOTNoSymbols' files: [ 'iOSSampleAppNativeAOTNoSymbols.zip' ] @@ -142,6 +154,7 @@ jobs: buildType: 'coreclr_arm64_ios' dependencyJobName: build_ios_arm64_release_iOSCoreCLR repoName: ${{ parameters.repoName }} + sha: ${{ parameters.sha }} artifacts: - artifactName: 'iOSSampleAppCoreCLRInterpreterNoSymbols' files: [ 'iOSSampleAppCoreCLRInterpreterNoSymbols.zip' ] @@ -154,6 +167,7 @@ jobs: buildType: 'wasm_coreclr' dependencyJobName: build_browser_wasm_linux_Release_wasm_coreclr repoName: ${{ parameters.repoName }} + sha: ${{ parameters.sha }} artifacts: - artifactName: 'BrowserWasmCoreCLR' files: [ 'BrowserWasmCoreCLR.tar.gz' ] @@ -164,6 +178,7 @@ jobs: buildType: 'coreclr_r2r_interpreter' dependencyJobName: build_linux_x64_release_coreclr_r2r_interpreter repoName: ${{ parameters.repoName }} + sha: ${{ parameters.sha }} artifacts: - artifactName: 'BuildArtifacts_linux_x64_Release_coreclr_r2r_interpreter' files: [ 'BuildArtifacts_linux_x64_Release_coreclr_r2r_interpreter.tar.gz' ] From 2853c6c79df7fa0d3be1b9ac0029cd736acb4e49 Mon Sep 17 00:00:00 2001 From: Parker Bibus Date: Mon, 22 Jun 2026 16:24:35 -0700 Subject: [PATCH 06/12] Split Windows build into one self-sufficient job per arch Replace the single multi-arch Windows_build job with three independent, boolean-gated jobs (Windows_x64_build / Windows_x86_build / Windows_arm64_build), mirroring the Linux job structure. Each job now: - is gated solely by its own aspnetcore_*_windows boolean (x64 is no longer an unconditional base build that runs whenever any Windows arch is requested), and - builds standalone with -all + -nativeToolsOnMachine; x86/arm64 keep -noBuildNative so native runtime bits restore from packages. Repoint the three Windows aspnetcore_*_windows upload branches' dependencyJobName to the new per-arch job names. This gives exact per-config gating for the forward-compat indexer (queue only arm64 -> only Windows_arm64_build runs) and runs the three Windows arches in parallel instead of sequentially. Standalone Windows-arch native restore is validated by the first real queued run. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/aspnetcore-perf-build-jobs.yml | 232 ++++++++++++------ eng/pipelines/upload-build-artifacts-jobs.yml | 6 +- 2 files changed, 158 insertions(+), 80 deletions(-) diff --git a/eng/pipelines/aspnetcore-perf-build-jobs.yml b/eng/pipelines/aspnetcore-perf-build-jobs.yml index 4eafc533745..546c0a344de 100644 --- a/eng/pipelines/aspnetcore-perf-build-jobs.yml +++ b/eng/pipelines/aspnetcore-perf-build-jobs.yml @@ -14,7 +14,8 @@ # moved eng/pipelines/tools/pack-bcs-archives.ps1, and publish one pipeline # artifact per config. # -# Job names (Windows_build / Linux_x64_build / Linux_arm64_build) are load-bearing: +# Job names (Windows_x64_build / Windows_x86_build / Windows_arm64_build / +# Linux_x64_build / Linux_arm64_build) are load-bearing: # they must match the `dependencyJobName` values in # eng/pipelines/upload-build-artifacts-jobs.yml's aspnetcore_* branches. # @@ -40,19 +41,27 @@ parameters: jobs: # =========================================================================== -# Windows multi-arch build job (x64 + x86 + arm64 in one agent) +# Windows build jobs (one self-sufficient job per arch, mirroring the Linux +# jobs below). Each job is independently gated by its config boolean, checks out +# the aspnetcore mirror + this repo, runs aspnetcore's eng/build.cmd standalone +# for its arch, packs the per-RID archive, and publishes one artifact. # -# Mirrors aspnetcore ci-public.yml's Windows_build pattern: build.cmd is invoked -# x64-first WITH native (-nativeToolsOnMachine), then x86 and arm64 with -# -noBuildNative because they cross-pack against the x64 native bits. Each -# invocation emits its own per-RID Microsoft.AspNetCore.App.Runtime.win-{arch}.*.nupkg -# into artifacts/packages/Release/Shipping. The x64 build always runs when any -# Windows arch is enabled (it produces the native base the others reuse); pack + -# publish for each arch are individually gated by that arch's boolean. +# Unlike aspnetcore ci-public.yml -- which builds x64/x86/arm64 sequentially in +# ONE job so x86/arm64 reuse the x64 step's managed build and on-machine native +# toolchain -- these jobs run on separate agents, so each must be self-sufficient: +# * every arch carries -all (full managed build) and -nativeToolsOnMachine +# (set up the native toolchain the shared x64 step used to provide); +# * x86/arm64 keep -noBuildNative (faithful to ci-public's per-arch args; their +# native runtime bits restore from packages rather than cross-building on the +# x64 host). The first real queued run validates standalone native restore. +# +# Job names (Windows_x64_build / Windows_x86_build / Windows_arm64_build) are +# load-bearing: they must match the `dependencyJobName` values in +# eng/pipelines/upload-build-artifacts-jobs.yml's aspnetcore_*_windows branches. # =========================================================================== -- ${{ if or(eq(parameters.aspnetcore_x64_windows, true), eq(parameters.aspnetcore_arm64_windows, true), eq(parameters.aspnetcore_x86_windows, true)) }}: - - job: Windows_build - displayName: 'Build: Windows x64/x86/arm64 (perf-build)' +- ${{ if eq(parameters.aspnetcore_x64_windows, true) }}: + - job: Windows_x64_build + displayName: 'Build: Windows x64 (perf-build)' timeoutInMinutes: 180 pool: name: $(DncEngInternalBuildPool) @@ -75,7 +84,6 @@ jobs: fetchTags: false clean: true - # x64 -- native base build (always runs when any Windows arch is requested). - script: >- eng\build.cmd -ci @@ -94,80 +102,150 @@ jobs: env: MSBUILDUSESERVER: "1" - - ${{ if eq(parameters.aspnetcore_x86_windows, true) }}: - - script: >- - eng\build.cmd - -ci - -prepareMachine - -Configuration Release - -arch x86 - -pack - -all - -noBuildJava - -noBuildNative - /p:OnlyPackPlatformSpecificPackages=true - $(_BuildArgs) - -ExcludeCIBinaryLog - workingDirectory: $(_AspNetCoreRoot) - displayName: Build x86 - env: - MSBUILDUSESERVER: "1" + - pwsh: >- + & "$(_PackScript)" -Rids win-x64 -Format zip -ShippingDir "$(_ShippingDir)" -StagingRoot "$(_StagingRoot)" + displayName: 'Pack BCS archive (win-x64)' + - task: PublishPipelineArtifact@1 + displayName: 'Publish BuildArtifacts_windows_x64_Release_aspnetcore' + inputs: + targetPath: $(_StagingRoot)\BuildArtifacts_windows_x64_Release_aspnetcore + artifactName: BuildArtifacts_windows_x64_Release_aspnetcore + + - task: PublishPipelineArtifact@1 + displayName: 'Publish Windows x64 build logs' + condition: always() + continueOnError: true + inputs: + targetPath: $(_AspNetCoreRoot)\artifacts\log + artifactName: Windows_x64_perf_build_Logs_Attempt_$(System.JobAttempt) + +# =========================================================================== +# Windows x86 build job +# =========================================================================== +- ${{ if eq(parameters.aspnetcore_x86_windows, true) }}: + - job: Windows_x86_build + displayName: 'Build: Windows x86 (perf-build)' + timeoutInMinutes: 180 + pool: + name: $(DncEngInternalBuildPool) + demands: ImageOverride -equals windows.vs2026preview.scout.amd64 + variables: + _AspNetCoreRoot: $(Agent.BuildDirectory)\s\aspnetcore + _PackScript: $(Agent.BuildDirectory)\s\performance\eng\pipelines\tools\pack-bcs-archives.ps1 + _ShippingDir: $(Agent.BuildDirectory)\s\aspnetcore\artifacts\packages\Release\Shipping + _StagingRoot: $(Build.ArtifactStagingDirectory)\bcs + MSBUILDUSESERVER: "1" + steps: + - checkout: ${{ parameters.aspnetcoreRepoAlias }} + path: s/aspnetcore + fetchDepth: 1 + fetchTags: false + clean: true + - checkout: ${{ parameters.performanceRepoAlias }} + path: s/performance + fetchDepth: 1 + fetchTags: false + clean: true + + - script: >- + eng\build.cmd + -ci + -prepareMachine + -nativeToolsOnMachine + -Configuration Release + -arch x86 + -pack + -all + -noBuildJava + -noBuildNative + /p:OnlyPackPlatformSpecificPackages=true + $(_BuildArgs) + /bl:artifacts/log/Release/Build.x86.binlog + workingDirectory: $(_AspNetCoreRoot) + displayName: Build x86 + env: + MSBUILDUSESERVER: "1" + + - pwsh: >- + & "$(_PackScript)" -Rids win-x86 -Format zip -ShippingDir "$(_ShippingDir)" -StagingRoot "$(_StagingRoot)" + displayName: 'Pack BCS archive (win-x86)' + - task: PublishPipelineArtifact@1 + displayName: 'Publish BuildArtifacts_windows_x86_Release_aspnetcore' + inputs: + targetPath: $(_StagingRoot)\BuildArtifacts_windows_x86_Release_aspnetcore + artifactName: BuildArtifacts_windows_x86_Release_aspnetcore - - ${{ if eq(parameters.aspnetcore_arm64_windows, true) }}: - - script: >- - eng\build.cmd - -ci - -prepareMachine - -Configuration Release - -arch arm64 - -pack - -noBuildJava - -noBuildNative - /p:OnlyPackPlatformSpecificPackages=true - $(_BuildArgs) - /bl:artifacts/log/Release/Build.arm64.binlog - workingDirectory: $(_AspNetCoreRoot) - displayName: Build ARM64 - env: - MSBUILDUSESERVER: "1" + - task: PublishPipelineArtifact@1 + displayName: 'Publish Windows x86 build logs' + condition: always() + continueOnError: true + inputs: + targetPath: $(_AspNetCoreRoot)\artifacts\log + artifactName: Windows_x86_perf_build_Logs_Attempt_$(System.JobAttempt) - - ${{ if eq(parameters.aspnetcore_x64_windows, true) }}: - - pwsh: >- - & "$(_PackScript)" -Rids win-x64 -Format zip -ShippingDir "$(_ShippingDir)" -StagingRoot "$(_StagingRoot)" - displayName: 'Pack BCS archive (win-x64)' - - task: PublishPipelineArtifact@1 - displayName: 'Publish BuildArtifacts_windows_x64_Release_aspnetcore' - inputs: - targetPath: $(_StagingRoot)\BuildArtifacts_windows_x64_Release_aspnetcore - artifactName: BuildArtifacts_windows_x64_Release_aspnetcore +# =========================================================================== +# Windows arm64 build job +# =========================================================================== +- ${{ if eq(parameters.aspnetcore_arm64_windows, true) }}: + - job: Windows_arm64_build + displayName: 'Build: Windows ARM64 (perf-build)' + timeoutInMinutes: 180 + pool: + name: $(DncEngInternalBuildPool) + demands: ImageOverride -equals windows.vs2026preview.scout.amd64 + variables: + _AspNetCoreRoot: $(Agent.BuildDirectory)\s\aspnetcore + _PackScript: $(Agent.BuildDirectory)\s\performance\eng\pipelines\tools\pack-bcs-archives.ps1 + _ShippingDir: $(Agent.BuildDirectory)\s\aspnetcore\artifacts\packages\Release\Shipping + _StagingRoot: $(Build.ArtifactStagingDirectory)\bcs + MSBUILDUSESERVER: "1" + steps: + - checkout: ${{ parameters.aspnetcoreRepoAlias }} + path: s/aspnetcore + fetchDepth: 1 + fetchTags: false + clean: true + - checkout: ${{ parameters.performanceRepoAlias }} + path: s/performance + fetchDepth: 1 + fetchTags: false + clean: true - - ${{ if eq(parameters.aspnetcore_x86_windows, true) }}: - - pwsh: >- - & "$(_PackScript)" -Rids win-x86 -Format zip -ShippingDir "$(_ShippingDir)" -StagingRoot "$(_StagingRoot)" - displayName: 'Pack BCS archive (win-x86)' - - task: PublishPipelineArtifact@1 - displayName: 'Publish BuildArtifacts_windows_x86_Release_aspnetcore' - inputs: - targetPath: $(_StagingRoot)\BuildArtifacts_windows_x86_Release_aspnetcore - artifactName: BuildArtifacts_windows_x86_Release_aspnetcore + - script: >- + eng\build.cmd + -ci + -prepareMachine + -nativeToolsOnMachine + -Configuration Release + -arch arm64 + -pack + -all + -noBuildJava + -noBuildNative + /p:OnlyPackPlatformSpecificPackages=true + $(_BuildArgs) + /bl:artifacts/log/Release/Build.arm64.binlog + workingDirectory: $(_AspNetCoreRoot) + displayName: Build ARM64 + env: + MSBUILDUSESERVER: "1" - - ${{ if eq(parameters.aspnetcore_arm64_windows, true) }}: - - pwsh: >- - & "$(_PackScript)" -Rids win-arm64 -Format zip -ShippingDir "$(_ShippingDir)" -StagingRoot "$(_StagingRoot)" - displayName: 'Pack BCS archive (win-arm64)' - - task: PublishPipelineArtifact@1 - displayName: 'Publish BuildArtifacts_windows_arm64_Release_aspnetcore' - inputs: - targetPath: $(_StagingRoot)\BuildArtifacts_windows_arm64_Release_aspnetcore - artifactName: BuildArtifacts_windows_arm64_Release_aspnetcore + - pwsh: >- + & "$(_PackScript)" -Rids win-arm64 -Format zip -ShippingDir "$(_ShippingDir)" -StagingRoot "$(_StagingRoot)" + displayName: 'Pack BCS archive (win-arm64)' + - task: PublishPipelineArtifact@1 + displayName: 'Publish BuildArtifacts_windows_arm64_Release_aspnetcore' + inputs: + targetPath: $(_StagingRoot)\BuildArtifacts_windows_arm64_Release_aspnetcore + artifactName: BuildArtifacts_windows_arm64_Release_aspnetcore - task: PublishPipelineArtifact@1 - displayName: 'Publish Windows build logs' + displayName: 'Publish Windows arm64 build logs' condition: always() continueOnError: true inputs: targetPath: $(_AspNetCoreRoot)\artifacts\log - artifactName: Windows_perf_build_Logs_Attempt_$(System.JobAttempt) + artifactName: Windows_arm64_perf_build_Logs_Attempt_$(System.JobAttempt) # =========================================================================== # Linux x64 build job diff --git a/eng/pipelines/upload-build-artifacts-jobs.yml b/eng/pipelines/upload-build-artifacts-jobs.yml index cab491526ea..71618b0493e 100644 --- a/eng/pipelines/upload-build-artifacts-jobs.yml +++ b/eng/pipelines/upload-build-artifacts-jobs.yml @@ -216,7 +216,7 @@ jobs: - template: /eng/pipelines/templates/upload-build-artifacts-job.yml@${{ parameters.performanceRepoAlias }} parameters: buildType: 'aspnetcore_x64_windows' - dependencyJobName: Windows_build + dependencyJobName: Windows_x64_build repoName: ${{ parameters.repoName }} sha: ${{ parameters.sha }} artifacts: @@ -227,7 +227,7 @@ jobs: - template: /eng/pipelines/templates/upload-build-artifacts-job.yml@${{ parameters.performanceRepoAlias }} parameters: buildType: 'aspnetcore_arm64_windows' - dependencyJobName: Windows_build + dependencyJobName: Windows_arm64_build repoName: ${{ parameters.repoName }} sha: ${{ parameters.sha }} artifacts: @@ -238,7 +238,7 @@ jobs: - template: /eng/pipelines/templates/upload-build-artifacts-job.yml@${{ parameters.performanceRepoAlias }} parameters: buildType: 'aspnetcore_x86_windows' - dependencyJobName: Windows_build + dependencyJobName: Windows_x86_build repoName: ${{ parameters.repoName }} sha: ${{ parameters.sha }} artifacts: From c43515f2f1ce449edb3a69ad2bff02ee1dd4b107 Mon Sep 17 00:00:00 2001 From: Parker Bibus Date: Tue, 23 Jun 2026 10:57:39 -0700 Subject: [PATCH 07/12] Refresh stale sha-parameter comment in upload dispatcher After U1 (4f417513d) every upload branch threads sha: ${{ parameters.sha }}, so the parameter doc claiming runtime branches omit sha and inherit the leaf default is no longer accurate. Reword to say all branches pass sha explicitly and it resolves to the $(Build.SourceVersion) default when no caller overrides it. Comment-only; no behavior change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/upload-build-artifacts-jobs.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/eng/pipelines/upload-build-artifacts-jobs.yml b/eng/pipelines/upload-build-artifacts-jobs.yml index 71618b0493e..1d783a6740a 100644 --- a/eng/pipelines/upload-build-artifacts-jobs.yml +++ b/eng/pipelines/upload-build-artifacts-jobs.yml @@ -11,8 +11,9 @@ parameters: # repo commit ($(Build.SourceVersion)) for back-compat with the runtime # perf-build pipeline. Resource-built tenants (e.g. aspnetcore, whose source # is a repository resource rather than self) pass the resource's commit, e.g. - # sha: $(resources.repositories.aspnetcore.version). Runtime config branches - # below omit sha and inherit the leaf template's $(Build.SourceVersion) default. + # sha: $(resources.repositories.aspnetcore.version). All config branches below + # pass sha through explicitly; with no caller override it resolves to this + # $(Build.SourceVersion) default, so runtime behavior is unchanged. sha: '$(Build.SourceVersion)' jobs: From 02530d954afc0b1cb7fabd8ff9357f68a1505208 Mon Sep 17 00:00:00 2001 From: Parker Bibus Date: Tue, 23 Jun 2026 12:29:19 -0700 Subject: [PATCH 08/12] Address Copilot re-review: gate Build stage + robust pack-script defaults Two follow-up Copilot comments on the latest push (c43515f2f): 1. aspnetcore-perf-build.yml: the Build stage was the only ungated stage. It checks out the internal-only aspnetcore mirror on the internal DncEng pool, so a manual queue in a public project would fail confusingly while the already-gated RegisterBuild/UploadArtifacts stages are compile-time removed. Add a runtime condition: matching the sibling internal + (ResourceTrigger | Manual) gate. A runtime condition (not ${{ if }}) keeps the stage present-but-skipped in public, avoiding a zero-stage (invalid) pipeline. Behavior-neutral in the steady-state internal trigger path. 2. pack-bcs-archives.ps1: $ShippingDir/$StagingRoot defaults called Join-Path on BUILD_SOURCESDIRECTORY/BUILD_ARTIFACTSTAGINGDIRECTORY, which throws during parameter binding when those env vars are unset (local runs). Fall back to the current directory so the documented local-override workflow works off-agent. Unchanged on the agent where the BUILD_* vars are always set. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/aspnetcore-perf-build.yml | 7 +++++++ eng/pipelines/tools/pack-bcs-archives.ps1 | 7 +++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/eng/pipelines/aspnetcore-perf-build.yml b/eng/pipelines/aspnetcore-perf-build.yml index b5cb5ad72ea..252cc7606b0 100644 --- a/eng/pipelines/aspnetcore-perf-build.yml +++ b/eng/pipelines/aspnetcore-perf-build.yml @@ -141,9 +141,16 @@ stages: - ${{ if eq(parameters.aspnetcore_x86_windows, true) }}: - aspnetcore_x86_windows +# Build checks out the internal-only aspnetcore resource on the internal DncEng +# pool, so it cannot succeed outside the internal project. Gate it with the same +# internal + (ResourceTrigger | Manual) expression as the sibling stages. This is +# a runtime `condition:` (not the `${{ if }}` the siblings use) so the stage is +# present-but-skipped in public/non-trigger contexts -- if all three stages were +# `${{ if }}`-removed the pipeline would compile to zero stages, which is invalid. - stage: Build displayName: Build dependsOn: [] + condition: and(ne(variables['System.TeamProject'], 'public'), or(eq(variables['Build.Reason'], 'ResourceTrigger'), eq(variables['Build.Reason'], 'Manual'))) jobs: - template: /eng/pipelines/aspnetcore-perf-build-jobs.yml parameters: diff --git a/eng/pipelines/tools/pack-bcs-archives.ps1 b/eng/pipelines/tools/pack-bcs-archives.ps1 index b91c978eddd..7e890963d89 100644 --- a/eng/pipelines/tools/pack-bcs-archives.ps1 +++ b/eng/pipelines/tools/pack-bcs-archives.ps1 @@ -62,9 +62,12 @@ param( [ValidateSet('zip', 'targz')] [string]$Format, - [string]$ShippingDir = (Join-Path $env:BUILD_SOURCESDIRECTORY 'artifacts/packages/Release/Shipping'), + # Default to the BUILD_* agent paths in the pipeline; fall back to the current + # directory off-agent (those env vars are unset locally, and Join-Path $null + # throws during parameter binding), keeping the documented local-override flow. + [string]$ShippingDir = (Join-Path ($env:BUILD_SOURCESDIRECTORY ? $env:BUILD_SOURCESDIRECTORY : (Get-Location).Path) 'artifacts/packages/Release/Shipping'), - [string]$StagingRoot = (Join-Path $env:BUILD_ARTIFACTSTAGINGDIRECTORY 'bcs') + [string]$StagingRoot = (Join-Path ($env:BUILD_ARTIFACTSTAGINGDIRECTORY ? $env:BUILD_ARTIFACTSTAGINGDIRECTORY : (Get-Location).Path) 'bcs') ) $ErrorActionPreference = 'Stop' From 7b1ca1d0470b3835949743a0bd1e04fd0690ec6f Mon Sep 17 00:00:00 2001 From: Parker Bibus Date: Tue, 23 Jun 2026 12:41:08 -0700 Subject: [PATCH 09/12] Rename pack-bcs-archives.ps1 -> pack-bcs-archives-aspnetcore.ps1 The pack script is aspnetcore-specific: it is hardcoded to find Microsoft.AspNetCore.App.Runtime.{rid} nupkgs and emit the lowercase microsoft.aspnetcore.app.runtime.{rid}/Release layout. It shares the eng/pipelines/tools/ directory with the dotnet/runtime perf-build pipeline, so an -aspnetcore suffix makes its scope explicit and avoids a future collision with a runtime-specific pack script. Matches the aspnetcore-* naming of its sibling pipeline files. Updated the 5 _PackScript references and the header comment in aspnetcore-perf-build-jobs.yml and the 2 .EXAMPLE self-references in the script. No contract depends on the filename (only the pipeline references it). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/aspnetcore-perf-build-jobs.yml | 12 ++++++------ ...archives.ps1 => pack-bcs-archives-aspnetcore.ps1} | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) rename eng/pipelines/tools/{pack-bcs-archives.ps1 => pack-bcs-archives-aspnetcore.ps1} (98%) diff --git a/eng/pipelines/aspnetcore-perf-build-jobs.yml b/eng/pipelines/aspnetcore-perf-build-jobs.yml index 546c0a344de..98aee00caad 100644 --- a/eng/pipelines/aspnetcore-perf-build-jobs.yml +++ b/eng/pipelines/aspnetcore-perf-build-jobs.yml @@ -11,7 +11,7 @@ # triggering commit alongside this repo (for the pack script), run aspnetcore's # own eng/build with lean perf args (PUBLIC feeds only -- no internal runtime # download), pack the per-RID runtime nupkgs into the BCS archive layout via the -# moved eng/pipelines/tools/pack-bcs-archives.ps1, and publish one pipeline +# moved eng/pipelines/tools/pack-bcs-archives-aspnetcore.ps1, and publish one pipeline # artifact per config. # # Job names (Windows_x64_build / Windows_x86_build / Windows_arm64_build / @@ -68,7 +68,7 @@ jobs: demands: ImageOverride -equals windows.vs2026preview.scout.amd64 variables: _AspNetCoreRoot: $(Agent.BuildDirectory)\s\aspnetcore - _PackScript: $(Agent.BuildDirectory)\s\performance\eng\pipelines\tools\pack-bcs-archives.ps1 + _PackScript: $(Agent.BuildDirectory)\s\performance\eng\pipelines\tools\pack-bcs-archives-aspnetcore.ps1 _ShippingDir: $(Agent.BuildDirectory)\s\aspnetcore\artifacts\packages\Release\Shipping _StagingRoot: $(Build.ArtifactStagingDirectory)\bcs MSBUILDUSESERVER: "1" @@ -131,7 +131,7 @@ jobs: demands: ImageOverride -equals windows.vs2026preview.scout.amd64 variables: _AspNetCoreRoot: $(Agent.BuildDirectory)\s\aspnetcore - _PackScript: $(Agent.BuildDirectory)\s\performance\eng\pipelines\tools\pack-bcs-archives.ps1 + _PackScript: $(Agent.BuildDirectory)\s\performance\eng\pipelines\tools\pack-bcs-archives-aspnetcore.ps1 _ShippingDir: $(Agent.BuildDirectory)\s\aspnetcore\artifacts\packages\Release\Shipping _StagingRoot: $(Build.ArtifactStagingDirectory)\bcs MSBUILDUSESERVER: "1" @@ -195,7 +195,7 @@ jobs: demands: ImageOverride -equals windows.vs2026preview.scout.amd64 variables: _AspNetCoreRoot: $(Agent.BuildDirectory)\s\aspnetcore - _PackScript: $(Agent.BuildDirectory)\s\performance\eng\pipelines\tools\pack-bcs-archives.ps1 + _PackScript: $(Agent.BuildDirectory)\s\performance\eng\pipelines\tools\pack-bcs-archives-aspnetcore.ps1 _ShippingDir: $(Agent.BuildDirectory)\s\aspnetcore\artifacts\packages\Release\Shipping _StagingRoot: $(Build.ArtifactStagingDirectory)\bcs MSBUILDUSESERVER: "1" @@ -259,7 +259,7 @@ jobs: demands: ImageOverride -equals 1es-ubuntu-2204 variables: _AspNetCoreRoot: $(Agent.BuildDirectory)/s/aspnetcore - _PackScript: $(Agent.BuildDirectory)/s/performance/eng/pipelines/tools/pack-bcs-archives.ps1 + _PackScript: $(Agent.BuildDirectory)/s/performance/eng/pipelines/tools/pack-bcs-archives-aspnetcore.ps1 _ShippingDir: $(Agent.BuildDirectory)/s/aspnetcore/artifacts/packages/Release/Shipping _StagingRoot: $(Build.ArtifactStagingDirectory)/bcs steps: @@ -317,7 +317,7 @@ jobs: demands: ImageOverride -equals 1es-ubuntu-2204 variables: _AspNetCoreRoot: $(Agent.BuildDirectory)/s/aspnetcore - _PackScript: $(Agent.BuildDirectory)/s/performance/eng/pipelines/tools/pack-bcs-archives.ps1 + _PackScript: $(Agent.BuildDirectory)/s/performance/eng/pipelines/tools/pack-bcs-archives-aspnetcore.ps1 _ShippingDir: $(Agent.BuildDirectory)/s/aspnetcore/artifacts/packages/Release/Shipping _StagingRoot: $(Build.ArtifactStagingDirectory)/bcs steps: diff --git a/eng/pipelines/tools/pack-bcs-archives.ps1 b/eng/pipelines/tools/pack-bcs-archives-aspnetcore.ps1 similarity index 98% rename from eng/pipelines/tools/pack-bcs-archives.ps1 rename to eng/pipelines/tools/pack-bcs-archives-aspnetcore.ps1 index 7e890963d89..9966d5e1870 100644 --- a/eng/pipelines/tools/pack-bcs-archives.ps1 +++ b/eng/pipelines/tools/pack-bcs-archives-aspnetcore.ps1 @@ -44,12 +44,12 @@ .EXAMPLE # In the pipeline (ShippingDir/StagingRoot resolved from BUILD_* env vars): - ./pack-bcs-archives.ps1 -Rids win-x64,win-x86,win-arm64 -Format zip + ./pack-bcs-archives-aspnetcore.ps1 -Rids win-x64,win-x86,win-arm64 -Format zip .EXAMPLE # Local run -- the BUILD_* env vars are not set off-agent, so pass the # directories explicitly: - ./pack-bcs-archives.ps1 -Rids linux-x64 -Format targz ` + ./pack-bcs-archives-aspnetcore.ps1 -Rids linux-x64 -Format targz ` -ShippingDir ./artifacts/packages/Release/Shipping ` -StagingRoot ./artifacts/bcs #> From faa66aeddb5a28ab24f0af90a048f00637eed7c1 Mon Sep 17 00:00:00 2001 From: Parker Bibus Date: Tue, 23 Jun 2026 12:49:32 -0700 Subject: [PATCH 10/12] Fail loudly on duplicate runtime-pack nupkgs in pack-bcs-archives-aspnetcore Copilot review: the pack script selected the runtime-pack nupkg with `Sort-Object Name | Select-Object -First 1`, which silently picks the lexicographically-smallest match if multiple versions are present in the Shipping directory -- packing an unintended version into the BCS archive. A clean from-source build produces exactly one non-symbols Microsoft.AspNetCore.App.Runtime.{rid} pack per RID, so collect all non-symbols matches and assert exactly one: throw a clear error listing the offenders when more than one is found, instead of guessing. Matches the script's existing assert-the-contract style (single archive root entry). Renamed the local to $nupkgMatches to avoid shadowing PowerShell's automatic $Matches variable under Set-StrictMode. Verified parse + 0/1/2-match behavior off-agent (symbols pack correctly excluded). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../tools/pack-bcs-archives-aspnetcore.ps1 | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/eng/pipelines/tools/pack-bcs-archives-aspnetcore.ps1 b/eng/pipelines/tools/pack-bcs-archives-aspnetcore.ps1 index 9966d5e1870..b86eabe1fdc 100644 --- a/eng/pipelines/tools/pack-bcs-archives-aspnetcore.ps1 +++ b/eng/pipelines/tools/pack-bcs-archives-aspnetcore.ps1 @@ -113,13 +113,21 @@ foreach ($rid in $Rids) { Write-Host "=== Packing $configKey (rid=$rid, format=$Format) ===" $pattern = "Microsoft.AspNetCore.App.Runtime.$rid.*.nupkg" - $nupkg = Get-ChildItem -Path $ShippingDir -Filter $pattern -File -ErrorAction SilentlyContinue | + $matches = @(Get-ChildItem -Path $ShippingDir -Filter $pattern -File -ErrorAction SilentlyContinue | Where-Object { $_.Name -notmatch '\.symbols\.nupkg$' } | - Sort-Object Name | - Select-Object -First 1 - if (-not $nupkg) { + Sort-Object Name) + if ($matches.Count -eq 0) { throw "Could not find runtime pack nupkg matching '$pattern' under '$ShippingDir'." } + if ($matches.Count -gt 1) { + # A clean from-source build produces exactly one non-symbols runtime pack + # per RID. More than one means a stale/duplicate version is lingering in + # the Shipping dir; pick-first would silently pack the lexicographically + # smallest (likely wrong) version, so fail loudly instead. + $names = ($matches | ForEach-Object { $_.Name }) -join ', ' + throw "Expected exactly one runtime pack nupkg matching '$pattern' under '$ShippingDir', found $($matches.Count): $names." + } + $nupkg = $matches[0] Write-Host "Found nupkg: $($nupkg.FullName)" # microsoft.aspnetcore.app.runtime.{rid}/Release/ <-- archive root (lowercase!) From c96b8cd45a337436c3b16462bfe7b05698ccc068 Mon Sep 17 00:00:00 2001 From: Parker Bibus Date: Tue, 23 Jun 2026 14:30:39 -0700 Subject: [PATCH 11/12] Rename $matches local to $nupkgMatches in pack-bcs-archives-aspnetcore PowerShell's $Matches is an automatic variable populated by the -match operator; assigning to the case-insensitive local $matches shadows it and is risky under Set-StrictMode if regex matching is later added. Rename the local to $nupkgMatches and update all references. No behavior change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/tools/pack-bcs-archives-aspnetcore.ps1 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/pipelines/tools/pack-bcs-archives-aspnetcore.ps1 b/eng/pipelines/tools/pack-bcs-archives-aspnetcore.ps1 index b86eabe1fdc..c005373c7d6 100644 --- a/eng/pipelines/tools/pack-bcs-archives-aspnetcore.ps1 +++ b/eng/pipelines/tools/pack-bcs-archives-aspnetcore.ps1 @@ -113,21 +113,21 @@ foreach ($rid in $Rids) { Write-Host "=== Packing $configKey (rid=$rid, format=$Format) ===" $pattern = "Microsoft.AspNetCore.App.Runtime.$rid.*.nupkg" - $matches = @(Get-ChildItem -Path $ShippingDir -Filter $pattern -File -ErrorAction SilentlyContinue | + $nupkgMatches = @(Get-ChildItem -Path $ShippingDir -Filter $pattern -File -ErrorAction SilentlyContinue | Where-Object { $_.Name -notmatch '\.symbols\.nupkg$' } | Sort-Object Name) - if ($matches.Count -eq 0) { + if ($nupkgMatches.Count -eq 0) { throw "Could not find runtime pack nupkg matching '$pattern' under '$ShippingDir'." } - if ($matches.Count -gt 1) { + if ($nupkgMatches.Count -gt 1) { # A clean from-source build produces exactly one non-symbols runtime pack # per RID. More than one means a stale/duplicate version is lingering in # the Shipping dir; pick-first would silently pack the lexicographically # smallest (likely wrong) version, so fail loudly instead. - $names = ($matches | ForEach-Object { $_.Name }) -join ', ' - throw "Expected exactly one runtime pack nupkg matching '$pattern' under '$ShippingDir', found $($matches.Count): $names." + $names = ($nupkgMatches | ForEach-Object { $_.Name }) -join ', ' + throw "Expected exactly one runtime pack nupkg matching '$pattern' under '$ShippingDir', found $($nupkgMatches.Count): $names." } - $nupkg = $matches[0] + $nupkg = $nupkgMatches[0] Write-Host "Found nupkg: $($nupkg.FullName)" # microsoft.aspnetcore.app.runtime.{rid}/Release/ <-- archive root (lowercase!) From a86792389ee58c29722505e33dd6b17d49a7e696 Mon Sep 17 00:00:00 2001 From: Parker Bibus Date: Tue, 23 Jun 2026 15:41:42 -0700 Subject: [PATCH 12/12] Stage aspnetcore BCS artifact as the raw runtime-pack nupkg Replace the extract/validate/repack pack-bcs-archives-aspnetcore.ps1 with a thin stage-bcs-nupkg-aspnetcore.ps1 that copies the single runtime-pack nupkg to the fixed BCS artifact name. The BCS now stores the verbatim nupkg (crank filters at consume time) rather than a transformed archive, so there is no produce-time layout transform to maintain and crank can re-derive whatever it needs. - New stage-bcs-nupkg-aspnetcore.ps1 (find the one runtime-pack nupkg, copy to BuildArtifacts_{os}_{arch}_Release_aspnetcore.nupkg); old pack script removed - Build jobs drop -Format and invoke the staging script - Upload dispatcher aspnetcore files -> .nupkg Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/aspnetcore-perf-build-jobs.yml | 38 ++-- .../tools/pack-bcs-archives-aspnetcore.ps1 | 206 ------------------ .../tools/stage-bcs-nupkg-aspnetcore.ps1 | 135 ++++++++++++ eng/pipelines/upload-build-artifacts-jobs.yml | 10 +- 4 files changed, 159 insertions(+), 230 deletions(-) delete mode 100644 eng/pipelines/tools/pack-bcs-archives-aspnetcore.ps1 create mode 100644 eng/pipelines/tools/stage-bcs-nupkg-aspnetcore.ps1 diff --git a/eng/pipelines/aspnetcore-perf-build-jobs.yml b/eng/pipelines/aspnetcore-perf-build-jobs.yml index 98aee00caad..6c9dcaac55d 100644 --- a/eng/pipelines/aspnetcore-perf-build-jobs.yml +++ b/eng/pipelines/aspnetcore-perf-build-jobs.yml @@ -8,11 +8,11 @@ # `@self` always resolves to the ROOT pipeline repo (performance), which does not # carry aspnetcore's eng/common layout/contract. So we stand up the build # ourselves: pick the dnceng internal pool, check out the aspnetcore mirror at the -# triggering commit alongside this repo (for the pack script), run aspnetcore's +# triggering commit alongside this repo (for the staging script), run aspnetcore's # own eng/build with lean perf args (PUBLIC feeds only -- no internal runtime -# download), pack the per-RID runtime nupkgs into the BCS archive layout via the -# moved eng/pipelines/tools/pack-bcs-archives-aspnetcore.ps1, and publish one pipeline -# artifact per config. +# download), stage each per-RID runtime-pack nupkg verbatim as the BCS artifact via +# the moved eng/pipelines/tools/stage-bcs-nupkg-aspnetcore.ps1, and publish one +# pipeline artifact per config. # # Job names (Windows_x64_build / Windows_x86_build / Windows_arm64_build / # Linux_x64_build / Linux_arm64_build) are load-bearing: @@ -68,7 +68,7 @@ jobs: demands: ImageOverride -equals windows.vs2026preview.scout.amd64 variables: _AspNetCoreRoot: $(Agent.BuildDirectory)\s\aspnetcore - _PackScript: $(Agent.BuildDirectory)\s\performance\eng\pipelines\tools\pack-bcs-archives-aspnetcore.ps1 + _StageNupkgScript: $(Agent.BuildDirectory)\s\performance\eng\pipelines\tools\stage-bcs-nupkg-aspnetcore.ps1 _ShippingDir: $(Agent.BuildDirectory)\s\aspnetcore\artifacts\packages\Release\Shipping _StagingRoot: $(Build.ArtifactStagingDirectory)\bcs MSBUILDUSESERVER: "1" @@ -103,8 +103,8 @@ jobs: MSBUILDUSESERVER: "1" - pwsh: >- - & "$(_PackScript)" -Rids win-x64 -Format zip -ShippingDir "$(_ShippingDir)" -StagingRoot "$(_StagingRoot)" - displayName: 'Pack BCS archive (win-x64)' + & "$(_StageNupkgScript)" -Rids win-x64 -ShippingDir "$(_ShippingDir)" -StagingRoot "$(_StagingRoot)" + displayName: 'Stage BCS nupkg (win-x64)' - task: PublishPipelineArtifact@1 displayName: 'Publish BuildArtifacts_windows_x64_Release_aspnetcore' inputs: @@ -131,7 +131,7 @@ jobs: demands: ImageOverride -equals windows.vs2026preview.scout.amd64 variables: _AspNetCoreRoot: $(Agent.BuildDirectory)\s\aspnetcore - _PackScript: $(Agent.BuildDirectory)\s\performance\eng\pipelines\tools\pack-bcs-archives-aspnetcore.ps1 + _StageNupkgScript: $(Agent.BuildDirectory)\s\performance\eng\pipelines\tools\stage-bcs-nupkg-aspnetcore.ps1 _ShippingDir: $(Agent.BuildDirectory)\s\aspnetcore\artifacts\packages\Release\Shipping _StagingRoot: $(Build.ArtifactStagingDirectory)\bcs MSBUILDUSESERVER: "1" @@ -167,8 +167,8 @@ jobs: MSBUILDUSESERVER: "1" - pwsh: >- - & "$(_PackScript)" -Rids win-x86 -Format zip -ShippingDir "$(_ShippingDir)" -StagingRoot "$(_StagingRoot)" - displayName: 'Pack BCS archive (win-x86)' + & "$(_StageNupkgScript)" -Rids win-x86 -ShippingDir "$(_ShippingDir)" -StagingRoot "$(_StagingRoot)" + displayName: 'Stage BCS nupkg (win-x86)' - task: PublishPipelineArtifact@1 displayName: 'Publish BuildArtifacts_windows_x86_Release_aspnetcore' inputs: @@ -195,7 +195,7 @@ jobs: demands: ImageOverride -equals windows.vs2026preview.scout.amd64 variables: _AspNetCoreRoot: $(Agent.BuildDirectory)\s\aspnetcore - _PackScript: $(Agent.BuildDirectory)\s\performance\eng\pipelines\tools\pack-bcs-archives-aspnetcore.ps1 + _StageNupkgScript: $(Agent.BuildDirectory)\s\performance\eng\pipelines\tools\stage-bcs-nupkg-aspnetcore.ps1 _ShippingDir: $(Agent.BuildDirectory)\s\aspnetcore\artifacts\packages\Release\Shipping _StagingRoot: $(Build.ArtifactStagingDirectory)\bcs MSBUILDUSESERVER: "1" @@ -231,8 +231,8 @@ jobs: MSBUILDUSESERVER: "1" - pwsh: >- - & "$(_PackScript)" -Rids win-arm64 -Format zip -ShippingDir "$(_ShippingDir)" -StagingRoot "$(_StagingRoot)" - displayName: 'Pack BCS archive (win-arm64)' + & "$(_StageNupkgScript)" -Rids win-arm64 -ShippingDir "$(_ShippingDir)" -StagingRoot "$(_StagingRoot)" + displayName: 'Stage BCS nupkg (win-arm64)' - task: PublishPipelineArtifact@1 displayName: 'Publish BuildArtifacts_windows_arm64_Release_aspnetcore' inputs: @@ -259,7 +259,7 @@ jobs: demands: ImageOverride -equals 1es-ubuntu-2204 variables: _AspNetCoreRoot: $(Agent.BuildDirectory)/s/aspnetcore - _PackScript: $(Agent.BuildDirectory)/s/performance/eng/pipelines/tools/pack-bcs-archives-aspnetcore.ps1 + _StageNupkgScript: $(Agent.BuildDirectory)/s/performance/eng/pipelines/tools/stage-bcs-nupkg-aspnetcore.ps1 _ShippingDir: $(Agent.BuildDirectory)/s/aspnetcore/artifacts/packages/Release/Shipping _StagingRoot: $(Build.ArtifactStagingDirectory)/bcs steps: @@ -289,8 +289,8 @@ jobs: displayName: Build linux-x64 - pwsh: >- - & "$(_PackScript)" -Rids linux-x64 -Format targz -ShippingDir "$(_ShippingDir)" -StagingRoot "$(_StagingRoot)" - displayName: 'Pack BCS archive (linux-x64)' + & "$(_StageNupkgScript)" -Rids linux-x64 -ShippingDir "$(_ShippingDir)" -StagingRoot "$(_StagingRoot)" + displayName: 'Stage BCS nupkg (linux-x64)' - task: PublishPipelineArtifact@1 displayName: 'Publish BuildArtifacts_linux_x64_Release_aspnetcore' inputs: @@ -317,7 +317,7 @@ jobs: demands: ImageOverride -equals 1es-ubuntu-2204 variables: _AspNetCoreRoot: $(Agent.BuildDirectory)/s/aspnetcore - _PackScript: $(Agent.BuildDirectory)/s/performance/eng/pipelines/tools/pack-bcs-archives-aspnetcore.ps1 + _StageNupkgScript: $(Agent.BuildDirectory)/s/performance/eng/pipelines/tools/stage-bcs-nupkg-aspnetcore.ps1 _ShippingDir: $(Agent.BuildDirectory)/s/aspnetcore/artifacts/packages/Release/Shipping _StagingRoot: $(Build.ArtifactStagingDirectory)/bcs steps: @@ -347,8 +347,8 @@ jobs: displayName: Build linux-arm64 - pwsh: >- - & "$(_PackScript)" -Rids linux-arm64 -Format targz -ShippingDir "$(_ShippingDir)" -StagingRoot "$(_StagingRoot)" - displayName: 'Pack BCS archive (linux-arm64)' + & "$(_StageNupkgScript)" -Rids linux-arm64 -ShippingDir "$(_ShippingDir)" -StagingRoot "$(_StagingRoot)" + displayName: 'Stage BCS nupkg (linux-arm64)' - task: PublishPipelineArtifact@1 displayName: 'Publish BuildArtifacts_linux_arm64_Release_aspnetcore' inputs: diff --git a/eng/pipelines/tools/pack-bcs-archives-aspnetcore.ps1 b/eng/pipelines/tools/pack-bcs-archives-aspnetcore.ps1 deleted file mode 100644 index c005373c7d6..00000000000 --- a/eng/pipelines/tools/pack-bcs-archives-aspnetcore.ps1 +++ /dev/null @@ -1,206 +0,0 @@ -<# -.SYNOPSIS - Packs per-RID Microsoft.AspNetCore.App runtime packs into Build Cache - Service (BCS) archives for the perf-build pipeline. - -.DESCRIPTION - This is the single, canonical "find the runtime-pack nupkg and zip it into - the BCS archive layout" recipe used by every job in - aspnetcore-perf-build-jobs.yml (Windows x64/x86/arm64, Linux x64, Linux - arm64). Each build job invokes it as an explicit `pwsh` step (pwsh, NOT - Windows PowerShell 5.1, whose Compress-Archive writes backslash separators - that corrupt the archive for crank) with the RID(s) it produced and the - archive format that matches its platform convention (.zip on Windows, - .tar.gz on Linux). - - For each RID it: - 1. Locates artifacts/packages/Release/Shipping/Microsoft.AspNetCore.App.Runtime.{rid}.*.nupkg - (excluding the *.symbols.nupkg). - 2. Extracts it into the LOWERCASE archive root - microsoft.aspnetcore.app.runtime.{rid}/Release/ -- this directory name - is a load-bearing contract for the future dotnet/crank aspnetcore - overlay path, which mirrors BuildCacheClient's - FindDirectory(extractDir, "microsoft.aspnetcore.app.runtime.{rid}"). - 3. Validates the managed Microsoft.AspNetCore.*.dll assemblies landed under - runtimes/{rid}/lib and strips any stray *.pdb / *.dbg files. - 4. Compresses the lowercase root directory into the per-config archive. - 5. Asserts the archive's single root entry matches the contract. - -.PARAMETER Rids - One or more .NET RIDs to pack (e.g. win-x64, win-x86, win-arm64, linux-x64, - linux-arm64). os/arch and the BCS naming are derived from each RID. - -.PARAMETER Format - Archive format: 'zip' (Windows convention) or 'targz' (Linux convention). - -.PARAMETER ShippingDir - Directory containing the runtime-pack nupkgs. Defaults to - $(Build.SourcesDirectory)/artifacts/packages/Release/Shipping so the - pipeline can call this with no path arguments; override it for local runs. - -.PARAMETER StagingRoot - Directory under which the per-config archive staging folders are created. - Defaults to $(Build.ArtifactStagingDirectory)/bcs. - -.EXAMPLE - # In the pipeline (ShippingDir/StagingRoot resolved from BUILD_* env vars): - ./pack-bcs-archives-aspnetcore.ps1 -Rids win-x64,win-x86,win-arm64 -Format zip - -.EXAMPLE - # Local run -- the BUILD_* env vars are not set off-agent, so pass the - # directories explicitly: - ./pack-bcs-archives-aspnetcore.ps1 -Rids linux-x64 -Format targz ` - -ShippingDir ./artifacts/packages/Release/Shipping ` - -StagingRoot ./artifacts/bcs -#> -[CmdletBinding()] -param( - [Parameter(Mandatory = $true)] - [string[]]$Rids, - - [Parameter(Mandatory = $true)] - [ValidateSet('zip', 'targz')] - [string]$Format, - - # Default to the BUILD_* agent paths in the pipeline; fall back to the current - # directory off-agent (those env vars are unset locally, and Join-Path $null - # throws during parameter binding), keeping the documented local-override flow. - [string]$ShippingDir = (Join-Path ($env:BUILD_SOURCESDIRECTORY ? $env:BUILD_SOURCESDIRECTORY : (Get-Location).Path) 'artifacts/packages/Release/Shipping'), - - [string]$StagingRoot = (Join-Path ($env:BUILD_ARTIFACTSTAGINGDIRECTORY ? $env:BUILD_ARTIFACTSTAGINGDIRECTORY : (Get-Location).Path) 'bcs') -) - -$ErrorActionPreference = 'Stop' -Set-StrictMode -Version Latest -Add-Type -AssemblyName System.IO.Compression.FileSystem - -function Get-RidParts { - param([string]$Rid) - - $dash = $Rid.IndexOf('-') - if ($dash -lt 1) { - throw "RID '$Rid' is not in the expected '-' form." - } - - $osToken = $Rid.Substring(0, $dash) - $arch = $Rid.Substring($dash + 1) - $os = switch ($osToken) { - 'win' { 'windows' } - 'linux' { 'linux' } - 'osx' { 'osx' } - default { throw "Unsupported RID os token '$osToken' in RID '$Rid'." } - } - - return [pscustomobject]@{ Os = $os; Arch = $arch } -} - -if (-not (Test-Path $ShippingDir)) { - throw "Shipping directory '$ShippingDir' does not exist." -} - -New-Item -ItemType Directory -Force -Path $StagingRoot | Out-Null - -foreach ($rid in $Rids) { - $parts = Get-RidParts -Rid $rid - $os = $parts.Os - $arch = $parts.Arch - $configKey = "aspnetcore_${arch}_${os}" - $artifactName = "BuildArtifacts_${os}_${arch}_Release_aspnetcore" - $ext = if ($Format -eq 'zip') { 'zip' } else { 'tar.gz' } - $archiveFile = "${artifactName}.${ext}" - - Write-Host '' - Write-Host "=== Packing $configKey (rid=$rid, format=$Format) ===" - - $pattern = "Microsoft.AspNetCore.App.Runtime.$rid.*.nupkg" - $nupkgMatches = @(Get-ChildItem -Path $ShippingDir -Filter $pattern -File -ErrorAction SilentlyContinue | - Where-Object { $_.Name -notmatch '\.symbols\.nupkg$' } | - Sort-Object Name) - if ($nupkgMatches.Count -eq 0) { - throw "Could not find runtime pack nupkg matching '$pattern' under '$ShippingDir'." - } - if ($nupkgMatches.Count -gt 1) { - # A clean from-source build produces exactly one non-symbols runtime pack - # per RID. More than one means a stale/duplicate version is lingering in - # the Shipping dir; pick-first would silently pack the lexicographically - # smallest (likely wrong) version, so fail loudly instead. - $names = ($nupkgMatches | ForEach-Object { $_.Name }) -join ', ' - throw "Expected exactly one runtime pack nupkg matching '$pattern' under '$ShippingDir', found $($nupkgMatches.Count): $names." - } - $nupkg = $nupkgMatches[0] - Write-Host "Found nupkg: $($nupkg.FullName)" - - # microsoft.aspnetcore.app.runtime.{rid}/Release/ <-- archive root (lowercase!) - $stageDir = Join-Path $StagingRoot $artifactName - $payloadDir = Join-Path $stageDir "microsoft.aspnetcore.app.runtime.$rid" - $releaseDir = Join-Path $payloadDir 'Release' - if (Test-Path $payloadDir) { - Remove-Item -LiteralPath $payloadDir -Recurse -Force - } - New-Item -ItemType Directory -Force -Path $releaseDir | Out-Null - - # Extract the nupkg (it's a zip) into Release/. Using the .NET API keeps the - # extraction identical on Windows and Linux agents (no unzip/Expand-Archive split). - [System.IO.Compression.ZipFile]::ExtractToDirectory($nupkg.FullName, $releaseDir) - - $runtimesDir = Join-Path $releaseDir "runtimes/$rid" - if (-not (Test-Path $runtimesDir)) { - throw "Extracted runtime pack is missing expected directory '$runtimesDir'." - } - - # Sanity-check that managed assemblies landed where we expect. - $libMatches = @(Get-ChildItem -Path (Join-Path $runtimesDir 'lib') -Recurse -Filter 'Microsoft.AspNetCore.*.dll' -ErrorAction SilentlyContinue) - if ($libMatches.Count -eq 0) { - throw "Extracted runtime pack for $rid is missing managed Microsoft.AspNetCore.*.dll under 'runtimes/$rid/lib'." - } - Write-Host "Validated managed lib dir contains $($libMatches.Count) Microsoft.AspNetCore.*.dll(s)." - - # Defensive: strip debug-symbol files that occasionally tag along. - Get-ChildItem -Path $runtimesDir -Recurse -Include '*.pdb', '*.dbg' -ErrorAction SilentlyContinue | - ForEach-Object { Remove-Item -LiteralPath $_.FullName -Force } - - $archivePath = Join-Path $stageDir $archiveFile - if (Test-Path $archivePath) { - Remove-Item -LiteralPath $archivePath -Force - } - - $expectedRoot = "microsoft.aspnetcore.app.runtime.$rid/" - - if ($Format -eq 'zip') { - # Zip the lowercase root directory so the archive contents start with - # `microsoft.aspnetcore.app.runtime.{rid}/...`. - Compress-Archive -Path $payloadDir -DestinationPath $archivePath -CompressionLevel Optimal -Force - - # Assert the archive root matches the load-bearing contract. - $zip = [System.IO.Compression.ZipFile]::OpenRead($archivePath) - try { - $rootEntries = @($zip.Entries | ForEach-Object { ($_.FullName -split '/')[0] } | Sort-Object -Unique) - if ($rootEntries.Count -ne 1 -or "$($rootEntries[0])/" -ne $expectedRoot) { - throw "Archive '$archivePath' has root entries [$($rootEntries -join ', ')] but expected exactly '$expectedRoot' (load-bearing contract for future crank PR)." - } - } - finally { - $zip.Dispose() - } - } - else { - # Tar the lowercase root directory (so the archive contents start with - # `microsoft.aspnetcore.app.runtime.{rid}/...`). - & tar -czf $archivePath -C $stageDir "microsoft.aspnetcore.app.runtime.$rid" - if ($LASTEXITCODE -ne 0) { - throw "tar of '$payloadDir' failed (exit $LASTEXITCODE)." - } - - # Assert the archive root matches the load-bearing contract. - $firstEntry = (& tar -tzf $archivePath | Select-Object -First 1) - if (-not $firstEntry.StartsWith($expectedRoot)) { - throw "Archive '$archivePath' first entry '$firstEntry' does not start with '$expectedRoot' (load-bearing contract for future crank PR)." - } - } - - # Drop the working payload directory so only the archive ends up in the published pipeline artifact. - Remove-Item -LiteralPath $payloadDir -Recurse -Force - - $size = (Get-Item $archivePath).Length - Write-Host "Produced archive: $archivePath ($size bytes)" -} diff --git a/eng/pipelines/tools/stage-bcs-nupkg-aspnetcore.ps1 b/eng/pipelines/tools/stage-bcs-nupkg-aspnetcore.ps1 new file mode 100644 index 00000000000..f10675c94ad --- /dev/null +++ b/eng/pipelines/tools/stage-bcs-nupkg-aspnetcore.ps1 @@ -0,0 +1,135 @@ +<# +.SYNOPSIS + Stages the per-RID Microsoft.AspNetCore.App runtime-pack nupkg as the Build + Cache Service (BCS) artifact for the perf-build pipeline. + +.DESCRIPTION + The single, canonical "find the runtime-pack nupkg and stage it under the BCS + artifact name" recipe used by every job in aspnetcore-perf-build-jobs.yml + (Windows x64/x86/arm64, Linux x64, Linux arm64). Each build job invokes it as + an explicit `pwsh` step with the RID(s) it produced. + + The BCS stores the runtime-pack nupkg VERBATIM (no extract/repack). A nupkg is + a zip on every OS, so dotnet/crank's BuildCacheClient extracts it with the same + ZipFile path on Windows and Linux; the nupkg's own `runtimes/{rid}/...` layout + is the load-bearing contract consumed there. Storing the raw nupkg preserves the + full shipped artifact (crank filters at consume time) rather than a lossy + projection that cannot be re-derived for historical bisection. + + For each RID it: + 1. Locates artifacts/packages/Release/Shipping/Microsoft.AspNetCore.App.Runtime.{rid}.*.nupkg + (excluding the *.symbols.nupkg), asserting exactly one match. + 2. Copies it to $StagingRoot/{artifactName}/{artifactName}.nupkg, renaming to + the fixed, version-independent BCS artifact name crank resolves from a sha. + +.PARAMETER Rids + One or more .NET RIDs to stage (e.g. win-x64, win-x86, win-arm64, linux-x64, + linux-arm64). os/arch and the BCS naming are derived from each RID. + +.PARAMETER ShippingDir + Directory containing the runtime-pack nupkgs. Defaults to + $(Build.SourcesDirectory)/artifacts/packages/Release/Shipping so the pipeline + can call this with no path arguments; override it for local runs. + +.PARAMETER StagingRoot + Directory under which the per-config artifact staging folders are created. + Defaults to $(Build.ArtifactStagingDirectory)/bcs. + +.EXAMPLE + # In the pipeline (ShippingDir/StagingRoot resolved from BUILD_* env vars): + ./stage-bcs-nupkg-aspnetcore.ps1 -Rids win-x64,win-x86,win-arm64 + +.EXAMPLE + # Local run -- the BUILD_* env vars are not set off-agent, so pass the + # directories explicitly: + ./stage-bcs-nupkg-aspnetcore.ps1 -Rids linux-x64 ` + -ShippingDir ./artifacts/packages/Release/Shipping ` + -StagingRoot ./artifacts/bcs +#> +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [string[]]$Rids, + + # Default to the BUILD_* agent paths in the pipeline; fall back to the current + # directory off-agent (those env vars are unset locally, and Join-Path $null + # throws during parameter binding), keeping the documented local-override flow. + [string]$ShippingDir = (Join-Path ($env:BUILD_SOURCESDIRECTORY ? $env:BUILD_SOURCESDIRECTORY : (Get-Location).Path) 'artifacts/packages/Release/Shipping'), + + [string]$StagingRoot = (Join-Path ($env:BUILD_ARTIFACTSTAGINGDIRECTORY ? $env:BUILD_ARTIFACTSTAGINGDIRECTORY : (Get-Location).Path) 'bcs') +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version Latest + +function Get-RidParts { + param([string]$Rid) + + $dash = $Rid.IndexOf('-') + if ($dash -lt 1) { + throw "RID '$Rid' is not in the expected '-' form." + } + + $osToken = $Rid.Substring(0, $dash) + $arch = $Rid.Substring($dash + 1) + $os = switch ($osToken) { + 'win' { 'windows' } + 'linux' { 'linux' } + 'osx' { 'osx' } + default { throw "Unsupported RID os token '$osToken' in RID '$Rid'." } + } + + return [pscustomobject]@{ Os = $os; Arch = $arch } +} + +if (-not (Test-Path $ShippingDir)) { + throw "Shipping directory '$ShippingDir' does not exist." +} + +New-Item -ItemType Directory -Force -Path $StagingRoot | Out-Null + +foreach ($rid in $Rids) { + $parts = Get-RidParts -Rid $rid + $os = $parts.Os + $arch = $parts.Arch + $configKey = "aspnetcore_${arch}_${os}" + $artifactName = "BuildArtifacts_${os}_${arch}_Release_aspnetcore" + + Write-Host '' + Write-Host "=== Staging $configKey (rid=$rid) ===" + + $pattern = "Microsoft.AspNetCore.App.Runtime.$rid.*.nupkg" + $nupkgMatches = @(Get-ChildItem -Path $ShippingDir -Filter $pattern -File -ErrorAction SilentlyContinue | + Where-Object { $_.Name -notmatch '\.symbols\.nupkg$' } | + Sort-Object Name) + if ($nupkgMatches.Count -eq 0) { + throw "Could not find runtime pack nupkg matching '$pattern' under '$ShippingDir'." + } + if ($nupkgMatches.Count -gt 1) { + # A clean from-source build produces exactly one non-symbols runtime pack + # per RID. More than one means a stale/duplicate version is lingering in + # the Shipping dir; pick-first would silently stage the lexicographically + # smallest (likely wrong) version, so fail loudly instead. + $names = ($nupkgMatches | ForEach-Object { $_.Name }) -join ', ' + throw "Expected exactly one runtime pack nupkg matching '$pattern' under '$ShippingDir', found $($nupkgMatches.Count): $names." + } + $nupkg = $nupkgMatches[0] + Write-Host "Found nupkg: $($nupkg.FullName)" + + # Stage the nupkg verbatim under the fixed, version-independent BCS artifact + # name. The real nupkg filename embeds the build version (e.g. + # ...win-x64.10.0.0-dev.nupkg), which crank cannot predict from a commit sha; + # crank resolves the blob as .../{configKey}/{artifactFile} with a fixed + # artifactFile, so we rename to the predictable name here. + $stageDir = Join-Path $StagingRoot $artifactName + if (Test-Path $stageDir) { + Remove-Item -LiteralPath $stageDir -Recurse -Force + } + New-Item -ItemType Directory -Force -Path $stageDir | Out-Null + + $stagedPath = Join-Path $stageDir "$artifactName.nupkg" + Copy-Item -LiteralPath $nupkg.FullName -Destination $stagedPath -Force + + $size = (Get-Item $stagedPath).Length + Write-Host "Staged nupkg: $stagedPath ($size bytes)" +} diff --git a/eng/pipelines/upload-build-artifacts-jobs.yml b/eng/pipelines/upload-build-artifacts-jobs.yml index 1d783a6740a..27899bbc2db 100644 --- a/eng/pipelines/upload-build-artifacts-jobs.yml +++ b/eng/pipelines/upload-build-artifacts-jobs.yml @@ -200,7 +200,7 @@ jobs: sha: ${{ parameters.sha }} artifacts: - artifactName: 'BuildArtifacts_linux_x64_Release_aspnetcore' - files: [ 'BuildArtifacts_linux_x64_Release_aspnetcore.tar.gz' ] + files: [ 'BuildArtifacts_linux_x64_Release_aspnetcore.nupkg' ] - ${{ if containsValue(parameters.buildType, 'aspnetcore_arm64_linux') }}: - template: /eng/pipelines/templates/upload-build-artifacts-job.yml@${{ parameters.performanceRepoAlias }} @@ -211,7 +211,7 @@ jobs: sha: ${{ parameters.sha }} artifacts: - artifactName: 'BuildArtifacts_linux_arm64_Release_aspnetcore' - files: [ 'BuildArtifacts_linux_arm64_Release_aspnetcore.tar.gz' ] + files: [ 'BuildArtifacts_linux_arm64_Release_aspnetcore.nupkg' ] - ${{ if containsValue(parameters.buildType, 'aspnetcore_x64_windows') }}: - template: /eng/pipelines/templates/upload-build-artifacts-job.yml@${{ parameters.performanceRepoAlias }} @@ -222,7 +222,7 @@ jobs: sha: ${{ parameters.sha }} artifacts: - artifactName: 'BuildArtifacts_windows_x64_Release_aspnetcore' - files: [ 'BuildArtifacts_windows_x64_Release_aspnetcore.zip' ] + files: [ 'BuildArtifacts_windows_x64_Release_aspnetcore.nupkg' ] - ${{ if containsValue(parameters.buildType, 'aspnetcore_arm64_windows') }}: - template: /eng/pipelines/templates/upload-build-artifacts-job.yml@${{ parameters.performanceRepoAlias }} @@ -233,7 +233,7 @@ jobs: sha: ${{ parameters.sha }} artifacts: - artifactName: 'BuildArtifacts_windows_arm64_Release_aspnetcore' - files: [ 'BuildArtifacts_windows_arm64_Release_aspnetcore.zip' ] + files: [ 'BuildArtifacts_windows_arm64_Release_aspnetcore.nupkg' ] - ${{ if containsValue(parameters.buildType, 'aspnetcore_x86_windows') }}: - template: /eng/pipelines/templates/upload-build-artifacts-job.yml@${{ parameters.performanceRepoAlias }} @@ -244,4 +244,4 @@ jobs: sha: ${{ parameters.sha }} artifacts: - artifactName: 'BuildArtifacts_windows_x86_Release_aspnetcore' - files: [ 'BuildArtifacts_windows_x86_Release_aspnetcore.zip' ] + files: [ 'BuildArtifacts_windows_x86_Release_aspnetcore.nupkg' ]