Skip to content

fix: strip Scroll to Text Fragment anchors in link checks#86

Merged
zeitlinger merged 5 commits intomainfrom
strip-scroll-to-text-fragments
Mar 2, 2026
Merged

fix: strip Scroll to Text Fragment anchors in link checks#86
zeitlinger merged 5 commits intomainfrom
strip-scroll-to-text-fragments

Conversation

@zeitlinger
Copy link
Copy Markdown
Member

Summary

  • Adds remap rules to strip Scroll to Text Fragment anchors (#:~:text=...) from GitHub blob URLs before lychee tries to verify them
  • Applied to both repo-specific (build_remap_args) and global (build_global_github_args) remaps

Context

Scroll to Text Fragment anchors are a browser-only feature — they highlight text on the page but don't correspond to any HTML element ID. Lychee can't verify them in static HTML.

Without this fix, the "other fragments → raw.githubusercontent.com" remap rule catches these URLs first and redirects them to raw content, where fragment validation fails because #:~:text=extendedAgent isn't a valid anchor.

The consuming repo's lychee.toml already has a remap to strip this specific fragment, but it never fires because CLI --remap args from flint match first (first-match-wins).

Observed in open-telemetry/opentelemetry-java-instrumentation CI — the link works locally because build_remap_args skips when on the base branch.

Test plan

Copilot AI review requested due to automatic review settings March 2, 2026 17:17
@zeitlinger zeitlinger changed the title Strip Scroll to Text Fragment anchors in link checks fix: strip Scroll to Text Fragment anchors in link checks Mar 2, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the lychee link-check remap rules so GitHub “Scroll to Text Fragment” anchors (#:~:text=...) are stripped from /blob/ URLs before verification, avoiding false failures caused by remap precedence and fragment validation limitations in static HTML.

Changes:

  • Add a repo-specific /blob/ remap rule that strips #:~:text=... fragments and remaps to the PR head branch.
  • Add a global GitHub /blob/ remap rule that strips #:~:text=... fragments for all repositories.
  • Update inline documentation/comments to reflect the new remap ordering.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tasks/lint/links.sh Outdated
Comment on lines +78 to +85
# 1. Line-number anchors (#L123): strip fragment, remap to head branch
echo "--remap"
echo "^${base_url}/blob/${base_ref}/(.*?)#L[0-9]+\$ ${head_url}/blob/${head_ref}/\$1"

# 2. Other fragment URLs (#section): remap to raw.githubusercontent.com
# 2. Scroll to Text Fragment anchors (#:~:text=...): browser-only,
# strip fragment, remap to head branch
echo "--remap"
echo "^${base_url}/blob/${base_ref}/(.*?)#:~:text=.*\$ ${head_url}/blob/${head_ref}/\$1"
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The repo-specific line-anchor remap only matches #L<digits> at end of URL (#L[0-9]+$). GitHub also generates range fragments like #L10-L20; those won’t match rule (1) and will fall through to the “other fragments → raw.githubusercontent.com” rule, where lychee still can’t verify the fragment. Update this rule (and the analogous /tree/ rule) to also match range anchors (e.g., #L[0-9]+(-L[0-9]+)?$ or similar) so they get stripped instead of remapped to raw.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — changed the repo-specific blob and tree rules from #L[0-9]+$ to #L[0-9]+.*$ to match range fragments like #L10-L20, consistent with the global remap. Added a same-repo range test case (links.sh#L6-L10) to cover this.

@zeitlinger zeitlinger enabled auto-merge (squash) March 2, 2026 17:56
Scroll to Text Fragment anchors (#:~:text=...) are a browser-only
feature that lychee cannot verify in static HTML. Without this fix,
the "other fragments" remap rule catches these URLs first and redirects
them to raw.githubusercontent.com, where the fragment validation fails.

This was observed in open-telemetry/opentelemetry-java-instrumentation
where a link to build.gradle#:~:text=extendedAgent fails in CI but
works locally (because build_remap_args skips when on the base branch).

Fixes the issue by adding explicit rules (in both repo-specific and
global remaps) to strip the fragment before the catch-all fragment
rule matches.
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
The repo-specific remap used #L[0-9]+$ which didn't match range
fragments like #L10-L20. These would fall through to the "other
fragments → raw.githubusercontent.com" rule where they also can't
be verified. Changed to #L[0-9]+.*$ to match ranges, consistent
with the global remap rule.
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
@zeitlinger zeitlinger force-pushed the strip-scroll-to-text-fragments branch from c989255 to 1156abc Compare March 2, 2026 18:09
@zeitlinger zeitlinger merged commit b630cdf into main Mar 2, 2026
8 checks passed
@zeitlinger zeitlinger deleted the strip-scroll-to-text-fragments branch March 2, 2026 18:11
zeitlinger pushed a commit that referenced this pull request Mar 2, 2026
🤖 I have created a release *beep* *boop*
---


## [0.7.1](v0.7.0...v0.7.1)
(2026-03-02)


### Bug Fixes

* strip Scroll to Text Fragment anchors in link checks
([#86](#86))
([b630cdf](b630cdf))

---
> [!IMPORTANT]
> Close and reopen this PR to trigger CI checks.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
zeitlinger pushed a commit that referenced this pull request Apr 13, 2026
## 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>
This was referenced Apr 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants