Skip to content
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
0f51445
[release/13.4] Stabilizing builds in preparation for 13.4 release (#1…
joperezr May 27, 2026
1cdf6f9
[release/13.4] Fix remaining issue 17244 items (#17522)
aspire-repo-bot[bot] May 27, 2026
c69a57f
Fix Aspire skills attestation build type (#17525)
aspire-repo-bot[bot] May 27, 2026
d7e6a8d
[release/13.4] Migrate playground/SqlServerEndToEnd to EF Hosting int…
aspire-repo-bot[bot] May 27, 2026
a3766e9
Fix Y/n input race in ChannelUpdateWorkflowTests + add troubleshootin…
aspire-repo-bot[bot] May 27, 2026
4f77825
[release/13.4] Address remaining Blazor integration PR feedback (#17540)
aspire-repo-bot[bot] May 27, 2026
45a30c8
[release/13.4] Friendly error for 'aspire do --list-steps' without a …
aspire-repo-bot[bot] May 27, 2026
fd9cee2
[release/13.4] Validate Helm CLI version (>= 4.2.0) before Kubernetes…
aspire-repo-bot[bot] May 27, 2026
f18d66b
[release/13.4] Add embedded Aspire skills fallback (#17548)
IEvangelist May 27, 2026
6a82355
Fix 13.4 staging CLI dropping nuget.config without Aspire package sou…
mitchdenny May 27, 2026
f89b2b7
[create-pull-request] automated change (#17555)
aspire-repo-bot[bot] May 27, 2026
e59b9ba
[release/13.4] Enrich AppHost codegen TypeLoadException diagnostics (…
aspire-repo-bot[bot] May 27, 2026
3f0998a
Prefer current CLI template version for aspire new (#17564)
sebastienros May 27, 2026
5d0da8b
[release/13.4] Stabilize PrebuiltAppHostServer staging globalPackages…
danegsta May 28, 2026
e18fdb8
[release/13.4] Use KnownConfigNames for resource service endpoint URL…
aspire-repo-bot[bot] May 28, 2026
03caf53
Fix VS Code AppHost launch path resolution (#17408) (#17560)
adamint May 28, 2026
ce6bac8
[release/13.4] Update --search option description with aka.ms link an…
aspire-repo-bot[bot] May 28, 2026
b8f2999
Remove ATS experimental markers (#17602)
aspire-repo-bot[bot] May 28, 2026
847c2e6
[release/13.4] Remove resource data from `aspire ps`; use `aspire des…
adamint May 28, 2026
a0823e6
Remove Corepack from extension build scripts (#17628)
joperezr May 28, 2026
1e7134f
[release/13.4] Fix `aspire stop` falsely reporting failure on Unix (#…
aspire-repo-bot[bot] May 28, 2026
26b661b
Forward IdentityChannel to TemplateInputs.Channel for dotnet-runtime …
mitchdenny May 29, 2026
d8c4e96
Remove 'aspire new aspire-starter' step from CLI archive verifier (#1…
mitchdenny May 29, 2026
34be339
[release/13.4] Accept dev localhost resource service URLs (#17644)
aspire-repo-bot[bot] May 29, 2026
56e6967
Restore dotnet watch dashboard auto-launch signal (#17653)
JamesNK May 29, 2026
cc4b7ea
Backport CLI cancellation fixes to release/13.4 (#17641)
JamesNK May 29, 2026
cbc3523
[release/13.4] Improve dashboard summary log formatting (#17640)
aspire-repo-bot[bot] May 29, 2026
a364a66
Backport PR #17553 to release/13.4 (#17673)
IEvangelist May 29, 2026
0cbaf82
[release/13.4] Update Foundry hosted agent builder APIs (#17669)
davidfowl May 29, 2026
e258349
Handle process inspection race during shutdown (#17676)
danegsta May 29, 2026
6c9c5e8
Fix TypeScript AppHost async callback deadlock (#17689)
aspire-repo-bot[bot] May 29, 2026
dfd226a
[release/13.4] Mark Aspire.Hosting.Blazor as preview (#17694)
joperezr May 29, 2026
565af53
Use TerminalRun in TypeScript deadlock repro E2E test (#17703)
aspire-repo-bot[bot] May 29, 2026
64efcef
[release/13.4] Reference Foundry project from hosted agent target (#1…
aspire-repo-bot[bot] May 30, 2026
11bea2e
[release/13.4] Fix five `aspire ls` bugs from #17620 (L1–L5) (#17688)
aspire-repo-bot[bot] May 30, 2026
56e8866
fix(cli): restore implicit-channel discovery + guard non-interactive …
mitchdenny May 30, 2026
da473d2
API review fixes for 13.4 (PR #17700) (#17706)
joperezr May 30, 2026
2574ef5
Fix DistributedApplicationTestingBuilder failing when dashboard enabl…
aspire-repo-bot[bot] May 30, 2026
f2e540e
Assert allocated dashboard service URI has expected host and non-zero…
JamesNK May 31, 2026
9c260c2
Add Foundry hosted agent protocol selection (#17732)
davidfowl May 31, 2026
e138509
Route staging-identity CLI to its darc feed regardless of version sha…
mitchdenny May 31, 2026
becb48e
Resolve cross-compute-environment endpoint references for Foundry hos…
davidfowl Jun 1, 2026
3cbbeb2
[create-pull-request] automated change (#17700)
aspire-repo-bot[bot] Jun 1, 2026
87569e8
Merge release/13.4 into main after v13.4.0 release
joperezr Jun 2, 2026
dd519dc
Revert stale api/ baselines pulled in from release/13.4
joperezr Jun 2, 2026
fcc00e1
Revert "Revert stale api/ baselines pulled in from release/13.4"
joperezr Jun 2, 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
28 changes: 14 additions & 14 deletions .github/workflows/backmerge-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ jobs:
- name: Check for changes to backmerge
id: check
run: |
git fetch origin main release/13.3
BEHIND_COUNT=$(git rev-list --count origin/main..origin/release/13.3)
git fetch origin main release/13.4
BEHIND_COUNT=$(git rev-list --count origin/main..origin/release/13.4)
echo "behind_count=$BEHIND_COUNT" >> $GITHUB_OUTPUT
if [ "$BEHIND_COUNT" -gt 0 ]; then
echo "changes=true" >> $GITHUB_OUTPUT
echo "Found $BEHIND_COUNT commits in release/13.3 not in main"
echo "Found $BEHIND_COUNT commits in release/13.4 not in main"
else
echo "changes=false" >> $GITHUB_OUTPUT
echo "No changes to backmerge - release/13.3 is up-to-date with main"
echo "No changes to backmerge - release/13.4 is up-to-date with main"
fi

- name: Attempt merge and create branch
Expand All @@ -52,12 +52,12 @@ jobs:
git config user.email "github-actions[bot]@users.noreply.github.com"

git checkout origin/main
git checkout -b backmerge/release-13.3-to-main
git checkout -b backmerge/release-13.4-to-main

# Attempt the merge
if git merge origin/release/13.3 --no-edit; then
if git merge origin/release/13.4 --no-edit; then
echo "merge_success=true" >> $GITHUB_OUTPUT
git push origin backmerge/release-13.3-to-main --force
git push origin backmerge/release-13.4-to-main --force
echo "Merge successful, branch pushed"
else
echo "merge_success=false" >> $GITHUB_OUTPUT
Expand All @@ -72,15 +72,15 @@ jobs:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
# Check if a PR already exists for this branch
EXISTING_PR=$(gh pr list --head backmerge/release-13.3-to-main --base main --json number --jq '.[0].number // empty')
EXISTING_PR=$(gh pr list --head backmerge/release-13.4-to-main --base main --json number --jq '.[0].number // empty')

if [ -n "$EXISTING_PR" ]; then
echo "PR #$EXISTING_PR already exists, updating it"
echo "pull_request_number=$EXISTING_PR" >> $GITHUB_OUTPUT
else
PR_BODY="## Automated Backmerge

This PR merges changes from \`release/13.3\` back into \`main\`.
This PR merges changes from \`release/13.4\` back into \`main\`.

**Commits to merge:** ${{ steps.check.outputs.behind_count }}

Expand All @@ -94,9 +94,9 @@ jobs:
PR_BODY=$(echo "$PR_BODY" | sed 's/^ //')

PR_URL=$(gh pr create \
--head backmerge/release-13.3-to-main \
--head backmerge/release-13.4-to-main \
--base main \
--title "[Automated] Backmerge release/13.3 to main" \
--title "[Automated] Backmerge release/13.4 to main" \
--body "$PR_BODY" \
--assignee joperezr,radical \
--label area-engineering-systems)
Expand Down Expand Up @@ -142,15 +142,15 @@ jobs:
const issueBody = [
'## Backmerge Conflict',
'',
'The automated backmerge from `release/13.3` to `main` failed due to merge conflicts.',
'The automated backmerge from `release/13.4` to `main` failed due to merge conflicts.',
'',
'### What to do',
'',
'1. Checkout main and attempt the merge locally:',
' ```bash',
' git checkout main',
' git pull origin main',
' git merge origin/release/13.3',
' git merge origin/release/13.4',
' ```',
'2. Resolve the conflicts',
'3. Push the merge commit or create a PR manually',
Expand All @@ -167,7 +167,7 @@ jobs:
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: '[Backmerge] Merge conflicts between release/13.3 and main',
title: '[Backmerge] Merge conflicts between release/13.4 and main',
body: issueBody,
assignees: ['joperezr', 'radical'],
labels: ['area-engineering-systems', 'backmerge-conflict']
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/generate-api-diffs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: release/13.4
ref: main

- name: Restore and build
run: |
Expand All @@ -40,7 +40,7 @@ jobs:
with:
token: ${{ steps.app-token.outputs.token }}
branch: update-api-diffs
base: release/13.4
base: main
labels: |
NO-MERGE
title: "[Automated] Update API Surface Area"
Expand Down
148 changes: 148 additions & 0 deletions docs/cli-staging-validation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# Validating staging feed routing with a local CLI build

This document describes how to make a locally built Aspire CLI resolve `Aspire.*`
packages exactly the way an official **staging** (or **stable**) build would, so the
staging feed-routing behavior can be validated end-to-end without an official build.

## Background

A staging-identity CLI is an official release-branch build whose own commit always has
a SHA-specific `darc-pub-microsoft-aspire-<commit>` feed carrying its matching packages
(prerelease-shaped `13.4.0-preview.*` and stable-shaped `13.4.0` alike). Feed
**provenance** is decided by the CLI's baked build **identity** (`AspireCliChannel`),
while version **filtering** (the channel quality) is decided by the CLI's **version
shape**. See `PackagingService.ShouldUseSharedStagingFeed`.

A locally built CLI bakes a `local` identity and an unstamped informational version, so
it never synthesizes a staging channel and never derives a darc feed. The two diagnostic
overrides below let you simulate the staging path locally.

## The two diagnostic overrides

Both are read by `PackagingService` only (their blast radius is limited to staging
feed-routing decisions — they do **not** change the global identity used for hive or
package-directory lookups):

| Config key | Purpose |
| --- | --- |
| `overrideCliIdentityChannel` | Forces the identity used for staging-feed routing decisions. Must be a valid channel (`stable`, `staging`, `daily`, `local`, or `pr-<N>`); invalid values are ignored and the real identity is used. |
| `overrideCliInformationalVersion` | Forces the informational version that both the SHA-derivation provider and the version-shape (quality) predicate read. The part after `+` (truncated to 8 chars) builds the darc URL; the version part determines stable-vs-prerelease shape. |

**Both overrides are required** to reach the darc path from a local build:

- Identity override alone → the SHA is still unstamped, so the darc URL can't be derived.
- Version override alone → the identity stays `local`, so routing never selects the darc feed.

When either override is set, the CLI emits a one-time warning so an overridden
identity/feed can't silently resolve packages on a normal invocation.

## Recipe

1. Build the CLI locally:

```bash
./build.sh --build /p:SkipNativeBuild=true
```

2. In the apphost directory, set `channel: staging` in `aspire.config.json` (this is what
`aspire add` filters the synthesized channels to):

```json
{
"channel": "staging"
}
```

3. Set the two overrides (environment variables are the simplest; they are read
case-insensitively with no prefix):

```bash
export overrideCliIdentityChannel=staging
export overrideCliInformationalVersion=13.4.0-preview.1.26280.6+<full-commit-hash>
```

Use a real release-branch build commit hash so the derived feed actually exists if you
intend to restore; any 8+ char hex suffix works for inspecting the resolved feed URL.

4. Run `aspire add` with debug logging and confirm the resolved darc feed:

```bash
aspire add foundry --debug
```

The logs should show the staging channel resolving `Aspire*` to
`.../darc-pub-microsoft-aspire-<first8-of-commit>/...` rather than the shared
`dnceng/.../dotnet9` daily feed.

To simulate a **stable**-shaped staging build, use a stable-shaped version override
(e.g. `13.4.0+<full-commit-hash>`); the channel quality becomes `Stable` while the feed
stays the darc feed.

## Helper scripts

`eng/scripts/debug-staging.{sh,ps1}` and `eng/scripts/debug-stable.{sh,ps1}` wrap the
recipe above. Both target identity `staging` and expect the **same** darc feed; they
differ only in version shape/quality:

| Script | Version shape | Expected quality | Scenario |
| --- | --- | --- | --- |
| `debug-staging` | prerelease (`13.4.0-preview.*`) | `Both` | [#17744](https://github.com/microsoft/aspire/issues/17744) — the bug this PR fixes |
| `debug-stable` | stable (`13.4.0`) | `Stable` | [#17527](https://github.com/microsoft/aspire/issues/17527) — stable-shaped release build |

Each script computes the expected `darc-pub-microsoft-aspire-<sha8>` feed and supports
three modes:

- **Validate (default):** runs `aspire add <pkg> --debug` in a throwaway directory and
asserts the darc feed appears in the resolution log. Exits non-zero if it doesn't.
- **`--print-env` / `-PrintEnv`:** emits `export`/`$env:` lines you apply to your current
shell. Every subsequent `aspire` command then behaves like the simulated build.
- **`--shell` / `-Shell`:** opens an interactive subshell with the overrides applied and
the target CLI first on `PATH`. It also points `NUGET_PACKAGES` at an isolated, per-sha
cache so restores from the simulated staging feed never contaminate your real global
package cache. Exiting the subshell restores normal behavior.

Common flags: `--sha <commit>` (required, 8–40 hex), `--cli <path>` (CLI to drive),
`--pr <N>` (install that PR's full-bundle build first, then target it), `--version <ver>`.

### Interactive validation against an installed PR build

You don't need a local source build — the easiest carrier is an installed **PR build**,
which is a real full-bundle `~/.aspire` install. Install it, then make it behave like a
staging build for a full `aspire new` / `aspire add` / run flow:

```bash
# 1. Install the PR's full-bundle build.
./eng/scripts/get-aspire-cli-pr.sh 17743

# 2a. Apply staging overrides to the CURRENT shell (every aspire command is staging-flavored):
eval "$(./eng/scripts/debug-stable.sh --sha <commit> --print-env)"
aspire new # behaves like the simulated staging build
aspire add foundry
# revert when done:
unset channel overrideCliIdentityChannel overrideCliInformationalVersion

# 2b. ...or get a throwaway subshell instead (overrides vanish on 'exit'):
./eng/scripts/debug-stable.sh --pr 17743 --sha <commit> --shell
```

PowerShell is identical with the `.ps1` siblings:

```powershell
./eng/scripts/get-aspire-cli-pr.ps1 17743
./eng/scripts/debug-stable.ps1 -Sha <commit> -PrintEnv | Invoke-Expression
# ...or:
./eng/scripts/debug-stable.ps1 -Pr 17743 -Sha <commit> -Shell
```

The overrides are scoped to `PackagingService` feed routing and only ever live in the
shell/subshell environment, so nothing is written to global or per-project config.

## Validation matrix

| Identity | Version shape | Expected feed | Expected quality |
| --- | --- | --- | --- |
| `staging` | prerelease | `darc-pub-microsoft-aspire-<sha8>` | `Both` |
| `staging` | stable | `darc-pub-microsoft-aspire-<sha8>` | `Stable` |
| `daily` | any | shared `dnceng/.../dotnet9` daily feed | `Both` |
| `local` / `pr-<N>` | any | local/PR hive + implicit (no staging synthesis) | n/a |
| `stable` | stable | nuget.org | `Stable` |
2 changes: 2 additions & 0 deletions docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ dotnet test --no-launch-profile -- \

To test changes from a specific pull request locally, see [dogfooding-pull-requests.md](/docs/dogfooding-pull-requests.md) for instructions on installing Aspire CLI and NuGet packages built by that PR's CI run.

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).

## 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
21 changes: 21 additions & 0 deletions eng/pipelines/azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,24 @@ parameters:
displayName: 'Package VS Code Extension as Pre-Release'
type: boolean
default: false
# Operator-controlled override for the AspireCliChannel baked into native CLI
# binaries by build_sign_native.yml. The default 'auto' lets the pipeline pick
# stable / staging / daily / pr-<N> from Build.Reason and Build.SourceBranch
# (where release/* and internal/release/* branches always resolve to 'staging'
# so stabilizing dogfood builds aren't mis-baked as 'stable' — see
# https://github.com/microsoft/aspire/issues/17527). Set this to 'stable' when
# kicking off the official GA ship build from a release/* branch so the
# distributed binary identifies as stable and `aspire init` writes a
# nuget.org-only nuget.config matching the promoted package set.
- name: aspireCliChannelOverride
displayName: 'Aspire CLI channel override (auto = derive from branch; set to stable for the GA ship build)'
type: string
default: 'auto'
values:
- auto
- stable
- staging
- daily

trigger:
batch: true
Expand Down Expand Up @@ -158,6 +176,7 @@ extends:
- osx-x64
codeSign: true
teamName: $(_TeamName)
aspireCliChannelOverride: ${{ parameters.aspireCliChannelOverride }}
extraBuildArgs: >-
/p:Configuration=$(_BuildConfig)
$(_SignArgs)
Expand All @@ -173,6 +192,7 @@ extends:
# no need to sign ELF binaries on linux
codeSign: false
teamName: $(_TeamName)
aspireCliChannelOverride: ${{ parameters.aspireCliChannelOverride }}
extraBuildArgs: >-
/p:Configuration=$(_BuildConfig)
$(_OfficialBuildIdArgs)
Expand All @@ -185,6 +205,7 @@ extends:
- win-arm64
codeSign: true
teamName: $(_TeamName)
aspireCliChannelOverride: ${{ parameters.aspireCliChannelOverride }}
extraBuildArgs: >-
/p:Configuration=$(_BuildConfig)
$(_SignArgs)
Expand Down
47 changes: 44 additions & 3 deletions eng/pipelines/templates/build_sign_native.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ parameters:
extraBuildArgs: ''
codeSign: false
teamName: ''
# Optional override for AspireCliChannel. Accepted values: 'auto' (default;
# let computeCliChannel below pick stable/staging/daily/pr-<N> from Build.Reason
# and Build.SourceBranch), 'stable', 'staging', 'daily'. Set this to 'stable'
# when running the official ship pipeline so the GA CLI binary is baked with
# AspireCliChannel=stable (and aspire init then writes a nuget.org-only
# nuget.config, which is correct because the packages have been promoted to
# nuget.org). For routine stabilizing builds from a release/* branch — which
# also set DotNetFinalVersionKind=release — leave this on 'auto' so the
# channel falls through to 'staging' and aspire init writes a nuget.config
# that maps Aspire.* to the staging feed. See
# https://github.com/microsoft/aspire/issues/17527 for the bug that made this
# override necessary.
aspireCliChannelOverride: 'auto'

jobs:

Expand Down Expand Up @@ -84,15 +97,32 @@ jobs:
$reason = '$(Build.Reason)'
$sourceBranch = '$(Build.SourceBranch)'
$prNumber = '$(System.PullRequest.PullRequestNumber)'
# Template-time substitution: the value is the resolved
# aspireCliChannelOverride parameter literal, never a runtime
# variable. Quoting protects an empty/default value.
$override = '${{ parameters.aspireCliChannelOverride }}'
Write-Host "Build.Reason: '$reason'"
Write-Host "Build.SourceBranch: '$sourceBranch'"
Write-Host "System.PullRequest.PullRequestNumber: '$prNumber'"
Write-Host "aspireCliChannelOverride: '$override'"

$versionKind = & "$(Build.SourcesDirectory)/$(dotnetScript)" msbuild "$(Build.SourcesDirectory)/eng/Versions.props" -getProperty:DotNetFinalVersionKind
$versionKind = $versionKind.Trim()
Write-Host "DotNetFinalVersionKind: '$versionKind'"

if ($reason -eq 'PullRequest') {
if ($override -and $override -ne 'auto') {
# Operator override path. Validate against the same accepted set
# that IdentityChannelReader.IsValidChannel enforces at CLI startup
# so a typo here fails the pipeline step rather than producing a
# binary that refuses to boot. pr-<N> is intentionally excluded
# from the override set — PR builds always come from the
# PullRequest reason arm below.
if ($override -notin @('stable', 'staging', 'daily')) {
throw "aspireCliChannelOverride='$override' is not one of: auto, stable, staging, daily."
}
$channel = $override.ToLowerInvariant()
}
elseif ($reason -eq 'PullRequest') {
# Defense in depth: validate digit-only PR number rather than just
# non-emptiness. If the agent ever returns the literal macro string
# (e.g. '$(System.PullRequest.PullRequestNumber)' unresolved) this
Expand All @@ -105,10 +135,21 @@ jobs:
# Bake the resolved hive label directly into AspireCliChannel. The CLI
# consumes this verbatim and avoids the legacy "pr" + parsed-PrNumber join.
$channel = "pr-$prNumber"
} elseif ($versionKind -eq 'release') {
$channel = 'stable'
} elseif ($sourceBranch -match '^refs/heads/(release|internal/release)/') {
# Release/internal-release branches always produce staging artifacts —
# they are published to the staging feed for dogfooding and only later
# promoted to nuget.org. This must be checked BEFORE the
# `versionKind == release` arm, because a release-branch build also sets
# StabilizePackageVersion=true (→ DotNetFinalVersionKind=release) once
# we are stabilizing for ship. Without this ordering, the stabilized
# staging build would bake AspireCliChannel=stable and `aspire init`
# would drop a nuget.config with no staging feed mapping, causing
# `aspire add` to resolve Aspire.* packages from nuget.org (older
# versions) or fail to resolve the +sha-pinned Aspire.AppHost.Sdk.
# See https://github.com/microsoft/aspire/issues/17527.
$channel = 'staging'
} elseif ($versionKind -eq 'release') {
$channel = 'stable'
} else {
# main and any other branch fall through to daily
$channel = 'daily'
Expand Down
Loading
Loading