Skip to content

Commit 35c381a

Browse files
Merge pull request #35 from amd/dholanda/link_verification
External Reference Audit
2 parents bc3853c + e92c337 commit 35c381a

4 files changed

Lines changed: 138 additions & 2 deletions

File tree

.github/lychee.toml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Shared lychee configuration.
2+
#
3+
# Used by:
4+
# .github/workflows/validate.yml (--offline, blocks PRs)
5+
# .github/workflows/external-reference-check.yml (online, informational + scheduled)
6+
#
7+
# Both workflows pass `--config .github/lychee.toml` explicitly because lychee
8+
# only auto-discovers `lychee.toml` in the current working directory.
9+
10+
# Cache successful checks across the run so we don't hammer the same host.
11+
cache = true
12+
max_cache_age = "1d"
13+
14+
# Be polite when retrying transient failures.
15+
max_retries = 2
16+
retry_wait_time = 2
17+
18+
# A 429 means the host exists but rate-limited us; treat as success rather
19+
# than as a broken reference.
20+
accept = ["200..=206", "429"]
21+
22+
# URL regex patterns to skip. Use this for hosts that are flaky, auth-gated,
23+
# or routinely block CI runners. Prefer narrow patterns over broad ones so
24+
# real link rot keeps getting caught.
25+
#
26+
# Examples:
27+
# '^https://intranet\.example\.com/',
28+
# '^https://github\.com/[^/]+/[^/]+/pull/',
29+
exclude = [
30+
# openai.com blocks GitHub-hosted runner IPs with 403; the page is fine
31+
'^https://openai\.com/codex/?$',
32+
]
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
name: external-reference-check
2+
3+
# Reach out to every external URL referenced from our markdown and report
4+
# anything that's unreachable. This is intentionally split from `validate`
5+
# because external checks are flaky (transient 5xx, rate limits, runner IP
6+
# blocks, DNS hiccups). The workflow fails on PR and manual runs so broken
7+
# links are visible; whether they actually block merge is controlled by
8+
# whether this check is marked required in branch protection (recommended:
9+
# leave it not-required so a transient hiccup can't block a merge).
10+
#
11+
# Internal references and anchors are checked by `validate.yml` with
12+
# `lychee --offline`.
13+
14+
on:
15+
pull_request:
16+
paths:
17+
- "**/*.md"
18+
- ".github/lychee.toml"
19+
- ".github/workflows/external-reference-check.yml"
20+
schedule:
21+
# Mondays at 12:00 UTC. Catches link rot in unchanged content.
22+
- cron: "0 12 * * 1"
23+
workflow_dispatch:
24+
25+
permissions:
26+
contents: read
27+
issues: write
28+
29+
jobs:
30+
external-references:
31+
name: Check external references in skills
32+
runs-on: ubuntu-latest
33+
steps:
34+
- name: Check out repository
35+
uses: actions/checkout@v4
36+
37+
- name: Check external references
38+
id: lychee
39+
uses: lycheeverse/lychee-action@v2
40+
with:
41+
args: --config .github/lychee.toml --no-progress "./**/*.md"
42+
# Fail the workflow on PR and manual runs so broken external
43+
# references show up as a red check (mark this workflow as
44+
# not-required in branch protection if you don't want it to
45+
# block merges).
46+
#
47+
# On the scheduled cron run we deliberately do NOT fail here,
48+
# so the issue-filing step below can still execute.
49+
fail: ${{ github.event_name != 'schedule' }}
50+
51+
# Scheduled runs file (or update) an issue when something is rotten,
52+
# so unchanged-but-broken links don't silently sit in main.
53+
- name: File or update link-rot issue on scheduled failure
54+
if: github.event_name == 'schedule' && steps.lychee.outputs.exit_code != 0
55+
env:
56+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
57+
REPO: ${{ github.repository }}
58+
SERVER_URL: ${{ github.server_url }}
59+
RUN_ID: ${{ github.run_id }}
60+
run: |
61+
set -euo pipefail
62+
63+
# Create the label on first use; ignore if it already exists.
64+
gh label create external-reference-rot \
65+
--color ffaa00 \
66+
--description "Unreachable external references found by scheduled lychee run" \
67+
2>/dev/null || true
68+
69+
run_url="${SERVER_URL}/${REPO}/actions/runs/${RUN_ID}"
70+
71+
if [ ! -s ./lychee/out.md ]; then
72+
echo "lychee report file is missing or empty; nothing to file." >&2
73+
exit 0
74+
fi
75+
76+
existing=$(gh issue list \
77+
--label external-reference-rot \
78+
--state open \
79+
--json number \
80+
--jq '.[0].number // empty')
81+
82+
if [ -n "$existing" ]; then
83+
echo "Updating existing issue #$existing"
84+
gh issue edit "$existing" --body-file ./lychee/out.md
85+
gh issue comment "$existing" \
86+
--body "Refreshed by scheduled run: $run_url"
87+
else
88+
echo "Filing new issue"
89+
gh issue create \
90+
--title "External reference rot detected" \
91+
--label external-reference-rot \
92+
--body-file ./lychee/out.md
93+
fi

.github/workflows/validate.yml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ on:
66
pull_request:
77
paths:
88
- "scripts/**"
9-
- "**/SKILL.md"
9+
- "**/*.md"
1010
- "skills/**"
1111
- ".claude-plugin/**"
1212
- ".cursor-plugin/**"
1313
- ".github/workflows/validate.yml"
14+
- ".github/lychee.toml"
1415
workflow_dispatch:
1516

1617
jobs:
@@ -26,3 +27,13 @@ jobs:
2627

2728
- name: Validate skills and generated manifests
2829
run: ./scripts/check.sh
30+
31+
# Deterministic, offline-only reference check: relative paths and
32+
# heading anchors. External URLs are intentionally not checked here
33+
# because their failure modes are flaky; see
34+
# `.github/workflows/external-reference-check.yml` for that.
35+
- name: Check internal references and anchors
36+
uses: lycheeverse/lychee-action@v2
37+
with:
38+
args: --config .github/lychee.toml --offline --include-fragments --no-progress "./**/*.md"
39+
fail: true

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
[![Cursor](https://img.shields.io/badge/Cursor-Compatible-000000?logo=cursor&logoColor=white)](https://cursor.com)
1010
[![Claude Code](https://img.shields.io/badge/Claude_Code-Compatible-F07535?logo=claude&logoColor=white)](https://www.anthropic.com/claude-code)
1111
[![OpenAI Codex](https://img.shields.io/badge/OpenAI_Codex-Compatible-412991?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0id2hpdGUiIGQ9Ik0yMi4yODIgOS44MjFhNS45ODUgNS45ODUgMCAwIDAtLjUxNi00LjkxIDYuMDQ2IDYuMDQ2IDAgMCAwLTYuNTEtMi45QTYuMDY1IDYuMDY1IDAgMCAwIDQuOTgxIDQuMThhNS45ODUgNS45ODUgMCAwIDAtMy45OTggMi45IDYuMDQ2IDYuMDQ2IDAgMCAwIC43NDMgNy4wOTcgNS45OCA1Ljk4IDAgMCAwIC41MSA0LjkxMSA2LjA1MSA2LjA1MSAwIDAgMCA2LjUxNSAyLjlBNS45ODUgNS45ODUgMCAwIDAgMTMuMjYgMjRhNi4wNTYgNi4wNTYgMCAwIDAgNS43NzItNC4yMDUgNS45OSA1Ljk5IDAgMCAwIDMuOTk3LTIuOSA2LjA1NiA2LjA1NiAwIDAgMC0uNzQ3LTcuMDc0ek0xMy4yNiAyMi40M2E0LjQ3NiA0LjQ3NiAwIDAgMS0yLjg3Ni0xLjA0bC4xNDEtLjA4MSA0Ljc3OS0yLjc1OGEuNzk1Ljc5NSAwIDAgMCAuMzkyLS42ODF2LTYuNzM3bDIuMDIgMS4xNjhhLjA3MS4wNzEgMCAwIDEgLjAzOC4wNTJ2NS41ODNhNC41MDQgNC41MDQgMCAwIDEtNC40OTQgNC40OTR6TTMuNiAxOC4zMDRhNC40NyA0LjQ3IDAgMCAxLS41MzUtMy4wMTRsLjE0Mi4wODUgNC43ODMgMi43NTlhLjc3MS43NzEgMCAwIDAgLjc4IDBsNS44NDMtMy4zNjl2Mi4zMzJhLjA4LjA4IDAgMCAxLS4wMzMuMDYyTDkuNzQgMTkuOTVhNC41IDQuNSAwIDAgMS02LjE0LTEuNjQ2ek0yLjM0IDcuODk2YTQuNDg1IDQuNDg1IDAgMCAxIDIuMzY2LTEuOTczVjExLjZhLjc2Ni43NjYgMCAwIDAgLjM4OC42NzdsNS44MTUgMy4zNTUtMi4wMiAxLjE2OGEuMDc2LjA3NiAwIDAgMS0uMDcxIDBsLTQuODMtMi43ODZBNC41MDQgNC41MDQgMCAwIDEgMi4zNCA3Ljg3MnptMTYuNTk3IDMuODU1bC01LjgzMy0zLjM4N0wxNS4xMTkgNy4yYS4wNzYuMDc2IDAgMCAxIC4wNzEgMGw0LjgzIDIuNzkxYTQuNDk0IDQuNDk0IDAgMCAxLS42NzYgOC4xMDV2LTUuNjc4YS43OS43OSAwIDAgMC0uNDA3LS42Njd6bTIuMDEtMy4wMjNsLS4xNDEtLjA4NS00Ljc3NC0yLjc4MmEuNzc2Ljc3NiAwIDAgMC0uNzg1IDBMOS40MDkgOS4yM1Y2Ljg5N2EuMDY2LjA2NiAwIDAgMSAuMDI4LS4wNjFsNC44My0yLjc4N2E0LjUgNC41IDAgMCAxIDYuNjggNC42NnptLTEyLjY0IDQuMTM1bC0yLjAyLTEuMTY0YS4wOC4wOCAwIDAgMS0uMDM4LS4wNTdWNi4wNzVhNC41IDQuNSAwIDAgMSA3LjM3NS0zLjQ1M2wtLjE0Mi4wOC00Ljc3OCAyLjc1OGEuNzk1Ljc5NSAwIDAgMC0uMzkzLjY4MXptMS4wOTctMi4zNjVsMi42MDItMS41IDIuNjA3IDEuNXYyLjk5OWwtMi41OTcgMS41LTIuNjA3LTEuNXoiLz48L3N2Zz4=)](https://openai.com/codex/)
12-
[![Gemini CLI](https://img.shields.io/badge/Gemini_CLI-Compatible-4285F4?logo=googlegemini&logoColor=white)](https://ai.google.dev/gemini-api/docs/cli)
12+
[![Gemini CLI](https://img.shields.io/badge/Gemini_CLI-Compatible-4285F4?logo=googlegemini&logoColor=white)](https://ai.google.dev/gemini-api/docs)
1313
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
1414

1515
<img src="assets/banner.gif" alt="AMD Skills"/>

0 commit comments

Comments
 (0)