Bump version to 0.3.0 and update related configurations; add misaki patch script and remove obsolete build workflow

This commit is contained in:
remsky 2025-04-04 16:49:10 -06:00
parent e2313abe72
commit 6a2d3a54cf
15 changed files with 411 additions and 297 deletions

View file

@ -1,87 +0,0 @@
name: Docker Build and push
on:
push:
tags: [ 'v*.*.*' ]
paths-ignore:
- '**.md'
- 'docs/**'
workflow_dispatch:
inputs:
version:
description: 'Version to build and publish (e.g. v0.2.0)'
required: true
type: string
jobs:
prepare-release:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.get-version.outputs.version }}
is_prerelease: ${{ steps.check-prerelease.outputs.is_prerelease }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Get version
id: get-version
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "version=${{ inputs.version }}" >> $GITHUB_OUTPUT
else
echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT
fi
- name: Check if prerelease
id: check-prerelease
run: |
echo "is_prerelease=${{ contains(steps.get-version.outputs.version, '-pre') }}" >> $GITHUB_OUTPUT
build-images:
needs: prepare-release
runs-on: ubuntu-latest
permissions:
packages: write
env:
DOCKER_BUILDKIT: 1
BUILDKIT_STEP_LOG_MAX_SIZE: 10485760
# This environment variable will override the VERSION variable in your HCL file.
VERSION: ${{ needs.prepare-release.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Free disk space
run: |
echo "Listing current disk space"
df -h
echo "Cleaning up disk space..."
sudo rm -rf /usr/share/dotnet
sudo rm -rf /usr/local/lib/android
sudo rm -rf /opt/ghc
sudo rm -rf /opt/hostedtoolcache
docker system prune -af
echo "Disk space after cleanup"
df -h
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
driver-opts: |
image=moby/buildkit:latest
network=host
- name: Log in to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push images
run: |
# No need to override VERSION via --set; the env var does the job.
docker buildx bake --push

View file

@ -1,102 +0,0 @@
name: Docker Build and Publish
on:
push:
tags: [ 'v*.*.*' ]
paths-ignore:
- '**.md'
- 'docs/**'
workflow_dispatch:
inputs:
version:
description: 'Version to release (e.g. v0.2.0)'
required: true
type: string
jobs:
prepare-release:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.get-version.outputs.version }}
is_prerelease: ${{ steps.check-prerelease.outputs.is_prerelease }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Get version
id: get-version
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "version=${{ inputs.version }}" >> $GITHUB_OUTPUT
else
echo "version=$(cat VERSION)" >> $GITHUB_OUTPUT
fi
- name: Check if prerelease
id: check-prerelease
run: echo "is_prerelease=${{ contains(steps.get-version.outputs.version, '-pre') }}" >> $GITHUB_OUTPUT
build-images:
needs: prepare-release
runs-on: ubuntu-latest
permissions:
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Free disk space
run: |
echo "Listing current disk space"
df -h
echo "Cleaning up disk space..."
sudo rm -rf /usr/share/dotnet
sudo rm -rf /usr/local/lib/android
sudo rm -rf /opt/ghc
sudo rm -rf /opt/hostedtoolcache
docker system prune -af
echo "Disk space after cleanup"
df -h
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
driver-opts: |
image=moby/buildkit:latest
network=host
- name: Log in to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push images
env:
DOCKER_BUILDKIT: 1
BUILDKIT_STEP_LOG_MAX_SIZE: 10485760
VERSION: ${{ needs.prepare-release.outputs.version }}
run: docker buildx bake --push
create-release:
needs: [prepare-release, build-images]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Create Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ needs.prepare-release.outputs.version }}
generate_release_notes: true
draft: true
prerelease: ${{ needs.prepare-release.outputs.is_prerelease }}

95
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,95 @@
name: Create Release and Publish Docker Images
on:
push:
branches:
- release # Trigger when commits are pushed to the release branch (e.g., after merging master)
paths-ignore:
- '**.md'
- 'docs/**'
jobs:
prepare-release:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.get-version.outputs.version }}
version_tag: ${{ steps.get-version.outputs.version_tag }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Get version from VERSION file
id: get-version
run: |
VERSION_PLAIN=$(cat VERSION)
echo "version=${VERSION_PLAIN}" >> $GITHUB_OUTPUT
echo "version_tag=v${VERSION_PLAIN}" >> $GITHUB_OUTPUT # Add 'v' prefix for tag
build-images:
needs: prepare-release
runs-on: ubuntu-latest
permissions:
packages: write # Needed to push images to GHCR
env:
DOCKER_BUILDKIT: 1
BUILDKIT_STEP_LOG_MAX_SIZE: 10485760
# This environment variable will override the VERSION variable in docker-bake.hcl
VERSION: ${{ needs.prepare-release.outputs.version_tag }} # Use tag version (vX.Y.Z) for bake
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Free disk space # Optional: Keep if needed for large builds
run: |
echo "Listing current disk space"
df -h
echo "Cleaning up disk space..."
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache
docker system prune -af
echo "Disk space after cleanup"
df -h
- name: Set up QEMU
uses: docker/setup-qemu-action@v3 # Use v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 # Use v3
with:
driver-opts: |
image=moby/buildkit:latest
network=host
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3 # Use v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push images using Docker Bake
run: |
echo "Building and pushing images for version ${{ needs.prepare-release.outputs.version_tag }}"
# The VERSION env var above sets the tag for the bake file targets
docker buildx bake --push
create-release:
needs: [prepare-release, build-images]
runs-on: ubuntu-latest
permissions:
contents: write # Needed to create releases
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history for release notes generation
- name: Create GitHub Release
uses: softprops/action-gh-release@v2 # Use v2
with:
tag_name: ${{ needs.prepare-release.outputs.version_tag }} # Use vX.Y.Z tag
name: Release ${{ needs.prepare-release.outputs.version_tag }}
generate_release_notes: true # Auto-generate release notes
draft: false # Publish immediately
prerelease: false # Mark as a stable release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -3,12 +3,12 @@
</p> </p>
# <sub><sub>_`FastKoko`_ </sub></sub> # <sub><sub>_`FastKoko`_ </sub></sub>
[![Tests](https://img.shields.io/badge/tests-69%20passed-darkgreen)]() [![Tests](https://img.shields.io/badge/tests-69-darkgreen)]()
[![Coverage](https://img.shields.io/badge/coverage-52%25-tan)]() [![Coverage](https://img.shields.io/badge/coverage-54%25-tan)]()
[![Try on Spaces](https://img.shields.io/badge/%F0%9F%A4%97%20Try%20on-Spaces-blue)](https://huggingface.co/spaces/Remsky/Kokoro-TTS-Zero) [![Try on Spaces](https://img.shields.io/badge/%F0%9F%A4%97%20Try%20on-Spaces-blue)](https://huggingface.co/spaces/Remsky/Kokoro-TTS-Zero)
[![Kokoro](https://img.shields.io/badge/kokoro-v0.7.9::31a2b63-BB5420)](https://github.com/hexgrad/kokoro) [![Kokoro](https://img.shields.io/badge/kokoro-0.9.2-BB5420)](https://github.com/hexgrad/kokoro)
[![Misaki](https://img.shields.io/badge/misaki-v0.7.9::ebc76c2-B8860B)](https://github.com/hexgrad/misaki) [![Misaki](https://img.shields.io/badge/misaki-0.9.3-B8860B)](https://github.com/hexgrad/misaki)
[![Tested at Model Commit](https://img.shields.io/badge/last--tested--model--commit-1.0::9901c2b-blue)](https://huggingface.co/hexgrad/Kokoro-82M/commit/9901c2b79161b6e898b7ea857ae5298f47b8b0d6) [![Tested at Model Commit](https://img.shields.io/badge/last--tested--model--commit-1.0::9901c2b-blue)](https://huggingface.co/hexgrad/Kokoro-82M/commit/9901c2b79161b6e898b7ea857ae5298f47b8b0d6)
@ -24,10 +24,6 @@ Dockerized FastAPI wrapper for [Kokoro-82M](https://huggingface.co/hexgrad/Kokor
### Integration Guides ### Integration Guides
[![Helm Chart](https://img.shields.io/badge/Helm%20Chart-black?style=flat&logo=helm&logoColor=white)](https://github.com/remsky/Kokoro-FastAPI/wiki/Setup-Kubernetes) [![DigitalOcean](https://img.shields.io/badge/DigitalOcean-black?style=flat&logo=digitalocean&logoColor=white)](https://github.com/remsky/Kokoro-FastAPI/wiki/Integrations-DigitalOcean) [![SillyTavern](https://img.shields.io/badge/SillyTavern-black?style=flat&color=red)](https://github.com/remsky/Kokoro-FastAPI/wiki/Integrations-SillyTavern) [![Helm Chart](https://img.shields.io/badge/Helm%20Chart-black?style=flat&logo=helm&logoColor=white)](https://github.com/remsky/Kokoro-FastAPI/wiki/Setup-Kubernetes) [![DigitalOcean](https://img.shields.io/badge/DigitalOcean-black?style=flat&logo=digitalocean&logoColor=white)](https://github.com/remsky/Kokoro-FastAPI/wiki/Integrations-DigitalOcean) [![SillyTavern](https://img.shields.io/badge/SillyTavern-black?style=flat&color=red)](https://github.com/remsky/Kokoro-FastAPI/wiki/Integrations-SillyTavern)
[![OpenWebUI](https://img.shields.io/badge/OpenWebUI-black?style=flat&color=white)](https://github.com/remsky/Kokoro-FastAPI/wiki/Integrations-OpenWebUi) [![OpenWebUI](https://img.shields.io/badge/OpenWebUI-black?style=flat&color=white)](https://github.com/remsky/Kokoro-FastAPI/wiki/Integrations-OpenWebUi)
## Get Started ## Get Started
<details> <details>
@ -41,8 +37,8 @@ Refer to the core/config.py file for a full list of variables which can be manag
# the `latest` tag can be used, but should not be considered stable as it may include `nightly` branch builds # the `latest` tag can be used, but should not be considered stable as it may include `nightly` branch builds
# it may have some bonus features however, and feedback/testing is welcome # it may have some bonus features however, and feedback/testing is welcome
docker run -p 8880:8880 ghcr.io/remsky/kokoro-fastapi-cpu:v0.2.2 # CPU, or: docker run -p 8880:8880 ghcr.io/remsky/kokoro-fastapi-cpu:v0.3.0 # CPU, or:
docker run --gpus all -p 8880:8880 ghcr.io/remsky/kokoro-fastapi-gpu:v0.2.2 #NVIDIA GPU docker run --gpus all -p 8880:8880 ghcr.io/remsky/kokoro-fastapi-gpu:v0.3.0 #NVIDIA GPU
``` ```
@ -66,7 +62,7 @@ docker run --gpus all -p 8880:8880 ghcr.io/remsky/kokoro-fastapi-gpu:v0.2.2 #NV
# *Note for Apple Silicon (M1/M2) users: # *Note for Apple Silicon (M1/M2) users:
# The current GPU build relies on CUDA, which is not supported on Apple Silicon. # The current GPU build relies on CUDA, which is not supported on Apple Silicon.
# If you are on an M1/M2/M3 Mac, please use the `docker/cpu` setup. # If you are on an M1/M2/M3 Mac, please use the `docker/cpu` setup.
# MPS (Apples GPU acceleration) support is planned but not yet available. # MPS (Apple's GPU acceleration) support is planned but not yet available.
# Models will auto-download, but if needed you can manually download: # Models will auto-download, but if needed you can manually download:
python docker/scripts/download_model.py --output api/src/models/v1_0 python docker/scripts/download_model.py --output api/src/models/v1_0
@ -139,8 +135,8 @@ with client.audio.speech.with_streaming_response.create(
</details> </details>
## Features ## Features
<details> <details>
<summary>OpenAI-Compatible Speech Endpoint</summary> <summary>OpenAI-Compatible Speech Endpoint</summary>

View file

@ -1 +1 @@
v0.2.1 0.3.0

View file

@ -2,8 +2,8 @@ apiVersion: v2
name: kokoro-fastapi name: kokoro-fastapi
description: A Helm chart for deploying the Kokoro FastAPI TTS service to Kubernetes description: A Helm chart for deploying the Kokoro FastAPI TTS service to Kubernetes
type: application type: application
version: 0.2.0 version: 0.3.0
appVersion: "0.2.0" appVersion: "0.3.0"
keywords: keywords:
- tts - tts

View file

@ -20,7 +20,7 @@ services:
# # Gradio UI service [Comment out everything below if you don't need it] # # Gradio UI service [Comment out everything below if you don't need it]
# gradio-ui: # gradio-ui:
# image: ghcr.io/remsky/kokoro-fastapi-ui:v0.2.0 # image: ghcr.io/remsky/kokoro-fastapi-ui:v${VERSION}
# # Uncomment below (and comment out above) to build from source instead of using the released image # # Uncomment below (and comment out above) to build from source instead of using the released image
# build: # build:
# context: ../../ui # context: ../../ui

View file

@ -1,7 +1,7 @@
name: kokoro-tts-gpu name: kokoro-tts-gpu
services: services:
kokoro-tts: kokoro-tts:
# image: ghcr.io/remsky/kokoro-fastapi-gpu:v0.2.0 # image: ghcr.io/remsky/kokoro-fastapi-gpu:v${VERSION}
build: build:
context: ../.. context: ../..
dockerfile: docker/gpu/Dockerfile dockerfile: docker/gpu/Dockerfile
@ -24,7 +24,7 @@ services:
# # Gradio UI service # # Gradio UI service
# gradio-ui: # gradio-ui:
# image: ghcr.io/remsky/kokoro-fastapi-ui:v0.2.0 # image: ghcr.io/remsky/kokoro-fastapi-ui:v${VERSION}
# # Uncomment below to build from source instead of using the released image # # Uncomment below to build from source instead of using the released image
# # build: # # build:
# # context: ../../ui # # context: ../../ui

View file

@ -1,6 +1,6 @@
[project] [project]
name = "kokoro-fastapi" name = "kokoro-fastapi"
version = "0.1.4" version = "0.3.0"
description = "FastAPI TTS Service" description = "FastAPI TTS Service"
readme = "README.md" readme = "README.md"
requires-python = ">=3.10" requires-python = ">=3.10"
@ -31,10 +31,11 @@ dependencies = [
"matplotlib>=3.10.0", "matplotlib>=3.10.0",
"mutagen>=1.47.0", "mutagen>=1.47.0",
"psutil>=6.1.1", "psutil>=6.1.1",
"kokoro @ git+https://github.com/hexgrad/kokoro.git@31a2b6337b8c1b1418ef68c48142328f640da938", "espeakng-loader==0.2.4",
'misaki[en,ja,ko,zh] @ git+https://github.com/hexgrad/misaki.git@ebc76c21b66c5fc4866ed0ec234047177b396170', "kokoro==0.9.2",
"spacy==3.7.2", "misaki[en,ja,ko,zh]==0.9.3",
"en-core-web-sm @ https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.7.1/en_core_web_sm-3.7.1-py3-none-any.whl", "spacy==3.8.5",
"en-core-web-sm @ https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.8.0/en_core_web_sm-3.8.0-py3-none-any.whl",
"inflect>=7.5.0", "inflect>=7.5.0",
"phonemizer-fork>=3.3.2", "phonemizer-fork>=3.3.2",
"av>=14.2.0", "av>=14.2.0",
@ -53,8 +54,8 @@ test = [
"pytest-cov==6.0.0", "pytest-cov==6.0.0",
"httpx==0.26.0", "httpx==0.26.0",
"pytest-asyncio==0.25.3", "pytest-asyncio==0.25.3",
"openai>=1.59.6",
"tomli>=2.0.1", "tomli>=2.0.1",
"jinja2>=3.1.6"
] ]
[tool.uv] [tool.uv]

11
readme-parts/config.yaml Normal file
View file

@ -0,0 +1,11 @@
# README Configuration
# version is now automatically loaded from the VERSION file
tests_passed: "69"
coverage: "52"
kokoro_version: "0.7.9"
kokoro_commit: "31a2b63"
misaki_version: "0.7.9"
misaki_commit: "ebc76c2"
model_version: "1.0"
model_commit: "9901c2b"
model_commit_full: "9901c2b79161b6e898b7ea857ae5298f47b8b0d6"

45
scripts/fix_misaki.py Normal file
View file

@ -0,0 +1,45 @@
"""
Patch for misaki package to fix the EspeakWrapper.set_data_path issue.
"""
import os
import sys
import importlib.util
# Find the misaki package
try:
import misaki
misaki_path = os.path.dirname(misaki.__file__)
print(f"Found misaki package at: {misaki_path}")
except ImportError:
print("Misaki package not found. Make sure it's installed.")
sys.exit(1)
# Path to the espeak.py file
espeak_file = os.path.join(misaki_path, "espeak.py")
if not os.path.exists(espeak_file):
print(f"Could not find {espeak_file}")
sys.exit(1)
# Read the current content
with open(espeak_file, 'r') as f:
content = f.read()
# Check if the problematic line exists
if "EspeakWrapper.set_data_path(espeakng_loader.get_data_path())" in content:
# Replace the problematic line
new_content = content.replace(
"EspeakWrapper.set_data_path(espeakng_loader.get_data_path())",
"# Fixed line to use data_path attribute instead of set_data_path method\n"
"EspeakWrapper.data_path = espeakng_loader.get_data_path()"
)
# Write the modified content back
with open(espeak_file, 'w') as f:
f.write(new_content)
print(f"Successfully patched {espeak_file}")
else:
print(f"The problematic line was not found in {espeak_file}")
print("The file may have already been patched or the issue is different.")

View file

@ -4,39 +4,36 @@ import tomli
from pathlib import Path from pathlib import Path
def extract_dependency_info(): def extract_dependency_info():
"""Extract version and commit hash for kokoro and misaki from pyproject.toml""" """Extract version for kokoro and misaki from pyproject.toml"""
with open("pyproject.toml", "rb") as f: with open("pyproject.toml", "rb") as f:
pyproject = tomli.load(f) pyproject = tomli.load(f)
deps = pyproject["project"]["dependencies"] deps = pyproject["project"]["dependencies"]
info = {} info = {}
kokoro_found = False
misaki_found = False
# Extract kokoro info
for dep in deps: for dep in deps:
if dep.startswith("kokoro @"): # Match kokoro==version
# Extract version from the dependency string if available kokoro_match = re.match(r"^kokoro==(.+)$", dep)
version_match = re.search(r"kokoro @ git\+https://github\.com/hexgrad/kokoro\.git@", dep) if kokoro_match:
if version_match: info["kokoro"] = {"version": kokoro_match.group(1)}
# If no explicit version, use v0.7.9 as shown in the README kokoro_found = True
version = "v0.7.9"
commit_match = re.search(r"@([a-f0-9]{7})", dep) # Match misaki[...] ==version or misaki==version
if commit_match: misaki_match = re.match(r"^misaki(?:\[.*?\])?==(.+)$", dep)
info["kokoro"] = { if misaki_match:
"version": version, info["misaki"] = {"version": misaki_match.group(1)}
"commit": commit_match.group(1) misaki_found = True
}
elif dep.startswith("misaki["): # Stop if both found
# Extract version from the dependency string if available if kokoro_found and misaki_found:
version_match = re.search(r"misaki\[.*?\] @ git\+https://github\.com/hexgrad/misaki\.git@", dep) break
if version_match:
# If no explicit version, use v0.7.9 as shown in the README if not kokoro_found:
version = "v0.7.9" raise ValueError("Kokoro version not found in pyproject.toml dependencies")
commit_match = re.search(r"@([a-f0-9]{7})", dep) if not misaki_found:
if commit_match: raise ValueError("Misaki version not found in pyproject.toml dependencies")
info["misaki"] = {
"version": version,
"commit": commit_match.group(1)
}
return info return info
@ -98,17 +95,21 @@ def update_readme_badges(passed_tests, coverage_percentage, dep_info):
# Update kokoro badge # Update kokoro badge
if "kokoro" in dep_info: if "kokoro" in dep_info:
# Find badge like kokoro-v0.9.2::abcdefg-BB5420 or kokoro-v0.9.2-BB5420
kokoro_version = dep_info["kokoro"]["version"]
content = re.sub( content = re.sub(
r'!\[Kokoro\]\(https://img\.shields\.io/badge/kokoro-[^)]+\)', r'(!\[Kokoro\]\(https://img\.shields\.io/badge/kokoro-)[^)-]+(-BB5420\))',
f'![Kokoro](https://img.shields.io/badge/kokoro-{dep_info["kokoro"]["version"]}::{dep_info["kokoro"]["commit"]}-BB5420)', lambda m: f"{m.group(1)}{kokoro_version}{m.group(2)}",
content content
) )
# Update misaki badge # Update misaki badge
if "misaki" in dep_info: if "misaki" in dep_info:
# Find badge like misaki-v0.9.3::abcdefg-B8860B or misaki-v0.9.3-B8860B
misaki_version = dep_info["misaki"]["version"]
content = re.sub( content = re.sub(
r'!\[Misaki\]\(https://img\.shields\.io/badge/misaki-[^)]+\)', r'(!\[Misaki\]\(https://img\.shields\.io/badge/misaki-)[^)-]+(-B8860B\))',
f'![Misaki](https://img.shields.io/badge/misaki-{dep_info["misaki"]["version"]}::{dep_info["misaki"]["commit"]}-B8860B)', lambda m: f"{m.group(1)}{misaki_version}{m.group(2)}",
content content
) )
@ -128,9 +129,9 @@ def main():
print(f"- Tests: {passed_tests} passed") print(f"- Tests: {passed_tests} passed")
print(f"- Coverage: {coverage_percentage}%") print(f"- Coverage: {coverage_percentage}%")
if "kokoro" in dep_info: if "kokoro" in dep_info:
print(f"- Kokoro: {dep_info['kokoro']['version']}::{dep_info['kokoro']['commit']}") print(f"- Kokoro: {dep_info['kokoro']['version']}")
if "misaki" in dep_info: if "misaki" in dep_info:
print(f"- Misaki: {dep_info['misaki']['version']}::{dep_info['misaki']['commit']}") print(f"- Misaki: {dep_info['misaki']['version']}")
else: else:
print("Failed to update badges") print("Failed to update badges")

195
scripts/update_version.py Executable file
View file

@ -0,0 +1,195 @@
#!/usr/bin/env python3
"""
Version Update Script
This script reads the version from the VERSION file and updates references
in pyproject.toml, the Helm chart, and README.md.
"""
import re
import yaml
from pathlib import Path
# Get the project root directory
ROOT_DIR = Path(__file__).parent.parent
# --- Configuration ---
VERSION_FILE = ROOT_DIR / "VERSION"
PYPROJECT_FILE = ROOT_DIR / "pyproject.toml"
HELM_CHART_FILE = ROOT_DIR / "charts" / "kokoro-fastapi" / "Chart.yaml"
README_FILE = ROOT_DIR / "README.md"
# --- End Configuration ---
def update_pyproject(version: str):
"""Updates the version in pyproject.toml"""
if not PYPROJECT_FILE.exists():
print(f"Skipping: {PYPROJECT_FILE} not found.")
return
try:
content = PYPROJECT_FILE.read_text()
# Regex to find and capture current version = "X.Y.Z" under [project]
pattern = r'(^\[project\]\s*(?:.*\s)*?version\s*=\s*)"([^"]+)"'
match = re.search(pattern, content, flags=re.MULTILINE)
if not match:
print(f"Warning: Version pattern not found in {PYPROJECT_FILE}")
return
current_version = match.group(2)
if current_version == version:
print(f"Already up-to-date: {PYPROJECT_FILE} (version {version})")
else:
# Perform replacement
new_content = re.sub(pattern, rf'\1"{version}"', content, count=1, flags=re.MULTILINE)
PYPROJECT_FILE.write_text(new_content)
print(f"Updated {PYPROJECT_FILE} from {current_version} to {version}")
except Exception as e:
print(f"Error processing {PYPROJECT_FILE}: {e}")
def update_helm_chart(version: str):
"""Updates the version and appVersion in the Helm chart"""
if not HELM_CHART_FILE.exists():
print(f"Skipping: {HELM_CHART_FILE} not found.")
return
try:
content = HELM_CHART_FILE.read_text()
original_content = content
updated_count = 0
# Update 'version:' line (unquoted)
# Looks for 'version:' followed by optional whitespace and the version number
version_pattern = r"^(version:\s*)(\S+)"
current_version_match = re.search(version_pattern, content, flags=re.MULTILINE)
if current_version_match and current_version_match.group(2) != version:
content = re.sub(version_pattern, rf"\g<1>{version}", content, count=1, flags=re.MULTILINE)
print(f"Updating 'version' in {HELM_CHART_FILE} from {current_version_match.group(2)} to {version}")
updated_count += 1
elif current_version_match:
print(f"Already up-to-date: 'version' in {HELM_CHART_FILE} is {version}")
else:
print(f"Warning: 'version:' pattern not found in {HELM_CHART_FILE}")
# Update 'appVersion:' line (quoted or unquoted)
# Looks for 'appVersion:' followed by optional whitespace, optional quote, the version, optional quote
app_version_pattern = r"^(appVersion:\s*)(\"?)([^\"\s]+)(\"?)"
current_app_version_match = re.search(app_version_pattern, content, flags=re.MULTILINE)
if current_app_version_match:
leading_whitespace = current_app_version_match.group(1) # e.g., "appVersion: "
opening_quote = current_app_version_match.group(2) # e.g., '"' or ''
current_app_ver = current_app_version_match.group(3) # e.g., '0.2.0'
closing_quote = current_app_version_match.group(4) # e.g., '"' or ''
# Check if quotes were consistent (both present or both absent)
if opening_quote != closing_quote:
print(f"Warning: Inconsistent quotes found for appVersion in {HELM_CHART_FILE}. Skipping update for this line.")
elif current_app_ver == version and opening_quote == '"': # Check if already correct *and* quoted
print(f"Already up-to-date: 'appVersion' in {HELM_CHART_FILE} is \"{version}\"")
else:
# Always replace with the quoted version
replacement = f'{leading_whitespace}"{version}"' # Ensure quotes
original_display = f"{opening_quote}{current_app_ver}{closing_quote}" # How it looked before
target_display = f'"{version}"' # How it should look
# Only report update if the displayed value actually changes
if original_display != target_display:
content = re.sub(app_version_pattern, replacement, content, count=1, flags=re.MULTILINE)
print(f"Updating 'appVersion' in {HELM_CHART_FILE} from {original_display} to {target_display}")
updated_count += 1
else:
# It matches the target version but might need quoting fixed silently if we didn't update
# Or it was already correct. Check if content changed. If not, report up-to-date.
if not (content != original_content and updated_count > 0): # Avoid double message if version also changed
print(f"Already up-to-date: 'appVersion' in {HELM_CHART_FILE} is {target_display}")
else:
print(f"Warning: 'appVersion:' pattern not found in {HELM_CHART_FILE}")
# Write back only if changes were made
if content != original_content:
HELM_CHART_FILE.write_text(content)
# Confirmation message printed above during the specific update
elif updated_count == 0 and current_version_match and current_app_version_match :
# If no updates were made but patterns were found, confirm it's up-to-date overall
print(f"Already up-to-date: {HELM_CHART_FILE} (version {version})")
except Exception as e:
print(f"Error processing {HELM_CHART_FILE}: {e}")
def update_readme(version_with_v: str):
"""Updates Docker image tags in README.md"""
if not README_FILE.exists():
print(f"Skipping: {README_FILE} not found.")
return
try:
content = README_FILE.read_text()
# Regex to find and capture current ghcr.io/.../kokoro-fastapi-(cpu|gpu):vX.Y.Z
pattern = r'(ghcr\.io/remsky/kokoro-fastapi-(?:cpu|gpu)):(v\d+\.\d+\.\d+)'
matches = list(re.finditer(pattern, content)) # Find all occurrences
if not matches:
print(f"Warning: Docker image tag pattern not found in {README_FILE}")
else:
updated_needed = False
for match in matches:
current_tag = match.group(2)
if current_tag != version_with_v:
updated_needed = True
break # Only need one mismatch to trigger update
if updated_needed:
# Perform replacement on all occurrences
new_content = re.sub(pattern, rf'\1:{version_with_v}', content)
README_FILE.write_text(new_content)
print(f"Updated Docker image tags in {README_FILE} to {version_with_v}")
else:
print(f"Already up-to-date: Docker image tags in {README_FILE} (version {version_with_v})")
# Check for ':latest' tag usage remains the same
if ':latest' in content:
print(f"Warning: Found ':latest' tag in {README_FILE}. Consider updating manually if needed.")
except Exception as e:
print(f"Error processing {README_FILE}: {e}")
def main():
# Read the version from the VERSION file
if not VERSION_FILE.exists():
print(f"Error: {VERSION_FILE} not found.")
return
try:
version = VERSION_FILE.read_text().strip()
if not re.match(r"^\d+\.\d+\.\d+$", version):
print(f"Error: Invalid version format '{version}' in {VERSION_FILE}. Expected X.Y.Z")
return
except Exception as e:
print(f"Error reading {VERSION_FILE}: {e}")
return
print(f"Read version: {version} from {VERSION_FILE}")
print("-" * 20)
# Prepare versions (with and without 'v')
version_plain = version
version_with_v = f"v{version}"
# Update files
update_pyproject(version_plain)
update_helm_chart(version_plain)
update_readme(version_with_v)
print("-" * 20)
print("Version update script finished.")
if __name__ == "__main__":
main()

View file

@ -1,49 +0,0 @@
{
"document": "doc.report.command",
"version": "ov/command/slim/1.1",
"engine": "linux/amd64|ALP|x.1.42.2|29e62e7836de7b1004607c51c502537ffe1969f0|2025-01-16_07:48:54AM|x",
"containerized": false,
"host_distro": {
"name": "Ubuntu",
"version": "22.04",
"display_name": "Ubuntu 22.04.5 LTS"
},
"type": "slim",
"state": "error",
"target_reference": "kokoro-fastapi:latest",
"system": {
"type": "",
"release": "",
"distro": {
"name": "",
"version": "",
"display_name": ""
}
},
"source_image": {
"identity": {
"id": ""
},
"size": 0,
"size_human": "",
"create_time": "",
"architecture": "",
"container_entry": {
"exe_path": ""
}
},
"minified_image_size": 0,
"minified_image_size_human": "",
"minified_image": "",
"minified_image_id": "",
"minified_image_digest": "",
"minified_image_has_data": false,
"minified_by": 0,
"artifact_location": "",
"container_report_name": "",
"seccomp_profile_name": "",
"apparmor_profile_name": "",
"image_stack": null,
"image_created": false,
"image_build_engine": ""
}

View file

@ -10,9 +10,17 @@ export PYTHONPATH=$PROJECT_ROOT:$PROJECT_ROOT/api
export MODEL_DIR=src/models export MODEL_DIR=src/models
export VOICES_DIR=src/voices/v1_0 export VOICES_DIR=src/voices/v1_0
export WEB_PLAYER_PATH=$PROJECT_ROOT/web export WEB_PLAYER_PATH=$PROJECT_ROOT/web
# Set the espeak-ng data path to your location
export ESPEAK_DATA_PATH=/usr/lib/x86_64-linux-gnu/espeak-ng-data
# Run FastAPI with CPU extras using uv run # Run FastAPI with CPU extras using uv run
# Note: espeak may still require manual installation, # Note: espeak may still require manual installation,
uv pip install -e ".[cpu]" uv pip install -e ".[cpu]"
uv run --no-sync python docker/scripts/download_model.py --output api/src/models/v1_0 uv run --no-sync python docker/scripts/download_model.py --output api/src/models/v1_0
# Apply the misaki patch to fix possible EspeakWrapper issue in older versions
# echo "Applying misaki patch..."
# python scripts/fix_misaki.py
# Start the server
uv run --no-sync uvicorn api.src.main:app --host 0.0.0.0 --port 8880 uv run --no-sync uvicorn api.src.main:app --host 0.0.0.0 --port 8880