Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 20 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,20 +212,23 @@ to the base branch resolve against the PR branch instead. This
ensures that links like `/blob/main/README.md` don't break when
the file was added or moved in the PR.

For `/blob/` URLs, three ordered remap rules are applied
For `/blob/` URLs, four ordered remap rules are applied
(lychee uses first-match-wins):

1. **Line-number anchors** (`#L123`): GitHub renders these with
JavaScript, so lychee can never verify the fragment. The anchor
1. **Line-number anchors** (`#L123`, `#L10-L20`): GitHub renders
these with JavaScript, so lychee can never verify the fragment.
The anchor is stripped and the file is checked on the PR branch.
2. **[Scroll to Text Fragment][stf] anchors** (`#:~:text=...`):
Browser-only feature, not present in static HTML. The anchor
is stripped and the file is checked on the PR branch.
2. **Other fragment URLs** (`#section`): Remapped to
3. **Other fragment URLs** (`#section`): Remapped to
`raw.githubusercontent.com` where lychee can verify the fragment
in the raw file content (workaround for
[lychee#1729](https://github.com/lycheeverse/lychee/issues/1729)).
3. **Non-fragment URLs**: Remapped from the base branch to the PR
4. **Non-fragment URLs**: Remapped from the base branch to the PR
branch (the original behavior).

For `/tree/` URLs, rules 1 and 3 apply (no raw remap needed).
For `/tree/` URLs, rules 1 and 4 apply (no raw remap needed).

**Global GitHub URL handling:**

Expand All @@ -237,6 +240,9 @@ two patterns that affect ALL GitHub URLs (any repository):
JS-rendered line-number fragment is skipped. This means
consuming repos don't need to exclude these in their
`lychee.toml`.
- **Scroll to Text Fragment anchors** (`#:~:text=...`): Stripped
from any GitHub `/blob/` URL. These are a browser-only feature
not present in static HTML.
- **Issue comment anchors** (`#issuecomment-*`): The fragment
is stripped so the issue/PR page is still checked, but the
JS-rendered comment anchor is skipped.
Expand All @@ -254,11 +260,14 @@ via `--remap` arguments:
[lychee#1729](https://github.com/lycheeverse/lychee/issues/1729)**
— flint remaps fragment URLs to `raw.githubusercontent.com`
for the current PR's head branch, and strips line-number
anchors globally.
and Scroll to Text Fragment anchors globally.
- **`#issuecomment-*` excludes** — flint strips the fragment
via remap so the issue/PR page is still checked.
- **`#L\d+` line-number excludes** — flint strips the fragment
via remap so the file is still checked.
- **`#L\d+` / `#L\d+-L\d+` line-number excludes** — flint strips
the fragment via remap so the file is still checked.
- **`#:~:text=...` [Scroll to Text Fragment][stf] excludes** —
flint strips the fragment via remap so the file is still
checked.

Note: flint uses `--remap` (not `--exclude`) for these because
lychee's CLI `--exclude` flags override config file excludes
Expand Down Expand Up @@ -444,3 +453,5 @@ When conventional commits land on `main`, Release Please opens
> **Note:** CI checks don't trigger automatically on release-please
> PRs because they are created with `GITHUB_TOKEN`. To run CI,
> either click **Update branch** or **close and reopen** the PR.

[stf]: https://developer.mozilla.org/en-US/docs/Web/URI/Fragment/Text_fragments
34 changes: 24 additions & 10 deletions tasks/lint/links.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ eval "lychee_args=(${usage_lychee_args:-})"
# https://github.com/lycheeverse/lychee/issues/1729).
#
# Lychee uses first-match-wins for remaps, so order matters:
# 1. Line-number anchors → strip fragment, remap to head branch
# 2. Other fragments → remap to raw.githubusercontent.com
# 3. No fragment → remap to head branch (existing behavior)
# 1. Line-number anchors → strip fragment, remap to head branch
# 2. Scroll to Text Fragments → strip fragment, remap to head branch
# 3. Other fragments → remap to raw.githubusercontent.com
# 4. No fragment → remap to head branch (existing behavior)
#
# Set LYCHEE_SKIP_GITHUB_REMAPS=true to skip the GitHub-specific remaps
# emitted by this function (escape hatch if they cause unexpected behavior;
Expand Down Expand Up @@ -72,26 +73,31 @@ build_remap_args() {
local base_url="https://github.com/${repo}"
local head_url="https://github.com/${head_repo}"

# /blob/ URLs — three rules, order matters (first-match-wins):
# /blob/ URLs — four rules, order matters (first-match-wins):

# 1. Line-number anchors (#L123): strip fragment, remap to head branch
# 1. Line-number anchors (#L123, #L10-L20): strip fragment, remap to head branch
echo "--remap"
echo "^${base_url}/blob/${base_ref}/(.*?)#L[0-9]+\$ ${head_url}/blob/${head_ref}/\$1"
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"

# 3. Other fragment URLs (#section): remap to raw.githubusercontent.com
# so lychee can verify the fragment in raw content
echo "--remap"
echo "^${base_url}/blob/${base_ref}/(.*#.*)\$ https://raw.githubusercontent.com/${head_repo}/${head_ref}/\$1"

# 3. Non-fragment URLs: branch-remap only (existing behavior)
# 4. Non-fragment URLs: branch-remap only (existing behavior)
echo "--remap"
echo "^${base_url}/blob/${base_ref}/(.*)\$ ${head_url}/blob/${head_ref}/\$1"

# /tree/ URLs — two rules:

# 1. Line-number anchors: strip fragment, remap to head branch
# 1. Line-number anchors (#L123, #L10-L20): strip fragment, remap to head branch
echo "--remap"
echo "^${base_url}/tree/${base_ref}/(.*?)#L[0-9]+\$ ${head_url}/tree/${head_ref}/\$1"
echo "^${base_url}/tree/${base_ref}/(.*?)#L[0-9]+.*\$ ${head_url}/tree/${head_ref}/\$1"

# 2. Non-fragment URLs: branch-remap only
echo "--remap"
Expand All @@ -105,6 +111,9 @@ build_remap_args() {
# - Line-number anchors (#L123, #L10-L20): rendered by JavaScript,
# lychee cannot verify them. We strip the fragment so the file
# itself is still checked.
# - Scroll to Text Fragment anchors (#:~:text=...): browser-only,
# lychee cannot verify them. We strip the fragment so the file
# itself is still checked.
# - Issue comment anchors (#issuecomment-*): rendered by JavaScript,
# lychee cannot verify them. The fragment is stripped so the
# issue/PR page itself is still checked.
Expand All @@ -122,6 +131,11 @@ build_global_github_args() {
# shellcheck disable=SC2016 # single quotes are intentional: these are regex capture groups, not shell vars
echo '^https://github.com/([^/]+/[^/]+)/blob/([^/]+)/(.*?)#L[0-9]+.*$ https://github.com/$1/blob/$2/$3'

# Strip Scroll to Text Fragment anchors from /blob/ URLs (browser-only, not in static HTML)
echo "--remap"
# shellcheck disable=SC2016 # single quotes are intentional: these are regex capture groups, not shell vars
echo '^https://github.com/([^/]+/[^/]+)/blob/([^/]+)/(.*?)#:~:text=.*$ https://github.com/$1/blob/$2/$3'

# Strip issue comment anchors (JS-rendered, not in static HTML).
# The issue page is still checked, just not the fragment.
# We use --remap instead of --exclude because CLI --exclude
Expand Down
14 changes: 13 additions & 1 deletion tests/test-links.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,22 @@ These links exercise the GitHub URL remap rules in `tasks/lint/links.sh`.
On PR branches, lychee rewrites `blob/main/` URLs to the PR branch —
these links verify that each remap rule works correctly during CI.

## Line-number anchors (`#L123`) — fragment stripped, file checked on PR branch
## Line-number anchors (`#L123`, `#L10-L20`) — fragment stripped, file checked on PR branch

- [README.md#L1](https://github.com/grafana/flint/blob/main/README.md#L1)
- [links.sh#L6](https://github.com/grafana/flint/blob/main/tasks/lint/links.sh#L6)
- [links.sh#L6-L10](https://github.com/grafana/flint/blob/main/tasks/lint/links.sh#L6-L10)

## Scroll to Text Fragment anchors (`#:~:text=...`) — fragment stripped, file checked on PR branch

<!-- editorconfig-checker-disable -->

- [links.sh text fragment](https://github.com/grafana/flint/blob/main/tasks/lint/links.sh#:~:text=build_remap_args)
<!-- editorconfig-checker-enable -->

## External Scroll to Text Fragment anchors — fragment stripped globally

- [okhttp text fragment](https://github.com/square/okhttp/blob/master/README.md#:~:text=OkHttp)

## Section fragments (`#section`) — remapped to raw.githubusercontent.com

Expand Down