-
Notifications
You must be signed in to change notification settings - Fork 2
145 lines (132 loc) · 5.53 KB
/
Copy pathexternal-reference-check.yml
File metadata and controls
145 lines (132 loc) · 5.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
name: external-reference-check
# Reach out to every external URL referenced from our markdown and report
# anything that's unreachable. This is intentionally split from `validate`
# because external checks are flaky (transient 5xx, rate limits, runner IP
# blocks, DNS hiccups). The workflow fails on PR and manual runs so broken
# links are visible; whether they actually block merge is controlled by
# whether this check is marked required in branch protection (recommended:
# leave it not-required so a transient hiccup can't block a merge).
#
# Internal references and anchors are checked by `validate.yml` with
# `lychee --offline`.
on:
pull_request:
paths:
- "**/*.md"
- ".github/lychee.toml"
- ".github/workflows/external-reference-check.yml"
schedule:
# Mondays at 12:00 UTC. Catches link rot in unchanged content.
- cron: "0 12 * * 1"
workflow_dispatch:
# Lets OTHER repos reuse this exact check instead of copying it, so the
# logic never drifts. A consumer adds a ~10-line caller workflow:
#
# jobs:
# external-references:
# uses: amd/skills/.github/workflows/external-reference-check.yml@main
# permissions:
# contents: read
# issues: write
#
# The lychee config is NOT configurable: callers always use the canonical
# .github/lychee.toml from amd/skills (pinned to the same commit as this
# workflow), so neither the logic nor the config can drift.
workflow_call:
inputs:
markdown_glob:
description: "Glob of markdown files to check, relative to the caller repo root."
type: string
default: "./**/*.md"
permissions:
contents: read
issues: write
jobs:
external-references:
name: Check external references in skills
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
# The lychee config always comes from amd/skills so it can never drift.
# When this repo runs the check on itself, the working-tree copy is used
# (so PRs that edit the config are tested against their own changes).
# When another repo calls this workflow, the canonical config is fetched
# from amd/skills at the SAME commit as this workflow file
# (github.job_workflow_sha), keeping logic and config in lockstep.
# Sparse-checkout grabs only the config file so its markdown can't
# pollute the scan.
- name: Fetch canonical lychee config
if: ${{ github.repository != 'amd/skills' }}
uses: actions/checkout@v4
with:
repository: amd/skills
ref: ${{ github.job_workflow_sha || 'main' }}
path: .external-reference-check-config
sparse-checkout: |
.github/lychee.toml
sparse-checkout-cone-mode: false
persist-credentials: false
- name: Resolve lychee config path
id: cfg
shell: bash
env:
IS_CANONICAL: ${{ github.repository == 'amd/skills' }}
run: |
set -euo pipefail
if [ "${IS_CANONICAL}" = "true" ]; then
echo "path=.github/lychee.toml" >> "$GITHUB_OUTPUT"
else
echo "path=.external-reference-check-config/.github/lychee.toml" >> "$GITHUB_OUTPUT"
fi
- name: Check external references
id: lychee
uses: lycheeverse/lychee-action@v2
with:
args: --config ${{ steps.cfg.outputs.path }} --no-progress "${{ inputs.markdown_glob || './**/*.md' }}"
# Fail the workflow on PR and manual runs so broken external
# references show up as a red check (mark this workflow as
# not-required in branch protection if you don't want it to
# block merges).
#
# On the scheduled cron run we deliberately do NOT fail here,
# so the issue-filing step below can still execute.
fail: ${{ github.event_name != 'schedule' }}
# Scheduled runs file (or update) an issue when something is rotten,
# so unchanged-but-broken links don't silently sit in main.
- name: File or update link-rot issue on scheduled failure
if: github.event_name == 'schedule' && steps.lychee.outputs.exit_code != 0
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
SERVER_URL: ${{ github.server_url }}
RUN_ID: ${{ github.run_id }}
run: |
set -euo pipefail
# Create the label on first use; ignore if it already exists.
gh label create external-reference-rot \
--color ffaa00 \
--description "Unreachable external references found by scheduled lychee run" \
2>/dev/null || true
run_url="${SERVER_URL}/${REPO}/actions/runs/${RUN_ID}"
if [ ! -s ./lychee/out.md ]; then
echo "lychee report file is missing or empty; nothing to file." >&2
exit 0
fi
existing=$(gh issue list \
--label external-reference-rot \
--state open \
--json number \
--jq '.[0].number // empty')
if [ -n "$existing" ]; then
echo "Updating existing issue #$existing"
gh issue edit "$existing" --body-file ./lychee/out.md
gh issue comment "$existing" \
--body "Refreshed by scheduled run: $run_url"
else
echo "Filing new issue"
gh issue create \
--title "External reference rot detected" \
--label external-reference-rot \
--body-file ./lychee/out.md
fi