Skip to content
139 changes: 138 additions & 1 deletion .vsts-dnup-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,24 @@ extends:
containers:
azureLinux30Amd64:
image: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-build-amd64
# Cross-build containers for NAOT executables (link against older glibc/musl sysroots).
# See: https://github.com/dotnet/runtime/blob/main/docs/workflow/using-docker.md
crossBuildLinuxX64:
image: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-cross-amd64
env:
ROOTFS_DIR: /crossrootfs/x64
crossBuildLinuxArm64:
image: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-cross-arm64
env:
ROOTFS_DIR: /crossrootfs/arm64
crossBuildMuslX64:
image: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-cross-amd64-musl
env:
ROOTFS_DIR: /crossrootfs/x64
crossBuildMuslArm64:
image: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-cross-arm64-musl
env:
ROOTFS_DIR: /crossrootfs/arm64

sdl:
sourceAnalysisPool:
Expand Down Expand Up @@ -135,4 +153,123 @@ extends:
pool:
name: $(DncEngInternalBuildPool)
image: 1es-windows-2022
os: windows
os: windows

### Executables ###
- stage: executables
displayName: 🏗️ Build dotnetup executables
dependsOn: []
jobs:
############### WINDOWS ###############
### win-x64 ###
- template: /eng/pipelines/templates/jobs/dotnetup/dotnetup-executables.yml@self
parameters:
rid: win-x64
pool:
name: $(DncEngInternalBuildPool)
image: windows.vs2022.amd64
os: windows
emoji: 🪟
oneESCompat:
templateFolderName: templates-official
publishTaskPrefix: 1ES.
preSteps:
- powershell: New-Item -ItemType Directory -Path $(Build.SourcesDirectory)/artifacts/bin -Force
displayName: Create artifacts/bin directory
### win-arm64 ###
- template: /eng/pipelines/templates/jobs/dotnetup/dotnetup-executables.yml@self
parameters:
rid: win-arm64
pool:
name: $(DncEngInternalBuildPool)
image: windows.vs2022.amd64
os: windows
emoji: 💪
oneESCompat:
templateFolderName: templates-official
publishTaskPrefix: 1ES.
preSteps:
- powershell: New-Item -ItemType Directory -Path $(Build.SourcesDirectory)/artifacts/bin -Force
displayName: Create artifacts/bin directory

############### LINUX ###############
### linux-x64 ###
- template: /eng/pipelines/templates/jobs/dotnetup/dotnetup-executables.yml@self
parameters:
rid: linux-x64
container: crossBuildLinuxX64
pool:
name: $(DncEngInternalBuildPool)
image: 1es-ubuntu-2204
os: linux
emoji: 🐧
oneESCompat:
templateFolderName: templates-official
publishTaskPrefix: 1ES.
### linux-arm64 ###
- template: /eng/pipelines/templates/jobs/dotnetup/dotnetup-executables.yml@self
parameters:
rid: linux-arm64
container: crossBuildLinuxArm64
pool:
name: $(DncEngInternalBuildPool)
image: 1es-ubuntu-2204
os: linux
emoji: 💪
oneESCompat:
templateFolderName: templates-official
publishTaskPrefix: 1ES.
### linux-musl-x64 ###
- template: /eng/pipelines/templates/jobs/dotnetup/dotnetup-executables.yml@self
parameters:
rid: linux-musl-x64
container: crossBuildMuslX64
pool:
name: $(DncEngInternalBuildPool)
image: 1es-ubuntu-2204
os: linux
emoji: 🏔️
oneESCompat:
templateFolderName: templates-official
publishTaskPrefix: 1ES.
enableSbom: false
### linux-musl-arm64 ###
- template: /eng/pipelines/templates/jobs/dotnetup/dotnetup-executables.yml@self
parameters:
rid: linux-musl-arm64
container: crossBuildMuslArm64
pool:
name: $(DncEngInternalBuildPool)
image: 1es-ubuntu-2204
os: linux
emoji: 🏔️
oneESCompat:
templateFolderName: templates-official
publishTaskPrefix: 1ES.
enableSbom: false

############### MACOS ###############
### osx-x64 ###
- template: /eng/pipelines/templates/jobs/dotnetup/dotnetup-executables.yml@self
parameters:
rid: osx-x64
pool:
name: Azure Pipelines
image: macOS-latest
os: macOS
emoji: 🍎
oneESCompat:
templateFolderName: templates-official
publishTaskPrefix: 1ES.
### osx-arm64 ###
- template: /eng/pipelines/templates/jobs/dotnetup/dotnetup-executables.yml@self
parameters:
rid: osx-arm64
pool:
name: Azure Pipelines
image: macOS-15-arm64
os: macOS
emoji: 💪
oneESCompat:
templateFolderName: templates-official
publishTaskPrefix: 1ES.
16 changes: 15 additions & 1 deletion .vsts-dnup-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,23 @@ stages:
parameters:
pool:
name: Azure Pipelines
vmImage: macOS-latest
vmImage: macOS-15-arm64
os: macOS
emoji: 💪
helixTargetQueue: osx.13.arm64.open

- stage: executables
displayName: 🏗️ Build dotnetup executables
dependsOn: []
jobs:
### Build a single platform executable for PR validation ###
- template: /eng/pipelines/templates/jobs/dotnetup/dotnetup-executables.yml@self
parameters:
rid: linux-x64
pool:
name: $(DncEngPublicBuildPool)
demands: ImageOverride -equals build.ubuntu.2204.amd64.open
os: linux
emoji: 🐧


81 changes: 81 additions & 0 deletions eng/pipelines/templates/jobs/dotnetup/dotnetup-executables.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
parameters:
### GENERAL ###
variables: {}
dependsOn: ''
oneESCompat:
templateFolderName: templates
publishTaskPrefix: ''
enableSbom: true
timeoutInMinutes: 150
### PUBLISH ###
# The RID to publish for (e.g. win-x64, linux-arm64, osx-x64, linux-musl-x64, etc.)
rid: ''
pool: {}
container: ''
preSteps: []

jobs:
- template: /eng/common/${{ parameters.oneESCompat.templateFolderName }}/job/job.yml
parameters:
displayName: '${{ parameters.pool.emoji }} dotnetup executable: ${{ parameters.rid }}'
pool: ${{ parameters.pool }}
container: ${{ parameters.container }}
helixRepo: dotnet/sdk
timeoutInMinutes: ${{ parameters.timeoutInMinutes }}
enableMicrobuild: true
enablePublishBuildAssets: true
enableTelemetry: true
enablePublishUsingPipelines: true
enableSbom: ${{ parameters.enableSbom }}
variables:
- ${{ insert }}: ${{ parameters.variables }}
- name: _officialBuildProperties
value: /p:OfficialBuilder=Microsoft /p:OfficialBuildId=$(Build.BuildNumber)
dependsOn: ${{ parameters.dependsOn }}
preSteps: ${{ parameters.preSteps }}
templateContext:
sdl:
binskim:
analyzeTargetGlob: +:f|artifacts\bin\**\*.dll;+:f|artifacts\bin\**\*.exe;-:f|artifacts\bin\*Tests\**;
outputs:
- output: pipelineArtifact
displayName: '📦 Publish dotnetup ${{ parameters.rid }} executable'
condition: always()
targetPath: '$(Build.SourcesDirectory)/artifacts/publish/dotnetup/${{ parameters.rid }}/'
artifactName: 'dotnetup-executable-${{ parameters.rid }}'
publishLocation: Container
- output: pipelineArtifact
displayName: '📊 Publish dotnetup ${{ parameters.rid }} binlogs'
condition: always()
targetPath: '$(Build.SourcesDirectory)/artifacts/binlogs/'
artifactName: 'dotnetup-executable-binlogs-${{ parameters.rid }}'
publishLocation: Container

steps:
- ${{ if eq(parameters.pool.os, 'windows') }}:
- powershell: |
& .\restore.cmd $(_officialBuildProperties)
displayName: 🍱 Bootstrap toolset (Windows)
- powershell: |
New-Item -ItemType Directory -Path "$(Build.SourcesDirectory)/artifacts/publish/dotnetup/${{ parameters.rid }}" -Force
& .\.dotnet\dotnet publish src\Installer\dotnetup\dotnetup.csproj -c Release -r ${{ parameters.rid }} --self-contained -p:EnforceCodeStyleInBuild=false -bl:$(Build.SourcesDirectory)/artifacts/binlogs/dotnetup-publish-${{ parameters.rid }}.binlog $(_officialBuildProperties)
# Copy the published output to the artifact staging directory (use wildcard for TFM to avoid hardcoding)
$publishDir = Get-ChildItem -Path "$(Build.SourcesDirectory)\artifacts\bin\dotnetup\Release" -Directory | Select-Object -First 1
Copy-Item -Path "$($publishDir.FullName)\${{ parameters.rid }}\publish\*" -Destination "$(Build.SourcesDirectory)\artifacts\publish\dotnetup\${{ parameters.rid }}\" -Recurse -Force
displayName: 🚀 Publish dotnetup (${{ parameters.rid }})

- ${{ if ne(parameters.pool.os, 'windows') }}:
- script: |
./restore.sh $(_officialBuildProperties)
displayName: 🍱 Bootstrap toolset (Unix)
- script: |
mkdir -p "$(Build.SourcesDirectory)/artifacts/publish/dotnetup/${{ parameters.rid }}"
CROSS_ARGS=""
if [ -n "$ROOTFS_DIR" ]; then
CROSS_ARGS="-p:SysRoot=$ROOTFS_DIR -p:LinkerFlavor=lld"
fi
./.dotnet/dotnet publish src/Installer/dotnetup/dotnetup.csproj -c Release -r ${{ parameters.rid }} --self-contained $CROSS_ARGS -p:EnforceCodeStyleInBuild=false -bl:$(Build.SourcesDirectory)/artifacts/binlogs/dotnetup-publish-${{ parameters.rid }}.binlog $(_officialBuildProperties)
# Copy the published output to the artifact staging directory (use wildcard for TFM to avoid hardcoding)
PUBLISH_DIR=$(find "$(Build.SourcesDirectory)/artifacts/bin/dotnetup/Release" -mindepth 1 -maxdepth 1 -type d | head -1)
cp -r "$PUBLISH_DIR/${{ parameters.rid }}/publish/"* "$(Build.SourcesDirectory)/artifacts/publish/dotnetup/${{ parameters.rid }}/"
displayName: 🚀 Publish dotnetup (${{ parameters.rid }})
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ jobs:
& .\restore.cmd $(_officialBuildProperties)
displayName: 🍱 Bootstrap toolset (Windows)
- powershell: |
& .\.dotnet\dotnet build test\dotnetup.Tests\dotnetup.Tests.csproj -c Release -bl:$(Build.SourcesDirectory)/artifacts/binlogs/dotnetup-library-build.binlog $(_officialBuildProperties)
& .\.dotnet\dotnet build test\dotnetup.Tests\dotnetup.Tests.csproj -c Release -p:EnforceCodeStyleInBuild=false -bl:$(Build.SourcesDirectory)/artifacts/binlogs/dotnetup-library-build.binlog $(_officialBuildProperties)
displayName: 💻 Build Windows
- powershell: |
& .\.dotnet\dotnet pack .\src\Installer\Microsoft.Dotnet.Installation\Microsoft.Dotnet.Installation.csproj -bl:$(Build.SourcesDirectory)/artifacts/binlogs/dotnetup-library-package.binlog $(_officialBuildProperties)
Expand Down
6 changes: 6 additions & 0 deletions eng/pipelines/templates/jobs/dotnetup/dotnetup-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ jobs:
- powershell: |
& .\.dotnet\dotnet build test\dotnetup.Tests\dotnetup.Tests.csproj -c Release --no-restore -p:TreatWarningsAsErrors=false
displayName: 💻 Build Windows
- powershell: |
& .\.dotnet\dotnet publish src\Installer\dotnetup\dotnetup.csproj -c Release --self-contained
displayName: 🔨 Publish dotnetup AOT (Windows)
- powershell: |
New-Item -Path "$(Build.SourcesDirectory)/artifacts/dotnetupTestResults" -ItemType Directory -Force
displayName: 📁 Create test results directory (Windows)
Expand All @@ -72,6 +75,9 @@ jobs:
- script: |
./.dotnet/dotnet build test/dotnetup.Tests/dotnetup.Tests.csproj -c Release --no-restore -p:TreatWarningsAsErrors=false
displayName: 🐧 Build (Unix)
- script: |
./.dotnet/dotnet publish src/Installer/dotnetup/dotnetup.csproj -c Release --self-contained
displayName: 🔨 Publish dotnetup AOT (Unix)
- script: |
mkdir -p "$(Build.SourcesDirectory)/artifacts/dotnetupTestResults"
displayName: 📁 Create test results directory (Unix)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ internal class DotnetArchiveDownloader : IArchiveDownloader

private readonly HttpClient _httpClient;
private readonly bool _shouldDisposeHttpClient;
private readonly ReleaseManifest _releaseManifest;
private ReleaseManifest _releaseManifest;
private readonly DownloadCache _downloadCache;

public DotnetArchiveDownloader()
Expand Down Expand Up @@ -81,7 +81,7 @@ private static HttpClient CreateDefaultHttpClient()
/// <param name="downloadUrl">The URL to download from</param>
/// <param name="destinationPath">The local path to save the downloaded file</param>
/// <param name="progress">Optional progress reporting</param>
private async Task DownloadArchiveAsync(string downloadUrl, string destinationPath, IProgress<DownloadProgress>? progress = null)
async Task DownloadArchiveAsync(string downloadUrl, string destinationPath, IProgress<DownloadProgress>? progress = null)
{
// Create temp file path in same directory for atomic move when complete
string tempPath = $"{destinationPath}.download";
Expand All @@ -97,15 +97,15 @@ private async Task DownloadArchiveAsync(string downloadUrl, string destinationPa
long? totalBytes = null;

// Make the actual download request
using var response = await _httpClient.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
using var response = await _httpClient.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();

if (response.Content.Headers.ContentLength.HasValue)
{
totalBytes = response.Content.Headers.ContentLength.Value;
}

using var contentStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
using var contentStream = await response.Content.ReadAsStreamAsync();
using var fileStream = new FileStream(tempPath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true);

var buffer = new byte[81920]; // 80KB buffer
Expand All @@ -114,9 +114,9 @@ private async Task DownloadArchiveAsync(string downloadUrl, string destinationPa

var lastProgressReport = DateTime.MinValue;

while ((read = await contentStream.ReadAsync(buffer).ConfigureAwait(false)) > 0)
while ((read = await contentStream.ReadAsync(buffer)) > 0)
{
await fileStream.WriteAsync(buffer.AsMemory(0, read)).ConfigureAwait(false);
await fileStream.WriteAsync(buffer.AsMemory(0, read));

bytesRead += read;

Expand All @@ -133,7 +133,7 @@ private async Task DownloadArchiveAsync(string downloadUrl, string destinationPa
progress?.Report(new DownloadProgress(bytesRead, totalBytes));

// Ensure all data is written to disk
await fileStream.FlushAsync().ConfigureAwait(false);
await fileStream.FlushAsync();
fileStream.Close();

// Atomic move to final destination
Expand All @@ -149,7 +149,7 @@ private async Task DownloadArchiveAsync(string downloadUrl, string destinationPa
{
if (attempt < MaxRetryCount)
{
await Task.Delay(RetryDelayMilliseconds * attempt).ConfigureAwait(false); // Linear backoff
await Task.Delay(RetryDelayMilliseconds * attempt); // Linear backoff
}
else
{
Expand Down Expand Up @@ -182,7 +182,7 @@ private async Task DownloadArchiveAsync(string downloadUrl, string destinationPa
/// <param name="downloadUrl">The URL to download from</param>
/// <param name="destinationPath">The local path to save the downloaded file</param>
/// <param name="progress">Optional progress reporting</param>
private void DownloadArchive(string downloadUrl, string destinationPath, IProgress<DownloadProgress>? progress = null)
void DownloadArchive(string downloadUrl, string destinationPath, IProgress<DownloadProgress>? progress = null)
{
DownloadArchiveAsync(downloadUrl, destinationPath, progress).GetAwaiter().GetResult();
}
Expand Down Expand Up @@ -281,6 +281,7 @@ public void DownloadArchiveWithVerification(
}
}


/// <summary>
/// Computes the SHA512 hash of a file.
/// </summary>
Expand All @@ -305,7 +306,11 @@ public static string ComputeFileHash(string filePath)
// osx-x64 dotnet-runtime-7.0.20 — repackaged archive with different hash
[
"acdcde92f2f2e43584ee59be447f778f4a152c308975c7bdc5c2372b5bbd3092eb9d2233aec3b82756ba1e352a0877ffc17e4c8cfb20a9de91ca6db54d79b591"
] = "5cc4e788f9fffefb4a92ada95c9d99011fb7d52eb213e6e33b10ff02c63b2cc59fb8d01bfe247e3067a5cadb69feda03da3dccb20f662a8afe24c53a9cbad891"
] = "5cc4e788f9fffefb4a92ada95c9d99011fb7d52eb213e6e33b10ff02c63b2cc59fb8d01bfe247e3067a5cadb69feda03da3dccb20f662a8afe24c53a9cbad891",
// osx-arm64 dotnet-runtime-7.0.20 — repackaged archive with different hash
[
"af1cb62e29c69648ebe334e651c2703cd5e87fa0bb28c670bacb3b3dd1608aeae35ae53402c5eb4ed8bf34abd831a08ccb5ef84e5ec70617d9f8d9969fe7b8fa"
] = "5eb176f4df5ea0795cf29f01661913cf1f5b8c8b889e80c175de8fe7984cb8db4d8aa188556832e9fcd9973b1a4ca065545ce1db9a2e3fc492466b931a58554b"
};

/// <summary>
Expand Down
Loading
Loading