Skip to content

feat(cli): cleanup commands for Aspire CLI installs and installer scripts#17462

Draft
radical wants to merge 6 commits into
microsoft:mainfrom
radical:radical/installs-cleanup
Draft

feat(cli): cleanup commands for Aspire CLI installs and installer scripts#17462
radical wants to merge 6 commits into
microsoft:mainfrom
radical:radical/installs-cleanup

Conversation

@radical
Copy link
Copy Markdown
Member

@radical radical commented May 25, 2026

Builds on #17461. The description below covers the diff this PR adds on top of #17461.

What was missing. #17461 introduces aspire installs for discovering Aspire CLI state, but provides no way to remove that state. Cleaning up a pr-<N> dogfood install, an orphan NuGet hive, or a leftover ~/.aspire/bundle layout still requires remembering the install layout by heart. The installer scripts have no uninstall mode either, so users can't clean up at all if the CLI on PATH is broken or absent.

The fix. Add cleanup commands to the aspire installs surface and to the install scripts:

  • aspire installs uninstall <id> [--remove-shared-install] [--dry-run] [--yes] — removes Aspire-owned state for a discovered install or orphan hive listed by aspire installs list. Refuses managed installs (Homebrew, WinGet, dotnet tool) before any prompt and points the user back at the owning package manager. aspire installs list rows gain a Cleanup hint pointing at the right command for that row's source.
  • aspire hives list / aspire hives delete <name> — lower-level package-hive operations.
  • aspire uninstall --channel <ch> | --all — lower-level channel cleanup (drives the same CliCleanupService as installs uninstall).
  • get-aspire-cli{,-pr}.{sh,ps1} --uninstall (or -Uninstall) — clean stale state even when no Aspire CLI is on PATH. The PR script infers the channel from --pr-number; the release script requires explicit --channel or --all. Both require --yes for destruction and support --dry-run / -WhatIf.

Destructive-path hardening:

  • Channel names are validated against path separators and .. segments before being interpolated into any deletion path (CliCleanupService.ValidateChannel, plus validate_channel / Test-UninstallChannel in all four installer scripts).
  • PowerShell Remove-CleanupPath runs Remove-Item -ErrorAction Stop inside try/catch so a failed delete emits failed: <path> and propagates a non-zero exit instead of falling through to a removed: log line.
  • Get-BundleVersionTarget (both PowerShell scripts) wraps Get-Item with -ErrorAction Stop; an IO failure on the bundle symlink now throws rather than returning $null and letting Remove-BundleLayout silently strand the versions/<v>/ tree.
  • CliCleanupService.AddSharedInstallOperations resolves the bundle symlink target before deleting anything. When another aspire process holds a lease on the bundle version, both the symlink and the version directory are skipped — deleting the symlink alone would still break the lease holder's ability to re-resolve ~/.aspire/bundle. The exit code is non-zero outside --dry-run when the requested --remove-shared-install could not be carried out in full.
  • installs uninstall <id> requires an exact id match. A typoed-suffix id like pr-17416-2 is rejected instead of silently stripping to pr-17416 and deleting the wrong hive.
  • The release script's -Uninstall path tracks $Script:QualityExplicit (mirrors QUALITY_EXPLICIT in the bash sibling), so get-aspire-cli.ps1 -Uninstall -Yes with no other arguments errors out instead of inferring stable from the install-default -Quality.

Refs #15614

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 25, 2026

🚀 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 -- 17462

Or

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

@radical radical changed the title feat(cli): aspire installs surface for listing and uninstalling CLI state feat(cli): cleanup commands for Aspire CLI installs and installer scripts May 25, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Re-running the failed jobs in the CI workflow for this pull request because 1 job was identified as retry-safe transient failures in the CI run attempt.
GitHub was asked to rerun all failed jobs for that attempt, and the rerun is being tracked in the rerun attempt.
The job links below point to the failed attempt jobs that matched the retry-safe transient failure rules.

radical and others added 6 commits May 25, 2026 18:27
Move CLI install enumeration and per-install metadata reporting out of
`aspire doctor` into a new `aspire installs` command surface, and align
the install-metadata vocabulary on `source` everywhere so the CLI,
sidecar spec, installer scripts, and documentation use the same term.

`aspire installs list` reports every discovered Aspire CLI install and
every orphan hive in a vertical, color-aware layout. `--format json`
emits the same rows as a stable contract documented in
`docs/specs/cli-output-formats.md`.

`aspire installs --self --format json` replaces the hidden `aspire
doctor --self` endpoint that install discovery uses to cross-check a
peer install's reported metadata. `aspire doctor` no longer ships an
install table or `--self` flag; it now reports environment checks only.
Install discovery still discovers peers, but the peer probe now invokes
`aspire installs --self`.

The sidecar spec moves to `docs/specs/install-sources.md`; the
`InstallSidecarReader`, `InstallationInfo`, scripts, homebrew template,
localhive helpers, and tests all read and write `source` (formerly
`route`). Homebrew installs are tagged with the explicit `homebrew`
source value in the sidecar and user-facing output.

The WinGet first-run install sidecar probe now runs before `aspire
installs list` instead of `aspire doctor`, so PR users still get a fresh
sidecar populated on the first invocation after `winget install
Microsoft.Aspire`.

On Windows, the shared `PathLookupHelper` preserves the executable
casing recorded on disk after PATHEXT resolution, so `aspire.exe` does
not render as `aspire.EXE` just because PATHEXT contains `.EXE`.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The install-source sidecar's `source` field is the wire-level schema
value: the release script, PR installer, dotnet-tool packaging, and the
Homebrew cask all write the literal `brew` string (matching the cask's
`postflight` block and the actual Homebrew CLI binary name).
`BundleService.ComputeDefaultExtractDir`, `ParseInstallSource`, and the
installer test theory rows consume the same wire value.

The user-facing surfaces in `aspire installs list` — `GetInstallKind`,
`GetCleanupHint`, `GetManagedBy`, and the matching test expectations —
render the friendlier `homebrew` / `Homebrew` label because that's what
users recognise.

Match on `"brew"` at the wire layer, translate to `"homebrew"` at the
display layer. The schema stays minimal, the cask sidecar stays a
literal one-liner, and users still see the conventional spelling.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…xt factory

WingetFirstRunProbe is the writer that lets a winget portable install identify
itself as such on first run — winget has no install hook, so the running CLI
self-stamps `.aspire-install.json` after consulting Windows ARP. It was being
invoked from two unrelated consumer sites:

  - InstallsCommand.ListCommand.ExecuteAsync, before discovery reads the sidecar
  - BundleService.GetBundleExtractDirForCurrentProcess, before the extract dir
    is computed from the sidecar source

Each consumer paid the same try/catch cost and each leaked installer-specific
knowledge into a layer that should be installer-agnostic ("commands shouldn't
depend on any specific installer"). Worse, the factory that builds
CliExecutionContext also reads the sidecar (via GetUsersAspirePath ->
CliPathHelper.GetAspireHomeDirectory) but ran *before* either consumer fired
the probe — so a fresh winget install on its first invocation derived its
Aspire home with the sidecar still unstamped.

Hoist the invocation into the CliExecutionContext DI singleton factory in
Program.cs, immediately before BuildCliExecutionContext. The factory body now
runs the probe once per CLI invocation, before any downstream code reads the
sidecar. BundleService drops its optional WingetFirstRunProbe? parameter and
InstallsCommand drops its ctor parameter, field, and explicit call site.

Tests:

  - WindowsRegistryReaderTests (new, 7 tests, Windows-only) exercises the real
    HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall hive: matching
    entry returns true; wrong identifier / missing target / different target /
    surrounded by noise all return false; path comparison is case-insensitive;
    empty processPath returns false. Each test creates a uniquely-named
    `Microsoft.Aspire.Tests.<guid>` subkey and deletes it in a using; a static
    sweeper removes leftovers from any crashed prior run before tests start.

  - WingetStartupProbeTests (new, 2 tests) pins the new TryRunWingetFirstRunProbe
    contract: it resolves the probe from DI and invokes Run with the binary
    directory of Environment.ProcessPath, and any IWindowsRegistryReader
    exception is swallowed so CLI startup is never broken by a probe failure.
    TryRunWingetFirstRunProbe is internal for testing; the factory's use of it
    remains a one-line check at code review.

Net: -54/+50 production lines; +279 test lines; all existing tests still pass
(1366/1366 in the Acquisition + Commands + Bundles surface on macOS).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add an Aspire-owned cleanup surface for script and PR installs so users
can remove stale Aspire CLI state without remembering install layouts.

`aspire installs uninstall <id>` removes Aspire-owned install state for
a discovered install or orphan hive, refusing managed installs (Homebrew,
WinGet, dotnet tool) before any prompt and pointing the user back at the
owning package manager. PR installs also clean their dogfood install
directory; the shared script install under `~/.aspire/bin`,
`~/.aspire/bundle`, and `~/.aspire/versions/<v>` is only removed when the
caller passes `--remove-shared-install`.

Lower-level commands let callers operate on the pieces individually:
`aspire hives list/delete` for package hives, and `aspire uninstall
--channel|--all` for direct channel cleanup. `aspire installs list`
output now carries a per-row `Cleanup` hint pointing at the matching
command (`aspire installs uninstall <id>` for Aspire-owned rows,
package-manager command for managed rows).

The release and PR acquisition scripts gain a `--uninstall` /
`-Uninstall` mode so users can clean stale state even when no correct
Aspire CLI binary is on PATH. The scripts infer the channel from the PR
number (PR script) or `--quality` (release script), support
`--dry-run` / `-WhatIf`, require `--yes` for destructive actions, and
keep the shared script install untouched unless `--remove-shared-install`
is passed explicitly.

Refs microsoft#15614

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…aller uninstall paths

`validate_channel` was only present in `get-aspire-cli.sh`; `get-aspire-cli-pr.sh` accepted any channel value and interpolated it straight into the `rm -rf` target. Both `get-aspire-cli.ps1` and `get-aspire-cli-pr.ps1` had no validation at all, so a `-Channel "../../some-dir"` value resolved outside the hives root and was passed to `Remove-Item -Recurse`.

Mirror the existing `validate_channel` from `get-aspire-cli.sh` into `get-aspire-cli-pr.sh`, and add an equivalent `Test-UninstallChannel` to both PowerShell installer scripts. Reject channel names that contain path separators or `..` segments before composing any deletion path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two paths in `aspire uninstall --remove-shared-install` and the matching `--uninstall --remove-shared-install` script flows reported success on failure:

  - `TryGetBundleVersionTarget` caught `ResolveLinkTarget` exceptions and returned `false`, conflating "doesn't apply" with "couldn't resolve". The bundle symlink itself was then deleted on the way out and the user saw a clean operations list, while the on-disk `versions/<v>/` tree was silently stranded.
  - `UninstallAsync` with `--remove-shared-install` enumerated the bundle version directory and deleted it without checking `BundleVersionLease.HasActiveLease`. `BundleService.TryCleanupStaleVersions` explicitly skips leased entries; without the equivalent check here, another running CLI / AppHost could lose files mid-execution.
  - `Remove-CleanupPath` in `get-aspire-cli{,-pr}.ps1` called `Remove-Item -Recurse -Force` without `-ErrorAction Stop` and without a try/catch. With the script's default error action of `Continue`, `Remove-Item` failures (permission denied, file in use) were written to the error stream and execution fell through to the `Write-Message "removed: $Path"` line, producing a success-looking exit while the path was still on disk.

Refactor the shared-install cleanup so the bundle link target is resolved before the symlink is deleted; surface `ResolveLinkTarget` failures as a `Failed` cleanup operation; and skip the version directory with a clear reason when a `BundleVersionLease` is active. Wrap the PowerShell helper in try/catch with `-ErrorAction Stop` so a failed delete emits `failed: <path>` and propagates a non-zero exit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@radical radical force-pushed the radical/installs-cleanup branch from 67ef4a8 to 82397bb Compare May 25, 2026 22:54
@radical radical added this to the 13.4 milestone May 26, 2026
@IEvangelist
Copy link
Copy Markdown
Member

PR Testing Report

PR Information

CLI Version Verification

  • Expected Commit: 82397bb
  • Installed Version: 13.4.0-pr.17462.g82397bb0
  • Status: ✅ Verified (82397bb0 present in installed PR CLI version)

Changes Analyzed

Files Changed

Key changed areas reviewed from the PR diff:

  • src/Aspire.Cli/Commands/InstallsCommand.cs - install listing and uninstall command surface
  • src/Aspire.Cli/Commands/HivesCommand.cs - package hive list/delete command surface
  • src/Aspire.Cli/Commands/UninstallCommand.cs - channel cleanup command surface
  • src/Aspire.Cli/Uninstall/CliCleanupService.cs - cleanup operation planning/execution and path validation
  • src/Aspire.Cli/Acquisition/* - install discovery, sidecar, source, peer probing, and winget detection changes
  • eng/scripts/get-aspire-cli-pr.ps1, eng/scripts/get-aspire-cli.ps1 - PowerShell installer uninstall/validation/failure handling
  • eng/scripts/get-aspire-cli-pr.sh, eng/scripts/get-aspire-cli.sh - bash installer uninstall/validation/failure handling
  • localhive.ps1, localhive.sh, eng/clipack/Common.projitems - install-source sidecar/archive behavior
  • tests/Aspire.Acquisition.Tests/**, tests/Aspire.Cli.Tests/**, tests/Aspire.Hosting.Tests/PathLookupHelperTests.cs - test coverage for the new behavior
  • docs/specs/install-sources.md, docs/specs/cli-output-formats.md, docs/dogfooding-pull-requests.md - documentation/spec updates

Change Categories

  • CLI changes detected
  • Hosting integration changes
  • Dashboard changes
  • Client/component changes
  • Template changes
  • Installer script changes detected
  • Test changes detected

Test Scenarios Executed

Scenario 1: Dogfood install and version verification

Objective: Install the PR CLI from the dogfood command into an isolated temp path and verify the installed binary matches the PR head.
Coverage Type: Happy path
Status: ✅ Passed

Steps:

  1. Ran the PR dogfood PowerShell install script with -InstallPath, -SkipExtension, and -SkipPath.
  2. Verified the installed binary path under dogfood\pr-17462\bin\aspire.exe.
  3. Ran the installed binary with --version.

Evidence:

  • Log: C:\Users\dapine\.copilot\session-state\f58eca7f-8f83-4099-b95f-03511834c4a5\files\pr-17462-testing\install-output.txt
  • Version: C:\Users\dapine\.copilot\session-state\f58eca7f-8f83-4099-b95f-03511834c4a5\files\pr-17462-testing\version.txt

Observations:

  • Installed version was 13.4.0-pr.17462.g82397bb0, which matches PR head 82397bb0.

Scenario 2: Basic CLI/template/AppHost smoke

Objective: Verify the PR CLI can create and run a fresh Aspire app from the PR hive.
Coverage Type: Happy path
Status: ✅ Passed

Steps:

  1. Created a fresh aspire-empty app with --source <pr-hive>, --version 13.4.0-pr.17462.g82397bb0, --localhost-tld false, and --suppress-agent-init.
  2. Started the generated file-based AppHost with aspire start --apphost <app-root> --isolated --format json.
  3. Ran aspire describe --format json --include-hidden and verified the dashboard resource was running/healthy.
  4. Stopped the AppHost with aspire stop --apphost <app-root>.

Evidence:

  • Project creation: C:\Users\dapine\.copilot\session-state\f58eca7f-8f83-4099-b95f-03511834c4a5\files\pr-17462-testing\evidence\retry-smoke-new.txt
  • AppHost start: C:\Users\dapine\.copilot\session-state\f58eca7f-8f83-4099-b95f-03511834c4a5\files\pr-17462-testing\evidence\retry-smoke-start-fileapphost.txt
  • AppHost describe: C:\Users\dapine\.copilot\session-state\f58eca7f-8f83-4099-b95f-03511834c4a5\files\pr-17462-testing\evidence\retry-smoke-describe-fileapphost.txt
  • AppHost stop: C:\Users\dapine\.copilot\session-state\f58eca7f-8f83-4099-b95f-03511834c4a5\files\pr-17462-testing\evidence\retry-smoke-stop-fileapphost.txt

Observations:

  • The generated aspire-empty app is file-based (apphost.cs), so the AppHost target was the app directory, not a .csproj.
  • Dashboard resource reached Running / Healthy in describe output.

Scenario 3: Install discovery/list output

Objective: Validate aspire installs list --format json reports the PR install with an exact cleanup hint.
Coverage Type: Happy path
Status: ✅ Passed

Steps:

  1. Ran aspire installs list --format json using the installed PR CLI.
  2. Parsed the JSON and checked for the pr-17462 row.
  3. Verified the row pointed at the isolated install/hive and emitted Use: aspire installs uninstall pr-17462.

Evidence:

  • JSON output: C:\Users\dapine\.copilot\session-state\f58eca7f-8f83-4099-b95f-03511834c4a5\files\pr-17462-testing\evidence\installs-list-json.txt
  • Parsed check: C:\Users\dapine\.copilot\session-state\f58eca7f-8f83-4099-b95f-03511834c4a5\files\pr-17462-testing\evidence\installs-list-check.txt

Observations:

  • The PR install row had status: notOnPath, expected because installation used -SkipPath.

Scenario 4: Cleanup safety and exact-id behavior

Objective: Validate cleanup refuses unsafe or ambiguous operations without deleting the PR install unexpectedly.
Coverage Type: Unhappy path / safety
Status: ✅ Passed

Steps:

  1. Ran aspire installs uninstall pr-17462 --dry-run --yes from the same PR CLI.
  2. Verified the command left the CLI and hive in place.
  3. Ran aspire installs uninstall pr-17462-typo --dry-run --yes.
  4. Verified the typoed ID failed with a clear no-match error and did not delete pr-17462.

Evidence:

  • Exact dry-run: C:\Users\dapine\.copilot\session-state\f58eca7f-8f83-4099-b95f-03511834c4a5\files\pr-17462-testing\evidence\installs-uninstall-dry-run.txt
  • Dry-run preservation check: C:\Users\dapine\.copilot\session-state\f58eca7f-8f83-4099-b95f-03511834c4a5\files\pr-17462-testing\evidence\installs-uninstall-dry-run-check.txt
  • Typoed ID failure: C:\Users\dapine\.copilot\session-state\f58eca7f-8f83-4099-b95f-03511834c4a5\files\pr-17462-testing\evidence\installs-uninstall-typo.txt
  • Typo preservation check: C:\Users\dapine\.copilot\session-state\f58eca7f-8f83-4099-b95f-03511834c4a5\files\pr-17462-testing\evidence\installs-uninstall-typo-check.txt

Expected Unhappy-Path Outcome: Invalid or unsafe cleanup should produce a non-zero/clear failure and leave the install intact.

Observations:

  • Same-process cleanup dry-run reported the hive as would remove but refused the dogfood install path because the running CLI was inside the target; no state was removed.
  • pr-17462-typo failed with: No Aspire CLI install or hive named 'pr-17462-typo' was found. Run 'aspire installs list' to see uninstallable IDs.

Scenario 5: Hive command lifecycle

Objective: Validate hive listing, safe refusal, dry-run behavior, and forced isolated hive deletion.
Coverage Type: Happy path / unhappy path
Status: ✅ Passed

Steps:

  1. Ran aspire hives list and verified the isolated pr-17462 hive appeared.
  2. Ran aspire hives delete pr-17462 --dry-run --yes without --force.
  3. Verified deletion was refused because a matching dogfood install existed.
  4. Ran aspire hives delete pr-17462 --force --yes after template/AppHost use was complete.
  5. Verified only the isolated temp hive was removed.

Evidence:

  • Hive list: C:\Users\dapine\.copilot\session-state\f58eca7f-8f83-4099-b95f-03511834c4a5\files\pr-17462-testing\evidence\hives-list.txt
  • Safe refusal: C:\Users\dapine\.copilot\session-state\f58eca7f-8f83-4099-b95f-03511834c4a5\files\pr-17462-testing\evidence\hives-delete-dry-run.txt
  • Refusal/preservation check: C:\Users\dapine\.copilot\session-state\f58eca7f-8f83-4099-b95f-03511834c4a5\files\pr-17462-testing\evidence\hives-delete-dry-run-check.txt
  • Forced delete: C:\Users\dapine\.copilot\session-state\f58eca7f-8f83-4099-b95f-03511834c4a5\files\pr-17462-testing\evidence\hives-delete-force.txt
  • Forced delete check: C:\Users\dapine\.copilot\session-state\f58eca7f-8f83-4099-b95f-03511834c4a5\files\pr-17462-testing\evidence\hives-delete-force-check.txt

Expected Unhappy-Path Outcome: Deleting a hive with a matching dogfood install should fail unless --force is supplied.

Observations:

  • The no-force delete failed safely with guidance to use aspire uninstall --channel pr-17462 or pass --force.
  • Forced deletion removed the isolated temp hive path only.

Scenario 6: CLI and installer script uninstall validation

Objective: Validate invalid channel hardening and PowerShell PR script cleanup behavior.
Coverage Type: Happy path / unhappy path
Status: ✅ Passed

Steps:

  1. Ran aspire uninstall --channel ..\bad --dry-run --yes and verified channel validation rejected path traversal/separators.
  2. Downloaded the changed PowerShell installer scripts from the PR branch into the temp workspace.
  3. Ran get-aspire-cli.ps1 -Uninstall -Channel ..\bad -Yes and verified script-side validation rejected the channel.
  4. Ran get-aspire-cli-pr.ps1 17462 -InstallPath <temp> -Uninstall -WhatIf and verified it reported planned dogfood cleanup.
  5. Ran get-aspire-cli-pr.ps1 17462 -InstallPath <temp> -Uninstall -Yes and verified it removed the isolated PR hive and dogfood install.

Evidence:

  • CLI invalid channel: C:\Users\dapine\.copilot\session-state\f58eca7f-8f83-4099-b95f-03511834c4a5\files\pr-17462-testing\evidence\cli-uninstall-invalid-channel.txt
  • Release script invalid channel: C:\Users\dapine\.copilot\session-state\f58eca7f-8f83-4099-b95f-03511834c4a5\files\pr-17462-testing\evidence\script-release-invalid-channel.txt
  • PR script dry-run: C:\Users\dapine\.copilot\session-state\f58eca7f-8f83-4099-b95f-03511834c4a5\files\pr-17462-testing\evidence\script-pr-uninstall-dry-run.txt
  • PR script uninstall: C:\Users\dapine\.copilot\session-state\f58eca7f-8f83-4099-b95f-03511834c4a5\files\pr-17462-testing\evidence\retry-lifecycle-final-cleanup.txt

Expected Unhappy-Path Outcome: Invalid channel values should be rejected before path cleanup and return non-zero.

Observations:

  • CLI and script validation both rejected ..\bad before deletion.
  • PR script uninstall removed the isolated temp install/hive successfully.

Summary

Scenario Status Notes
Dogfood install/version ✅ Passed Version matched 82397bb0
Basic CLI/template/AppHost smoke ✅ Passed Fresh file-based AppHost created, started, described, stopped
Install discovery/list output ✅ Passed pr-17462 row and cleanup hint present
Cleanup safety/exact-id behavior ✅ Passed Unsafe self cleanup and typoed ID failed safely
Hive command lifecycle ✅ Passed No-force delete refused safely; forced isolated hive delete succeeded
CLI/installer uninstall validation ✅ Passed Invalid channels rejected; PR script cleanup removed isolated install

Overall Result

✅ PR VERIFIED

No blocking issues were found in the locally tested Windows/PowerShell path. The same-process aspire installs uninstall pr-17462 --dry-run behavior returns non-zero because the running CLI is inside the target; the command does not delete state and the message is clear. If that non-zero dry-run exit is intentional, no follow-up is needed.

Recommendations

  • Consider whether same-process --dry-run should exit successfully when it only reports planned deletion, or whether the current non-zero safety refusal is the intended contract.
  • I did not run the bash installer scripts locally because this run targeted Windows PowerShell mode.

Artifacts

  • Sanitized report: C:\Users\dapine\.copilot\session-state\f58eca7f-8f83-4099-b95f-03511834c4a5\files\pr-17462-testing\pr-17462-testing-report.md
  • Sanitized evidence directory: C:\Users\dapine\.copilot\session-state\f58eca7f-8f83-4099-b95f-03511834c4a5\files\pr-17462-testing\evidence
  • Original temp workspace: C:\Users\dapine\AppData\Local\Temp\aspire-pr-test-2d949d7f15934eda8c13431c5899cd1b
  • Screenshots: None captured; this PR changes CLI/acquisition behavior, so command logs and JSON output were captured instead.

Copy link
Copy Markdown
Member

@IEvangelist IEvangelist left a comment

Choose a reason for hiding this comment

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

Posted 1 code review comment.

return CleanupOperation.Failed(target.FullName, "Could not determine the running CLI path; refusing to delete cleanup targets. Re-run from a fully-resolved CLI invocation.");
}

if (IsPathUnderTarget(currentProcessPath, target.FullName))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This guard runs before the dryRun branch below, so previewing cleanup can fail even though nothing would be deleted. For example, running aspire installs uninstall pr-17462 --dry-run from that PR install hits this path because the current CLI is inside the dogfood target and returns a failure instead of showing the planned removals. Move the dry-run case ahead of the self-delete guard (or make this guard non-fatal for dry-run) so --dry-run remains describe-only.

@radical radical removed this from the 13.4 milestone May 28, 2026
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.

2 participants