Skip to content

Commit d809b29

Browse files
committed
feat: add auto modification to docs files on reference frontmatter changes
this is a stage one for the pipeline of modifying underlying docs files after a detected change in the frontmatter in stage one, the diff is only sent to slack and is not actually applied please note that this will require the addition of claude and api keys to the ci workflow requirements claude (Claude Code CLI) - needs to be installed in CI runner ANTHROPIC_API_KEY SLACK_BOT_TOKEN
1 parent 14cd506 commit d809b29

File tree

7 files changed

+970
-0
lines changed

7 files changed

+970
-0
lines changed

.github/workflows/ci3.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ jobs:
7373
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
7474
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
7575
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
76+
# For automatic documentation updates via Claude Code
77+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
7678
# Nightly test env vars.
7779
EXTERNAL_ETHEREUM_HOSTS: "https://json-rpc.${{ secrets.GCP_SEPOLIA_URL }}?key=${{ secrets.GCP_SEPOLIA_API_KEY }},${{ secrets.INFURA_SEPOLIA_URL }}"
7880
EXTERNAL_ETHEREUM_CONSENSUS_HOST: "https://beacon.${{ secrets.GCP_SEPOLIA_URL }}"

docs/README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,57 @@ When documentation references source code files, the CI system can automatically
487487
- For individual files: `"path/to/file.ts"`
488488
- For directories (all files within): `"path/to/directory/*"`
489489

490+
### Automatic Documentation Update Notifications
491+
492+
Building on the DevRel review automation, the docs CI can analyze PRs and notify the team when documentation updates may be needed.
493+
494+
**How it works:**
495+
496+
1. **Detection**: When a PR modifies files that are referenced in documentation (via the `references` frontmatter), the system detects which docs may need updates.
497+
498+
2. **AI Analysis**: The system uses Claude Code to:
499+
- Analyze the code changes (diffs) in the PR
500+
- Review the affected documentation files
501+
- Determine if documentation updates are needed
502+
- Generate suggested documentation changes
503+
504+
3. **Slack Notification**: If documentation updates are suggested:
505+
- A message is sent to the configured Slack channel (default: `#devrel`)
506+
- The message includes the PR details, affected docs, and suggested changes
507+
- The DevRel team can review and apply the changes manually
508+
509+
**Requirements**:
510+
- `ANTHROPIC_API_KEY` must be set in CI secrets
511+
- `SLACK_BOT_TOKEN` must be set for Slack notifications
512+
- Claude Code CLI must be installed (`@anthropic-ai/claude-code`)
513+
- The PR must not be a draft
514+
515+
**Environment Variables**:
516+
- `SLACK_DOC_UPDATE_CHANNEL` - Slack channel for notifications (default: `#devrel`)
517+
- `DRY_RUN=1` - Skip Slack notification, just print what would be sent
518+
519+
**Implementation**: The automation is handled by `scripts/update_doc_references.sh`, which runs as part of the docs CI pipeline after `check_doc_references.sh`.
520+
521+
**Script Architecture**:
522+
- `scripts/update_doc_references.sh` - Main script that orchestrates the workflow
523+
- `scripts/lib/extract_doc_references.sh` - Shared library for parsing frontmatter references
524+
- `scripts/lib/create_doc_update_pr.sh` - (Reserved for future use) PR creation logic
525+
- `scripts/test_update_doc_references.sh` - Local testing helper
526+
527+
**Local Testing**:
528+
```bash
529+
# Find a PR with referenced file changes and test
530+
./scripts/test_update_doc_references.sh
531+
532+
# Test against a specific PR
533+
LOCAL_TEST=1 DRY_RUN=1 ./scripts/update_doc_references.sh 19803
534+
```
535+
536+
**Limitations**:
537+
- Only analyzes documentation in the source folders (`docs-developers/`, `docs-network/`), not versioned docs
538+
- Suggested changes should always be reviewed by a human before applying
539+
- The AI may occasionally suggest unnecessary or incorrect changes
540+
490541
## Contributing
491542

492543
We welcome contributions from the community. Please review our [contribution guidelines](CONTRIBUTING.md) for more information.

docs/bootstrap.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,17 @@ function check_references {
5555
./scripts/check_doc_references.sh docs
5656
}
5757

58+
function update_doc_references {
59+
echo_header "Auto-update doc references"
60+
# Only run if Claude Code CLI is available
61+
if command -v claude &> /dev/null; then
62+
./scripts/update_doc_references.sh docs
63+
else
64+
echo "Claude Code CLI not available. Skipping automatic doc updates."
65+
echo "To enable automatic doc updates, install Claude Code: npm install -g @anthropic-ai/claude-code"
66+
fi
67+
}
68+
5869
function build_examples {
5970
echo_header "Building examples"
6071
(cd examples && ./bootstrap.sh "$@")
@@ -66,6 +77,7 @@ case "$cmd" in
6677
build_docs
6778
test
6879
check_references
80+
update_doc_references
6981
;;
7082
"")
7183
build_examples
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
#!/usr/bin/env bash
2+
# create_doc_update_pr.sh - Create a PR with documentation updates
3+
#
4+
# This script is extracted from update_doc_references.sh for future use.
5+
# It handles:
6+
# - Creating a branch for doc updates
7+
# - Committing changes
8+
# - Pushing to remote
9+
# - Creating/updating a PR
10+
# - Commenting on the original PR
11+
#
12+
# Usage: source this file and call create_doc_update_pr
13+
#
14+
# Required variables (must be set before calling):
15+
# PR_NUMBER - Original PR number
16+
# PR_TITLE - Original PR title
17+
# PR_URL - Original PR URL
18+
# PR_AUTHOR - Original PR author
19+
# BASE_BRANCH - Base branch name
20+
# MODIFIED_FILES - List of modified doc files
21+
# FILE_TO_DOCS_MAP - Associative array of source files to doc files
22+
#
23+
# Optional variables:
24+
# DRY_RUN - Set to 1 to skip PR creation
25+
26+
create_doc_update_pr() {
27+
local pr_number="$1"
28+
local pr_title="$2"
29+
local pr_url="$3"
30+
local pr_author="$4"
31+
local base_branch="$5"
32+
local modified_files="$6"
33+
34+
local doc_update_branch="docs/auto-update-pr-${pr_number}"
35+
local original_ref=$(git rev-parse HEAD)
36+
local original_branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
37+
38+
echo "Creating documentation update branch: $doc_update_branch"
39+
40+
# Configure git for CI (needed for commits)
41+
git config user.email "[email protected]"
42+
git config user.name "Aztec Bot"
43+
44+
# Check if branch already exists on remote (from a previous run)
45+
if git ls-remote --exit-code --heads origin "$doc_update_branch" &>/dev/null; then
46+
echo "Remote branch $doc_update_branch already exists. Will update it..."
47+
git fetch origin "$doc_update_branch" 2>/dev/null || true
48+
fi
49+
50+
# Delete local branch if it exists
51+
git branch -D "$doc_update_branch" 2>/dev/null || true
52+
53+
# Create the new branch from the base branch
54+
git checkout -b "$doc_update_branch" "origin/$base_branch"
55+
56+
# Stage and commit the changes
57+
echo "Committing documentation updates..."
58+
59+
git add -A
60+
git commit -m "docs: auto-update documentation for PR #$pr_number
61+
62+
This commit contains automatic documentation updates generated by Claude Code
63+
in response to code changes in PR #$pr_number.
64+
65+
Original PR: $pr_url
66+
67+
The following documentation files were updated based on changes to their
68+
referenced source files:
69+
70+
$(echo "$modified_files" | sed 's/^/- /')
71+
72+
Co-Authored-By: Claude Opus 4.5 <[email protected]>"
73+
74+
# Check for dry run mode
75+
if [[ "${DRY_RUN:-0}" == "1" ]]; then
76+
echo ""
77+
echo "DRY_RUN mode: Skipping PR creation."
78+
echo "Changes committed to branch: $doc_update_branch"
79+
git checkout "$original_ref" 2>/dev/null || git checkout "$original_branch" 2>/dev/null || true
80+
return 0
81+
fi
82+
83+
# Push the branch
84+
echo "Pushing branch to origin..."
85+
git push -u origin "$doc_update_branch" --force
86+
87+
# Create the PR
88+
echo "Creating pull request..."
89+
90+
local pr_body=$(cat << EOF
91+
## Summary
92+
93+
This PR contains automatic documentation updates generated by Claude Code in response to code changes.
94+
95+
### Original PR
96+
- **PR:** #$pr_number
97+
- **Title:** $pr_title
98+
- **Author:** @$pr_author
99+
- **URL:** $pr_url
100+
101+
### Documentation Updates
102+
103+
The following documentation files were updated based on changes to their referenced source files:
104+
105+
$(echo "$modified_files" | sed 's/^/- `/' | sed 's/$/`/')
106+
107+
---
108+
109+
> **Note:** This PR was automatically generated. Please review the changes carefully before merging.
110+
111+
Generated with [Claude Code](https://claude.ai/code)
112+
EOF
113+
)
114+
115+
# Check if a PR already exists for this branch
116+
local existing_pr=$(gh pr list --head "$doc_update_branch" --json number --jq '.[0].number' 2>/dev/null || echo "")
117+
local doc_pr_url=""
118+
119+
if [[ -n "$existing_pr" ]]; then
120+
echo "Updating existing PR #$existing_pr..."
121+
gh pr edit "$existing_pr" --body "$pr_body"
122+
doc_pr_url=$(gh pr view "$existing_pr" --json url -q .url)
123+
else
124+
doc_pr_url=$(gh pr create \
125+
--title "docs: auto-update for PR #$pr_number - $pr_title" \
126+
--body "$pr_body" \
127+
--base "$base_branch" \
128+
--head "$doc_update_branch" \
129+
--label "documentation" \
130+
--label "auto-generated" 2>/dev/null || echo "")
131+
fi
132+
133+
if [[ -n "$doc_pr_url" ]]; then
134+
echo ""
135+
echo "Documentation update PR created successfully!"
136+
echo " URL: $doc_pr_url"
137+
138+
# Add a comment to the original PR linking to the docs PR
139+
local link_comment="**Automatic Documentation Update**
140+
141+
A documentation update PR has been created in response to code changes in this PR:
142+
143+
**Documentation PR:** $doc_pr_url
144+
145+
Please review the documentation changes and merge them after this PR is merged."
146+
147+
gh pr comment "$pr_number" --body "$link_comment" 2>/dev/null || echo "Warning: Could not add comment to original PR"
148+
else
149+
echo "Warning: Could not create documentation update PR"
150+
fi
151+
152+
# Return to original ref/branch
153+
git checkout "$original_ref" 2>/dev/null || git checkout "$original_branch" 2>/dev/null || true
154+
155+
echo "$doc_pr_url"
156+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
#!/usr/bin/env bash
2+
# extract_doc_references.sh - Shared functions for extracting doc references
3+
#
4+
# This library provides reusable functions for extracting references from
5+
# documentation frontmatter.
6+
#
7+
# Usage: source this file and call the functions
8+
9+
# Extract references from markdown frontmatter and build a mapping file
10+
# Arguments:
11+
# $1 - docs_dir: The documentation directory (e.g., "docs")
12+
# $2 - output_file: Path to write the mapping (format: "ref_file|doc_file")
13+
extract_references_mapping() {
14+
local docs_dir="$1"
15+
local output_file="$2"
16+
17+
# Clear output file
18+
> "$output_file"
19+
20+
# Scan docs-developers and docs-network subdirectories
21+
for docs_subdir in docs-developers docs-network; do
22+
if [[ -d "$docs_dir/$docs_subdir/docs" ]]; then
23+
_extract_from_directory "$docs_dir/$docs_subdir/docs" "$output_file"
24+
fi
25+
done
26+
27+
# Also scan main docs folder if it exists
28+
if [[ -d "$docs_dir/docs" ]]; then
29+
_extract_from_directory "$docs_dir/docs" "$output_file"
30+
fi
31+
}
32+
33+
# Internal function to extract references from a directory
34+
_extract_from_directory() {
35+
local search_dir="$1"
36+
local output_file="$2"
37+
38+
find "$search_dir" -type f -name "*.md" -print0 | while IFS= read -r -d '' doc_file; do
39+
awk -v doc="$doc_file" '
40+
BEGIN { in_frontmatter = 0 }
41+
/^---$/ {
42+
if (NR == 1) {
43+
in_frontmatter = 1
44+
} else if (in_frontmatter) {
45+
in_frontmatter = 0
46+
}
47+
next
48+
}
49+
in_frontmatter && /^references:/ {
50+
if (match($0, /\[.*\]/)) {
51+
refs = substr($0, RSTART, RLENGTH)
52+
gsub(/[\[\]"'\''"]/, "", refs)
53+
split(refs, arr, /,[ ]*/)
54+
for (i in arr) {
55+
if (arr[i] != "") {
56+
print arr[i] "|" doc
57+
}
58+
}
59+
}
60+
}
61+
' "$doc_file"
62+
done >> "$output_file"
63+
}
64+
65+
# Get unique reference files from a mapping file
66+
# Arguments:
67+
# $1 - mapping_file: Path to the mapping file
68+
get_unique_references() {
69+
local mapping_file="$1"
70+
cut -d'|' -f1 "$mapping_file" | sort -u
71+
}
72+
73+
# Find which docs reference a given file pattern
74+
# Arguments:
75+
# $1 - mapping_file: Path to the mapping file
76+
# $2 - pattern: The file pattern to search for
77+
find_docs_for_reference() {
78+
local mapping_file="$1"
79+
local pattern="$2"
80+
grep -F "$pattern" "$mapping_file" | cut -d'|' -f2 | sort -u
81+
}
82+
83+
# Build associative arrays for file-to-docs and docs-to-files mappings
84+
# Arguments:
85+
# $1 - mapping_file: Path to the mapping file
86+
# $2 - changed_files: Newline-separated list of changed files
87+
# Sets global variables:
88+
# FILE_TO_DOCS_MAP - associative array
89+
# DOC_TO_FILES_MAP - associative array
90+
# CHANGED_REFERENCES - newline-separated list of changed reference files
91+
build_change_mappings() {
92+
local mapping_file="$1"
93+
local changed_files="$2"
94+
95+
# Initialize global associative arrays
96+
declare -gA FILE_TO_DOCS_MAP
97+
declare -gA DOC_TO_FILES_MAP
98+
CHANGED_REFERENCES=""
99+
100+
local reference_files=$(get_unique_references "$mapping_file")
101+
102+
while IFS= read -r ref_file; do
103+
[[ -z "$ref_file" ]] && continue
104+
local match_pattern="${ref_file%/\*}"
105+
106+
if echo "$changed_files" | grep -qF "$match_pattern"; then
107+
CHANGED_REFERENCES="${CHANGED_REFERENCES}${ref_file}\n"
108+
109+
while IFS='|' read -r src_file doc_file; do
110+
if [[ "$src_file" == "$ref_file" ]]; then
111+
if [[ -n "${FILE_TO_DOCS_MAP[$ref_file]:-}" ]]; then
112+
FILE_TO_DOCS_MAP[$ref_file]="${FILE_TO_DOCS_MAP[$ref_file]}|${doc_file}"
113+
else
114+
FILE_TO_DOCS_MAP[$ref_file]="$doc_file"
115+
fi
116+
117+
if [[ -n "${DOC_TO_FILES_MAP[$doc_file]:-}" ]]; then
118+
DOC_TO_FILES_MAP[$doc_file]="${DOC_TO_FILES_MAP[$doc_file]}|${ref_file}"
119+
else
120+
DOC_TO_FILES_MAP[$doc_file]="$ref_file"
121+
fi
122+
fi
123+
done < "$mapping_file"
124+
fi
125+
done <<< "$reference_files"
126+
}

0 commit comments

Comments
 (0)