Skip to content
Merged
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
15 changes: 2 additions & 13 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -570,24 +570,13 @@ Building on the DevRel review automation, the docs CI can analyze PRs and notify
- `SLACK_DOC_UPDATE_CHANNEL` - Slack channel for notifications (default: `#devrel`)
- `DRY_RUN=1` - Skip Slack notification, just print what would be sent

**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`.
**Implementation**: The automation is handled by `scripts/check_doc_references.sh`, which detects changed references, requests devrel review, sends a Slack notification, and dispatches ClaudeBox — all in a single pass.

**Script Architecture**:

- `scripts/update_doc_references.sh` - Main script that orchestrates the workflow
- `scripts/check_doc_references.sh` - Main script that handles detection, review requests, Slack, and ClaudeBox dispatch
- `scripts/lib/extract_doc_references.sh` - Shared library for parsing frontmatter references
- `scripts/lib/create_doc_update_pr.sh` - (Reserved for future use) PR creation logic
- `scripts/test_update_doc_references.sh` - Local testing helper

**Local Testing**:

```bash
# Find a PR with referenced file changes and test
./scripts/test_update_doc_references.sh

# Test against a specific PR
LOCAL_TEST=1 DRY_RUN=1 ./scripts/update_doc_references.sh 19803
```

**Limitations**:

Expand Down
10 changes: 4 additions & 6 deletions docs/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,14 @@ function test {
}

function check_references {
if [[ "${GITHUB_EVENT_NAME:-}" != "merge_group" ]]; then
echo "Skipping doc reference check (only runs in merge queue)."
return
fi
echo_header "Check doc references"
./scripts/check_doc_references.sh docs || true
}

function update_doc_references {
echo_header "Auto-update doc references"
./scripts/update_doc_references.sh docs || true
}

function build_examples {
echo_header "Building examples"
(cd examples && ./bootstrap.sh "$@")
Expand All @@ -72,7 +71,6 @@ case "$cmd" in
build_docs
test
check_references
update_doc_references
;;
"")
build_examples
Expand Down
200 changes: 143 additions & 57 deletions docs/scripts/check_doc_references.sh
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
#!/usr/bin/env bash
set -euo pipefail

# check_doc_references - Request devrel review when referenced source files change
# check_doc_references - Request devrel review and dispatch ClaudeBox when referenced source files change
#
# This script:
# 1. Extracts all 'references' fields from documentation markdown frontmatter
# 2. Checks if any referenced files were changed in the current PR
# 3. Requests AztecProtocol/devrel team as reviewers if files changed and PR is not draft
# 4. Sends a Slack message to #devrel-docs-updates showing which changed files are referenced by which docs
# 5. Skips reviewer request if devrel team is already requested or a member has approved
# 5. Dispatches ClaudeBox to analyze changes and update documentation
# 6. Skips reviewer request if devrel team is already requested or a member has approved
#
# Usage: check_doc_references.sh [pr_number] [docs_dir]
#
Expand Down Expand Up @@ -96,11 +97,13 @@ elif [[ $# -ge 1 ]]; then
DOCS_DIR="$1"
fi

# Check if gh CLI is available
if ! command -v gh &> /dev/null; then
echo "gh CLI not found. Skipping devrel review check."
exit 0
fi
# Check required tools
for tool in gh jq; do
if ! command -v "$tool" &> /dev/null; then
echo "$tool not found. Skipping devrel review check."
exit 0
fi
done

# Get the PR number from various sources
PR_NUMBER=""
Expand Down Expand Up @@ -260,85 +263,168 @@ REQUESTED_TEAMS=$(echo "$REVIEW_REQUESTS_JSON" | jq -r '.reviewRequests[]? | sel

echo "Requested teams: ${REQUESTED_TEAMS:-none}"

# Determine if devrel review request and Slack notification should be skipped
SKIP_REVIEW=false

if [[ -n "$REQUESTED_TEAMS" ]] && echo "$REQUESTED_TEAMS" | grep -q "devrel"; then
echo "AztecProtocol/devrel team is already a requested reviewer for PR #$PR_NUMBER. Skipping."
exit 0
echo "AztecProtocol/devrel team is already a requested reviewer for PR #$PR_NUMBER."
SKIP_REVIEW=true
fi

# Check if any devrel team member has already approved
echo "Checking if devrel team member has already approved..."
DEVREL_MEMBERS=$(gh api orgs/AztecProtocol/teams/devrel/members --jq '.[].login' 2>/dev/null || echo "")
if [[ -n "$DEVREL_MEMBERS" ]]; then
APPROVERS=$(gh pr view "$PR_NUMBER" --json reviews -q '.reviews[] | select(.state == "APPROVED") | .author.login' 2>/dev/null || echo "")
if [[ -n "$APPROVERS" ]]; then
while IFS= read -r approver; do
if echo "$DEVREL_MEMBERS" | grep -qx "$approver"; then
echo "PR #$PR_NUMBER already approved by devrel team member: $approver. Skipping team review request."
exit 0
fi
done <<< "$APPROVERS"
if [[ "$SKIP_REVIEW" != "true" ]]; then
# Check if any devrel team member has already approved
echo "Checking if devrel team member has already approved..."
DEVREL_MEMBERS=$(gh api orgs/AztecProtocol/teams/devrel/members --jq '.[].login' 2>/dev/null || echo "")
if [[ -n "$DEVREL_MEMBERS" ]]; then
APPROVERS=$(gh pr view "$PR_NUMBER" --json reviews -q '.reviews[] | select(.state == "APPROVED") | .author.login' 2>/dev/null || echo "")
if [[ -n "$APPROVERS" ]]; then
while IFS= read -r approver; do
if echo "$DEVREL_MEMBERS" | grep -qx "$approver"; then
echo "PR #$PR_NUMBER already approved by devrel team member: $approver."
SKIP_REVIEW=true
break
fi
done <<< "$APPROVERS"
fi
fi
fi

# Build Slack message with file-to-docs mapping
# Get PR URL for linking
# Get PR URL and title (needed for both Slack and ClaudeBox)
PR_URL=$(gh pr view "$PR_NUMBER" --json url -q .url 2>/dev/null || echo "https://github.com/AztecProtocol/aztec-packages/pull/$PR_NUMBER")
PR_TITLE=$(gh pr view "$PR_NUMBER" --json title -q .title 2>/dev/null || echo "")

# Request devrel review (only if not already done)
if [[ "$SKIP_REVIEW" != "true" ]]; then
echo "Requesting AztecProtocol/devrel team as a reviewer for PR #$PR_NUMBER..."
if gh pr edit "$PR_NUMBER" --add-reviewer AztecProtocol/devrel 2>/dev/null; then
echo "✓ Successfully requested AztecProtocol/devrel team as a reviewer."
else
echo "⚠ Failed to request AztecProtocol/devrel team as a reviewer."
fi
else
echo "Skipping reviewer request (already handled)."
fi

SLACK_MESSAGE="📚 *Documentation References Updated*\\n\\nThe following source files changed in <$PR_URL|PR #$PR_NUMBER> are referenced by documentation:\\n"

# Get unique doc files count
# Build changed references summary (used for both Slack and ClaudeBox)
CHANGED_SUMMARY=""
ALL_DOCS=""
CHANGED_FILE_COUNT=0

# Sort the keys for consistent output
SORTED_KEYS=$(for key in "${!FILE_TO_DOCS_MAP[@]}"; do echo "$key"; done | sort)

while IFS= read -r changed_file; do
[[ -z "$changed_file" ]] && continue
CHANGED_FILE_COUNT=$((CHANGED_FILE_COUNT + 1))

SLACK_MESSAGE="${SLACK_MESSAGE}\\n*\`${changed_file}\`*"

# Get docs for this file and split by pipe
docs="${FILE_TO_DOCS_MAP[$changed_file]}"

# Convert pipe-separated list to array and iterate
IFS='|' read -ra DOC_ARRAY <<< "$docs"
DOC_LIST=$(printf ", %s" "${DOC_ARRAY[@]}")
DOC_LIST="${DOC_LIST:2}"
CHANGED_SUMMARY="${CHANGED_SUMMARY}${changed_file} -> ${DOC_LIST}; "
for doc_file in "${DOC_ARRAY[@]}"; do
SLACK_MESSAGE="${SLACK_MESSAGE}\\n • \`${doc_file}\`"

# Track all unique docs
if ! echo "$ALL_DOCS" | grep -qF "$doc_file"; then
ALL_DOCS="${ALL_DOCS}${doc_file}\n"
fi
done
done <<< "$SORTED_KEYS"

# Count unique docs
CHANGED_SUMMARY="${CHANGED_SUMMARY%%; }"
DOC_FILE_COUNT=$(echo -e "$ALL_DOCS" | grep -v '^$' | sort -u | wc -l)

SLACK_MESSAGE="${SLACK_MESSAGE}\\n\\n*Summary:* ${CHANGED_FILE_COUNT} changed file(s) referenced by ${DOC_FILE_COUNT} documentation file(s)"

echo "Requesting AztecProtocol/devrel team as a reviewer for PR #$PR_NUMBER..."
# Dispatch ClaudeBox
echo "Dispatching ClaudeBox workflow..."
CLAUDEBOX_RUNS_URL="https://github.com/AztecProtocol/aztec-packages/actions/workflows/claudebox.yml"
CLAUDEBOX_STATUS=""

# Request AztecProtocol/devrel team as a reviewer
REVIEWER_REQUESTED=false
if gh pr edit "$PR_NUMBER" --add-reviewer AztecProtocol/devrel 2>/dev/null; then
echo "✓ Successfully requested AztecProtocol/devrel team as a reviewer."
REVIEWER_REQUESTED=true
SLACK_MESSAGE="${SLACK_MESSAGE}\\n\\n@AztecProtocol/devrel team has been requested for review."
if gh workflow run claudebox.yml \
-f prompt="PR #$PR_NUMBER ($PR_TITLE) changed source files referenced by documentation. Analyze the changes and update the affected documentation. Changed references: $CHANGED_SUMMARY. Follow the update-doc-references skill (.claude/skills/update-doc-references/SKILL.md)." \
-f link="$PR_URL"; then
echo "✓ ClaudeBox dispatched for PR #$PR_NUMBER."
CLAUDEBOX_STATUS="dispatched"
else
echo "⚠ Failed to request AztecProtocol/devrel team as a reviewer. They may need to be added manually."
SLACK_MESSAGE="${SLACK_MESSAGE}\\n\\n⚠️ Failed to automatically add @AztecProtocol/devrel as reviewers. Please add them manually."
echo "⚠ Failed to dispatch ClaudeBox workflow. Manual review may be needed."
CLAUDEBOX_STATUS="failed"
fi

# Send Slack notification
echo "Sending Slack notification to #devrel-docs-updates..."
if send_slack_message "$SLACK_MESSAGE"; then
echo "✓ Successfully sent Slack notification."
# Send or update Slack notification (one message per PR, updated on each run)
SLACK_CHANNEL="${SLACK_DOC_UPDATE_CHANNEL:-devrel-docs-updates}"
TS=""
CHANNEL_ID=""

if [[ -n "${SLACK_BOT_TOKEN:-}" ]]; then
# Build the Slack message
NOW=$(date -u '+%Y-%m-%d %H:%M UTC')
SLACK_MESSAGE="📚 *Documentation References Updated*\\n\\nThe following source files changed in <$PR_URL|PR #$PR_NUMBER> are referenced by documentation:\\n"

while IFS= read -r changed_file; do
[[ -z "$changed_file" ]] && continue
SLACK_MESSAGE="${SLACK_MESSAGE}\\n*\`${changed_file}\`*"
docs="${FILE_TO_DOCS_MAP[$changed_file]}"
IFS='|' read -ra DOC_ARRAY <<< "$docs"
for doc_file in "${DOC_ARRAY[@]}"; do
SLACK_MESSAGE="${SLACK_MESSAGE}\\n • \`${doc_file}\`"
done
done <<< "$SORTED_KEYS"

SLACK_MESSAGE="${SLACK_MESSAGE}\\n\\n*Summary:* ${CHANGED_FILE_COUNT} changed file(s) referenced by ${DOC_FILE_COUNT} documentation file(s)"

if [[ "$CLAUDEBOX_STATUS" == "dispatched" ]]; then
SLACK_MESSAGE="${SLACK_MESSAGE}\\n\\n:robot_face: *ClaudeBox dispatched* — <$CLAUDEBOX_RUNS_URL|view workflow runs>\\n_Last updated: ${NOW}_"
else
SLACK_MESSAGE="${SLACK_MESSAGE}\\n\\n:warning: *ClaudeBox dispatch failed* — manual review needed\\n_Last updated: ${NOW}_"
fi

# Look up channel ID
CHANNEL_ID=$(curl -sS "https://slack.com/api/conversations.list?types=public_channel&limit=200" \
-H "Authorization: Bearer $SLACK_BOT_TOKEN" | jq -r ".channels[] | select(.name==\"$SLACK_CHANNEL\") | .id" 2>/dev/null || echo "")

if [[ -n "$CHANNEL_ID" ]]; then
# Search channel history for an existing message about this PR (paginate up to 1000 messages)
EXISTING_TS=""
CURSOR=""
PAGES=0
while [[ $PAGES -lt 5 ]]; do
PAGES=$((PAGES + 1))
HISTORY_URL="https://slack.com/api/conversations.history?channel=$CHANNEL_ID&limit=200"
[[ -n "$CURSOR" ]] && HISTORY_URL="${HISTORY_URL}&cursor=$CURSOR"

HISTORY_RESP=$(curl -sS "$HISTORY_URL" -H "Authorization: Bearer $SLACK_BOT_TOKEN")

EXISTING_TS=$(echo "$HISTORY_RESP" | \
jq -r ".messages[]? | select(.text != null and .bot_id != null and (.text | contains(\"PR #$PR_NUMBER\"))) | .ts" 2>/dev/null | head -1 || echo "")
[[ -n "$EXISTING_TS" ]] && break

CURSOR=$(echo "$HISTORY_RESP" | jq -r '.response_metadata.next_cursor // empty' 2>/dev/null)
[[ -z "$CURSOR" ]] && break
done

if [[ -n "$EXISTING_TS" ]]; then
# Update existing message
echo "Updating existing Slack message for PR #$PR_NUMBER..."
RESP=$(curl -sS -X POST https://slack.com/api/chat.update \
-H "Authorization: Bearer $SLACK_BOT_TOKEN" \
-H "Content-type: application/json" \
-d "$(jq -n --arg c "$CHANNEL_ID" --arg ts "$EXISTING_TS" --arg t "$SLACK_MESSAGE" \
'{channel:$c, ts:$ts, text:$t}')")
TS="$EXISTING_TS"
else
# Post new message
echo "Sending Slack notification to #${SLACK_CHANNEL}..."
RESP=$(curl -sS -X POST https://slack.com/api/chat.postMessage \
-H "Authorization: Bearer $SLACK_BOT_TOKEN" \
-H "Content-type: application/json" \
-d "$(jq -n --arg c "$CHANNEL_ID" --arg t "$SLACK_MESSAGE" '{channel:$c, text:$t}')")
TS=$(echo "$RESP" | jq -r '.ts // empty')
fi

SLACK_OK=$(echo "$RESP" | jq -r '.ok // empty')
if [[ "$SLACK_OK" == "true" ]]; then
echo "✓ Slack notification sent/updated successfully."
else
SLACK_ERR=$(echo "$RESP" | jq -r '.error // "unknown error"' 2>/dev/null)
echo "⚠ Slack API error: $SLACK_ERR" >&2
fi
else
echo "⚠ Could not find Slack channel #$SLACK_CHANNEL"
fi
else
echo "⚠ Failed to send Slack notification." >&2
echo "SLACK_BOT_TOKEN not set, skipping Slack notification"
fi

# Exit successfully even if Slack notification fails (don't block builds)
exit 0
2 changes: 1 addition & 1 deletion docs/scripts/lib/create_doc_update_pr.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
# create_doc_update_pr.sh - Create a PR with documentation updates
#
# This script is extracted from update_doc_references.sh for future use.
# This script is extracted from check_doc_references.sh for future use.
# It handles:
# - Creating a branch for doc updates
# - Committing changes
Expand Down
Loading
Loading