feat(links): add GitHub URL remaps for line-number and fragment anchors#28
feat(links): add GitHub URL remaps for line-number and fragment anchors#28zeitlinger merged 1 commit intomainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the lychee link-lint task to better handle GitHub /blob/ and /tree/ URLs that include fragments, especially line-number anchors that lychee can’t verify reliably on GitHub’s JS-rendered pages.
Changes:
- Add ordered GitHub
--remaprules for/blob/and/tree/URLs to handle line-number anchors and other fragments. - Strip
#L123fragments (and introduce a raw-content remap for other fragments) to reduce false negatives during fragment checking. - Add
LYCHEE_SKIP_GITHUB_REMAPS=trueto opt out of GitHub-specific CLI remaps emitted by the task.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # 1. Line-number anchors (#L123): strip fragment, check file on base branch | ||
| echo "--remap" | ||
| echo "^${base_url}/blob/${base_ref}/(.*?)#L[0-9]+\$ ${base_url}/blob/${base_ref}/\$1" | ||
|
|
There was a problem hiding this comment.
The line-anchor remap keeps the URL on the base branch (${base_ref}), which contradicts the function’s stated goal of making base-branch links resolve on the PR/head branch. This will make PRs fail when they add a new file (or rename/move one) and link to it via .../blob/${base_ref}/...#L123, because the stripped URL will 404 on the base branch even though it exists on the head branch. Consider stripping the fragment but still rewriting to the head repo/ref (similar to the non-fragment rule).
| # 1. Line-number anchors: strip fragment (unlikely for tree but consistent) | ||
| echo "--remap" | ||
| echo "^${base_url}/tree/${base_ref}/(.*?)#L[0-9]+\$ ${base_url}/tree/${base_ref}/\$1" |
There was a problem hiding this comment.
Same issue as the /blob/ rule: the /tree/ line-anchor remap strips the fragment but keeps the URL on the base branch. If a PR introduces a new directory/link target (or moves it) and uses a .../tree/${base_ref}/...#L123 link, the remapped URL will be checked against the base branch and can 404 even though it exists on the head branch. Consider stripping the fragment while still remapping to ${head_url}/tree/${head_ref}/....
| # 1. Line-number anchors: strip fragment (unlikely for tree but consistent) | |
| echo "--remap" | |
| echo "^${base_url}/tree/${base_ref}/(.*?)#L[0-9]+\$ ${base_url}/tree/${base_ref}/\$1" | |
| # 1. Line-number anchors: strip fragment, remap to head branch | |
| echo "--remap" | |
| echo "^${base_url}/tree/${base_ref}/(.*?)#L[0-9]+\$ ${head_url}/tree/${head_ref}/\$1" |
| # fragment. We strip the fragment and check the file exists instead. | ||
| # | ||
| # For other fragment URLs (#section), we remap to raw.githubusercontent.com | ||
| # which lets lychee verify the fragment in the raw HTML (workaround for |
There was a problem hiding this comment.
The comment says raw.githubusercontent.com “lets lychee verify the fragment in the raw HTML”, but raw.githubusercontent.com serves the raw file contents (not rendered HTML). Consider rewording to avoid implying GitHub is returning HTML here (e.g., “verify fragments against the raw content”, or describing the specific behavior lychee uses for fragment checks).
| # which lets lychee verify the fragment in the raw HTML (workaround for | |
| # which lets lychee verify the fragment against the raw file content (workaround for |
| # 2. Other fragments → remap to raw.githubusercontent.com | ||
| # 3. No fragment → remap to head branch (existing behavior) | ||
| # | ||
| # Set LYCHEE_SKIP_GITHUB_REMAPS=true to disable all GitHub-specific remaps. |
There was a problem hiding this comment.
LYCHEE_SKIP_GITHUB_REMAPS=true currently only disables the GitHub remaps emitted by build_remap_args(), but this repo’s .github/config/lychee.toml also defines a GitHub remap. Consider clarifying the comment to avoid implying this env var disables all GitHub remapping behavior in lychee, or remove/move the config remap if the intent is to centralize remaps here.
| # Set LYCHEE_SKIP_GITHUB_REMAPS=true to disable all GitHub-specific remaps. | |
| # Set LYCHEE_SKIP_GITHUB_REMAPS=true to disable the GitHub-specific remaps | |
| # emitted by this function (does not affect remaps defined in lychee config). |
0b01df2 to
7da8508
Compare
GitHub blob URLs with line-number anchors (#L123) are JavaScript-rendered and can't be verified by lychee. When the existing branch remap rewrites blob/main/ to blob/<pr-branch>/, these URLs fail fragment checking. Add ordered remaps in build_remap_args() (first-match-wins): 1. Line-number anchors (#L123): strip the fragment, check file exists 2. Other fragment URLs (#section): remap to raw.githubusercontent.com (workaround for lychee#1729) 3. Non-fragment URLs: branch-remap only (existing behavior) This removes the need for consuming repos to maintain their own remap rules in lychee.toml for these common patterns. Also add LYCHEE_SKIP_GITHUB_REMAPS=true opt-out env var. Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
7da8508 to
e8b2ad7
Compare
🤖 I have created a release *beep* *boop* --- ## [0.6.0](v0.5.0...v0.6.0) (2026-02-18) ### Features * **links:** add GitHub URL remaps for line-number and fragment anchors ([#28](#28)) ([5b59065](5b59065)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## flint v0.20.0 — Rust rewrite flint has been rewritten from scratch in Rust. The bash + Docker stack is replaced by a fast, cross-platform native binary with no container dependency. ### Highlights - **Native binary** — no Docker, no bash wrappers; runs anywhere mise can install a Rust crate - **Parallel linting** — all checks run concurrently - **Diff-aware** — only checks files changed since the merge base by default (`--full` for everything) - **`flint update`** — migrates obsolete `mise.toml` tool keys automatically (e.g. `ubi:` → `github:`, `npm:markdownlint-cli` → `npm:markdownlint-cli2`) - **`flint hook install`** — installs a pre-commit hook without any mise task knowledge - **New linters**: `gofmt`, `google-java-format`, `ktlint`, `dotnet-format`, `markdownlint-cli2`, `xmllint` (via `cargo:xmloxide`), `license-header` (pure-Rust, no binary) - **Windows support** — ktlint self-executing JAR handled via explicit `java -jar` invocation ### Migration See [AGENTS-V2.md](https://github.com/grafana/flint/blob/main/AGENTS-V2.md) and [README.md](https://github.com/grafana/flint/blob/main/README.md) for full setup instructions. Quick reference for existing consumers: ```toml [tools] # While installing from source (pre-crates.io release): "cargo:https://github.com/grafana/flint" = "branch:main" [env] FLINT_CONFIG_DIR = ".github/config" [tasks.lint] run = "flint run" [tasks."lint:fix"] run = "flint run --fix" ``` Remove: `lint:super-linter`, `lint:links`, `lint:renovate-deps`, `setup:native-lint-tools`, `setup:pre-commit-hook` tasks. Run `flint update` to auto-migrate any obsolete tool keys. > [!NOTE] > The changelog below includes entries from the legacy v1 bash era — see [README-V1.md](https://github.com/grafana/flint/blob/main/README-V1.md) for v1 docs. --- ## [0.20.0](flint-v0.19.0...flint-v0.20.0) (2026-04-13) ### Features * add flint v2 Rust binary ([#139](#139)) ([19f2b25](19f2b25)) * add native linting mode and version mapping infrastructure ([#93](#93)) ([24b06da](24b06da)) * add Renovate shareable preset for consuming repos ([#17](#17)) ([8a06590](8a06590)) * consolidate link checking and add autofix flags ([#7](#7)) ([086a5e9](086a5e9)) * flint update command, explicit JAR flag, v0.20.0 ([#146](#146)) ([b43bf52](b43bf52)) * handle line-number anchors and issue comments globally ([#56](#56)) ([cf751df](cf751df)) * **links:** add GitHub URL remaps for line-number and fragment anchors ([#28](#28)) ([5b59065](5b59065)) * **links:** auto-remap base-branch GitHub URLs to PR branch ([#18](#18)) ([dd6cc61](dd6cc61)) * **renovate:** support SHA-pinned URLs in Renovate preset ([#21](#21)) ([4fd1f28](4fd1f28)) * **super-linter:** default to slim image ([#24](#24)) ([c8eeab8](c8eeab8)) * support NATIVE env var for container-free linting ([#107](#107)) ([0a8193d](0a8193d)) ### Bug Fixes * activate mise environment in native lint mode ([#123](#123)) ([d0fec45](d0fec45)) * add 'mise run fix' hint to lint failure output ([#90](#90)) ([5b4ad5d](5b4ad5d)) * decouple version mapping generation from pinned super-linter version ([#112](#112)) ([5370e77](5370e77)) * **deps:** update rust crate crossterm to 0.29 ([#156](#156)) ([c59ae3e](c59ae3e)) * **deps:** update rust crate similar to v3 ([#160](#160)) ([684be4e](684be4e)) * **deps:** update rust crate toml to v1 ([#161](#161)) ([3aae614](3aae614)) * **deps:** update rust crate toml_edit to 0.25 ([#158](#158)) ([42d9efd](42d9efd)) * exclude GitHub compare links from lychee checks ([#10](#10)) ([e714608](e714608)) * fail native lint when enabled tools are missing ([#111](#111)) ([163bb6b](163bb6b)) * improve link checker reliability against GitHub rate limiting ([#95](#95)) ([7a5282d](7a5282d)) * include staged files in native lint file list ([#135](#135)) ([34412d6](34412d6)) * **links:** add regex anchors to remap patterns ([#19](#19)) ([2e17348](2e17348)) * native lint in worktrees, trust toml, use ec binary, drop isort ([#134](#134)) ([8594bba](8594bba)) * **release-please:** fix footer not appearing on release PRs ([#40](#40)) ([d7a55e4](d7a55e4)) * remap same-repo GitHub URLs to local file paths ([#100](#100)) ([b4feadd](b4feadd)) * **renovate-deps:** forward GITHUB_TOKEN as GITHUB_COM_TOKEN ([#132](#132)) ([4d6510b](4d6510b)) * replace broken release-please PR comment with docs ([#12](#12)) ([817b37d](817b37d)) * run shellcheck on .bats files in native mode ([#137](#137)) ([a4fd3f8](a4fd3f8)) * strip Scroll to Text Fragment anchors in link checks ([#86](#86)) ([b630cdf](b630cdf)) * tighten markdownlint config for native mode ([#106](#106)) ([6ef25b2](6ef25b2)) * use remap instead of exclude for issue comment anchors ([#58](#58)) ([656f355](656f355)) --- > [!IMPORTANT] > Close and reopen this PR to trigger CI checks. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Summary
build_remap_args()for GitHub/blob/and/tree/URLs with line-number anchors (#L123) and other fragments (#section)raw.githubusercontent.comas a workaround for lychee#1729remaprules inlychee.tomlfor these patternsLYCHEE_SKIP_GITHUB_REMAPS=trueopt-out env varContext
GitHub blob URLs with line-number anchors (
#L6) are JavaScript-rendered and can't be verified by lychee (which fetches static HTML). When the existing branch remap rewritesblob/main/→blob/<pr-branch>/, these URLs fail fragment checking.Since lychee uses first-match-wins (no chaining), config file remaps never run after flint's CLI
--remapmatches. The fix belongs here inbuild_remap_args().Remap order (first-match-wins)
For
/blob/URLs:#L123): strip fragment, check file on base branch#section): remap toraw.githubusercontent.com+ head branchFor
/tree/URLs: rules 1 and 3 only (no raw remap needed).Test plan
mise run lint:links --fullin a consuming repo (e.g. grafana-opentelemetry-java) with a#L6linkremaprules inlychee.toml