Skip to content
Merged
Show file tree
Hide file tree
Changes from 68 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
b7a1197
Migrate integration restore to in-process NuGet
danegsta Sep 23, 2026
ba9dc92
Harden integration restore failure handling
danegsta Sep 23, 2026
cfb1a91
Fix integration restore tests in CI
danegsta Sep 23, 2026
5549de9
Use pipeline NuGet override in restore test
danegsta Sep 23, 2026
8c9a91c
Redact generated restore failure diagnostics
danegsta Sep 23, 2026
b344d20
Use integration source hints in restore graph test
danegsta Sep 23, 2026
61f85a9
Redact NuGet diagnostic logger output
danegsta Sep 23, 2026
0c70bbd
Propagate GitHub tokens to specialized tests
danegsta Sep 23, 2026
e8f00cf
Redact derived NuGet resource credentials
danegsta Sep 24, 2026
3c9b4ca
Isolate mixed restore source mappings
danegsta Sep 24, 2026
6b7c877
Refine polyglot restore source policy
danegsta Sep 25, 2026
aeeb014
Supply ambient restore sources in integration restore tests
danegsta Sep 28, 2026
8549734
Clarify NuGet settings in restore tests
danegsta Sep 29, 2026
14f19bb
Prevent stale restore graph redaction
danegsta Sep 29, 2026
5affd66
Support NuGet fallback package folders
danegsta Sep 29, 2026
bcb93ea
Respect effective NuGet package folder in test
danegsta Sep 30, 2026
776b845
Document polyglot restore trade-offs
danegsta Sep 30, 2026
1c3e083
Move NuGet helpers into CLI
danegsta Sep 30, 2026
2970fbe
Clean up in-process NuGet configuration
danegsta Sep 30, 2026
bdd70fd
Keep skills cache helper scoped to skills
danegsta Sep 30, 2026
d13ba4e
Remove redundant AppHost directory creation
danegsta Sep 30, 2026
e276dd9
Normalize add command source once
danegsta Sep 30, 2026
10378fe
Remove NuGet RPC model leftovers
danegsta Sep 30, 2026
035cb98
Prevent runsheet template injection
danegsta Sep 30, 2026
9369b25
Reuse AppHost file discovery helper
danegsta Sep 30, 2026
604f004
Trust AppHost parent directory invariant
danegsta Sep 30, 2026
af3a330
Remove partial fallback package support
danegsta Sep 30, 2026
675b170
Redact ambient NuGet search sources
danegsta Sep 30, 2026
fdb33af
Clarify persisted channel selection state
danegsta Sep 30, 2026
e7af8ba
Inline persisted channel transition cases
danegsta Sep 30, 2026
df71a27
Explain persisted channel transition
danegsta Sep 30, 2026
3774bc3
Combine update regeneration and config save
danegsta Oct 1, 2026
f8d0834
Scope exact package pins to source overrides
danegsta Oct 1, 2026
48ead51
Include selected sources in staging cache identity
danegsta Oct 1, 2026
c2063af
Preserve selected AppHost during SDK generation
danegsta Oct 1, 2026
00ea8e5
Fix add command test fixtures
danegsta Oct 1, 2026
87e7ec8
Remove inactive Azure pipeline override
danegsta Oct 1, 2026
58b2e43
Narrow integration downgrade diagnostics
danegsta Oct 1, 2026
e614f7b
Redact credentials from CLI invocation logs
danegsta Oct 2, 2026
b5a9ca3
Preserve identity sources for scoped restores
danegsta Oct 2, 2026
4a7cabc
Run scoped source E2E in PR CI
danegsta Oct 2, 2026
51ab056
Expand PR restore E2E coverage
danegsta Oct 2, 2026
b22cdbf
Fix PR restore E2E assumptions
danegsta Oct 2, 2026
2fb7d28
Use ambient NuGet overlays for channel operations
danegsta Oct 2, 2026
2d74573
Infer NuGet search mode from source overrides
danegsta Oct 2, 2026
dfd5a2d
Cover template NuGet overlay behavior
danegsta Oct 2, 2026
073aeb1
Fix template overlay E2E expectations
danegsta Oct 2, 2026
21223cb
Unify NuGet configuration and discovery policy
danegsta Oct 5, 2026
4201671
Normalize NuGet mapping identities and clarify integration ordering
danegsta Oct 5, 2026
4d94447
Validate C# update candidates before persisting restore policy
danegsta Oct 7, 2026
b1f2751
Fix restore preflight and isolate NuGet regression tests
danegsta Oct 7, 2026
7c2ccbc
Prioritize official integrations across CLI listings and search
danegsta Oct 7, 2026
c5cd650
Merge main and reconcile CLI resources and workflow routing
danegsta Oct 7, 2026
605f7f9
Preserve ambient feeds during C# stable updates
danegsta Oct 8, 2026
d09c637
Honor empty NuGet package search source policy
danegsta Oct 8, 2026
676a975
Allow additional project sources during channel updates
danegsta Oct 8, 2026
4208bae
Set read-only permissions for specialized test jobs
danegsta Oct 8, 2026
adbcbbb
Narrow CLI-managed NuGet source detection
danegsta Oct 8, 2026
448bcd2
Fix .NET AppHost channel update failures
danegsta Oct 8, 2026
640ca6d
Fix native SDK repair test cache assertion
danegsta Oct 8, 2026
105ceb1
Omit redundant NuGet source masks from persisted configurations
danegsta Oct 9, 2026
ed0c091
Update persisted NuGet configuration snapshots
danegsta Oct 9, 2026
bb01089
Use AppHost hierarchy for update NuGet config defaults
danegsta Oct 9, 2026
03dff96
Synchronize AppHost SDK metadata for stable updates
danegsta Oct 9, 2026
22e70af
Align update tests with SDK metadata synchronization
danegsta Oct 9, 2026
bc6cf16
Merge main into shared integration closure
danegsta Oct 9, 2026
74b0136
Merge latest main into shared integration closure
danegsta Oct 13, 2026
24e280b
Merge main while preserving specialized test artifact access
danegsta Oct 14, 2026
0ef87af
Fix file-based updates and propagate guest update failures
danegsta Oct 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ on:
default: false

permissions:
actions: read
contents: read

jobs:
Expand Down Expand Up @@ -324,6 +325,14 @@ jobs:
echo "TEST_PROJECT_PATH=$projectPath" >> $env:GITHUB_ENV
echo "TEST_ASSEMBLY_NAME=$filenameWithoutExtension" >> $env:GITHUB_ENV

- name: Configure CLI test NuGet service index
shell: pwsh
run: |
if ($env:TEST_ASSEMBLY_NAME -eq 'Aspire.Cli.Tests') {
"ASPIRE_CLI_NUGET_SERVICE_INDEX=https://packagefeedproxy.microsoft.io/nuget/v3/index.json" |
Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
}

- name: Build test project
if: ${{ fromJson(inputs.properties).requiresNugets != true }}
run: >
Expand Down
40 changes: 28 additions & 12 deletions .github/workflows/specialized-test-runner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,33 +80,48 @@ jobs:

- name: Inject enablePlaywrightInstall into runsheet properties
id: inject_properties
env:
RUNSHEET: ${{ steps.generate_tests_matrix_raw.outputs.runsheet }}
ENABLE_PLAYWRIGHT_INSTALL: ${{ inputs.enablePlaywrightInstall }}
run: |
RUNSHEET='${{ steps.generate_tests_matrix_raw.outputs.runsheet }}'
if [ "$RUNSHEET" = "[]" ] || [ -z "$RUNSHEET" ]; then
echo "runsheet=[]" >> $GITHUB_OUTPUT
echo "runsheet=[]" >> "$GITHUB_OUTPUT"
else
UPDATED=$(echo "$RUNSHEET" | jq -c --argjson epw ${{ inputs.enablePlaywrightInstall }} '[.[] | .properties.enablePlaywrightInstall = $epw]')
echo "runsheet=$UPDATED" >> $GITHUB_OUTPUT
UPDATED=$(printf '%s\n' "$RUNSHEET" | jq -c --argjson epw "$ENABLE_PLAYWRIGHT_INSTALL" '[.[] | .properties.enablePlaywrightInstall = $epw]')
echo "runsheet=$UPDATED" >> "$GITHUB_OUTPUT"
fi

- name: Check if any test requires NuGets
id: check_nugets
env:
RUNSHEET: ${{ steps.inject_properties.outputs.runsheet }}
run: |
RUNSHEET='${{ steps.inject_properties.outputs.runsheet }}'
if echo "$RUNSHEET" | jq -e 'any(.[]; .properties.requiresNugets == true)' > /dev/null 2>&1; then
echo "requiresNugets=true" >> $GITHUB_OUTPUT
if printf '%s\n' "$RUNSHEET" | jq -e 'any(.[]; .properties.requiresNugets == true)' > /dev/null 2>&1; then
echo "requiresNugets=true" >> "$GITHUB_OUTPUT"
else
echo "requiresNugets=false" >> $GITHUB_OUTPUT
echo "requiresNugets=false" >> "$GITHUB_OUTPUT"
fi

- name: Check if any test requires CLI archives
id: check_cli_archive
env:
RUNSHEET: ${{ steps.inject_properties.outputs.runsheet }}
run: |
if printf '%s\n' "$RUNSHEET" | jq -e 'any(.[]; .properties.requiresCliArchive == true)' > /dev/null 2>&1; then
echo "requiresCliArchive=true" >> "$GITHUB_OUTPUT"
else
echo "requiresCliArchive=false" >> "$GITHUB_OUTPUT"
fi

- name: Check if any test requires GitHub token
id: check_github_token
env:
RUNSHEET: ${{ steps.inject_properties.outputs.runsheet }}
run: |
RUNSHEET='${{ steps.inject_properties.outputs.runsheet }}'
if echo "$RUNSHEET" | jq -e 'any(.[]; .properties.requiresCliArchive == true)' > /dev/null 2>&1; then
echo "requiresCliArchive=true" >> $GITHUB_OUTPUT
if printf '%s\n' "$RUNSHEET" | jq -e 'any(.[]; .properties.requiresGitHubToken == true)' > /dev/null 2>&1; then
echo "requiresGitHubToken=true" >> "$GITHUB_OUTPUT"
else
echo "requiresCliArchive=false" >> $GITHUB_OUTPUT
echo "requiresGitHubToken=false" >> "$GITHUB_OUTPUT"
fi

- name: Upload logs, and test results
Expand Down Expand Up @@ -160,6 +175,7 @@ jobs:
}}
uses: ./.github/workflows/run-tests.yml
permissions:
actions: read
contents: read
with:
testShortName: ${{ matrix.tests.project }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/tests-outerloop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ jobs:
if: ${{ github.repository == 'microsoft/aspire' }}
uses: ./.github/workflows/specialized-test-runner.yml
permissions:
actions: read
contents: read
with:
testRunnerName: "OuterloopTestRunsheetBuilder"
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/tests-quarantine.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ jobs:
if: ${{ github.repository == 'microsoft/aspire' }}
uses: ./.github/workflows/specialized-test-runner.yml
permissions:
actions: read
contents: read
with:
testRunnerName: "QuarantinedTestRunsheetBuilder"
Expand Down
6 changes: 6 additions & 0 deletions docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@ To test changes from a specific pull request locally, see [dogfooding-pull-reque

To validate how the CLI resolves `Aspire.*` packages for **staging** and **stable** release-branch builds (including making an installed PR build behave like a staging build), see [cli-staging-validation.md](/docs/cli-staging-validation.md).

### NuGet configuration in CLI unit tests

Use `NuGetTestHelper.CreateClient()` or `CreateService()` for tests that compose real NuGet configuration, and `NuGetTestHelper.LoadSettings()` for native settings assertions. These helpers preserve directory-level configuration inheritance while replacing the runner's user-wide settings with a controlled baseline and excluding machine-wide settings by default. Supply `TestMachineWideSettings` explicitly when testing machine-wide policy.

`TemporaryWorkspace.CreateForCli()` isolates Aspire settings, not NuGet settings. Do not change process-wide environment variables to isolate NuGet configuration in parallel tests.

## Coding Agents

Aspire uses GitHub Copilot automatic code review on pull requests. We expect Copilot review comments to be reviewed and addressed before merging, either by making the requested change or by explaining why a suggested change is not needed.
Expand Down
4 changes: 3 additions & 1 deletion docs/specs/cli-identity-sidecar.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,16 @@ The CLI exposes the override on demand via a small API on `CliExecutionContext`:
public string? NuGetServiceIndexOverride { get; }
```

Call sites that today emit `api.nuget.org` directly (`NuGetConfigMerger`, `TemplateNuGetConfigService`, etc.) consult this property; when it returns non-null, the emitted config carries the override URL. When null, the existing canonical URL is used. This is the bytes-side half of the identity story tracked separately in #17823; the API and the sidecar/env plumbing land **now** so that #17823's proxy work has a stable consumer surface to slot into.
Source-override policy composition consults this property when it needs a fallback source: a non-null value selects the override URL, while null selects the canonical URL. Shared configuration writing materializes the composed policy for temporary overlays and persisted template configuration. Existing ambient source definitions are not rewritten.

#### `packages`

The `packages` field points the CLI at a **flat directory of `.nupkg` files** (for example `artifacts/packages/Release/Shipping`). When set, the CLI synthesizes a package channel — named after the resolved identity channel — that maps `Aspire*` package globs to this directory (everything else resolves from nuget.org). This synthesized channel **replaces** any same-named built-in, discovered-hive, or PR-install channel, so the CLI resolves Aspire packages from the directory while still presenting the emulated channel/version/commit identity. It is exposed on `CliExecutionContext` as `IdentityPackagesDirectory` (a `DirectoryInfo?`, null when no override is active) — distinct from the existing `PackagesDirectory`, which is the restore cache.

This lets a locally built CLI test against packages it just built (or against a PR's CI-built packages staged into a directory) without copying them into a hive. It pairs with `ASPIRE_CLI_CHANNEL` for the "PR build identity + locally built packages" and "released repro with a CLI+hosting fix spanning packages" scenarios (see the `cli-channel-debugging` skill for the full matrix).

For an existing C# AppHost, an unqualified `aspire update` uses current native NuGet configuration and does not enumerate identity or hive feeds. Applying a synthesized package channel requires an explicit `--channel` or legacy `--quality` option; feeds are persisted only when the desired policy differs. See [shared policy previews and .NET persistence](polyglot-integration-restore.md#shared-policy-previews-and-net-persistence).

**Cleanliness guardrail (fail-fast).** A flat directory has no "latest stable vs latest prerelease" semantics: if the same `Aspire*` package id appears with more than one version, NuGet would silently resolve the highest and mask the intended version. The CLI therefore **fails fast** at channel-resolution time when the directory is missing, or when any `Aspire*` package id carries more than one distinct version anywhere under the directory (the check recurses, matching how local package discovery walks subdirectories), with a diagnostic listing the conflicting ids and versions (e.g. `Aspire.Hosting (13.4.1, 13.4.2)`). Only `Aspire*` packages are checked because the synthesized channel only routes those to the directory. This is intentional friction — `localhive.sh` accumulates auto-versioned builds in `Shipping`, so the directory must be cleaned (or freshly packed) before it can be used as an override.

### Resolution order
Expand Down
Loading
Loading