-
Notifications
You must be signed in to change notification settings - Fork 4
183 lines (168 loc) · 7.98 KB
/
Copy pathapps-docs-linkcheck.yml
File metadata and controls
183 lines (168 loc) · 7.98 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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
name: docs-linkcheck
# Builds the BoringStack docs site and runs lychee against the built output.
# Internal broken links fail the job; external 4xx/5xx are warnings only
# (rate-limited APIs and intermittent third-party 503s are not the docs'
# problem to solve at PR-review time).
on:
push:
branches: [main]
paths:
- "apps/docs/src/**"
- "apps/docs/scripts/**"
- "apps/docs/astro.config.mjs"
- "apps/docs/package.json"
- "apps/docs/bun.lock"
# Build/install/scan config: wrangler (Cloudflare asset + build settings),
# bunfig (install behavior), osv-scanner (vuln allowlist). A config-only
# push must still rebuild + linkcheck so a broken config can't land
# unverified.
- "apps/docs/wrangler.jsonc"
- "apps/docs/bunfig.toml"
- "apps/docs/osv-scanner.toml"
- ".github/workflows/apps-docs-linkcheck.yml"
# Catalog inputs: the generated docs data is derived from sibling-app
# scripts/README.md, package.json scripts, and lint-meta export-catalog.
# Changes here can stale the committed catalogs, so they must trigger the
# freshness check even when no apps/docs file changed.
- "apps/api/scripts/**"
- "apps/api/package.json"
- "apps/ui/scripts/**"
- "apps/ui/package.json"
# PR trigger has NO `paths:` filter — the `linkcheck` check is in
# branch protection's required-checks list, so the workflow must run
# on every PR or branch protection will wait forever for a status
# that never comes ("Expected — Waiting for status to be reported").
# The expensive build + lychee steps are gated on the internal
# paths-filter below, so PRs that don't touch docs report green
# without doing any real work.
pull_request: {}
workflow_dispatch:
permissions:
contents: read
concurrency:
group: apps-docs-linkcheck-docs-${{ github.ref }}
cancel-in-progress: true
jobs:
linkcheck:
defaults:
run:
working-directory: apps/docs
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Detect docs changes
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: filter
with:
filters: |
docs:
- 'apps/docs/src/**'
- 'apps/docs/scripts/**'
- 'apps/docs/astro.config.mjs'
- 'apps/docs/package.json'
- 'apps/docs/bun.lock'
- 'apps/docs/wrangler.jsonc'
- 'apps/docs/bunfig.toml'
- 'apps/docs/osv-scanner.toml'
- '.github/workflows/apps-docs-linkcheck.yml'
catalog:
- 'apps/api/scripts/**'
- 'apps/api/package.json'
- 'apps/ui/scripts/**'
- 'apps/ui/package.json'
- name: No-op notice for unaffected PRs
if: steps.filter.outputs.docs != 'true' && steps.filter.outputs.catalog != 'true'
run: echo "No docs or catalog-input files changed in this PR — skipped."
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
if: steps.filter.outputs.docs == 'true' || steps.filter.outputs.catalog == 'true'
with:
bun-version: 1.3.14
- name: Cache bun install
if: steps.filter.outputs.docs == 'true' || steps.filter.outputs.catalog == 'true'
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.bun/install/cache
key: bun-${{ runner.os }}-${{ hashFiles('apps/docs/bun.lock', 'apps/ui/bun.lock', 'apps/api/bun.lock') }}
restore-keys: |
bun-${{ runner.os }}-
- name: Install docs deps
if: steps.filter.outputs.docs == 'true' || steps.filter.outputs.catalog == 'true'
run: bun install --frozen-lockfile
- name: Install apps/ui deps for lint-meta export
if: steps.filter.outputs.docs == 'true' || steps.filter.outputs.catalog == 'true'
working-directory: apps/ui
run: bun install --frozen-lockfile
- name: Install apps/api deps for lint-meta export
if: steps.filter.outputs.docs == 'true' || steps.filter.outputs.catalog == 'true'
working-directory: apps/api
run: bun install --frozen-lockfile
- name: Verify generated docs data
if: steps.filter.outputs.docs == 'true' || steps.filter.outputs.catalog == 'true'
env:
BORINGSTACK_UI_DIR: ${{ github.workspace }}/apps/ui
BORINGSTACK_API_DIR: ${{ github.workspace }}/apps/api
run: bun run check:docs-data
# The full site build + lychee link check stay gated on docs changes
# only — a catalog-input change needs the freshness check above, not a
# multi-minute rebuild of the whole site.
- name: Build docs site
if: steps.filter.outputs.docs == 'true'
env:
BORINGSTACK_UI_DIR: ${{ github.workspace }}/apps/ui
BORINGSTACK_API_DIR: ${{ github.workspace }}/apps/api
run: bun run build:ci
- name: Internal link check (fail on broken)
if: steps.filter.outputs.docs == 'true'
uses: lycheeverse/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411 # v2.8.0
with:
args: >-
--no-progress
--offline
--root-dir ${{ github.workspace }}/apps/docs/dist
'${{ github.workspace }}/apps/docs/dist/**/*.html'
fail: true
- name: External link check (warning only)
if: steps.filter.outputs.docs == 'true'
uses: lycheeverse/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411 # v2.8.0
continue-on-error: true
with:
# This step is warning-only, so it must never block the runner. Two
# changes keep it fast on a shared CI IP that github.com throttles:
#
# --max-retries 0 A throttled link (HTTP 429 + a giant
# Retry-After) becomes an immediate warning
# instead of a per-retry backoff loop
# ("unexpectedly big rate limit backoff … capping
# to 1m") that serialised into a multi-minute hang.
#
# exclude self-repo Starlight renders an "Edit this page" link
# (github.com/.../edit/main/<page>) on EVERY doc
# page — ~one github.com request per page. Plus
# the blob/tree source links. None are API-eligible,
# so the token can't help them and they dominated
# the throttling. They are auto-generated /
# self-referential and verifying our own repo from
# CI adds nothing a public repo + the internal link
# check don't already cover. Excluding the whole
# boringstack-xyz org covers edit + blob + tree +
# org/repo-root in one pattern.
#
# Third-party github.com links (ripgrep, gitleaks, …) are still
# checked — the GITHUB_TOKEN below routes those parseable owner/repo
# URLs through api.github.com (5000 req/h), where they don't throttle.
args: >-
--no-progress
--max-retries 0
--root-dir ${{ github.workspace }}/apps/docs/dist
--exclude-path './llms-full.txt'
--exclude-path './llms-small.txt'
--exclude '^https?://(localhost|127\.0\.0\.1|.*\.localhost)'
--exclude 'github\.com/boringstack-xyz'
'${{ github.workspace }}/apps/docs/dist/**/*.html'
fail: false
env:
# Routes the remaining (third-party) github.com links through the
# GitHub API (5000 req/h) instead of anonymous HTTP (60 req/h), so
# they don't throttle. Needs only the default contents:read.
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}