CLI identity sidecar#18087
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 18087Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 18087" |
|
@copilot review this pr |
There was a problem hiding this comment.
Pull request overview
This PR introduces two related features: (1) a CLI identity sidecar that replaces compile-time stamping of channel/quality/version into the CLI binary with a runtime resolution chain (environment variables → sidecar config file → assembly metadata), and (2) a Dogfooder TUI tool (tools/Dogfooder/) — a Hex1b-based developer test rig that drives the identity sidecar to reproduce versioning bugs end-to-end with an embedded NuGet proxy, per-session workspace isolation, and scenario-based configuration.
Changes:
- New
IdentityResolverwith a 3-layer env→sidecar→assembly fallback chain, plus env-var stripping inProcessExecutionFactoryandPeerInstallProbeto prevent identity leakage to child processes, andNuGetServiceIndexOverrideplumbing through packaging/template code. - Build script fix: changed
-warnAsError 0to-warnAsError falsein botheng/build.shandeng/build.ps1to resolve MSB4030 errors when Arcade forwards the value to MSBuild'sTreatWarningsAsErrorsproperty. - Full
tools/Dogfooder/TUI application with scenario registry, embedded NuGet proxy server, environment validation probes, per-session temp workspace isolation, and NuGet traffic inspector.
Show a summary per file
| File | Description |
|---|---|
src/Aspire.Cli/Acquisition/IIdentityResolver.cs |
New interface defining identity resolution contract with IdentityValue<T> and IdentitySource enum |
src/Aspire.Cli/Acquisition/IdentityResolver.cs |
Implementation: env→sidecar→assembly fallback, Lazy<T> caching, IdentityEnvVarNames for stripping |
src/Shared/AspireCliIdentityEnvVars.cs |
Shared constants for ASPIRE_CLI_* env var names, link-included by CLI and Dogfooder |
src/Aspire.Cli/Acquisition/IInstallSidecarReader.cs |
Extended InstallSidecarInfo record with Channel/Version/Commit/NuGetServiceIndexOverride |
src/Aspire.Cli/Acquisition/InstallSidecarReader.cs |
Refactored to read all sidecar fields via new SidecarFields struct |
src/Aspire.Cli/CliExecutionContext.cs |
Added IdentityVersion, IdentityCommit, NuGetServiceIndexOverride properties |
src/Aspire.Cli/Program.cs |
DI wiring for IIdentityResolver, new BuildCliExecutionContext overload |
src/Aspire.Cli/DotNet/ProcessExecutionFactory.cs |
Strips ASPIRE_CLI_* env vars before spawning child processes |
src/Aspire.Cli/Acquisition/PeerInstallProbe.cs |
Same env var stripping for peer probes |
src/Aspire.Cli/Packaging/PackagingService.cs |
Uses NuGetOrgUrl for service index override |
src/Aspire.Cli/Packaging/PackageSourceOverrideMappings.cs |
Accepts nugetServiceIndexOverride parameter |
src/Aspire.Cli/Templating/TemplateNuGetConfigService.cs |
Passes through NuGet override |
src/Aspire.Cli/Templating/DotNetTemplateFactory.cs |
Passes through NuGet override |
src/Aspire.Cli/Projects/PrebuiltAppHostServer.cs |
Uses NuGet override for source lists |
src/Aspire.Cli/Aspire.Cli.csproj |
Added link to shared AspireCliIdentityEnvVars.cs |
eng/build.sh |
Changed -warnAsError 0 to -warnAsError false |
eng/build.ps1 |
Changed -warnAsError 0 to -warnAsError false |
tests/Aspire.Cli.Tests/Acquisition/IdentityResolverTests.cs |
12 test methods covering env/sidecar/assembly fallback |
tests/Aspire.Cli.Tests/Acquisition/InstallSidecarReaderTests.cs |
Tests for new sidecar fields |
tools/Dogfooder/README.md |
Documentation (significantly outdated vs implementation) |
tools/Dogfooder/State/SessionWorkspace.cs |
Per-session temp directory + NuGet.config + manifest |
tools/Dogfooder/State/SessionPreparationState.cs |
Mutable build-phase state with lock-based concurrency |
tools/Dogfooder/State/NuGetTrafficState.cs |
Live NuGet traffic snapshot with editor caching |
tools/Dogfooder/State/EnvironmentValidationState.cs |
Probe results for dotnet/gh/cert validation |
tools/Dogfooder/Services/VCurrentVersionResolver.cs |
Resolves latest stable version from nuget.org |
tools/Dogfooder/Services/DogfoodingNuGetServer.cs |
Embedded NuGet v3 proxy with local package overlay |
tools/Dogfooder/Services/DogfoodSessionPreparer.cs |
Orchestrates build→proxy→env pipeline |
tools/Dogfooder/Commands/RunCommand.cs |
TUI boot, validation probes, screen routing |
tools/Dogfooder/Screens/Workspace/SessionNuGetTrafficContent.cs |
NuGet traffic inspector with tabbed detail view |
Aspire.slnx |
Added Dogfooder project to solution |
Copilot's findings
- Files reviewed: 61/61 changed files
- Comments generated: 3
| public Phase CurrentPhase | ||
| { | ||
| get { lock (_gate) { return _phase; } } | ||
| } | ||
|
|
||
| public string? FailureReason { get; private set; } |
…mit + nuget service index Replace the single assembly-stamped channel reader with a layered identity resolver: env var → .aspire-install.json sidecar field → assembly-baked fallback (or terminal default). Each resolved value is tagged with its source so 'aspire doctor --self' can show whether an override is active. Adds ASPIRE_CLI_CHANNEL/_VERSION/_COMMIT/_NUGET_SERVICE_INDEX env vars plus matching optional sidecar fields. NuGet service-index override only rewrites URLs the CLI writes into new configs — never URLs it reads from existing user configs. Identity env vars are stripped at every child process spawn (PeerInstallProbe, ProcessExecutionFactory) so a parent's override never leaks into peer probing. Spec: docs/specs/cli-identity-sidecar.md Issues addressed: #17527, #17580, #17596, #17750 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Phase 1: skeleton + environment validation + session list + embedded terminal with ASPIRE_CLI_* env-var injection + JSON persistence at ~/.aspire/dogfooder/sessions.json. self-test subcommand reserved (stub). - Moves ASPIRE_CLI_* env var constants from IdentityResolver into src/Shared/AspireCliIdentityEnvVars.cs so both Aspire.Cli and the new tools/Dogfooder project can reference them without a project ref. - State/Services/Screens layering keeps all mutable state out of widget builders so future Hex1bTerminalAutomator-driven self-test can drive the same state machine without a TTY. - Embedded terminal panel uses Hex1bTerminalProcessOptions.Environment to inject overrides directly into the child shell; no per-shell export/set/$env formatters needed. See tools/Dogfooder/README.md for the 5-phase roadmap. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Stop silently injecting ASPIRE_CLI_* env vars and a PATH-prepend through Hex1bTerminalProcessOptions.Environment. Instead, after the embedded shell launches, drive a Hex1bTerminalAutomator that types the equivalent export/$env:/set commands into the PTY so the dogfooding setup is visible in the terminal scrollback. Also wires up: - LocalAspireCliLocator probe (gates EnvironmentValidationScreen) that finds artifacts/bin/Aspire.Cli/**/aspire and exposes its directory. - DogfoodSessionPreparer.BuildPlan returning a SessionEnvironmentPlan (ordered identity overrides + optional PATH-prepend dir). - SessionListPanel: re-selecting an already-highlighted session flicks back to its terminal (OnItemActivated alongside OnSelectionChanged). - AppState.SelectSession picks Config vs Terminal based on Plan presence rather than session Status, so exited shells still show scrollback. - Mouse support on the embedded terminal (.WithMouse(true)). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the fixed left/right splitter (MainScreen + SessionListPanel +
right detail) with a floating-window workspace modeled on
hex1b/samples/WindowingDemo:
MenuBar File: New Session / Quit
Sessions: dynamic submenu of existing sessions
Help: About
WindowPanel one floating window per session (MDI area)
InfoBar status + session count + open-window count
Per-session window flow:
1. File → New Session creates the session and opens a new window whose
body is the config form (channel/version/commit/nuget-index, with
inline 'pr-<N>' parsing).
2. Inside the window, 'Continue →' calls preparer.BuildPlan(...) and
stamps session.Plan, which causes the window's content lambda to
re-evaluate on the next frame and swap to the embedded terminal body.
3. The embedded terminal then runs Hex1bTerminalAutomator and types the
identity-override commands visibly into the spawned shell.
4. Closing the window disposes the PTY and unregisters from the window
map; reopening from the Sessions menu spins up a fresh shell.
Factoring (all new in Screens/Workspace/):
WorkspaceScreen root composition (MenuBar+WindowPanel+InfoBar);
owns the registries for the workspace lifetime
MenuBarBuilder pure menu construction
StatusBarBuilder pure footer construction
SessionWindowOpener open-or-focus dispatch; size/position/cleanup
SessionConfigContent form body builder (replaces SessionConfigPanel)
SessionTerminalContent PTY body builder (replaces SessionTerminalPanel)
SessionWindowRegistry session.Id → WindowHandle map
SessionTerminalRegistry session.Id → (terminal, handle, cts) map
ShellCommandFormatter extracted shell detection + per-kind formatting
AppState trimmed accordingly: DetailMode/DraftConfig/DraftSessionName/
ActiveSession and the BeginNewSession/SelectSession/SwitchToTerminal/
UpdateDraft* methods are gone — each window edits its session in place
and the workspace's window registry is the source of truth for which
sessions are 'open' vs merely 'stored'. AppState gains a StatusMessage
free-form string used by the footer.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the free-text 'Channel' field with a horizontal ToggleSwitch (Stable / Staging / Daily / PR / Local). Beside it sits a narrow PR-number FormTextField that is enabled only when the toggle is on PR (via FormTextFieldWidget.EnableWhen) and dimmed otherwise. Both controls live inside the existing Form so the PR-number field gets the form's registry/EnableWhen pipeline; an HStack inside the form's children list provides the visual grouping without disturbing the remaining label-above text fields above/below it. Switching off the PR branch clears Config.PrNumber so a stale value from a prior selection can't leak into ASPIRE_CLI_CHANNEL when Continue is pressed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Match the WindowingDemo terminal default (.WithScrollback(1000)) but bumped to 5000 lines so the typed-in identity-override commands plus a reasonable interactive session (build/test output, log tails) all stay reachable. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the per-knob config form (channel + version + commit + suffix + proxy + build toggles) with pre-baked scenarios. Each scenario owns its own coherent settings bundle (channel/version/build/proxy/...) so users pick an intent rather than assembling combinations themselves. Catalog: repro-vcurrent-local repro-vcurrent-published vnext-minor-stable-local vnext-hotfix-stable-local vnext-pr-local (only scenario with an input: prNumber) vnext-daily-local vnext-staging-local DogfoodSessionConfig collapses to (ScenarioId, Inputs). The preparer resolves the scenario via DogfoodScenarioRegistry, calls Build(inputs) to obtain a ScenarioPlan, then runs the existing build/proxy/env pipeline against that plan. The plan is cached on the session so the window opener can dispatch tabs without re-deriving it. Form: scenario list + dynamic per-scenario inputs. Persistence bumps to v3 (ScenarioId + Inputs). v1/v2 records fall back to 'repro-vcurrent-published' (no build, no proxy) so existing sessions stay openable. Terminal: .Fill() on TerminalWidget so it claims the full window/tab bounds (TerminalNode.Arrange auto-resizes the PTY to its layout bounds, so this is both the visual fix and the resize fix). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The package build must always happen in a pristine environment — never inside the dogfooded shell. If the user launched the Dogfooder from a shell that already had ASPIRE_CLI_CHANNEL/VERSION/COMMIT/NUGET_SERVICE_INDEX set (e.g. from a prior dogfood session that left them exported), those would propagate into the build's NuGet restore and version stamping and produce confusing failures. Explicitly strip the full identity surface from ProcessStartInfo.Environment before launching ./build.sh / build.cmd so the build sees the same env it would see if invoked directly from a fresh terminal. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the MDI workspace (menu bar + window manager + session list + on-disk session persistence) with a single full-screen SessionScreen. One Dogfooder process owns exactly one dogfooding session — if you want a second session, launch a second Dogfooder (typically in a second worktree). Nothing is persisted between launches. Removed: - WorkspaceScreen, MenuBarBuilder, StatusBarBuilder - SessionWindowOpener, SessionWindowRegistry - DogfoodSessionStore, ISessionStoreFile, SessionStoreFile - DogfoodingNuGetServerRegistry (collapsed into the screen) - IPrCatalog/StubPrCatalog (was a stub for the old PR-picker form) Added: - Screens/SessionScreen.cs — three-way dispatch (Config | Preparing | Terminal/Tabs) anchored at the root context, with a single-line info bar at the top. Updated: - AppState — dropped DogfoodSessionStore. - RunCommand — boots SessionScreen directly; no load/save. - SessionConfigContent — takes an onContinue callback instead of poking the window manager. - SessionNuGetTrafficContent — takes a DogfoodingNuGetServer? directly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The in-TUI build log lives only in memory, which is useless after the
TUI exits (or after a panic). PackageBuildRunner now mirrors every stdout
+ stderr line into a per-invocation file at:
artifacts/log/dogfooder/build-{yyyyMMdd-HHmmss}-{pid}.log
under the repo root. The log path is the first line of the Build log
tab and the last line on exit, so it is visible inside the TUI too. The
StreamWriter uses AutoFlush so a crash mid-build still leaves a partial
log on disk to grep.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
NU5104 ('stable release should not have a prerelease dependency') is a
publish-time gate that nuget.org uses to prevent shipping stable packages
which would force consumers onto prerelease deps at restore time. For
dogfooding we are NEVER publishing — every nupkg produced lives at most
inside the local DogfoodingNuGetServer overlay — so the gate just blocks
scenarios like repro-vcurrent-local from building until transitive
Azure.Provisioning / Milvus / Azure.AI deps are promoted out of beta in
the 13.5 cycle.
Suppress NU5104 and NU5125 (missing readme — same publish-time concern)
via /p:NoWarn on the dogfood pack only. The real release pipeline never
sets VersionSuffix='' until the upstream deps are stable, so its gates
are unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
MSBuild's command-line parser splits raw ';' inside /p: values, reporting MSB1006 'Property is not valid' against the second half. URL-encode the separator so MSBuild forwards the full 'NU5104;NU5125' value to the NoWarn property unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
An empty VersionSuffix puts Arcade on the stable-release code path which turns TreatWarningsAsErrors on, escalating CS1591/CS1573/CS8629/etc. into tens of thousands of build errors across tests/ and analyzers. Dogfood packages never leave the in-process proxy, so disable the strict gate explicitly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
/p:NoWarn=... becomes an MSBuild global property which replaces every project's <NoWarn>$(NoWarn),1573,1591,...</NoWarn> assignment (global properties can't be overridden in a csproj PropertyGroup without TreatAsLocalProperty). That wiped per-project CS1591/CS1573/etc. suppressions and turned the pack into 24k unrelated XML-doc and nullable errors across tests/ and analyzers/. Switch to WarningsNotAsErrors, which only demotes NU5104/NU5125 from error back to warning and leaves every project's NoWarn intact. Also drop the TreatWarningsAsErrors=false override since it's no longer needed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…etion Two related fixes to the Preparing screen: 1. Add a 150ms invalidation pump on SessionScreen.RunPreparationAsync. SessionPreparationState.OnChanged previously fired into the void because nothing bridged it to the Hex1b app invalidation. The pump ticks Notifier.Notify() while the build/proxy phases are running so streaming output renders without requiring user input. Cancelled in a finally block when preparation reaches a terminal phase. 2. On Complete/Failed, swap the streaming TextBlock tail for a read-only Hex1b Editor populated with the full log. Adds SessionPreparationState.GetOrCreateLogEditor() which lazily wraps the accumulated log lines in an EditorState; the cache is invalidated on Append so a late line still shows up if one slips in after the phase change. The Editor gives full keyboard navigation, scrollback, and text selection so users can copy errors out without re-tailing the on-disk build-*.log file. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Arcade's eng/build.sh always re-injects /p:TreatWarningsAsErrors=true on the inner MSBuild command line through eng/common/tools.sh's RunBuildTool helper, which silently overrode our outer /p:WarningsNotAsErrors=NU5104 and made the pack fail on Azure.AI.* / Milvus.Client / etc. again. The documented escape hatch is the TreatWarningsAsErrors=false *env var*: eng/build.sh checks for it and forwards '-warnAsError 0' down to Arcade, which then flips TWAE=false for the inner MSBuild call. Set it on the build subprocess environment so dogfood packs run with warnings not promoted to errors, without touching per-project NoWarn. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…=false Arcade's eng/common/tools.sh substitutes the value verbatim into /p:TreatWarningsAsErrors=$warn_as_error on the inner MSBuild command line. The Csc task only accepts boolean literals (true/false); passing '0' fails with MSB4030: '"0" is an invalid value for the "TreatWarningsAsErrors" parameter of the "Csc" task'. The earlier '0' was shell-truthy enough to pass through Arcade's bash/PowerShell checks but blew up the actual compile. This makes the documented TreatWarningsAsErrors=false env-var escape hatch actually work end-to-end, which the Dogfooder relies on to pack without NU5104 (stable-with-prerelease-deps) erroring out on Azure.AI.* / Milvus.Client / etc. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Aspire CLI's NuGet plumbing rejects cleartext package sources unless allowInsecureConnections="true" is set in NuGet.Config, and the CLI's own ConfigureNuGetSourcesAsync path does not currently set it. Instead of plumbing that flag through (and pinning the CLI to a specific NuGet schema version), bind Kestrel to https://127.0.0.1:0 and let its default certificate provider resolve the 'dotnet dev-certs https' cert from the user store. NuGet's HttpClient trusts the OS root store, so the trusted dev cert just works. Also add a 'dev cert' probe to the EnvironmentValidationScreen so a missing/untrusted cert surfaces with a clear remediation hint ('dotnet dev-certs https --trust') rather than as an opaque TLS error at proxy startup. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
GetOrCreateTerminal previously launched the embedded shell PTY on a
fire-and-forget Task whose exceptions were silently dropped. When the
shell binary couldn't be found, or the PTY couldn't allocate, the
user just saw a blank black rectangle on the Terminal tab with no
indication that anything had gone wrong — indistinguishable from
"shell still booting".
Capture exceptions onto DogfoodSession.TerminalCrashMessage and have
SessionTerminalContent.Build render a clear failure panel
('[ Terminal failed to start ] <exception>') when it's set, so the
real failure surfaces in the TUI.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CreateSlimBuilder trims the Kestrel HTTPS configuration loader, so binding to https:// throws 'Call UseKestrelHttpsConfiguration() to enable HTTPS configuration' at startup. Switch to the full builder so Kestrel can resolve the dev cert from the user store. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…er view NuGet view UX overhaul: - Replaces the cobbled-together text table with a real Hex1b TableWidget (focusable rows, headers, key-stable selection), wrapped in a VSplitter with a kind-specific details pane below. - Selection lives on NuGetTrafficState.SelectedEventId as a Guid? so it survives new inbound events shifting the table; the view layer falls back to 'first visible row' when the selected event scrolls off the ring buffer or the filter excludes it (otherwise the details pane would go permanently blank). Per-kind details: - New polymorphic NuGetTrafficDetails record hierarchy (ServiceIndex, FlatVersions, Nupkg, Nuspec, Registration, Search, Autocomplete, Unknown). Each handler stashes a typed details record onto the HandlerState; the middleware copies it onto the emitted event. - The details pane pattern-matches the subtype so each request kind gets a tailored explanation (versions list for flatcontainer, local-vs-upstream byte source for nupkg, merged item counts for registration, URL-rewrite counter for search, etc.). - Common envelope (Source, UpstreamUrl, LocalPackagesUsed, ErrorMessage) stays on the response — no duplication with the per-kind details. URL rewriting in proxy responses: - New RewriteUpstreamUrls walks JSON nodes and rewrites @id / registration / packageContent / catalogEntry / parent / items strings whose value sits under the resolved upstream flat-container or registration base URI, mapping to the equivalent proxy path. Only these structural keys are rewritten — free-form text fields like description/summary are left alone so we don't mangle unrelated URLs. - Wired into /v3/search (rewrites the merged page so CLI follows-up via the proxy instead of going direct to nuget.org) and /v3/reg (sweeps upstream-clone catalogEntry payloads after MergeRegistration). Without this the CLI's restore + template install paths bypass the proxy on the second hop, defeating the local-override semantics that motivate the proxy in the first place. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| // Shared services. | ||
| // Main's `IdentityChannelReader` is constructed in CliStartupContext so | ||
| // the channel can be logged at startup before DI is fully wired. This PR | ||
| // adds a richer `IIdentityResolver` that also handles sidecar/env | ||
| // overrides for version/commit/nuget service index. Both coexist: | ||
| // `IdentityChannelReader` continues to power the early startup log, | ||
| // while `IIdentityResolver` powers `CliExecutionContext` construction. |
| /// <summary> | ||
| /// Resolver had nothing to read — no env var, no sidecar field, no | ||
| /// assembly stamp — and used the terminal default. For channel this is | ||
| /// <c>local</c>. Other fields do not use this source today. | ||
| /// </summary> | ||
| TerminalDefault, |
|
|
||
| ### Validation | ||
|
|
||
| Channel values are validated against the existing accepted shape: `stable`, `staging`, `daily`, `local`, or `pr-<N>` where `<N>` is one or more ASCII digits. Invalid values from any source (env, sidecar, assembly) fail fast at resolve time with a diagnostic naming the source, so a typo in `ASPIRE_CLI_CHANNEL` does not silently fall through to the next layer. |
|
Re-running the failed jobs in the CI workflow for this pull request because 4 jobs were identified as retry-safe transient failures in the CI run attempt.
|
For polyglot apphosts that persist a channel pin (e.g. "channel": "daily") in aspire.config.json, `aspire add` ranked the implicit/ambient channel ahead of the pinned channel. The implicit channel mirrors nuget.org and returns the stable version, while the pinned channel maps Aspire* to its own feed (e.g. dotnet9 for daily). Implicit-first ranking made the stable version the non-interactive auto-pick and the interactive default, but the project's NuGet.config can only restore from the pinned feed - so the wrong version was selected and restore failed. Thread the apphost's configured channel into GetPackageByInteractiveFlow and the version prompter, and rank a package from the pinned channel ahead of the implicit channel. C# apphosts (configuredChannel == null) keep the prior implicit-first behavior. This is the polyglot `aspire add` selection-path manifestation of #17294. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
|
||
| ### Validation | ||
|
|
||
| Channel values are validated against the existing accepted shape: `stable`, `staging`, `daily`, `local`, or `pr-<N>` where `<N>` is one or more ASCII digits. Invalid values from any source (env, sidecar, assembly) fail fast at resolve time with a diagnostic naming the source, so a typo in `ASPIRE_CLI_CHANNEL` does not silently fall through to the next layer. |
…site Audit finding: CliExecutionContext.IdentityVersion/IdentityCommit were resolved but never consumed, so ASPIRE_CLI_VERSION/ASPIRE_CLI_COMMIT (and the install sidecar's version/commit fields) were inert. Only IdentityChannel and the NuGet service-index override were wired through. This completes the sidecar per docs/specs/cli-identity-sidecar.md. - CliExecutionContext: add IdentitySdkVersion (IdentityVersion with +build metadata stripped) and IdentityOverridden (true when any field came from an env var or the sidecar rather than the assembly stamp). - Migrate every identity-conditional call site to read CliExecutionContext: PackagingService staging feed/version, PackageChannel template filter, New/Add/Update commands, TemplateNuGetConfigService, InitCommand and ScaffoldingService version stamping, GuestAppHostProject CLI/SDK skew warning, ExtensionRpcTarget, and SDK/skills generation. VersionHelper is now identity-agnostic (match takes the version as a parameter). Genuine physical-binary reads stay on the assembly and are annotated // physical-binary-version-by-design. - Telemetry split: keep binary cli.version/cli.build_id; emit identity.version/identity.channel (and identity.commit when present) alongside so override-driven runs don't poison the telemetry corpus. - aspire --version now prints the identity version via a custom IdentityVersionAction; non-override output is byte-identical to the default. - Add a yellow stderr startup notice whenever identity is overridden and output is human-readable, so a diagnostic run is never mistaken for a real one. aspire doctor intentionally still reports the physical install. - Add IdentityCallSiteGuardrailTests greps the CLI tree for physical version reads against a tight file-level allow-list (plus a stale-entry check). - Acceptance tests for the --version override, IdentityOverridden flag computation, and telemetry identity tags. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds an ASPIRE_CLI_PACKAGES identity override (env var + .aspire-install.json `packages` field) that points the CLI's Aspire* package feed at a flat directory of .nupkg files. The resolved directory surfaces as CliExecutionContext.IdentityPackagesDirectory and PackagingService synthesizes a channel (named after the identity channel) that routes Aspire* to the directory, replacing any same-named built-in/discovered/PR channel. A fail-fast guardrail rejects a missing directory or duplicate Aspire* versions so a flat feed can't silently mask the intended version. Also adds the cli-channel-debugging skill documenting the full CLI-identity emulation matrix (local/PR/daily/staging/stable scenarios and which override knobs to set), and updates the cli-identity-sidecar spec to document the new override and sidecar field. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
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.
|
…debugging skill
Bundles two cross-platform (.sh/.ps1) helper scripts with the skill and documents
the Terminal-canvas-per-scenario workflow:
- get-aspire-channel-version.{sh,ps1}: resolves the latest Aspire version for an
emulation channel by querying the exact feed the CLI's package channels use
(stable -> nuget.org, daily -> dnceng/dotnet9, staging -> darc-pub-microsoft-
aspire-<sha8>). The dnceng flat2 arrays are unsorted and interleave old 9.x
builds, so it parses and SemVer-sorts itself; prints only the version to stdout.
- emulate-aspire-cli.{sh,ps1}: sourceable/dot-sourceable one-liner that resolves
the version (for feed-backed channels), builds the CLI, exports ASPIRE_CLI_*,
and defines an `aspire` function pointing at the local build.
SKILL.md gains a "Helper scripts" section, a "Driving a scenario from a Terminal
canvas (Copilot app)" workflow (open_canvas/send_terminal_input, with the
read_terminal_output-unavailable fallback), wires the scripts into the daily/
stable recipes, and lists them in References.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Version, commit, and NuGet service-index overrides (from env vars or the install sidecar) are now shape-validated when resolved, so a typo fails fast with a diagnostic naming the source instead of silently producing a bogus staging-feed name, an unrestorable NuGet.config URL, or a SemVer parse failure deep in the stack. Channel is intentionally left unvalidated for env/sidecar so bespoke labels (e.g. pr-17580) remain legitimate overrides; only the assembly-baked channel is shape-checked. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A stable/GA build resolves all Aspire.* packages from nuget.org, so 'aspire new' must not drop a per-project NuGet.config feed pin -- doing so is redundant and wipes the consumer's ambient feeds. This was an accidental 13.4 regression (PR #17120) that broadened channel selection so a stable build began selecting the Explicit 'stable' channel and tripping the config-drop path. The drop and channel pin are now gated on the channel routing Aspire* to a CUSTOM feed: daily (dotnet9), staging (darc-pub-microsoft-aspire-<sha8>), and pr/local (on-disk) still drop a config; stable does not. Differentiation is by channel NAME, not version shape, because a staging build can be stable-shaped (e.g. 13.4.4) yet still needs its darc-feed config. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two E2E tests exercise the CLI identity sidecar by making a locally built CLI impersonate a shipped build via ASPIRE_CLI_* overrides, then asserting the version- and channel-sensitive 'aspire new'/'aspire add' behavior: - EmulatedReleasedBuildTests: a stable/GA build resolves Aspire.* from nuget.org, so 'aspire new' must NOT drop a NuGet.config (C# and TypeScript starters). - EmulatedStagingBuildTests: a staging build resolves Aspire.* from its commit-specific darc feed, so 'aspire new' MUST drop a NuGet.config mapping Aspire* to darc-pub-microsoft-aspire-<sha8>, the AppHost SDK is pinned to the staging version, and 'aspire add' actually restores from that feed. The staging test discovers the latest real staging build's version+commit lightweightly (resolve the rc/daily download redirect, then probe recent release-branch commits' darc feeds) and skips if it cannot, with an ASPIRE_E2E_STAGING_VERSION/COMMIT escape hatch to pin it explicitly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| var nugetOverride = identityResolver.ResolveNuGetServiceIndexOverride().Value; | ||
| var packagesOverride = identityResolver.ResolvePackagesDirectory(); | ||
|
|
||
| // The CLI is "emulating" another build whenever any identity field was supplied by an | ||
| // ASPIRE_CLI_* env var or the install sidecar rather than the assembly's build-time stamp. | ||
| // This drives the startup override notice so a diagnostic run is never mistaken for a real one. | ||
| static bool IsOverride(IdentitySource source) => source is IdentitySource.Environment or IdentitySource.Sidecar; | ||
| var identityOverridden = IsOverride(channel.Source) || IsOverride(version.Source) || IsOverride(commit.Source) || IsOverride(packagesOverride.Source); |
When the CLI emulates a different build via ASPIRE_CLI_* identity overrides (or the install sidecar), GuestAppHostProject.IsUsingProjectReferences now returns false. A source (DEBUG) build run from inside the Aspire repo would otherwise trip AspireRepositoryDetector (via its Environment.ProcessPath fallback) and report project-reference mode for a polyglot apphost living in an arbitrary directory, short-circuiting channel resolution so an emulated staging/daily apphost silently resolved stable nuget.org packages instead of its pinned channel's feed. This keeps source-run emulation faithful to the installed CLI it impersonates (Release builds only honor ASPIRE_REPO_ROOT, so real installs are unaffected). Also completes the AppHost-language x channel-emulation E2E matrix: - staging x TypeScript test (EmulatedStagingBuildTests): asserts no NuGet.config is dropped, the SDK version equals the emulated staging version, and `aspire add valkey` resolves the staging darc-feed version (regression guard for the polyglot variant of #18114). - stable x TypeScript test renamed to EmulatedStableIdentityScaffoldsTypeScriptStarterWithoutNuGetConfig and now asserts no NuGet.config is dropped. - Class docs on both test classes explain the asymmetric matrix and why each language cell is exercised independently. - Bump the aspire-new project-name/output-path prompt timeouts to templateTimeout so slow staging/daily darc-feed template resolution doesn't time out (and hang the run during teardown). - Add GuestAppHostProject unit test + cli-channel-debugging skill note. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
❓ CLI E2E Tests unknown — 119 passed, 0 failed, 2 unknown (commit View all recordings
📹 Recordings uploaded automatically from CI run #27431061073 |
Description
Fixes #18114
This branch carries two related threads:
aspire addpinned-channel preference fix (aspire addon a polyglot AppHost pinned to a non-default channel selects the implicit/stable version over the pinned channel, breaking restore #18114) — On a daily CLI, a polyglot (JS/TS) apphost that pins"channel": "daily"inaspire.config.jsonranaspire add <pkg>and selected the implicit/ambient (nuget.org → stable 13.4.x) version instead of the pinned channel's preview. Because the project'sNuGet.configmapsAspire*to the daily feed only, restore then failed.AddCommand.GetPackageByInteractiveFlowranked the implicit/ambient channel first, ignoring the apphost's pinned channel — both for the non-interactive auto-pick and the interactive prompt default.MatchesConfiguredChannelranking helper so the pinned channel outranks implicit, and reorder the interactive root menu so the pinned channel is the default choice. C# apphosts (configuredChannel == null) keep prior implicit-first behavior, so existing tests stay green.AddCommandPrompter_ShowsConfiguredChannelAsFirstChoiceWhenChannelPinned(interactive ordering) andAddCommandNonInteractiveTypeScriptAppHostPinnedToDailyPrefersDailyChannelOverImplicitStable(end-to-end non-interactive). ExtendedTestTypeScriptStarterProjectwith anAddPackageAsyncCallback.CLI identity sidecar — back out compile-time stamping of channel/quality (staging/stable) into the CLI executable that landed in 13.4. Instead, the CLI reads its identity (version, quality, commit SHA, channel) from environment variables / a sidecar config file. This makes it possible to
dotnet run --project src/Aspire.Cli -- runfrom any directory and have the CLI assume the personality of a locally-installed CLI by setting a handful of env vars (ASPIRE_CLI_VERSION,ASPIRE_CLI_QUALITY,ASPIRE_CLI_COMMIT,ASPIRE_CLI_CHANNEL).Key implementation details:
IdentityResolverwith a 3-layer env → sidecar field → assembly-baked fallback chain per resolved field (channel, version, commit, NuGet service-index override).ASPIRE_CLI_*identity env vars are stripped before every child-process spawn (ProcessExecutionFactory,PeerInstallProbe) so overrides stay parent-process-local and do not corruptaspire doctorpeer probing.NuGetServiceIndexOverrideplumbed throughPackagingServiceandTemplateNuGetConfigServiceas a write-side-only rewrite — URLs the CLI reads from existing user configs are never touched.AspireCliIdentityEnvVarsconstants live insrc/Shared/and are link-included into the CLI to avoid a project reference dependency.ASPIRE_CLI_PACKAGESpackages-directory override: a new identity override (env var +.aspire-install.jsonpackagesfield) points the CLI'sAspire*feed at a flat directory of.nupkgfiles. It resolves toCliExecutionContext.IdentityPackagesDirectory, andPackagingServicesynthesizes a channel (named after the identity channel) that routesAspire*to the directory, replacing any same-named built-in/discovered/PR channel. A fail-fast guardrail rejects a missing directory or duplicateAspire*versions so a flat feed can't silently mask the intended version. This enables the "PR/released identity + locally built packages" repro scenarios.cli-channel-debuggingskill: a new.agents/skills/cli-channel-debugging/SKILL.mddocuments the full CLI-identity emulation matrix (local / PR / daily / staging / stable scenarios) and exactly which override knobs to set for each, so a reported channel/version-specific bug can be reproduced and fixed from a locally built CLI without an install or PR-build loop.Identity version/commit consumption migration (completes the sidecar): an audit found
IdentityVersion/IdentityCommitwere resolved but never consumed — every version/commit decision still read the assembly, soASPIRE_CLI_VERSION/ASPIRE_CLI_COMMITwere inert. This PR wires them through, perdocs/specs/cli-identity-sidecar.md:CliExecutionContextgainsIdentitySdkVersion(IdentityVersionwith+buildmetadata stripped) andIdentityOverridden(true when any field came from env/sidecar).CliExecutionContext:PackagingServicestaging feed/version,PackageChanneltemplate filter,New/Add/Updatecommands,TemplateNuGetConfigService,InitCommand/ScaffoldingServiceversion stamping, theGuestAppHostProjectCLI/SDK skew warning,ExtensionRpcTarget, and SDK/skills generation. Genuine physical-binary reads stay on the assembly and are annotated// physical-binary-version-by-design.cli.version/cli.build_idkept;identity.version/identity.channel(andidentity.commitwhen present) emitted alongside, so override-driven runs don't poison the telemetry corpus.aspire --versionnow prints the identity version (honorsASPIRE_CLI_VERSION/sidecar); non-override output is byte-identical to the previous default.IdentityCallSiteGuardrailTestsgrepssrc/Aspire.Cli/for physical version reads against a tight file-level allow-list (plus a stale-entry check), forcing the migration to stay exhaustive.--versionoverride,IdentityOverriddenflag computation (Program.BuildCliExecutionContext), and telemetry identity tags.aspire doctoris physical by design: doctor reports the real install truth (like--self); emulation is surfaced via the startup notice rather than rewiring doctor's assembly-backed channel read (whichDoctorCommandTestspin). Flagging for reviewer awareness.Build script fix — changed
-warnAsError 0to-warnAsError falsein botheng/build.shandeng/build.ps1to resolve MSB4030 errors when Arcade forwards the value to MSBuild'sTreatWarningsAsErrorsproperty.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com
Checklist
<remarks />and<code />elements on your triple slash comments?