Skip to content

ci: check added links on PRs, sweep the rest weekly, fix two dead links - #712

Merged
rfay merged 2 commits into
mainfrom
20260803_rfay_linkspector_cloudflare_challenge_ignores
Aug 4, 2026
Merged

ci: check added links on PRs, sweep the rest weekly, fix two dead links#712
rfay merged 2 commits into
mainfrom
20260803_rfay_linkspector_cloudflare_challenge_ignores

Conversation

@rfay

@rfay rfay commented Aug 3, 2026

Copy link
Copy Markdown
Member

The Issue

The Link check workflow has failed on every push to main since 2026-07-31. Stack Overflow, tag1.com, and timeanddate.com return HTTP 403 to linkspector's headless Chrome. The 403 carries cf-mitigated: challenge, reproducible with plain curl, so it is a Cloudflare bot challenge rather than a link problem; Cloudflare's "Agent" category, offered to site owners on 2026-07-01, covers browser-driving agents.

Auditing every URL the ignore list already suppressed turned up two genuinely dead links that the list was hiding. Separately, the job's failure semantics are wrong: internal links are already validated by astro-link-validator during the build, so this workflow only ever checks third-party URLs, and it failed on 9 of the last 20 pushes to main.

How This PR Solves The Issue

Dead links fixed in the posts:

  • news.extly.com (February 2026 newsletter) resolves to 0.0.0.0. It was a syndicated copy; now points at the original Laravel News article.
  • A LinkedIn Pulse article (August 2025 newsletter) redirects to article_not_found with no Wayback snapshots. Repointed to the same tutorial on The Drop Times.
  • The timeanddate.com link to the past 2024-03-06 meeting is removed; the date remains as text.

Ignore patterns added for hosts serving a Cloudflare challenge: stackoverflow.com, timeanddate.com (kept for future meeting announcements), pixabay.com. Widened the existing tag1consulting.com pattern to cover the newer tag1.com domain.

Split the workflow into two jobs, each with the failure behavior its case deserves. PRs keep blocking on filter_mode: added, which is the case worth gating — a typo'd URL in a new post fails while the author can still fix it. The full sweep moves off push-to-main onto a weekly schedule, where it opens or updates one tracking issue instead of breaking the branch, and closes that issue when everything resolves.

Imported the hard-line-break guidance from ddev/ddev#8662 into AGENTS.md, extended to cover commit bodies (reused verbatim as PR descriptions here) and gh-posted workflow reports.

Manual Testing Instructions

Preview: https://20260803-rfay-linkspector-cl.ddev-com-front-end.pages.dev/

  • /blog/ddev-february-2026-newsletter — "Read on Laravel News" resolves
  • /blog/ddev-august-2025-newsletter — Ollama item resolves to The Drop Times
  • /blog/2024-plans — meeting date reads as plain text
  • The blocking "Check blog links" job passes on this PR
  • Run the Sweep all blog links job via workflow_dispatch to confirm it reports without failing

Automated Testing Overview

No new tests; the change is link-checker configuration, CI wiring, and content. Verified the linkspector config YAML parses, all 19 patterns compile, and they match the 12 failing URLs from run 30811952142 without matching the replacements. All 7 Stack Overflow URLs confirmed live via api.stackexchange.com. The workflow YAML parses, every run: block is shellcheck-clean at warning level, and the issue-report generation was tested end-to-end against real linkspector JSON.

Release/Deployment Notes

Link checking no longer runs on push to main, so a merge will not surface link rot. The weekly sweep is the replacement; the first run lands the following Monday, and workflow_dispatch triggers it on demand.

A Cloudflare challenge returns 403 whether or not the page exists, so the suppressed URLs on those hosts cannot be validated by CI and need occasional manual spot-checks. Cloudflare's new defaults land 2026-09-15, so more hosts may need this treatment.

🤖 Developed with assistance from Claude Code

…in linkspector

## The Issue

The Link check workflow has failed on every push to main since 2026-07-31. Stack
Overflow, tag1.com, and timeanddate.com return HTTP 403 to linkspector's headless
Chrome. The 403 carries `cf-mitigated: challenge`, reproducible with plain curl,
so it is a Cloudflare bot challenge rather than a link problem; Cloudflare's
"Agent" category, offered to site owners on 2026-07-01, covers browser-driving
agents.

Auditing every URL the ignore list already suppressed turned up two genuinely
dead links that the list was hiding.

## How This PR Solves The Issue

Dead links fixed in the posts:

- `news.extly.com` (February 2026 newsletter) resolves to 0.0.0.0. It was a
  syndicated copy; now points at the original Laravel News article.
- A LinkedIn Pulse article (August 2025 newsletter) redirects to
  `article_not_found` with no Wayback snapshots. Repointed to the same tutorial
  on The Drop Times.
- The timeanddate.com link to the past 2024-03-06 meeting is removed; the date
  remains as text.

Ignore patterns added for hosts serving a Cloudflare challenge: stackoverflow.com,
timeanddate.com (kept for future meeting announcements), pixabay.com. Widened the
existing tag1consulting.com pattern to cover the newer tag1.com domain.

## Manual Testing Instructions

Preview: https://20260803-rfay-linkspector-cl.ddev-com-front-end.pages.dev/

- /blog/ddev-february-2026-newsletter — "Read on Laravel News" resolves
- /blog/ddev-august-2025-newsletter — Ollama item resolves to The Drop Times
- /blog/2024-plans — meeting date reads as plain text
- The Link check job passes

## Automated Testing Overview

No new tests; the change is link-checker configuration and content. Verified the
YAML parses, all 19 patterns compile, and they match the 12 failing URLs from run
30811952142 without matching the replacements. All 7 Stack Overflow URLs confirmed
live via api.stackexchange.com.

## Release/Deployment Notes

A Cloudflare challenge returns 403 whether or not the page exists, so the
suppressed URLs on those hosts cannot be validated by CI and need occasional
manual spot-checks. Cloudflare's new defaults land 2026-09-15, so more hosts may
need this treatment.

🤖 Developed with assistance from [Claude Code](https://claude.ai/code)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 3, 2026

Copy link
Copy Markdown

Deploying ddev-com-front-end with  Cloudflare Pages  Cloudflare Pages

Latest commit: 360c999
Status: ✅  Deploy successful!
Preview URL: https://0fe2ece2.ddev-com-front-end.pages.dev
Branch Preview URL: https://20260803-rfay-linkspector-cl.ddev-com-front-end.pages.dev

View logs

## The Issue

Internal links are already validated by `astro-link-validator` during the Astro build, so this workflow only ever checked third-party URLs. Running the full sweep on every push to main meant any one of ~500 external links rotting turned the branch red: 9 of the last 20 pushes failed. A gate that is wrong half the time stops being read, and the ignore list grew an entry per newly-blocked host to keep it quiet.

## How This PR Solves The Issue

Split the workflow into two jobs with the failure behavior each case deserves. The `pr` job keeps blocking with `filter_mode: added`, which is the case worth gating, since a typo'd URL in a new post fails while the author is still there to fix it. The `sweep` job takes over the full run, moves off push-to-main onto a weekly cron plus `workflow_dispatch`, and opens or updates a single tracking issue rather than failing; it closes that issue when every link resolves again. Link rot becomes a chore queue instead of a broken build.

Also imported the hard-line-break guidance from ddev/ddev#8662 into `AGENTS.md`, extended to cover two cases this repo hits: commit bodies, which are reused verbatim as pull request descriptions here, and reports a workflow posts through `gh`. The sweep job's issue body is written one-line-per-paragraph for that reason.

## Manual Testing Instructions

Preview: https://20260803-rfay-linkspector-cl.ddev-com-front-end.pages.dev/

- The blocking `Check blog links` job runs and passes on this PR
- Run `Sweep all blog links` via workflow_dispatch on the branch; confirm it files an issue titled "Link check: broken external links in blog content" and that the job itself succeeds
- Run it a second time and confirm the existing issue is updated rather than a duplicate created

## Automated Testing Overview

No new tests. The workflow YAML parses and both job guards resolve as intended, every `run:` block is shellcheck-clean at warning level, and the report generation was tested end-to-end against real linkspector JSON output. The linkspector CLI contract the sweep depends on was verified directly: `-j` emits `{source, severity, diagnostics[]}` and exit status is non-zero when links fail.

## Release/Deployment Notes

Link checking no longer runs on push to main, so merging will not surface link rot; the weekly sweep replaces it and first runs the following Monday. The sweep needs `issues: write`, granted at job scope only. If linkspector itself fails rather than merely finding bad links, the job fails loudly instead of silently reporting zero.

🤖 Developed with assistance from [Claude Code](https://claude.ai/code)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
@rfay rfay changed the title fix: replace two dead blog links, ignore Cloudflare-challenged hosts in linkspector ci: check added links on PRs, sweep the rest weekly, fix two dead links Aug 3, 2026
@rfay
rfay merged commit 10b0857 into main Aug 4, 2026
6 checks passed
@rfay
rfay deleted the 20260803_rfay_linkspector_cloudflare_challenge_ignores branch August 4, 2026 00:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant