Skip to content

Commit aad7a16

Browse files
committed
docs: sync Fern code reference across versions
1 parent 503c285 commit aad7a16

1 file changed

Lines changed: 130 additions & 21 deletions

File tree

fern/scripts/fern-published-branch.py

Lines changed: 130 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
from pathlib import Path
1515

1616
DEVNOTES_SECTION_RE = re.compile(r"^ - section:\s+Dev Notes\s*$")
17+
CODE_REFERENCE_SECTION_RE = re.compile(r"^ - section:\s+Code Reference\s*$")
18+
CODE_REFERENCE_PAGE_ROOT_RE = re.compile(r"path:\s+\./([^/]+)/pages/code_reference/")
1719
NAV_PATH_RE = re.compile(r"^(\s*path:\s+)\./([^#\s]+)(.*)$")
1820
REDIRECT_VERSION_RE = re.compile(
1921
r'^\s*destination:\s+["\']/nemo/datadesigner/((?:v[0-9][^/"\']*)|older-versions)(?:/|["\'])'
@@ -41,6 +43,44 @@
4143
"fern/styles/metrics-table.css",
4244
"fern/styles/trajectory-viewer.css",
4345
]
46+
CONFIG_CODE_REFERENCE_PAGES = [
47+
"analysis.mdx",
48+
"column_configs.mdx",
49+
"config_builder.mdx",
50+
"data_designer_config.mdx",
51+
"mcp.mdx",
52+
"models.mdx",
53+
"processors.mdx",
54+
"run_config.mdx",
55+
"sampler_params.mdx",
56+
"validator_params.mdx",
57+
]
58+
CODE_REFERENCE_STRUCTURE_PAGES = [
59+
"index.mdx",
60+
"config/index.mdx",
61+
"config/seeds.mdx",
62+
"engine/column_generators.mdx",
63+
"engine/index.mdx",
64+
"engine/mcp.mdx",
65+
"engine/processors.mdx",
66+
"engine/seed_readers.mdx",
67+
"interface/data_designer.mdx",
68+
"interface/errors.mdx",
69+
"interface/index.mdx",
70+
"interface/results.mdx",
71+
]
72+
CODE_REFERENCE_LINK_REPLACEMENTS = [
73+
("/code-reference/topic-overviews/data-designer-config", "/code-reference/config/data-designer-config"),
74+
("/code-reference/topic-overviews/column-configs", "/code-reference/config/column-configs"),
75+
("/code-reference/topic-overviews/config-builder", "/code-reference/config/config-builder"),
76+
("/code-reference/topic-overviews/run-config", "/code-reference/config/run-config"),
77+
("/code-reference/topic-overviews/sampler-params", "/code-reference/config/sampler-params"),
78+
("/code-reference/topic-overviews/validator-params", "/code-reference/config/validator-params"),
79+
("/code-reference/topic-overviews/models", "/code-reference/config/models"),
80+
("/code-reference/topic-overviews/mcp", "/code-reference/config/mcp"),
81+
("/code-reference/topic-overviews/processors", "/code-reference/config/processors"),
82+
("/code-reference/topic-overviews/analysis", "/code-reference/config/analysis"),
83+
]
4484

4585

4686
class PublishedBranchError(RuntimeError):
@@ -145,6 +185,16 @@ def copy_path(source: Path, target: Path) -> None:
145185
shutil.copy2(source, target)
146186

147187

188+
def copy_mdx_with_link_rewrites(source: Path, target: Path) -> None:
189+
if not source.exists():
190+
return
191+
target.parent.mkdir(parents=True, exist_ok=True)
192+
content = source.read_text()
193+
for old, new in CODE_REFERENCE_LINK_REPLACEMENTS:
194+
content = content.replace(old, new)
195+
target.write_text(content)
196+
197+
148198
def clear_published_tree(root: Path) -> None:
149199
root.mkdir(parents=True, exist_ok=True)
150200
for path in root.iterdir():
@@ -168,6 +218,83 @@ def merge_preserved_versions(source_versions: Path, published_versions: Path, pr
168218
copy_path(path, target)
169219

170220

221+
def extract_navigation_section(path: Path, section_re: re.Pattern[str]) -> list[str]:
222+
lines = path.read_text().splitlines(keepends=True)
223+
start = next((i for i, line in enumerate(lines) if section_re.match(line)), -1)
224+
if start == -1:
225+
raise PublishedBranchError(f"Section not found in {path}")
226+
end = start + 1
227+
while end < len(lines):
228+
if lines[end].startswith(" - ") and lines[end].strip():
229+
break
230+
end += 1
231+
return lines[start:end]
232+
233+
234+
def replace_navigation_section(path: Path, section_re: re.Pattern[str], block: list[str]) -> None:
235+
lines = path.read_text().splitlines(keepends=True)
236+
start = next((i for i, line in enumerate(lines) if section_re.match(line)), -1)
237+
if start == -1:
238+
raise PublishedBranchError(f"Section not found in {path}")
239+
end = start + 1
240+
while end < len(lines):
241+
if lines[end].startswith(" - ") and lines[end].strip():
242+
break
243+
end += 1
244+
lines[start:end] = block
245+
path.write_text("".join(lines))
246+
247+
248+
def code_reference_page_root(block: list[str]) -> str | None:
249+
for line in block:
250+
match = CODE_REFERENCE_PAGE_ROOT_RE.search(line)
251+
if match:
252+
return match.group(1)
253+
return None
254+
255+
256+
def rewrite_code_reference_block(block: list[str], page_root: str) -> list[str]:
257+
return [line.replace("./latest/pages/code_reference/", f"./{page_root}/pages/code_reference/") for line in block]
258+
259+
260+
def sync_code_reference_pages(source_root: Path, published_root: Path, page_root: str) -> None:
261+
source_base = source_root / "fern" / "versions" / "latest" / "pages" / "code_reference"
262+
target_base = published_root / "fern" / "versions" / page_root / "pages" / "code_reference"
263+
if not source_base.exists() or not target_base.exists():
264+
return
265+
266+
for rel_path in CODE_REFERENCE_STRUCTURE_PAGES:
267+
copy_mdx_with_link_rewrites(source_base / rel_path, target_base / rel_path)
268+
269+
for filename in CONFIG_CODE_REFERENCE_PAGES:
270+
flat_source = target_base / filename
271+
nested_source = target_base / "config" / filename
272+
latest_source = source_base / "config" / filename
273+
source = flat_source if flat_source.exists() else nested_source if nested_source.exists() else latest_source
274+
copy_mdx_with_link_rewrites(source, target_base / "config" / filename)
275+
276+
277+
def sync_code_reference_archive(source_root: Path, published_root: Path) -> None:
278+
source_nav = source_root / "fern" / "versions" / "latest.yml"
279+
if not source_nav.exists():
280+
return
281+
source_block = extract_navigation_section(source_nav, CODE_REFERENCE_SECTION_RE)
282+
283+
versions_dir = published_root / "fern" / "versions"
284+
for nav in sorted(path for path in versions_dir.glob("*.yml") if path.name != "latest.yml"):
285+
try:
286+
current_block = extract_navigation_section(nav, CODE_REFERENCE_SECTION_RE)
287+
except PublishedBranchError:
288+
continue
289+
page_root = code_reference_page_root(current_block)
290+
if page_root is None:
291+
continue
292+
sync_code_reference_pages(source_root, published_root, page_root)
293+
replace_navigation_section(
294+
nav, CODE_REFERENCE_SECTION_RE, rewrite_code_reference_block(source_block, page_root)
295+
)
296+
297+
171298
def sync_source(args: argparse.Namespace) -> int:
172299
source_root = Path(args.source_root)
173300
published_root = Path(args.published_root)
@@ -185,22 +312,14 @@ def sync_source(args: argparse.Namespace) -> int:
185312
merge_preserved_versions(
186313
source_root / "fern" / "versions", published_root / "fern" / "versions", preserved_versions
187314
)
315+
sync_code_reference_archive(source_root, published_root)
188316
restore_versions_block(published_root / "fern" / "docs.yml", preserved_versions_block)
189317
validate_redirect_targets(published_root)
190318
return 0
191319

192320

193321
def extract_devnotes_block(path: Path) -> list[str]:
194-
lines = path.read_text().splitlines(keepends=True)
195-
start = next((i for i, line in enumerate(lines) if DEVNOTES_SECTION_RE.match(line)), -1)
196-
if start == -1:
197-
raise PublishedBranchError(f"Dev Notes section not found in {path}")
198-
end = start + 1
199-
while end < len(lines):
200-
if lines[end].startswith(" - ") and lines[end].strip():
201-
break
202-
end += 1
203-
return lines[start:end]
322+
return extract_navigation_section(path, DEVNOTES_SECTION_RE)
204323

205324

206325
def rewrite_devnotes_block(source_root: Path, published_root: Path, block: list[str]) -> list[str]:
@@ -227,17 +346,7 @@ def rewrite_devnotes_block(source_root: Path, published_root: Path, block: list[
227346

228347

229348
def replace_devnotes_block(path: Path, block: list[str]) -> None:
230-
lines = path.read_text().splitlines(keepends=True)
231-
start = next((i for i, line in enumerate(lines) if DEVNOTES_SECTION_RE.match(line)), -1)
232-
if start == -1:
233-
raise PublishedBranchError(f"Dev Notes section not found in {path}")
234-
end = start + 1
235-
while end < len(lines):
236-
if lines[end].startswith(" - ") and lines[end].strip():
237-
break
238-
end += 1
239-
lines[start:end] = block
240-
path.write_text("".join(lines))
349+
replace_navigation_section(path, DEVNOTES_SECTION_RE, block)
241350

242351

243352
def patch_devnotes(args: argparse.Namespace) -> int:

0 commit comments

Comments
 (0)