Skip to content

Commit 459406e

Browse files
committed
feat: Implement CI workflow for content validation and build process; add validation script for blog content integrity
1 parent 03e0af7 commit 459406e

7 files changed

Lines changed: 147 additions & 6 deletions

File tree

.github/workflows/ci.yml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
branches: [main, develop]
6+
push:
7+
branches: [main, develop]
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
validate:
14+
name: Validate
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Checkout
18+
uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 0
21+
22+
- name: Setup pnpm
23+
uses: pnpm/action-setup@v4
24+
with:
25+
version: latest
26+
27+
- name: Setup Node
28+
uses: actions/setup-node@v4
29+
with:
30+
node-version: "20"
31+
cache: pnpm
32+
33+
- name: Install dependencies
34+
run: pnpm install --frozen-lockfile
35+
36+
- name: Validate blog content
37+
run: node scripts/validate-content.mjs
38+
39+
- name: TypeScript type check
40+
run: pnpm typecheck
41+
42+
- name: Build website
43+
env:
44+
DOCUSAURUS_ON_BROKEN_LINKS: throw
45+
run: pnpm build

.github/workflows/deploy.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ jobs:
3939
- name: Install dependencies
4040
run: pnpm install --frozen-lockfile
4141

42+
- name: Validate blog content
43+
run: node scripts/validate-content.mjs
44+
45+
- name: TypeScript type check
46+
run: pnpm typecheck
47+
4248
- name: Build website
4349
run: pnpm build
4450

docusaurus.config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ const config: Config = {
2020
organizationName: "inclusionAI",
2121
projectName: "inclusionai.github.io",
2222

23-
onBrokenLinks: "warn",
23+
onBrokenLinks: "throw",
24+
onBrokenMarkdownLinks: "throw",
2425

2526
i18n: {
2627
defaultLocale: "en",

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"serve": "docusaurus serve",
1313
"write-translations": "docusaurus write-translations",
1414
"write-heading-ids": "docusaurus write-heading-ids",
15-
"typecheck": "tsc"
15+
"typecheck": "tsc",
16+
"validate": "node scripts/validate-content.mjs"
1617
},
1718
"dependencies": {
1819
"@docusaurus/core": "3.9.2",

scripts/validate-content.mjs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// ---------------------------------------------------------------------------
2+
// Blog content validation script
3+
// Checks every blog post for required frontmatter fields and data integrity.
4+
// Run: node scripts/validate-content.mjs
5+
// ---------------------------------------------------------------------------
6+
import fs from "node:fs";
7+
import path from "node:path";
8+
import matter from "gray-matter";
9+
10+
const BLOG_DIR = path.join(process.cwd(), "blog");
11+
const REQUIRED_FIELDS = ["title", "date", "authors"];
12+
13+
let errors = 0;
14+
let checked = 0;
15+
16+
// --- Blog frontmatter validation ---
17+
const dirs = fs
18+
.readdirSync(BLOG_DIR, { withFileTypes: true })
19+
.filter((d) => d.isDirectory());
20+
21+
for (const dir of dirs) {
22+
const base = path.join(BLOG_DIR, dir.name);
23+
const file = ["index.mdx", "index.md"]
24+
.map((n) => path.join(base, n))
25+
.find((f) => fs.existsSync(f));
26+
27+
if (!file) {
28+
console.error(`ERROR blog/${dir.name}/ — missing index.mdx or index.md`);
29+
errors++;
30+
continue;
31+
}
32+
33+
checked++;
34+
const raw = fs.readFileSync(file, "utf-8");
35+
let fm;
36+
try {
37+
fm = matter(raw).data;
38+
} catch (e) {
39+
console.error(`ERROR blog/${dir.name}/ — invalid frontmatter: ${e.message}`);
40+
errors++;
41+
continue;
42+
}
43+
44+
for (const field of REQUIRED_FIELDS) {
45+
if (fm.draft === true) continue; // skip draft posts
46+
if (!fm[field]) {
47+
console.error(`ERROR blog/${dir.name}/ — missing required field "${field}"`);
48+
errors++;
49+
}
50+
}
51+
52+
// Validate date is parseable
53+
if (fm.date && isNaN(new Date(fm.date).getTime())) {
54+
console.error(`ERROR blog/${dir.name}/ — invalid date "${fm.date}"`);
55+
errors++;
56+
}
57+
58+
// Title should not be empty
59+
if (fm.title && fm.title.trim().length === 0) {
60+
console.error(`ERROR blog/${dir.name}/ — title is empty`);
61+
errors++;
62+
}
63+
}
64+
65+
// --- Data file import validation ---
66+
const DATA_DIR = path.join(process.cwd(), "src", "data");
67+
const dataFiles = fs
68+
.readdirSync(DATA_DIR)
69+
.filter((f) => f.endsWith(".ts"));
70+
71+
for (const file of dataFiles) {
72+
const filePath = path.join(DATA_DIR, file);
73+
const content = fs.readFileSync(filePath, "utf-8");
74+
75+
// Check for empty export arrays (likely a mistake)
76+
const emptyArrayMatch = content.match(/export const \w+:\s*\w+\[\]\s*=\s*\[\s*\]/);
77+
if (emptyArrayMatch) {
78+
console.error(`WARN src/data/${file} — contains empty exported array`);
79+
}
80+
}
81+
82+
console.log(`\nValidated ${checked} blog posts, found ${errors} error(s).`);
83+
84+
if (errors > 0) {
85+
process.exit(1);
86+
}
87+
88+
console.log("All content checks passed.");

src/pages/model.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useState, useMemo } from "react";
2-
import type { ReactNode } from "react";
2+
import type { ReactNode, Key } from "react";
33
import Layout from "@theme/Layout";
44
import Link from "@docusaurus/Link";
55
import clsx from "clsx";
@@ -18,7 +18,7 @@ function categoryClass(cat: ModelCategory): string {
1818
return styles.catMultiModal;
1919
}
2020

21-
function ModelCard({ item }: { item: ModelItem }) {
21+
function ModelCard({ item }: { key?: Key; item: ModelItem }) {
2222
return (
2323
<Link to={`/blog/${item.blogSlug}`} className={styles.card}>
2424
<div className={styles.cardHeader}>

src/pages/system.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useState, useMemo } from "react";
2-
import type { ReactNode } from "react";
2+
import type { ReactNode, Key } from "react";
33
import Layout from "@theme/Layout";
44
import Link from "@docusaurus/Link";
55
import clsx from "clsx";
@@ -22,7 +22,7 @@ function categoryClass(cat: SystemCategory): string {
2222
return styles.catBenchmarks;
2323
}
2424

25-
function SystemCard({ item }: { item: SystemItem }) {
25+
function SystemCard({ item }: { key?: Key; item: SystemItem }) {
2626
return (
2727
<Link to={`/blog/${item.blogSlug}`} className={styles.card}>
2828
<div className={styles.cardHeader}>

0 commit comments

Comments
 (0)