Skip to content
2 changes: 2 additions & 0 deletions docs/source/_toctree.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,6 @@
title: kernels download
- local: cli-skills
title: kernels skills
- local: cli-copy-readme
title: kernels copy-readme
title: CLI Reference
16 changes: 16 additions & 0 deletions docs/source/cli-copy-readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
### kernels copy-readme

Use `kernels copy-readme <repo_id>` to copy the most recent version's README
to the `main` branch of a kernel repository, so visitors landing on the
default branch see the latest documentation.

By default, the README is printed to stdout. Use `--push-to-hub` to upload
it directly to the `main` branch on the Hub.

```bash
# Print
kernels copy-readme kernels-community/activation

# Push to the Hub
kernels copy-readme kernels-community/activation --push-to-hub
```
24 changes: 24 additions & 0 deletions kernels/src/kernels/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from huggingface_hub import ModelCard, ModelCardData

from kernels.cli.copy_readme import copy_readme_to_main
from kernels.cli.doc import generate_readme_for_kernel
from kernels.cli.init import parse_kernel_name, run_init
from kernels.cli.kernel_card_utils import (
Expand Down Expand Up @@ -268,6 +269,22 @@ def main():
)
fill_card_parser.set_defaults(func=fill_kernel_card)

copy_readme_parser = subparsers.add_parser(
"copy-readme",
help="Copy the most recent version's README to the main branch.",
)
copy_readme_parser.add_argument(
"repo_id",
type=str,
help="The kernel repo ID (e.g., kernels-community/activation)",
)
copy_readme_parser.add_argument(
"--push-to-hub",
action="store_true",
help="Push the generated README to the main branch on the Hub.",
)
copy_readme_parser.set_defaults(func=run_copy_readme)

args = parser.parse_args()
args.func(args)

Expand Down Expand Up @@ -338,6 +355,13 @@ def upload_kernels(args):
)


def run_copy_readme(args):
copy_readme_to_main(
repo_id=args.repo_id,
push_to_hub=args.push_to_hub,
)


def fill_kernel_card(args):
kernel_dir = Path(args.kernel_dir).resolve()
if not (kernel_dir / "build.toml").exists():
Expand Down
46 changes: 46 additions & 0 deletions kernels/src/kernels/cli/copy_readme.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from kernels._versions import _get_available_versions
from kernels.utils import _get_hf_api


def get_latest_version_readme(repo_id: str) -> str:
versions = _get_available_versions(repo_id)

if not versions:
raise ValueError(
f"No versions found for `{repo_id}`. "
"Upload at least one versioned kernel before generating a main README."
)

latest_version = max(versions.keys())
branch = f"v{latest_version}"

api = _get_hf_api()
readme_content = api.hf_hub_download(
repo_id=repo_id,
filename="README.md",
revision=branch,
)

with open(readme_content) as f:
return f.read()


def copy_readme_to_main(
repo_id: str,
push_to_hub: bool = False,
):
"""Copy the most recent version's README to the main branch."""
readme_content = get_latest_version_readme(repo_id)

if push_to_hub:
api = _get_hf_api()
api.upload_file(
path_or_fileobj=readme_content.encode("utf-8"),
path_in_repo="README.md",
repo_id=repo_id,
revision="main",
commit_message="Update main README from latest version.",
)
print(f"README pushed to https://huggingface.co/{repo_id}")
else:
print(readme_content)
117 changes: 117 additions & 0 deletions kernels/tests/test_copy_readme.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
from unittest.mock import MagicMock, mock_open, patch

import pytest

from kernels.cli.copy_readme import (
copy_readme_to_main,
get_latest_version_readme,
)


def _make_versions(version_nums: list[int]) -> dict:
versions = {}
for v in version_nums:
ref = MagicMock()
ref.name = f"v{v}"
ref.ref = f"refs/heads/v{v}"
versions[v] = ref
return versions


PATCH_VERSIONS = "kernels.cli.copy_readme._get_available_versions"
PATCH_API = "kernels.cli.copy_readme._get_hf_api"

SAMPLE_README = """\
---
license: apache-2.0
library_name: kernels
---

# my-kernel

This is the repository card of org/my-kernel.

## How to use

```python
from kernels import get_kernel
kernel_module = get_kernel("org/my-kernel")
```
"""


class TestGetLatestVersionReadme:
@patch("builtins.open", mock_open(read_data=SAMPLE_README))
@patch(PATCH_API)
@patch(PATCH_VERSIONS)
def test_fetches_latest_version(self, mock_versions, mock_api):
mock_versions.return_value = _make_versions([1, 2, 3])
api_instance = MagicMock()
api_instance.hf_hub_download.return_value = "/tmp/README.md"
mock_api.return_value = api_instance

readme = get_latest_version_readme("org/my-kernel")

api_instance.hf_hub_download.assert_called_once_with(
repo_id="org/my-kernel",
filename="README.md",
revision="v3",
)
assert readme == SAMPLE_README

@patch("builtins.open", mock_open(read_data=SAMPLE_README))
@patch(PATCH_API)
@patch(PATCH_VERSIONS)
def test_picks_highest_version(self, mock_versions, mock_api):
mock_versions.return_value = _make_versions([3, 1, 5, 2])
api_instance = MagicMock()
api_instance.hf_hub_download.return_value = "/tmp/README.md"
mock_api.return_value = api_instance

get_latest_version_readme("org/kernel")

api_instance.hf_hub_download.assert_called_once_with(
repo_id="org/kernel",
filename="README.md",
revision="v5",
)

@patch(PATCH_VERSIONS)
def test_no_versions_raises(self, mock_versions):
mock_versions.return_value = {}

with pytest.raises(ValueError, match="No versions found"):
get_latest_version_readme("org/kernel")


class TestCopyReadmeCLI:
@patch("builtins.open", mock_open(read_data=SAMPLE_README))
@patch(PATCH_API)
@patch(PATCH_VERSIONS)
def test_prints_to_stdout(self, mock_versions, mock_api, capsys):
mock_versions.return_value = _make_versions([1, 2])
api_instance = MagicMock()
api_instance.hf_hub_download.return_value = "/tmp/README.md"
mock_api.return_value = api_instance

copy_readme_to_main(repo_id="org/my-kernel")

captured = capsys.readouterr()
assert "# my-kernel" in captured.out

@patch("builtins.open", mock_open(read_data=SAMPLE_README))
@patch(PATCH_API)
@patch(PATCH_VERSIONS)
def test_push_to_hub(self, mock_versions, mock_api):
mock_versions.return_value = _make_versions([1])
api_instance = MagicMock()
api_instance.hf_hub_download.return_value = "/tmp/README.md"
mock_api.return_value = api_instance

copy_readme_to_main(repo_id="org/kernel", push_to_hub=True)

api_instance.upload_file.assert_called_once()
call_kwargs = api_instance.upload_file.call_args[1]
assert call_kwargs["path_in_repo"] == "README.md"
assert call_kwargs["repo_id"] == "org/kernel"
assert call_kwargs["revision"] == "main"
Loading