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: 5 additions & 1 deletion .agents/skills/update-integrations/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ This skill synchronizes the integration package catalog with documentation URL m

## Overview

The automated flow also reconciles documentation mappings and runs `pnpm test:unit:structured-data` before API regeneration or PR creation. This uses a data-only Vitest configuration so validation does not rewrite Astro's generated assets. Mapping changes are included in the generated PR.

The aspire.dev site maintains three key data locations:

- **`src/frontend/src/data/aspire-integrations.json`** — Package metadata fetched from the configured package feeds (titles, descriptions, icons, versions, download counts).
Expand Down Expand Up @@ -61,6 +63,8 @@ pnpm --dir ./src/frontend update:integrations

This writes updated package metadata to `src/frontend/src/data/aspire-integrations.json`. The script queries the NuGet v3 API for packages matching `owner:aspire`, `Aspire.Hosting.`, and `CommunityToolkit.Aspire`, then filters out deprecated, unlisted, and excluded packages.

It also removes documentation mappings for Aspire and Community Toolkit packages absent from the updated catalog. Manually curated third-party mappings are preserved, and new documentation URLs still require manual resolution.

On `release/*` branches, the script queries the branch-specific official Aspire feed for `Aspire.*` packages and continues to query nuget.org for `CommunityToolkit.Aspire.*` packages.

### 2. Read the updated package data
Expand All @@ -76,7 +80,7 @@ Load `src/frontend/src/data/integration-docs.json` and reconcile it with the pac
- If an entry exists, verify the `href` is correct
- If no entry exists, determine the appropriate documentation URL

- **Remove stale entries** from `integration-docs.json` that reference packages no longer in `aspire-integrations.json`
- **Remove stale entries** for catalog-managed Aspire and Community Toolkit packages no longer in `aspire-integrations.json`. Preserve mappings for third-party packages outside the catalog's sources.

- **Preserve existing correct mappings** — do not change entries that are already accurate

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/update-integration-data.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ jobs:
# source, manifests, or docs. Directories with no changes are no-ops.
git add -- \
src/frontend/src/data/aspire-integrations.json \
src/frontend/src/data/integration-docs.json \
src/frontend/src/data/github-stats.json \
src/frontend/src/data/samples.json \
src/frontend/src/assets/samples \
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"test:unit:contracts": "vitest run --config vitest.config.ts tests/unit/analytics-script-contracts.vitest.test.ts tests/unit/redirects.vitest.test.ts",
"test:unit:components": "vitest run --config vitest.config.ts tests/unit/custom-components.vitest.test.ts tests/unit/site-tour.vitest.test.ts",
"test:unit:docs": "vitest run --config vitest.config.ts tests/unit/filetree-format.vitest.test.ts",
"test:unit:structured-data": "vitest run --config vitest.config.ts tests/unit/structured-data.vitest.test.ts tests/unit/update-integrations.vitest.test.ts",
"test:unit:structured-data": "vitest run --config vitest.structured-data.config.ts",
"test:unit:cli-config-schema": "vitest run --config vitest.config.ts tests/unit/cli-config-schema.vitest.test.ts",
"test:unit:llms-small-whitespace": "vitest run --config vitest.config.ts tests/unit/llms-small-whitespace.vitest.test.ts",
"test:unit:llms-txt": "vitest run --config vitest.config.ts tests/unit/llms-txt-twoslash.vitest.test.ts",
Expand Down
11 changes: 10 additions & 1 deletion src/frontend/scripts/update-integration-data.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

Phases:
1. `pnpm update:all` — integration metadata, GitHub stats, sample metadata.
Reconciles documentation mappings, then runs structured-data tests.
2. Version-change detection — compares the committed
aspire-integrations.json against the freshly written one by package
title -> version. Metadata-only changes (icons, descriptions, download
Expand All @@ -27,7 +28,7 @@

Exit codes:
0 success (whether or not there were changes)
1 a required phase failed (update:all, TS API regen, out-of-scope diff).
1 a required phase failed (data update/validation, TS API regen, out-of-scope diff).
The caller must NOT open a PR on a non-zero exit.

Packages without a public API surface are reported as explicit skips. Any
Expand Down Expand Up @@ -71,6 +72,7 @@ $PkgGenScript = Join-Path $RepoRoot 'src' 'tools' 'PackageJsonGenerator' 'genera
# this set appearing in `git status` is treated as a scope violation.
$AllowedPaths = @(
'src/frontend/src/data/aspire-integrations.json',
'src/frontend/src/data/integration-docs.json',
'src/frontend/src/data/github-stats.json',
'src/frontend/src/data/samples.json',
'src/frontend/src/assets/samples/',
Expand Down Expand Up @@ -261,6 +263,12 @@ try {
Write-Error "pnpm update:all failed (exit $LASTEXITCODE). Aborting; no PR will be opened."
exit 1
}

& pnpm run test:unit:structured-data
if ($LASTEXITCODE -ne 0) {
Write-Error "Structured-data validation failed (exit $LASTEXITCODE). Aborting; no PR will be opened."
exit 1
}
}
finally {
Pop-Location
Expand Down Expand Up @@ -559,6 +567,7 @@ $sb = [System.Text.StringBuilder]::new()
[void]$sb.AppendLine("")
[void]$sb.AppendLine("### What's updated")
[void]$sb.AppendLine("- ``src/frontend/src/data/aspire-integrations.json`` — latest package information")
[void]$sb.AppendLine("- ``src/frontend/src/data/integration-docs.json`` — documentation mappings, when packages are removed")
[void]$sb.AppendLine("- ``src/frontend/src/data/github-stats.json`` — repository statistics")
[void]$sb.AppendLine("- ``src/frontend/src/data/samples.json`` — sample metadata, when changed")
[void]$sb.AppendLine("- ``src/frontend/src/assets/samples/`` — sample thumbnails, when changed")
Expand Down
31 changes: 31 additions & 0 deletions src/frontend/scripts/update-integrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const EXCLUDED_PACKAGES = [
'CommunityToolkit.Aspire.EventStore',
];
const OUTPUT_PATH = './src/data/aspire-integrations.json';
const DOCS_OUTPUT_PATH = './src/data/integration-docs.json';
export const DEFAULT_NUGET_ICON_URL =
'https://www.nuget.org/Content/gallery/img/default-package-icon.svg';

Expand Down Expand Up @@ -112,6 +113,11 @@ export interface IntegrationOutput {
version?: string;
}

interface IntegrationDoc {
match: string;
href: string;
}

type PrereleaseIdentifier = { n: number } | { s: string };

interface ParsedSemVer {
Expand Down Expand Up @@ -270,6 +276,22 @@ export function getOfficialAspireDefaultIconPackages(output: IntegrationOutput[]
.map((pkg) => `${pkg.title}@${pkg.version ?? 'unknown'}`);
}

export function reconcileIntegrationDocs(
docs: IntegrationDoc[],
integrations: Pick<IntegrationOutput, 'title'>[]
): IntegrationDoc[] {
const catalogPackages = new Set(integrations.map(({ title }) => title.toLowerCase()));

return docs.filter(({ match }) => {
const packageId = match.toLowerCase();
// Other publishers are curated manually, outside this updater's package sources.
if (!isOfficialAspirePackage(packageId) && !packageId.startsWith('communitytoolkit.aspire')) {
return true;
}
return catalogPackages.has(packageId);
});
}

function filterAndTransform(pkgs: PackageRecord[]): IntegrationOutput[] {
const excludedLower = EXCLUDED_PACKAGES.map((pkg) => pkg.toLowerCase());

Expand Down Expand Up @@ -732,8 +754,17 @@ export async function updateIntegrations(): Promise<void> {
`⚠️ Official Aspire packages resolved to the default NuGet icon: ${defaultIconPackages.join(', ')}`
);
}
const docs = JSON.parse(fs.readFileSync(DOCS_OUTPUT_PATH, 'utf8')) as IntegrationDoc[];
const reconciledDocs = reconcileIntegrationDocs(docs, output);

fs.writeFileSync(OUTPUT_PATH, JSON.stringify(output, null, 2));
console.log(`✅ Saved ${output.length} packages to ${OUTPUT_PATH}`);
if (reconciledDocs.length !== docs.length) {
fs.writeFileSync(DOCS_OUTPUT_PATH, `${JSON.stringify(reconciledDocs, null, 2)}\n`);
console.log(
`Removed ${docs.length - reconciledDocs.length} stale documentation mapping(s) from ${DOCS_OUTPUT_PATH}`
);
}
}

const isMainModule = process.argv[1]
Expand Down
Loading
Loading