Fix and augment relative links in MD files to GitHub blob URLs (#2061)

* Add script to augment relative links in MD files to GitHub blob URLs
* fix(pre_commit): 🎨 auto format pre-commit hooks
* Add type annotations to `augment_links.py` and update config
* Add `lychee-action` for link checking in CI workflows
* Apply suggestions from code review
* links in README.md

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Jirka Borovec 2026-02-03 01:47:59 +09:00 committed by GitHub
parent 86b268d396
commit 6149bacc77
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 95 additions and 9 deletions

70
.github/scripts/augment_links.py vendored Executable file
View File

@ -0,0 +1,70 @@
#!/usr/bin/env python3
"""
Script to augment relative links in markdown files to GitHub URLs.
"""
import argparse
import os
import re
from re import Match
def get_repo_root() -> str:
"""Get the repository root path."""
script_dir = os.path.dirname(os.path.abspath(__file__))
return os.path.dirname(os.path.dirname(script_dir))
def augment_links_in_file(file_path: str, branch: str = "main") -> None:
"""
Augment relative links in a markdown file to GitHub URLs.
Args:
file_path: Path to the markdown file.
branch: Branch name, default "main".
"""
repo_root = get_repo_root()
if not file_path.endswith(".md"):
return
with open(file_path) as f:
content = f.read()
def replace_link(match: Match[str]) -> str:
full_match = match.group(0)
text = match.group(2)
url = match.group(3)
if not url.startswith("http"):
# Resolve relative to an absolute path
abs_path = os.path.normpath(os.path.join(os.path.dirname(file_path), url))
if os.path.exists(abs_path):
# Use 'tree' for directories and 'blob' for files
ref = "tree" if os.path.isdir(abs_path) else "blob"
rel_to_root = os.path.relpath(abs_path, repo_root)
new_url = f"https://github.com/roboflow/supervision/{ref}/{branch}/{rel_to_root}"
if full_match.startswith("!"):
return f"![{text}]({new_url})"
else:
return f"[{text}]({new_url})"
return full_match
new_content = re.sub(r"(!?)\[([^\]]+)\]\(([^)]+)\)", replace_link, content)
with open(file_path, "w") as f:
f.write(new_content)
def main() -> None:
parser = argparse.ArgumentParser(
description="Augment relative links to GitHub URLs."
)
parser.add_argument("--branch", default="main", help="Branch name")
parser.add_argument("files", nargs="+", help="Files to process")
args = parser.parse_args()
for file in args.files:
augment_links_in_file(file, args.branch)
if __name__ == "__main__":
main()

View File

@ -24,6 +24,21 @@ jobs:
python-version: ${{ inputs.python-version }}
activate-environment: true
- name: 🎨 Augment paths in README
run: |
uv run python .github/scripts/augment_links.py README.md
cat README.md
- name: 🔗 Link Checker
uses: lycheeverse/lychee-action@v2
with:
lycheeVersion: v0.22.0
args: |
--config .github/lychee.toml
--header "x-ratelimit-bypass: ${{ secrets.UNIVERSE_RATELIMIT_BYPASS_KEY }}"
README.md
fail: true
- name: 🏗️ Build source and wheel distributions
run: |
uv pip install -r pyproject.toml --group build

View File

@ -21,7 +21,7 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v6
- name: Link Checker
- name: 🔗 Link Checker
uses: lycheeverse/lychee-action@v2
with:
lycheeVersion: v0.22.0

View File

@ -288,4 +288,4 @@ uv run pytest --cov=supervision
## 📄 License
By contributing, you agree that your contributions will be licensed under an [MIT license](https://github.com/roboflow/supervision/blob/develop/LICENSE.md).
By contributing, you agree that your contributions will be licensed under an [MIT license](./LICENSE.md).

View File

@ -17,7 +17,7 @@
[![version](https://badge.fury.io/py/supervision.svg)](https://badge.fury.io/py/supervision)
[![downloads](https://img.shields.io/pypi/dm/supervision)](https://pypistats.org/packages/supervision)
[![snyk](https://snyk.io/advisor/python/supervision/badge.svg)](https://snyk.io/advisor/python/supervision)
[![license](https://img.shields.io/pypi/l/supervision)](https://github.com/roboflow/supervision/blob/main/LICENSE.md)
[![license](https://img.shields.io/pypi/l/supervision)](LICENSE.md)
[![python-version](https://img.shields.io/pypi/pyversions/supervision)](https://badge.fury.io/py/supervision)
[![colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/roboflow/supervision/blob/main/demo.ipynb)
[![gradio](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Spaces-blue)](https://huggingface.co/spaces/Roboflow/Annotators)
@ -221,7 +221,7 @@ for path, image, annotation in ds:
## 🎬 tutorials
Want to learn how to use Supervision? Explore our [how-to guides](https://supervision.roboflow.com/develop/how_to/detect_and_annotate/), [end-to-end examples](https://github.com/roboflow/supervision/tree/develop/examples), [cheatsheet](https://roboflow.github.io/cheatsheet-supervision/), and [cookbooks](https://supervision.roboflow.com/develop/cookbooks/)!
Want to learn how to use Supervision? Explore our [how-to guides](https://supervision.roboflow.com/develop/how_to/detect_and_annotate/), [end-to-end examples](./examples), [cheatsheet](https://roboflow.github.io/cheatsheet-supervision/), and [cookbooks](https://supervision.roboflow.com/develop/cookbooks/)!
<br/>
@ -255,7 +255,7 @@ Visit our [documentation](https://roboflow.github.io/supervision) page to learn
## 🏆 contribution
We love your input! Please see our [contributing guide](https://github.com/roboflow/supervision/blob/main/CONTRIBUTING.md) to get started. Thank you 🙏 to all our contributors!
We love your input! Please see our [contributing guide](CONTRIBUTING.md) to get started. Thank you 🙏 to all our contributors!
<p align="center">
<a href="https://github.com/roboflow/supervision/graphs/contributors">

View File

@ -44,7 +44,7 @@ You can install `supervision` in a
=== "pip (recommended)"
[![version](https://badge.fury.io/py/supervision.svg)](https://badge.fury.io/py/supervision)
[![downloads](https://img.shields.io/pypi/dm/supervision)](https://pypistats.org/packages/supervision)
[![license](https://img.shields.io/pypi/l/supervision)](https://github.com/roboflow/supervision/blob/main/LICENSE.md)
[![license](https://img.shields.io/pypi/l/supervision)](../LICENSE.md)
[![python-version](https://img.shields.io/pypi/pyversions/supervision)](https://badge.fury.io/py/supervision)
```bash
@ -54,7 +54,7 @@ You can install `supervision` in a
=== "poetry"
[![version](https://badge.fury.io/py/supervision.svg)](https://badge.fury.io/py/supervision)
[![downloads](https://img.shields.io/pypi/dm/supervision)](https://pypistats.org/packages/supervision)
[![license](https://img.shields.io/pypi/l/supervision)](https://github.com/roboflow/supervision/blob/main/LICENSE.md)
[![license](https://img.shields.io/pypi/l/supervision)](../LICENSE.md)
[![python-version](https://img.shields.io/pypi/pyversions/supervision)](https://badge.fury.io/py/supervision)
```bash
@ -64,7 +64,7 @@ You can install `supervision` in a
=== "uv"
[![version](https://badge.fury.io/py/supervision.svg)](https://badge.fury.io/py/supervision)
[![downloads](https://img.shields.io/pypi/dm/supervision)](https://pypistats.org/packages/supervision)
[![license](https://img.shields.io/pypi/l/supervision)](https://github.com/roboflow/supervision/blob/main/LICENSE.md)
[![license](https://img.shields.io/pypi/l/supervision)](../LICENSE.md)
[![python-version](https://img.shields.io/pypi/pyversions/supervision)](https://badge.fury.io/py/supervision)
```bash
@ -80,7 +80,7 @@ You can install `supervision` in a
=== "rye"
[![version](https://badge.fury.io/py/supervision.svg)](https://badge.fury.io/py/supervision)
[![downloads](https://img.shields.io/pypi/dm/supervision)](https://pypistats.org/packages/supervision)
[![license](https://img.shields.io/pypi/l/supervision)](https://github.com/roboflow/supervision/blob/main/LICENSE.md)
[![license](https://img.shields.io/pypi/l/supervision)](../LICENSE.md)
[![python-version](https://img.shields.io/pypi/pyversions/supervision)](https://badge.fury.io/py/supervision)
```bash

View File

@ -199,6 +199,7 @@ ignore_missing_imports = false
explicit_package_bases = true
strict = true
exclude = [
".github",
"docs",
"test",
"examples",