Skip to content

Commit 9cd6199

Browse files
louis-preclaude
andcommitted
ci: Add redirect target validator and fix 18 broken redirects
Add validate-redirects CI check that verifies all .gitbook.yaml redirect targets resolve to existing pages. Fix 18 broken targets caused by pages being moved or removed over time. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 74bd840 commit 9cd6199

4 files changed

Lines changed: 124 additions & 18 deletions

File tree

.gitbook.yaml

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ root: ./docs/guides/
44
readme: README.md
55
summary: SUMMARY.md
66
redirects:
7-
latest/quickstart/get-api-key: latest/quickstart.md
7+
latest/quickstart/get-api-key: quickstart.md
88
quickstart/get-api-key: quickstart.md
99
quickstart/installation: quickstart.md
1010
quickstart/authentication: quickstart.md
@@ -16,18 +16,18 @@ redirects:
1616
core-concepts/workspaces/personal-access-tokens: core-concepts/authentication/personal-access-tokens.md
1717
core-concepts/workspaces/client-session-tokens: core-concepts/authentication/client-session-tokens/README.md
1818
core-concepts/workspaces/client-session-tokens/implementing-client-sessions-for-device-management-in-the-backend: core-concepts/authentication/client-session-tokens/implementing-client-sessions-for-device-management-in-the-backend.md
19-
products/access-systems/user-management: capability-guides/access-systems/user-management/README.md
20-
capability-guides/access-systems/managing-credentials: capability-guides/access-systems/managing-credentials/README.md
19+
products/access-systems/user-management: capability-guides/access-systems/user-management.md
20+
capability-guides/access-systems/managing-credentials: capability-guides/access-systems/managing-credentials.md
2121
capability-guides/access-systems/assigning-credentials-to-users: capability-guides/access-systems/managing-credentials/assigning-credentials-to-users.md
2222
products/access-systems/suspending-and-unsuspending-users: capability-guides/access-systems/user-management/suspending-and-unsuspending-users.md
2323
products/seam-bridge-in-development: capability-guides/seam-bridge.md
2424
core-concepts/connect-webviews: core-concepts/connect-webviews/README.md
2525
device-guides/4suites-locks: device-and-system-integration-guides/4suites-locks/README.md
2626
device-guides/assa-abloy-visionline-access-control-system-in-development: device-and-system-integration-guides/assa-abloy-visionline-access-control-system/README.md
27-
device-guides/assa-abloy-visionline-access-control-system-in-development/credential-types: device-and-system-integration-guides/assa-abloy-visionline-access-control-system/credential-types.md
28-
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/visionline-credential-metadata: device-and-system-integration-guides/assa-abloy-visionline-access-control-system/visionline-credential-metadata.md
27+
device-guides/assa-abloy-visionline-access-control-system-in-development/credential-types: device-and-system-integration-guides/assa-abloy-visionline-access-control-system
28+
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/visionline-credential-metadata: device-and-system-integration-guides/assa-abloy-visionline-access-control-system
2929
device-guides/assa-abloy-visionline-access-control-system-in-development/common-use-cases: device-and-system-integration-guides/assa-abloy-visionline-access-control-system/common-use-cases.md
30-
capability-guides/access-systems/understanding-acs-differences: capability-guides/access-systems/understanding-access-control-system-differences.md
30+
capability-guides/access-systems/understanding-acs-differences: capability-guides/access-systems
3131
capability-guides/thermostats/creating-and-managing-climate-schedules: capability-guides/thermostats/creating-and-managing-thermostat-schedules.md
3232
device-guides/get-started-with-schlage-locks: device-and-system-integration-guides/schlage-locks/get-started-with-schlage-locks.md
3333
api-clients/access-codes/convert-an-unmanaged-access-code: api/access_codes/unmanaged/convert_to_managed.md
@@ -95,16 +95,16 @@ redirects:
9595
api-clients/connected-accounts/list-connected-accounts: api/connected_accounts/list.md
9696
api-clients/connected-accounts/update-a-connected-account: api/connected_accounts/update.md
9797
api-clients/connected-accounts: api/connected_accounts/README.md
98-
api-clients/devices/delete-device: api/devices/delete.md
98+
api-clients/devices/delete-device: api/devices
9999
api-clients/devices/get-device-1: api/devices/unmanaged/get.md
100100
api-clients/devices/get-device: api/devices/get.md
101101
api-clients/devices/list-device-providers: api/devices/list_device_providers.md
102102
api-clients/devices/list-devices: api/devices/list.md
103103
api-clients/devices/list-unmanaged-devices: api/devices/unmanaged/list.md
104104
api-clients/devices/update-device: api/devices/update.md
105105
api-clients/devices/update-unmanaged-device: api/devices/unmanaged/update.md
106-
api-clients/events/get-an-event: api-clients/events/get.md
107-
api-clients/events/list-events: api-clients/events/list.md
106+
api-clients/events/get-an-event: api/events/get.md
107+
api-clients/events/list-events: api/events/list.md
108108
api-clients/locks/get-lock: api/locks/get.md
109109
api-clients/locks/list-locks: api/locks/list.md
110110
api-clients/locks/lock-a-lock: api/locks/lock_door.md
@@ -114,7 +114,7 @@ redirects:
114114
api-clients/noise-sensors/list-noise-thresholds: api/noise_sensors/noise_thresholds/list.md
115115
api-clients/noise-sensors/update-noise-threshold: api/noise_sensors/noise_thresholds/update.md
116116
api-clients/noise-sensors: api/noise_sensors/README.md
117-
api-clients/thermostats/get-thermostat: api/thermostats/get.md
117+
api-clients/thermostats/get-thermostat: api/thermostats
118118
api-clients/thermostats/list-thermostats: api/thermostats/list.md
119119
api-clients/thermostats/set-fan-mode: api/thermostats/set_fan_mode.md
120120
api-clients/thermostats/set-to-cool-mode: api/thermostats/cool.md
@@ -136,7 +136,7 @@ redirects:
136136
api-clients/workspaces/get-workspace: api/workspaces/get.md
137137
api-clients/workspaces/reset-workspace: api/workspaces/reset_sandbox.md
138138
api-clients/phones/create-a-sandbox-phone: api/phones/simulate/create_sandbox_phone.md
139-
device-and-system-integration-guides/assa-abloy-credential-services-credential-manager-in-development: device-and-system-integration-guides/assa-abloy-credential-services-credential-manager/README.md
139+
device-and-system-integration-guides/assa-abloy-credential-services-credential-manager-in-development: device-and-system-integration-guides/assa-abloy-vingcard-credential-services.md
140140
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development: device-and-system-integration-guides/assa-abloy-visionline-access-control-system/README.md
141141
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/visionline-acs-setup-instructions: device-and-system-integration-guides/assa-abloy-visionline-access-control-system/visionline-acs-setup-instructions/README.md
142142
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/visionline-acs-setup-instructions/developing-and-launching-your-visionline-plastic-card-encoding-app: device-and-system-integration-guides/assa-abloy-visionline-access-control-system/visionline-acs-setup-instructions/developing-and-launching-your-visionline-plastic-card-encoding-app/README.md
@@ -183,13 +183,13 @@ redirects:
183183
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/visionline-acs-setup-instructions/developing-and-launching-your-visionline-mobile-key-app/launching-your-visionline-mobile-key-app/step-7-purchase-and-import-the-callback-and-mobile-service-options-from-assa-abloy: device-and-system-integration-guides/assa-abloy-visionline-access-control-system/visionline-acs-setup-instructions/developing-and-launching-your-visionline-mobile-key-app/launching-your-visionline-mobile-key-app/step-7-purchase-and-import-the-callback-and-mobile-service-options-from-assa-abloy.md
184184
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/visionline-acs-setup-instructions/developing-and-launching-your-visionline-mobile-key-app/launching-your-visionline-mobile-key-app/step-8-create-a-production-workspace: device-and-system-integration-guides/assa-abloy-visionline-access-control-system/visionline-acs-setup-instructions/developing-and-launching-your-visionline-mobile-key-app/launching-your-visionline-mobile-key-app/step-8-create-a-production-workspace.md
185185
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/visionline-acs-setup-instructions/developing-and-launching-your-visionline-mobile-key-app/launching-your-visionline-mobile-key-app/step-9-connect-your-visionline-production-account-to-seam: device-and-system-integration-guides/assa-abloy-visionline-access-control-system/visionline-acs-setup-instructions/developing-and-launching-your-visionline-mobile-key-app/launching-your-visionline-mobile-key-app/step-9-connect-your-visionline-production-account-to-seam.md
186-
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/credential-types: device-and-system-integration-guides/assa-abloy-visionline-access-control-system/credential-types/README.md
187-
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/credential-types/issuing-various-types-of-guest-mobile-credentials: device-and-system-integration-guides/assa-abloy-visionline-access-control-system/credential-types/issuing-various-types-of-guest-mobile-credentials.md
188-
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/credential-types/issuing-various-types-of-guest-joiner-mobile-credentials: device-and-system-integration-guides/assa-abloy-visionline-access-control-system/credential-types/issuing-various-types-of-guest-joiner-mobile-credentials.md
189-
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/credential-types/retrieving-guest-and-common-entrances: device-and-system-integration-guides/assa-abloy-visionline-access-control-system/credential-types/retrieving-guest-and-common-entrances.md
190-
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/credential-types/checking-if-a-user-identity-has-a-phone-that-is-set-up-for-a-credential-manager: device-and-system-integration-guides/assa-abloy-visionline-access-control-system/credential-types/checking-if-a-user-identity-has-a-phone-that-is-set-up-for-a-credential-manager.md
191-
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/credential-types/updating-guest-mobile-credentials: device-and-system-integration-guides/assa-abloy-visionline-access-control-system/credential-types/updating-guest-mobile-credentials.md
192-
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/credential-types/revoking-mobile-credentials: device-and-system-integration-guides/assa-abloy-visionline-access-control-system/credential-types/revoking-mobile-credentials.md
186+
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/credential-types: device-and-system-integration-guides/assa-abloy-visionline-access-control-system
187+
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/credential-types/issuing-various-types-of-guest-mobile-credentials: device-and-system-integration-guides/assa-abloy-visionline-access-control-system
188+
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/credential-types/issuing-various-types-of-guest-joiner-mobile-credentials: device-and-system-integration-guides/assa-abloy-visionline-access-control-system
189+
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/credential-types/retrieving-guest-and-common-entrances: device-and-system-integration-guides/assa-abloy-visionline-access-control-system
190+
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/credential-types/checking-if-a-user-identity-has-a-phone-that-is-set-up-for-a-credential-manager: device-and-system-integration-guides/assa-abloy-visionline-access-control-system
191+
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/credential-types/updating-guest-mobile-credentials: device-and-system-integration-guides/assa-abloy-visionline-access-control-system
192+
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/credential-types/revoking-mobile-credentials: device-and-system-integration-guides/assa-abloy-visionline-access-control-system
193193
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/mobile-credential-related-properties: device-and-system-integration-guides/assa-abloy-visionline-access-control-system/mobile-credential-related-properties.md
194194
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/common-use-cases: device-and-system-integration-guides/assa-abloy-visionline-access-control-system/common-use-cases.md
195195
device-and-system-integration-guides/assa-abloy-visionline-access-control-system-in-development/special-requirements-for-android-mobile-access-sdk-development: device-and-system-integration-guides/assa-abloy-visionline-access-control-system/special-requirements-for-android-mobile-access-sdk-development.md

.github/workflows/generate.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,5 @@ jobs:
8282
run: npm run validate-paths
8383
- name: Validate cross-site links
8484
run: npm run validate-links
85+
- name: Validate redirect targets
86+
run: npm run validate-redirects

codegen/validate-redirects.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { existsSync, readFileSync } from 'node:fs'
2+
import { join } from 'node:path'
3+
4+
import { siteSections } from './lib/config.js'
5+
6+
// Parse the redirects section from .gitbook.yaml.
7+
// Each redirect line is: " source-path: target-path"
8+
const gitbookYaml = readFileSync('.gitbook.yaml', 'utf-8')
9+
const lines = gitbookYaml.split('\n')
10+
11+
interface Redirect {
12+
line: number
13+
source: string
14+
target: string
15+
}
16+
17+
const redirects: Redirect[] = []
18+
let inRedirects = false
19+
20+
for (let i = 0; i < lines.length; i++) {
21+
const line = lines[i] ?? ''
22+
23+
if (line === 'redirects:') {
24+
inRedirects = true
25+
continue
26+
}
27+
28+
// A non-indented, non-empty line ends the redirects section
29+
if (inRedirects && line !== '' && !line.startsWith(' ')) {
30+
break
31+
}
32+
33+
if (!inRedirects) continue
34+
35+
const match = line.match(/^\s+(.+?):\s+(.+)$/)
36+
if (match?.[1] != null && match[2] != null) {
37+
redirects.push({ line: i + 1, source: match[1], target: match[2] })
38+
}
39+
}
40+
41+
// Check if a path resolves to a page as a file, URL slug, or directory index.
42+
function pageExists(fullPath: string): boolean {
43+
// Exact file match (e.g., quickstart.md)
44+
if (existsSync(fullPath)) return true
45+
// Directory with README.md (e.g., core-concepts/ → core-concepts/README.md)
46+
if (existsSync(join(fullPath, 'README.md'))) return true
47+
// URL-style target without .md extension (e.g., api/devices → api/devices.md)
48+
if (!fullPath.endsWith('.md') && existsSync(fullPath + '.md')) return true
49+
return false
50+
}
51+
52+
// Resolve a redirect target to check it points to a real page.
53+
// Targets may reference other site sections via URL prefix.
54+
function resolveTarget(target: string): boolean {
55+
// Try matching against site sections by URL prefix (most specific first).
56+
// The siteSections array is already ordered with more-specific prefixes first.
57+
for (const section of siteSections) {
58+
if (section.urlPrefix === '') continue
59+
60+
const prefix = section.urlPrefix.replace(/^\//, '') + '/'
61+
if (target.startsWith(prefix)) {
62+
const relativePath = target.slice(prefix.length)
63+
return pageExists(join(section.root, relativePath))
64+
}
65+
}
66+
67+
// Fall back to the default section (Guides, urlPrefix '')
68+
const guidesSection = siteSections.find((s) => s.urlPrefix === '')
69+
if (guidesSection == null) return false
70+
71+
return pageExists(join(guidesSection.root, target))
72+
}
73+
74+
interface BrokenRedirect {
75+
line: number
76+
source: string
77+
target: string
78+
}
79+
80+
const broken: BrokenRedirect[] = []
81+
82+
for (const redirect of redirects) {
83+
if (!resolveTarget(redirect.target)) {
84+
broken.push(redirect)
85+
}
86+
}
87+
88+
if (broken.length > 0) {
89+
// eslint-disable-next-line no-console
90+
console.error(
91+
`Found ${broken.length} redirect(s) with missing target(s) in .gitbook.yaml:\n`,
92+
)
93+
for (const { line, source, target } of broken) {
94+
// eslint-disable-next-line no-console
95+
console.error(` line ${line}: ${source}`)
96+
// eslint-disable-next-line no-console
97+
console.error(` target: ${target}\n`)
98+
}
99+
process.exit(1)
100+
} else {
101+
// eslint-disable-next-line no-console
102+
console.log('All .gitbook.yaml redirect targets are valid.')
103+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"postgenerate": "npm run format",
1919
"validate-paths": "tsx codegen/validate-paths.ts",
2020
"validate-links": "tsx codegen/validate-links.ts",
21+
"validate-redirects": "tsx codegen/validate-redirects.ts",
2122
"typecheck": "tsc",
2223
"lint": "eslint .",
2324
"postlint": "prettier --check --ignore-path .prettierignore .",

0 commit comments

Comments
 (0)