Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions .github/skills/doc-writer/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ Python specific content here.

If a heading needs to appear in the **On this page** table of contents, keep the heading outside the `Pivot` content and put only the variant-specific body content inside each `Pivot`.

#### On this page and "Overview" headings

When a page shows the **On this page** table of contents (the default behavior unless `tableOfContents: false` is set), do **not** add an `Overview` heading at any level (`##`, `###`, etc.). The docs site already provides an implicit overview link to the top of the page, so an explicit `Overview` heading becomes redundant.

If your opening section is truly introductory, keep it as body copy without an `Overview` heading. If that section has a more specific purpose, use a descriptive heading such as `Key concepts`, `Prerequisites`, or another topic-specific label.

For Aspire AppHost docs, use a single page-level `PivotSelector` with `key="aspire-lang"` when the surrounding section flow should switch as one unit. If a page would otherwise need multiple `aspire-lang` selectors, keep the page-level selector for the main flow and use synced `Tabs`/`TabItem` with `syncKey='aspire-lang'` for repeated language-specific examples later on the page.

```mdx
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import LearnMore from '@components/LearnMore.astro';

Aspire provides powerful APIs for building container images from your resources during publishing and deployment operations. This article covers the key components that enable programmatic container image creation and progress reporting.

## Overview

During publishing and deployment, the container image builder is available to create images for resources that need them. Aspire uses this builder when a resource requires a container image, such as when publishing with Docker Compose. The process involves two main components:

- `IResourceContainerImageBuilder`: The service that turns resource definitions into runnable container images.
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/src/content/docs/deployment/pipelines.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Aspire uses a pipeline-based deployment system that enables extensible, composab
functionality may change in future releases.
</Aside>

## Pipeline model
## Pipeline deployment model

The pipeline deployment system provides a flexible, concurrent model for deploying cloud applications. Pipelines break deployment into discrete, well-defined steps that can be optimized for performance and reliability while maintaining clear visibility into the deployment process.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ In this introduction, you'll see how to install and use the Aspire Azure Contain
To follow this guide, you must have created an Aspire solution to work with. To learn how to do that, see [Build your first Aspire app](/get-started/first-app/).
</Aside>

## Overview
## Key capabilities

Aspire apps often build and run container images locally but require secure registries for staging and production environments. The Azure Container Registry integration provides the following capabilities:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import containerRegistryIcon from '@assets/icons/azure-container-registry-icon.s

[Azure Container Registry](https://learn.microsoft.com/azure/container-registry) is a managed Docker container registry service that simplifies the storage, management, and deployment of container images. The Aspire integration allows you to provision or reference an existing Azure Container Registry and seamlessly integrate it with your app's compute environments.

## Overview
## Key capabilities

Aspire apps often build and run container images locally but require secure registries for staging and production environments. The Azure Container Registry integration provides the following capabilities:

Expand Down
65 changes: 65 additions & 0 deletions src/frontend/tests/unit/filetree-format.vitest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const testsDir = path.dirname(fileURLToPath(import.meta.url));
const frontendRoot = path.resolve(testsDir, '..', '..');
const docsRoot = path.join(frontendRoot, 'src', 'content', 'docs');
const fileTreeBlockPattern = /<FileTree\b[^>]*>([\s\S]*?)<\/FileTree>/g;
const frontmatterPattern = /^\uFEFF?\s*---\r?\n([\s\S]*?)\r?\n---/;

function collectDocs(dirPath: string): string[] {
const entries = readdirSync(dirPath, { withFileTypes: true });
Expand Down Expand Up @@ -78,6 +79,56 @@ function findFileTreeIssues(source: string): Array<{ line: number; reason: strin
return issues;
}

function isTableOfContentsEnabled(source: string): boolean {
const frontmatterMatch = source.match(frontmatterPattern);
const frontmatter = frontmatterMatch?.[1] ?? '';
Comment thread
IEvangelist marked this conversation as resolved.

return !/^\s*tableOfContents\s*:\s*false\b/m.test(frontmatter);
}

function findOverviewHeadingIssues(source: string): Array<{ line: number; reason: string }> {
if (!isTableOfContentsEnabled(source)) {
return [];
}

const issues: Array<{ line: number; reason: string }> = [];
const lines = source.split(/\r?\n/);
let currentFence: { char: '`' | '~'; length: number } | null = null;

for (const [index, line] of lines.entries()) {
const fenceMatch = line.match(/^\s*((`{3,})|(~{3,}))/);
if (fenceMatch) {
const fenceDelimiter = fenceMatch[1];
const fenceChar = fenceDelimiter[0] as '`' | '~';
const fenceLength = fenceDelimiter.length;

if (!currentFence) {
currentFence = { char: fenceChar, length: fenceLength };
continue;
}

if (currentFence.char === fenceChar && fenceLength >= currentFence.length) {
currentFence = null;
continue;
}
}

if (currentFence) {
continue;
}

if (/^\s*##+\s+Overview\s*$/.test(line)) {
issues.push({
line: index + 1,
reason:
'TOC-enabled pages must not include an explicit "Overview" heading; use intro text or a more specific heading.',
});
}
Comment thread
IEvangelist marked this conversation as resolved.
}

return issues;
}

test('FileTree markdown lists remain newline-delimited in docs source', () => {
const docPaths = collectDocs(docsRoot);
const failures = docPaths.flatMap((docPath) => {
Expand All @@ -91,3 +142,17 @@ test('FileTree markdown lists remain newline-delimited in docs source', () => {

expect(failures, failures.join('\n')).toEqual([]);
});

test('TOC-enabled docs do not include an explicit Overview heading', () => {
const docPaths = collectDocs(docsRoot);
const failures = docPaths.flatMap((docPath) => {
const source = readFileSync(docPath, 'utf8');
const relativePath = path.relative(frontendRoot, docPath).replaceAll(path.sep, '/');

return findOverviewHeadingIssues(source).map(
({ line, reason }) => `${relativePath}:${line} ${reason}`
);
});

expect(failures, failures.join('\n')).toEqual([]);
});
Loading