mirror of
https://github.com/modelscope/FunASR.git
synced 2026-10-12 02:19:18 +00:00
ci: add runtime downloads to package releases
Ensure package releases link the current llama.cpp runtime assets.
This commit is contained in:
@@ -19,3 +19,7 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: gh release create "${{ github.ref_name }}" --generate-notes --verify-tag --title "${{ github.ref_name }}"
|
||||
- name: Add Runtime downloads to GitHub Release
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: python scripts/update_release_runtime_downloads.py "${{ github.ref_name }}"
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Add prebuilt runtime download links to a Python package GitHub release."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
RUNTIME_SECTION_HEADING = "## Runtime downloads"
|
||||
|
||||
|
||||
def run_gh_json(args: list[str]) -> Any:
|
||||
completed = subprocess.run(
|
||||
["gh", *args],
|
||||
check=True,
|
||||
stdout=subprocess.PIPE,
|
||||
text=True,
|
||||
)
|
||||
return json.loads(completed.stdout)
|
||||
|
||||
|
||||
def run_gh(args: list[str]) -> None:
|
||||
subprocess.run(["gh", *args], check=True)
|
||||
|
||||
|
||||
def platform_label(asset_name: str) -> str:
|
||||
if "linux-arm64" in asset_name:
|
||||
return "Linux arm64"
|
||||
if "linux-x64-vulkan" in asset_name:
|
||||
return "Linux x64 Vulkan"
|
||||
if "linux-x64-avx2" in asset_name:
|
||||
return "Linux x64 AVX2"
|
||||
if "linux-x64" in asset_name:
|
||||
return "Linux x64 portable"
|
||||
if "macos-arm64" in asset_name:
|
||||
return "macOS arm64"
|
||||
if "windows-x64-cuda" in asset_name:
|
||||
return "Windows x64 CUDA"
|
||||
if "windows-x64-avx2" in asset_name:
|
||||
return "Windows x64 AVX2"
|
||||
if "windows-x64" in asset_name:
|
||||
return "Windows x64 portable"
|
||||
return asset_name
|
||||
|
||||
|
||||
def latest_runtime_tag(repository: str) -> str:
|
||||
releases = run_gh_json(
|
||||
[
|
||||
"release",
|
||||
"list",
|
||||
"--repo",
|
||||
repository,
|
||||
"--limit",
|
||||
"50",
|
||||
"--json",
|
||||
"tagName,publishedAt,isDraft,isPrerelease",
|
||||
]
|
||||
)
|
||||
for release in releases:
|
||||
if release["tagName"].startswith("runtime-llamacpp-v") and not release.get(
|
||||
"isDraft"
|
||||
):
|
||||
return release["tagName"]
|
||||
raise RuntimeError("No published runtime-llamacpp release found")
|
||||
|
||||
|
||||
def load_release(repository: str, tag: str) -> dict[str, Any]:
|
||||
return run_gh_json(
|
||||
[
|
||||
"release",
|
||||
"view",
|
||||
tag,
|
||||
"--repo",
|
||||
repository,
|
||||
"--json",
|
||||
"tagName,name,body,url,assets",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def build_runtime_downloads_section(
|
||||
*, python_tag: str, runtime_release: dict[str, Any]
|
||||
) -> str:
|
||||
assets = sorted(runtime_release.get("assets") or [], key=lambda asset: asset["name"])
|
||||
runtime_assets = [
|
||||
asset for asset in assets if asset["name"].startswith("funasr-llamacpp-")
|
||||
]
|
||||
if not runtime_assets:
|
||||
raise RuntimeError(f"{runtime_release['tagName']} has no llama.cpp assets")
|
||||
|
||||
python_version = python_tag.removeprefix("v")
|
||||
lines = [
|
||||
RUNTIME_SECTION_HEADING,
|
||||
"",
|
||||
(
|
||||
"This Python release pairs with the current prebuilt llama.cpp / GGUF "
|
||||
f"runtime release: [{runtime_release['tagName']}]({runtime_release['url']})."
|
||||
),
|
||||
"",
|
||||
(
|
||||
"Use these assets when you want the self-contained `llama-funasr-*` "
|
||||
"binaries instead of the Python package. The runtime release is shared "
|
||||
f"by the current Python releases; `{python_tag}` itself is the PyPI package release."
|
||||
),
|
||||
"",
|
||||
"| Platform | Asset | SHA-256 |",
|
||||
"|---|---|---|",
|
||||
]
|
||||
for asset in runtime_assets:
|
||||
digest = (asset.get("digest") or "").removeprefix("sha256:")
|
||||
lines.append(
|
||||
f"| {platform_label(asset['name'])} | "
|
||||
f"[{asset['name']}]({asset['url']}) | "
|
||||
f"`{digest}` |"
|
||||
)
|
||||
|
||||
lines.extend(
|
||||
[
|
||||
"",
|
||||
(
|
||||
"Quick start: download one asset, unpack it, then run the bundled "
|
||||
"`download-funasr-model.sh <sensevoice|paraformer|nano>` helper and "
|
||||
"one of `llama-funasr-cli`, `llama-funasr-sensevoice`, or "
|
||||
"`llama-funasr-paraformer`."
|
||||
),
|
||||
"",
|
||||
"For Python users, install from PyPI:",
|
||||
"",
|
||||
"```bash",
|
||||
f'python -m pip install -U "funasr=={python_version}"',
|
||||
"```",
|
||||
]
|
||||
)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def merge_release_body(
|
||||
*, current_body: str, python_tag: str, runtime_release: dict[str, Any]
|
||||
) -> str:
|
||||
body_without_runtime = current_body.split(RUNTIME_SECTION_HEADING, maxsplit=1)[
|
||||
0
|
||||
].rstrip()
|
||||
runtime_section = build_runtime_downloads_section(
|
||||
python_tag=python_tag, runtime_release=runtime_release
|
||||
)
|
||||
return f"{body_without_runtime}\n\n{runtime_section}\n"
|
||||
|
||||
|
||||
def update_release_body(repository: str, python_tag: str, runtime_tag: str | None) -> None:
|
||||
runtime_tag = runtime_tag or latest_runtime_tag(repository)
|
||||
python_release = load_release(repository, python_tag)
|
||||
runtime_release = load_release(repository, runtime_tag)
|
||||
merged = merge_release_body(
|
||||
current_body=python_release.get("body") or "",
|
||||
python_tag=python_tag,
|
||||
runtime_release=runtime_release,
|
||||
)
|
||||
|
||||
with tempfile.NamedTemporaryFile("w", encoding="utf-8", delete=False) as handle:
|
||||
handle.write(merged)
|
||||
notes_path = Path(handle.name)
|
||||
try:
|
||||
run_gh(
|
||||
[
|
||||
"release",
|
||||
"edit",
|
||||
python_tag,
|
||||
"--repo",
|
||||
repository,
|
||||
"--notes-file",
|
||||
str(notes_path),
|
||||
]
|
||||
)
|
||||
finally:
|
||||
notes_path.unlink(missing_ok=True)
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("python_tag", help="Python package release tag, for example v1.3.26")
|
||||
parser.add_argument(
|
||||
"--repo",
|
||||
default=os.environ.get("GITHUB_REPOSITORY", "modelscope/FunASR"),
|
||||
help="GitHub repository in owner/name form",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--runtime-tag",
|
||||
default=None,
|
||||
help="Runtime release tag. Defaults to the latest runtime-llamacpp-v* release.",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
update_release_body(args.repo, args.python_tag, args.runtime_tag)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,112 @@
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def load_release_script():
|
||||
script_path = ROOT / "scripts" / "update_release_runtime_downloads.py"
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
"update_release_runtime_downloads", script_path
|
||||
)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def runtime_release_payload():
|
||||
return {
|
||||
"tagName": "runtime-llamacpp-v0.1.8",
|
||||
"url": "https://github.com/modelscope/FunASR/releases/tag/runtime-llamacpp-v0.1.8",
|
||||
"assets": [
|
||||
{
|
||||
"name": "funasr-llamacpp-linux-arm64.tar.gz",
|
||||
"url": "https://example.test/linux-arm64.tar.gz",
|
||||
"digest": "sha256:linuxarm64",
|
||||
},
|
||||
{
|
||||
"name": "funasr-llamacpp-linux-x64-avx2.tar.gz",
|
||||
"url": "https://example.test/linux-avx2.tar.gz",
|
||||
"digest": "sha256:linuxavx2",
|
||||
},
|
||||
{
|
||||
"name": "funasr-llamacpp-linux-x64-vulkan.tar.gz",
|
||||
"url": "https://example.test/linux-vulkan.tar.gz",
|
||||
"digest": "sha256:linuxvulkan",
|
||||
},
|
||||
{
|
||||
"name": "funasr-llamacpp-linux-x64.tar.gz",
|
||||
"url": "https://example.test/linux-x64.tar.gz",
|
||||
"digest": "sha256:linuxx64",
|
||||
},
|
||||
{
|
||||
"name": "funasr-llamacpp-macos-arm64.tar.gz",
|
||||
"url": "https://example.test/macos-arm64.tar.gz",
|
||||
"digest": "sha256:macosarm64",
|
||||
},
|
||||
{
|
||||
"name": "funasr-llamacpp-windows-x64-avx2.zip",
|
||||
"url": "https://example.test/windows-avx2.zip",
|
||||
"digest": "sha256:windowsavx2",
|
||||
},
|
||||
{
|
||||
"name": "funasr-llamacpp-windows-x64-cuda.zip",
|
||||
"url": "https://example.test/windows-cuda.zip",
|
||||
"digest": "sha256:windowscuda",
|
||||
},
|
||||
{
|
||||
"name": "funasr-llamacpp-windows-x64.zip",
|
||||
"url": "https://example.test/windows-x64.zip",
|
||||
"digest": "sha256:windowsx64",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def test_release_on_tag_workflow_adds_runtime_downloads_to_latest_release():
|
||||
workflow = (ROOT / ".github" / "workflows" / "release-on-tag.yml").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
|
||||
assert "scripts/update_release_runtime_downloads.py" in workflow
|
||||
assert "Runtime downloads" in workflow
|
||||
|
||||
|
||||
def test_runtime_download_section_lists_all_prebuilt_assets():
|
||||
module = load_release_script()
|
||||
|
||||
section = module.build_runtime_downloads_section(
|
||||
python_tag="v1.3.26",
|
||||
runtime_release=runtime_release_payload(),
|
||||
)
|
||||
|
||||
assert "## Runtime downloads" in section
|
||||
assert "runtime-llamacpp-v0.1.8" in section
|
||||
assert 'python -m pip install -U "funasr==1.3.26"' in section
|
||||
assert section.count("| [funasr-llamacpp-") == 8
|
||||
assert "Windows x64 CUDA" in section
|
||||
assert "Linux x64 Vulkan" in section
|
||||
assert "`windowscuda`" in section
|
||||
|
||||
|
||||
def test_merge_release_body_replaces_stale_runtime_download_section():
|
||||
module = load_release_script()
|
||||
old_body = """## What's Changed
|
||||
* old release note
|
||||
|
||||
## Runtime downloads
|
||||
|
||||
old runtime table
|
||||
"""
|
||||
|
||||
merged = module.merge_release_body(
|
||||
current_body=old_body,
|
||||
python_tag="v1.3.26",
|
||||
runtime_release=runtime_release_payload(),
|
||||
)
|
||||
|
||||
assert "* old release note" in merged
|
||||
assert "old runtime table" not in merged
|
||||
assert merged.count("## Runtime downloads") == 1
|
||||
assert "funasr-llamacpp-windows-x64-cuda.zip" in merged
|
||||
Reference in New Issue
Block a user