-
Notifications
You must be signed in to change notification settings - Fork 17
175 lines (143 loc) · 5.33 KB
/
Copy pathcheck-links.yml
File metadata and controls
175 lines (143 loc) · 5.33 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
name: Check Links
on:
# Run weekly on Sundays at midnight UTC
schedule:
- cron: '0 0 * * 0'
# Allow manual trigger
workflow_dispatch:
# Run on PRs that modify markdown files
pull_request:
paths:
- '**/*.md'
jobs:
check-links:
name: Check Markdown Links
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Check links with lychee
uses: lycheeverse/lychee-action@v1
with:
# Check all markdown files
args: >-
--verbose
--no-progress
--accept 200,204,206,301,302,307,308
--exclude-mail
--exclude-path node_modules
--exclude-path .git
--exclude-path .worktrees
--exclude 'linkedin\.com'
--exclude 'twitter\.com'
--exclude 'x\.com'
--exclude 'localhost'
--exclude '127\.0\.0\.1'
--timeout 30
--retry-wait-time 5
--max-retries 3
'**/*.md'
# Don't fail the workflow on broken links (report only)
fail: false
# Output format
format: markdown
# Output file
output: ./lychee-report.md
- name: Upload link check report
if: always()
uses: actions/upload-artifact@v4
with:
name: link-check-report
path: ./lychee-report.md
retention-days: 30
- name: Comment on PR with link issues
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const reportPath = './lychee-report.md';
if (fs.existsSync(reportPath)) {
const report = fs.readFileSync(reportPath, 'utf8');
// Only comment if there are issues
if (report.includes('Errors') || report.includes('Warnings')) {
const body = `## Link Check Report
The following link issues were found in this PR:
<details>
<summary>Click to expand report</summary>
${report}
</details>
> Note: Some external links may fail due to rate limiting. Please verify manually if needed.`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: body
});
}
}
check-internal-links:
name: Check Internal Links
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Check internal markdown links
run: |
echo "Checking internal markdown links..."
# Create a simple internal link checker
cat << 'SCRIPT' > check_internal_links.py
import os
import re
import sys
from pathlib import Path
from urllib.parse import unquote
def find_broken_links():
broken_links = []
for md_file in Path('.').rglob('*.md'):
if any(x in str(md_file) for x in ['node_modules', '.git', '.worktrees']):
continue
try:
content = md_file.read_text(encoding='utf-8', errors='ignore')
except:
continue
# Find markdown links: [text](path) but not external URLs
links = re.findall(r'\[([^\]]*)\]\(([^)]+)\)', content)
for link_text, link_path in links:
# Skip external links, anchors only, and mailto
if link_path.startswith(('http://', 'https://', '#', 'mailto:')):
continue
# Remove anchor from path
clean_path = link_path.split('#')[0]
if not clean_path:
continue
# URL decode the path
clean_path = unquote(clean_path)
# Resolve relative path
if clean_path.startswith('/'):
target = Path('.') / clean_path[1:]
else:
target = md_file.parent / clean_path
# Check if target exists
try:
target = target.resolve()
if not target.exists():
broken_links.append((str(md_file), link_path, link_text))
except:
pass
return broken_links
def main():
broken = find_broken_links()
if broken:
print("::warning::Potentially broken internal links found:")
for source, link, text in broken[:20]: # Limit output
print(f" {source}: [{text}]({link})")
if len(broken) > 20:
print(f" ... and {len(broken) - 20} more")
else:
print("All internal links appear valid!")
sys.exit(0)
if __name__ == '__main__':
main()
SCRIPT
python check_internal_links.py