Skip to content

Commit 435acc6

Browse files
committed
add documentation and ci check
1 parent a48e3dd commit 435acc6

File tree

10 files changed

+227
-7
lines changed

10 files changed

+227
-7
lines changed

.ci/lint-docs-source-page.sh

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#!/bin/bash
2+
set -e
3+
4+
python3 - << 'EOF'
5+
import os
6+
import re
7+
import sys
8+
from pathlib import Path
9+
10+
integration_dir = Path("./docs/en/integrations")
11+
if not integration_dir.exists():
12+
print("Info: Directory './docs/en/integrations' not found. Skipping linting.")
13+
sys.exit(0)
14+
15+
ALLOWED_ORDER = [
16+
"About",
17+
"Available Tools",
18+
"Requirements",
19+
"Example",
20+
"Reference",
21+
"Advanced Usage",
22+
"Troubleshooting",
23+
"Additional Resources"
24+
]
25+
REQUIRED = {"About", "Example", "Reference"}
26+
27+
# Regex to catch any variation of the list-tools shortcode, including parameters
28+
SHORTCODE_PATTERN = r"\{\{<\s*list-tools.*?>\}\}"
29+
30+
has_errors = False
31+
32+
# Find all _index.md files inside the subdirectories of integrations/
33+
for filepath in integration_dir.rglob("_index.md"):
34+
# Skip the top-level integrations/_index.md if it exists
35+
if filepath.parent == integration_dir:
36+
continue
37+
38+
with open(filepath, "r", encoding="utf-8") as f:
39+
content = f.read()
40+
41+
# Separate YAML frontmatter from the markdown body
42+
match = re.match(r'^\s*---\s*\n(.*?)\n---\s*(.*)', content, re.DOTALL)
43+
if match:
44+
frontmatter = match.group(1)
45+
body = match.group(2)
46+
else:
47+
frontmatter = ""
48+
body = content
49+
50+
# If the file has no markdown content (metadata placeholder only), skip it entirely
51+
if not body.strip():
52+
continue
53+
54+
file_errors = False
55+
56+
# 1. Check Frontmatter Title
57+
title_source = frontmatter if frontmatter else content
58+
title_match = re.search(r"^title:\s*[\"']?(.*?)[\"']?\s*$", title_source, re.MULTILINE)
59+
if not title_match or not title_match.group(1).strip().endswith("Source"):
60+
found_title = title_match.group(1) if title_match else "None"
61+
print(f"[{filepath}] Error: Frontmatter title must end with 'Source'. Found: '{found_title}'")
62+
file_errors = True
63+
64+
# 2. Check Shortcode Placement ONLY IF "Available Tools" heading is present
65+
tools_section_match = re.search(r"^##\s+Available Tools\s*(.*?)(?=^##\s|\Z)", body, re.MULTILINE | re.DOTALL)
66+
if tools_section_match:
67+
if not re.search(SHORTCODE_PATTERN, tools_section_match.group(1)):
68+
print(f"[{filepath}] Error: The list-tools shortcode must be placed under the '## Available Tools' heading.")
69+
file_errors = True
70+
else:
71+
# Prevent edge case where shortcode is used but the heading was forgotten
72+
if re.search(SHORTCODE_PATTERN, body):
73+
print(f"[{filepath}] Error: A list-tools shortcode was found, but the '## Available Tools' heading is missing.")
74+
file_errors = True
75+
76+
# 3. Strip code blocks from body to avoid linting example markdown headings
77+
clean_body = re.sub(r"```.*?```", "", body, flags=re.DOTALL)
78+
79+
# 4. Check H1 Headings
80+
if re.search(r"^#\s+\w+", clean_body, re.MULTILINE):
81+
print(f"[{filepath}] Error: H1 headings (#) are forbidden in the body.")
82+
file_errors = True
83+
84+
# 5. Check H2 Headings
85+
h2s = re.findall(r"^##\s+(.*)", clean_body, re.MULTILINE)
86+
h2s = [h2.strip() for h2 in h2s]
87+
88+
# Missing Required
89+
missing = REQUIRED - set(h2s)
90+
if missing:
91+
print(f"[{filepath}] Error: Missing required H2 headings: {missing}")
92+
file_errors = True
93+
94+
# Unauthorized Headings
95+
unauthorized = set(h2s) - set(ALLOWED_ORDER)
96+
if unauthorized:
97+
print(f"[{filepath}] Error: Unauthorized H2 headings found: {unauthorized}")
98+
file_errors = True
99+
100+
# Strict Ordering
101+
filtered_h2s = [h for h in h2s if h in ALLOWED_ORDER]
102+
expected_order = [h for h in ALLOWED_ORDER if h in h2s]
103+
if filtered_h2s != expected_order:
104+
print(f"[{filepath}] Error: Headings are out of order.")
105+
print(f" Expected: {expected_order}")
106+
print(f" Found: {filtered_h2s}")
107+
file_errors = True
108+
109+
if file_errors:
110+
has_errors = True
111+
112+
if has_errors:
113+
print("Linting failed. Please fix the structure errors above.")
114+
sys.exit(1)
115+
else:
116+
print("Success: All Source pages passed structure validation.")
117+
EOF

.github/workflows/docs_lint.yaml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
name: Lint Documentation
16+
17+
on:
18+
pull_request:
19+
paths:
20+
- 'integrations/**/*.md'
21+
- '.ci/lint-docs-source-page.sh'
22+
23+
jobs:
24+
lint-source-pages:
25+
name: Validate Source Page Structure
26+
runs-on: ubuntu-latest
27+
28+
steps:
29+
- name: Checkout repository
30+
uses: actions/checkout@v4
31+
32+
- name: Set up Python
33+
uses: actions/setup-python@v5
34+
with:
35+
python-version: '3.x'
36+
37+
- name: Check for large files in docs/
38+
run: |
39+
if [ -d "docs" ]; then
40+
LARGE_FILES=$(find docs/ -type f -size +24M)
41+
if [ -n "$LARGE_FILES" ]; then
42+
echo "Error: The following files in the docs/ directory exceed the 24MB size limit:"
43+
echo "$LARGE_FILES"
44+
exit 1
45+
else
46+
echo "Success: No files in docs/ exceed 24MB."
47+
fi
48+
else
49+
echo "Info: docs/ directory not found. Skipping file size check."
50+
fi
51+
52+
- name: Make script executable
53+
run: chmod +x .ci/lint-docs-source-page.sh
54+
55+
- name: Run Structure Linter for Source Pages
56+
run: .ci/lint-docs-source-page.sh

DEVELOPER.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,29 @@ tools.
229229
230230
* **Adding Top-Level Sections:** If you add a completely new top-level documentation directory (e.g., a new section alongside `integrations`, `user-guide`, etc.), you **must** update the AI documentation layout files located at `.hugo/layouts/index.llms.txt` and `.hugo/layouts/index.llms-full.txt`. Specifically, you need to update the "Diátaxis Narrative Framework" preamble in both files so that the AI models understand the purpose of your new section.
231231
232+
#### Integration Documentation Rules
233+
234+
When generating or editing documentation for this repository, you must strictly adhere to the following CI-enforced rules. Failure to do so will break the build.
235+
236+
##### Source Page Constraints (`integrations/**/_index.md`)
237+
238+
1. **Title Convention:** The YAML frontmatter `title` must always end with "Source" (e.g., `title: "Postgres Source"`).
239+
2. **No H1 Tags:** Never generate H1 (`#`) headings in the markdown body.
240+
3. **Strict H2 Ordering:** You must use the following H2 (`##`) headings in this exact sequence.
241+
* `## About` (Required)
242+
* `## Available Tools` (Optional)
243+
* `## Requirements` (Optional)
244+
* `## Example` (Required)
245+
* `## Reference` (Required)
246+
* `## Advanced Usage` (Optional)
247+
* `## Troubleshooting` (Optional)
248+
* `## Additional Resources` (Optional)
249+
4. **Shortcode Placement:** If you generate the `## Available Tools` section, you must include the `{{< list-tools >}}` shortcode beneath it.
250+
251+
##### Asset Constraints (`docs/`)
252+
253+
1. **File Size Limits:** Never add files larger than 24MB to the `docs/` directory.
254+
232255
#### Adding Prebuilt Tools
233256
234257
You can provide developers with a set of "build-time" tools to aid common

GEMINI.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,27 @@ There are 6 workflows in total, handling parallel deployments to both GitHub Pag
111111
- For a new source: Add source documentation to `docs/en/integrations/<source_name>/`. Be sure to include the `{{< list-tools >}}` shortcode on this page to dynamically display its available tools.
112112
- For a new tool: Add tool documentation to `docs/en/integrations/<source_name>/<tool_name>`. Be sure to include the `{{< compatible-sources >}}` shortcode on this page to list its supported data sources.
113113
- **New Top-Level Directories:** If adding a completely new top-level section to the documentation site, you must update the "Diátaxis Narrative Framework" section inside both `.hugo/layouts/index.llms.txt` and `.hugo/layouts/index.llms-full.txt` to keep the AI context synced with the site structure.
114+
115+
116+
#### Integration Documentation Rules
117+
118+
When generating or editing documentation for this repository, you must strictly adhere to the following CI-enforced rules. Failure to do so will break the build.
119+
120+
##### Source Page Constraints (`integrations/**/_index.md`)
121+
122+
1. **Title Convention:** The YAML frontmatter `title` must always end with "Source" (e.g., `title: "Postgres Source"`).
123+
2. **No H1 Tags:** Never generate H1 (`#`) headings in the markdown body.
124+
3. **Strict H2 Ordering:** You must use the following H2 (`##`) headings in this exact sequence.
125+
* `## About` (Required)
126+
* `## Available Tools` (Optional)
127+
* `## Requirements` (Optional)
128+
* `## Example` (Required)
129+
* `## Reference` (Required)
130+
* `## Advanced Usage` (Optional)
131+
* `## Troubleshooting` (Optional)
132+
* `## Additional Resources` (Optional)
133+
4. **Shortcode Placement:** If you generate the `## Available Tools` section, you must include the `{{< list-tools >}}` shortcode beneath it.
134+
135+
##### Asset Constraints (`docs/`)
136+
137+
1. **File Size Limits:** Never add files larger than 24MB to the `docs/` directory.

docs/en/integrations/alloydb/_index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: "AlloyDB for PostgreSQL"
2+
title: "AlloyDB for PostgreSQL Source"
33
linkTitle: "AlloyDB"
44
type: docs
55
weight: 1

docs/en/integrations/cloud-sql-mssql/_index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: "Cloud SQL for SQL Server"
2+
title: "Cloud SQL for SQL Server Source"
33
linkTitle: "Cloud SQL (SQL Server)"
44
type: docs
55
weight: 1

docs/en/integrations/cloud-sql-mysql/_index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: "Cloud SQL for MySQL"
2+
title: "Cloud SQL for MySQL Source"
33
linkTitle: "Cloud SQL (MySQL)"
44
type: docs
55
weight: 1

docs/en/integrations/cloud-sql-pg/_index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: "Cloud SQL for PostgreSQL"
2+
title: "Cloud SQL for PostgreSQL Source"
33
linkTitle: "Cloud SQL (Postgres)"
44
type: docs
55
weight: 1

docs/en/integrations/cloudgda/_index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: "Gemini Data Analytics"
2+
title: "Gemini Data Analytics Source"
33
type: docs
44
weight: 1
55
description: >
@@ -18,7 +18,7 @@ Authentication can be handled in two ways:
1818

1919
## Available Tools
2020

21-
{{< list-tools>}}
21+
{{< list-tools >}}
2222

2323
## Example
2424

docs/en/integrations/mariadb/_index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: "MariaDB"
2+
title: "MariaDB Source"
33
type: docs
44
weight: 1
55
description: >

0 commit comments

Comments
 (0)