From 32b404976c44df37464455a2a69e2e6dc89008f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CKevin=E2=80=9D?= Date: Thu, 28 May 2026 09:18:24 -0700 Subject: [PATCH 1/4] ci: add lychee dead-link detection workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds two workflows plus a shared lychee config: - link-check-pr.yml runs on PRs that touch .md/.mdx files, scoped to the changed files only and limited to external http(s) URLs. Internal links are already validated by Docusaurus in build.yml. Non-blocking via continue-on-error during a tuning period. - link-check-weekly.yml runs Mondays 12:00 UTC and on workflow_dispatch. Builds the site and crawls rendered HTML under build/docs, community, and faq. On failure, opens or updates a single rolling GitHub issue labeled link-rot. - lychee.toml: shared cache, retries, and excludes for both jobs. Signed-off-by: “Kevin” --- .github/workflows/link-check-pr.yml | 60 +++++++++++++++++++++ .github/workflows/link-check-weekly.yml | 72 +++++++++++++++++++++++++ lychee.toml | 29 ++++++++++ 3 files changed, 161 insertions(+) create mode 100644 .github/workflows/link-check-pr.yml create mode 100644 .github/workflows/link-check-weekly.yml create mode 100644 lychee.toml diff --git a/.github/workflows/link-check-pr.yml b/.github/workflows/link-check-pr.yml new file mode 100644 index 000000000..da681e228 --- /dev/null +++ b/.github/workflows/link-check-pr.yml @@ -0,0 +1,60 @@ +# Pull-request link check. +# +# Scope: external HTTP(S) links in markdown files changed by the PR. +# Internal/relative links are validated by Docusaurus during build.yml +# (onBrokenLinks: 'throw', onBrokenMarkdownLinks: 'throw'); re-checking +# them here would be redundant and prone to false positives because +# lychee cannot replay Docusaurus's slug routing. +name: Link check (PR) + +on: + pull_request: + paths: + - '**/*.md' + - '**/*.mdx' + - 'lychee.toml' + - '.github/workflows/link-check-pr.yml' + +permissions: + contents: read + +jobs: + lychee: + runs-on: ubuntu-latest + # Non-blocking on day one. Once the check is stable for a couple of + # weeks, remove this line and mark "Link check (PR) / lychee" as a + # required status check in branch protection. + continue-on-error: true + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Detect changed markdown files + id: changed + uses: tj-actions/changed-files@v45 + with: + files: | + **/*.md + **/*.mdx + + - name: Restore lychee cache + if: steps.changed.outputs.any_changed == 'true' + uses: actions/cache@v4 + with: + path: .lycheecache + key: lychee-pr-${{ github.run_id }} + restore-keys: lychee-pr- + + - name: Run lychee on changed files + if: steps.changed.outputs.any_changed == 'true' + uses: lycheeverse/lychee-action@v2 + with: + # --scheme http --scheme https restricts lychee to absolute + # HTTP(S) URLs only (i.e., external links). See header comment. + args: --no-progress --scheme http --scheme https --config lychee.toml ${{ steps.changed.outputs.all_changed_files }} + fail: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/link-check-weekly.yml b/.github/workflows/link-check-weekly.yml new file mode 100644 index 000000000..23220b264 --- /dev/null +++ b/.github/workflows/link-check-weekly.yml @@ -0,0 +1,72 @@ +# Weekly site-wide link check. +# +# Builds the Docusaurus site and runs lychee against the rendered HTML +# under build/docs, build/community, and build/faq (blog and changelog +# are intentionally excluded). On failure, opens or updates a single +# rolling GitHub issue labeled "link-rot" so findings are tracked over +# time without spamming new issues each week. +name: Link check (weekly) + +on: + schedule: + - cron: '0 12 * * 1' # Mondays 12:00 UTC + workflow_dispatch: + +permissions: + contents: read + issues: write + +jobs: + lychee: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Use Node.js lts/jod (v22) + uses: actions/setup-node@v4 + with: + node-version: lts/jod + cache: 'npm' + + - name: Install and build + run: | + npm ci + npm run build + + - name: Restore lychee cache + uses: actions/cache@v4 + with: + path: .lycheecache + key: lychee-weekly-${{ github.run_id }} + restore-keys: lychee-weekly- + + - name: Run lychee on built docs, community, and faq + id: lychee + uses: lycheeverse/lychee-action@v2 + with: + args: >- + --no-progress + --config lychee.toml + --base ./build + './build/docs/**/*.html' + './build/community/**/*.html' + './build/faq/**/*.html' + # Do not fail the job so the next step can open/update the + # tracking issue with the report. + fail: false + output: ./lychee-report.md + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Create or update tracking issue + if: env.lychee_exit_code != '0' + uses: peter-evans/create-issue-from-file@v5 + with: + title: 'Link check report' + content-filepath: ./lychee-report.md + labels: | + link-rot + automated diff --git a/lychee.toml b/lychee.toml new file mode 100644 index 000000000..66798b112 --- /dev/null +++ b/lychee.toml @@ -0,0 +1,29 @@ +# Lychee link checker config. +# Shared by .github/workflows/link-check-pr.yml and link-check-weekly.yml. +# Docs: https://lychee.cli.rs/usage/config/ + +cache = true +max_cache_age = "1d" +max_retries = 3 +retry_wait_time = 2 +timeout = 20 +max_concurrency = 16 + +# Treat rate-limit/partial responses as success so transient throttling +# from external sites is not reported as link rot. +accept = [200, 203, 206, 429] + +# Skip URLs that routinely 403 bots regardless of how politely we crawl. +exclude = [ + "^http(s)?://localhost", + "^http(s)?://127\\.0\\.0\\.1", + "^http(s)?://0\\.0\\.0\\.0", + "linkedin\\.com", +] + +# Never traverse these paths when expanding inputs. +exclude_path = [ + "node_modules", + ".docusaurus", + ".preview-pages", +] From fa572c8c4a082cd759b44f3fee8c22acc64c3d30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CKevin=E2=80=9D?= Date: Thu, 28 May 2026 09:21:05 -0700 Subject: [PATCH 2/4] test(ci): add canary broken link to verify lychee PR check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Temporary. This commit will be reverted in the next commit on this branch. Verifies that link-check-pr.yml correctly fails when a PR introduces an unreachable external URL. Signed-off-by: “Kevin” --- docs/03-concepts/11-data-converter.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/03-concepts/11-data-converter.md b/docs/03-concepts/11-data-converter.md index 93b1bba48..f02cad650 100644 --- a/docs/03-concepts/11-data-converter.md +++ b/docs/03-concepts/11-data-converter.md @@ -449,5 +449,6 @@ Cadence enforces a per-payload size limit of approximately 2 MB by default (clus ## References +- [lychee-canary-do-not-merge](https://httpbin.org/status/404) - [Go SDK godoc: `go.uber.org/cadence/encoded`](https://pkg.go.dev/go.uber.org/cadence/encoded) - [Java SDK Javadoc: `com.uber.cadence.converter`](https://javadoc.io/doc/com.uber.cadence/cadence-client/latest/com/uber/cadence/converter/package-summary.html) From d93aef73b45fc1d928747a78b0f81b00274dca25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CKevin=E2=80=9D?= Date: Thu, 28 May 2026 12:55:13 -0700 Subject: [PATCH 3/4] test(ci): remove canary broken link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: “Kevin” --- docs/03-concepts/11-data-converter.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/03-concepts/11-data-converter.md b/docs/03-concepts/11-data-converter.md index f02cad650..93b1bba48 100644 --- a/docs/03-concepts/11-data-converter.md +++ b/docs/03-concepts/11-data-converter.md @@ -449,6 +449,5 @@ Cadence enforces a per-payload size limit of approximately 2 MB by default (clus ## References -- [lychee-canary-do-not-merge](https://httpbin.org/status/404) - [Go SDK godoc: `go.uber.org/cadence/encoded`](https://pkg.go.dev/go.uber.org/cadence/encoded) - [Java SDK Javadoc: `com.uber.cadence.converter`](https://javadoc.io/doc/com.uber.cadence/cadence-client/latest/com/uber/cadence/converter/package-summary.html) From 766aba7efc0c91527e82a83f2cc6617740bada53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CKevin=E2=80=9D?= Date: Thu, 28 May 2026 13:03:53 -0700 Subject: [PATCH 4/4] fix(ci): use step output for lychee exit code in weekly workflow lycheeverse/lychee-action@v2 exposes exit_code as a step output, not an env var. The previous env.lychee_exit_code reference was always empty, and '' != '0' evaluates to true, which would have caused the tracking issue to be opened/updated on every weekly run regardless of whether any links were actually broken. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: “Kevin” --- .github/workflows/link-check-weekly.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/link-check-weekly.yml b/.github/workflows/link-check-weekly.yml index 23220b264..50f59ff08 100644 --- a/.github/workflows/link-check-weekly.yml +++ b/.github/workflows/link-check-weekly.yml @@ -62,7 +62,11 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Create or update tracking issue - if: env.lychee_exit_code != '0' + # lychee-action@v2 exposes exit_code as a STEP OUTPUT, not an env var. + # Using env.lychee_exit_code here would always be empty, and + # '' != '0' evaluates to true in GitHub Actions expressions, so the + # issue would be created on every run. + if: steps.lychee.outputs.exit_code != '0' uses: peter-evans/create-issue-from-file@v5 with: title: 'Link check report'