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
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,32 @@ When running in default mode, if a config change is detected
(matching `LYCHEE_CONFIG_CHANGE_PATTERN`), the script falls back
to `--full` behavior.

**GitHub URL remaps:**

When running on a PR branch, the script automatically remaps GitHub
`/blob/<base-branch>/` and `/tree/<base-branch>/` URLs so that links
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
(lychee uses first-match-wins):

1. **Line-number anchors** (`#L123`): 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. **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
branch (the original behavior).

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

Set `LYCHEE_SKIP_GITHUB_REMAPS=true` to disable all GitHub-specific
remaps as an escape hatch if they cause unexpected behavior.

**Environment variables:**

<!-- editorconfig-checker-disable -->
Expand All @@ -199,6 +225,7 @@ to `--full` behavior.
| ------------------------------ | -------------------------------------------------------------------- | -------------------------------------------------------------------- |
| `LYCHEE_CONFIG` | `.github/config/lychee.toml` | Path to the lychee config file |
| `LYCHEE_CONFIG_CHANGE_PATTERN` | `^(\.github/config/lychee\.toml\|\.mise/tasks/lint/.*\|mise\.toml)$` | Regular expression for files whose change triggers a full link check |
| `LYCHEE_SKIP_GITHUB_REMAPS` | unset | Set to `true` to disable all GitHub URL remaps |

<!-- editorconfig-checker-enable -->

Expand Down
43 changes: 41 additions & 2 deletions tasks/lint/links.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,26 @@ eval "lychee_args=(${usage_lychee_args:-})"

# Build --remap args to redirect base-branch GitHub URLs to the PR branch.
# This ensures links like /blob/main/README.md resolve on the PR branch.
#
# GitHub blob/tree URLs with line-number anchors (#L123) are rendered by
# JavaScript, so lychee (which fetches static HTML) can never verify the
# 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 against the raw file content (workaround for
# 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)
#
# Set LYCHEE_SKIP_GITHUB_REMAPS=true to skip the GitHub-specific remaps
# emitted by this function (escape hatch if they cause unexpected behavior;
# does not affect remaps defined in the lychee config file).
build_remap_args() {
[ "${LYCHEE_SKIP_GITHUB_REMAPS:-}" != "true" ] || return 0

local repo base_ref head_ref head_repo

# Resolve repo name
Expand Down Expand Up @@ -53,10 +72,30 @@ 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):

# 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
# 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)
echo "--remap"
echo "^${base_url}/blob/${base_ref}/(.*)$ ${head_url}/blob/${head_ref}/\$1"
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
echo "--remap"
echo "^${base_url}/tree/${base_ref}/(.*?)#L[0-9]+\$ ${head_url}/tree/${head_ref}/\$1"

# 2. Non-fragment URLs: branch-remap only
echo "--remap"
echo "^${base_url}/tree/${base_ref}/(.*)$ ${head_url}/tree/${head_ref}/\$1"
echo "^${base_url}/tree/${base_ref}/(.*)\$ ${head_url}/tree/${head_ref}/\$1"
}

run_lychee() {
Expand Down
22 changes: 22 additions & 0 deletions tests/test-links.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Link remap smoke test

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

- [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)

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

- [CHANGELOG.md heading](https://github.com/grafana/flint/blob/main/CHANGELOG.md#changelog)

## Non-fragment blob URLs — remapped to PR branch

- [LICENSE](https://github.com/grafana/flint/blob/main/LICENSE)

## Tree URLs — remapped to PR branch

- [tasks/lint directory](https://github.com/grafana/flint/tree/main/tasks/lint)