Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Docs] Generate initial mkdocs site with static pages #1108

Merged
Merged
Show file tree
Hide file tree
Changes from 15 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
105 changes: 28 additions & 77 deletions .github/workflows/deploy-website.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
name: Deploy Redirect Page
name: Deploy MkDocs to GitHub Pages

on:
push:
branches: [main]
paths:
- "autogen/**"
- "website/**"
- ".github/workflows/deploy-website.yml"
- ".github/workflows/docs-check-broken-links.yml"
- "scripts/broken-links-check.sh"
- "scripts/docs_build_mkdocs.sh"
- "scripts/docs_serve_mkdocs.sh"
- ".muffet-excluded-links.txt"
workflow_dispatch:
permissions:
contents: write
Expand All @@ -15,90 +22,34 @@
# Step 1: Check out the repository
- name: Checkout repository
uses: actions/checkout@v4
with:
lfs: true
fetch-depth: 0

# Step 2: Create redirect HTML files
- name: Create redirect HTML
run: |
mkdir -p dist
# Homepage redirect
cat > dist/index.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0; url=https://docs.ag2.ai">
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-M24T8371GM"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-M24T8371GM');
</script>
<script>
setTimeout(function() {
window.location.href = "https://docs.ag2.ai";
}, 500);
</script>
<title>Page Redirection</title>
</head>
<body>
If you are not redirected automatically, follow this <a href="https://docs.ag2.ai">link to the new documentation</a>.
</body>
</html>
EOF

# Deep link handling
cat > dist/404.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-M24T8371GM"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-M24T8371GM');
</script>
<script>
const newDomain = 'https://docs.ag2.ai';
let path = window.location.pathname;
const hash = window.location.hash;

// Remove /ag2/ prefix and trailing slash
path = path.replace(/^\/ag2\//, '/').replace(/\/$/, "");

// Transform blog and talks URLs
if (path.includes('/blog/') || path.includes('/talks/')) {
const afterPrefix = path.split(/\/(blog|talks)\//)[2];
const transformed = afterPrefix.replace(/\//g, '-');
path = path.replace(afterPrefix, transformed);
}

// Handle -index and create final URL with hash
const redirectUrl = (newDomain + path).replace(/-index$/, "/index") + hash;
- uses: astral-sh/setup-uv@v5

Check warning

Code scanning / CodeQL

Unpinned tag for a non-immutable Action in workflow Medium

Unpinned 3rd party Action 'Deploy MkDocs to GitHub Pages' step
Uses Step
uses 'astral-sh/setup-uv' with ref 'v5', not a pinned commit hash
with:
version: "latest"
- uses: actions/setup-python@v5
with:
python-version: 3.x

setTimeout(function() {
window.location.href = redirectUrl;
}, 500);
</script>
<title>Page Redirection</title>
</head>
<body>
If you are not redirected automatically, follow this <a href="https://docs.ag2.ai">link to the new documentation</a>.
</body>
</html>
EOF
# Step 3: Build MkDocs
- name: Build documentation
run: |
uv venv
. .venv/bin/activate
uv pip install -e ".[docs]"
./scripts/docs_build_mkdocs.sh
ls -la ./website/mkdocs/site
working-directory: .

# Step 3: Deploy to gh-pages branch
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./dist
publish_dir: ./website/mkdocs/site
force_orphan: true
user_name: 'github-actions[bot]'
user_email: 'github-actions[bot]@users.noreply.github.com'
commit_message: 'Deploy redirect page'
commit_message: 'Deploy MkDocs site to GitHub pages'
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ repos:
- id: check-added-large-files
- id: check-ast
- id: check-yaml
exclude: 'website/mkdocs/mkdocs.yml'
- id: check-toml
- id: check-json
exclude: |
Expand Down
26 changes: 13 additions & 13 deletions .secrets.baseline
Original file line number Diff line number Diff line change
Expand Up @@ -1197,72 +1197,72 @@
"is_secret": false
}
],
"website/_blogs/2023-07-14-Local-LLMs/index.mdx": [
"website/docs/_blogs/2023-07-14-Local-LLMs/index.mdx": [
{
"type": "Secret Keyword",
"filename": "website/_blogs/2023-07-14-Local-LLMs/index.mdx",
"filename": "website/docs/_blogs/2023-07-14-Local-LLMs/index.mdx",
"hashed_secret": "eef19c54306daa69eda49c0272623bdb5e2b341f",
"is_verified": false,
"line_number": 83,
"is_secret": false
}
],
"website/_blogs/2023-10-18-RetrieveChat/index.mdx": [
"website/docs/_blogs/2023-10-18-RetrieveChat/index.mdx": [
{
"type": "Secret Keyword",
"filename": "website/_blogs/2023-10-18-RetrieveChat/index.mdx",
"filename": "website/docs/_blogs/2023-10-18-RetrieveChat/index.mdx",
"hashed_secret": "1e3667aaaaa887721550cf5cc8a0c5c5760810ed",
"is_verified": false,
"line_number": 155,
"is_secret": false
}
],
"website/_blogs/2023-11-26-Agent-AutoBuild/index.mdx": [
"website/docs/_blogs/2023-11-26-Agent-AutoBuild/index.mdx": [
{
"type": "Base64 High Entropy String",
"filename": "website/_blogs/2023-11-26-Agent-AutoBuild/index.mdx",
"filename": "website/docs/_blogs/2023-11-26-Agent-AutoBuild/index.mdx",
"hashed_secret": "76c1da7555caa23cfbad2a5a25caa463e8a81b30",
"is_verified": false,
"line_number": 46,
"is_secret": false
},
{
"type": "Secret Keyword",
"filename": "website/_blogs/2023-11-26-Agent-AutoBuild/index.mdx",
"filename": "website/docs/_blogs/2023-11-26-Agent-AutoBuild/index.mdx",
"hashed_secret": "5bc604777adb22ae457a5473c397202e00689e23",
"is_verified": false,
"line_number": 174,
"is_secret": false
}
],
"website/_blogs/2024-06-24-AltModels-Classes/index.mdx": [
"website/docs/_blogs/2024-06-24-AltModels-Classes/index.mdx": [
{
"type": "Secret Keyword",
"filename": "website/_blogs/2024-06-24-AltModels-Classes/index.mdx",
"filename": "website/docs/_blogs/2024-06-24-AltModels-Classes/index.mdx",
"hashed_secret": "7d80773e54e49b5cc36d28a4a7baf9fff268491e",
"is_verified": false,
"line_number": 89,
"is_secret": false
},
{
"type": "Secret Keyword",
"filename": "website/_blogs/2024-06-24-AltModels-Classes/index.mdx",
"filename": "website/docs/_blogs/2024-06-24-AltModels-Classes/index.mdx",
"hashed_secret": "5f7eb17aeb646b3cda7af7c395fc9e291c62273e",
"is_verified": false,
"line_number": 94,
"is_secret": false
},
{
"type": "Secret Keyword",
"filename": "website/_blogs/2024-06-24-AltModels-Classes/index.mdx",
"filename": "website/docs/_blogs/2024-06-24-AltModels-Classes/index.mdx",
"hashed_secret": "9918921badd6ab97ccf2355ca3dda0fefbf79d03",
"is_verified": false,
"line_number": 99,
"is_secret": false
},
{
"type": "Secret Keyword",
"filename": "website/_blogs/2024-06-24-AltModels-Classes/index.mdx",
"filename": "website/docs/_blogs/2024-06-24-AltModels-Classes/index.mdx",
"hashed_secret": "2da1eec5f20a39f9f7b1449ea3f3ad0abc84642c",
"is_verified": false,
"line_number": 104,
Expand Down Expand Up @@ -1541,7 +1541,7 @@
"filename": "website/docs/user-guide/models/vLLM.mdx",
"hashed_secret": "aa572c1a118d643cfe6442e2ab56f7f3bb5d63d2",
"is_verified": false,
"line_number": 90,
"line_number": 91,
"is_secret": false
}
],
Expand Down
157 changes: 157 additions & 0 deletions autogen/_website/generate_mkdocs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# Copyright (c) 2023 - 2025, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors
#
# SPDX-License-Identifier: Apache-2.0


import json
import re
import shutil
from pathlib import Path

from ..import_utils import optional_import_block, require_optional_import
from .utils import NavigationGroup, copy_files, get_git_tracked_and_untracked_files_in_directory

with optional_import_block():
from jinja2 import Template


def filter_excluded_files(files: list[Path], exclusion_list: list[str], website_dir: Path) -> list[Path]:
return [
file
for file in files
if not any(str(file.relative_to(website_dir)).startswith(excl) for excl in exclusion_list)
]


def copy_file(file: Path, mkdocs_output_dir: Path) -> None:
dest = mkdocs_output_dir / file.relative_to(file.parents[1])
dest.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(file, dest)


def transform_content_for_mkdocs(content: str) -> str:
# Transform admonitions (Tip, Warning, Note)
tag_mappings = {
"Tip": "tip",
"Warning": "warning",
"Note": "note",
"Danger": "danger",
}
for html_tag, mkdocs_type in tag_mappings.items():
pattern = f"<{html_tag}>(.*?)</{html_tag}>"

def replacement(match):
inner_content = match.group(1).strip()
return f"!!! {mkdocs_type}\n {inner_content}"

content = re.sub(pattern, replacement, content, flags=re.DOTALL)

# Clean up style tags with double curly braces
style_pattern = r"style\s*=\s*{{\s*([^}]+)\s*}}"

def style_replacement(match):
style_content = match.group(1).strip()
return f"style={{ {style_content} }}"

content = re.sub(style_pattern, style_replacement, content)

return content


def process_and_copy_files(input_dir: Path, output_dir: Path, files: list[Path]) -> None:
for file in files:
if file.suffix == ".mdx":
content = file.read_text()
processed_content = transform_content_for_mkdocs(content)
dest = output_dir / file.relative_to(input_dir).with_suffix(".md")
dest.parent.mkdir(parents=True, exist_ok=True)
dest.write_text(processed_content)
else:
copy_files(input_dir, output_dir, [file])
# copy_file(file, output_dir)


def format_title(title: str, keywords: dict[str, str]) -> str:
"""Format a page title with proper capitalization for special keywords."""
words = title.replace("-", " ").title().split()
return " ".join(keywords.get(word, word) for word in words)


def format_page_entry(page_path: str, indent: str, keywords: dict[str, str]) -> str:
"""Format a single page entry as either a parenthesized path or a markdown link."""
path = f"{page_path}.md"
title = format_title(Path(page_path).name, keywords)
return f"{indent} - [{title}]({path})"


def format_navigation(nav: list[NavigationGroup], depth: int = 0, keywords: dict[str, str] = None) -> str:
"""
Recursively format navigation structure into markdown-style nested list.

Args:
nav: List of navigation items with groups and pages
depth: Current indentation depth
keywords: Dictionary of special case word capitalizations

Returns:
Formatted navigation as a string
"""
if keywords is None:
keywords = {
"Ag2": "AG2",
"Rag": "RAG",
"Llm": "LLM",
}

indent = " " * depth
result = []

for item in nav:
# Add group header
result.append(f"{indent}- {item['group']}")

# Process each page
for page in item["pages"]:
if isinstance(page, dict):
# Handle nested navigation groups
result.append(format_navigation([page], depth + 1, keywords))
else:
# Handle individual pages
result.append(format_page_entry(page, indent, keywords))

return "\n".join(result)


@require_optional_import("jinja2", "docs")
def generate_mkdocs_navigation(website_dir: Path, mkdocs_root_dir: Path, nav_exclusions: list[str]) -> None:
mintlify_nav_template_path = website_dir / "mint-json-template.json.jinja"
mkdocs_nav_path = mkdocs_root_dir / "docs" / "navigation_template.txt"
summary_md_path = mkdocs_root_dir / "docs" / "SUMMARY.md"

mintlify_json = json.loads(Template(mintlify_nav_template_path.read_text(encoding="utf-8")).render())
mintlify_nav = mintlify_json["navigation"]
filtered_nav = [item for item in mintlify_nav if item["group"] not in nav_exclusions]

mkdocs_nav_content = "---\nsearch:\n exclude: true\n---\n" + format_navigation(filtered_nav) + "\n"
mkdocs_nav_path.write_text(mkdocs_nav_content)
summary_md_path.write_text(mkdocs_nav_content)


def main() -> None:
root_dir = Path(__file__).resolve().parents[2]
website_dir = root_dir / "website"

mint_inpur_dir = website_dir / "docs"

mkdocs_root_dir = website_dir / "mkdocs"
mkdocs_output_dir = mkdocs_root_dir / "docs" / "docs"

exclusion_list = ["docs/_blogs", "docs/home", "docs/.gitignore", "docs/use-cases"]
nav_exclusions = ["Use Cases"]

files_to_copy = get_git_tracked_and_untracked_files_in_directory(mint_inpur_dir)
filtered_files = filter_excluded_files(files_to_copy, exclusion_list, website_dir)

process_and_copy_files(mint_inpur_dir, mkdocs_output_dir, filtered_files)

generate_mkdocs_navigation(website_dir, mkdocs_root_dir, nav_exclusions)
Loading
Loading