diff --git a/.github/workflows/link-check.yml b/.github/workflows/link-check.yml index b01d1e5..b553c06 100644 --- a/.github/workflows/link-check.yml +++ b/.github/workflows/link-check.yml @@ -1,18 +1,86 @@ -name: Link check +name: Link Check on: + schedule: + - cron: "0 3 * * 0" # Every Sunday at 3 AM UTC pull_request: + paths: + - "**.md" workflow_dispatch: - schedule: - - cron: "0 17 1 * *" + +permissions: + contents: read + issues: write jobs: - links: + link-check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + + - name: Restore lychee cache + uses: actions/cache@v4 + with: + path: .lycheecache + key: cache-lychee-${{ github.sha }} + restore-keys: cache-lychee- + + # On PRs, only check changed markdown files to avoid failing on + # pre-existing broken links unrelated to this PR. + - name: Get changed markdown files + id: changed + if: github.event_name == 'pull_request' + run: | + files=$(gh pr diff ${{ github.event.pull_request.number }} --name-only | grep '\.md$' || true) + echo "md_files<> "$GITHUB_OUTPUT" + echo "$files" >> "$GITHUB_OUTPUT" + echo "EOF" >> "$GITHUB_OUTPUT" + if [ -z "$files" ]; then + echo "has_md=false" >> "$GITHUB_OUTPUT" + else + echo "has_md=true" >> "$GITHUB_OUTPUT" + fi + env: + GH_TOKEN: ${{ github.token }} + + - name: Check links (all files) + id: lychee-all + if: github.event_name != 'pull_request' + uses: lycheeverse/lychee-action@v2 + with: + args: >- + --verbose + --no-progress + --timeout 10 + --max-retries 3 + --max-concurrency 8 + --cache + --max-cache-age 1d + '**/*.md' + fail: false + output: /tmp/lychee/out.md + + - name: Check links (changed files only) + id: lychee-pr + if: github.event_name == 'pull_request' && steps.changed.outputs.has_md == 'true' + uses: lycheeverse/lychee-action@v2 + with: + args: >- + --verbose + --no-progress + --timeout 10 + --max-retries 3 + --max-concurrency 8 + --cache + --max-cache-age 1d + ${{ steps.changed.outputs.md_files }} + fail: true + output: /tmp/lychee/out.md + + - name: Open issue on broken links (scheduled runs only) + if: github.event_name == 'schedule' && steps.lychee-all.outputs.exit_code != 0 + uses: peter-evans/create-issue-from-file@v5 with: - python-version: "3.x" - - name: Check links - run: python scripts/check_links.py --online --include-resources + title: "🔗 Broken links found" + content-filepath: /tmp/lychee/out.md + labels: maintenance diff --git a/.lycheeignore b/.lycheeignore new file mode 100644 index 0000000..aa8c9b8 --- /dev/null +++ b/.lycheeignore @@ -0,0 +1,16 @@ +# Known flaky or rate-limited URLs +# Add URLs (one per line) that should be excluded from link checking. +# Supports regex patterns. + +# Twitter/X often rate-limits or blocks automated checks +https?://(www\.)?twitter\.com(/.*)? +https?://(www\.)?x\.com(/.*)? + +# LinkedIn blocks automated requests +https?://(www\.)?linkedin\.com(/.*)? + +# Facebook blocks automated requests +https?://(www\.)?facebook\.com(/.*)? + +# Archive.org can be slow or intermittently unavailable +https?://web\.archive\.org(/.*)? diff --git a/README.md b/README.md index d44a241..a41f68b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Learn to Code +[![Link Check](https://github.com/ashleymcnamara/learn_to_code/actions/workflows/link-check.yml/badge.svg)](https://github.com/ashleymcnamara/learn_to_code/actions/workflows/link-check.yml) + A free-first, beginner-friendly guide to learning programming, computer science, AI, and the practical tools developers use every day.