Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c267ff4
Add AzDO RID CLI tool packaging
radical Apr 1, 2026
ca58541
Fix AzDO CLI tool packaging build
radical Apr 1, 2026
54d4a65
Avoid notarizing staged Mac tool packages
radical Apr 1, 2026
23ecbc9
Run mac native build before tool packing
radical Apr 1, 2026
6939ef1
Merge remote-tracking branch 'origin/main' into ankj/dotnet-tool-naot…
radical Apr 1, 2026
6923588
Fix unofficial pipeline merge resolution
radical Apr 1, 2026
8b0aa52
Repair unofficial pipeline YAML
radical Apr 1, 2026
2ba9546
Stage non-Windows tool packages after native build
radical Apr 2, 2026
e314153
Fix Windows AzDO tool packaging
radical Apr 2, 2026
44a68a1
Fix Windows projects switch
radical Apr 2, 2026
1552577
Gate VS Code publish validation
radical Apr 2, 2026
3925125
Fix pointer package validation
radical Apr 2, 2026
a7dc41e
Deduplicate CLI pointer package validation
radical Apr 2, 2026
f5998f2
Fix CLI tool smoke test package sources
radical Apr 2, 2026
8371048
Fix BuildAndTest YAML indentation
radical Apr 2, 2026
11c3962
Merge remote-tracking branch 'origin/main' into ankj/dotnet-tool-naot…
radical Apr 4, 2026
36fd5b1
Merge remote-tracking branch 'origin/main' into ankj/dotnet-tool-naot…
radical Apr 4, 2026
57d7f2f
Merge remote-tracking branch 'origin/main' into ankj/dotnet-tool-naot…
radical Apr 4, 2026
e842afd
Fix CLI tool smoke test executable discovery
radical Apr 4, 2026
b0e6b6b
Fix CLI smoke test: use --help instead of help subcommand
radical Apr 4, 2026
48f3f42
Merge remote-tracking branch 'origin/main' into ankj/dotnet-tool-naot…
radical Apr 6, 2026
9479fb0
Add NativeAOT binary smoke test validation
radical Apr 7, 2026
5f772d4
Fix smoke test binary path resolution for standalone invocation
radical Apr 7, 2026
e10d54b
Merge remote-tracking branch 'origin/main' into ankj/dotnet-tool-naot…
radical Apr 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/workflows/build-cli-native-archives.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,30 @@ jobs:
/p:BundlePayloadPath=${{ github.workspace }}/artifacts/bundle/aspire-ci-bundlepayload-${{ matrix.targets.rids }}.tar.gz
${{ inputs.versionOverrideArg }}

- name: Smoke test CLI binary (Windows)
if: ${{ runner.os == 'Windows' }}
shell: pwsh
run: >
.\dotnet.cmd
msbuild
eng/clipack/Aspire.Cli.${{ matrix.targets.rids }}.csproj
/t:SmokeTestNativeBinary
/p:Configuration=${{ inputs.configuration }}
/bl:${{ github.workspace }}/artifacts/log/${{ inputs.configuration }}/SmokeTest.binlog
${{ inputs.versionOverrideArg }}

- name: Smoke test CLI binary (Unix)
if: ${{ runner.os != 'Windows' }}
shell: bash
run: >
./dotnet.sh
msbuild
eng/clipack/Aspire.Cli.${{ matrix.targets.rids }}.csproj
/t:SmokeTestNativeBinary
/p:Configuration=${{ inputs.configuration }}
/bl:${{ github.workspace }}/artifacts/log/${{ inputs.configuration }}/SmokeTest.binlog
${{ inputs.versionOverrideArg }}

- name: Upload CLI archives
if: success()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
Expand Down
2 changes: 2 additions & 0 deletions eng/Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
SkipTestProjects - if 'true', skips building test projects, defaults to 'false'
SkipPlaygroundProjects - if 'true', skips building playground projects, defaults to 'false'
SkipBundleDeps - if 'true', skips building eng/dcppack, eng/dashboardpack, Aspire.Dashboard, and Aspire.Cli projects during the main managed build, defaults to 'false'
SkipCliToolProject - if 'true', removes Aspire.Cli.Tool.csproj from the main managed build, defaults to 'false'
BuildBundleDepsOnly - if 'true', builds only the RID-specific eng/dcppack and eng/dashboardpack projects for $(TargetRids), plus Aspire.Dashboard for restore, defaults to 'false'
TargetRids - colon separated list of RIDs to build native projects for
-->
Expand Down Expand Up @@ -45,6 +46,7 @@

<ProjectToBuild Include="$(RepoRoot)src\**\*.csproj" Exclude="$(RepoRoot)src\Aspire.ProjectTemplates\templates\**\*.csproj" />
<ProjectToBuild Remove="$(RepoRoot)src\Aspire.Cli\Aspire.Cli.*csproj" Condition="'$(SkipBundleDeps)' == 'true'" />
<ProjectToBuild Remove="$(RepoRoot)src\Aspire.Cli\Aspire.Cli.Tool.csproj" Condition="'$(SkipCliToolProject)' == 'true'" />
<ProjectToBuild Remove="$(RepoRoot)src\Aspire.Dashboard\Aspire.Dashboard.csproj" Condition="'$(SkipBundleDeps)' == 'true'" />
<ProjectToBuild Include="$(RepoRoot)eng\dcppack\**\*.csproj" Condition="'$(SkipBundleDeps)' != 'true'" />
<ProjectToBuild Include="$(RepoRoot)eng\dashboardpack\**\*.csproj" Condition="'$(SkipBundleDeps)' != 'true'" />
Expand Down
27 changes: 22 additions & 5 deletions eng/Publishing.props
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@
<_ArchiveFiles Include="$(ArtifactsPackagesDir)\**\aspire-cli-*.zip" />
<_ArchiveFiles Include="$(ArtifactsPackagesDir)\**\aspire-cli-*.tar.gz" />
<!-- VS Code extension artifacts: VSIX, manifest, and signature file -->
<_ExtensionFilesToPublish Include="$(ArtifactsPackagesDir)**\aspire-vscode-*.vsix" />
<_ExtensionManifestFiles Include="$(ArtifactsPackagesDir)**\aspire-vscode-*.manifest" />
<_ExtensionSignatureFiles Include="$(ArtifactsPackagesDir)**\aspire-vscode-*.signature.p7s" />
<_ExtensionFilesToPublish Include="$(ArtifactsPackagesDir)**\aspire-vscode-*.vsix" Condition="'$(BuildExtension)' == 'true'" />
<_ExtensionManifestFiles Include="$(ArtifactsPackagesDir)**\aspire-vscode-*.manifest" Condition="'$(BuildExtension)' == 'true'" />
<_ExtensionSignatureFiles Include="$(ArtifactsPackagesDir)**\aspire-vscode-*.signature.p7s" Condition="'$(BuildExtension)' == 'true'" />
</ItemGroup>

<!-- Validate VS Code extension artifacts: we expect exactly 1 VSIX and 1 manifest file.
The signature file is created by signVsix.proj which runs after publish, so validation happens there. -->
<Error Condition="@(_ExtensionFilesToPublish->Count()) != 1" Text="Expected exactly 1 VS Code extension VSIX file but found @(_ExtensionFilesToPublish->Count()). Files: @(_ExtensionFilesToPublish, ', ')" />
<Error Condition="@(_ExtensionManifestFiles->Count()) != 1" Text="Expected exactly 1 VS Code extension manifest file but found @(_ExtensionManifestFiles->Count()). Files: @(_ExtensionManifestFiles, ', ')" />
<Error Condition="'$(BuildExtension)' == 'true' and @(_ExtensionFilesToPublish->Count()) != 1" Text="Expected exactly 1 VS Code extension VSIX file but found @(_ExtensionFilesToPublish->Count()). Files: @(_ExtensionFilesToPublish, ', ')" />
<Error Condition="'$(BuildExtension)' == 'true' and @(_ExtensionManifestFiles->Count()) != 1" Text="Expected exactly 1 VS Code extension manifest file but found @(_ExtensionManifestFiles->Count()). Files: @(_ExtensionManifestFiles, ', ')" />
<Error Condition="!Exists('$(RepoRoot)eng\scripts\get-aspire-cli.sh')" Text="Expected CLI install script at $(RepoRoot)eng\scripts\get-aspire-cli.sh." />
<Error Condition="!Exists('$(RepoRoot)eng\scripts\get-aspire-cli.ps1')" Text="Expected CLI install script at $(RepoRoot)eng\scripts\get-aspire-cli.ps1." />

Expand All @@ -55,13 +55,30 @@

<_CliPackProjects Include="$(RepoRoot)eng\clipack\Aspire.Cli.*.csproj" />
<_ExpectedRids Include="@(_CliPackProjects->'%(Filename)'->Replace('Aspire.Cli.', ''))" />
<_CliToolRidPackageFiles Include="$(ArtifactsPackagesDir)\**\Aspire.Cli.*.nupkg" Exclude="$(ArtifactsPackagesDir)\**\*.symbols.nupkg" />
<_CliToolPointerPackageFiles Include="$(ArtifactsPackagesDir)\**\Aspire.Cli.*.nupkg" Exclude="$(ArtifactsPackagesDir)\**\*.symbols.nupkg" />
<_CliToolPointerPackageFiles Remove="@(_CliToolPointerPackageFiles)" Condition="!$([System.Text.RegularExpressions.Regex]::IsMatch('%(_CliToolPointerPackageFiles.FileName)', '^Aspire\.Cli\.\d+'))" />
</ItemGroup>

<PropertyGroup>
<_ExpectedCliToolRidPattern>@(_ExpectedRids, '|')</_ExpectedCliToolRidPattern>
</PropertyGroup>

<ItemGroup>
<_CliToolRidPackageFilesWithRid Include="@(_CliToolRidPackageFiles)">
<ExtractedRid>$([System.Text.RegularExpressions.Regex]::Match('%(_CliToolRidPackageFiles.FileName)', '^Aspire\.Cli\.($(_ExpectedCliToolRidPattern))\.\d+').Groups[1].Value)</ExtractedRid>
</_CliToolRidPackageFilesWithRid>
<_DistinctCliToolPointerPackageFiles Include="@(_CliToolPointerPackageFiles -> '%(Filename)%(Extension)' -> Distinct())" />

<_MissingRids Include="@(_ExpectedRids)" Exclude="@(_ArchiveFilesWithRid -> '%(ExtractedRid)')" />
<_UnexpectedRids Include="@(_ArchiveFilesWithRid -> '%(ExtractedRid)')" Exclude="@(_ExpectedRids)" />
<_MissingCliToolRids Include="@(_ExpectedRids)" Exclude="@(_CliToolRidPackageFilesWithRid -> '%(ExtractedRid)')" />
</ItemGroup>

<Warning Condition="@(_UnexpectedRids->Count()) > 0" Text="Found unexpected CLI archives for @(_UnexpectedRids, ',') . These are all the cli archives found - @(_ArchiveFiles, ', ')" />
<Error Condition="@(_MissingRids->Count()) > 0" Text="Missing CLI archive(s) for runtime identifiers: @(_MissingRids, ', '). These are all the cli archives found - @(_ArchiveFiles, ', ')" />
<Error Condition="@(_MissingCliToolRids->Count()) > 0" Text="Missing RID-specific Aspire.Cli tool package(s) for runtime identifiers: @(_MissingCliToolRids, ', '). These are all the CLI tool packages found - @(_CliToolRidPackageFiles, ', ')" />
<Error Condition="@(_DistinctCliToolPointerPackageFiles->Count()) != 1" Text="Expected exactly 1 Aspire.Cli pointer package but found @(_DistinctCliToolPointerPackageFiles->Count()). Unique package names: @(_DistinctCliToolPointerPackageFiles, ', '). Files: @(_CliToolPointerPackageFiles, ', ')" />

<!-- Validate aspire-cli archive file extensions match expected format for each RID -->
<ItemGroup>
Expand Down
5 changes: 5 additions & 0 deletions eng/build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Param(
[switch]$mauirestore,
[switch]$bundle,
[string]$runtimeVersion,
[string]$projects,

[Parameter(ValueFromRemainingArguments=$true)][String[]]$properties
)
Expand Down Expand Up @@ -51,6 +52,9 @@ function Get-Help() {
Write-Host " -bundle Build the self-contained bundle (CLI + Runtime + Dashboard + DCP)."
Write-Host " -runtimeVersion <ver> .NET runtime version for bundle (default: from eng/Versions.props RuntimeVersion)."
Write-Host ""
Write-Host "Advanced settings:"
Write-Host " -projects <value> Semi-colon delimited list of sln/proj's to build. Globbing is supported (*.sln)."
Write-Host ""

Write-Host "Command-line arguments not listed above are passed through to MSBuild."
Write-Host "The above arguments can be shortened as much as to be unambiguous."
Expand Down Expand Up @@ -108,6 +112,7 @@ foreach ($argument in $PSBoundParameters.Keys)
"mauirestore" { $arguments += " -restoreMaui" }
"bundle" { } # Handled after main build
"runtimeVersion" { } # Handled after main build
"projects" { $arguments += " -projects " + $($PSBoundParameters[$argument]) }
default { $arguments += " /p:$argument=$($PSBoundParameters[$argument])" }
}
}
Expand Down
62 changes: 62 additions & 0 deletions eng/clipack/Common.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,66 @@
vs regex: $(FileSignatureRegex)" />
</Target>

<!--
Smoke test target: validates the native binary after build.
1. File signature check (Unix only, works for cross-compiled binaries)
2. aspire - -version execution (only when host can run the binary)

_CanExecuteNativeBinary is true only for exact OS+arch matches:
linux-x64 on linux-x64 glibc host, osx-arm64 on osx-arm64 host, win-x64 on win-x64 host
musl binaries cannot run on glibc hosts so are excluded.
-->
<PropertyGroup>
<!-- Host architecture as a RID-style string (X64 -> x64, Arm64 -> arm64) -->
<_HostArch>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLowerInvariant())</_HostArch>

<!-- Determine whether the binary can actually execute on this machine.
PublishNativeAot already ensures same-OS; we additionally check:
- Architecture match (CliRuntime must end with the host arch)
- Not a musl binary on a glibc host (linux-musl-* can't run on standard linux) -->
<_CanExecuteNativeBinary Condition="'$(PublishNativeAot)' == 'true'
and $(CliRuntime.EndsWith('$(_HostArch)'))
and !$(CliRuntime.Contains('musl'))">true</_CanExecuteNativeBinary>
</PropertyGroup>

<!-- When SmokeTestNativeBinary is invoked standalone (not as part of PublishToDisk),
$(OutputPath) may differ from the path used during the build. The Microsoft.Build.NoTargets
SDK publishes into the intermediate output tree, but a standalone msbuild invocation
evaluates OutputPath to the bin tree. Search the artifacts directory for the binary. -->
<Target Name="_ResolveSmokeTestBinaryPath">
<Message Importance="high" Text="Looking for native binary at default path: $(_NativeBinaryPath)" />

<ItemGroup Condition="!Exists('$(_NativeBinaryPath)')">
<_CandidateBinary Condition="!$(CliRuntime.StartsWith('win-'))"
Include="$(RepoRoot)artifacts/**/Aspire.Cli.$(CliRuntime)/**/aspire"
Exclude="$(RepoRoot)artifacts/**/aspire.xml;$(RepoRoot)artifacts/**/aspire.dll;$(RepoRoot)artifacts/**/aspire.pdb" />
<_CandidateBinary Condition="$(CliRuntime.StartsWith('win-'))"
Include="$(RepoRoot)artifacts/**/Aspire.Cli.$(CliRuntime)/**/aspire.exe" />
</ItemGroup>

<PropertyGroup Condition="!Exists('$(_NativeBinaryPath)') and '@(_CandidateBinary)' != ''">
<_AllCandidates>@(_CandidateBinary->'%(FullPath)', ';')</_AllCandidates>
<_NativeBinaryPath>$(_AllCandidates.Split(';')[0])</_NativeBinaryPath>
</PropertyGroup>

<Error Condition="!Exists('$(_NativeBinaryPath)')"
Text="Could not find native binary for $(CliRuntime). Default: $(OutputPath)aspire. Search found: @(_CandidateBinary->'%(FullPath)', ', ')" />

<Message Importance="high" Text="Resolved native binary: $(_NativeBinaryPath)" />
</Target>

<Target Name="_RunVersionCheck" Condition="'$(_CanExecuteNativeBinary)' == 'true'">
<Message Importance="high" Text="Running 'aspire --version' on $(CliRuntime) binary..." />
<Exec Command="$(_NativeBinaryPath) --version" />
</Target>

<Target Name="_SkipVersionCheck" Condition="'$(_CanExecuteNativeBinary)' != 'true'">
<Message Importance="high" Text="Skipping 'aspire --version' for $(CliRuntime) — cannot execute on this host (arch=$(_HostArch), nativeAot=$(PublishNativeAot))." />
</Target>

<Target Name="SmokeTestNativeBinary"
DependsOnTargets="_ResolveSmokeTestBinaryPath;_ValidateNativeBinaryFileSignatureOnUnix;_RunVersionCheck;_SkipVersionCheck">
<Message Importance="high" Text="Smoke test complete for $(CliRuntime)." />
</Target>

</Project>
10 changes: 9 additions & 1 deletion eng/pipelines/azure-pipelines-unofficial.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ extends:

variables:
- name: _buildScript
value: $(Build.SourcesDirectory)/build.cmd -ci
value: $(Build.SourcesDirectory)/build.cmd -ci /p:SkipCliToolProject=true

preSteps:
- checkout: self
Expand All @@ -154,6 +154,7 @@ extends:
itemPattern: |
**/aspire-cli-*.zip
**/aspire-cli-*.tar.gz
**/Aspire.Cli.*.nupkg
targetPath: '$(Build.SourcesDirectory)/artifacts/packages/$(_BuildConfig)'

- task: PowerShell@2
Expand Down Expand Up @@ -209,6 +210,12 @@ extends:
targetRids:
- win-x64
- win-arm64
packCliToolPackages: true

# Extract the Aspire version from a generated nupkg filename so downstream
# stages can use it. Aspire.Hosting.AppHost has no RID in its filename and
# does not suppress final package version stabilization, so stripping the
# prefix cleanly yields the stable version for installer publishing.
- pwsh: |
$ErrorActionPreference = 'Stop'
$shippingDir = "$(Build.SourcesDirectory)/artifacts/packages/$(_BuildConfig)/Shipping"
Expand Down Expand Up @@ -248,6 +255,7 @@ extends:
name: computeArtifactVersion
displayName: 🟣Compute installer artifact version

# Determine stable vs prerelease for installer pipelines.
- pwsh: |
$ErrorActionPreference = 'Stop'
$versionKind = dotnet msbuild "$(Build.SourcesDirectory)/eng/Versions.props" -getProperty:DotNetFinalVersionKind
Expand Down
4 changes: 3 additions & 1 deletion eng/pipelines/azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ extends:

variables:
- name: _buildScript
value: $(Build.SourcesDirectory)/build.cmd -ci
value: $(Build.SourcesDirectory)/build.cmd -ci /p:SkipCliToolProject=true

preSteps:
- checkout: self
Expand All @@ -236,6 +236,7 @@ extends:
itemPattern: |
**/aspire-cli-*.zip
**/aspire-cli-*.tar.gz
**/Aspire.Cli.*.nupkg
targetPath: '$(Build.SourcesDirectory)/artifacts/packages/$(_BuildConfig)'

- task: PowerShell@2
Expand Down Expand Up @@ -291,6 +292,7 @@ extends:
targetRids:
- win-x64
- win-arm64
packCliToolPackages: true
publishVSCodeExtension: ${{ parameters.publishVSCodeExtension }}
vscePublishToken: $(VscePublishToken)
vscePublishPreRelease: ${{ parameters.vscePublishPreRelease }}
Expand Down
Loading
Loading