diff --git a/website/scripts/GenerateReleaseCatalogs.ts b/website/scripts/GenerateReleaseCatalogs.ts index 4a15a735..7b0b7749 100644 --- a/website/scripts/GenerateReleaseCatalogs.ts +++ b/website/scripts/GenerateReleaseCatalogs.ts @@ -9,6 +9,14 @@ const CATALOGS_DIR = path.join(__dirname, '../../catalogs'); const OUTPUT_DIR = path.join(__dirname, '../src/data/ccc-releases'); const DELIVERY_TOOLKIT_DIR = path.join(__dirname, '../../delivery-toolkit'); +/** Must all exist for generate-release-artifacts; partial dirs are skipped (matches delivery-toolkit loadCatalog). */ +const REQUIRED_CATALOG_FILES = [ + 'metadata.yaml', + 'controls.yaml', + 'capabilities.yaml', + 'threats.yaml', +] as const; + interface CatalogDirectory { category: string; service: string; @@ -137,30 +145,29 @@ async function discoverCatalogDirectories(): Promise { const releaseDetailsPath = path.join(servicePath, 'release-details.yaml'); const hasReleaseDetails = fs.existsSync(releaseDetailsPath); - // Check if this directory has the required catalog files (controls.yaml, capabilities.yaml, etc.) - const hasControls = fs.existsSync(path.join(servicePath, 'controls.yaml')); - const hasCapabilities = fs.existsSync(path.join(servicePath, 'capabilities.yaml')); - const hasThreats = fs.existsSync(path.join(servicePath, 'threats.yaml')); - - // Only include directories that look like valid catalogs - const isValidCatalog = hasControls || hasCapabilities || hasThreats; - - if (isValidCatalog) { - catalogs.push({ - category, - service, - fullPath: servicePath, - hasReleaseDetails, - needsDevReleaseDetails: true // Always create DEV version - }); - - if (hasReleaseDetails) { - console.log(` ✅ Found with release details: ${category}/${service} (will also create DEV version)`); - } else { - console.log(` 🔧 Will create DEV version: ${category}/${service}`); - } + const missingRequired = REQUIRED_CATALOG_FILES.filter( + (f) => !fs.existsSync(path.join(servicePath, f)) + ); + + if (missingRequired.length > 0) { + console.log( + ` ⏭️ Skipping incomplete catalog: ${category}/${service} (missing: ${missingRequired.join(', ')})` + ); + continue; + } + + catalogs.push({ + category, + service, + fullPath: servicePath, + hasReleaseDetails, + needsDevReleaseDetails: true // Always create DEV version + }); + + if (hasReleaseDetails) { + console.log(` ✅ Found with release details: ${category}/${service} (will also create DEV version)`); } else { - console.log(` ⏭️ Skipping non-catalog: ${category}/${service}`); + console.log(` 🔧 Will create DEV version: ${category}/${service}`); } } } diff --git a/website/src/components/ccc/Control/index.tsx b/website/src/components/ccc/Control/index.tsx index dcd1b1c0..87df7a53 100644 --- a/website/src/components/ccc/Control/index.tsx +++ b/website/src/components/ccc/Control/index.tsx @@ -1,6 +1,7 @@ import React from "react"; import Layout from "@theme/Layout"; import Link from "@docusaurus/Link"; +import useBrokenLinks from "@docusaurus/useBrokenLinks"; import { Card, CardContent, CardHeader, CardTitle } from "../../ui/card"; import { Badge } from "../../ui/badge"; import { ControlPageData } from "@site/src/types/ccc"; @@ -11,6 +12,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from ". export default function CCCControlTemplate({ pageData }: { pageData: ControlPageData }) { const { control, releaseTitle, releaseSlug, related_threats, related_capabilities } = pageData; + const brokenLinks = useBrokenLinks(); return ( @@ -65,28 +67,34 @@ export default function CCCControlTemplate({ pageData }: { pageData: ControlPage - {control.test_requirements.map((tr) => ( - - - - {tr.id} - - {tr.text} - - {tr.applicability?.length > 0 ? ( -
- {tr.applicability.map((level) => ( - - {level} - - ))} -
- ) : ( - - - )} -
-
- ))} + {control.test_requirements.map((tr) => { + const anchorId = tr.id.split(".").pop()?.toLowerCase(); + if (anchorId) { + brokenLinks.collectAnchor(anchorId); + } + return ( + + + + {tr.id} + + {tr.text} + + {tr.applicability?.length > 0 ? ( +
+ {tr.applicability.map((level) => ( + + {level} + + ))} +
+ ) : ( + - + )} +
+
+ ); + })}