Fix regressions in aspire update#17192
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 17192Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 17192" |
There was a problem hiding this comment.
Pull request overview
Fixes two independent regressions in aspire update: (A) a crash when the configured AppHost's pinned Aspire.AppHost.Sdk can no longer be resolved by MSBuild, and (B) update silently defaulting to the implicit/daily channel for users whose AppHost predates the channel-bake change, even when the running CLI clearly identifies as a specific PR/daily build.
Changes:
- Introduces
AppHostSettingsValidation(Strict / TrustConfiguredPath) plus overloads onIProjectLocator; onlyUpdateCommandopts into trust mode so MSBuild validation is skipped for the configured path. UpdateCommandnow falls back toCliExecutionContext.IdentityChannel(case-insensitive match againstallChannels, excludinglocal) before prompting/implicit selection.- Adds 4 locator tests and 8 update-command tests covering trust-mode contract and identity-channel resolution matrix.
Show a summary per file
| File | Description |
|---|---|
| src/Aspire.Cli/Projects/ProjectLocator.cs | New AppHostSettingsValidation enum + interface/impl overloads; GetValidatedAppHostProjectFileFromSettingsAsync short-circuits MSBuild validation in trust mode. |
| src/Aspire.Cli/Commands/UpdateCommand.cs | Calls locator with TrustConfiguredPath; consults ExecutionContext.IdentityChannel between per-project/global lookup and the hives prompt. |
| tests/Aspire.Cli.Tests/Projects/ProjectLocatorTests.cs | Locks down trust-mode contract (returns unvalidated path, still requires existence + handler) and strict behavior for other commands. |
| tests/Aspire.Cli.Tests/Commands/UpdateCommandTests.cs | Asserts UpdateCommand uses trust mode and identity-channel matrix (match wins, local skipped, stale falls through, explicit/per-project still override). |
| tests/Aspire.Cli.Tests/TestServices/TestProjectLocator.cs | Adds validation-aware callback + overloads to allow tests to capture/route the new locator API. |
Copilot's findings
- Files reviewed: 5/5 changed files
- Comments generated: 3
* Update no longer crashes when the AppHost's pinned Aspire.AppHost.Sdk
version cannot be resolved (e.g. moving between PR builds after the
hive was refreshed). UpdateCommand now calls the project locator in
TrustConfiguredPath mode, which skips MSBuild validation of the
configured AppHost path so ProjectUpdater can rewrite the SDK pin
via its existing fallback parser.
* Update no longer defaults to the Implicit ("daily") channel when a
PR-built CLI is run against an AppHost that has no per-project or
global channel pin. UpdateCommand now consults
ExecutionContext.IdentityChannel as a default before the prompt
fallback, restoring the pre-#16820 behavior without re-introducing
any global channel writes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
0f07799 to
7b47902
Compare
When 'aspire update' merges a project's NuGet.config to a new channel, any ~/.aspire/hives/pr-<N>/packages entry that lives only in <packageSources> (no corresponding <packageSourceMapping> element) used to survive the merge. RemoveEmptyPackageSourceElements only cleans up <packageSource> mapping entries that become empty during the merge, so a source that was never mapped — or whose mapping was rewritten by an earlier merge — would linger forever. As soon as the hive directory is replaced on disk (which now happens routinely for refreshed PR builds), 'dotnet restore' fails with NU1301: The local source '...' doesn't exist. Add a final pass over <packageSources> that removes any safe-to-remove source (the existing IsSourceSafeToRemove heuristic — paths under .aspire/hives) that is not in sourcesInUse and not in the new channel's RequiredSources. Add two regression tests covering both shapes: original config with a PR-hive source mapped (already cleaned by the empty-element pass; locked down) and original config with the same PR-hive source listed but with no mapping element (the actual failure mode). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
aspire updateaspire update
aspire updateaspire update
Two additional regressions that compounded the aspire update problems:
(D) get-aspire-cli-pr.{sh,ps1} appended a fresh PATH entry on every PR
install because the install path embeds the PR number
(/Users/midenn/.aspire/dogfood/pr-<N>/bin), so the literal-line dedup in
add_to_path never matched any prior PR install. Detect any existing
dogfood/pr-*/bin line and replace it in place when adding a new one;
do the same for the Windows user PATH registry entry.
(E) aspire update --self prompts {Stable, Daily, Staging} and routes
through _cliDownloader.DownloadLatestCliAsync. PR channels have no
cliDownloadBaseUrl, so a PR-built CLI silently moved the user off
the PR build whenever they ran --self. Refuse with a clear message
pointing at the acquisition script (the supported refresh path for
PR installs) when the running CLI's IdentityChannel starts with
'pr-'. An explicit --channel still opts out.
Tests:
- AddToPath_PrInstall_ReplacesExistingDogfoodPrLine
- AddToPath_PrInstall_WithNoPriorDogfoodLine_AppendsAsBefore
- AddToPath_NonPrInstall_AppendsAndDoesNotMatchDogfoodHeuristic
- UpdateCommand_SelfUpdate_WhenIdentityChannelIsPr_RefusesWithAcquisitionScriptHint
- UpdateCommand_SelfUpdate_WhenIdentityChannelIsPrAndExplicitChannelGiven_AllowsDownload
- UpdateCommand_SelfUpdate_WhenIdentityChannelIsDaily_AllowsDownload
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
aspire updateaspire update
Per user feedback: 'aspire update --self' should remain a valid ejection mechanism for PR builds. The hard-coded Stable/Daily/Staging prompt already lets a PR-built CLI move to a real channel; refusing with an acquisition-script hint was unhelpful gatekeeping. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The .NET starter and apphost templates went through DotNetTemplateFactory without ever writing aspire.config.json#channel. The TypeScript starter (CliTemplateFactory.TypeScriptStarterTemplate) and the empty-template / init paths (ScaffoldingService) already mirrored the resolved channel into the per-project config. The .NET path was an asymmetric gap. Without a pin in the scaffolded project, `aspire update` skips its local-config precedence step and falls through to either an interactive prompt (when hives exist) or the Implicit/nuget.org channel — silently moving a project scaffolded by a PR-built or daily CLI onto stable. Mirror the TS pattern: when the resolved channel is Explicit (pr-<N>, daily, staging, local), call AspireConfigFile.LoadOrCreate + Save with the channel name. Implicit channels (stable/nuget.org) intentionally stay unpinned so the user's ambient NuGet config governs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
aspire-py-starter and aspire-go-starter previously declined to persist the resolved channel into aspire.config.json, on the rationale that PrebuiltAppHostServer aggregates package sources from every registered channel when no pin exists. That rationale only held for a daily CLI — on a PR-built CLI the next 'aspire update' falls through to a daily prompt or the Implicit/nuget.org channel because the local-config step in the channel-resolution precedence finds nothing. Mirror the TypeScript starter / .NET starter pattern: when NewCommand resolves an Explicit channel, write it to aspire.config.json#channel before the SDK generation step. Implicit channels (stable/nuget.org) remain unpinned. Extend NewCommandTemplateConfigPersistenceTests to cover GoStarter and PythonStarter across all three pin scenarios (identity unregistered, identity matches, --channel overrides), and drop the now-obsolete NonChannelPinningStarter_NeverPersistsChannel tripwire. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PR installs land under <prefix>/dogfood/pr-<N>/bin, a per-PR path used for session-scoped dogfooding. Writing it into ~/.zshrc / ~/.bashrc (Unix) or HKCU\Environment (Windows) silently demoted a developer's daily/stable install on every new shell until they hunted down the stale entry. Treat PR installs as session-only: update the current session PATH and print the activation hint so the user can opt into persistence manually, but leave shell profiles and the Windows user PATH untouched. Non-PR routes through these same scripts (release channels, --install-prefix overrides) keep the previous persistent-PATH behavior. The earlier dogfood/pr-* dedup pass in add_to_path / Update-PathEnvironment is no longer needed and has been removed; the obsolete add_to_path PR dedup tests are replaced with a single test pinning the simpler append contract. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/deployment-test |
|
🚀 Deployment tests starting on PR #17192... This will deploy to real Azure infrastructure. Results will be posted here when complete. |
Repo convention is that committed comments describe the current contract as seen against origin/main; they should not reference internal PR-N identifiers or branch-evolution narrative. Rewrite the affected block headers in UpdateCommandTests.cs (and any matching narrative in UpdateCommand.cs / ProjectLocator.cs found by audit) to describe what the code / test group guards, in terms of contract rather than history. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
aspire updateaspire update
|
❌ Deployment E2E Tests failed — 37 passed, 1 failed, 0 cancelled View test results and recordings
|
|
❓ CLI E2E Tests unknown — 86 passed, 0 failed, 1 unknown (commit View all recordings
📹 Recordings uploaded automatically from CI run #26022398534 |
|
✅ No documentation update needed. Documentation changes were authored for |
* Fix two regressions in `aspire update`
* Update no longer crashes when the AppHost's pinned Aspire.AppHost.Sdk
version cannot be resolved (e.g. moving between PR builds after the
hive was refreshed). UpdateCommand now calls the project locator in
TrustConfiguredPath mode, which skips MSBuild validation of the
configured AppHost path so ProjectUpdater can rewrite the SDK pin
via its existing fallback parser.
* Update no longer defaults to the Implicit ("daily") channel when a
PR-built CLI is run against an AppHost that has no per-project or
global channel pin. UpdateCommand now consults
ExecutionContext.IdentityChannel as a default before the prompt
fallback, restoring the pre-microsoft#16820 behavior without re-introducing
any global channel writes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Strip orphaned PR-hive sources from <packageSources> on update
When 'aspire update' merges a project's NuGet.config to a new channel, any
~/.aspire/hives/pr-<N>/packages entry that lives only in <packageSources>
(no corresponding <packageSourceMapping> element) used to survive the merge.
RemoveEmptyPackageSourceElements only cleans up <packageSource> mapping
entries that become empty during the merge, so a source that was never
mapped — or whose mapping was rewritten by an earlier merge — would linger
forever. As soon as the hive directory is replaced on disk (which now
happens routinely for refreshed PR builds), 'dotnet restore' fails with
NU1301: The local source '...' doesn't exist.
Add a final pass over <packageSources> that removes any safe-to-remove
source (the existing IsSourceSafeToRemove heuristic — paths under
.aspire/hives) that is not in sourcesInUse and not in the new channel's
RequiredSources.
Add two regression tests covering both shapes: original config with a
PR-hive source mapped (already cleaned by the empty-element pass; locked
down) and original config with the same PR-hive source listed but with no
mapping element (the actual failure mode).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix PR install PATH dedup and refuse PR-channel --self update
Two additional regressions that compounded the aspire update problems:
(D) get-aspire-cli-pr.{sh,ps1} appended a fresh PATH entry on every PR
install because the install path embeds the PR number
(/Users/midenn/.aspire/dogfood/pr-<N>/bin), so the literal-line dedup in
add_to_path never matched any prior PR install. Detect any existing
dogfood/pr-*/bin line and replace it in place when adding a new one;
do the same for the Windows user PATH registry entry.
(E) aspire update --self prompts {Stable, Daily, Staging} and routes
through _cliDownloader.DownloadLatestCliAsync. PR channels have no
cliDownloadBaseUrl, so a PR-built CLI silently moved the user off
the PR build whenever they ran --self. Refuse with a clear message
pointing at the acquisition script (the supported refresh path for
PR installs) when the running CLI's IdentityChannel starts with
'pr-'. An explicit --channel still opts out.
Tests:
- AddToPath_PrInstall_ReplacesExistingDogfoodPrLine
- AddToPath_PrInstall_WithNoPriorDogfoodLine_AppendsAsBefore
- AddToPath_NonPrInstall_AppendsAndDoesNotMatchDogfoodHeuristic
- UpdateCommand_SelfUpdate_WhenIdentityChannelIsPr_RefusesWithAcquisitionScriptHint
- UpdateCommand_SelfUpdate_WhenIdentityChannelIsPrAndExplicitChannelGiven_AllowsDownload
- UpdateCommand_SelfUpdate_WhenIdentityChannelIsDaily_AllowsDownload
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Revert PR-channel refusal on aspire update --self
Per user feedback: 'aspire update --self' should remain a valid
ejection mechanism for PR builds. The hard-coded Stable/Daily/Staging
prompt already lets a PR-built CLI move to a real channel; refusing
with an acquisition-script hint was unhelpful gatekeeping.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Persist resolved channel from `aspire new` (.NET scaffold)
The .NET starter and apphost templates went through DotNetTemplateFactory
without ever writing aspire.config.json#channel. The TypeScript starter
(CliTemplateFactory.TypeScriptStarterTemplate) and the empty-template /
init paths (ScaffoldingService) already mirrored the resolved channel
into the per-project config. The .NET path was an asymmetric gap.
Without a pin in the scaffolded project, `aspire update` skips its
local-config precedence step and falls through to either an interactive
prompt (when hives exist) or the Implicit/nuget.org channel — silently
moving a project scaffolded by a PR-built or daily CLI onto stable.
Mirror the TS pattern: when the resolved channel is Explicit (pr-<N>,
daily, staging, local), call AspireConfigFile.LoadOrCreate + Save with
the channel name. Implicit channels (stable/nuget.org) intentionally
stay unpinned so the user's ambient NuGet config governs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Extend channel pin to Python and Go starters
aspire-py-starter and aspire-go-starter previously declined to persist
the resolved channel into aspire.config.json, on the rationale that
PrebuiltAppHostServer aggregates package sources from every registered
channel when no pin exists. That rationale only held for a daily CLI —
on a PR-built CLI the next 'aspire update' falls through to a daily
prompt or the Implicit/nuget.org channel because the local-config step
in the channel-resolution precedence finds nothing.
Mirror the TypeScript starter / .NET starter pattern: when NewCommand
resolves an Explicit channel, write it to aspire.config.json#channel
before the SDK generation step. Implicit channels (stable/nuget.org)
remain unpinned.
Extend NewCommandTemplateConfigPersistenceTests to cover GoStarter and
PythonStarter across all three pin scenarios (identity unregistered,
identity matches, --channel overrides), and drop the now-obsolete
NonChannelPinningStarter_NeverPersistsChannel tripwire.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Skip persistent profile/PATH writes for PR installs
PR installs land under <prefix>/dogfood/pr-<N>/bin, a per-PR path used
for session-scoped dogfooding. Writing it into ~/.zshrc / ~/.bashrc (Unix)
or HKCU\Environment (Windows) silently demoted a developer's daily/stable
install on every new shell until they hunted down the stale entry.
Treat PR installs as session-only: update the current session PATH and
print the activation hint so the user can opt into persistence manually,
but leave shell profiles and the Windows user PATH untouched. Non-PR
routes through these same scripts (release channels, --install-prefix
overrides) keep the previous persistent-PATH behavior.
The earlier dogfood/pr-* dedup pass in add_to_path / Update-PathEnvironment
is no longer needed and has been removed; the obsolete add_to_path PR
dedup tests are replaced with a single test pinning the simpler append
contract.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Drop PR-N / date narrative from committed comments
Repo convention is that committed comments describe the current contract
as seen against origin/main; they should not reference internal PR-N
identifiers or branch-evolution narrative. Rewrite the affected block
headers in UpdateCommandTests.cs (and any matching narrative in
UpdateCommand.cs / ProjectLocator.cs found by audit) to describe what
the code / test group guards, in terms of contract rather than history.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Mitch Denny <midenn@orangecake.localdomain>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: mitchdenny <mitchdenny@users.noreply.github.com>
Co-authored-by: Mitch Denny <midenn@Mac.localdomain>
PR and localhive acquisition scripts no longer touch shell profiles, the persistent Windows user PATH, or global channel configuration. The success hints now name the installed Aspire binary directly and only offer session-scoped PATH activation where that can help. Session-scoped dogfood installs were silently demoting a developer's daily or stable install whenever installer state survived into future shells. Follow the microsoft#17192 pattern by keeping these routes local to the current invocation and letting project configuration or CLI identity resolve channels instead of installer-seeded global state. Refs microsoft#17192 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PR install and localhive install are session-scoped dogfood routes. They must not modify shell startup files or HKCU\\Environment, because a persistent PATH entry for a PR or local build can shadow the user's daily or stable CLI in every new shell. Pin the script contract in tests: PR and localhive scripts must leave persistent PATH state alone, name the direct binary path in activation hints, and avoid global channel writes. The PR script assertions follow the microsoft#17192 dry-run / WhatIf pattern and extend it for the stricter session-only contract. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fixes a cluster of regressions that surface together when running
aspire updateagainst a project whose AppHost still references a since-replaced PR hive, plus the CLI-acquisition andaspire newpapercuts that compound them.Symptoms
aspire updateexits withNo AppHosts were found (there may be AppHost project files with syntax errors/invalid SDK versions)even when an AppHost is explicitly configured inaspire.config.json.daily(resolves Aspire packages from nuget.org).dotnet restorefails withNU1301: The local source '/Users/.../.aspire/hives/pr-<old>/packages' doesn't exist.export PATH=...line into~/.zshrc/~/.bashrc(and the Windows userPATH), silently demoting the user's daily/stable install on every new shell until they hunted down the stale entry.aspire newdoes not pin the channel it scaffolded from: a project created by a PR-built or daily CLI ends up with nochannelinaspire.config.json, so the nextaspire updateeither prompts to flip to daily or silently routes through the implicit/nuget.org channel.Root causes
A — Settings-configured AppHost path goes through MSBuild validation
UseOrFindAppHostProjectFileAsyncrunsDotNetAppHostProject.ValidateAppHostAsyncagainst the AppHost path stored inaspire.config.json. Validation invokes MSBuild, which fails whenAspire.AppHost.Sdk/<pinned-version>can no longer be resolved (e.g. the user updated to a new build of the same PR and the old hive is gone). The discovery scan then falls back toPossiblyUnbuildablefor the same file, producing aProjectLocatorException. This blocks the very command whose job it is to repair the unresolvable SDK.B — The CLI no longer falls back to its identity channel
UpdateCommandchannel resolution is--channel→ per-project config → global config → prompt/implicit. Previously the PR install script wrotechannel: pr-<N>into the globalaspire.config.json; that was removed in favor ofCliExecutionContext.IdentityChannel(baked into the assembly), butUpdateCommandwas never wired up to consult it. Result: a PR-built CLI sees no channel anywhere and silently falls through to the implicit/default ("daily") option.C — Orphaned PR-hive sources linger in
<packageSources>NuGetConfigMergerremoves a stale source only when its<packageSource>mapping element becomes empty during the merge. A source that has no mapping element at all (e.g. inherited from a parent config or written by a prior partial merge) survives forever. Previously the same hive directory got refreshed in-place so the dangling source still resolved; once PR builds started rotating hive directories the source points at a missing path anddotnet restorefails with NU1301.D — PR install writes persistent PATH entries
get-aspire-cli-pr.{sh,ps1}was treating PR installs the same as release-channel installs and writing thedogfood/pr-<N>/binpath into the user's shell profile (Unix) orHKCU\Environment(Windows). PR builds are a per-session dogfood activation, not a baseline tool install; writing them persistently meant the next terminal silently picked up the PR CLI instead of the user's daily/stable install, and the entries accumulated as the user tried more PRs.E —
aspire newdoes not persist the resolved channelA project scaffolded with
aspire new aspire-starter(oraspire-apphost,aspire-py-starter,aspire-go-starter) using a PR-built or daily CLI did not getchannelwritten into the scaffoldedaspire.config.json. Runningaspire updateon that project then either prompts to switch to daily or silently routes through the Implicit/nuget.org channel, moving the project off the channel the CLI built it from. The TypeScript starter path was already pinning the channel; the.NET, Python, and Go starter paths were not.Fix
A —
UpdateCommandconsults settings without MSBuild validationProjectLocator.GetAppHostFromSettingsAsync(existing helper, name already implied "from settings") now only does the file-exists + handler check; the MSBuildValidateAppHostAsynccall moved out.UpdateCommand.ExecuteAsynccallsGetAppHostFromSettingsAsyncfirst and only falls back toUseOrFindAppHostProjectFileAsyncwhen there is no explicit--apphostand no entry inaspire.config.json. Other commands keep the existing strict-validation behavior ofUseOrFindAppHostProjectFileAsync.This is intentionally smaller than the original enum/overload approach we tried —
updateis the one command that needs to tolerate an AppHost whose SDK pin no longer resolves, because that is what it is there to fix.B — Identity channel fallback in
UpdateCommandAfter the existing
--channel/ per-project / global lookups fail, look upExecutionContext.IdentityChannelagainstallChannels(case-insensitive). If a match is found, use it silently.localis excluded so a developer-built CLI does not silently pin a shared project to a per-machine hive.C — Sweep orphaned safe-to-remove sources after merge
New final pass in
UpdateExistingPackageSourceMappingwalks<packageSources>and removes any entry whose URL passes the existingIsSourceSafeToRemoveheuristic (paths under.aspire/hives) and is not insourcesInUseand is not in the new channel'sRequiredSources. This is conservative — only PR-hive style paths are affected; user feeds, nuget.org, and Microsoft-controlled sources are left alone.D — Treat PR installs as session-only
get-aspire-cli-pr.shskipsadd_to_shell_profileentirely whenPR_NUMBERis set;get-aspire-cli-pr.ps1skips the persistent user-PATHwrite when the install target matchesdogfood\pr-*\bin. Both still update the current-sessionPATHand print the activation hint so the user can opt into persistence manually if they choose. Non-PR routes through these scripts (release channels, explicit--install-prefix) keep the previous persistent-PATH behavior. Restarting a terminal now returns the user to the daily/stable install they had before.E — Scaffolding persists the resolved channel across all starters
DotNetTemplateFactory.ApplyTemplateAsync,CliTemplateFactory.PythonStarterTemplate, andCliTemplateFactory.GoStarterTemplatenow mirror the TypeScript starter pattern: when the resolved channel isPackageChannelType.Explicit, load the scaffoldedaspire.config.json(creating one ifdotnet newdid not), setChannel, and save. Implicit channels (stable/nuget.org) are intentionally not persisted soaspire add/aspire restorecontinue to use the user's ambient NuGet config.Note on
aspire update --selfand PR buildsThe existing
--selfprompt offers{ Stable, Daily, (Staging) }and routes through_cliDownloader.DownloadLatestCliAsync, which cannot fetch a PR build (PR channels have noCliDownloadBaseUrl). This is deliberately left alone: it doubles as the ejection mechanism for users who want off a PR build. Users who want to refresh the same PR re-run the acquisition script (the supported refresh path).Tests
ProjectLocatorTests(Fix A):GetAppHostFromSettings_*series — settings lookup returns the configured path even when MSBuild evaluation would fail, but still requires the file to exist and to have a registered handler.UseOrFindAppHostProjectFile_RejectsUnbuildableSettingsAppHost— locks down the existing strict behavior for non-update callers.UpdateCommandTests(Fix A + B):UpdateCommand_WhenAppHostSdkVersionUnresolvable_UsesSettingsLookup— regression guard for the crash.UpdateCommand_WhenIdentityChannel*— covers identity fallback, thelocalexclusion, the "identity has no matching channel" path, and the precedence ordering against--channeland per-project config.UpdateCommand_SelfUpdate_WhenIdentityChannelIsPrAndExplicitChannelGiven_AllowsDownload— explicit--channelstill wins on--selffor a PR build.UpdateCommand_SelfUpdate_WhenIdentityChannelIsDaily_AllowsDownload— guard that ordinary identity channels still proceed to download.NuGetConfigMergerTests(Fix C):CreateOrUpdateAsync_RemovesOldPrHive_WhenSwitchingBetweenPrHives— original config with the old hive mapped (locks down the existing empty-element path).CreateOrUpdateAsync_RemovesOldPrHive_WhenItHasNoMappingElement— original config with the old hive listed but unmapped (the actual failure mode).PRScriptFunctionTests(Fix D):AddToPath_AppendsExportLine— pins the simple append contract for the lower-level helper now that PR installs bypassadd_to_shell_profileentirely.NewCommandTemplateConfigPersistenceTests(Fix E):KnownTemplateId.DotNetStarter,DotNetAppHost,PythonStarter,GoStarter, and the existing TS starter — asserts each starter writeschannelintoaspire.config.jsonwhen the resolved channel is Explicit, and writes nothing for Implicit channels.Full
Aspire.Cli.Tests(3110) andAspire.Acquisition.Tests(179) suites pass.