Skip to content

Commit 777bddc

Browse files
committed
Enhance documentation workflows with cross-reference checking
- Adds cross-reference validation to detect broken links when files or headings change - Centralizes file processing for better efficiency - Creates a reusable docs-setup action to reduce redundancy - Updates PR comment with cross-reference validation results - Improves documentation with updated features - Optimizes code for better maintainability
1 parent 4fcb322 commit 777bddc

File tree

5 files changed

+231
-26
lines changed

5 files changed

+231
-26
lines changed

.github/docs/README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ jobs:
2727
lint-markdown: true
2828
check-format: true
2929
check-links: true
30+
check-cross-references: true
3031
lint-vale: true
3132
generate-preview: true
3233
post-comment: true
@@ -47,11 +48,12 @@ The `docs-link-check.yaml` workflow runs after merges to main and on a weekly sc
4748
1. **Documentation Preview**: Generates preview links for documentation changes
4849
2. **Vale Style Checking**: Enforces consistent terminology and style
4950
3. **Link Validation**: Checks for broken links in documentation
50-
4. **Markdown Linting**: Ensures proper markdown formatting with markdownlint-cli2
51-
5. **Markdown Table Format Checking**: Checks (but doesn't apply) markdown table formatting
52-
6. **PR Comments**: Creates or updates PR comments with preview links and validation results
53-
7. **Post-Merge Validation**: Ensures documentation quality after merges to main
54-
8. **Issue Creation**: Automatically creates GitHub issues for broken links
51+
4. **Cross-Reference Validation**: Detects broken references when files or headings are changed/removed
52+
5. **Markdown Linting**: Ensures proper markdown formatting with markdownlint-cli2
53+
6. **Markdown Table Format Checking**: Checks (but doesn't apply) markdown table formatting
54+
7. **PR Comments**: Creates or updates PR comments with preview links and validation results
55+
8. **Post-Merge Validation**: Ensures documentation quality after merges to main
56+
9. **Issue Creation**: Automatically creates GitHub issues for broken links
5557

5658
## Formatting Local Workflow
5759

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: 'Docs Setup'
2+
description: 'Sets up the environment for docs-related workflows'
3+
author: 'Coder'
4+
5+
inputs:
6+
node-version:
7+
description: 'Node.js version'
8+
required: false
9+
default: '20'
10+
fetch-depth:
11+
description: 'Git fetch depth'
12+
required: false
13+
default: '0'
14+
15+
runs:
16+
using: 'composite'
17+
steps:
18+
- name: Checkout code
19+
uses: actions/checkout@v4
20+
with:
21+
fetch-depth: ${{ inputs.fetch-depth }}
22+
23+
- name: Setup Node.js
24+
uses: actions/setup-node@v4
25+
with:
26+
node-version: ${{ inputs.node-version }}
27+
cache: 'pnpm'
28+
29+
- name: Install PNPM
30+
uses: pnpm/action-setup@v3
31+
with:
32+
run_install: false
33+
34+
- name: Install dependencies
35+
shell: bash
36+
run: ./scripts/pnpm_install.sh

.github/docs/actions/docs-shared/action.yaml

Lines changed: 170 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ inputs:
3030
description: 'Whether to run Vale style checks on documentation'
3131
required: false
3232
default: 'true'
33+
check-cross-references:
34+
description: 'Whether to check for broken cross-references when files or headings change'
35+
required: false
36+
default: 'true'
3337
generate-preview:
3438
description: 'Whether to generate preview links'
3539
required: false
@@ -84,6 +88,9 @@ outputs:
8488
vale_results:
8589
description: 'Results from Vale style checks'
8690
value: ${{ steps.lint-vale.outputs.result || '' }}
91+
cross_ref_results:
92+
description: 'Results from cross-reference checking'
93+
value: ${{ steps.cross-references.outputs.cross_ref_results || '' }}
8794

8895
runs:
8996
using: 'composite'
@@ -110,6 +117,31 @@ runs:
110117
${{ inputs.include-md-files == 'true' && '**.md' || '' }}
111118
separator: ','
112119
json: true
120+
121+
- name: Process file lists
122+
id: process-files
123+
shell: bash
124+
run: |
125+
# Set up environment
126+
CHANGED_FILES='${{ steps.changed-files.outputs.all_changed_files_json }}'
127+
DELETED_FILES='${{ steps.changed-files.outputs.deleted_files_json || '[]' }}'
128+
129+
# Process files into different formats once
130+
echo "md_files_comma<<EOF" >> $GITHUB_OUTPUT
131+
echo "${{ steps.changed-files.outputs.all_changed_files }}" >> $GITHUB_OUTPUT
132+
echo "EOF" >> $GITHUB_OUTPUT
133+
134+
echo "md_files_line<<EOF" >> $GITHUB_OUTPUT
135+
echo "$CHANGED_FILES" | jq -r '.[] | select(endswith(".md"))' >> $GITHUB_OUTPUT
136+
echo "EOF" >> $GITHUB_OUTPUT
137+
138+
echo "docs_files_line<<EOF" >> $GITHUB_OUTPUT
139+
echo "$CHANGED_FILES" | jq -r '.[] | select(endswith(".md")) | select(startswith("${{ inputs.docs-dir }}/"))' >> $GITHUB_OUTPUT
140+
echo "EOF" >> $GITHUB_OUTPUT
141+
142+
echo "deleted_md_files_line<<EOF" >> $GITHUB_OUTPUT
143+
echo "$DELETED_FILES" | jq -r '.[] | select(endswith(".md"))' >> $GITHUB_OUTPUT
144+
echo "EOF" >> $GITHUB_OUTPUT
113145
114146
- name: Check if manifest changed
115147
id: manifest-check
@@ -239,7 +271,7 @@ runs:
239271
id: lint-docs
240272
shell: bash
241273
run: |
242-
lint_output=$(pnpm exec markdownlint-cli2 ${{ steps.changed-files.outputs.all_changed_files }} 2>&1) || true
274+
lint_output=$(pnpm exec markdownlint-cli2 ${{ steps.process-files.outputs.md_files_comma }} 2>&1) || true
243275
echo "result<<EOF" >> $GITHUB_OUTPUT
244276
echo "$lint_output" >> $GITHUB_OUTPUT
245277
echo "EOF" >> $GITHUB_OUTPUT
@@ -256,7 +288,7 @@ runs:
256288
shell: bash
257289
run: |
258290
# markdown-table-formatter requires a space separated list of files
259-
format_output=$(echo ${{ steps.changed-files.outputs.all_changed_files }} | tr ',' '\n' | pnpm exec markdown-table-formatter --check 2>&1) || true
291+
format_output=$(echo "${{ steps.process-files.outputs.md_files_line }}" | pnpm exec markdown-table-formatter --check 2>&1) || true
260292
echo "result<<EOF" >> $GITHUB_OUTPUT
261293
echo "$format_output" >> $GITHUB_OUTPUT
262294
echo "EOF" >> $GITHUB_OUTPUT
@@ -289,7 +321,7 @@ runs:
289321
shell: bash
290322
run: |
291323
# Run Vale on changed files and capture output
292-
vale_output=$(echo ${{ steps.changed-files.outputs.all_changed_files }} | tr ',' '\n' | grep '\.md$' | xargs -r vale --config=.github/docs/vale/.vale.ini --output=line 2>&1) || true
324+
vale_output=$(echo "${{ steps.process-files.outputs.md_files_line }}" | xargs -r vale --config=.github/docs/vale/.vale.ini --output=line 2>&1) || true
293325
294326
echo "result<<EOF" >> $GITHUB_OUTPUT
295327
echo "$vale_output" >> $GITHUB_OUTPUT
@@ -300,6 +332,137 @@ runs:
300332
echo "$vale_output"
301333
exit 1
302334
fi
335+
336+
- name: Check for broken cross-references
337+
if: inputs.check-cross-references == 'true' && steps.docs-analysis.outputs.has_changes == 'true'
338+
id: cross-references
339+
shell: bash
340+
run: |
341+
# Get the base branch (usually main)
342+
BASE_SHA=$(git merge-base HEAD origin/main)
343+
344+
echo "Checking for broken cross-references..."
345+
346+
# Initialize results
347+
BROKEN_REFS=""
348+
349+
# Process deleted files
350+
if [ -n "${{ steps.process-files.outputs.deleted_md_files_line }}" ]; then
351+
echo "Processing deleted files"
352+
353+
# Loop through deleted markdown files
354+
while IFS= read -r file; do
355+
[ -z "$file" ] && continue
356+
357+
echo "File $file was deleted, checking for references..."
358+
359+
# Convert file path to potential link formats (removing .md extension)
360+
OLD_PATH=$(echo "$file" | sed 's/\.md$//')
361+
362+
# Search in docs directory
363+
DOC_REFS=$(grep -r --include="*.md" -l -E "\[$OLD_PATH\]|\($OLD_PATH\)" ${{ inputs.docs-dir }} || echo "")
364+
365+
# Search in codebase (excluding specific directories)
366+
CODE_REFS=$(grep -r --include="*.{go,ts,js,py,java,cs,php}" -l "$OLD_PATH" . --exclude-dir={node_modules,.git,build,dist} || echo "")
367+
368+
if [ -n "$DOC_REFS" ] || [ -n "$CODE_REFS" ]; then
369+
BROKEN_REFS="${BROKEN_REFS}## References to deleted file: $file\n\n"
370+
371+
if [ -n "$DOC_REFS" ]; then
372+
BROKEN_REFS="${BROKEN_REFS}### In documentation:\n"
373+
BROKEN_REFS="${BROKEN_REFS}$(echo "$DOC_REFS" | sed 's/^/- /')\n\n"
374+
fi
375+
376+
if [ -n "$CODE_REFS" ]; then
377+
BROKEN_REFS="${BROKEN_REFS}### In codebase:\n"
378+
BROKEN_REFS="${BROKEN_REFS}$(echo "$CODE_REFS" | sed 's/^/- /')\n\n"
379+
fi
380+
fi
381+
done <<< "${{ steps.process-files.outputs.deleted_md_files_line }}"
382+
fi
383+
384+
# Process modified files for heading changes
385+
while IFS= read -r file; do
386+
[ -z "$file" ] && continue
387+
388+
if [ -f "$file" ]; then
389+
echo "Checking for changed headings in $file..."
390+
391+
# Extract headings before the change
392+
OLD_HEADINGS=$(git show "$BASE_SHA:$file" 2>/dev/null | grep -E "^#{1,6} " | sed 's/^#\{1,6\} \(.*\)$/\1/' || echo "")
393+
394+
# Extract current headings
395+
NEW_HEADINGS=$(cat "$file" | grep -E "^#{1,6} " | sed 's/^#\{1,6\} \(.*\)$/\1/')
396+
397+
# Find removed headings
398+
REMOVED_HEADINGS=$(comm -23 <(echo "$OLD_HEADINGS" | sort) <(echo "$NEW_HEADINGS" | sort))
399+
400+
if [ -n "$REMOVED_HEADINGS" ]; then
401+
while IFS= read -r heading; do
402+
[ -z "$heading" ] && continue
403+
404+
# Convert heading to anchor format (lowercase, spaces to hyphens)
405+
ANCHOR=$(echo "$heading" | tr '[:upper:]' '[:lower:]' | tr -cd '[:alnum:] -' | tr ' ' '-')
406+
407+
# Search for references to this anchor in documentation
408+
HEAD_REFS=$(grep -r --include="*.md" -l "#$ANCHOR" ${{ inputs.docs-dir }} || echo "")
409+
410+
if [ -n "$HEAD_REFS" ]; then
411+
BROKEN_REFS="${BROKEN_REFS}## References to removed heading: '$heading' in $file\n\n"
412+
BROKEN_REFS="${BROKEN_REFS}$(echo "$HEAD_REFS" | sed 's/^/- /')\n\n"
413+
fi
414+
done <<< "$REMOVED_HEADINGS"
415+
fi
416+
fi
417+
done <<< "${{ steps.process-files.outputs.docs_files_line }}"
418+
419+
# Check for renamed files by comparing paths
420+
while IFS= read -r file; do
421+
[ -z "$file" ] && continue
422+
423+
# Use git to check if this is a renamed file
424+
PREV_PATH=$(git diff --name-status "$BASE_SHA" | grep "^R" | grep "$file$" | cut -f2)
425+
426+
if [ -n "$PREV_PATH" ] && [ "$PREV_PATH" != "$file" ]; then
427+
echo "File renamed from $PREV_PATH to $file, checking for references..."
428+
429+
# Convert old file path to potential link formats
430+
OLD_PATH=$(echo "$PREV_PATH" | sed 's/\.md$//')
431+
432+
# Search in docs directory
433+
DOC_REFS=$(grep -r --include="*.md" -l -E "\[$OLD_PATH\]|\($OLD_PATH\)" ${{ inputs.docs-dir }} || echo "")
434+
435+
# Search in codebase (excluding specific directories)
436+
CODE_REFS=$(grep -r --include="*.{go,ts,js,py,java,cs,php}" -l "$OLD_PATH" . --exclude-dir={node_modules,.git,build,dist} || echo "")
437+
438+
if [ -n "$DOC_REFS" ] || [ -n "$CODE_REFS" ]; then
439+
BROKEN_REFS="${BROKEN_REFS}## References to renamed file: $PREV_PATH → $file\n\n"
440+
441+
if [ -n "$DOC_REFS" ]; then
442+
BROKEN_REFS="${BROKEN_REFS}### In documentation:\n"
443+
BROKEN_REFS="${BROKEN_REFS}$(echo "$DOC_REFS" | sed 's/^/- /')\n\n"
444+
fi
445+
446+
if [ -n "$CODE_REFS" ]; then
447+
BROKEN_REFS="${BROKEN_REFS}### In codebase:\n"
448+
BROKEN_REFS="${BROKEN_REFS}$(echo "$CODE_REFS" | sed 's/^/- /')\n\n"
449+
fi
450+
fi
451+
fi
452+
done <<< "${{ steps.process-files.outputs.md_files_line }}"
453+
454+
if [ -n "$BROKEN_REFS" ]; then
455+
echo "cross_ref_results<<EOF" >> $GITHUB_OUTPUT
456+
echo -e "$BROKEN_REFS" >> $GITHUB_OUTPUT
457+
echo "EOF" >> $GITHUB_OUTPUT
458+
459+
if [ "${{ inputs.fail-on-error }}" == "true" ]; then
460+
echo "::error::Broken cross-references found. See output for details."
461+
exit 1
462+
fi
463+
else
464+
echo "No broken cross-references found"
465+
fi
303466
304467
- name: Generate Preview URL
305468
if: inputs.generate-preview == 'true' && steps.docs-analysis.outputs.has_changes == 'true'
@@ -368,6 +531,10 @@ runs:
368531
${{ steps.lint-vale.outputs.result != '' && steps.lint-vale.outputs.result || '' }}
369532
${{ steps.lint-vale.outputs.result != '' && '```' || '' }}
370533
534+
${{ steps.cross-references.outputs.cross_ref_results != '' && '### Broken Cross-References' || '' }}
535+
${{ steps.cross-references.outputs.cross_ref_results != '' && 'The following cross-references may be broken due to file or heading changes:' || '' }}
536+
${{ steps.cross-references.outputs.cross_ref_results != '' && steps.cross-references.outputs.cross_ref_results || '' }}
537+
371538
---
372539
<sub>🤖 This comment is automatically generated and updated when documentation changes.</sub>
373540
edit-mode: replace

.github/workflows/docs-reusable-example.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ jobs:
1919
lint-markdown: true
2020
check-format: true
2121
check-links: true
22+
check-cross-references: true
2223
lint-vale: true
2324
generate-preview: true
2425
post-comment: true
@@ -77,6 +78,7 @@ jobs:
7778
check-links: "true"
7879
lint-markdown: "true"
7980
check-format: "true"
81+
check-cross-references: "true"
8082
lint-vale: "true"
8183
generate-preview: "true"
8284
post-comment: "true"
@@ -92,4 +94,8 @@ jobs:
9294
9395
if [ "${{ steps.docs-shared.outputs.format_results }}" != "" ]; then
9496
echo "Formatting issues found, please run 'make fmt/markdown' locally"
97+
fi
98+
99+
if [ "${{ steps.docs-shared.outputs.cross_ref_results }}" != "" ]; then
100+
echo "Broken cross-references found"
95101
fi

.github/workflows/docs-unified.yaml

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ on:
1717
required: false
1818
type: boolean
1919
default: true
20+
check-cross-references:
21+
description: 'Whether to check for broken cross-references when files or headings change'
22+
required: false
23+
type: boolean
24+
default: true
2025
lint-vale:
2126
description: 'Whether to run Vale style checks on documentation'
2227
required: false
@@ -51,24 +56,8 @@ jobs:
5156
with:
5257
egress-policy: audit
5358

54-
- name: Checkout
55-
uses: actions/checkout@v4
56-
with:
57-
fetch-depth: 0
58-
59-
- name: Setup Node
60-
uses: actions/setup-node@v4
61-
with:
62-
node-version: 20
63-
cache: 'pnpm'
64-
65-
- name: Install pnpm
66-
uses: pnpm/action-setup@v3
67-
with:
68-
run_install: false
69-
70-
- name: Install dependencies
71-
run: ./scripts/pnpm_install.sh
59+
- name: Setup Environment
60+
uses: ./.github/docs/actions/docs-setup
7261

7362
- name: Get PR info
7463
id: pr_info
@@ -90,6 +79,7 @@ jobs:
9079
check-links: ${{ inputs.check-links }}
9180
lint-markdown: ${{ inputs.lint-markdown }}
9281
check-format: ${{ inputs.check-format }}
82+
check-cross-references: ${{ inputs.check-cross-references }}
9383
lint-vale: ${{ inputs.lint-vale }}
9484
generate-preview: ${{ inputs.generate-preview }}
9585
post-comment: ${{ inputs.post-comment }}
@@ -115,4 +105,8 @@ jobs:
115105
116106
if [ "${{ steps.docs-shared.outputs.vale_results }}" != "" ]; then
117107
echo "Vale style issues found"
108+
fi
109+
110+
if [ "${{ steps.docs-shared.outputs.cross_ref_results }}" != "" ]; then
111+
echo "Broken cross-references found"
118112
fi

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy