Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
a2d5b95
[docs] Document timestamp search qualifier for telemetry filtering (#…
aspire-repo-bot[bot] Jun 6, 2026
27cf1ce
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 7, 2026
70fc7aa
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 8, 2026
a3fb06a
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 8, 2026
05a46fd
[docs] Fix persistent container endpoint proxy default docs (#1227)
IEvangelist Jun 8, 2026
935ad64
Add Aspire 13.5 release scaffold
IEvangelist Jun 8, 2026
69c9cb7
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 8, 2026
add6be9
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 8, 2026
956a98e
[docs] Update Foundry Local docs to reflect CLI-based lifecycle (aspi…
aspire-repo-bot[bot] Jun 9, 2026
b2af95a
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 9, 2026
df5f07b
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 10, 2026
9b28ec6
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 10, 2026
627f68f
docs: document coding agent telemetry detection (aspire#18065)
aspire-repo-bot[bot] Jun 10, 2026
81d4938
Merge pull request #1242 from microsoft/docs/coding-agent-telemetry-1…
DamianEdwards Jun 10, 2026
7aa48a8
[docs] Add WithTerminal() interactive terminal sessions page (#1244)
mitchdenny Jun 11, 2026
88769ea
docs: update 13.5 banners and release notes
IEvangelist Jun 11, 2026
52a4a0d
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 11, 2026
1619d44
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 12, 2026
643504f
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 16, 2026
43f1d24
Document OS information check in aspire doctor command
aspire-repo-bot[bot] Jun 17, 2026
ff5fe72
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 18, 2026
1dddc9a
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 22, 2026
612131f
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 22, 2026
2e4e93f
Fix broken WithTerminal link in 13.5 whats-new (#1281)
sebastienros Jun 22, 2026
76def87
Add dashboard troubleshooting page (#1255)
JamesNK Jun 23, 2026
becb334
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 23, 2026
5edf0f4
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 25, 2026
131768e
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 25, 2026
378ca1f
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 25, 2026
2550c15
[docs] Add deprecation notices to GitHub Models integration docs (#1279)
aspire-repo-bot[bot] Jun 25, 2026
32ccc5c
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 25, 2026
bc8c313
Fix forbidden phrases flagged by CI (#1301)
IEvangelist Jun 25, 2026
f0be337
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 25, 2026
1064f2b
docs: mention Cohere and MistralAI model families in Foundry host doc…
aspire-repo-bot[bot] Jun 25, 2026
8811d57
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 26, 2026
40baf73
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 26, 2026
0a92774
[docs] Clarify C# file-based AppHost launch profile location (#1176)
aspire-repo-bot[bot] Jun 26, 2026
ce8929c
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 26, 2026
0eb6148
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 26, 2026
93d0681
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 28, 2026
840be81
[docs] Document icon fallback behavior for resource commands (#1277)
aspire-repo-bot[bot] Jun 29, 2026
3892cf0
[docs] Add Nix installation path for Aspire CLI (#1286)
aspire-repo-bot[bot] Jun 29, 2026
bc33b3d
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 29, 2026
58647e1
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 29, 2026
2ec60a3
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 29, 2026
ba2ab8b
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 29, 2026
f4d564a
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 29, 2026
9a1db57
Address aspire doctor doc feedback
danegsta Jun 29, 2026
5951a0c
Merge pull request #1267 from microsoft/docs-aspire-18252-os-doctor-6…
danegsta Jun 29, 2026
70d4cdf
[docs] Document proxyless endpoint port pre-allocation (Aspire 13.5) …
aspire-repo-bot[bot] Jun 29, 2026
0b1f791
Add Aspire version placeholders to release docs (#1314)
danegsta Jun 30, 2026
9047b16
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 30, 2026
9d2a467
Fix OOM in aspire-version-placeholders build hook (#1318)
IEvangelist Jun 30, 2026
7389910
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 30, 2026
38d323b
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 30, 2026
17c8373
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jun 30, 2026
66a17d3
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jul 1, 2026
cec3703
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jul 1, 2026
64afd5e
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jul 2, 2026
2dbdea3
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jul 2, 2026
a969c9e
Merge remote-tracking branch 'origin/main' into release/13.5
github-actions[bot] Jul 3, 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
34 changes: 34 additions & 0 deletions .agents/skills/doc-pr-reviewer/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,40 @@ For each non-`narrative` claim:
- `contradicted` — the source code says something different than the PR claims; include both texts
4. Do not infer from documentation, blog posts, or other branches. The branch you actually checked out and recorded for that repo (per the **Source of truth** section — the PR's matching branch, or the documented fallback when none matches) is the only source of truth.

## Current-version placeholder review

When a docs PR adds or edits Aspire version strings, check whether the version
is intended to represent the current release or an intentionally fixed version.
The docs site provides placeholders for current-version values:

| Placeholder | Use for |
|-------------|---------|
| `%ASPIRE_VERSION%` | Full current Aspire version, including patch (for example, `13.5.0`) |
| `%ASPIRE_VERSION_MAJOR_MINOR%` | Current Aspire major/minor display version (for example, `13.5`) |

Flag hard-coded Aspire versions as a review finding when they appear in current
copy/paste guidance that should track the active release, including:

- `Aspire.AppHost.Sdk` declarations in project files or file-based apps.
- `#:package Aspire.*@...` file-based app package directives.
- Aspire CLI or AppHost sample output that reports the current CLI/AppHost
version.
- Getting started, installation, or "use this today" examples that should move
with the release branch.

Do **not** require placeholders for intentionally fixed versions, including:

- What's-new or release-note pages that describe a specific historical release.
- Upgrade examples that deliberately compare old and new versions.
- CLI examples where the point is pinning a specific version with `--version`,
`-Version`, or `Aspire.ProjectTemplates::...`.
- Versioned schema URLs, package compatibility notes, minimum-version
requirements, third-party dependency versions, container image tags, or issue
reproduction snippets.

If the intent is ambiguous, leave a `COMMENT` asking whether the version should
track the current release instead of requesting changes outright.

## Phase A artifact

When Phase A finishes, write down (in memory) a frozen Phase A result containing:
Expand Down
21 changes: 18 additions & 3 deletions .github/agents/release-verifier.agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,22 @@ Scan the documentation for version strings that should have been updated for thi

Determine the immediately prior version. For `13.2` the prior is `13.1`; for `13.0` the prior is `9.5` (or whatever the last release of the previous major is). Use the existing what's-new files to determine this.

#### 4b. Scan for stale version references
#### 4b. Verify shared version placeholders

Read `src/frontend/config/aspire-versions.mjs` and verify the exported current
version constants match the release being verified:

| Export | Expected value |
|--------|----------------|
| `currentAspireMajorMinorVersion` | `{VERSION}` (for example, `13.2`) |
| `currentAspireVersion` | `{NUGET_VERSION}` (for example, `13.2.0`) |

These constants drive the `%ASPIRE_VERSION_MAJOR_MINOR%` and
`%ASPIRE_VERSION%` documentation placeholders. If either value is stale, flag it
as a **critical** failure because generated docs can show the wrong current
release version.

#### 4c. Scan for stale version references

Search the docs content tree for references to the prior NuGet version that appear **outside** of intentional historical context (e.g., upgrade-from examples that deliberately show the old version).

Expand All @@ -139,7 +154,7 @@ grep -rn 'Aspire.AppHost.Sdk.*Version="{PRIOR_NUGET_VERSION}"' src/frontend/src/
--include="*.mdx"
```

#### 4c. Evaluate each match
#### 4d. Evaluate each match

For every match found:

Expand All @@ -149,7 +164,7 @@ For every match found:
- **Stale (should be updated)**: The reference is in current guidance, installation instructions, or sample code that a user would copy today. Flag these for update.
3. Log each stale reference with file path, line number, and surrounding context.

#### 4d. Verify new version references
#### 4e. Verify new version references

Spot-check that key documentation pages reference the release version:

Expand Down
7 changes: 6 additions & 1 deletion src/frontend/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { cookieConfig } from './config/cookie.config';
import { locales } from './config/locales.ts';
import { headAttrs } from './config/head.attrs.ts';
import { socialConfig } from './config/socials.config.ts';
import { aspireVersionPlaceholdersIntegration } from './config/aspire-version-placeholders-integration.mjs';
import { remarkAspireVersionPlaceholders } from './config/remark-aspire-version-placeholders.mjs';
import catppuccin from '@catppuccin/starlight';
import lunaria from './config/lunaria-starlight.mjs';
import mermaid from 'astro-mermaid';
Expand Down Expand Up @@ -48,7 +50,9 @@ export default defineConfig({
site: 'https://aspire.dev',
trailingSlash: 'always',
markdown: {
processor: unified(),
processor: unified({
remarkPlugins: [remarkAspireVersionPlaceholders],
}),
},
redirects: redirects,
integrations: [
Expand Down Expand Up @@ -238,6 +242,7 @@ export default defineConfig({
}),
jopSoftwarecookieconsent(cookieConfig),
...(isBuildTimingEnabled ? [buildTiming()] : []),
aspireVersionPlaceholdersIntegration(),
],
build: {
concurrency: buildConcurrency,
Expand Down
91 changes: 91 additions & 0 deletions src/frontend/config/aspire-version-placeholders-integration.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { readdir, readFile, writeFile } from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { replaceAspireVersionPlaceholders } from './remark-aspire-version-placeholders.mjs';

// Per-page Markdown copies emitted by `starlight-page-actions` are the only
// generated artifacts that still contain raw `%ASPIRE_VERSION%` placeholders:
// that plugin `viteStaticCopy`s `src/content/docs/**/*.{md,mdx}` straight to
// `dist/**/*.md` through a regex-only transform, so it never runs through the
// `remarkAspireVersionPlaceholders` remark plugin.
//
// Everything else is already handled before it reaches `dist`:
// - `.html` pages -> rendered via the remark pipeline (placeholders replaced
// in the mdast before expressive-code renders code blocks)
// - `llms*.txt` -> `starlight-llms-txt` sources rendered HTML (`render(entry)`)
// - `reference/**.md` -> generated from API/sample data, not docs content
//
// So this post-build pass only needs to touch `.md` files. Scoping it this way
// (instead of walking every `.html`/`.txt` in `dist`) avoids re-reading the bulk
// of the output — including the large `llms-full.txt` assets — which is what
// previously exhausted the Node heap.
const placeholderCopyExtensions = new Set(['.md']);

// Process the Markdown copies through a small worker pool rather than a single
// recursive `Promise.all` over the whole tree, so peak memory stays proportional
// to the concurrency limit instead of the number of files held open at once.
const DEFAULT_CONCURRENCY = 16;

export function aspireVersionPlaceholdersIntegration() {
return {
name: 'aspire-version-placeholders',
hooks: {
'astro:build:done': async ({ dir }) => {
await replaceAspireVersionPlaceholdersInDirectory(fileURLToPath(dir));
},
},
};
}

export async function replaceAspireVersionPlaceholdersInDirectory(
directory,
concurrency = DEFAULT_CONCURRENCY
) {
const files = [];
await collectMarkdownCopies(directory, files);

if (files.length === 0) {
return;
}

// Normalize to a finite positive integer so a stray NaN/0/negative value can't
// collapse the worker pool to an empty array and silently skip every file.
const limit = Number.isFinite(concurrency) ? Math.floor(concurrency) : DEFAULT_CONCURRENCY;
const workerCount = Math.min(Math.max(1, limit), files.length);
let cursor = 0;

const runWorker = async () => {
while (cursor < files.length) {
const filePath = files[cursor++];
await replaceAspireVersionPlaceholdersInFile(filePath);
}
};

await Promise.all(Array.from({ length: workerCount }, runWorker));
}

async function collectMarkdownCopies(directory, files) {
const entries = await readdir(directory, { withFileTypes: true });

for (const entry of entries) {
const resolvedPath = path.join(directory, entry.name);

if (entry.isDirectory()) {
await collectMarkdownCopies(resolvedPath, files);
continue;
}

if (entry.isFile() && placeholderCopyExtensions.has(path.extname(entry.name))) {
files.push(resolvedPath);
}
}
}

async function replaceAspireVersionPlaceholdersInFile(filePath) {
const content = await readFile(filePath, 'utf8');
const updated = replaceAspireVersionPlaceholders(content);

if (updated !== content) {
await writeFile(filePath, updated);
}
}
7 changes: 7 additions & 0 deletions src/frontend/config/aspire-versions.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const currentAspireMajorMinorVersion = '13.5';
export const currentAspireVersion = '13.5.0';

export const aspireVersionPlaceholders = Object.freeze({
'%ASPIRE_VERSION_MAJOR_MINOR%': currentAspireMajorMinorVersion,
'%ASPIRE_VERSION%': currentAspireVersion,
});
40 changes: 40 additions & 0 deletions src/frontend/config/remark-aspire-version-placeholders.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { aspireVersionPlaceholders } from './aspire-versions.mjs';

const placeholderEntries = Object.entries(aspireVersionPlaceholders);

export function replaceAspireVersionPlaceholders(value) {
return placeholderEntries.reduce(
(current, [placeholder, version]) => current.replaceAll(placeholder, version),
value
);
}

export function remarkAspireVersionPlaceholders() {
return (tree) => {
replaceNodeValues(tree);
};
}

function replaceNodeValues(node) {
if (!node || typeof node !== 'object') {
return;
}

if (typeof node.value === 'string') {
node.value = replaceAspireVersionPlaceholders(node.value);
}

if (Array.isArray(node.attributes)) {
for (const attribute of node.attributes) {
if (attribute && typeof attribute.value === 'string') {
attribute.value = replaceAspireVersionPlaceholders(attribute.value);
}
}
}

if (Array.isArray(node.children)) {
for (const child of node.children) {
replaceNodeValues(child);
}
}
}
21 changes: 21 additions & 0 deletions src/frontend/config/sidebar/dashboard.topics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,27 @@ export const dashboardTopics: StarlightSidebarTopicsUserConfig = {
},
slug: 'dashboard/security-considerations',
},
{
label: 'Troubleshooting',
translations: {
da: 'Fejlfinding',
de: 'Fehlerbehebung',
en: 'Troubleshooting',
es: 'Solución de problemas',
fr: 'Dépannage',
hi: 'समस्या निवारण',
id: 'Pemecahan masalah',
it: 'Risoluzione dei problemi',
ja: 'トラブルシューティング',
ko: '문제 해결',
'pt-BR': 'Solução de problemas',
ru: 'Устранение неполадок',
tr: 'Sorun giderme',
uk: 'Усунення неполадок',
'zh-CN': '故障排除',
},
slug: 'dashboard/troubleshooting',
},
{
label: 'Telemetry',
translations: {
Expand Down
8 changes: 8 additions & 0 deletions src/frontend/config/sidebar/docs.topics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ export const docsTopics: StarlightSidebarTopicsUserConfig = {
label: "What's new",
collapsed: true,
items: [
{
label: 'Aspire 13.5',
slug: 'whats-new/aspire-13-5',
},
{
label: 'Aspire 13.4',
slug: 'whats-new/aspire-13-4',
Expand Down Expand Up @@ -936,6 +940,10 @@ export const docsTopics: StarlightSidebarTopicsUserConfig = {
ja: '実行可能リソース',
},
},
{
label: 'Interactive terminals',
slug: 'app-host/withterminal',
},
],
},
{
Expand Down
41 changes: 39 additions & 2 deletions src/frontend/src/content/docs/app-host/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,14 @@ AppHost configuration is provided through launch profiles:
]}
/>
<Pivot id="csharp">
In C# AppHosts, profiles live in `launchSettings.json`:
C# AppHosts come in two forms, and each stores launch profiles differently:

```json title="launchSettings.json"
- **Project-based AppHost** (the default `dotnet new aspire-apphost` template): profiles live in `Properties/launchSettings.json`.
- **File-based AppHost** (created with `aspire new` using the empty C# template): profiles live in `apphost.run.json`. In this template, `aspire.config.json` only points at the entry file — it does **not** contain a `profiles` block.

**Project-based AppHost** — `Properties/launchSettings.json`:

```json title="Properties/launchSettings.json"
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
Expand All @@ -45,6 +50,38 @@ In C# AppHosts, profiles live in `launchSettings.json`:
}
}
```

**File-based AppHost** — `apphost.run.json` (profiles) and `aspire.config.json` (entry point only):

```json title="apphost.run.json"
{
"profiles": {
"https": {
"applicationUrl": "https://localhost:17134;http://localhost:15170",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21030",
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22057"
}
}
}
}
```

```json title="aspire.config.json"
{
"appHost": {
"path": "apphost.cs"
}
}
```

<Aside type="note">
`aspire run`, `dotnet run apphost.cs`, and C# Dev Kit all read `apphost.run.json`
for launch profiles when it is present. In the `aspire new` empty C# template,
`aspire.config.json` intentionally omits `profiles` to avoid duplicating that data.
</Aside>
</Pivot>
<Pivot id="typescript">
In TypeScript AppHosts, profiles live in `aspire.config.json`:
Expand Down
11 changes: 9 additions & 2 deletions src/frontend/src/content/docs/app-host/resource-lifetimes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ description: Learn how session, persistent, resource-scoped, and parent-process

import { Tabs, TabItem } from '@astrojs/starlight/components';
import { Image } from 'astro:assets';
import LearnMore from '@components/LearnMore.astro';
import persistentContainer from '@assets/whats-new/aspire-9/persistent-container.png';
import persistentContainerDocker from '@assets/whats-new/aspire-9/persistent-container-docker-desktop.png';

Expand Down Expand Up @@ -38,7 +39,13 @@ Use persistent lifetime for resources that are expensive to initialize, need sta
Persistent resources are automatically recreated when the AppHost detects meaningful configuration changes. If the configuration differs, the resource is recreated with the new settings.
:::

Persistent resources default to proxyless endpoints so a stable local endpoint can remain reachable after the AppHost stops. You can still configure endpoint proxy behavior explicitly. For example, set `isProxied: true` or `IsProxied = true` when you need Aspire's proxy for a specific endpoint, or disable endpoint proxy support when you intentionally want every endpoint on a resource to be proxyless. Persistent executable endpoints must have a concrete `port` or `targetPort`; automatically persisted random executable ports aren't supported.
Persistent containers use proxied endpoints by default, just like session containers. The proxy runs only while the AppHost is running, so the proxy address isn't reachable after the AppHost stops. Persistent executables and projects default to proxyless endpoints so their direct addresses stay stable and reachable even after the AppHost stops.

You can still configure endpoint proxy behavior explicitly on any persistent resource. Set `isProxied: false` on an individual endpoint, or call `WithEndpointProxySupport(false)` to make every endpoint on a resource proxyless. When a proxyless endpoint doesn't specify a public `port`, Aspire allocates one before the resource is created. For persistent resources, Aspire stores the allocated port in user secrets when user secrets are available and reuses it on later AppHost runs.

<LearnMore>
For more information, see [Allocate ports for dynamic proxyless endpoints](/fundamentals/networking-overview/#allocate-ports-for-dynamic-proxyless-endpoints).
</LearnMore>

:::danger[Persistent container &ne; persistent data]
Persistent container lifetime doesn't guarantee data durability. For details, see [Container lifetime vs. data durability](#container-lifetime-vs-data-durability).
Expand Down Expand Up @@ -155,7 +162,7 @@ await builder.build().run();
</TabItem>
</Tabs>

Configure a concrete `port` or `targetPort` for persistent executable endpoints; automatically persisted random executable ports aren't supported.
The preceding example configures a concrete `port` so the endpoint address is explicit. If you omit the public `port`, Aspire allocates one before the executable starts and reuses it from user secrets on later AppHost runs when user secrets are available.

## Configure a persistent project

Expand Down
Loading
Loading