mirror of
https://github.com/zoriya/v10.git
synced 2026-08-05 13:48:14 +00:00
428 lines
16 KiB
YAML
428 lines
16 KiB
YAML
name: API Reference Sync
|
|
|
|
on:
|
|
pull_request:
|
|
types: [closed]
|
|
|
|
permissions:
|
|
actions: read
|
|
contents: write
|
|
pull-requests: write
|
|
issues: write
|
|
id-token: write
|
|
|
|
concurrency:
|
|
group: api-reference-sync-${{ github.event.pull_request.number || github.run_id }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
# ─── Job 0: Build API JSON at HEAD~1 and HEAD, diff them ─────────────
|
|
analyze:
|
|
if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'main'
|
|
runs-on: ubuntu-latest
|
|
|
|
outputs:
|
|
has_diff: ${{ steps.classify.outputs.has_diff }}
|
|
has_new: ${{ steps.classify.outputs.has_new }}
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v5
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v5
|
|
with:
|
|
node-version-file: '.nvmrc'
|
|
cache: pnpm
|
|
|
|
- name: Collect PR metadata
|
|
id: meta
|
|
env:
|
|
PR_TITLE: ${{ github.event.pull_request.title || 'manual run' }}
|
|
run: |
|
|
mkdir -p /tmp/api-sync
|
|
|
|
jq -n \
|
|
--arg number "${{ github.event.pull_request.number || '' }}" \
|
|
--arg url "${{ github.event.pull_request.html_url || '' }}" \
|
|
--arg author "${{ github.event.pull_request.user.login || '' }}" \
|
|
--arg title "$PR_TITLE" \
|
|
'{number: $number, url: $url, author: $author, title: $title}' \
|
|
> /tmp/api-sync/pr-meta.json
|
|
|
|
cat /tmp/api-sync/pr-meta.json
|
|
|
|
- name: Build API JSON at HEAD
|
|
run: |
|
|
pnpm install --frozen-lockfile
|
|
pnpm -C site api-docs
|
|
|
|
mkdir -p /tmp/api-sync/after-components /tmp/api-sync/after-utils
|
|
cp -r site/src/content/generated-component-reference/* /tmp/api-sync/after-components/ 2>/dev/null || true
|
|
cp -r site/src/content/generated-util-reference/* /tmp/api-sync/after-utils/ 2>/dev/null || true
|
|
|
|
- name: Build API JSON at HEAD~1
|
|
run: |
|
|
git checkout HEAD~1
|
|
|
|
# If install or build fails (e.g. api-docs-builder itself changed),
|
|
# treat everything as new by leaving before dirs empty
|
|
mkdir -p /tmp/api-sync/before-components /tmp/api-sync/before-utils
|
|
|
|
if pnpm install --frozen-lockfile && pnpm -C site api-docs; then
|
|
cp -r site/src/content/generated-component-reference/* /tmp/api-sync/before-components/ 2>/dev/null || true
|
|
cp -r site/src/content/generated-util-reference/* /tmp/api-sync/before-utils/ 2>/dev/null || true
|
|
else
|
|
echo "::warning::HEAD~1 build failed — treating all components/utils as new"
|
|
fi
|
|
|
|
git checkout -
|
|
|
|
- name: Classify changes
|
|
id: classify
|
|
run: |
|
|
# Unified diff of both JSON directories
|
|
diff -ruN /tmp/api-sync/before-components /tmp/api-sync/after-components > /tmp/api-sync/diff.patch || true
|
|
diff -ruN /tmp/api-sync/before-utils /tmp/api-sync/after-utils >> /tmp/api-sync/diff.patch || true
|
|
|
|
# New: files in after but not in before
|
|
comm -23 \
|
|
<(ls /tmp/api-sync/after-components/ 2>/dev/null | sort) \
|
|
<(ls /tmp/api-sync/before-components/ 2>/dev/null | sort) \
|
|
> /tmp/api-sync/new-components.txt
|
|
|
|
comm -23 \
|
|
<(ls /tmp/api-sync/after-utils/ 2>/dev/null | sort) \
|
|
<(ls /tmp/api-sync/before-utils/ 2>/dev/null | sort) \
|
|
> /tmp/api-sync/new-utils.txt
|
|
|
|
# Changed: files in both, but content differs
|
|
comm -12 \
|
|
<(ls /tmp/api-sync/after-components/ 2>/dev/null | sort) \
|
|
<(ls /tmp/api-sync/before-components/ 2>/dev/null | sort) \
|
|
| while read -r f; do
|
|
if ! diff -q "/tmp/api-sync/before-components/$f" "/tmp/api-sync/after-components/$f" >/dev/null 2>&1; then
|
|
echo "$f"
|
|
fi
|
|
done > /tmp/api-sync/changed-components.txt
|
|
|
|
comm -12 \
|
|
<(ls /tmp/api-sync/after-utils/ 2>/dev/null | sort) \
|
|
<(ls /tmp/api-sync/before-utils/ 2>/dev/null | sort) \
|
|
| while read -r f; do
|
|
if ! diff -q "/tmp/api-sync/before-utils/$f" "/tmp/api-sync/after-utils/$f" >/dev/null 2>&1; then
|
|
echo "$f"
|
|
fi
|
|
done > /tmp/api-sync/changed-utils.txt
|
|
|
|
# Log results
|
|
echo "=== New components ===" && cat /tmp/api-sync/new-components.txt
|
|
echo "=== New utils ===" && cat /tmp/api-sync/new-utils.txt
|
|
echo "=== Changed components ===" && cat /tmp/api-sync/changed-components.txt
|
|
echo "=== Changed utils ===" && cat /tmp/api-sync/changed-utils.txt
|
|
echo "=== Diff ===" && head -100 /tmp/api-sync/diff.patch
|
|
|
|
# Set outputs
|
|
if [ -s /tmp/api-sync/diff.patch ]; then
|
|
echo "has_diff=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "has_diff=false" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
if [ -s /tmp/api-sync/new-components.txt ] || [ -s /tmp/api-sync/new-utils.txt ]; then
|
|
echo "has_new=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "has_new=false" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
- name: Upload artifact
|
|
if: steps.classify.outputs.has_diff == 'true'
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: api-diff
|
|
path: /tmp/api-sync/
|
|
retention-days: 1
|
|
|
|
# ─── Job 1: Open issues for new components/utils without reference pages
|
|
new-references:
|
|
needs: analyze
|
|
if: needs.analyze.outputs.has_new == 'true'
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Download artifact
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: api-diff
|
|
path: /tmp/api-sync
|
|
|
|
- name: Open issues for missing reference pages
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
PR_NUMBER=$(jq -r .number /tmp/api-sync/pr-meta.json)
|
|
PR_URL=$(jq -r .url /tmp/api-sync/pr-meta.json)
|
|
PR_AUTHOR=$(jq -r .author /tmp/api-sync/pr-meta.json)
|
|
|
|
REF_DIR="site/src/content/docs/reference"
|
|
|
|
# --- New components ---
|
|
if [ -s /tmp/api-sync/new-components.txt ]; then
|
|
while read -r filename; do
|
|
JSON_PATH="/tmp/api-sync/after-components/$filename"
|
|
NAME=$(jq -r .name "$JSON_PATH")
|
|
SLUG="${filename%.json}"
|
|
|
|
# Check if a reference page already uses this component
|
|
if grep -rq "<ComponentReference component=\"$NAME\"" "$REF_DIR/"; then
|
|
echo "Reference exists for component $NAME — skipping"
|
|
continue
|
|
fi
|
|
|
|
# Check for existing open issue (dedup by drift-type + component name)
|
|
EXISTING=$(gh issue list \
|
|
--label "docs:reference" \
|
|
--search "docs(reference): add $NAME API reference page" \
|
|
--state open \
|
|
--json number \
|
|
--jq 'length')
|
|
if [ "$EXISTING" -gt 0 ]; then
|
|
echo "Open issue already exists for $NAME — skipping"
|
|
continue
|
|
fi
|
|
|
|
ASSIGNEE_FLAG=""
|
|
if [ -n "$PR_AUTHOR" ]; then
|
|
ASSIGNEE_FLAG="--assignee $PR_AUTHOR"
|
|
fi
|
|
|
|
BODY="$(cat <<EOF
|
|
<!-- drift-type:new-reference -->
|
|
<!-- trigger-pr:$PR_NUMBER -->
|
|
<!-- component:$NAME -->
|
|
|
|
## New Component: \`$NAME\`
|
|
|
|
A new component \`$NAME\` was added in #${PR_NUMBER} but has no API reference page yet.
|
|
|
|
### What's needed
|
|
- [ ] Create reference page with anatomy, prose, demos, and \`<ComponentReference component="$NAME" />\`
|
|
- [ ] Add sidebar entry in \`site/src/docs.config.ts\`
|
|
- [ ] Verify build: \`pnpm -C site build\`
|
|
|
|
### Context
|
|
- Triggering PR: $PR_URL
|
|
- Generated JSON: \`site/src/content/generated-component-reference/$filename\`
|
|
|
|
> Use the \`/api-reference $SLUG\` skill to scaffold this page.
|
|
EOF
|
|
)"
|
|
|
|
gh issue create \
|
|
--title "docs(reference): add $NAME API reference page" \
|
|
--body "$BODY" \
|
|
--label "docs,docs:reference,site,components" \
|
|
$ASSIGNEE_FLAG
|
|
|
|
echo "Created issue for component $NAME"
|
|
|
|
done < /tmp/api-sync/new-components.txt
|
|
fi
|
|
|
|
# --- New utils ---
|
|
if [ -s /tmp/api-sync/new-utils.txt ]; then
|
|
while read -r filename; do
|
|
JSON_PATH="/tmp/api-sync/after-utils/$filename"
|
|
NAME=$(jq -r .name "$JSON_PATH")
|
|
SLUG="${filename%.json}"
|
|
|
|
# Check if a reference page already uses this util
|
|
if grep -rq "<UtilReference util=\"$NAME\"" "$REF_DIR/"; then
|
|
echo "Reference exists for util $NAME — skipping"
|
|
continue
|
|
fi
|
|
|
|
# Check for existing open issue
|
|
EXISTING=$(gh issue list \
|
|
--label "docs:reference" \
|
|
--search "docs(reference): add $NAME API reference page" \
|
|
--state open \
|
|
--json number \
|
|
--jq 'length')
|
|
if [ "$EXISTING" -gt 0 ]; then
|
|
echo "Open issue already exists for $NAME — skipping"
|
|
continue
|
|
fi
|
|
|
|
ASSIGNEE_FLAG=""
|
|
if [ -n "$PR_AUTHOR" ]; then
|
|
ASSIGNEE_FLAG="--assignee $PR_AUTHOR"
|
|
fi
|
|
|
|
BODY="$(cat <<EOF
|
|
<!-- drift-type:new-reference -->
|
|
<!-- trigger-pr:$PR_NUMBER -->
|
|
<!-- util:$NAME -->
|
|
|
|
## New Utility: \`$NAME\`
|
|
|
|
A new utility \`$NAME\` was added in #${PR_NUMBER} but has no API reference page yet.
|
|
|
|
### What's needed
|
|
- [ ] Create reference page with usage examples and \`<UtilReference util="$NAME" />\`
|
|
- [ ] Add sidebar entry in \`site/src/docs.config.ts\`
|
|
- [ ] Verify build: \`pnpm -C site build\`
|
|
|
|
### Context
|
|
- Triggering PR: $PR_URL
|
|
- Generated JSON: \`site/src/content/generated-util-reference/$filename\`
|
|
|
|
> Use the \`/api-reference $SLUG\` skill to scaffold this page.
|
|
EOF
|
|
)"
|
|
|
|
gh issue create \
|
|
--title "docs(reference): add $NAME API reference page" \
|
|
--body "$BODY" \
|
|
--label "docs,docs:reference,site" \
|
|
$ASSIGNEE_FLAG
|
|
|
|
echo "Created issue for util $NAME"
|
|
|
|
done < /tmp/api-sync/new-utils.txt
|
|
fi
|
|
|
|
# ─── Job 2: Detect stale docs (Claude Opus) ─────────────────────────
|
|
stale-docs:
|
|
needs: analyze
|
|
if: needs.analyze.outputs.has_diff == 'true'
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Download artifact
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: api-diff
|
|
path: /tmp/api-sync
|
|
|
|
- name: Detect stale docs
|
|
uses: anthropics/claude-code-action@v1
|
|
with:
|
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
claude_args: |
|
|
--model opus
|
|
--max-turns 25
|
|
--allowedTools "Read" "Glob" "Grep" "Bash(gh:*)" "Bash(cat:*)" "Bash(jq:*)" "Bash(diff:*)" "Bash(ls:*)" "Bash(head:*)" "Bash(tail:*)" "Bash(wc:*)" "Bash(sort:*)"
|
|
prompt: |
|
|
You are the stale-docs detection agent. Your job: given a pre-computed API diff,
|
|
find documentation that may need updating and open ONE GitHub issue summarizing all findings.
|
|
|
|
## Inputs
|
|
|
|
The following files are at /tmp/api-sync/:
|
|
- `diff.patch` — unified diff of API JSON between HEAD~1 and HEAD
|
|
- `changed-components.txt` — filenames of changed component JSONs (one per line, may be empty)
|
|
- `changed-utils.txt` — filenames of changed util JSONs (one per line, may be empty)
|
|
- `after-components/` — current component JSON files
|
|
- `after-utils/` — current util JSON files
|
|
- `pr-meta.json` — `{number, url, author, title}` of the triggering PR
|
|
|
|
Important: Focus on items in `changed-components.txt` and `changed-utils.txt`.
|
|
Skip any components/utils listed in `new-components.txt` or `new-utils.txt` —
|
|
a separate job already opens issues for those.
|
|
|
|
## Process
|
|
|
|
Step 1: Read `pr-meta.json` and `diff.patch`. Understand what API surfaces changed and how.
|
|
|
|
Step 2: For each changed component/util, read the "after" JSON to understand the current API shape.
|
|
|
|
Step 3: Search for documentation that references these APIs. Check ALL of:
|
|
- `site/src/content/docs/reference/` — MDX reference pages (look for stale prop descriptions,
|
|
missing new props/state/data-attributes, outdated type signatures)
|
|
- `site/src/content/docs/concepts/` — concept pages mentioning these APIs
|
|
- `site/src/content/docs/how-to/` — how-to guides mentioning these APIs
|
|
- `site/src/components/docs/demos/` — demo code using changed props/state/signatures
|
|
- `packages/*/README.md` — package READMEs mentioning these APIs
|
|
|
|
Use Grep and Glob to search efficiently. Look for:
|
|
- The component/util name (PascalCase, camelCase)
|
|
- Specific prop/state/parameter names that were added, removed, or changed in the diff
|
|
- HTML element names from `platforms.html.tagName` in the JSON (e.g. `media-play-button`)
|
|
- Data attribute names that changed
|
|
|
|
Step 4: For each file with hits, read the relevant section and assess:
|
|
- `high` — definitely stale (references a removed/renamed prop by old name, shows old signature)
|
|
- `medium` — likely stale (mentions API whose behavior/type changed)
|
|
- `low` — possibly stale (mentions the component but may not be affected)
|
|
|
|
Discard clear false positives. Include borderline cases for human review.
|
|
|
|
Step 5: If you found ANY stale references, open exactly ONE issue using `gh issue create`.
|
|
If you found NOTHING stale, do NOT create an issue — just output "No stale docs detected."
|
|
|
|
## Issue format
|
|
|
|
Read `pr-meta.json` to get PR_NUMBER, PR_URL, PR_AUTHOR, and PR_TITLE.
|
|
|
|
Assign the issue to the PR author: `--assignee $PR_AUTHOR` (skip if author is empty).
|
|
|
|
Title: `docs(site): stale docs from #$PR_NUMBER`
|
|
|
|
Labels: `docs,docs:reference,site,triage`
|
|
|
|
Body (use this structure exactly):
|
|
```
|
|
<!-- drift-type:stale-docs -->
|
|
<!-- trigger-pr:{PR_NUMBER} -->
|
|
|
|
## Summary
|
|
API changes in #{PR_NUMBER} ({PR_TITLE}) may have made the following documentation stale.
|
|
|
|
## Triggering PR
|
|
{PR_URL}
|
|
|
|
## API Changes
|
|
{Brief summary of what changed in the API — new props, removed props, type changes, etc.}
|
|
|
|
## Stale Documentation Found
|
|
|
|
### High Confidence
|
|
| File | Line(s) | Issue | API Change |
|
|
|---|---|---|---|
|
|
{rows or "None found"}
|
|
|
|
### Medium Confidence
|
|
| File | Line(s) | Issue | API Change |
|
|
|---|---|---|---|
|
|
{rows or "None found"}
|
|
|
|
### Low Confidence
|
|
| File | Line(s) | Issue | API Change |
|
|
|---|---|---|---|
|
|
{rows or "None found"}
|
|
|
|
## Recommended Actions
|
|
{Bulleted list of specific things to fix}
|
|
```
|
|
|
|
## Rules
|
|
- Open at most ONE issue per run. Group everything into one issue.
|
|
- Do NOT create or modify any files. Do NOT create PRs. Issues only.
|
|
- Do NOT open an issue if there is nothing stale.
|
|
- Before creating an issue, check for existing open issues that have BOTH
|
|
`<!-- drift-type:stale-docs -->` and `<!-- trigger-pr:{PR_NUMBER} -->` in their body.
|
|
If one exists, skip creation to avoid duplicates.
|
|
- Be thorough but efficient — use Grep to find files, then Read only relevant sections.
|