Skip to content

[release/13.2] Make VerifyAspireCliVersionAsync resilient across branches#15561

Open
mitchdenny wants to merge 1 commit intorelease/13.2from
fix/resilient-version-check-release-13.2
Open

[release/13.2] Make VerifyAspireCliVersionAsync resilient across branches#15561
mitchdenny wants to merge 1 commit intorelease/13.2from
fix/resilient-version-check-release-13.2

Conversation

@mitchdenny
Copy link
Member

@mitchdenny mitchdenny commented Mar 25, 2026

Description

Replaces the hardcoded "13.2.0" version check in VerifyAspireCliVersionAsync with a dynamic approach that reads the VersionPrefix from eng/Versions.props at test time.

Context: Why this fix exists

PR #15540 merges release/13.2 into main. On the release/13.2 branch, VerifyAspireCliVersionAsync was changed to hardcode "13.2.0" (with a TODO: "This change should be reverted on the integration to the main branch") because stabilized release builds don't include the commit SHA suffix in the version string.

When that merge PR reaches main (where version is 13.3.0-preview.1), the hardcoded "13.2.0" check fails. This pattern will repeat every time release/* merges into main (or vice versa) if version strings are hardcoded.

This PR fixes the root cause on release/13.2 so that future merges in either direction just work. The same fix was also pushed directly to PR #15540's branch (merge/release-13.2-to-main).

Problem

The version check was hardcoded to "13.2.0" on the release/13.2 branch because stabilized builds don't include the commit SHA suffix. This breaks when:

  • Merging to main (where version is 13.3.0)
  • Any future branch with a different version number
  • The version is bumped (e.g., 13.2.013.2.1)

Solution

  • CliE2ETestHelpers.cs: Added GetVersionPrefix() and IsStabilizedBuild() that parse eng/Versions.props dynamically
  • CliE2EAutomatorHelpers.cs: Updated VerifyAspireCliVersionAsync to:
    1. Always verify CLI version contains the dynamic VersionPrefix (adapts per branch)
    2. For non-stabilized builds (all PR CI builds), also verify commit SHA suffix for exact build identity
    3. Removed hardcoded "13.2.0" and #pragma warning suppression

Why this is safe across branch merges

Scenario Versions.props CLI output Result
main at 13.3.0 13.3.0 13.3.0-preview.1.xxx+g{sha} ✅ prefix match + SHA check
release/13.2 at 13.2.0 13.2.0 13.2.0-pr.xxx ✅ prefix match + SHA check
Merge release→main main's version matches main CLI
Stabilized official release 13.2.0 13.2.0 ✅ SHA check skipped

NuGet package isolation

PR build packages remain isolated via the hive mechanism:

  • Packages have -pr.XXXX.sha suffixes (never match published GA versions)
  • StabilizePackageVersion=false in all PR builds
  • NuGet.config source mapping routes Aspire* packages to local hive files

Related PRs

Checklist

  • Is this feature complete?
    • Yes
  • Has the code been tested?
    • Builds successfully with zero warnings

Read VersionPrefix dynamically from eng/Versions.props instead of
hardcoding a specific version. For non-stabilized builds (all normal
PR builds), also verify the commit SHA suffix for exact build identity.

This replaces the hardcoded '13.2.0' check that was added for the
stabilized 13.2.0 release build.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 25, 2026 01:21
@github-actions
Copy link
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 15561

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 15561"

@mitchdenny mitchdenny changed the title Make VerifyAspireCliVersionAsync resilient across branches [release/13.2] Make VerifyAspireCliVersionAsync resilient across branches Mar 25, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates Aspire CLI E2E test helpers to avoid hardcoding an expected CLI version, making VerifyAspireCliVersionAsync work across branches by deriving the version prefix from eng/Versions.props.

Changes:

  • Add helpers to parse MajorVersion/MinorVersion/PatchVersion from eng/Versions.props to build a dynamic version prefix.
  • Add a helper intended to detect whether builds are “stabilized” (no SHA suffix).
  • Update VerifyAspireCliVersionAsync to validate the dynamic version prefix and (when non-stabilized) the commit SHA suffix.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
tests/Aspire.Cli.EndToEnd.Tests/Helpers/CliE2ETestHelpers.cs Adds XML parsing helpers for VersionPrefix and stabilized-build detection.
tests/Aspire.Cli.EndToEnd.Tests/Helpers/CliE2EAutomatorHelpers.cs Switches CLI version verification from a hardcoded prefix to dynamic prefix + optional SHA suffix validation.

Comment on lines +353 to +358
// The default value in Versions.props uses a Condition to default to "false",
// so we read the element's text directly.
var stabilize = doc.Descendants(ns + "StabilizePackageVersion")
.FirstOrDefault()?.Value;

return string.Equals(stabilize, "true", StringComparison.OrdinalIgnoreCase);
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IsStabilizedBuild reads the raw element value from eng/Versions.props, but that property is conditionally assigned (and can be overridden via /p:StabilizePackageVersion=...). With the current Versions.props shape, this method can return a value that doesn’t match the effective build setting (e.g., PR builds overriding it to false), which will cause downstream version validation to skip/enable SHA checks incorrectly. Consider evaluating the property via MSBuild (same global properties as the build) or inferring stabilization from the actual aspire --version output (presence/absence of +g{sha} / prerelease label).

Copilot uses AI. Check for mistakes.
Comment on lines +159 to +176
var versionPrefix = CliE2ETestHelpers.GetVersionPrefix();
var isStabilized = CliE2ETestHelpers.IsStabilizedBuild();

await auto.TypeAsync("aspire --version");
await auto.EnterAsync();

// When the build is stabilized (StabilizePackageVersion=true), the CLI version
// is just "13.2.0" with no commit SHA suffix. When not stabilized, it includes
// the SHA (e.g., "13.2.0-preview.1.g<sha>"). In both cases, "13.2.0" is present.
// TODO: This change should be reverted on the integration to the main branch.
await auto.WaitUntilTextAsync("13.2.0", timeout: TimeSpan.FromSeconds(10));
// Always verify the version prefix matches the branch's version (e.g., "13.3.0").
await auto.WaitUntilTextAsync(versionPrefix, timeout: TimeSpan.FromSeconds(10));

// For non-stabilized builds (all PR CI builds), also verify the commit SHA suffix
// to uniquely identify the exact build. Stabilized builds (official releases only)
// produce versions without SHA suffixes, so we skip this check.
if (!isStabilized && commitSha.Length == 40)
{
var shortCommitSha = commitSha[..8];
var expectedVersionSuffix = $"g{shortCommitSha}";
await auto.WaitUntilTextAsync(expectedVersionSuffix, timeout: TimeSpan.FromSeconds(10));
}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VerifyAspireCliVersionAsync’s SHA-suffix validation depends on IsStabilizedBuild(), but IsStabilizedBuild currently parses Versions.props without evaluating MSBuild conditions/overrides. This can make the SHA check get skipped even in PR CI runs, reducing the test’s ability to prove the CLI came from the current PR build. Suggest basing the decision on the observed aspire --version output (e.g., wait until the screen contains the prefix and then, if it contains +g, require the matching g{shortSha}; otherwise require an exact stabilized version).

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Contributor

🎬 CLI E2E Test Recordings — 51 recordings uploaded (commit 6f84c9e)

View recordings
Test Recording
AddPackageInteractiveWhileAppHostRunningDetached ▶️ View Recording
AddPackageWhileAppHostRunningDetached ▶️ View Recording
AgentCommands_AllHelpOutputs_AreCorrect ▶️ View Recording
AgentInitCommand_DefaultSelection_InstallsSkillOnly ▶️ View Recording
AgentInitCommand_MigratesDeprecatedConfig ▶️ View Recording
AspireAddPackageVersionToDirectoryPackagesProps ▶️ View Recording
AspireUpdateRemovesAppHostPackageVersionFromDirectoryPackagesProps ▶️ View Recording
Banner_DisplayedOnFirstRun ▶️ View Recording
Banner_DisplayedWithExplicitFlag ▶️ View Recording
CertificatesClean_RemovesCertificates ▶️ View Recording
CertificatesTrust_WithNoCert_CreatesAndTrustsCertificate ▶️ View Recording
CertificatesTrust_WithUntrustedCert_TrustsCertificate ▶️ View Recording
ConfigSetGet_CreatesNestedJsonFormat ▶️ View Recording
CreateAndRunAspireStarterProject ▶️ View Recording
CreateAndRunAspireStarterProjectWithBundle ▶️ View Recording
CreateAndRunEmptyAppHostProject ▶️ View Recording
CreateAndRunJsReactProject ▶️ View Recording
CreateAndRunPythonReactProject ▶️ View Recording
CreateAndRunTypeScriptEmptyAppHostProject ▶️ View Recording
CreateAndRunTypeScriptStarterProject ▶️ View Recording
CreateStartAndStopAspireProject ▶️ View Recording
CreateTypeScriptAppHostWithViteApp ▶️ View Recording
DescribeCommandResolvesReplicaNames ▶️ View Recording
DescribeCommandShowsRunningResources ▶️ View Recording
DetachFormatJsonProducesValidJson ▶️ View Recording
DoctorCommand_DetectsDeprecatedAgentConfig ▶️ View Recording
DoctorCommand_WithSslCertDir_ShowsTrusted ▶️ View Recording
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted ▶️ View Recording
GlobalMigration_HandlesCommentsAndTrailingCommas ▶️ View Recording
GlobalMigration_HandlesMalformedLegacyJson ▶️ View Recording
GlobalMigration_PreservesAllValueTypes ▶️ View Recording
GlobalMigration_SkipsWhenNewConfigExists ▶️ View Recording
GlobalSettings_MigratedFromLegacyFormat ▶️ View Recording
InvalidAppHostPathWithComments_IsHealedOnRun ▶️ View Recording
LegacySettingsMigration_AdjustsRelativeAppHostPath ▶️ View Recording
LogsCommandShowsResourceLogs ▶️ View Recording
PsCommandListsRunningAppHost ▶️ View Recording
PsFormatJsonOutputsOnlyJsonToStdout ▶️ View Recording
PublishWithDockerComposeServiceCallbackSucceeds ▶️ View Recording
RestoreGeneratesSdkFiles ▶️ View Recording
RunFromParentDirectory_UsesExistingConfigNearAppHost ▶️ View Recording
RunWithMissingAwaitShowsHelpfulError ▶️ View Recording
SecretCrudOnDotNetAppHost ▶️ View Recording
SecretCrudOnTypeScriptAppHost ▶️ View Recording
StagingChannel_ConfigureAndVerifySettings_ThenSwitchChannels ▶️ View Recording
StopAllAppHostsFromAppHostDirectory ▶️ View Recording
StopAllAppHostsFromUnrelatedDirectory ▶️ View Recording
StopNonInteractiveMultipleAppHostsShowsError ▶️ View Recording
StopNonInteractiveSingleAppHost ▶️ View Recording
StopWithNoRunningAppHostExitsSuccessfully ▶️ View Recording
TypeScriptAppHostWithProjectReferenceIntegration ▶️ View Recording

📹 Recordings uploaded automatically from CI run #23520298644

Copy link
Member

@JamesNK JamesNK left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach of dynamically reading eng/Versions.props instead of hardcoding the version is a good idea, but IsStabilizedBuild() reads the source file on disk rather than the actual build-time value (which CI overrides via /p:StabilizePackageVersion=false). This means the SHA suffix check is dead code on release/13.2. See inline comments.


// For non-stabilized builds (all PR CI builds), also verify the commit SHA suffix
// to uniquely identify the exact build. Stabilized builds (official releases only)
// produce versions without SHA suffixes, so we skip this check.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the release/13.2 branch, eng/Versions.props has StabilizePackageVersion=true. This method reads that file value, so IsStabilizedBuild() always returns true on this branch — making the SHA verification block below dead code.

The PR description states "StabilizePackageVersion=false in all PR builds", but that's a CI pipeline override (/p:StabilizePackageVersion=false), not the file on disk. Since this reads the source file rather than querying the actual build configuration, the if (!isStabilized && ...) check never executes on release/13.2.

The test still passes (version prefix alone is sufficient), but the commit SHA check described in the PR's scenario table for release/13.2 PR builds won't actually run. Consider reading the stabilization state from the CLI's own version output (e.g., presence of + in the version string) instead of the source file.

/// of "13.2.0-preview.1.25175.1+g{sha}"). This is only true for official release builds,
/// never for normal PR CI builds.
/// </summary>
internal static bool IsStabilizedBuild()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Both GetVersionPrefix() and IsStabilizedBuild() independently call GetRepoRoot(), build the same eng/Versions.props path, and parse it with XDocument.Load(). Since they're called back-to-back from VerifyAspireCliVersionAsync, this parses the same XML file twice. Consider a single helper that loads the document once and returns both values.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants