diff --git a/.github/workflows/link-check.yml b/.github/workflows/link-check.yml new file mode 100644 index 00000000..b31d1e4e --- /dev/null +++ b/.github/workflows/link-check.yml @@ -0,0 +1,113 @@ +name: Link Checker + +on: + pull_request: + types: [opened, synchronize, reopened] + workflow_dispatch: + +jobs: + link-check: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Restore lychee cache + uses: actions/cache@v4 + with: + path: .lycheecache + key: cache-lychee-${{ github.sha }} + restore-keys: cache-lychee- + + - name: Run Lychee link checker + uses: lycheeverse/lychee-action@v2 + id: lychee + with: + # Check all markdown files + args: | + --cache + --max-cache-age 1d + --verbose + --no-progress + --exclude-mail + '**/*.md' + # Output results to file for parsing + output: lychee-output.md + # Don't fail the build on broken links (warning mode) + fail: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Parse link check results + id: parse-results + if: always() + run: | + # Initialize defaults + BROKEN="0" + TOTAL="0" + + # Parse lychee output for statistics + if [ -f "lychee-output.md" ]; then + # Extract statistics from lychee output + TOTAL=$(grep -oP 'Total\s+\|\s+\K\d+' lychee-output.md || echo "0") + BROKEN=$(grep -oP 'Errors\s+\|\s+\K\d+' lychee-output.md || echo "0") + + # If stats not found in that format, try alternate parsing + if [ "$TOTAL" = "0" ] && [ "$BROKEN" = "0" ]; then + BROKEN=$(grep -c "❌\|✗\|ERROR" lychee-output.md || echo "0") + fi + fi + + # Set status based on results + if [ "$BROKEN" = "0" ]; then + STATUS_ICON="✅" + STATUS_TEXT="All links are working!" + COLOR="#36a64f" + else + STATUS_ICON="⚠️" + STATUS_TEXT="Found $BROKEN broken link(s)" + COLOR="#ffa500" + fi + + # Extract broken links for summary (limit to first 15 lines) + BROKEN_LINKS="" + if [ -f "lychee-output.md" ] && [ "$BROKEN" != "0" ]; then + # Get the errors section from lychee output + BROKEN_LINKS=$(awk '/## Errors/,/^$/' lychee-output.md | grep -E "^\*|http|^ " | head -15 || echo "") + + # If no errors section found, try to extract failed URLs + if [ -z "$BROKEN_LINKS" ]; then + BROKEN_LINKS=$(grep -B 1 "❌\|✗\|ERROR" lychee-output.md | head -15 || echo "") + fi + fi + + # Export for Slack notification + echo "STATUS_ICON=$STATUS_ICON" >> $GITHUB_ENV + echo "STATUS_TEXT=$STATUS_TEXT" >> $GITHUB_ENV + echo "COLOR=$COLOR" >> $GITHUB_ENV + echo "BROKEN_COUNT=$BROKEN" >> $GITHUB_ENV + echo "TOTAL_COUNT=$TOTAL" >> $GITHUB_ENV + + # Save broken links to multiline env var + { + echo 'BROKEN_LINKS<> $GITHUB_ENV + + - name: Send results to Slack + if: always() + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + SLACK_CHANNEL: ${{ vars.SLACK_CHANNEL }} + SLACK_TITLE: "🔗 Link Check Results" + SLACK_COLOR: ${{ env.COLOR }} + SLACK_MESSAGE: | + *Status:* ${{ env.STATUS_ICON }} ${{ env.STATUS_TEXT }} + *Total Links Checked:* ${{ env.TOTAL_COUNT }} + *Broken Links Found:* ${{ env.BROKEN_COUNT }} + + ${{ env.BROKEN_COUNT != '0' && format('*Sample Broken Links:*\n```\n{0}\n```\n\n_See full details in the workflow run_', env.BROKEN_LINKS) || '' }} + + *Run Details:* ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}