From eded88593234572adcbbf026e66ba1901428c6d5 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Thu, 4 Jun 2026 19:55:34 +0000 Subject: [PATCH 1/3] Add lychee link checker config and CI workflow Adds a lychee configuration and a Links GitHub Actions workflow so that stale or redirecting links are caught automatically going forward. The checker runs on push, pull request, and weekly to catch external link rot. max_redirects is 0 so links that have moved are surfaced and can be updated to their canonical destination. Part of STF-557. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/links.yml | 32 ++++++++++++++++++++ .gitignore | 3 ++ lychee.toml | 60 +++++++++++++++++++++++++++++++++++++ mise.toml | 5 ++++ 4 files changed, 100 insertions(+) create mode 100644 .github/workflows/links.yml create mode 100644 lychee.toml diff --git a/.github/workflows/links.yml b/.github/workflows/links.yml new file mode 100644 index 0000000..dcec89e --- /dev/null +++ b/.github/workflows/links.yml @@ -0,0 +1,32 @@ +name: Links + +on: + push: + pull_request: + schedule: + - cron: "0 13 * * 1" # weekly, to catch external link rot without a commit + workflow_dispatch: + +permissions: + contents: read + +jobs: + linkChecker: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: Setup mise + uses: jdx/mise-action@6d1e696aa24c1aa1bcc1adea0212707c71ab78a8 # v3.6.1 + with: + install: false + + # Install only lychee (not the repo's full toolchain) and run the check. + - name: Check links + env: + MISE_AUTO_INSTALL: "false" + run: | + mise install lychee + mise run check-links diff --git a/.gitignore b/.gitignore index ce3e166..cb0c4b7 100644 --- a/.gitignore +++ b/.gitignore @@ -114,3 +114,6 @@ sample/src/main/res/xml/network_security_config.xml device-sdk-*.aar device-sdk-*.jar *.asc + +# Lychee link checker cache +.lycheecache diff --git a/lychee.toml b/lychee.toml new file mode 100644 index 0000000..0ee32fb --- /dev/null +++ b/lychee.toml @@ -0,0 +1,60 @@ +# Lychee link checker configuration +# https://lychee.cli.rs/#/usage/config +# +# Run locally with: +# lychee './**/*.md' './**/*.kt' './**/*.java' + +# Include URL fragments in checks +include_fragments = true + +# Don't allow any redirects, so links that have moved are surfaced and can be +# updated to their canonical destination. +max_redirects = 0 + +# Accept these HTTP status codes +# 100-103: Informational responses +# 200-299: Success responses +# 403: Forbidden (some sites use this for rate limiting) +# 429: Too Many Requests +# 500-599: Server errors (temporary issues shouldn't fail CI) +# 999: LinkedIn's custom status code +accept = ["100..=103", "200..=299", "403", "429", "500..=599", "999"] + +# Exclude URL patterns from checking (treated as regular expressions) +exclude = [ + # GitHub blob URLs with line-number fragments (not parseable as page anchors) + '^https://github\.com/[^/]+/[^/]+/blob/[0-9a-fA-F]+/.+#L\d+$', + # Live / auth-gated MaxMind endpoints: appear as code string literals or + # require login, so they can't be verified by an anonymous request. + '^https://geoip\.maxmind\.com', + '^https://minfraud\.maxmind\.com', + '^https://sandbox\.maxmind\.com', + '^https://www\.maxmind\.com/en/accounts/', + '^https://www\.maxmind\.com/en/account/login', + # Device-tracking API endpoints: live collection endpoints hit by the SDK, + # not browsable pages. + '^https?://d-ipv[46]\.mmapiws\.com', + # Local / placeholder URLs (e.g. example hosts in docs and tests) + '^file://', + '^https?://example\.(com|org|net)', + '^http://localhost', + '127\.0\.0\.1', +] + +# Exclude file paths from getting checked (regular expressions, matched against +# the path relative to the working directory). Patterns are segment-anchored +# with (^|/) so short names like "build" don't match unintended paths. +exclude_path = [ + '(^|/)build/', + '(^|/)\.gradle/', + '(^|/)node_modules/', + # Changelog: historical entries are preserved as-is, not rewritten + '(^|/)CHANGELOG\.md$', +] + +# Cache results for 1 day to speed up repeated checks +cache = true +max_cache_age = "1d" + +# Skip missing input files instead of erroring +skip_missing = true diff --git a/mise.toml b/mise.toml index b72db2a..a794c58 100644 --- a/mise.toml +++ b/mise.toml @@ -9,6 +9,7 @@ yamllint = "latest" # prettier is used by precious for Markdown formatting prettier = "latest" android-sdk = "latest" +lychee = "latest" [tasks.setup] description = "Install Android SDK platform packages and configure local.properties" @@ -34,3 +35,7 @@ fi echo "Setup complete. Run './gradlew :device-sdk:test' to verify." """ + +[tasks.check-links] +description = "Check links with lychee" +run = "lychee --no-progress './**/*.md' './**/*.kt' './**/*.java'" From 3b8d302a8b563ab8711dbb1beb8deec6da9a8e6a Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Thu, 4 Jun 2026 19:56:19 +0000 Subject: [PATCH 2/3] Update stale and redirecting links Validated all links with lychee and updated the one that redirected, plus excluded placeholder example/test hosts that aren't real endpoints: - README.dev.md: http://central.sonatype.org/pages/working-with-pgp-signatures.html (301) -> https://central.sonatype.org/publish/requirements/gpg/ - lychee.toml: exclude placeholder hosts custom.maxmind.com and test.maxmind.com used only in docstring examples and unit tests. Historical CHANGELOG.md entries are intentionally left unchanged. Part of STF-557. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.dev.md | 3 +-- lychee.toml | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.dev.md b/README.dev.md index 19f2017..a74d11f 100644 --- a/README.dev.md +++ b/README.dev.md @@ -83,8 +83,7 @@ and enter `trust` and choose ultimate. Make sure the key shows up in `gpg --list-secret-keys`. Make sure you publish it to a keyserver. See -[here](http://central.sonatype.org/pages/working-with-pgp-signatures.html) for -more info. +[here](https://central.sonatype.org/publish/requirements/gpg/) for more info. ### gpg "inappropriate ioctl" errors diff --git a/lychee.toml b/lychee.toml index 0ee32fb..25b099c 100644 --- a/lychee.toml +++ b/lychee.toml @@ -34,6 +34,10 @@ exclude = [ # Device-tracking API endpoints: live collection endpoints hit by the SDK, # not browsable pages. '^https?://d-ipv[46]\.mmapiws\.com', + # Placeholder example/test server hosts used in docstrings and unit tests + # (not real, resolvable endpoints). + '^https://custom\.maxmind\.com', + '^https://test\.maxmind\.com', # Local / placeholder URLs (e.g. example hosts in docs and tests) '^file://', '^https?://example\.(com|org|net)', From 6b4b87709524133d85ec11c86ee7163e7f5f50e9 Mon Sep 17 00:00:00 2001 From: Gregory Oschwald Date: Thu, 4 Jun 2026 22:04:13 +0000 Subject: [PATCH 3/3] Add mise lockfile to pin lychee mise.toml did not previously use a lockfile; enable it and commit mise.lock so the lychee version (floated to "latest" in mise.toml) is pinned for reproducible link checking. Part of STF-557. Co-Authored-By: Claude Opus 4.8 (1M context) --- mise.lock | 29 +++++++++++++++++++++++++++++ mise.toml | 3 +++ 2 files changed, 32 insertions(+) create mode 100644 mise.lock diff --git a/mise.lock b/mise.lock new file mode 100644 index 0000000..156e912 --- /dev/null +++ b/mise.lock @@ -0,0 +1,29 @@ +# @generated - this file is auto-generated by `mise lock` https://mise.jdx.dev/dev-tools/mise-lock.html + +[[tools.lychee]] +version = "0.23.0" +backend = "aqua:lycheeverse/lychee" + +[tools.lychee."platforms.linux-arm64"] +checksum = "sha256:97eb93b02a7d78a752fc33e5b0983439ccaadbf3db952b68a0a4401acd92e6e0" +url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.23.0/lychee-aarch64-unknown-linux-gnu.tar.gz" + +[tools.lychee."platforms.linux-arm64-musl"] +checksum = "sha256:97eb93b02a7d78a752fc33e5b0983439ccaadbf3db952b68a0a4401acd92e6e0" +url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.23.0/lychee-aarch64-unknown-linux-gnu.tar.gz" + +[tools.lychee."platforms.linux-x64"] +checksum = "sha256:5538440d2c69a45a0a09983271e5dee0c2fe7137d8035d25b2632e10a66a090a" +url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.23.0/lychee-x86_64-unknown-linux-musl.tar.gz" + +[tools.lychee."platforms.linux-x64-musl"] +checksum = "sha256:5538440d2c69a45a0a09983271e5dee0c2fe7137d8035d25b2632e10a66a090a" +url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.23.0/lychee-x86_64-unknown-linux-musl.tar.gz" + +[tools.lychee."platforms.macos-arm64"] +checksum = "sha256:4c8034900e11083b68ac6f6582c377ff1f704e268991999e09d717973e493e7f" +url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.23.0/lychee-arm64-macos.dmg" + +[tools.lychee."platforms.windows-x64"] +checksum = "sha256:0fda7ff0a60c0250939fc25361c2d4e6e7853c31c996733fdd5a1dd760bcb824" +url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.23.0/lychee-x86_64-windows.exe" diff --git a/mise.toml b/mise.toml index a794c58..7c54837 100644 --- a/mise.toml +++ b/mise.toml @@ -1,3 +1,6 @@ +[settings] +lockfile = true + [tools] # Java 21 is required - Dokka doesn't support Java 25+ yet java = "temurin-21"