@@ -4,15 +4,15 @@ name: Check Links
44# Key flags:
55# --cache: Cache results to speed up subsequent runs
66# --exclude-link-local: Skip localhost/127.0.0.1 links (not reachable in CI)
7- # --exclude-file: Use .lycheeignore for links that shouldn't be checked (e.g., rate-limited sites)
7+ # .lycheeignore is auto-detected for URL patterns to exclude
88# Creates a GitHub issue on failure for visibility
99
1010on :
1111 pull_request :
1212 paths :
13- - ' **.md'
13+ - " **.md"
1414 schedule :
15- - cron : ' 0 0 * * 0' # Weekly on Sunday at midnight UTC
15+ - cron : " 0 0 * * 0" # Weekly on Sunday at midnight UTC
1616 workflow_dispatch :
1717
1818concurrency :
@@ -21,33 +21,79 @@ concurrency:
2121
2222permissions :
2323 contents : read
24- issues : write # Required by peter-evans/create- issue-from-file on failure
24+ issues : write # Required for creating issue on failure
2525
2626jobs :
2727 linkChecker :
2828 name : Check Markdown Links
2929 runs-on : ubuntu-latest
30+ timeout-minutes : 10
3031 steps :
3132 - name : Checkout code
3233 uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
34+ with :
35+ persist-credentials : false
3336
37+ # Static key: lychee handles staleness via --max-cache-age 1d
38+ # Split restore/save pattern ensures cache is saved even on failure
3439 - name : Restore lychee cache
35- uses : actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
40+ id : restore-cache
41+ uses : actions/cache/restore@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
3642 with :
3743 path : .lycheecache
38- key : cache-lychee-${{ github.sha }}
39- restore-keys : cache-lychee-
44+ key : cache-lychee-v1
4045
4146 - name : Link Checker
4247 uses : lycheeverse/lychee-action@a8c4c7cb88f0c7386610c35eb25108e448569cb0 # v2.7.0
4348 with :
44- args : --cache --max-cache-age 1d --verbose --no-progress --exclude-link-local --exclude-file .lycheeignore '**/*.md'
49+ args : >-
50+ --cache
51+ --max-cache-age 1d
52+ --verbose
53+ --no-progress
54+ --exclude-link-local
55+ '**/*.md'
4556 fail : true
4657 env :
4758 GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
4859
49- - name : Create Issue From File
60+ - name : Save lychee cache
61+ uses : actions/cache/save@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
62+ if : always()
63+ with :
64+ path : .lycheecache
65+ key : cache-lychee-v1
66+
67+ # Check for existing issue to avoid duplicates, then create or comment
68+ - name : Check for existing open issue
5069 if : failure()
70+ id : check-issue
71+ run : |
72+ issue=$(gh issue list --state open \
73+ --search "in:title Broken links detected" \
74+ --json number --jq '.[0].number // empty')
75+ echo "issue_number=$issue" >> "$GITHUB_OUTPUT"
5176 env :
5277 GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
78+
79+ - name : Create issue for broken links
80+ if : failure() && steps.check-issue.outputs.issue_number == ''
5381 run : gh issue create --title "Broken links detected" --body-file ./lychee/out.md
82+ env :
83+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
84+
85+ - name : Comment on existing issue
86+ if : failure() && steps.check-issue.outputs.issue_number != ''
87+ run : |
88+ run_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${RUN_ID}"
89+ {
90+ echo "## 🔄 Link check failed again"
91+ echo ""
92+ echo "**Run**: [${RUN_ID}](${run_url})"
93+ echo "**Triggered by**: ${GITHUB_EVENT_NAME}"
94+ echo ""
95+ cat ./lychee/out.md
96+ } | gh issue comment "${{ steps.check-issue.outputs.issue_number }}" --body-file -
97+ env :
98+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
99+ RUN_ID : ${{ github.run_id }}
0 commit comments