Skip to content

chore: add docs update status and health check #499

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

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
57 changes: 57 additions & 0 deletions .github/workflows/docs-health-check.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Check Docs Health

on:
schedule:
- cron: '0 0 * * 0' # Run every Sunday at midnight
workflow_dispatch:

jobs:
health-check:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'

- name: Run health check script
id: health_check
continue-on-error: true
run: python scripts/check_docs_health.py

- name: Check for changes
id: git_status
run: |
if git diff --quiet -- docs_health_report.md; then
echo "No changes to report."
echo "changed=false" >> $GITHUB_OUTPUT
else
echo "Health report or badge has changed."
echo "changed=true" >> $GITHUB_OUTPUT
fi

- name: Create Pull Request with changes
if: steps.git_status.outputs.changed == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
BRANCH_NAME="docs-health/update-${{ github.run_id }}"
git checkout -b $BRANCH_NAME
git add docs_health_report.md
git commit -m "docs: Update health report"
git push origin $BRANCH_NAME
gh pr create \
--base "main" \
--head "$BRANCH_NAME" \
--title "docs: Update health report (${{ github.run_id }})" \
--body "Automated documentation health report update. Please review and merge."
50 changes: 50 additions & 0 deletions .github/workflows/version-check.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Generate Version Report

on:
pull_request:
types: [opened, synchronize]
branches:
- main
workflow_dispatch:

jobs:
version-check:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'

- name: Install dependencies
run: pip install -r requirements.txt

- name: Generate version report
run: python scripts/generate_version_report.py

- name: Check for changes
id: git_status
run: |
if git diff --quiet -- docs_health_report.md; then
echo "No changes to report."
echo "changed=false" >> $GITHUB_OUTPUT
else
echo "Version report has changed."
echo "changed=true" >> $GITHUB_OUTPUT
fi

- name: Commit and push changes
if: steps.git_status.outputs.changed == 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add docs_health_report.md
git commit -m "docs: Update version report"
git push
4 changes: 4 additions & 0 deletions docs/agents/index.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
---
version: 1.0.0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this going to be visible on the page? Also: because this number looks like a version number for ADK, I think at least some people are going to assume that it corresponds with a ADK version number, in which case this makes it look like the docs are way out of date. Maybe it's better to display a last updated date and keep the version as hidden metadata?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, the version is a bit confusing.

---

# Agents

In the Agent Development Kit (ADK), an **Agent** is a self-contained execution unit designed to act autonomously to achieve specific goals. Agents can perform tasks, interact with users, utilize external tools, and coordinate with other agents.
Expand Down
112 changes: 112 additions & 0 deletions docs_health_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<!-- BEGIN_DOCS_HEALTH_REPORT -->
# Sample Documentation Health Report
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to call this "Sample" since we are publishing this? Maybe date it?

Documentation Health Report - July 2025


**Summary:** **85.7%** of documentation pages were updated in the last 4 weeks. A total of **3** page(s) are considered stale (older than 90 days).

## Detailed Health by Section

### Root - ✅ Healthy
All 2 page(s) in this section are up-to-date.

### agents - ⚠️ Needs Review
2 of 5 page(s) in this section are stale:

- **docs/agents/custom-agents.md**: Last updated 215 days ago
- **docs/agents/multi-agents.md**: Last updated 198 days ago

### api-reference - ✅ Healthy
All 3 page(s) in this section are up-to-date.

### get-started - ⚠️ Needs Review
1 of 4 page(s) in this section are stale:

- **docs/get-started/quickstart.md**: Last updated 301 days ago

### tools - ✅ Healthy
All 10 page(s) in this section are up-to-date.
<!-- END_DOCS_HEALTH_REPORT -->


<!-- BEGIN_VERSION_REPORT -->
# Documentation Version Report

This report provides a summary of the versions of the documentation pages.

## Version 1.0.0

Found 1 pages with this version:

- [docs/agents/index.md](agents/index.html)

## Pages without Version Information

Found 66 pages without version metadata:

- [docs/index.md](index.html)
- [docs/community.md](community.html)
- [docs/contributing-guide.md](contributing-guide.html)
- [docs/mcp/index.md](mcp/index.html)
- [docs/api-reference/index.md](api-reference/index.html)
- [docs/api-reference/java/legal/jquery.md](api-reference/java/legal/jquery.html)
- [docs/api-reference/java/legal/dejavufonts.md](api-reference/java/legal/dejavufonts.html)
- [docs/api-reference/java/legal/jqueryUI.md](api-reference/java/legal/jqueryUI.html)
- [docs/runtime/index.md](runtime/index.html)
- [docs/runtime/runconfig.md](runtime/runconfig.html)
- [docs/safety/index.md](safety/index.html)
- [docs/evaluate/index.md](evaluate/index.html)
- [docs/deploy/agent-engine.md](deploy/agent-engine.html)
- [docs/deploy/index.md](deploy/index.html)
- [docs/deploy/gke.md](deploy/gke.html)
- [docs/deploy/cloud-run.md](deploy/cloud-run.html)
- [docs/grounding/vertex_ai_search_grounding.md](grounding/vertex_ai_search_grounding.html)
- [docs/grounding/google_search_grounding.md](grounding/google_search_grounding.html)
- [docs/artifacts/index.md](artifacts/index.html)
- [docs/get-started/index.md](get-started/index.html)
- [docs/get-started/installation.md](get-started/installation.html)
- [docs/get-started/about.md](get-started/about.html)
- [docs/get-started/testing.md](get-started/testing.html)
- [docs/get-started/quickstart.md](get-started/quickstart.html)
- [docs/get-started/streaming/index.md](get-started/streaming/index.html)
- [docs/get-started/streaming/quickstart-streaming-java.md](get-started/streaming/quickstart-streaming-java.html)
- [docs/get-started/streaming/quickstart-streaming.md](get-started/streaming/quickstart-streaming.html)
- [docs/streaming/index.md](streaming/index.html)
- [docs/streaming/streaming-tools.md](streaming/streaming-tools.html)
- [docs/streaming/custom-streaming.md](streaming/custom-streaming.html)
- [docs/streaming/custom-streaming-ws.md](streaming/custom-streaming-ws.html)
- [docs/streaming/configuration.md](streaming/configuration.html)
- [docs/streaming/dev-guide/part1.md](streaming/dev-guide/part1.html)
- [docs/tutorials/index.md](tutorials/index.html)
- [docs/tutorials/agent-team.md](tutorials/agent-team.html)
- [docs/observability/weave.md](observability/weave.html)
- [docs/observability/agentops.md](observability/agentops.html)
- [docs/observability/arize-ax.md](observability/arize-ax.html)
- [docs/observability/phoenix.md](observability/phoenix.html)
- [docs/observability/logging.md](observability/logging.html)
- [docs/tools/function-tools.md](tools/function-tools.html)
- [docs/tools/index.md](tools/index.html)
- [docs/tools/third-party-tools.md](tools/third-party-tools.html)
- [docs/tools/google-cloud-tools.md](tools/google-cloud-tools.html)
- [docs/tools/built-in-tools.md](tools/built-in-tools.html)
- [docs/tools/openapi-tools.md](tools/openapi-tools.html)
- [docs/tools/mcp-tools.md](tools/mcp-tools.html)
- [docs/tools/authentication.md](tools/authentication.html)
- [docs/agents/multi-agents.md](agents/multi-agents.html)
- [docs/agents/custom-agents.md](agents/custom-agents.html)
- [docs/agents/llm-agents.md](agents/llm-agents.html)
- [docs/agents/models.md](agents/models.html)
- [docs/agents/workflow-agents/index.md](agents/workflow-agents/index.html)
- [docs/agents/workflow-agents/sequential-agents.md](agents/workflow-agents/sequential-agents.html)
- [docs/agents/workflow-agents/parallel-agents.md](agents/workflow-agents/parallel-agents.html)
- [docs/agents/workflow-agents/loop-agents.md](agents/workflow-agents/loop-agents.html)
- [docs/context/index.md](context/index.html)
- [docs/events/index.md](events/index.html)
- [docs/callbacks/index.md](callbacks/index.html)
- [docs/callbacks/design-patterns-and-best-practices.md](callbacks/design-patterns-and-best-practices.html)
- [docs/callbacks/types-of-callbacks.md](callbacks/types-of-callbacks.html)
- [docs/sessions/index.md](sessions/index.html)
- [docs/sessions/memory.md](sessions/memory.html)
- [docs/sessions/express-mode.md](sessions/express-mode.html)
- [docs/sessions/state.md](sessions/state.html)
- [docs/sessions/session.md](sessions/session.html)

<!-- END_VERSION_REPORT -->
13 changes: 13 additions & 0 deletions overrides/main.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,16 @@
<meta name="twitter:description" content="Build powerful multi-agent systems with Agent Development Kit">
<meta name="twitter:image" content="https://google.github.io/adk-docs/assets/adk-social-card.png">
{% endblock %}


{% block content %}
{{ super() }}

{% if page.meta.version %}
<div style="display: flex; justify-content: flex-end; margin-top: 2em;">
<div class="md-source-date">
<small>Version: {{ page.meta.version }}</small>
</div>
</div>
{% endif %}
{% endblock %}
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
mkdocs-material==9.6.14
mkdocs-redirects==1.2.2
mkdocs-linkcheck==1.0.6
mkdocs-linkcheck==1.0.6
140 changes: 140 additions & 0 deletions scripts/check_docs_health.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import re
import subprocess
from datetime import datetime, timedelta

# Configuration
STALENESS_THRESHOLD_DAYS = 90
RECENT_UPDATE_THRESHOLD_WEEKS = 4
DOCS_DIRECTORY = "docs"
REPORT_FILENAME = "docs_health_report.md"
HEALTH_REPORT_START_MARKER = "<!-- BEGIN_DOCS_HEALTH_REPORT -->"
HEALTH_REPORT_END_MARKER = "<!-- END_DOCS_HEALTH_REPORT -->"

def get_last_commit_date(file_path):
"""Gets the last commit date of a file."""
try:
commit_date_str = subprocess.check_output(
["git", "log", "-1", "--format=%ci", file_path]
).decode("utf-8").strip()
return datetime.strptime(commit_date_str, "%Y-%m-%d %H:%M:%S %z")
except subprocess.CalledProcessError:
return None

def check_docs_health():
"""Checks the health of the documentation and generates a detailed report."""
section_stats = {}
recently_updated_count = 0
total_docs_count = 0
total_stale_files = 0
now = datetime.now(datetime.now().astimezone().tzinfo)
staleness_threshold = now - timedelta(days=STALENESS_THRESHOLD_DAYS)
recent_update_threshold = now - timedelta(weeks=RECENT_UPDATE_THRESHOLD_WEEKS)

# Change to the adk-docs directory
original_cwd = os.getcwd()
script_dir = os.path.dirname(os.path.abspath(__file__))
adk_docs_root = os.path.abspath(os.path.join(script_dir, ".."))
os.chdir(adk_docs_root)

for root, _, files in os.walk(DOCS_DIRECTORY):
for file in files:
if file.endswith(".md"):
total_docs_count += 1
file_path = os.path.join(root, file)
section = os.path.basename(root) if root != DOCS_DIRECTORY else "Root"

section_stats.setdefault(section, {
"total_files": 0,
"stale_files": []
})
section_stats[section]["total_files"] += 1

last_commit_date = get_last_commit_date(file_path)

if last_commit_date:
if last_commit_date < staleness_threshold:
section_stats[section]["stale_files"].append(
(file_path, now - last_commit_date)
)
total_stale_files += 1

if last_commit_date > recent_update_threshold:
recently_updated_count += 1

# Revert to original working directory
os.chdir(original_cwd)

recent_percentage = (recently_updated_count / total_docs_count * 100) if total_docs_count > 0 else 0
exit_code = 0 if total_stale_files == 0 else 1

# --- Generate the new report content ---
report_parts = [f"{HEALTH_REPORT_START_MARKER}\n"]
report_parts.append("# Documentation Health Report\n\n")
report_parts.append(f"**Summary:** **{recent_percentage:.1f}%** of documentation pages were updated in the last "
f"{RECENT_UPDATE_THRESHOLD_WEEKS} weeks. ")
report_parts.append(f"A total of **{total_stale_files}** page(s) are considered stale (older than {STALENESS_THRESHOLD_DAYS} days).\n\n")

if total_stale_files == 0:
report_parts.append("**All documentation is up-to-date!**\n")
else:
report_parts.append("## Detailed Health by Section\n\n")
for section, stats in sorted(section_stats.items()):
stale_count = len(stats["stale_files"])
if stale_count == 0:
report_parts.append(f"### {section} - ✅ Healthy\n")
report_parts.append(f"All {stats['total_files']} page(s) in this section are up-to-date.\n\n")
else:
report_parts.append(f"### {section} - ⚠️ Needs Review\n")
report_parts.append(f"{stale_count} of {stats['total_files']} page(s) in this section are stale:\n\n")
for file_path, days_since_update in stats["stale_files"]:
report_parts.append(f"- **{file_path}**: Last updated {days_since_update.days} days ago\n")
report_parts.append("\n")
report_parts.append(HEALTH_REPORT_END_MARKER)

report_content = "".join(report_parts)

# --- Read the existing report and replace the health section ---
report_path = os.path.join(adk_docs_root, REPORT_FILENAME)
try:
with open(report_path, "r") as f:
existing_content = f.read()
except FileNotFoundError:
existing_content = ""

pattern = re.compile(f"{HEALTH_REPORT_START_MARKER}.*{HEALTH_REPORT_END_MARKER}", re.DOTALL)

if pattern.search(existing_content):
new_full_content = pattern.sub(report_content, existing_content)
else:
new_full_content = existing_content + "\n\n" + report_content

with open(report_path, "w") as f:
f.write(new_full_content)

print("Docs Health Analysis done.")
# (Not used currently) Set outputs for GitHub Actions
# To be used if SVG badge is to be displayed in README.
if 'GITHUB_OUTPUT' in os.environ:
with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
f.write(f"recent_percentage={recent_percentage:.1f}\n")
f.write(f"exit_code={exit_code}\n")

return exit_code

if __name__ == "__main__":
exit(check_docs_health())
Loading