Check broken links #79
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Check broken links | |
| on: | |
| schedule: | |
| - cron: '0 8 * * *' # Run daily at 08:00 UTC | |
| # Triggers the workflow on push or pull request events | |
| push: | |
| branches: [ main ] | |
| paths: | |
| - '**.md' | |
| pull_request: | |
| paths: | |
| - '**.md' | |
| # Allows you to run this workflow manually from the Actions tab | |
| workflow_dispatch: | |
| jobs: | |
| LinkChecker: | |
| name: Link Checker | |
| runs-on: ubuntu-latest | |
| concurrency: | |
| group: check-links-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| issues: write | |
| actions: write | |
| pull-requests: write | |
| steps: | |
| - uses: actions/checkout@v5.0.0 | |
| # Get latest curl version for user-agent header | |
| - name: Get latest curl version from GitHub API | |
| id: curl_version | |
| run: | | |
| version=$(curl -s https://api.github.com/repos/curl/curl/releases/latest \ | |
| | jq -r '.tag_name' | sed 's/curl-//' | sed 's/_/./g') | |
| echo "latest_version=$version" >> $GITHUB_OUTPUT | |
| # ========================================================================= | |
| # CACHE RESTORE STEP - The Decision Point | |
| # ========================================================================= | |
| - name: Restore lychee cache | |
| id: restore-cache | |
| uses: actions/cache/restore@v4.3.0 | |
| with: | |
| path: .lycheecache | |
| # Primary: Content-based key (most specific) | |
| key: lychee-${{ runner.os }}-${{ github.ref }}-${{ hashFiles('docs/Content/*.md', 'docs/PlusContent/*.md', 'docs/README.md') }} | |
| # Fallback hierarchy: | |
| restore-keys: | | |
| lychee-${{ runner.os }}-${{ github.ref }}- | |
| - name: List cache entries | |
| if: steps.restore-cache.outputs.cache-hit == 'true' | |
| run: cat .lycheecache | |
| # ========================================================================= | |
| # CACHE HIT SCENARIO - Fast Path | |
| # ========================================================================= | |
| - name: Log cache status | |
| run: | | |
| if [ "${{ steps.restore-cache.outputs.cache-hit }}" = "true" ]; then | |
| echo "🎯 CACHE HIT: Using cached link check results" | |
| echo "Cache Key: ${{ steps.restore-cache.outputs.cache-primary-key }}" | |
| else | |
| echo "🔄 CACHE MISS: Will check all links and create new cache" | |
| fi | |
| # Run Lychee link checker | |
| - name: Lychee Broken Link Checker | |
| id: lychee | |
| uses: lycheeverse/lychee-action@v2.7.0 | |
| with: | |
| # Check all markdown files in docs but exclude language folders under docs | |
| args: >- | |
| --root-dir "${{ github.workspace }}/docs" | |
| --user-agent "curl/${{ steps.curl_version.outputs.latest_version }}" | |
| --no-progress | |
| --max-redirects 100 | |
| --accept 200,403,429,500 | |
| --cache | |
| --max-cache-age 7d | |
| './**/*.md' | |
| --exclude './ar/**' | |
| --exclude './de/**' | |
| --exclude './en/**' | |
| --exclude './es/**' | |
| --exclude './fr/**' | |
| --exclude './hu/**' | |
| --exclude './id/**' | |
| --exclude './it/**' | |
| --exclude './ja/**' | |
| --exclude './ko/**' | |
| --exclude './nl/**' | |
| --exclude './pl/**' | |
| --exclude './pt/**' | |
| --exclude './pt-BR/**' | |
| --exclude './ru/**' | |
| --exclude './sv/**' | |
| --exclude './tr/**' | |
| --exclude './uk/**' | |
| --exclude './vi/**' | |
| --exclude './zh-CN/**' | |
| --exclude './zh-TW/**' | |
| # --verbose | |
| fail: true | |
| output: lychee-report.md | |
| # ========================================================================= | |
| # CACHE SAVE STEP - Different Behavior Based on Scenario | |
| # ========================================================================= | |
| - name: Save lychee cache | |
| if: steps.restore-cache.outputs.cache-hit != 'true' | |
| uses: actions/cache/save@v4.3.0 | |
| with: | |
| path: .lycheecache | |
| key: ${{ steps.restore-cache.outputs.cache-primary-key }} | |
| - name: Log cache save result | |
| if: always() | |
| run: | | |
| if [ "${{ steps.restore-cache.outputs.cache-hit }}" = "true" ]; then | |
| echo "✅ Cache refreshed with link data from the last 7 days" | |
| else | |
| echo "🆕 New cache created with current link check results" | |
| fi | |
| # Upload report as artifact if broken links are found | |
| - name: Upload Lychee report | |
| if: failure() | |
| uses: actions/upload-artifact@v5.0.0 | |
| with: | |
| name: lychee-report | |
| path: lychee-report.md | |
| # To avoid automated spam, try to find an existing open issue before opening a new one | |
| # Source: https://github.com/cilium/tetragon/pull/805 | |
| - name: Search for existing issue number | |
| if: failure() && github.ref == 'refs/heads/main' | |
| id: search_issue | |
| env: | |
| ISSUE_NAME: "Link Checker Report" | |
| run: | | |
| encoded_issue_name=$(echo "$ISSUE_NAME" | sed 's/ /%20/g') | |
| response=$(curl -s -X GET \ | |
| "https://api.github.com/search/issues?q=$encoded_issue_name+repo:${{ github.repository }}+state:open+label:automated-report&type=Issues") | |
| issue_number=$(echo "$response" | jq -r '.items[0].number // empty') | |
| echo "issue_number=$issue_number" >> $GITHUB_OUTPUT | |
| - name: Create issue from file | |
| if: failure() && github.ref == 'refs/heads/main' | |
| uses: peter-evans/create-issue-from-file@v6.0.0 | |
| with: | |
| title: Link Checker Report | |
| content-filepath: lychee-report.md | |
| issue-number: ${{ steps.search_issue.outputs.issue_number }} | |
| labels: automated-report |