Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
da17453
[release/13.2] Stabilizing builds in preparation for 13.2 release (#1…
joperezr Mar 18, 2026
777972e
Remove redundant Path.GetDirectoryName call and rename _appPath/_AppP…
Copilot Mar 18, 2026
0b31e3d
Fix Foundry run-mode project restart (#15342)
sebastienros Mar 18, 2026
8e4a4b4
Fix Aspire.Hosting analyzer hookup (#15338)
sebastienros Mar 18, 2026
c25c645
Consolidate WithEnvironment polyglot exports using AspireUnionAttribu…
maddymontaquila Mar 18, 2026
860cf9c
Add TypeScript SDK refresh workflow (#15299)
sebastienros Mar 18, 2026
5da0723
[release/13.2] Fix Homebrew and WinGet publishing for stable artifact…
radical Mar 19, 2026
9e2ac82
Improve CLI error messages for dependency and project creation failur…
JamesNK Mar 19, 2026
dee5e32
Add right-click context menu on resource endpoint URLs (#15347)
mitchdenny Mar 19, 2026
ec9a2ea
Add API Review Agent Skill (#15359)
eerhardt Mar 19, 2026
12532e0
[release/13.2] Fix legacy settings migration path adjustment for appH…
mitchdenny Mar 19, 2026
930e921
Secure AppHost RPC with session token handshake (#15344)
sebastienros Mar 19, 2026
1465179
Clean up Aspire panel: context menus, loading state, welcome messages…
adamint Mar 19, 2026
b70a3e0
Stablize Aspire.Hosting.Azure Network and Sql packages (#15394)
eerhardt Mar 19, 2026
ae15739
Update Foundry models (#15389)
sebastienros Mar 19, 2026
e26dbb5
[release/13.2] Handle version selector in CPM CLI E2E test (#15395)
github-actions[bot] Mar 19, 2026
22edbe1
[release/13.2] API Review Feedback (#15370)
eerhardt Mar 19, 2026
1fab1b1
Use home-based CLI cache and randomized socket paths (#15346)
sebastienros Mar 19, 2026
2a24bf9
[release/13.2] Fix dashboard GitHub Copilot integration (#15381)
JamesNK Mar 19, 2026
6e58669
Mark code generation packages experimental (#15371)
sebastienros Mar 19, 2026
a79dde4
Include restored packages in `aspire cache clear` (#15387)
davidfowl Mar 19, 2026
96d4ccd
Bump Microsoft.Bcl.Memory to 10.0.5 to fix CVE-2026-26127 (#15411)
github-actions[bot] Mar 19, 2026
76b5976
Add AppHost CodeLens and gutter decoration support (#15397)
adamint Mar 19, 2026
896182e
[release/13.2] Stabilize Aspire.Hosting.Docker package (#15377)
mitchdenny Mar 19, 2026
e44e59b
Fix dashboard image in marketplace README and bump version to 1.0.6 (…
adamint Mar 20, 2026
652e35f
Fix default NuGet packages restore folder (#15417)
github-actions[bot] Mar 20, 2026
84563b3
[release/13.2] Store temporary AppHost service under user .aspire dir…
sebastienros Mar 20, 2026
aad1601
Fix crash when reading boolean config values from aspire.config.json …
mitchdenny Mar 20, 2026
e8bb007
Fix config discovery to search from apphost directory and add aspire.…
mitchdenny Mar 20, 2026
1b339b0
Fix Windows CLI backchannel socket path (#15444)
eerhardt Mar 20, 2026
30d3d97
Update CI workflow owner guards and repo references from dotnet to mi…
sebastienros Mar 21, 2026
f9a8c63
Quarantine Docker and Kubernetes E2E tests on release/13.2 (#15512)
mitchdenny Mar 24, 2026
9134219
[release/13.2] Pin Kusto emulator image and backport Cosmos fix (#15504)
sebastienros Mar 24, 2026
681ff0b
Merge branch release/13.2 into main
joperezr Mar 24, 2026
2d55ec6
Make VerifyAspireCliVersionAsync resilient across branches
Mar 25, 2026
0ea8f3a
Resolve merge conflict in NewCommand.cs (keep main's PromptAndChainAs…
Mar 25, 2026
9ad6fef
Fix NewCommandTests for main's PromptAndChainAsync error handling
Mar 25, 2026
d91a347
Fix NewCommandWithTypeScriptStarter test for main's error handling
Mar 25, 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
504 changes: 504 additions & 0 deletions .github/skills/api-review/SKILL.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,45 @@ echo ""

FAILED=()
PASSED=()
SKIPPED=()

# Packages that only produce prerelease versions (SuppressFinalPackageVersion=true) cannot be
# restored when the build is stabilized, because aspire restore requests stable versions.
# Skip these until the build infrastructure dynamically computes versions. See #15335.
SKIP_PREVIEW_ONLY=(
"Aspire.Hosting.Azure.Kusto"
"Aspire.Hosting.Azure.Network"
"Aspire.Hosting.Azure.Sql"
"Aspire.Hosting.Docker"
"Aspire.Hosting.Foundry"
"Aspire.Hosting.Keycloak"
"Aspire.Hosting.Kubernetes"
"Aspire.Hosting.Maui"
)

for app_dir in "${APP_DIRS[@]}"; do
app_name="$(basename "$(dirname "$app_dir")")/$(basename "$app_dir")"
integration_name="$(basename "$(dirname "$app_dir")")"

# Check if this integration is in the skip list
skip=false
for skip_pkg in "${SKIP_PREVIEW_ONLY[@]}"; do
if [ "$integration_name" = "$skip_pkg" ]; then
skip=true
break
fi
done

if [ "$skip" = true ]; then
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Testing: $app_name"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " ⏭ Skipping (preview-only package, see #15335)"
SKIPPED+=("$app_name")
echo ""
continue
fi

echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Testing: $app_name"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
Expand Down Expand Up @@ -97,7 +133,7 @@ done

echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Results: ${#PASSED[@]} passed, ${#FAILED[@]} failed out of ${#APP_DIRS[@]} apps"
echo "Results: ${#PASSED[@]} passed, ${#FAILED[@]} failed, ${#SKIPPED[@]} skipped out of ${#APP_DIRS[@]} apps"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

if [ ${#FAILED[@]} -gt 0 ]; then
Expand Down
45 changes: 45 additions & 0 deletions .github/workflows/refresh-typescript-sdks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Refresh TypeScript Playground SDKs

on:
workflow_dispatch:
schedule:
- cron: '0 16 * * *' # 8am PT / 16:00 UTC - same schedule as manifest refresh

permissions:
contents: write
pull-requests: write

jobs:
generate-and-pr:
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'microsoft' }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Setup .NET SDK
uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4
with:
global-json-file: global.json

- name: Setup Node.js
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
with:
node-version: 20.x

- name: Run refreshTypeScriptSdks script
shell: pwsh
run: |
./eng/refreshTypeScriptSdks.ps1

- name: Create or update pull request
uses: dotnet/actions-create-pull-request@e8d799aa1f8b17f324f9513832811b0a62f1e0b1
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: update-typescript-playground-sdks
base: main
commit-message: "[Automated] Update TypeScript Playground AppHost SDKs"
labels: |
area-app-model
area-engineering-systems
title: "[Automated] Update TypeScript Playground AppHost SDKs"
body: "Auto-generated update to refresh the TypeScript playground AppHost SDKs."
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ The following specialized skills are available in `.github/skills/`:
- **test-management**: Quarantines or disables flaky/problematic tests using the QuarantineTools utility
- **connection-properties**: Expert for creating and improving Connection Properties in Aspire resources
- **dependency-update**: Guides dependency version updates by checking nuget.org, triggering the dotnet-migrate-package Azure DevOps pipeline, and monitoring runs
- **api-review**: Reviews .NET API surface area PRs for design guideline violations, applies rules from .NET Framework Design Guidelines and Aspire conventions, and attributes findings to the author who introduced each API
- **startup-perf**: Measures Aspire application startup performance using dotnet-trace and the TraceAnalyzer tool

## Pattern-Based Instructions
Expand Down
6 changes: 3 additions & 3 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
<PackageVersion Include="Microsoft.Extensions.Azure" Version="1.13.1" />
<PackageVersion Include="Microsoft.Extensions.Configuration.AzureAppConfiguration" Version="8.4.0" />
<!-- Azure Management SDK for .NET dependencies -->
<PackageVersion Include="Azure.Provisioning" Version="1.4.0" />
<PackageVersion Include="Azure.Provisioning" Version="1.5.0" />
<PackageVersion Include="Azure.Provisioning.AppConfiguration" Version="1.1.0" />
<PackageVersion Include="Azure.Provisioning.AppContainers" Version="1.1.0" />
<PackageVersion Include="Azure.Provisioning.AppService" Version="1.3.1" />
Expand All @@ -52,7 +52,7 @@
<PackageVersion Include="Azure.Provisioning.Network" Version="1.0.0" />
<PackageVersion Include="Azure.Provisioning.OperationalInsights" Version="1.1.0" />
<PackageVersion Include="Azure.Provisioning.PostgreSql" Version="1.1.1" />
<PackageVersion Include="Azure.Provisioning.PrivateDns" Version="1.0.0-beta.1" />
<PackageVersion Include="Azure.Provisioning.PrivateDns" Version="1.0.0" />
<PackageVersion Include="Azure.Provisioning.Redis" Version="1.1.0" />
<PackageVersion Include="Azure.Provisioning.RedisEnterprise" Version="1.1.0" />
<PackageVersion Include="Azure.Provisioning.Search" Version="1.0.0" />
Expand Down Expand Up @@ -118,7 +118,7 @@
<PackageVersion Include="NATS.Net" Version="2.7.2" />
<PackageVersion Include="NCrontab.Signed" Version="3.4.0" />
<PackageVersion Include="Npgsql.DependencyInjection" Version="10.0.1" />
<PackageVersion Include="OpenAI" Version="2.7.0" />
<PackageVersion Include="OpenAI" Version="2.8.0" />
<PackageVersion Include="Oracle.ManagedDataAccess.OpenTelemetry" Version="23.26.100" />
<PackageVersion Include="Polly.Core" Version="8.6.5" />
<PackageVersion Include="Polly.Extensions" Version="8.6.5" />
Expand Down
2 changes: 1 addition & 1 deletion eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
<MicrosoftDotNetBuildTasksArchivesVersion>11.0.0-beta.25610.3</MicrosoftDotNetBuildTasksArchivesVersion>
<!-- dotnet/extensions -->
<MicrosoftExtensionsAIVersion>10.2.0</MicrosoftExtensionsAIVersion>
<MicrosoftExtensionsAIOpenAIVersion>10.1.0-preview.1.25608.1</MicrosoftExtensionsAIOpenAIVersion>
<MicrosoftExtensionsAIOpenAIVersion>10.2.0-preview.1.26063.2</MicrosoftExtensionsAIOpenAIVersion>
<MicrosoftExtensionsAIAzureAIInferenceVersion>10.0.0-preview.1.25559.3</MicrosoftExtensionsAIAzureAIInferenceVersion>
<MicrosoftExtensionsHttpResilienceVersion>10.2.0</MicrosoftExtensionsHttpResilienceVersion>
<MicrosoftExtensionsDependencyInjectionAutoActivationVersion>10.2.0</MicrosoftExtensionsDependencyInjectionAutoActivationVersion>
Expand Down
37 changes: 22 additions & 15 deletions eng/homebrew/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,20 @@ Aspire CLI is distributed via [Homebrew Cask](https://docs.brew.sh/Cask-Cookbook

```bash
brew install --cask aspire # stable
# brew install --cask aspire@prerelease # preview (not yet supported)
```

## Contents

| File | Description |
|---|---|
| `aspire.rb.template` | Cask template for stable releases |
| `aspire@prerelease.rb.template` | Cask template for prerelease builds |
| `generate-cask.sh` | Downloads tarballs, computes SHA256 hashes, generates cask from template |

### Pipeline templates

| File | Description |
|---|---|
| `eng/pipelines/templates/prepare-homebrew-cask.yml` | Generates, validates, audits, and tests the cask |
| `eng/pipelines/templates/prepare-homebrew-cask.yml` | Generates, styles, validates, audits, and tests the cask |
| `eng/pipelines/templates/publish-homebrew.yml` | Submits the cask as a PR to `Homebrew/homebrew-cask` |

## Supported Platforms
Expand All @@ -33,22 +31,22 @@ macOS only (arm64, x64). The cask uses `arch arm: "arm64", intel: "x64"` for URL
## Artifact URLs

```text
https://ci.dot.net/public/aspire/{VERSION}/aspire-cli-osx-{arch}-{VERSION}.tar.gz
https://ci.dot.net/public/aspire/{ARTIFACT_VERSION}/aspire-cli-osx-{arch}-{VERSION}.tar.gz
```

Where arch is `arm64` or `x64`.

## Why Cask

| Product | Type | Install command | Preview channel |
|---|---|---|---|
| GitHub Copilot CLI | homebrew-cask | `brew install --cask copilot-cli` | `copilot-cli@prerelease` |
| .NET SDK | homebrew-cask | `brew install --cask dotnet-sdk` | `dotnet-sdk@preview` |
| PowerShell | homebrew-cask | `brew install --cask powershell` | `powershell@preview` |
| Product | Type | Install command |
|---|---|---|
| GitHub Copilot CLI | homebrew-cask | `brew install --cask copilot-cli` |
| .NET SDK | homebrew-cask | `brew install --cask dotnet-sdk` |
| PowerShell | homebrew-cask | `brew install --cask powershell` |

- **URL templating**: `url "...osx-#{arch}-#{version}.tar.gz"` — a single line instead of nested `on_macos do / if Hardware::CPU.arm?` blocks
- **Official repo path**: Casks can be submitted to `Homebrew/homebrew-cask` for `brew install aspire` without a tap
- **Cleaner multi-channel**: `aspire` and `aspire@prerelease` follow established cask naming conventions
- **Stable-only release flow**: the current Aspire Homebrew publishing pipeline prepares and submits only the stable `aspire` cask, while a separate prerelease cask remains a possible future option

## CI Pipeline

Expand All @@ -57,17 +55,26 @@ Where arch is `arm64` or `x64`.
| `azure-pipelines.yml` (prepare stage) | Stable casks (artifacts only) | — |
| `release-publish-nuget.yml` (release) | — | Stable cask only |

Publishing submits a PR to `Homebrew/homebrew-cask` using `gh pr create`:
Publishing submits a PR to `Homebrew/homebrew-cask` using the GitHub REST API:

1. Forks `Homebrew/homebrew-cask` (idempotent — reuses existing fork)
2. Creates a branch named `aspire-{version}`
3. Copies the generated cask to `Casks/a/aspire.rb` (or `aspire@prerelease.rb`)
4. Pushes and opens a PR with title `aspire {version}`
2. Creates or resets a branch named `aspire-{version}`
3. Copies the generated cask to `Casks/a/aspire.rb`
4. Reuses the existing open PR for that branch when present
5. Force-pushes the same branch for reruns; if prior PRs from that branch were closed, the publish step opens a fresh PR and marks the old ones as superseded
6. Opens a PR with title `aspire {version}` when none exists

Prepare validation currently runs:

1. `ruby -c` for syntax validation
2. `brew style --fix` on the generated cask
3. `brew audit --cask --online`, or `brew audit --cask --new --online` when the cask does not yet exist upstream
4. `HOMEBREW_NO_INSTALL_FROM_API=1 brew install --cask ...` followed by uninstall validation

## Open Items

- [ ] Submit initial `aspire` cask PR to `Homebrew/homebrew-cask` for acceptance
- [ ] Submit `aspire@prerelease` cask PR to `Homebrew/homebrew-cask`
- [ ] (Future) Decide whether to add a separate prerelease cask (for example, `aspire@prerelease`) and update pipelines/docs accordingly
- [ ] Configure `aspire-homebrew-bot-pat` secret in the pipeline variable group

## References
Expand Down
10 changes: 4 additions & 6 deletions eng/homebrew/aspire.rb.template
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@ cask "aspire" do
arch arm: "arm64", intel: "x64"

version "${VERSION}"
sha256 arm: "${SHA256_OSX_ARM64}",
intel: "${SHA256_OSX_X64}"
sha256 arm: "${SHA256_OSX_ARM64}",
intel: "${SHA256_OSX_X64}"

url "https://ci.dot.net/public/aspire/#{version}/aspire-cli-osx-#{arch}-#{version}.tar.gz",
url "https://ci.dot.net/public/aspire/${ARTIFACT_VERSION}/aspire-cli-osx-#{arch}-#{version}.tar.gz",
verified: "ci.dot.net/public/aspire/"
name "Aspire CLI"
desc "CLI tool for building observable, production-ready distributed applications with Aspire"
desc "CLI for building observable, production-ready distributed applications"
homepage "https://aspire.dev/"

conflicts_with cask: "aspire@prerelease"

binary "aspire"

zap trash: "~/.aspire"
Expand Down
19 changes: 0 additions & 19 deletions eng/homebrew/aspire@prerelease.rb.template

This file was deleted.

60 changes: 31 additions & 29 deletions eng/homebrew/dogfood.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,20 @@ EOF
exit 0
}

is_cask_installed() {
local caskName="$1"

brew list --cask --versions 2>/dev/null | awk '{print $1}' | grep -Fx -- "$caskName" >/dev/null
}

uninstall() {
echo "Uninstalling dogfooded Aspire CLI..."

# Determine which cask is installed
for caskName in "aspire" "aspire@prerelease"; do
if brew list --cask "$TAP_NAME/$caskName" &>/dev/null; then
echo " Uninstalling $TAP_NAME/$caskName..."
brew uninstall --cask "$TAP_NAME/$caskName"
echo " Uninstalled."
fi
done
if brew list --cask "$TAP_NAME/aspire" &>/dev/null; then
echo " Uninstalling $TAP_NAME/aspire..."
brew uninstall --cask "$TAP_NAME/aspire"
echo " Uninstalled."
fi

if brew tap-info "$TAP_NAME" &>/dev/null; then
echo " Removing tap $TAP_NAME..."
Expand Down Expand Up @@ -74,16 +77,14 @@ fi

# Auto-detect cask file if not specified
if [[ -z "$CASK_FILE" ]]; then
for candidate in "$SCRIPT_DIR/aspire.rb" "$SCRIPT_DIR/aspire@prerelease.rb"; do
if [[ -f "$candidate" ]]; then
CASK_FILE="$candidate"
break
fi
done
candidate="$SCRIPT_DIR/aspire.rb"
if [[ -f "$candidate" ]]; then
CASK_FILE="$candidate"
fi

if [[ -z "$CASK_FILE" ]]; then
echo "Error: No cask file found in $SCRIPT_DIR"
echo "Expected aspire.rb or aspire@prerelease.rb"
echo "Expected aspire.rb"
exit 1
fi
fi
Expand All @@ -97,20 +98,24 @@ CASK_FILE="$(cd "$(dirname "$CASK_FILE")" && pwd)/$(basename "$CASK_FILE")"
CASK_FILENAME="$(basename "$CASK_FILE")"
CASK_NAME="${CASK_FILENAME%.rb}"

if [[ "$CASK_NAME" != "aspire" ]]; then
echo "Error: Only the stable Homebrew cask is supported."
echo "Expected aspire.rb"
exit 1
fi

echo "Aspire CLI Homebrew Dogfood Installer"
echo "======================================"
echo " Cask file: $CASK_FILE"
echo " Cask name: $CASK_NAME"
echo ""

# Check for conflicts with official installs
for check in "aspire" "aspire@prerelease"; do
if brew list --cask "$check" &>/dev/null; then
echo "Error: '$check' is already installed from the official Homebrew tap."
echo "Uninstall it first with: brew uninstall --cask $check"
exit 1
fi
done
if is_cask_installed "aspire"; then
echo "Error: 'aspire' is already installed."
echo "If this is a previous dogfood install, remove it with: $(basename "$0") --uninstall"
echo "Otherwise uninstall it first with: brew uninstall --cask aspire"
exit 1
fi

# Check for leftover local/aspire tap from pipeline testing
if brew tap-info "local/aspire" &>/dev/null 2>&1; then
Expand All @@ -128,12 +133,9 @@ fi
# Clean up any previous dogfood tap
if brew tap-info "$TAP_NAME" &>/dev/null 2>&1; then
echo "Removing previous dogfood tap..."
# Uninstall any casks from the old tap first
for old in "aspire" "aspire@prerelease"; do
if brew list --cask "$TAP_NAME/$old" &>/dev/null; then
brew uninstall --cask "$TAP_NAME/$old" || true
fi
done
if is_cask_installed "aspire"; then
brew uninstall --cask "aspire" || true
fi
brew untap "$TAP_NAME"
fi

Expand Down
Loading
Loading