1+ name : Check Broken Links in MDX Files
2+
3+ on :
4+ pull_request :
5+ paths :
6+ - ' mintlify/**/*.mdx'
7+
8+ permissions :
9+ contents : read
10+ pull-requests : write
11+
12+ jobs :
13+ check-broken-links :
14+ runs-on : ubuntu-latest
15+
16+ steps :
17+ - name : Checkout repository
18+ uses : actions/checkout@v4
19+
20+ - name : Setup Node.js
21+ uses : actions/setup-node@v4
22+ with :
23+ node-version : ' 18'
24+
25+ - name : Install Mintlify CLI
26+ run : npm install -g mintlify
27+
28+ - name : Run broken links check
29+ id : broken-links
30+ working-directory : mintlify
31+ run : |
32+ echo "Running mint broken-links in mintlify directory..."
33+
34+ # Run broken links check and capture both stdout and stderr
35+ set +e # Don't exit on error
36+ mint broken-links &> broken-links-output.txt
37+ mint_exit_code=$?
38+ set -e # Re-enable exit on error
39+
40+ echo "mint_exit_code=$mint_exit_code" >> $GITHUB_OUTPUT
41+
42+ # Show original output for debugging
43+ echo "Original mint output:"
44+ cat broken-links-output.txt
45+
46+ # Filter out /api-reference/ false positives with smarter grouping
47+ # This awk script processes the mint output and only keeps file sections that have non-/api-reference/ links
48+ awk '
49+ BEGIN {
50+ current_file = ""
51+ has_real_links = 0
52+ buffer = ""
53+ }
54+ /^[[:space:]]*$/ {
55+ # Empty line - could be separator
56+ if (current_file != "" && has_real_links) {
57+ print current_file
58+ printf "%s", buffer
59+ print ""
60+ }
61+ current_file = ""
62+ has_real_links = 0
63+ buffer = ""
64+ next
65+ }
66+ /^[^[:space:]]/ && !/^Checking for broken links/ && !/broken links found/ {
67+ # This is a filename line (starts at column 1, not whitespace, not header/footer)
68+ if (current_file != "" && has_real_links) {
69+ print current_file
70+ printf "%s", buffer
71+ }
72+ current_file = $0
73+ has_real_links = 0
74+ buffer = ""
75+ next
76+ }
77+ /^[[:space:]]+/ {
78+ # This is a link line (starts with whitespace)
79+ if ($0 !~ /\/api-reference\//) {
80+ has_real_links = 1
81+ }
82+ buffer = buffer $0 "\n"
83+ next
84+ }
85+ END {
86+ # Handle the last file section
87+ if (current_file != "" && has_real_links) {
88+ print current_file
89+ printf "%s", buffer
90+ }
91+ }
92+ ' broken-links-output.txt > filtered-output.txt
93+
94+ # Count the number of files with actual broken links (non-empty, non-header lines that don't start with space)
95+ broken_files_count=$(grep -c "^[^[:space:]].*\.mdx$" filtered-output.txt || echo "0")
96+
97+ # Check if there are any remaining broken links
98+ if [ "$broken_files_count" -gt 0 ]; then
99+ echo "has_broken_links=true" >> $GITHUB_OUTPUT
100+ echo "FILTERED_OUTPUT<<EOF" >> $GITHUB_OUTPUT
101+ cat filtered-output.txt >> $GITHUB_OUTPUT
102+ echo "EOF" >> $GITHUB_OUTPUT
103+ else
104+ echo "has_broken_links=false" >> $GITHUB_OUTPUT
105+ echo "FILTERED_OUTPUT=✅ No broken links found (after filtering out /api-reference/ paths)" >> $GITHUB_OUTPUT
106+ fi
107+
108+ - name : Comment on PR
109+ uses : actions/github-script@v7
110+ with :
111+ script : |
112+ const hasBrokenLinks = '${{ steps.broken-links.outputs.has_broken_links }}' === 'true';
113+ const filteredOutput = `${{ steps.broken-links.outputs.FILTERED_OUTPUT }}`;
114+
115+ let body;
116+ if (hasBrokenLinks) {
117+ body = `## 🔗 Broken Links Check Failed
118+
119+ The following broken links were found in the MDX files (after filtering out \`/api-reference/\` paths):
120+
121+ \`\`\`
122+ ${filteredOutput}
123+ \`\`\`
124+
125+ Please fix these broken links before merging.
126+
127+ > **Note**: Links under \`/api-reference/\` are automatically filtered out as they may be false positives.`;
128+ } else {
129+ body = `## ✅ Broken Links Check Passed
130+
131+ ${filteredOutput}
132+
133+ All MDX files in the \`mintlify/\` directory have been checked for broken links.
134+
135+ > **Note**: Links under \`/api-reference/\` are automatically filtered out as they may be false positives.`;
136+ }
137+
138+ // Find existing comment
139+ const comments = await github.rest.issues.listComments({
140+ owner: context.repo.owner,
141+ repo: context.repo.repo,
142+ issue_number: context.issue.number,
143+ });
144+
145+ const existingComment = comments.data.find(comment =>
146+ comment.user.login === 'github-actions[bot]' &&
147+ comment.body.includes('Broken Links Check')
148+ );
149+
150+ if (existingComment) {
151+ // Update existing comment
152+ await github.rest.issues.updateComment({
153+ owner: context.repo.owner,
154+ repo: context.repo.repo,
155+ comment_id: existingComment.id,
156+ body: body
157+ });
158+ } else {
159+ // Create new comment
160+ await github.rest.issues.createComment({
161+ owner: context.repo.owner,
162+ repo: context.repo.repo,
163+ issue_number: context.issue.number,
164+ body: body
165+ });
166+ }
167+
168+ - name : Fail if broken links found
169+ if : steps.broken-links.outputs.has_broken_links == 'true'
170+ run : |
171+ echo "Broken links found after filtering out /api-reference/ paths!"
172+ exit 1
0 commit comments