-
Notifications
You must be signed in to change notification settings - Fork 16
ci: check links in skill content #312
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
peter-trost
wants to merge
6
commits into
PostHog:main
Choose a base branch
from
peter-trost:ci/add-linter
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
51dffc5
ci: check links in skill content
peter-trost ddbc334
ci: address review feedback on link checks
peter-trost 896b3e8
ci: scan the whole repo, drop remote fragment checks
peter-trost f2d2fe8
ci: drop the cross-file comment reference and the flake disclaimer
peter-trost b4cfdca
ci: record why redirects are followed, not flagged
peter-trost 9a47fde
ci: raise link-check retries to absorb transient network failures
peter-trost File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| name: PostHog Context Mill - Link check (external) | ||
| permissions: | ||
| contents: read | ||
|
|
||
| # Nightly check of external URLs. Deliberately NOT a PR check: an external site | ||
| # going down has nothing to do with the PR in front of it, and blocking merges | ||
| # on someone else's 502 trains people to ignore CI. | ||
| # | ||
| # Why this exists: a dead link in context/ ships into agent context windows and | ||
| # degrades every wizard and MCP user until a human notices by hand. | ||
|
|
||
| on: | ||
| schedule: | ||
| # 08:00 UTC daily (00:00 PST). | ||
| - cron: '0 8 * * *' | ||
| workflow_dispatch: | ||
|
|
||
| jobs: | ||
| link-check-external: | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 20 | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | ||
|
|
||
| # We only want to hear about links that are GONE — a server successfully | ||
| # telling us the resource does not exist. Anything else is the server | ||
| # failing to answer, which says nothing about the link and resolves on | ||
| # its own. So each non-2xx below is accepted, with the reason: | ||
| # | ||
| # 403 Ambiguous: usually bot/WAF blocking of datacenter IPs (the link | ||
| # is fine in a browser), occasionally a genuine access change. Not | ||
| # distinguishable from the status code alone, and the noise case | ||
| # dominates. TRADE-OFF: if a docs page ever goes auth-only, this | ||
| # check stays silent about it. | ||
| # 429 Rate limiting — a statement about our request volume, not about | ||
| # whether the resource exists. | ||
| # timeouts Transient network trouble; --accept-timeouts covers the | ||
| # case retries do not. | ||
| # 3xx Followed, not flagged. posthog.com redirects moved docs rather | ||
| # than 404ing, so a moved page still resolves and an agent still | ||
| # lands on the content. Flagging redirects would mean triaging | ||
| # ~40 a night, most of them auth gates that are correct as | ||
| # written. When a redirect is eventually retired it becomes a | ||
| # 404 and this check catches it the same night. | ||
| # | ||
| # 5xx is deliberately NOT accepted. lychee retries (see --max-retries | ||
| # below), which absorbs a momentary blip; a 5xx that survives retries | ||
| # every night means the host is durably broken and the link is | ||
| # effectively dead for an agent, which is worth hearing about. | ||
| # | ||
| # --max-retries 5 / --retry-wait-time 2, up from the 3-at-1s default. | ||
| # Retries fire on connection-level failures (DNS, refused, reset) but NOT | ||
| # on a resolved status code: a 404 is returned immediately without | ||
| # retrying, so raising this costs nothing on the path that matters and | ||
| # only buys patience for transient network trouble. The wait backs off | ||
| # exponentially rather than staying flat, so these numbers are a ceiling | ||
| # of roughly a minute for a genuinely unreachable host, not 5x2s. | ||
| # | ||
| # No --include-fragments here, unlike the offline check. posthog.com | ||
| # renders some headings client-side, so a #fragment that works in a | ||
| # browser is absent from the HTML lychee sees — indistinguishable from | ||
| # real rot, on the host that dominates our links. Anchors are still | ||
| # checked offline, where the target is a local file and can't lie. | ||
| # | ||
| # Every .md in the repo, so a new directory is covered without touching | ||
| # this file. Scoped to *.md rather than '.' because bare '.' also parses | ||
| # HTML, where root-relative asset paths (/favicon.ico) can't resolve | ||
| # without --root-dir and report as errors. | ||
| - name: Check external links | ||
| id: lychee | ||
| uses: lycheeverse/lychee-action@e7477775783ea5526144ba13e8db5eec57747ce8 # v2.9.0 | ||
| with: | ||
| # Pinned: the action SHA alone doesn't pin the lychee binary it fetches. | ||
| lycheeVersion: v0.24.2 | ||
| args: >- | ||
| -X get | ||
| --accept 200..=204,403,429 | ||
| --accept-timeouts | ||
| --max-retries 5 | ||
| --retry-wait-time 2 | ||
| --no-progress | ||
| --exclude-path node_modules | ||
| --exclude-path dist | ||
| './**/*.md' | ||
| # Don't fail here — the webhook step below still needs to run. The job | ||
| # fails at the end instead, so GitHub shows it red too. | ||
| fail: false | ||
|
|
||
| # Mirrors the failure-reporting pattern in e2e.yml / integration.yml. | ||
| # -f so the workflow fails if the webhook is down and we can fix it. | ||
| - name: Send failure event to PostHog | ||
| if: steps.lychee.outputs.exit_code == 2 | ||
| env: | ||
| COMMIT_SHA: ${{ github.sha }} | ||
| GH_REF: ${{ github.ref }} | ||
| GH_WORKFLOW: ${{ github.workflow }} | ||
| RUN_ID: ${{ github.run_id }} | ||
| RUN_NUMBER: ${{ github.run_number }} | ||
| JOB_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | ||
| run: | | ||
| curl -f -X POST https://webhooks.us.posthog.com/public/webhooks/019a7a81-7961-0000-d3e3-b5f34cc2a32b \ | ||
| -H "Content-Type: application/json" \ | ||
| -d "$(jq -n \ | ||
| --arg event "posthog-context-mill-repo-link-rot" \ | ||
| --arg commitSha "$COMMIT_SHA" \ | ||
| --arg ref "$GH_REF" \ | ||
| --arg workflow "$GH_WORKFLOW" \ | ||
| --arg runId "$RUN_ID" \ | ||
| --arg runNumber "$RUN_NUMBER" \ | ||
| --arg jobUrl "$JOB_URL" \ | ||
| '{event: $event, commitSha: $commitSha, ref: $ref, workflow: $workflow, runId: $runId, runNumber: $runNumber, jobUrl: $jobUrl}')" | ||
|
|
||
| # Last, so the webhook always fires first. Also catches the case where | ||
| # lychee exits nonzero for a reason other than 2 — without this the job | ||
| # would go green having checked nothing. | ||
| - name: Fail on dead links | ||
| if: steps.lychee.outputs.exit_code != 0 | ||
| run: | | ||
| echo "::error::Link check failed (lychee exit ${{ steps.lychee.outputs.exit_code }}) — see the 'Check external links' step above for each file, line and URL." | ||
| echo "False positive? .lycheeignore at the repo root explains how to handle it." | ||
| exit 1 |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| name: PostHog Context Mill - Link check (offline) | ||
| permissions: | ||
| contents: read | ||
|
|
||
| # Blocking PR check for links that CANNOT flake: relative file paths and | ||
| # #anchors within the repo. | ||
|
|
||
| on: | ||
| push: | ||
| branches: [ main ] | ||
| pull_request: | ||
| branches: [ main ] | ||
|
|
||
| jobs: | ||
| link-check-offline: | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 10 | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | ||
|
|
||
| # Every .md in the repo, so a new directory is covered without touching | ||
| # this file. Scoped to *.md rather than '.' because bare '.' also parses | ||
| # HTML, where root-relative asset paths (/favicon.ico) can't resolve | ||
| # without --root-dir and report as errors. | ||
| - name: Check relative links and anchors | ||
| uses: lycheeverse/lychee-action@e7477775783ea5526144ba13e8db5eec57747ce8 # v2.9.0 | ||
| with: | ||
| # Pinned: the action SHA alone doesn't pin the lychee binary it fetches. | ||
| lycheeVersion: v0.24.2 | ||
| args: >- | ||
| --offline | ||
| --include-fragments | ||
| --no-progress | ||
| --exclude-path node_modules | ||
| --exclude-path dist | ||
| './**/*.md' | ||
| fail: true | ||
|
|
||
| - name: Explain the failure | ||
| if: failure() | ||
| run: | | ||
| echo "::error::A link points at a file or heading that no longer exists — see the step above for each file, line and target." |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| # Link-check exclusions, read automatically by lychee in both link-check | ||
| # workflows. One regex per line (Rust regex syntax, unanchored — use ^ when you | ||
| # mean "starts with"). | ||
| # | ||
| # Comments must be on their OWN line. A trailing comment is read as part of the | ||
| # regex, so the pattern silently stops matching. | ||
| # | ||
| # BEFORE ADDING A LINE HERE, prefer the structural fix: | ||
| # lychee never extracts URLs inside `inline code spans` or ``` fenced blocks ```. | ||
| # If the URL is illustrative — a config value, an API host, a placeholder like | ||
| # https://example.com/<id> — wrap it in backticks in the markdown and it stops | ||
| # being checked, with no entry here. | ||
| # | ||
| # Add a regex here only for URLs that are real links we cannot check from CI. | ||
| # Always say why. | ||
|
|
||
| # Dev-server URLs in example-app READMEs ("open http://localhost:3000"). | ||
| # Nothing is listening during a CI run, and nothing should be. | ||
| ^https?://localhost(:[0-9]+)? | ||
| ^https?://127\.0\.0\.1(:[0-9]+)? |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.