broken-links #5
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: broken-links | |
| on: | |
| schedule: | |
| # Nightly at 09:00 UTC (around 03:00 MT / 04:00 CT). | |
| - cron: '0 9 * * *' | |
| workflow_dispatch: {} | |
| permissions: | |
| issues: write | |
| contents: read | |
| jobs: | |
| crawl: | |
| name: Crawl site for broken links | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Pick target URL | |
| id: target | |
| # Default to the current preview host (beta.ontargetaba.com on | |
| # the OnTargetDevs CF Pages project). Override via the STAGING_URL | |
| # secret if a different host should be crawled. | |
| run: | | |
| if [ -n "${{ secrets.STAGING_URL }}" ]; then | |
| echo "url=${{ secrets.STAGING_URL }}" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "url=https://beta.ontargetaba.com/" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Run Lychee | |
| id: lychee | |
| uses: lycheeverse/lychee-action@v2 | |
| with: | |
| args: >- | |
| --verbose | |
| --no-progress | |
| --max-retries 2 | |
| --retry-wait-time 5 | |
| --timeout 20 | |
| --accept 200,206,429 | |
| --include-verbatim | |
| --max-recursion-depth 3 | |
| --exclude '^https://ontargetaba\.com/' | |
| --exclude '^https://static\.hotjar\.com/c/hotjar-$' | |
| --exclude 'search_term_string' | |
| --exclude '^https://(www\.)?linkedin\.com/' | |
| --exclude '^https://(www\.)?instagram\.com/' | |
| --exclude '^https://(www\.)?facebook\.com/' | |
| ${{ steps.target.outputs.url }} | |
| fail: false | |
| output: ./lychee-report.md | |
| - name: Find existing tracking issue | |
| id: existing | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| num=$(gh issue list --state open --label broken-links \ | |
| --search "Broken links found" \ | |
| --json number --jq '.[0].number // empty') | |
| echo "number=$num" >> "$GITHUB_OUTPUT" | |
| - name: Open or update tracking issue | |
| if: steps.lychee.outputs.exit_code != '0' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| TARGET_URL: ${{ steps.target.outputs.url }} | |
| RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| run: | | |
| date_str=$(date -u +%Y-%m-%d) | |
| title="Broken links found ${date_str}" | |
| # Build the body in a file. We previously stitched it with | |
| # bash $(...) + +=, but command substitution strips trailing | |
| # newlines, so when lychee-report.md didn't end with one the | |
| # closing ``` ended up smushed against the last content line. | |
| # Writing through printf to a real file gives us deterministic | |
| # line endings — closing fence is always on its own line. | |
| { | |
| printf '## Broken links\n\n' | |
| printf 'Target: %s\n' "$TARGET_URL" | |
| printf 'Run: %s\n\n' "$RUN_URL" | |
| printf '```\n' | |
| if [ -s ./lychee-report.md ]; then | |
| cat ./lychee-report.md | |
| else | |
| printf 'See run logs for details.\n' | |
| fi | |
| # Force a final newline before the closing fence regardless | |
| # of whether the report ended with one. | |
| printf '\n```\n' | |
| } > /tmp/issue-body.md | |
| if [ -n "${{ steps.existing.outputs.number }}" ]; then | |
| gh issue edit "${{ steps.existing.outputs.number }}" \ | |
| --title "$title" \ | |
| --body-file /tmp/issue-body.md | |
| else | |
| gh issue create \ | |
| --title "$title" \ | |
| --body-file /tmp/issue-body.md \ | |
| --label broken-links | |
| fi | |
| - name: Close tracking issue when clean | |
| if: steps.lychee.outputs.exit_code == '0' && steps.existing.outputs.number != '' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| gh issue close "${{ steps.existing.outputs.number }}" \ | |
| --comment "All previously broken links are now resolving. Closing." |