forked from ai-dynamo/dynamo
-
Notifications
You must be signed in to change notification settings - Fork 0
240 lines (207 loc) · 9.74 KB
/
docs-link-check.yml
File metadata and controls
240 lines (207 loc) · 9.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
name: Docs link check
on:
push:
branches:
- main
pull_request:
permissions:
contents: read
jobs:
lychee:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
# Cache lychee results (e.g. to avoid hitting rate limits)
# https://lychee.cli.rs/github_action_recipes/caching/
- name: Restore lychee cache
uses: actions/cache@v4
with:
path: .lycheecache
key: cache-lychee-${{ github.sha }}
restore-keys: cache-lychee-
- name: Check documentation links with lychee
uses: lycheeverse/lychee-action@a8c4c7cb88f0c7386610c35eb25108e448569cb0 # v2.0.2
with:
args: >-
--cache
--no-progress
--max-retries 2
--retry-wait-time 2
--timeout 20
--root-dir ${{ github.workspace }}
--exclude-path ".*ATTRIBUTIONS.*"
--exclude-path "./lib/llm/tests/data/.*"
--accept "200..=299, 403, 429"
--exclude-all-private
--exclude 0.0.0.0
${{ github.event_name == 'pull_request' && '--offline' || '' }}
.
fail: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
broken-links-check:
name: Check for broken markdown links
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
# No additional dependencies needed for the basic script
- name: Run broken links check
id: check-links
run: |
echo "Running broken links check on documentation files..."
# Run the broken links detection script and capture exit code
set +e # Don't exit immediately on error
python3 .github/workflows/detect_broken_links.py \
--verbose \
--format json \
--check-symlinks \
--output broken-links-report.json \
.
exit_code=$?
set -e # Re-enable exit on error
# Check if the script found any broken links (exit code 1 means broken links found)
if [ $exit_code -eq 1 ]; then
echo "::error::Broken links found in documentation files"
echo "broken_links_found=true" >> $GITHUB_OUTPUT
elif [ $exit_code -eq 0 ]; then
echo "::notice::No broken links found"
echo "broken_links_found=false" >> $GITHUB_OUTPUT
else
echo "::error::Script failed with exit code $exit_code"
echo "broken_links_found=error" >> $GITHUB_OUTPUT
exit $exit_code
fi
- name: Display broken links and create annotations
if: steps.check-links.outputs.broken_links_found == 'true'
run: |
echo "::group::🔗 Broken Links Found"
# Parse and display the JSON report in a readable format, plus create GitHub annotations
python3 -c "
import json
import sys
import os
try:
with open('broken-links-report.json', 'r') as f:
report = json.load(f)
summary = report['summary']
broken_links = report['broken_links']
problematic_symlinks = report.get('problematic_symlinks', {})
has_broken_links = bool(broken_links)
has_problematic_symlinks = bool(problematic_symlinks)
if has_broken_links:
print('❌ BROKEN LINKS DETECTED')
print('=' * 50)
print(f'📊 Summary:')
print(f' • Total files processed: {summary[\"total_files_processed\"]}')
print(f' • Files with broken links: {summary[\"files_with_broken_links\"]}')
print(f' • Total broken links: {summary[\"total_broken_links\"]}')
print()
if broken_links:
print('🔍 Detailed Broken Links Report:')
print('-' * 40)
for file_path, links in broken_links.items():
print(f'\\n📄 File: {file_path}')
print(f' {len(links)} broken link(s) found')
print()
for i, link in enumerate(links, 1):
line = link['line']
link_text = link['link_text']
link_url = link['link_url']
error_reason = link.get('error_reason', 'Link target not found')
github_url = link.get('github_url', '')
# Create GitHub annotation for each broken link
annotation_msg = f'Broken link: [{link_text}]({link_url}) - {error_reason}'
if github_url:
annotation_msg += f' - View: {github_url}'
print(f'::error file={file_path},line={line}::{annotation_msg}')
# Display in workflow output
print(f' {i}. Line {line}: [{link_text}]({link_url})')
print(f' ❌ {error_reason}')
if github_url:
print(f' 🔗 View on GitHub: {github_url}')
else:
print(f' 📍 Target: {link_url}')
print()
if has_problematic_symlinks:
if has_broken_links:
print('\\n' + '=' * 50)
print('🔗 PROBLEMATIC SYMBOLIC LINKS DETECTED')
print('=' * 50)
print(f'📊 Summary:')
print(f' • Total problematic symlinks: {summary.get(\"total_problematic_symlinks\", 0)}')
print()
for category, symlinks in problematic_symlinks.items():
if symlinks:
category_icons = {
'broken': '💔',
'circular': '🔄',
'external': '🌐',
'suspicious': '⚠️'
}
icon = category_icons.get(category, '❓')
print(f'{icon} {category.upper()} SYMLINKS ({len(symlinks)}):')
print('-' * 40)
for i, symlink in enumerate(symlinks, 1):
symlink_path = symlink['symlink_path']
target_path = symlink['target_path']
issue = symlink['issue']
# Create GitHub annotation for each problematic symlink
annotation_msg = f'Problematic symlink: {issue}'
print(f'::error file={symlink_path}::{annotation_msg}')
# Display in workflow output
print(f' {i}. {symlink_path}')
print(f' → {target_path}')
print(f' ❌ {issue}')
print()
print('=' * 50)
print('✅ Next Steps:')
if has_broken_links:
print('1. Check the annotations above in the Files Changed tab (for PRs)')
print('2. Click the GitHub links to jump directly to each broken link')
print('3. Fix all broken links before merging')
if has_problematic_symlinks:
step_num = 4 if has_broken_links else 1
print(f'{step_num}. Review and fix problematic symbolic links')
print(f'{step_num + 1}. Consider replacing broken symlinks with actual files or fixing targets')
print(f'{step_num + 2}. Evaluate if suspicious symlinks with many traversals are necessary')
final_step = (7 if has_broken_links and has_problematic_symlinks
else 4 if has_broken_links
else 4 if has_problematic_symlinks
else 1)
print(f'{final_step}. Re-run this workflow to verify fixes')
except Exception as e:
print(f'❌ Error reading broken links report: {e}')
sys.exit(1)
"
echo "::endgroup::"
- name: Alert for broken links
if: steps.check-links.outputs.broken_links_found == 'true' || steps.check-links.outputs.broken_links_found == 'error'
run: |
if [ "${{ steps.check-links.outputs.broken_links_found }}" = "error" ]; then
echo "::error::Workflow failed due to script error"
echo "Please check the workflow logs for details."
exit 1
else
echo "::error::❌ WORKFLOW FAILED: Broken links found in documentation files"
echo ""
echo "🔍 What to do next:"
echo "1. Check the 'Display broken links and create annotations' step above for details"
echo "2. Look for ::error annotations in the Files Changed tab (for PRs)"
echo "3. Click the GitHub URLs in the workflow output to jump directly to each broken link"
echo "4. Fix all broken links before merging"
echo ""
echo "💡 Each broken link shows:"
echo " - File name and line number where the broken link is located"
echo " - The broken link text and target URL"
echo " - A clickable GitHub URL to jump directly to the problem line"
exit 1
fi