Skip to content
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

- fix(index): include available SHA256 hashes in wheel links by @hakarrr in #2409
- fix(ci): use supported Python versions on Ubuntu by @abetlen in #2407
- feat: update llama.cpp to ggml-org/llama.cpp@7d7f02875

Expand Down
16 changes: 12 additions & 4 deletions scripts/releases-to-pep-503.sh
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,12 @@ for release in $releases; do
continue
fi

wheel_urls=$(echo "$response" | jq -r '.assets[] | select(.name | endswith(".whl")) | .browser_download_url')
if [ -z "$wheel_urls" ]; then
wheel_assets=$(echo "$response" | jq -r '
.assets[] | select(.name | endswith(".whl")) |
[.browser_download_url,
(if (.digest | type) == "string" then .digest else "" end)] | @tsv
')
if [ -z "$wheel_assets" ]; then
log_error "No wheel files found for release $release"
continue
fi
Expand All @@ -95,8 +99,12 @@ for release in $releases; do
release_version=$(echo "$release" | grep -oE "^[v]?[0-9]+\.[0-9]+\.[0-9]+")
echo " <h2>$release_version</h2>" >> "$output_dir/llama-cpp-python/index.html"

echo "$wheel_urls" | while read -r asset; do
echo " <a href=\"$asset\">$asset</a>" >> "$output_dir/llama-cpp-python/index.html"
echo "$wheel_assets" | while IFS=$'\t' read -r asset digest; do
href="$asset"
if [[ "$digest" =~ ^sha256:([[:xdigit:]]{64})$ ]]; then
href="$asset#sha256=${BASH_REMATCH[1]}"
fi
echo " <a href=\"$href\">$asset</a>" >> "$output_dir/llama-cpp-python/index.html"
echo " <br>" >> "$output_dir/llama-cpp-python/index.html"
done
done
Expand Down
130 changes: 130 additions & 0 deletions tests/test_release_index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
"""Exercise the release-index generator without downloading release assets."""

import json
import os
from pathlib import Path
import shutil
import subprocess
from html.parser import HTMLParser

import pytest


SCRIPT = Path(__file__).resolve().parents[1] / "scripts" / "releases-to-pep-503.sh"
WHEEL_URL = "https://example.invalid/llama_cpp_python-0.3.36-py3-none-any.whl"
SHA256 = "0123456789abcdef" * 4


class Links(HTMLParser):
def __init__(self):
super().__init__()
self.hrefs = []

def handle_starttag(self, tag, attrs):
if tag == "a":
self.hrefs.append(dict(attrs)["href"])


@pytest.fixture
def generate_index(tmp_path):
for tool in ("bash", "jq"):
if shutil.which(tool) is None:
pytest.skip(f"release-index tests require {tool}")
bin_dir = tmp_path / "bin"
bin_dir.mkdir()
curl = bin_dir / "curl"
curl.write_text('#!/bin/sh\ncat "$RELEASE_RESPONSE"\n')
curl.chmod(0o755)
response = tmp_path / "response.json"
env = {
**os.environ,
"PATH": f"{bin_dir}{os.pathsep}{os.environ['PATH']}",
"RELEASE_RESPONSE": str(response),
}

def run(assets, releases="v0.3.36\n", pattern=None):
response.write_text(json.dumps({"assets": assets}))
(tmp_path / "all_releases.txt").write_text(releases)
output = tmp_path / "index"
command = ["bash", str(SCRIPT), str(output)]
if pattern is not None:
command.append(pattern)
subprocess.run(command, cwd=tmp_path, env=env, check=True, capture_output=True)
page = (output / "llama-cpp-python" / "index.html").read_text()
parser = Links()
parser.feed(page)
return parser.hrefs, page

return run


def wheel(**kwargs):
return {
"name": "llama_cpp_python-0.3.36-py3-none-any.whl",
"browser_download_url": WHEEL_URL,
**kwargs,
}


@pytest.mark.parametrize("digest", [f"sha256:{SHA256}", f"sha256:{SHA256.upper()}"])
def test_available_sha256_digest(generate_index, digest):
links, page = generate_index([wheel(digest=digest)])
assert links == [f"{WHEEL_URL}#sha256={digest.split(':')[1]}"]
assert f">{WHEEL_URL}</a>" in page


@pytest.mark.parametrize(
"digest",
[
None,
"",
"sha256:abc",
"sha256:" + "z" * 64,
"sha512:" + "a" * 128,
"sha256:" + SHA256 + "extra",
123,
False,
{},
[],
],
)
def test_unusable_digest_preserves_original_link(generate_index, digest):
links, _ = generate_index([wheel(digest=digest)])
assert links == [WHEEL_URL]


def test_historical_asset_without_digest(generate_index):
links, _ = generate_index([wheel()])
assert links == [WHEEL_URL]


def test_mixed_release_assets(generate_index):
legacy_url = WHEEL_URL.replace("py3-none-any", "cp311-cp311-linux_x86_64")
assets = [
wheel(digest=f"sha256:{SHA256}"),
{**wheel(), "browser_download_url": legacy_url},
{
"name": "source.tar.gz",
"browser_download_url": "https://example.invalid/source.tar.gz",
"digest": f"sha256:{SHA256}",
},
]
links, _ = generate_index(assets)
assert links == [f"{WHEEL_URL}#sha256={SHA256}", legacy_url]


def test_backend_release_pattern(generate_index):
links, page = generate_index(
[wheel(digest=f"sha256:{SHA256}")],
releases="v0.3.36\nv0.3.36-cu130\n",
pattern=r"^[v]?[0-9]+\.[0-9]+\.[0-9]+-cu130$",
)
assert links == [f"{WHEEL_URL}#sha256={SHA256}"]
assert "<h2>v0.3.36</h2>" in page


@pytest.mark.parametrize("assets,releases", [([], "v0.3.36\n"), ([wheel()], "")])
def test_empty_indexes_still_generate(generate_index, assets, releases):
links, page = generate_index(assets, releases=releases)
assert links == []
assert page.rstrip().endswith("</html>")